docs(confidence-engine): resolve v0.60 investigation creation ownership
This commit is contained in:
+50
-6
@@ -201,20 +201,60 @@ Completed on branch `feature/multi-investigation-storage-v0.60`. Bounded archite
|
||||
| Mutable after creation | NO |
|
||||
| Route `[id]` represents same identity | YES — direct round-trip: app sets → route reads → storage uses |
|
||||
|
||||
### 3. Minimum contract operations
|
||||
### 3. Minimum contract operations — resolved in v0.60b
|
||||
|
||||
Five semantic operations. No speculative additions.
|
||||
Four semantic operations. `createEmptyInvestigation()` does **not** belong to the storage contract (resolved below).
|
||||
|
||||
| # | Operation | Input | Output | Product use |
|
||||
|---|---|---|---|---|
|
||||
| 1 | `listInvestigations()` | — | `InvestigationSummary[]` (lightweight) | Portfolio renders index/list |
|
||||
| 2 | `loadInvestigation(id)` | durable ID string | `Investigation \| null` | Investigation/Report page hydration |
|
||||
| 3 | `createEmptyInvestigation()` | — | `{ id: string }` | "+ Create new investigation" |
|
||||
| 4 | `saveInvestigation(id, investigation)` | ID + full snapshot | void | Autosave, Report generation, all writes |
|
||||
| 5 | `restartInvestigation(id)` | durable Investigation ID | void (reasoning state cleared) | "Restart this investigation" |
|
||||
| 3 | `saveInvestigation(investigation)` | full snapshot (with `id` already present) | void | Autosave, Report generation, all writes |
|
||||
| 4 | `restartInvestigation(id)` | durable Investigation ID | void (reasoning state cleared) | "Restart this investigation" |
|
||||
|
||||
**Explicitly rejected:** search, pagination, sorting, sync, merge, transactions, history, report versions, sharing, permissions, accounts. No existing product behaviour requires them.
|
||||
|
||||
---
|
||||
|
||||
### 3b. Investigation creation ownership — v0.60b resolution
|
||||
|
||||
**Contract correction decision.** The v0.60a contract contained a contradiction: Section 2 stated "ID allocation owner: Application" while Section 3 listed `createEmptyInvestigation()` as a storage operation producing `{ id: string }`. These are incompatible models (application-owned identity vs. storage-owned creation). They cannot both be true.
|
||||
|
||||
**Evidence from current product:**
|
||||
|
||||
1. **Create new behaviour today:** Portfolio's "+ Create new investigation" is a `<Link href="/investigations/case-1">` — a static navigation link with zero ID allocation and zero storage call. It navigates to the Investigation route which calls `loadInvestigation()`, receives null (no persisted data), and shows an empty ScenarioForm state. No empty Investigation is persisted on click.
|
||||
|
||||
2. **First meaningful persistence event:** `scenario-form.jsx` line 681 — after `submitScenarioForStartCase` returns successfully. The user has selected a scenario, submitted it, and the application receives a structured result. Only then does `saveInvestigation()` persist with real data (scenario + situationGraph + investigationRevision: 1). This is a domain-driven persistence boundary, not an identity-driven one.
|
||||
|
||||
3. **No product requirement for empty-persisted Investigations:** There is no current product behaviour that creates, displays, or expects empty persisted Investigations in Portfolio. The concept of persisting an abandoned start as a visible Portfolio item was asserted in v0.60a without product evidence.
|
||||
|
||||
4. **No product requirement for storage to allocate identity:** Current code uses hardcoded `INVESTIGATION_ID = "case-1"` in application code with no storage involvement in ID generation or allocation.
|
||||
|
||||
**Decision: MODEL A wins — application-owned identity, deferred persistence.**
|
||||
|
||||
- **ID allocation owner:** Application layer (not storage contract).
|
||||
- **When allocated:** On user click of "+ Create new investigation", before navigation.
|
||||
- **First persistence:** When the user produces meaningful Investigation state (scenario submitted) and `saveInvestigation(investigation)` is called — not on create-new click.
|
||||
- **Empty abandoned Investigation persisted:** NO. No durable Investigation exists until meaningful state triggers save.
|
||||
- **Storage allocates identity:** NO.
|
||||
|
||||
`createEmptyInvestigation()` does **not** belong in the storage contract. It never did — it was inconsistent with Section 2's "Application (not storage)" decision. The creation flow lives entirely at the application layer: app allocates durable ID → navigates to `/investigations/{id}` → user produces state → `saveInvestigation(investigation)` persists.
|
||||
|
||||
**Create new investigation flow (corrected):**
|
||||
|
||||
1. User clicks "+ Create new investigation" on Portfolio.
|
||||
2. Application allocates a durable immutable ID (library/algorithm deferred).
|
||||
3. Application navigates to `/investigations/{id}`.
|
||||
4. User fills out ScenarioForm → submits → first meaningful `saveInvestigation(investigation)` persists the Investigation under its already-known ID. The Investigation becomes listable in Portfolio at that point.
|
||||
|
||||
**Investigation properties (unchanged from v0.60a):**
|
||||
|
||||
- Carries durable id: YES
|
||||
- ID immutable: YES
|
||||
- ID exists before first persistence: YES
|
||||
|
||||
**Restart decision retained:** YES — identity preserved, reasoning/report state cleared.**
|
||||
|
||||
### 4. Portfolio listing returns lightweight metadata
|
||||
|
||||
`listInvestigations()` returns summaries, not full snapshots. Minimum information:
|
||||
@@ -234,7 +274,7 @@ A separate "Delete investigation" UI is not required by current MVP. Contract-le
|
||||
|
||||
### 6. Create new investigation flow
|
||||
|
||||
User clicks "+ Create new investigation" → app allocates durable ID (crypto.randomUUID or equivalent, library choice deferred) → persist an empty Investigation under that ID immediately → navigate to `/investigations/{id}` → existing Investigations untouched. Empty Investigation is persisted on creation so it appears in Portfolio; user can return later or abandon with container remaining visible.
|
||||
User clicks "+ Create new investigation" → application allocates durable immutable ID (library/algorithm deferred) → navigates to `/investigations/{id}` → Investigation page loads, `loadInvestigation(id)` returns null (no persisted state yet) → ScenarioForm renders in empty-start mode → user fills scenario and submits → first meaningful state is captured → `saveInvestigation(investigation)` persists the full snapshot under its already-known ID → Investigation becomes listable in Portfolio. No empty Investigation is persisted at creation time.
|
||||
|
||||
### 7. localStorage ≠ contract
|
||||
|
||||
@@ -244,6 +284,10 @@ The storage contract accepts/returns domain-level Investigation objects keyed by
|
||||
|
||||
Storage contract = persistence only. Not synchronization. No sync-specific fields (syncStatus, remoteId, dirtyFlags, lastSyncedAt) belong in the Investigation shape during this increment. Decisions here do not prevent future sync — the Investigation object carries its own durable ID sufficient for identity resolution.
|
||||
|
||||
## Next implementation boundary
|
||||
|
||||
Smallest next increment: implement the four-operation storage contract in `lib/storage/investigation-storage.js` as a re-export of a provider-backed interface whose signatures accept/return domain Investigation objects keyed by durable ID — without committing to any specific localStorage or database representation. This means defining the exported function signatures and the Investigation shape that flows through them, while deferring key scheme, row schema, and collection structure to a later implementation decision.
|
||||
|
||||
## Next restart point
|
||||
|
||||
Consult `docs/design-evolution/README.md` for progressive loading of product reasoning and provenance chronology; load the relevant chapter only when a specific historical question requires it.
|
||||
|
||||
Reference in New Issue
Block a user