Commit Graph

96 Commits

Author SHA1 Message Date
shitratgit[bot] 80639a382a fix: bound flowing capture and retire old memory surfaces 2026-08-25 07:22:16 +00:00
shitratgit[bot] 4e7d6ec353 fix(web): remove undeployable local dependencies 2026-07-29 22:24:12 -07:00
shitratgit[bot] 60d5333730 fix(search): retire Typesense run projections 2026-07-29 19:10:22 -07:00
Joel Hooks 2dd747de22 feat: retire run_chunks_dev write path
Joel-approved cutover (2026-07-20): sessions.db is the only
full-transcript chunk index. The index-chunks Typesense import is
removed, ensureCollections no longer recreates run_chunks_dev, and the
stale web polling hint now points at joelclaw sessions search. Typesense
keeps runs_dev metadata for provenance and health.
2026-07-19 21:38:00 -07:00
Joel Hooks 42d5c4a61f feat(convex): custody transferred to joelclaw-api — apps/web is now a client
convex/ (schema, functions, deploy role for :3210) moved to
joelclaw-api; apps/web consumes generated api/types via link:.
sessions schema is a deliberate temporary v.union (auth + call
telemetry shapes) — separation chartered. All four readback checks
pass: deploy clean, tsc clean, no old-path refs, live read works.
Worker-authored, steering-verified.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 09:25:54 -07:00
shitratgit[bot] 4ca283b185 fix(web): point docs proxy fallback upstream at flagg, not panda
Panda is being decommissioned. The docs proxy's hardcoded fallback in
apps/web/app/api/docs/[[...path]]/route.ts pointed at panda's Tailscale
Funnel URL; it now falls back to flagg's docs-api Funnel URL instead.
DOCS_API_UPSTREAM_URL is already set in Vercel and points at flagg, so
this is behaviorally inert -- panda-decommissioning hygiene only.

"Deprecation can feel like the dirty work of cleaning up the street
after the circus parade has just passed through town, yet these
efforts improve the overall software ecosystem by reducing maintenance
overhead and cognitive burden of engineers." -- Software Engineering at
Google: Lessons Learned from Programming Over Time, Ch. 15 "Deprecation"

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 14:38:38 +00:00
shitratgit[bot] 5cc9423eb5 fix: require explicit Inngest event keys 2026-06-26 08:05:41 -07:00
Joel Hooks cafdf2b45e feat(memory): Phase 3 — PDS App Password auth (code-complete; verify pending infra)
Replaces the dev-bearer table with PDS-backed App Password authentication
per ADR-0243 Rule 20, scoped to single-user (Joel only) for now.

packages/system-bus/src/lib/pds.ts
  + pdsCreateAppPassword — calls com.atproto.server.createAppPassword on
    the caller's session; returns { name, password, did, handle }
  + pdsRevokeAppPassword — revokes by name
  + pdsValidateAppPassword — one-shot validation via createSession; used
    at register time only, NOT per-request (hash lookup is cheaper +
    safer, and doesn't create server-side session state at PDS)

packages/memory/src/schemas/machines.ts — NEW Typesense schema for
  machines_dev collection: id, user_id, did, handle, machine_name,
  app_password_name, app_password_sha256, created_at, last_seen_at,
  revoked_at

apps/web/lib/memory-auth.ts — NEW auth middleware
  authenticateMemoryRequest(req) → MemoryIdentity | null
  Hot path: sha256(bearer) → Typesense machines_dev lookup →
    { user_id, machine_id, did, source: "app-password" }
  No PDS roundtrip per request. App Password validity is established
  at register time; the hash is the identity key thereafter.
  Dev-bearer fallback retained during transition (MEMORY_DEV_BEARER_TOKENS
  env var); effectively disappears when that var is empty in prod.
  Phase 3.5 TODO: users_dev collection (DID→user_id map is hardcoded for
  now).

apps/web/app/api/runs/*/route.ts — all 6 handlers now call
  authenticateMemoryRequest instead of the inline DEV_BEARER_TOKENS
  table + local authenticate() helper. Rule 4 privacy filtering is
  unchanged (still keyed on auth.user_id from the middleware).

scripts/joelclaw-machine-register.ts — NEW provisioning CLI
  Flow: load cached PDS session from ~/.joelclaw/pds-session.json →
  call com.atproto.server.createAppPassword → sha256(plaintext) →
  upsert machines_dev row → back up old auth.json → write new
  auth.json (0600) with the plaintext bearer. Ready to register Panda
  once the infra is back.

Verification status (pending Typesense reachability):
  [x] pds.ts compiles; curl-tested createAppPassword directly against
      PDS (returned valid password)
  [x] Bun fetch works against PDS directly (ruled out fetch-lib bugs)
  [x] All route handlers typecheck + biome clean
  [x] Auth middleware compiles
  [ ] End-to-end: register panda → POST /api/runs with new bearer →
      Run indexes → search returns it — BLOCKED on Colima SSH tunnel
      which is flapping (ssh 192.168.64.2:22 timing out; matches the
      2026-04-17 healer-suicide-loop memory note)

When infra comes back:
  TYPESENSE_API_KEY=<...> joelclaw-machine-register --name panda --user joel
  then POST /api/runs with the new bearer to confirm.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 20:12:04 -07:00
Joel Hooks 90e283ccdd feat(memory): GET /api/runs/forest + joelclaw-runs-tree — global pi-tree
Inspired by pi's /tree (badlogic/pi-mono). Pi's tree is per-turn within
one session; this one is per-Run across all sessions and all runtimes.
Every Run carries parent_run_id + root_run_id (Rule 3), so the forest
exists in the schema — we just didn't have a view.

apps/web/app/api/runs/forest/route.ts
  GET /api/runs/forest
    ?since=<epoch_ms>           default: 7d window
    ?runtime=pi,claude-code     comma-separated filter
    ?root_run_id=<id>           zoom into a subtree
    ?limit=<n>                  cap 1-250 (Typesense per_page max)

  Returns a flat list of Runs sorted by started_at + metadata for
  client-side tree assembly. Privacy-filtered by readable_by (Rule 4).

scripts/joelclaw-runs-tree.ts → ~/.bun/bin/joelclaw-runs-tree
  Terminal renderer with ANSI-colored runtime badges, box-drawing tree
  connectors, relative timestamps, turn counts, tag chips, intent
  clipped to 80 chars, clean sort order (started_at asc per bucket).

Orphan-root handling: a Run whose parent_run_id isn't in the result
set (trimmed by limit/since/runtime filters) becomes a visible root
rather than being dropped. Deterministic output.

Verified live:
  $ joelclaw-runs-tree --since=6h --limit=100
  3 Runs (3 roots) since 2026-04-19T20:09
  ├─ claude-code 22m ago 2t #phase1-v3
  │     third attempt with typesense key in env
  ├─ claude-code 21m ago 1t #smoke-e2e
  │     test
  └─ claude-code 18m ago 82t #smoke-e2e
        Explore the project at /Users/joel/Code/joelhooks/joelclaw…

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 19:09:44 -07:00
Joel Hooks 47188aebd2 feat(memory): Phase 1 slice — Run traversal endpoints
Adds the three GET traversal endpoints from ADR-0243 Rule 13 (D shape):

apps/web/app/api/runs/[id]/route.ts
  - GET: fetch the Run metadata row from runs_dev
  - Enforces Rule 4 at read time by checking readable_by.includes(caller)
  - 404 (not 403) on unauthorized — leaks zero information about existence

apps/web/app/api/runs/[id]/jsonl/route.ts
  - GET: stream the full jsonl transcript from NAS
  - Verifies readable_by on the Run row before streaming
  - 410 Gone if the blob is missing on disk (distinct from 404 "no Run")
  - Returns application/x-ndjson with Content-Length; zero buffering

apps/web/app/api/runs/[id]/descendants/route.ts
  - GET: walk a Run's subtree via root_run_id filter
  - Returns root + all descendants ordered by started_at ascending
  - Lets agents reconstruct a workload-rig DAG or a gateway session tree
    in one call

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 18:29:24 -07:00
Joel Hooks e269155fbe feat(memory): Phase 1 slice — POST /api/runs/search + E2E smoke script
Completes the Phase 1 ingest + retrieval loop per ADR-0243 build order step 7.

apps/web
  - new app/api/runs/search/route.ts — hybrid Typesense search with the
    full agent-first API surface (Rule 12 + 13):
      * Auto-applied privacy filters (user_id + readable_by) from the
        bearer token — NEVER from the request body (Rule 4)
      * Tag filters default to AND semantics
      * Modes: hybrid (default), semantic, keyword
      * Embeds the query at priority="query" so it preempts any pending
        ingest work in the Ollama queue (Rule 9a)
      * HATEOAS envelope with _links, next_actions, and timing breakdown
        (query_embed_ms, query_queued_ms, typesense_ms, total_ms)

scripts/memory-spike
  - new smoke-e2e.ts — full pipeline validation:
      1. POST /api/runs with a real claude-code jsonl fixture
      2. Poll Typesense for chunks matching the new run_id
      3. POST /api/runs/search filtered to the smoke-e2e tag
      4. Assert the ingested Run is findable
    Run against a live dev stack (Next.js on :3000, worker deployed,
    Typesense + Ollama + Inngest healthy).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 18:23:57 -07:00
Joel Hooks a02ece9702 feat(memory): Phase 1 slice — memory/run.captured + POST /api/runs
Adds the central ingest path for agent Run capture per ADR-0243 build order
steps 5 and 6:

packages/memory
  - src/schemas/runs.ts — Typesense `runs_dev` collection schema covering
    the full Run row from types.ts
  - src/nas.ts — blob writer honoring Rule 11 (user-partitioned paths);
    env var MEMORY_RUN_STORE defaults to ~/.joelclaw/runs-dev/ for local
    development, set to /nas/memory/runs in production

packages/system-bus
  - new memory/run.captured + memory/run.indexed event contracts
  - new functions/memory/run-captured.ts — receives event, loads jsonl
    from NAS (or inline payload), format-detects, chunks via per-turn
    chunker, embeds each chunk at ingest-realtime priority through
    @joelclaw/inference-router (so queries preempt), writes chunks to
    run_chunks_dev and Run row to runs_dev, emits OTEL + fanout indexed
    event; concurrency:4, retries:3; ensures collections exist idempotently
  - @joelclaw/memory added to dependencies

apps/web
  - new app/api/runs/route.ts — POST handler; dev bearer token auth
    (hardcoded allowlist, PDS flow lands Phase 3), writes jsonl to NAS
    via @joelclaw/memory writeRunBlob, fires memory/run.captured via
    HTTP to local Inngest, returns 202 HATEOAS envelope with run_id +
    _links + next_actions
  - @joelclaw/inference-router + @joelclaw/memory added to dependencies

Still v1 / dev scope:
  - Dev bearer token, not PDS App Password (Phase 3)
  - _dev collection suffix, will alias to run_chunks_current after cutover
  - No search endpoint yet (next slice)
  - No capture hooks yet (Phase 4)
  - No entity enrichment yet (Phase 6)

Typecheck clean, biome clean, 12 chunking tests still green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 18:21:35 -07:00
Joel Hooks aae6ab0c8c Degrade apps/web Convex static generation when env is missing, seed placeholder slug params for Cache Components, and no-op client Convex providers during prerender 2026-03-10 09:15:07 -07:00
Joel Hooks 26c7614233 Sanitize Convex env reads and keep dynamic slug helper routes out of static build collection so Vercel production builds pass 2026-03-09 13:36:49 -07:00
Joel Hooks bd191e83ad fix(web): handle missing CONVEX_URL in network page for Vercel builds 2026-03-09 07:09:06 -07:00
Joel Hooks 5d56dac230 Guard apps/web search top-hit access, route convex-content through canonical readers, and pass resourceId into the registry review FAB so apps/web/tsconfig compiles clean again 2026-03-08 20:49:22 -07:00
Joel Hooks 53207f60ca Add short ADR route aliases, resolve them to canonical Convex slugs, and redirect legacy full-slug ADR URLs 2026-03-07 21:55:25 -08:00
Joel Hooks d1f688d358 migrate AI job scheduling article from discovery to tutorial with redirect
- Created tutorial:ai-job-scheduling-macos-launchd
- Removed discovery:ai-job-scheduling-mac-local-first-video
- Added permanent redirect from /cool/ai-job-scheduling-mac-local-first-video to /ai-job-scheduling-macos-launchd
2026-03-07 14:09:43 -08:00
Joel Hooks 16ba67e7d3 Strip leading markdown H1 from post content in the article page renderer so published posts do not render a duplicate title below the page header 2026-03-07 13:08:53 -08:00
Joel Hooks 8a62b9b3ad Update /api/pi-mono and docs/web.md to replace the planned placeholder with real install steps for the public contributing-to-pi-mono skill and extension repo 2026-03-07 12:48:28 -08:00
Joel Hooks a9466b058e Implement pi-mono Restate corpus sync, expose public joelclaw.com discovery/search, and replace the bogus async /output hint with real workflow inspection 2026-03-07 11:48:41 -08:00
Joel Hooks 22ae72fab5 Move CLAWMAIL marker to head and add ASCII header 2026-03-03 13:47:16 -08:00
Joel Hooks 0e83fe6f31 web: move clawmail source comment to html root 2026-03-03 13:42:20 -08:00
Joel Hooks 919fd104ae web: place CLAWMAIL marker in head with strict content-type guidance 2026-03-03 13:37:54 -08:00
Joel Hooks 34a006f437 refine clawmail html source marker instructions 2026-03-03 13:33:15 -08:00
Joel Hooks 27d6b5d120 web: move CLAWMAIL marker to top html and add content-type checks 2026-03-03 13:32:16 -08:00
Joel Hooks ca0ce29784 web: add CLAWMAIL source comment for HTML pages 2026-03-03 13:16:36 -08:00
Joel Hooks fafdfda76f refactor: copy-for-your-claw now copies link to .md route with brief context, not full markdown 2026-03-01 15:07:26 -08:00
Joel Hooks f367e7996d style: right-justify copy-as-prompt button 2026-03-01 12:52:07 -08:00
Joel Hooks 01dd9267bf design: clean up article header — draft badge, inline copy button with tags, claw pink containment breach 2026-03-01 12:51:48 -08:00
Joel Hooks 8b10f62846 feat: copy-as-prompt button on articles — copies markdown + agent instructions to clipboard 2026-03-01 12:44:15 -08:00
Joel Hooks 48d41e64a6 fix: add @source for .ts files so tailwind-safelist.ts gets scanned 2026-03-01 12:10:00 -08:00
Joel Hooks c81249cd98 fix: add tailwind safelist for dynamic status-badge classes, revert to Tailwind classes 2026-03-01 12:05:14 -08:00
Joel Hooks 7d8c65cf17 fix: StatusPulseDot uses inline ping animation — Tailwind v4 missing animate-ping 2026-03-01 12:04:13 -08:00
Joel Hooks 4333138c2e style: add word wrap to code fences on joelclaw.com 2026-03-01 11:40:28 -08:00
Joel Hooks 8805397dbb fix: move notFound() outside 'use cache' boundary
notFound() is a runtime API that requires workUnitAsyncStorage context.
Calling it inside 'use cache' causes InvariantError during prerendering.

CachedArticleContent now returns null when post not found, and the
caller (StaticArticleShell) handles notFound() in request context.
Also uses cacheLife('minutes') so new articles appear without deploy.
2026-03-01 10:48:03 -08:00
Joel Hooks 4619e71e2d fix: use cacheLife('minutes') for getPost to enable deploy-free publishing
getPost() now caches for minutes instead of max, so new articles
appear within minutes of Convex upsert. Explicit tag revalidation
makes them appear instantly. 404 check moved outside cache boundary
in [slug]/page.tsx so stale null results don't block new content.
2026-03-01 10:43:57 -08:00
Joel Hooks a1d3d5bd36 fix: move 404 check outside cache boundary for deploy-free publishing
getPost() no longer uses 'use cache' — null results were cached
indefinitely, blocking new articles until a redeploy.

Now: getPost() fetches from Convex on every request (uncached).
CachedArticleContent receives post as prop, caches only MDX rendering.
New articles appear immediately after Convex upsert + tag revalidation.
2026-03-01 10:35:57 -08:00
Joel Hooks 8ff5642ade fix(adrs): default index sort to rubric priority 2026-03-01 07:51:42 -08:00
Joel Hooks 9a875710d8 feat(adrs): add priority band filter, sort controls, and band badges on index
ADR-0183 UX implementation:
- Status filter bar (existing, cleaned up)
- Priority band filter bar (do-now/next/de-risk/park/unscored)
- Sort controls: Priority (rubric rank), Newest, Oldest, Date
- Band badge + score pip on each ADR row
- Result count indicator
- Empty state for zero matches
- Rubric sort: band order → score desc → need desc → ADR number asc
2026-03-01 07:36:57 -08:00
Joel Hooks 94f210001e fix: upsertContent treats soft-deleted records as fresh inserts
Soft-deleted records (deletedAt set) were matching the hash guard
and returning 'skipped', preventing re-sync after removal.
Now skips hash guard for soft-deleted records so they get restored.
2026-03-01 07:34:17 -08:00
Joel Hooks 9ab8c6d7f6 ADR-0180 Phase 4: live streaming + agent watch CLI
- joelclaw agent watch <taskId|chainId> — NDJSON streaming watcher
- Redis pub/sub subscription to joelclaw:notify:gateway for real-time events
- Inngest API polling fallback when Redis is degraded or task completed before watch started
- Detects task (at-*) vs chain (ac-*) IDs, adjusts timeout accordingly
- Emits step/progress/result/error NDJSON stream events matching existing protocol
- Graceful degradation: Redis down → polling only, pre-completed → immediate result
- --timeout option (default: 300s tasks, 900s chains)
- SIGINT/SIGTERM cleanup
- HATEOAS next_actions in terminal events
- Import sorting from biome across capabilities/adapters (harmless)
2026-02-28 15:00:14 -08:00
Joel Hooks adcbfc9eb1 fix: wrap ConvexReaderProvider in Suspense for PPR compatibility
ConvexReactClient uses Math.random() internally, which breaks
Next.js static generation. Suspense boundary defers it as a
dynamic island.
2026-02-28 11:59:32 -08:00
Joel Hooks 80e71e5399 feat(ADR-0168): realtime content updates + ISR cache revalidation
- Add ContentLive client component: subscribes to Convex contentHash,
  triggers router.refresh() when content changes
- Add ConvexReaderProvider: lightweight read-only Convex provider (no auth)
- Wire ContentLive into ADR and post pages (outside cache boundary)
- Add getContentHash Convex query (lightweight hash-only subscription)
- Add revalidate-cache step to content-sync: purges Next.js cache tags
  after upserts so router.refresh() gets fresh server-rendered content
- Extract revalidateContentCache to shared lib (revalidate.ts)

Flow: Vault edit → content-sync → Convex upsert → revalidate cache tags
→ ContentLive detects hash change → router.refresh() → fresh MDX render
2026-02-28 11:53:45 -08:00
Joel Hooks 33888e908d sync: commit all pending changes 2026-02-28 11:51:21 -08:00
Joel Hooks 02c35a8022 fix: escape MDX angle brackets at render time for ADR pages
Adds escapeMdxAngleBrackets() utility that preserves code fences,
inline code, and valid HTML tags while escaping comparison operators
like < 0.5, >50%, <5MB that break MDX compilation.

Fixes 500 errors on ADR-0163, 0164, 0165, 0169 and any future ADRs
with raw angle brackets in Convex-sourced content.
2026-02-28 11:08:07 -08:00
Joel Hooks 5deb2eebc9 feat(content): migrate adr/discovery reads to convex 2026-02-28 10:36:57 -08:00
Joel Hooks ec33ac1235 web: harden article rendering + source diagnostics 2026-02-28 06:37:17 -08:00
Joel Hooks e0e1b0d257 delete: remove dogfooding discovery — exposes sensitive materials
Removed discovery note that referenced internal egghead infrastructure,
private Loom recording, and internal PR details.
2026-02-27 21:47:17 -08:00
Joel Hooks d8cd05f6aa fix: prevent horizontal overscroll on mobile article pages
- Add overflow-x-hidden to root layout wrapper
- Constrain pre/table/iframe/img/video to max-width:100% in prose
- Tables get block display + overflow-x-auto for internal scroll
2026-02-27 18:49:24 -08:00