feat(confidence-engine): use server investigation persistence

This commit is contained in:
2026-09-08 19:21:05 +01:00
parent 6dd447e56a
commit d6df1d210e
15 changed files with 425 additions and 114 deletions
+27 -40
View File
@@ -10,12 +10,20 @@ Initial-decomposition hardening is frozen for the current MVP stage.
## Authenticated product boundary (v0.62a)
- Confidence Engine uses self-hosted Supabase Auth with magic-link email, `/auth/callback` code exchange, cookie-backed sessions, and protected product routes/API requests; unauthenticated API requests receive 401.
- Investigation persistence remains wholly localStorage-backed and independent of authentication. No `confidence_engine` database schema, tables, snapshot ownership fields, Supabase server configuration, or PostgREST configuration were changed; server persistence remains future work.
- Investigation persistence is now server-authoritative via Supabase `confidence_engine.investigations`. Browser persistence flows through authenticated Next.js API. No `confidence_engine` database schema, tables, snapshot ownership fields, or PostgREST configuration were changed in v0.62c (established in v0.62b).
## Database foundation (v0.62b)
## Database foundation (v0.62c)
- The version-controlled `confidence_engine.investigations` migration is live and PostgREST exposure was configured externally. Live SQL proved RLS rejects one authenticated user inserting a row owned by another.
- Authenticated server save/load/list capability now uses the RLS-scoped `confidence_engine` schema with server-derived identity. Production CE persistence remains localStorage-backed: no migration or cutover has occurred.
- Server-authoritative investigation persistence via Supabase `confidence_engine.investigations` as durable authority; browser persistence flows through authenticated Next.js API (`/api/investigations`).
- `lib/storage/providers/server-http.js` replaces localStorage as the backing provider for `lib/storage/investigation-storage.js`. The storage seam now owns async load/save and per-investigation coalescing autosave (rapid concurrent saves collapse to the latest snapshot).
- Async hydration adapted across Portfolio, Investigation, Report, and ScenarioForm.
- Restart preserved via shared transformation in `lib/storage/restart-investigation.js`; server-backed restart endpoint reuses this same transformation.
- Portfolio-compatible server summary projection (`scenario`, `updatedAt`, `investigationRevision`, `reportExists`, `reportGeneratedFromRevision`).
- Missing-new-investigation 404 maps to `null` at the load boundary in `server-http.js`.
- Live save and Portfolio reload persistence proven by manual evidence on Sep 8.
- Live application-level user isolation proven: second authenticated user sees clean Portfolio; original user regains only their server-backed investigation.
- localStorage is no longer production authority. Legacy localStorage investigations remain physically present but invisible to normal product flow. No dual-write. No automatic legacy import.
- Duplicate investigation GETs observed on development reload; one database row and one Portfolio card confirmed. No data-integrity defect established. No optimisation undertaken.
**Current product checkpoint:** Read `docs/confidence-engine-product-checkpoint-2026-09-08.md` before planning new product, live-evidence, or commercial work. The core investigation loop is now sufficiently established to prioritise realistic end-to-end use, report experience, prospective-user value, repeat use, and willingness to pay—not endless isolated reasoning-mechanics experiments. Preserve user ownership and address trust-critical defects when found.
@@ -55,37 +63,16 @@ Does the complete investigation process leave real people materially clearer abo
## Repository checkpoint
- **Branch:** `feature/initial-decomposition-v0.61`
- **HEAD:** `5878ce4` — experiment(confidence-engine): add reconstruction-only helper flag
- **Working tree:** clean after this session's commit
- **Branch:** `feature/product-platform-foundation-v0.62`
- **HEAD:** `6dd447e` — feat(confidence-engine): add authenticated investigation persistence
- **Working tree:** dirty with completed v0.62c cutover (server-authoritative persistence, async seam, 404→null correction)
## Initial reconstruction — current status
## Persistence
**Semantically stable enough for current MVP stage.** Exact graph topology is not stable and is not treated as an invariant. Trust-critical meaning must remain stable. Some compression is acceptable when meaning survives downstream. Missing meaning cannot be faithfully recovered downstream. Causal hypotheses must remain visibly provisional.
Current production default: `reconstruct-v0.5` prompt + canonical reconstruction schema + Zod validation via `z.toJSONSchema()`.
The `/api/cases/start` route returns validated initial reconstruction, situation graph, and selected question. Observability seam exposes the exact object used by `buildInitialGraph()` for comparison.
**Frozen:** initial decomposition, prompt refinement, Qwen/Terra comparison — see CURRENT MVP DIRECTION above.
## Focused investigation — current status
Focused deconstruction plumbing fixes are complete:
- Schema mismatch resolved (focused route now supplies its own `focusedDeconstructJsonSchema`)
- Provider envelope no longer leaks into validator (inner `.response` unwrapped correctly)
- All 48 focused-investigation-boundary tests pass on first run
Focused deconstruction receives only:
- `centralStatement`
- `targetLabel`
- `targetDescription`
- `question`
- `answer`
Full SituationGraph / original scenario / previous findings are **not** supplied to that route. This is intentional epistemic separation.
Repeatability: supplier/weekend-shift epistemic separation repeated 3/3 on the fixed case after plumbing fix. Previous pre-fix semantic runs remain invalid (contaminated by provider-envelope misuse + wrong transport schema).
- **Owner:** `lib/storage/providers/server-http.js` (authenticated browser HTTP provider). `lib/storage/investigation-storage.js` owns the application-facing boundary with coalescing autosave and async load/save.
- **Durable authority:** Supabase `confidence_engine.investigations` (RLS-scoped, user-owned).
- **localStorage:** legacy only — physically present but invisible to normal product flow. No dual-write. No automatic import.
- **Restart transformation:** shared in `lib/storage/restart-investigation.js`; used by both browser seam and server persistence layer.
## Canonical experiment apparatus — currently valid
@@ -111,7 +98,7 @@ Three distinct routes:
/investigations/{id}/report → Investigation Report (derived summary)
```
**Portfolio:** investigation collection with actions per card (View report, Continue investigation, Restart). "+ Create new investigation" allocates durable ID via `crypto.randomUUID()` + navigates.
**Portfolio:** investigation collection loaded from server API (`/api/investigations`). Actions per card: View report, Continue investigation, Restart. "+ Create new investigation" allocates durable ID via `crypto.randomUUID()` + navigates.
**Investigation:** `ScenarioForm` + `ReasoningWorkspace`. Handles focused turns, Done/Re-open semantics, Current Understanding synthesis.
@@ -132,12 +119,12 @@ RAW USER EVIDENCE
## Persistence
- **Owner:** `lib/storage/providers/local-storage.js` (`saveInvestigation` / `loadInvestigation`)
- **Key prefix:** `confidence-engine-investigation:<durable-id>`
- **Storage contract:** `lib/storage/investigation-storage.js` (application-facing boundary)
- **Identity:** durable `id` allocated by application, not storage
- **First persistence:** when user produces meaningful state (scenario submitted), not on create-click
- **Restart:** preserves container/id/scenario; clears reasoning/report state
- **Owner:** `lib/storage/providers/server-http.js` (authenticated browser HTTP provider). `lib/storage/investigation-storage.js` owns the application-facing seam with coalescing autosave.
- **Durable authority:** Supabase `confidence_engine.investigations` (RLS-scoped, user-owned).
- **localStorage:** legacy only — physically present but invisible to normal product flow. No dual-write. No automatic import.
- **Identity:** durable `id` allocated by application, not storage.
- **First persistence:** when user produces meaningful state (scenario submitted), not on create-click.
- **Restart:** preserves container/id/scenario; clears reasoning/report state via shared transformation in `lib/storage/restart-investigation.js`.
## MVP boundaries
+6 -6
View File
@@ -34,9 +34,9 @@ The product direction is a **facilitated investigation** presented across three
**Report:** Renders persisted `investigationReport` snapshot. Generation is on-demand (exactly one `/api/cases/overview` call on first visit; zero on subsequent visits). The Report is a derived artefact, not canonical reasoning evidence.
**Authentication boundary:** Supabase Auth magic links gate product and CE API routes. Sessions are cookie-backed and `/auth/callback` exchanges the auth code before returning to `/`. This does not alter localStorage investigation persistence or introduce user ownership into CE snapshots; dedicated `confidence_engine` PostgreSQL persistence remains future work.
**Authentication boundary:** Supabase Auth magic links gate product and CE API routes. Sessions are cookie-backed and `/auth/callback` exchanges the auth code before returning to `/`. Server-authoritative investigation persistence via authenticated browser HTTP provider; localStorage is legacy only.
**Database contract (v0.62b):** The applied `confidence_engine.investigations` schema sits outside `public`. Its platform metadata is `id`, `user_id`, and timestamps; the CE payload remains an opaque JSONB `snapshot`. Authenticated RLS ownership is `user_id = auth.uid()`, and external PostgREST configuration exposes the schema. Server save/load/list capability is available through the authenticated/RLS path, but localStorage remains the production persistence authority; controlled cutover and legacy migration are future work.
**Database contract (v0.62c):** The applied `confidence_engine.investigations` schema sits outside `public`. Its platform metadata is `id`, `user_id`, and timestamps; the CE payload remains an opaque JSONB `snapshot`. Authenticated RLS ownership is `user_id = auth.uid()`, and external PostgREST configuration exposes the schema. Server persistence is now the production authority; localStorage is legacy only. No dual-write. No automatic legacy import.
The user controls which question to investigate, how deeply to investigate it, when to say Done for now, whether Current Understanding is sufficient, whether to reopen work, and when to review the Report. The engine facilitates — it does not steer or prioritise.
@@ -79,11 +79,11 @@ Three distinct routes, each with clear ownership:
### Persistence and report lifecycle
- Multi-Investigation collection via localStorage (key prefix `confidence-engine-investigation:<durable-id>`). Legacy singleton path retained for backward compatibility (unused by current product).
- `saveInvestigation()` / `loadInvestigation()` are the canonical storage seams.
- `listInvestigations()` returns lightweight summaries for Portfolio rendering.
- **Server-authoritative:** Supabase `confidence_engine.investigations` via authenticated browser HTTP provider (`lib/storage/providers/server-http.js`). localStorage is legacy only — invisible to normal product flow. No dual-write. No automatic legacy import.
- `saveInvestigation()` / `loadInvestigation()` are the canonical storage seams, backed by server-HTTP provider with coalescing autosave in `lib/storage/investigation-storage.js`.
- `listInvestigations()` returns lightweight summaries for Portfolio rendering from the server API.
- Report generation: first visit → one synthesis call + persist; subsequent visits → zero calls, renders persisted snapshot.
- Restart is destructive and confirmation-gated (dialog → explicit second confirmation`clearInvestigation()`).
- Restart is destructive and confirmation-gated (dialog → explicit second confirmation); uses shared transformation in `lib/storage/restart-investigation.js`.
### Reasoning-engine vs UX/product version lineage