O8E pre-production hardening (Contabo) — 05-f3-legb-verify-generalization-design
O8E Report 05 — F3 LegB/Verify generalization design package (G5)
- macro:
v0.6-o8e-pre-production-hardening-bundle - date_utc: 2026-05-21 · host: Contabo
vmi3080463 - gate covered: G5 — F3 LegB/Verify generalization design
- result: G5 PASS — implementation-ready design package (no code patched; Mac source work)
1. Current recorder assumptions — the N=60 / M1 pins
Inspected cutter_agent/ledger_v2_canonical_cut.py (LegBRecorder) and
cutter_agent/ledger_v2_canonical_verify.py (VerifyRecorder). Both are
single-use, frozen to the M1 Constitution cut. Pins found:
| Pin | Location | Nature |
|---|---|---|
PIN_CANDIDATE_COUNT = 60 |
ledger_v2_canonical_cut.py:65 (← prod_iu_adapter.PIN_CANDIDATE_COUNT) |
row-count, module-level |
_require_list(live_state, k, 60) ×11 keys |
LegBRecorder.plan |
hard-asserts EVERY list is length 60 |
body_hash_match_60 |
literal key name in VerifyRecorder.REQUIRED_KEYS + both recorders' live_state dicts |
hardcoded 60 in an identifier |
EXPECTED_SECTION_TYPE = {"principle":15,"section":3,"article":42} |
VerifyRecorder:185 (sums to 60) |
per-document histogram baked as a class constant |
PIN_M1_CHANGE_SET_ID, PIN_M1_REVIEW_DECISION_ID, PIN_M1_MANIFEST_ENVELOPE_ID, PIN_M1_EXECUTOR_SIGNATURE_ID, PIN_M1_PAYLOAD_HASH |
ledger_v2_canonical_verify.py:101-105 |
hardcoded UUIDs of the one M1 cut |
PIN_CUT_COMMITTED_AT, PIN_LEGB_COMMITTED_AT |
:76-77 |
hardcoded wall-clock timestamps |
VerifyRecorder.plan → _require_uuid(live_state,"change_set_id",PIN_M1_CHANGE_SET_ID) |
:204 |
hardest pin — refuses any change_set that is not M1 |
Consequence: VerifyRecorder will accept only the exact M1 Constitution
cut. A cut of any other document — any change_set_id, any row count ≠ 60, any
other section-type histogram — is rejected at plan(). LegBRecorder is
length-pinned to 60. Neither can serve a second cut. This is why O8D correctly
triaged F3 as not localised (it touches v0.5-ratified files).
Frozen regression anchor: the M1 Constitution cut's writer_digest is
d99a31d4a4be907c510ae15965e9f7bb3387e9e28676e9f32adf463828b1aa28
(re-observed in the O8E test run) — it MUST stay byte-identical after
generalization.
2. Implementation plan
2.1 Generic count — N becomes a per-run RunContext value
- Delete
PIN_CANDIDATE_COUNTas the source of truth for new runs. AddRunContext.candidate_count: int(set by the cutplan phase =len(cutplan_rows)). LegBRecorder.plan/VerifyRecorder.plantakenfromlive_state["candidate_count"]instead of the module pin;_require_list(live_state, k, n)uses it.- Keep
PIN_CANDIDATE_COUNTimportable only as the frozen M1 regression constant used bytest_ledger_v2_canonical_*— not by production paths.
2.2 Rename body_hash_match_60 → body_hash_match_count
- Mechanical rename of the literal key in
REQUIRED_KEYS, thelive_statebuilders, and_require_int(live_state,"body_hash_match_count",n). - Update the v0.5 equivalence tests to the new key while keeping their expected value = 60 for the M1 fixture.
2.3 EXPECTED_SECTION_TYPE → per-document expectation
- Remove the class constant. Add
RunContext.expected_section_type: dict[str,int], derived by the cutplan phase from the actual cutplan rows (Counter(row.section_type for row in cutplan_rows)). VerifyRecorder.planvalidatessection_type_cardinalityagainstlive_state["expected_section_type"](must sum tocandidate_count).
2.4 M1 identity pins → caller-supplied, not hardcoded
PIN_M1_*UUID/timestamp constants move intotests/fixtures/as the M1 regression fixture. Productionplan()readschange_set_id/review_decision_id/manifest_envelope_id/executor_signature_idfromlive_stateand validates shape (_require_uuid= "is a UUID"), not equality to M1.payload_hashbecomes a computed value (already hashed from canonical JSON) — drop the equality-to-PIN_M1_PAYLOAD_HASHassertion for new runs.
2.5 Manifest linkage & verification-result linkage
manifest_envelope_idlinkage: the leg-B writer emits the manifest; the verify writer must read it back fromlive_state, not assume M1's id.verify_resultrows link tochange_set_id+review_decision_idof the current run (already parameter-shaped once 2.4 lands).- Generic idempotency keys:
RunContext.idempotency_keysalready exists per phase — leg-B / verify idempotency keys derive fromsha256(change_set_id + phase_name), not from a 60-pinned digest.
2.6 Production governance_writer + verify_writer
Author the two injected collaborators the adapter expects (default = refuse):
def governance_writer(conn, ctx): # → {manifest_envelope_id, executor_signature_id, governance_row_count}
return LegBRecorder().record(conn, _live_state_from_ctx(ctx))
def verify_writer(conn, ctx): # → {verify_result_id, verifier_signature_id}
return VerifyRecorder().record(conn, _live_state_from_ctx(ctx))
_live_state_from_ctx builds live_state from RunContext.context_pins
(counts, ids, section-type histogram) — fully per-run, no module pin.
3. Test matrix (for the implementing macro)
T1 M1 regression: Constitution fixture (N=60) → writer_digest == d99a31d4… (frozen)
T2 generic N: synthetic N=3 cut → plan() accepts, lists length-checked to 3
T3 section histogram: N=3 {article:2,section:1} → verify accepts; wrong histogram rejects
T4 non-M1 change_set: a fresh UUID change_set → plan() accepts (no M1 equality)
T5 idempotency: re-run same change_set → LegBAlreadyRecorded / VerifyAlreadyRecorded
T6 fail-closed: missing live_state key → LegB/VerifyLiveStateMismatch
T7 real-DB rollback: governance_writer + verify_writer driven rollback-only vs live DB
4. Constraints & honest scope note
- These edits touch v0.5-ratified files (
ledger_v2_canonical_*). Per the O8E G5 rule, no code was patched in this macro — this is a design package. It needs sovereign design ratification before implementation (the v0.5 ratification is being widened, not bypassed). - The M1 Constitution-cut equivalence (
writer_digest d99a31d4…) is a frozen regression pin — generalization must not change M1's output. - Implementation is Mac source work (SSOT repo), bundled into the F3 implementation macro — see Report 09 §F3.
5. Verdict
recorder_pins_identified: N=60 ×11 lists, body_hash_match_60, section histogram,
5 PIN_M1_* UUIDs, 2 timestamps, M1 change_set equality
generalization_plan: 6-part (count, key rename, histogram, identity pins,
manifest/verify linkage, production writers)
test_matrix: 7 tests incl. frozen M1 regression
code_patched: NONE — design package only (needs sovereign ratify)
g5: PASS