MalloryEstate Briefing
M·009Approved
← All briefings

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.

Dated 2026-06-10Programme Context & Investigations

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.

migrations/006_investigations.sql
investigations table: id (correlation), conversation_id, question, status, timestamps, answer, vault_path, error.
POST /investigations/claim
Atomically picks the oldest pending row, marks it running; reclaims stale running rows (~30 min) for stuck-job recovery.
POST /investigations/result
Threads the answer into the D1 conversation via messages.metadata, sets terminal status, fires a typed push; idempotent on unknown/terminal id.
runInvestigationExtraction
New module mirroring threads.ts — a best-effort Haiku pass in ctx.waitUntil deciding enqueue + a self-contained research question.
jobs/investigate.sh (mallory-runtime)
Cron */1, claims a job, runs the deep-research skill via Opus (timeout ~1500s), writes a cited report to the vault, POSTs the result back.
webpush.ts
PushMessage gains optional type/data so investigation_complete can carry a correlation id without touching existing callers.
persona.ts
Scoped update: Mallory can now truthfully say he can commission a background investigation.

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

The PWA investigations view (data source only — UI is step 3)`proactive_nudge` and other outbound push types beyond `investigation_complete`Capability self-awareness / manifest-generated capability copy (carried from step 1 §7)
spec · docs/superpowers/specs/2026-06-10-mallory-investigate-route-design.md
plan · docs/superpowers/plans/2026-06-10-mallory-investigate-route.md
Mallory · Estate briefing