DELETE /api/chats/[id]?mode=preserve-graph|delete-nodes (mode required):
- preserve-graph: only extraction bookkeeping dies (coverage rows,
legacy checkpoints) — graph ledgers byte-untouched
- delete-nodes: the chat's ledger traces die. Node survival = surviving
provenance point / mapping line (notes imports carry empty
provenance) / surviving event. The creator's aliases + provenance are
stripped from a surviving node's create line (owner ruling
2026-09-23: name/statement/createdAt are the shared identity);
orphan nodes, their edges, events, mappings, positions, embedding
vectors and cluster members die; cluster labels stay (immutable)
Gates: 404 unknown, 409 turn_in_flight / jobs_running (with jobIds),
503 extraction_winding_down after a 10s abort-and-poll; the purge is
one sync block (ledgers → jobs → chat record last). rewriteLedgers is
atomic (tmp + rename). graphRevision (max mtime over graph data files)
rides the activity feed + the response as the change stamp.
Anti-resurrection: the chat pipeline re-checks chat liveness at each
merge — a run that slips past the wind-down aborts with nothing merged
or checkpointed; its partial spend dies with the chat.
Fix wave (2026-09-20), two owner-approved fixes:
Fix 1 — the searchAvailable gate was dead code on the live wiring path:
createTeacherTools RECEIVED ctx.searchAvailable but dropped it when
building the inner JobToolContext, so jobTools' research gate always took
its ?? true default (jobTools-direct tests passed; the production path
never saw the signal). Threaded through — the gate now reads the live
server signal on the teacher registry too.
Fix 2 — the prober registry gains the research + pdf dispatch tools
(owner ruling, 2026-09-20): these two are phase-omnipresent — the prober
can verify facts or read learner PDFs while assessing. demo/visual stay
teach-phase-only (they need the lesson's plan context). Same gating as
the teacher side: toggle-filtered, research subject to the search gate
(honestly absent when no engine is live — the prober's dispatch closes
over the SAME jobTools builder, one gate, one semantics). probe_done
stays always-on-by-construction; the learner-fs set untouched.
- ProberToolContext (extends ProbeDoneContext with the job wiring);
createProberAgent takes it; chat-handler injects jobStore + spawn +
searchAvailable into the prober ctx.
- TeacherToolContext.spawn's stale input union fixed (PdfInput was
missing from the TYPE — runtime already passed it).
- Tests: the threaded-gate describe in web-search.test.ts pins the LIVE
factory path both ways (teacher + prober); question-tools' registry
key-sets gain research/pdf; the prober toggle test pins the
research:false path.
Gates: typecheck, 807/807 tests, lint, build — all green.
Chunk A of the search port wave (2026-09-20), per master's assignment:
- web.ts: the Google Custom Search backend is gone — web_search now runs
on the core search port (SearchProvider), injected as a dependency
(import law: the tool never imports server code). createWebTools(search)
resolves the LIVE provider fresh per call; null = the honest
unavailability string (no silent fallback, owner decision). web_fetch
unchanged (needs no engine).
- runner: JobRunnerDeps.searchProvider — the server composition threaded
into the researcher; jobs already in flight when a key disappears still
fail honestly through the same seam, never crash.
- jobTools: the research dispatch tool DROPS from the teacher's registry
when ctx.searchAvailable() is false (owner decision: registry-level
drop — the model never sees the tool, no doomed dispatch, no error
card). Absent seam = available (tests). chat-handler injects
server/searchAvailable + searchProvider.
- tests: stubFetch (the Google wire stub) retired; jobs.test.ts threads
a recording stub provider through all four research runners and asserts
the scripted query hit the port; new web-search.test.ts covers the
factory contract (null → honest string, hits, error strings, fresh
resolution, web_fetch independence) + the registry drop matrix.
Gates: typecheck, 793/793 tests, lint, build — all green.