Nine ADRs (0254, 0256-0263) were merged into main and left stamped Proposed. Two carried an explicit ratify-on-merge predicate their own merge had already discharged, and ADR-0256 governs deduction_serving_enabled, which was ratified True on 2026-07-24 and is serving live traffic. So the governance record asserted "not yet decided" about a decision already in force. That is the asymmetry the assessment arc (#113) found one file over in workbench/api.py: the honest path degrades, the stale record lies. An unwritten ADR is a visible gap; a Proposed one that is actually in force is a false statement. Each stamp records the ACTUAL ratifying act -- "Accepted, ratified by Joshua Shay via <merge> (<sha>, <date>)" -- derived from the commit that added the file and verified an ancestor of main, not assumed. Merge authority is Shay's alone (AGENTS.md: no merge automation), so the merge IS the ratifying act. No ADR content changed. No flag changed. ADR-0262's stamp says so explicitly: accepting it does NOT enable curriculum_serving_enabled, which stays False pending ratified volume -- eleven bands re-measured today, still 24x-73x short. tests/test_adr_status_governance.py pins two independent invariants in the smoke (pre-push) suite: 1. A default-ON flag is not governed by a Proposed ADR. The flag -> ADR mapping is DERIVED by walking core/config.py for `<name>: bool = True` and reading ADR refs from the preceding comment block -- not a hand-written table, which would be the same second-copy-of-a-closed-set defect ADR-0256's arc fixed. 2. A ratify-on-merge predicate cannot coexist with Proposed. Self-discharging: the file being on main IS the merge having happened. Plus a vacuity guard, because a derivation that parses zero flags would make every other assertion pass on an empty set. Registered two orphans found in passing: test_adr_index.py (5) and test_ratification_ceremony.py (14) landed in #113 in NO curated suite, so 19 tests -- including the one mechanism that can move curriculum volume -- ran only under `full`, which gates nothing. Fifth instance of this shape. Deliberately NOT fixed, recorded in the research doc: the 312-file corpus has 27 unparseable status lines and draft/ratified/active variants. A closed-vocab assertion would fail on ~35 pre-existing files and get muted, and a muted gate reads as coverage. [Verification]: smoke 555 passed in 137.73s (236 baseline + 314 + 5, +1.2s); governance pin 314 passed in 1.18s standalone and MUTATION-CHECKED -- reverting ADR-0256 to Proposed fails both invariants independently (2 failed/312 passed); orphans 19 passed; ruff clean. Canonical Python 3.12.13, uv sync --locked.
80 lines
4 KiB
Markdown
80 lines
4 KiB
Markdown
# ADR-0263 — The ratified-ledger bridge
|
|
|
|
- **Status:** Accepted — ratified by Joshua Shay via the `feat/curriculum-serve-physics` merge (`0ae54ebb`, 2026-07-24). Amended 2026-07-25 with rule 5 (ledger-absence policy declared, not passed)
|
|
- **Date:** 2026-07-24
|
|
- **Arc:** generalization Phase 3.3 (docs/plans/generalization-arc-2026-07-24.md §2)
|
|
- **Governs:** `core/ratified_ledger.py` and the three adapters over it —
|
|
`generate/determine/estimation_license.py`, `chat/deduction_serve_license.py`,
|
|
`chat/curriculum_serve_license.py` — plus the sealed-artifact writer in
|
|
`evals/deduction_serve/practice/runner.py`.
|
|
|
|
## 1. Context
|
|
|
|
Three capabilities had independently converged on the same four-step pattern:
|
|
sealed practice writes a per-class tally table → the table is committed as a
|
|
ratified artifact → serving verifies its `content_sha256` on load → a per-class
|
|
gate decides SERVE against fixed ceilings.
|
|
|
|
Three near-identical implementations is the point at which a shared component
|
|
is an extraction rather than a guess. It matters now because the generalization
|
|
plan puts a per-subject arena behind every new subject: without a bridge, each
|
|
subject copies fifty lines of verification, and the copies drift.
|
|
|
|
## 2. Decision
|
|
|
|
`core/ratified_ledger.py` owns the mechanics — `seal_artifact`,
|
|
`write_sealed_ledger`, `load_sealed_ledger`, `serve_license`, `tally_dict` —
|
|
and states the four rules once:
|
|
|
|
1. **The engine reads; only sealed practice writes.**
|
|
2. **Tamper-evidence is structural** — a load that cannot reproduce
|
|
`content_sha256` REFUSES. A hand-edited ledger is not a slightly-wrong
|
|
ledger; it is an unratified one.
|
|
3. **Ceilings are not negotiable at the call site** (ADR-0175 invariant #4).
|
|
4. **Absent evidence is never a license** — a missing class yields `None`, and
|
|
every caller's `None` branch serves the disclosed surface.
|
|
|
|
Each capability keeps a thin adapter that names its artifact, keeps its
|
|
memoization, and preserves its public API. One genuine difference is now
|
|
declared rather than implied: `load_sealed_ledger(..., missing_ok=True)`
|
|
distinguishes a ledger a capability *ships with* (absence = broken deployment,
|
|
refuse) from one whose practice volume is still being built (absence = no class
|
|
has earned anything, serve disclosed). Curriculum serving is the second kind
|
|
today (ADR-0262 §5.1).
|
|
|
|
## 3. Why this is safe
|
|
|
|
The extraction's safety property is byte-identity: `seal_artifact` and
|
|
`write_sealed_ledger` reproduce exactly what the bespoke sealers wrote, so
|
|
re-sealing through the bridge leaves every committed artifact and every lane
|
|
pin unmoved. That is asserted, not assumed —
|
|
`test_committed_deduction_ledger_reseals_byte_identically` re-derives the
|
|
committed 25-band ledger through the bridge and compares the bytes.
|
|
|
|
## 4. What this unblocks
|
|
|
|
A new subject arena now needs a gold corpus and a band key. It does not need to
|
|
re-implement ratification — which is what the plan meant by "so each subject
|
|
arena plugs in without bespoke wiring", and why §3 sequenced this ahead of the
|
|
second subject.
|
|
|
|
## 5. Scope-outs
|
|
|
|
- **The estimation adapter keeps its predicate → converse-class naming.** That
|
|
mapping is genuinely local to estimation; the bridge gates a class name and
|
|
does not know how one is derived.
|
|
- **No ledger migration.** All three artifacts already share the schema shape;
|
|
nothing is rewritten, versioned, or moved.
|
|
- **`core/learning_arena`'s practice engine is untouched.** The bridge covers
|
|
the consumption half (seal → serve); the production half (run practice, tally)
|
|
is already shared.
|
|
|
|
## 6. Verification
|
|
|
|
`tests/test_ratified_ledger_bridge.py` — 8 tests: round-trip, hand-edit
|
|
rejection, required-vs-optional absence, absent class never licensed, the
|
|
Wilson volume floor, a single `wrong` costing the license, committed-ledger
|
|
byte-identity, and a guard that all three adapters read through the bridge
|
|
(they no longer import a hashing module of their own). Plus the existing
|
|
consumers unchanged: `core test --suite deductive` 252 passed; the estimation
|
|
and license test set 355 passed.
|