core/docs/analysis/L10-shapeBplus-persistence-scope-2026-06-05.md
Shay afbcc8e41d docs(design): L10/L11 lived-spine design records + AGI-candidacy roadmap
Commits the design/analysis artifacts produced while building the lived spine and
planning the next arc (the work itself already merged in #563-#573):

- AGI-candidacy-autonomous-improvement-roadmap-2026-06-05.md — the path from the
  lived spine to AGI-candidacy: the comprehend→realize→determine→learn loop, the
  cross-domain capability+calibration yardstick, the logical-necessity × technical-
  priority execution order, and the corrected epistemic foundation (grounded
  honesty designed-in; estimation learned/ratified; confidence always evidence-
  grounded; intake first-class — NOT "no ingestion"; calibration+grounding the
  measured invariant).
- L10-runtime-scoping + L10-continuity-spike-design — the L10 decision surface and
  the falsifiable spike spec (P1-P5) that became evals/l10_continuity/.
- L10-shapeBplus-persistence-scope — the A->E scope that became Shape B+ resume.

.gitignore: ignore the local .system-map/ navigation index (per-developer, never
tracked; regenerated on demand).
2026-06-05 15:11:08 -07:00

112 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Shape B+ Persistence — Scope
**Date:** 2026-06-05 · **Status:** SCOPE (await go-ahead before code) · **Parent:** [[milestone-l10-continuity-spike]] (the acceptance oracle) · **Telos:** [project-core-is-one-continuous-life] — this is the **T-resume** half (make a reboot transparent), not the always-on process.
## Goal (one sentence)
Make the lived session state — field, vault, anchor, graph, referents, dialogue blade — **durable across reboot, bit-exactly**, so that resuming from a checkpoint continues the *same* life instead of starting a fresh one sharing only recognizers/candidates.
## The acceptance oracle is already built
The L10 continuity spike (`evals/l10_continuity/`, #567) gives us a precise,
falsifiable target — we do **not** invent acceptance criteria:
- **The flip:** today `evaluate_p2b_reboot_transparency(...).post_reboot_transparent == False`
and `test_p2b_documents_current_resume_gap` pins that. Shape B+ makes it **True**.
The build is done when that test flips and we invert its assertion to assert
transparency (P2b becomes a green guard: a reboot is byte-identical to no-reboot).
- **The guards that must stay green prove the round-trip is honest:**
- **P1 closure** — `versor_condition < 1e-6` must survive serialize→deserialize
(the #1 risk: float round-trip must be bit-exact or closure breaks).
- **P2a determinism** — restored state must yield identical `trace_hash`.
- **P4 crash recovery** — recovery from a session-state checkpoint stays
deterministic + atomic (ADR-0156).
- **P5b/P5c** — anchor/coherence behavior unchanged across reboot.
- **The TDD loop writes itself:** restore SessionContext fields until P2b is
transparent. If a field is load-bearing and we miss it, P2b stays False and
tells us exactly that. The spike *drives* the scope.
## Lived state to persist (verified inventory)
`ChatRuntime._context` is a `SessionContext` assigned once and never restored
(`chat/runtime.py:608`). Everything it holds is **pure data** (no locks / files
/ RNG / threads — verified), so the whole object is serializable:
| Component | Fields | Serialization note |
|---|---|---|
| `state: FieldState` | `F` (32-vec), `node`, `step`, `holonomy` (opt array), `energy` (EnergyProfile), `valence` (ValenceBundle) | arrays **bit-exact**; energy/valence are frozen dataclasses of primitives |
| `vault: VaultStore` | `_versors` (deque of arrays), `_metadata` (deque of dicts), `_store_count`, `_reproject_interval`, `_max_entries` | the **bulk**; `_exact_index` + `_matrix_cache` are DERIVED → rebuild on load, do not persist |
| `_anchor_field` | 32-vec array | bit-exact; load-bearing for `_session_anchor_pull` |
| `graph: SessionGraph` | `_nodes: list[TurnNode]` (turn_idx, input/output versors, tokens, role, slots, edges) | per-node arrays bit-exact |
| `referents: ReferentRegistry` | `_slots`, `_history` (ReferentEntry: surface/versor/turn/slot), `_last_resolved_*` | per-entry versor bit-exact |
| `running_dialogue_blade` | array \| None | bit-exact |
| scalars/caches | `turn`, `_last_input_tokens`, `_last_resolved_input_tokens`, `_last_input_versor`, `_last_response_tokens`, `_dialogue_history_compat` | restore for safety; P2b reveals which are load-bearing |
## Architecture
- **Per-component `to_dict`/`from_dict`** mirroring the existing Shape-B
convention (`DerivedRecognizer.to_json`, `DiscoveryCandidate.as_dict/from_dict`):
`FieldState`, `VaultStore`, `SessionGraph`, `ReferentRegistry` (+ the small
nested types). Compose into `SessionContext.snapshot() -> dict` /
`SessionContext.restore(dict)`.
- **Exact array codec** — arrays serialize as `{dtype, shape, b64(raw bytes)}`
(NOT decimal JSON — decimal truncates and breaks both closure and determinism;
this is the Cl(4,1) float-truncation pitfall). Preserve `float32` vs `float64`
exactly.
- **Storage** — extend `engine_state/` with a `session_state.json` written via
the existing `_atomic_write_text` (ADR-0156), and bump `_SCHEMA_VERSION` 1→2.
The #563 migration discipline already makes this backward-compatible: a v1
checkpoint (no `session_state`) loads gracefully into a fresh session — i.e.
exactly today's behavior — so old checkpoints don't break.
- **Wiring** — `ChatRuntime.checkpoint_engine_state()` saves the snapshot;
`_load_engine_state()` restores it into `self._context`.
## Phases (entry → exit gates)
| Phase | Build | Exit gate (CLI/test) |
|---|---|---|
| **A. Exact array codec + FieldState** | `b64(dtype,shape,bytes)` codec; `FieldState.to_dict/from_dict` | bit-exact round-trip test + **closure preserved** (`versor_condition` identical post-restore) |
| **B. VaultStore (de)serialize** | snapshot versors+metadata+store_count; rebuild index/cache on load; **NO reproject/normalize on load** | round-trip test: `recall(q)` returns **identical** results post-restore (exact CGA recall preserved); bright-line review that load calls no normalization |
| **C. Graph + referents + anchor + blade → SessionContext.snapshot/restore** | compose all per-component codecs | `SessionContext` snapshot→restore round-trip is object-equal |
| **D. Wire into engine_state (schema v2) + ChatRuntime** | save in checkpoint, restore in load; v1 back-compat | a v1 checkpoint still loads (fresh session); a v2 checkpoint restores the context |
| **E. Flip the oracle** | invert `test_p2b_documents_current_resume_gap` to assert transparency | **P2b transparent** + P1/P2a/P4/P5 still green + the spike `deterministic_digest` stable; `python -m evals.l10_continuity` all gates pass incl. reboot transparency |
## Risks (surfaced up front)
1. **Bit-exact float round-trip (closure + determinism).** The whole flip
depends on it. De-risk in Phase A with a base64 raw-bytes codec; never decimal.
2. **`vault/store.py` is a CLAUDE.md forbidden normalization site.** Adding
pure (de)serialization is allowed, but the **load path must not reproject,
re-normalize, or repair** — it restores raw persisted versors and rebuilds
only the derived index/cache. Needs an explicit bright-line review and ideally
an architectural-invariant assertion that `from_dict` invokes no normalizer.
3. **Reproject interaction.** `reproject()` rewrites all versors every N stores,
so the vault content changes at reproject boundaries. A full snapshot captures
post-reproject state correctly; an append-only log would diverge. Start with
**full snapshot** (correct); optimize to incremental only if Phase E / P3 shows
the I/O cost matters.
4. **Persistence cost vs P3.** Full-snapshot each turn is O(n) I/O → O(n²) over a
session. Measure with the spike's P3 + a longer soak; if it breaches, move to
snapshot-at-reproject + append-between. Correctness first.
5. **`trace_hash` depends on `vault_hits` + recall surface.** This is *why* the
flip works (restored vault → identical recall → identical hash) and *why* the
round-trip must be exact. Phase B's exact-recall test de-risks it.
6. **Teaching-safety boundary.** This persists **session memory** (immediate per
CLAUDE.md), NOT reviewed/ratified memory — it is continuity, not a learning
path. Do not let it become a parallel correction/teaching channel.
## Scope boundary (what this is NOT)
- **NOT** the always-on process / idle-tick runtime (the *other* L10 half) — this
is resume-transparency only.
- **NOT** cross-reboot **identity-lineage** work (EngineIdentity / lineage chain)
— that builds *on top of* a transparent resume.
- **NOT** persisting reviewed/ratified memory (already the teaching path).
## Proposed PR shape
One stacked sequence, each PR green on its own exit gate: **A** (codec+FieldState)
**B** (vault) → **C** (context compose) → **D** (engine_state wiring) → **E**
(flip the oracle). AC are additive + unit-tested in isolation (fast); D wires;
E is the soak-lane flip. E is the load-bearing PR — it's the one that proves
resume-as-same-life.