Context & Investigations
Investigations View
A unified "Digs | Loops" bottom sheet so Alex can see in-flight, finished, and failed investigations — with reports rendered natively and pushes that deep-link straight to the answer.
Situation
Step 3 of the materialised-context architecture. Step 2 built the investigate data + notification plumbing but explicitly left the UI out of scope; this spec supersedes that placeholder. Goal: give Alex a place to see investigations pending/running, finished (distilled answer + full cited report, rendered natively), and failed — and make the investigation_complete push deep-link to the relevant dig instead of opening /.
Resolved in brainstorming: a unified bottom sheet ("Digs | Loops" segments) opened from a persistent tray handle under the chat input, replacing the existing standalone Loops sheet rather than adding a second surface. Swipe-pane, tab-bar, and route-based variants were all considered and rejected as un-unifying.
Key decisions
Unified tray + sheet
One tray handle shows live counts ("Digs 1 · Loops 3"); opening it reveals a segmented "Digs | Loops" sheet, and the old standalone Loops sheet migrates in wholesale.
D1 as list source
A new SSO-gated read endpoint over the investigations table — deriving from messages type='investigation' was rejected because it can't represent pending/running/failed.
Event-driven freshness only
Fetch on app load, sheet open, window focus, and push-triggered service-worker refresh — deliberately no polling.
Reports render natively
The runtime includes the full report body in its result callback; the Worker stores it in D1 (capped, truncated) as a frozen snapshot, while the vault note remains canonical via an obsidian:// link.
"Intel" naming (UI review amendment)
The segment reads "Intel" in all UI copy while internal identifiers keep digs; a --gold token marks the report/artefact affordance.
Retry (UI review amendment)
Failed digs get a "Retry →" backed by a new SSO-gated POST /investigations/:id/retry, rejecting the runtime CN.
Architecture
An additive migration adds the report column; two new SSO-only endpoints expose the list and detail; the PWA gets a tray/sheet component pair and a sanitized markdown viewer.
ALTER TABLE investigations ADD COLUMN report TEXT — capped at 100KB, truncated server-side with a trailing marker.session.svelte.ts) driving the event-driven refresh triggers.marked + sanitized with DOMPurify (reports quote untrusted web content), with an "Open in Obsidian" link./?dig=<correlationId> and posts a message to open clients for immediate refresh.Risks & guardrails
Untrusted report content
Sanitization (DOMPurify) is treated as non-negotiable since reports quote fetched web content verbatim.
Backwards compatibility
Pre-007 digs without a stored report fall back to answer + vault path + the obsidian:// link.
Least privilege both directions
Claim/result endpoints keep rejecting SSO tokens; the new read/retry endpoints keep rejecting the runtime CN.
Retention
Investigations are never deleted; the sheet lists the most recent 50, and reports remain in the vault permanently.
Delivery
Migration + db helpers
report column, capped list query.
SSO-only read endpoints
GET /investigations (list) and /investigations/:id (single, with report).
Result callback + runtime
Store the capped report; investigate.sh reads and includes it after a successful dig.
Web API client + state module
loadInvestigations()/loadInvestigation(id); investigations.svelte.ts.
TraySheet component — UI review gate
Built to a checkpoint requiring Alex's sign-off before proceeding (produced the naming/gold-token/retry amendments).
Push deep-link + report viewer + final PR
Service-worker deep-link wiring, sanitized viewer, then full verification.
Out of scope
docs/superpowers/specs/2026-06-10-mallory-investigations-view-design.mdplan ·
docs/superpowers/plans/2026-06-10-mallory-investigations-view.md