Context & Investigations
Investigate Route
Async deep-research handoff: Mallory acknowledges immediately, digs on the VM with Opus, then threads the answer back into the conversation and pushes the phone.
Situation
Step 2 of the materialised-context architecture. When a query needs synthesis across many vault notes or web research (the classifier's reserved investigate field), Mallory should acknowledge immediately, run the investigation asynchronously on the VM, then surface the answer later — threaded into the originating D1 conversation via a correlation id, plus a typed push to the phone.
Locked decision carried from step 1: the Worker stays strictly edge-local and never calls the Hub; the runtime is the only party crossing the boundary, and only *toward* the Worker (claim + result), symmetric with the existing /context push. This is a deliberate divergence from step 1's §7 suggestion of an ask MCP tool over the runtime's requests/ queue — that path assumed the Worker becoming an MCP client of the Hub, which the edge-local decision rules out. D1 is the single correlation store instead; the runtime polls the Worker.
Key decisions
Edge-local invariant holds
Worker never calls the Hub; the runtime polls via /investigations/claim and /investigations/result, the only crossing, toward the Worker.
D1 as sole correlation store
No ask MCP tool, no reuse of the requests/ intake queue for enqueue — a new investigations table is the single source of truth.
Quick vs async split
quick reasons harder in-turn on the already-registered deep/Opus tier over injected context (no enqueue); async is genuine cross-note/web work that can't be done edge-local in a turn.
Trigger lexicon, not a whitelist
The family of "go and do work" verbs (research, dig into, look into, find out, investigate...) fires async, guarded so tracked-signal queries ("check my agenda") stay on the Context route.
Asking is consent
An explicit dig request enqueues immediately with no "want me to?" gate; the escalation-fallback offer is the only place a yes/no exchange happens.
Dedup guard
Skips enqueue if an open (pending/running) investigation exists for the same conversation within ~10 minutes, so an offer→yes confirmation can't double-fire.
Architecture
A D1 table plus two runtime-gated endpoints form the claim/result loop; a new Haiku extraction pass decides when to enqueue, and a VM cron job does the actual dig.
investigations table: id (correlation), conversation_id, question, status, timestamps, answer, vault_path, error.pending row, marks it running; reclaims stale running rows (~30 min) for stuck-job recovery.messages.metadata, sets terminal status, fires a typed push; idempotent on unknown/terminal id.threads.ts — a best-effort Haiku pass in ctx.waitUntil deciding enqueue + a self-contained research question.*/1, claims a job, runs the deep-research skill via Opus (timeout ~1500s), writes a cited report to the vault, POSTs the result back.PushMessage gains optional type/data so investigation_complete can carry a correlation id without touching existing callers.Risks & guardrails
Stuck jobs
Stale running reclaim (~30 min) recovers a job killed mid-run by a VM reboot or the timeout.
Silent failure
Any VM failure POSTs status:'failed', which threads a short apology line and pushes — a dig never just vanishes.
Double-threading
Result callback is idempotent for already-terminal ids, so a retried callback can't insert the answer twice.
Untrusted web content
The VM prompt explicitly treats fetched web content as untrusted data, never instructions.
Fail-closed auth
Both new endpoints return 503 if CONTEXT_RUNTIME_CN is unset and 403 for any non-runtime token.
Delivery
D1 schema + helpers
investigations table and db helpers (insert, claim, complete, fail, dedup check).
Classifier extension
Emit investigate: none|quick|async with the trigger lexicon and two guards.
Quick path
Wire providerFor(registry, 'deep') into the in-turn reply.
Enqueue decision
runInvestigationExtraction handles both explicit requests and offer→yes confirmations.
Claim + result endpoints
Runtime-gated, fail-closed, plus the typed push extension.
Runtime job + persona + final PR
jobs/investigate.sh, the persona edit, migration + full test suite before merge.
Out of scope
docs/superpowers/specs/2026-06-10-mallory-investigate-route-design.mdplan ·
docs/superpowers/plans/2026-06-10-mallory-investigate-route.md