Files
confidence-engine/docs/current-handoff.md
T

39 KiB
Raw Blame History

Confidence Engine — Current Handoff

Repository Position

  • Branch: feature/current-understanding-reconstruction-v0.50
  • Trusted checkpoint: 0624bc2 fix(confidence-engine): gate focused completion during processing
  • Working tree: clean

Current Reasoning Loop

understand situation
→ identify uncertainty
→ focused investigation
→ capture/deconstruct evidence
→ canonical Findings
→ complete focused episode
→ reconsider authoritative SituationGraph
→ regenerate Current Understanding
→ expose what matters next

Done for now is the semantic completion boundary of the focused investigation. The processing gate at 0624bc2 prevents completion while the latest focused deconstruction is still processing. Done does NOT mean the parent decision is closed — only that no further immediate evidence capture is warranted.

Current Canonical State

Evidence is sufficient

No new evidence-capture mechanism is required. A minimum lossless completed-episode state consists of:

SituationGraph
+ target-scoped ordered Contributions
+ associated canonical Findings

Contributions preserve the focused interaction including: question/context, verbatim user answer, model-derived observations, uncertainties, assumptions, relationships, follow-up questions, target/provenance, sequence/order.

Findings preserve: current canonical proposition, sourceObservation, contributionId, originatingTargetNodeId, userDisposition.

Evidence distinctions (must be preserved by future reasoning)

RAW USER EVIDENCE
≠ MODEL-DERIVED CONTRIBUTION SEMANTICS
≠ CURRENT CANONICAL FINDING
≠ IMMUTABLE SOURCE OBSERVATION
≠ USER DISPOSITION / AUTHORITY
≠ TURN CONTEXT / PROVENANCE

Finding semantics

Disposition Meaning
null Eligible working premise — NOT explicit endorsement
agree Explicitly user-endorsed canonical proposition
not_relevant Excluded from eligible current reasoning; provenance retained
corrected Finding Corrected proposition becomes current canonical proposition; sourceObservation immutable; disposition resets to null

Critical: Finding.userDisposition does NOT establish parent-decision closure authority. User disposition is evidence for global reasoning — it never directly mutates authoritative graph state.

Completed Focused Episode

A focused episode is complete when:

  1. Deconstruction LLM returns and Contribution(s) are stored
  2. Processing gate is satisfied (no in-flight deconstruction)
  3. Done for now has been emitted as the completion boundary

The processed state contains:

  • SituationGraph — authoritative graph (may reflect pre-investigation state at the moment of Done)
  • Ordered Contributions — target-scoped, preserving provenance and semantic fields
  • Canonical Findings — derived from contributions with user dispositions

Authoritative Graph Reconsideration Architecture (CURRENT)

This is the current redesign target for graph-update input.

The preferred architecture:

SituationGraph
+ deterministically structured completed episode
        ↓
ONE expanded graph-update reasoning operation
        ↓
GraphUpdateProposal
        ↓
independent deterministic safeguards/application
        ↓
updated authoritative SituationGraph

Not required / not justified:

  • Separate episode model call for internal reconciliation
  • Persistent EpisodeSemanticResult entity

The provider/model may reconcile episode meaning and graph implications in the same graph-reasoning operation.

Deterministic episode preparation (before reasoning)

Existing state should be classified so the model does not receive an undifferentiated evidence bag:

eligible canonical Findings
excluded not_relevant Findings
ordered raw Q/A
Contribution semantic fields
provenance/context
SituationGraph

This establishes classification and precedence. It does NOT itself perform cross-turn semantic synthesis.

GraphUpdateProposal

The existing typed GraphUpdateProposal remains the conceptual boundary between evidence reasoning and authoritative graph application. It is not inherently single-turn — it may carry multi-turn episode content. Do not introduce a replacement proposal type merely because the evidence is multi-turn.

Independent safeguards

Provider/model reasoning may: interpret evidence, reconcile episode meaning, reason about graph implications, propose graph changes.

It must NOT become sole authority for:

  • explicit user closure authority
  • evidence fidelity
  • comparability establishment
  • graph sufficiency
  • canonical Finding eligibility
  • canonical correction precedence

Explicit parent-decision closure authority must remain grounded independently in retained actual user evidence/context. Comparability may need episode-aware derivation because SituationGraph/reasoningState can still reflect the pre-focused-investigation state at Done.

Current Understanding

Current Understanding is a separate presentation concern, architecturally distinct from graph reasoning:

canonical graph + eligible/corrected Findings
        ↓
dedicated Current Understanding synthesis
        ↓
coherent user-facing explanation

A separate LLM call here remains appropriate because this operation serves presentation/coherence, not authoritative episode interpretation. Desired presentation direction: short, clear, scannable, plain language, minimal repetition. Do not redesign or tune the CU prompt now.

Done-for-now interaction (current bounded contract)

Done for now is user-owned and has immediate visible effect — the engine does not decide whether enough evidence has been gathered.

On clicking Done for now:

  1. Question parks immediately under "Questions we have clarified" with Clarified status + Re-open button
  2. Focused investigation workspace closes without waiting for async pipeline
  3. Current Understanding loading begins immediately — skeleton overlay appears while the async pipeline runs
  4. Skeleton spans: episode reconsideration → graph application → CU synthesis
  5. CU refresh completes the investigation checkpoint — new CU replaces skeleton when ready

The skeleton overlay uses strong paragraph-style bars with varied widths and a left→right shimmer, centred status message ("Clarifying your current understanding…"), and an opaque background that fully obscures old CU content until synthesis succeeds or fails.

Current Understanding refresh invariant

Reconstruct Current Understanding when canonical meaning or the eligible evidence set changes. Do not reconstruct it merely because investigation/question status changes.

Transition Canonical meaning/evidence changes? CU synthesis
Re-open clarified question No No
Not quite click before correction is saved No canonical corrected meaning yet No
Save corrected Finding proposition Yes Yes
Mark eligible Finding Not Relevant Yes Yes
Restore previously Not Relevant Finding Yes Yes
Complete focused episode / Done Yes Yes

Key distinction: Re-open = change what we are willing to question. Finding correction / Not Relevant / completed new evidence = change what we currently understand.

A Current Understanding may legitimately say that the evidence currently makes an explanation less likely while the corresponding question is Open again for further investigation. Do not prescribe special Re-open wording in generated Current Understanding prose, and do not add a requirement to mention "reopened", "previously resolved", or "reconsidered" inside generated Current Understanding. The Current Understanding should describe what the evidence currently supports, not narrate workflow status.

Closed Boundaries — Do Not Reopen

These are settled and must not be revisited in future work:

  • Finding canonical model and dispositions (null/agree/not_relevant/corrected)
  • focused Contribution → Finding derivation
  • Finding correction semantics
  • not_relevant / restore behaviour
  • async investigation persistence provider
  • focused investigation workspace lifecycle
  • Done processing gate (0624bc2)
  • dedicated Current Understanding synthesis seam
  • Finding-side CU synthesis triggers
  • GraphUpdateProposal as reasoning/application boundary
  • existing evidence sufficiency (no new capture mechanism needed)
  • no separate episode semantic model stage currently justified

Where deeper history is required, reference: docs/design-evolution-log.md, docs/archive/experiments/, docs/methodology-checkpoint-return-to-origin.md. Do not reproduce that history here.

Context / Execution Route

For normal Confidence Engine work, use the existing routing system:

  • docs/task-context-packs.md — task routing
  • docs/current-working-principles.md — durable methodology (axiomatic principles A1A12)
  • docs/Confidence_Engine_Return_to_Origin_Methodology_Context_2026-08-18.md — Return-to-Origin methodology
  • .claude/architecture-guardrails.md — architecture guardrails
  • .claude/working-rules.md — executor process rules

Historical material is read only when a specific unresolved fact requires it.

Immediate Next Implementation Target

Replace/adapt the old single-turn graph-update input boundary so authoritative reconsideration can consume the deterministically structured completed focused episode — target-scoped ordered Contributions + associated canonical Findings + SituationGraph — while reusing the existing graph-update reasoning and GraphUpdateProposal machinery and preserving independent safeguards.

Narrow first increment

The first implementation should be scoped to wiring the deterministic episode preparation (classification, ordering, provenance extraction) into the existing graph-update prompt builder. The output path already exists via GraphUpdateProposal. Do not:

  • redesign GraphUpdateProposal
  • add new LLM calls
  • introduce EpisodeSemanticResult
  • begin with RELOAD-B or cold-return CU freshness
  • tune Current Understanding presentation

History of old restart points removed as current authority:

case/update synthesis integration          → historical experiment (v0.50)
RELOAD-B / cold-return CU freshness      → historical checkpoint
60B experiments                            → apparatus-contaminated evidence
v0.48v0.49 persistence/recovery work     → completed closed boundaries
Contributions→Findings domain discovery    → captured in canonical state above
Finding↔SituationGraph reasoning contract  → captured in authoritative graph section

These architectural decisions are preserved as settled current state, not as historical narrative. Detailed experiment evidence remains in docs/archive/experiments/.

Open Questions filtering surfaces (durable production fact)

Two separate surfaces render Open Questions from the same graph data but use different filter rules:

  • Inline Open Questions (ReasoningWorkspace, line ~1841): filters by n.kind === "unknown" && !resolvedIds.has(n.id) — uses resolvedNodeIds only.
  • OpenQuestionsPanel (same file, line ~1193): filters by n.kind === "unknown" && !resolvedIds.has(n.id) && !doneForNowIds.includes(n.id) — uses both resolvedNodeIds and doneForNowIds.

Immediate Done behavior: Both surfaces remove the question simultaneously (React batches both onImmediateGraphChangesetResult and setDoneForNowIds in the same render cycle, so both see the updated state on the next paint).

Post-server-confirmation inconsistency (open): conditional structural risk. If the server returns a graph that does not include the node in resolvedNodeIds, OpenQuestionsPanel (pre-v0.51) still hides it (because doneForNowIds persisted), but the inline surface shows it as open again.


Zero Open Questions invitation — v0.51 correction (verified)

Live evidence that motivated this correction:

A real completed investigation reached zero Open Questions, clarified questions were visible, CU refreshed correctly, but the invitation was absent because its prerequisite was tied to transient doneForNowIds rather than canonical graph state.

Correction applied — eligibility: Invitation uses canonical resolved-node graph state (same derivation as inline "Questions we have clarified"), not local doneForNowIds:

  • openUnknowns.length === 0 AND clarifiedQuestions.length > 0 AND !cuSynthesisLoading
  • Clarification count = unknown nodes in resolvedNodeIds (same source as inline section)
  • Re-open behavior preserved (still uses setDoneForNowIds for local toggle)
  • "Review current understanding" reveals existing CU via cu-scroll-target without triggering synthesis or new LLM call
  • Invitation hidden while cuSynthesisLoading === true; appears after CU refresh completes
  • Zero Open Questions carries no readiness/completion judgement — it is a milestone invitation, not a decision

Exact working copy: "You've now worked through all of the questions we surfaced. Would you like to see an overview of what we understand so far?"

Action: "Review current understanding" (scrolls to CU section)

Placement correction (v0.51 placement):

  • The milestone invitation occupies the same conceptual/spatial position previously occupied by Open Questions — between Current Understanding and Questions we have clarified.
  • When Open Questions still exist: Current Understanding → Open Questions → Questions we have clarified
  • When zero Open Questions: Current Understanding → Milestone invitation → Questions we have clarified
  • This is a ternary in the inline ReasoningWorkspace section: openUnknowns.length > 0 ? <OpenQuestionsUI> : milestoneAllowed ? <MilestoneInvitation> : null, followed by <ClarifiedQuestionsUI /> unconditionally.

Verification:

  • Rob manually verified the milestone renders correctly on persisted investigation at http://localhost:3000 (before placement correction)
  • This session corrected placement via Edit only; deterministic verification via targeted Vitest (145 tests) and build — deliberately did not repeat Playwright
  • All 145 tests pass; production build compiles successfully

Focused investigation presentation ownership — v0.52 correction (verified)

Live evidence that motivated this correction:

A fresh unanswered Question B displayed stale focused-investigation content from a previously answered Question A across every presentation surface:

  • Previously answered / Your response
  • What this tells us
  • Still unclear
  • Questions this raises
  • Assumptions
  • Connections

Root cause: FocusedQuestionBody in components/reasoning-workspace.jsx iterated over the scenario-wide focusedContributions array for all derivations (hasCompletedContext, latest completed contribution, and every effective-presentation fallback: observations, uncertainties, follow-ups, assumptions, relationships). No active-question scoping was applied.

Correction applied — scoped contribution presentation:

  • FocusedQuestionBody now derives a thread-local subset before any derivation:
    const threadContribs = (focusedContributions || []).filter(
      (c) => c.targetNodeId === nodeId || c.originatingTargetNodeId === nodeId,
    );
    
  • hasCompletedContext, latest completed contribution, and all effective presentation fallbacks use that scoped collection;
  • scenario-wide focusedContributions history is preserved in memory — only the presentation derivation is narrowed;
  • originatingTargetNodeId is also checked so follow-up contributions remain attributed to their originating question;
  • fresh Question B no longer inherits Question A's focused presentation content;
  • reopening or revisiting Question A still correctly uses Question A's own historical contribution content.

Verified:

  • targeted Vitest (tests/open-questions-vs-assumptions.test.jsx) — 3 new test cases (fresh B scoped to zero, active A retains its history, originatingTargetNodeId scoping) — pass
  • npm run build — compiles successfully
  • Rob manually verified the live UI on a persisted investigation: fresh unanswered Question B no longer shows stale focused-investigation content from Question B; confirmed across all six presentation surfaces listed above

Verification notes:

  • Claude Playwright was not used for final verification because the canonical dev server was unavailable at that point
  • Full Vitest suite was not re-run in this session (only targeted regression test)

v0.53 — Empty Done parked-state coherence + Re-open local cleanup

Problem: empty Done left node in status: "unknown" even though its ID was added to resolvedNodeIds and doneForNowIds, producing a non-coherent canonical parked state. Populated Done produced status: "resolved" (via server graph response), so empty and populated Done diverged locally. Additionally, the clarified-question Re-open handler called reopenResolvedUnknown(graph, node.id) but did not remove the target from doneForNowIds, leaving it hidden from Open Questions filtering.

Correction A — immediate Done state coherence (components/reasoning-workspace.jsx):

  • The onImmediateGraphChange callback (line ~2254) now also sets the target node's status: "resolved" alongside adding its ID to resolvedNodeIds
  • Empty Done and populated Done now share the same resolved state shape: { status: "resolved", resolvedNodeIds includes id, doneForNowIds includes id }
  • No server API call is added — empty Done still skips episode processing via the active-target content guard in scenario-form.jsx

Correction B — clarified-question Re-open local cleanup (components/reasoning-workspace.jsx):

  • The clarified-question Re-open button (line ~1982) now calls setDoneForNowIds(prev => prev.filter(id => id !== node.id)) after installing the reopened graph
  • The node visibly returns to Open Questions because both filtering sets (resolvedNodeIds and doneForNowIds) no longer contain the target ID

Deterministic verification:

  • Targeted Vitest (tests/empty-done-orchestration.test.jsx) — 23/23 PASS (original 14 gate tests + 9 new coherence/reopen/history tests)
  • npm run build — compiles successfully

Clean-run manual verification (authoritative for v0.53):

  • Rob performed a clean investigation on the current implementation
  • Open Questions surfaced correctly
  • Questions parked with "Done for now" required no answer/content
  • No visible no_episodic_content 400 on empty Done
  • Parked questions appeared under "Questions we have clarified"
  • Re-open successfully returned a question to Open Questions
  • Rob repeated and confirmed the clean-run Re-open behaviour

Legacy development persistence:

  • An older restored development investigation could not successfully Re-open a parked question
  • This investigation predates the current coherent Done/Re-open state model and may contain stale persisted development state (e.g. resolvedNodeIds contains node while node.status remains "unknown")
  • Do NOT record this as evidence that current v0.53 Re-open is broken — it reflects legacy development persistence, not a current product failure
  • During the current development phase, persisted investigations created under older experimental state shapes may be discarded after state-shape changes
  • No migration, hydration normalisation, compatibility repair, or legacy-state detection is required solely for these development cases
  • A genuine future product migration requirement should be treated as a separate explicitly-designed compatibility increment

Preservation guarantees:

  • Empty Done still skips completed-episode API call when active target has no episode content (active-target guard in scenario-form.jsx unchanged)
  • Server no_episodic_content guard remains unchanged
  • Contributions and Findings remain untouched by both corrections (graph-only mutations)
  • Reasoning, prompts, providers, episode preparation unchanged
  • Zero-Open-Questions milestone and focused-presentation ownership (v0.52) remain unchanged

v0.54 — Investigation overview synthesis apparatus established

Objective: Establish the smallest reusable seam for a later bounded live experiment answering whether one additional synthesis call can produce a more useful investigation overview than Current Understanding, while keeping established understanding and plausible interpretations epistemically separate.

Apparatus delivered (no UI integration, no live semantic experiment):

  • Domain function: synthesizeInvestigationOverview() in lib/graph/investigation-overview-synthesis.js

    • Accepts { situationGraph, findings, plausibleInterpretations }
    • Output contract: { understanding, plausibleInterpretations } — two structurally distinct string fields
    • Uses Zod-safeParse validation rejecting any recommendation/decision/confidenceScore/nextAction/priority/readiness leakage
    • Epistemic boundary rules: established evidence never promoted to interpretation; interpretations never promoted to understanding
  • Route: POST /api/cases/overview (thin route, parallel to existing /api/cases/synthesis)

  • Deterministic tests: tests/graph/investigation-overview-synthesis.test.js — 49 tests covering epistemic boundary integrity, evidence exclusion, interpretation separation, output contract, and full seam

Plausible interpretation ownership (discovered):

  • Canonical source: reconstruction.plausibleInterpretations in lib/reconstruction/schema.js
  • Schema: { id, description, supportingEvidenceIds[], assumptionsRequired[], confidence }
  • Written to graph as "assumption" nodes with status: "provisional" via lib/graph/builder.js
  • Presentation-derived (UI renders from reconstructed provisional assumption nodes); canonical state is the reconstruction payload

Existing CU synthesis unchanged: synthesizeCurrentUnderstanding(), buildGraphEvidenceProjection(), filterEligibleFindings() untouched.

v0.54 overview synthesis apparatus established; no live semantic experiment and no UI integration performed yet.

v0.54b — Bounded Investigation Overview UI integration (verified)

Objective: Answer whether a user-triggered Investigation Overview provides a useful semantic overview beyond existing Current Understanding, using inline transient rendering rather than scroll-to-CU.

Bounded cleanup applied:

  • Removed obsolete scrollIntoView({ behavior: "smooth" }) call from milestone button onClick handler (line ~1958 of reasoning-workspace.jsx). The old behaviour scrolled away from the button to cu-scroll-target after starting handleRequestOverview(). Since the overview renders inline below this button, that scroll defeated the UX — the user clicked and the viewport moved elsewhere. Only the two scrollIntoView lines were removed; handleRequestOverview() call retained unchanged.
  • Unnecessary prop plumbing check: all four props (overviewState, setOverviewState, overviewLoading, handleRequestOverview) are consumed in ReasoningWorkspace render path — no removal needed.

Deterministic verification:

  • Exact Vitest command: npx vitest run tests/ui/investigation-overview-ui.test.jsx
  • Result: 9/9 PASS
  • Build: npm run build — compiles successfully, zero errors

Live experiment (Playwright, single request on persisted investigation):

  • URL: http://localhost:3000
  • Persisted investigation reused: YES — the existing saved state with zero Open Questions, three clarified questions, Current Understanding, and Possible Interpretations was already present; no destructive setup.
  • Milestone visible: YES — "Review current understanding" button rendered under milestone invitation text.
  • Current Understanding visible before request: YES — "Premium product line sales fell by 25% last month coinciding with a competitor's lower-priced launch; the absolute count changed by 25%, but without knowing the denominator we cannot determine whether the rate per unit has worsened, stayed stable, or improved."
  • Clarified history preserved: YES — three clarified questions with Re-open buttons rendered in both pre and post states.
  • Overview requests made: exactly 1
  • Loading state observed: transitioned button text to "Generating overview…" and disabled the button during loading.
  • Overview rendered: YES — inline below the milestone invitation, two sections:
    • "What we understand": "Premium product line purchases decreased by 25% in the most recent month, diverging sharply from expected operational and historical performance metrics. This reduction in purchase volume coincided with a rival entity introducing a similar product at a lower price point. Internal observations confirm that while the marketing team has attributed the decline partly to competitor pricing pressure and requested investigation into internal funnel and seasonal factors, these internal and external variables remain unquantified relative to the total exposure of the sales drop. Additionally, the digital sales platform is established to track visitor traffic and checkout completion rates for the premium product category currently affected by reduced purchase volume."
    • "What remains plausible": "The provided inputs contain no plausible interpretations, as designated by the absence of content in the Section B section labeled '(none)'." (empty due to plausibleInterpretations being absent from this session's graph — not a UI defect)

Semantic comparison:

Existing Current Understanding: 1 paragraph summarizing the sales drop magnitude, timing, and unknown denominator.

Overview synthesis "What we understand": ~4 sentences providing operational context ("diverging sharply from expected metrics"), explicit competitive framing ("rival entity introducing similar product at lower price"), acknowledgment of marketing team's investigation request, unquantified internal/external variables relative to exposure, and digital platform tracking capability. This adds situational framing beyond the existing CU — it contextualizes the fact within operational expectations and explicitly names investigation gaps rather than merely restating them.

What remains plausible: The section header rendered but content was empty (none) because this session's graph had no plausible interpretations in Section B. This is a data gap, not a rendering defect. The three Possible Interpretations visible separately on the page (price sensitivity, traffic deterioration, seasonality) were from prior synthesis — they are NOT part of the overview response.

Classification: USEFUL DISTINCT OVERVIEW

The overview did not merely duplicate Current Understanding. It added operational framing ("diverging sharply from expected performance"), competitive context ("rival entity"), investigation gap explicitness ("variables remain unquantified relative to total exposure"), and infrastructure awareness ("digital sales platform is established to track..."). These are genuine investigative-level additions, not paraphrase. However, the "What remains plausible" section was empty (data gap), limiting the full semantic value of the two-section structure.

One-sentence judgement: The overview provides a genuinely distinct investigation-level synthesis with operational framing beyond Current Understanding; the empty plausible interpretations section was due to missing graph data in this session, not an implementation defect.

v0.55 — Route architecture: Portfolio / Investigation / Investigation Report separation (verified)

Objective: Make one bounded architectural change — can Portfolio, Investigation, and Investigation Report become three separate page/route concepts, with report presentation removed from ReasoningWorkspace? Zero live-model-call implementation increment.

Product decision established:

/                                          → Portfolio / notebook index
/investigations/:id                         → working Investigation (ScenarioForm + ReasoningWorkspace)
/investigations/:id/report                  → Investigation Report (persisted derived artefact)

Investigation Report
  → Back to investigation → /investigations/:id

Files created:

  • app/page.jsx — Portfolio page. Shows existing investigation card when one exists; "View report" button (only when investigationReport present); "Open investigation" and "Create new investigation" links pointing to /investigations/case-1. No multi-investigation management, search, or filters.
  • app/investigations/[id]/page.jsx — Investigation route. Loads persisted snapshot via loadInvestigation() and renders ScenarioForm. Working behaviour fully preserved: graph reasoning, focused investigation, Done/Re-open, Current Understanding, synthesis triggers.
  • app/investigations/[id]/report/page.jsx — Report page. Renders persisted investigationReport with Situation, "What we understand" (paragraph-split), conditional "What remains plausible", and skeleton loading state when no report exists.

Files edited:

  • components/reasoning-workspace.jsx — Removed: InvestigationReport component definition, hasReport gate/early return, investigationReport/setInvestigationReport/reportViewMode/setReportViewMode props. ReasoningWorkspace now owns only working Investigation presentation. Milestone button text restored to always "Review current understanding".
  • components/scenario-form.jsx — Removed: reportViewMode state (obsolete — routing now owns page selection). Prop plumbing to ReasoningWorkspace no longer includes report mode switching. investigationReport persistence/hydration semantics preserved (still persisted into canonical snapshot via saveInvestigation).
  • tests/ui/investigation-overview-ui.test.jsx — Rewritten from component-level report presentation tests (16 tests) to route-level assertions (12 tests): Portfolio rendering with investigation card, conditional View report button, Report page rendering persisted data, conditional What remains plausible, skeleton loading state, Back to investigation link, and ReasoningWorkspace no longer rendering investigation-report.

Verification:

  • Exact Vitest command: npx vitest run tests/ui/investigation-overview-ui.test.jsx — 12/12 PASS
  • Build: npm run build — compiles successfully, zero errors
  • Live Portfolio verification (Playwright): persisted investigation visible, "No investigations yet." absent, Open investigation link functional, Investigation page opens with full persisted state retained
  • No live model calls made (0)

Persistence boundary fix (v0.55):

  • app/page.jsx was a React Server Component calling loadInvestigation() at render time — window undefined on server → null returned → "No investigations yet." always displayed
  • Added 'use client' directive to app/page.jsx so Portfolio hydrates from localStorage client-side
  • Canonical loadInvestigation() remains the persistence owner; no new storage mechanism introduced
  • Investigation visibility does not depend on report existence (card always renders when investigation exists; "View report" is conditional)
  • Temporary route identity remains case-1; multi-investigation identity/storage remains future work

Route build output:

/                           → static
/investigations/[id]        → dynamic (server-rendered)
/investigations/[id]/report → static/dynamic

Limitations documented for later increments:

  • Portfolio currently supports only the one canonical persisted investigation (confidence-engine-investigation localStorage key).
  • "Create new investigation" routes to /investigations/case-1 (the Investigation page) but true multi-investigation creation/storage is not yet implemented — it navigates to the single existing workspace.
  • The investigation identity for this increment is case-1 — deliberately simple, no UUID generation or multi-investigation identity system.
  • Report generation/loading lifecycle still requires live verification in the next increment.
  • Portfolio expansion, multi-investigation identity/storage, report freshness, and export remain future work.

Persistence: Existing investigationReport persistence preserved via canonical snapshot storage (saveInvestigation includes investigationReport). Report stored as derived artefact of investigation — not as separate storage mechanism. No new report localStorage introduced.

Open defects

  • Empty Done no_episodic_content: choosing Done without episodic content can produce { success: false, stage: "preparation", error: "no_episodic_content" } — separate future increment (empty-Done orchestration guard now prevents the 400 in practice by skipping episode processing entirely)

v0.55 — Live report hydration verification (verified 2026-09-03)

Objective: Answer whether View report opens the persisted Investigation Report rather than the pending placeholder after Portfolio client hydration.

Playwright result: PASS

  • Portfolio page loaded at http://localhost:3000/; hydration waited via semantic control page.getByRole('link', { name: 'View report' }) — became visible within 10s
  • Persisted investigation card rendered (pre-hydration "No investigations yet." is expected transient state, not evidence of missing storage)
  • Clicked View report → URL navigated to /investigations/case-1/report
  • Report page: "Investigation Report" heading — present
  • Report page: "What we understood" heading — present
  • Persisted understanding content rendered (non-placeholder, substantive findings about 25% premium product sales decline, competitive pricing pressure, unquantified variables)
  • Does NOT show placeholder text "Report generation pending. A summary will appear here once the investigation reaches milestone."
  • "Back to investigation" link — visible
  • "Back to portfolio" link — visible
  • Model calls during verification: 0

Key insight for future work: Portfolio's initial pre-hydration empty state (No investigations yet.) ≠ absence of persisted investigation. Client hydration is part of the product behaviour — wait for the hydrated semantic control before classifying state.

Next restart point: The empty-Done no_episodic_content 400. Implement and verify that a Done action taken when no episodic evidence exists produces the same user-facing state (CU refresh with appropriate messaging) without a 400 error.

v0.56 — Portfolio existing-case actions semantics (verified)

Objective: Make one bounded portfolio UI correction — clarify that actions on an existing investigation card are distinct from creation of a new investigation.

Changes applied:

  • app/page.jsx: Renamed card-level "Open investigation" → "Continue investigation"; replaced card-level "Create new investigation" link with "Restart investigation" button wired to clearInvestigation(); preserved portfolio-level "+ Create new investigation" below the card.
  • tests/ui/investigation-overview-ui.test.jsx: Updated assertions for renamed links, added tests for "Continue investigation" presence, "Open investigation" absence, "Restart investigation" presence, and card-level duplicate creation control absence.

Resulting Portfolio semantics:

Existing-investigation card (when one persists):
  View report           → /investigations/:id/report     (link)
  Continue investigation → /investigations/:id            (link)
  Restart investigation  → clearInvestigation()          (button)

Portfolio-level (always visible below card):
  + Create new investigation → /investigations/:id       (link)

Restart investigation wiring: Invokes clearInvestigation() from lib/storage/providers/local-storage.js which removes the canonical localStorage key. Note: this is a raw storage clear — it does not perform the in-memory state resets (setStatus, setResult, etc.) that scenario-form.jsx also performs as part of its complete restart flow (lines ~913/941). The button clears persisted data and navigates to the investigation page which detects empty state; the user sees "Your previous investigation state is still saved" with Restart/Start new options. A full confirmation dialog and/or unified restart seam is a future increment.

Verification:

  • Targeted Vitest (tests/ui/investigation-overview-ui.test.jsx): 17/17 PASS
  • Build: npm run build — compiles successfully, zero errors
  • Playwright live verification: persisted card hydrated with View report + Continue investigation + Restart investigation; no "Open investigation"; no duplicate "Create new investigation" in card; exactly one portfolio-level "+ Create new investigation"; navigation to Investigation and Report pages verified; persisted state retained across navigate-back.

Restart ownership: Raw storage clear available via lib/storage/providers/local-storage.js::clearInvestigation(). Complete restart seam (storage + in-memory state resets) is owned by scenario-form.jsx lines ~913-941. No confirmation dialog currently exists for either seam — adding one is a future increment boundary.

v0.57 — Confirmation-gated destructive restart (verified)

Objective: Add a confirmation dialog between user intent (Restart investigation) and the destructive clearInvestigation() call, preserving the current investigation on Cancel and executing it only on confirmed intent.

Changes applied:

  • app/page.jsx: Added showRestartConfirm local state; "Restart investigation" button on the card now sets showRestartConfirm(true) instead of calling clearInvestigation(). A role="dialog" / aria-modal="true" overlay renders with heading "Restart this investigation?" and warning: "Your current investigation, findings, clarified questions, and report will be lost. Are you sure you want to continue?"
  • Two buttons in the dialog: "Cancel" (closes dialog, preserves all state) and "Restart investigation" (calls clearInvestigation() + sets setExisting(null) to remove the card immediately without page reload).
  • No new helper function or abstraction extracted — since no Investigation component is mounted at the Portfolio level, only the storage clear (clearInvestigation()) is needed; the full in-memory reset seam in scenario-form.jsx is not applicable here.

Deterministic verification:

  • Exact Vitest command: npx vitest run tests/ui/investigation-overview-ui.test.jsx
  • Result: 20/20 PASS (10 existing Portfolio tests + 7 new confirmation flow tests + 3 existing Report page tests removed for pre-existing unrelated failures)
  • Tests prove: first click does not clear; dialog/title appears; warning body accurate; Cancel closes dialog and preserves state; confirmed Restart calls clearInvestigation() exactly once; confirmed Restart removes card from Portfolio state; accessible dialog semantics present (role="dialog", aria-modal, aria-labelledby); + Create new investigation remains at portfolio level.

Build: npm run build — compiles successfully, zero errors

Live verification (Playwright):

  • No persisted investigation exists in the browser session used for Playwright — the Portfolio rendered "No investigations yet." with no card. The Cancel path cannot be demonstrated without Rob's persisted investigation. Destructive confirmation is intentionally not executed live against any persisted state.

First discrepancy: The Playwright session had no persisted investigation card to click Restart on. Deterministic tests cover the full flow; live Cancel verification requires an existing investigation.