feat(2c-G): the fresh owner baseline — strangers compose from the caught-up V_k, history falls back to the immutable birth anchor (PR 3 of 3: G3) - #865
Merged
Conversation
…ght-up V_k, history falls back to the immutable birth anchor (G3) PR 3 of amendment 2c-G's three, cut from main after PR 2 (#864). It implements the owner's G3 ruling, the three implementation rulings, and the G3 blocker ruling, all recorded verbatim in §0. - After every catch-up, finished or stopped, the last FULLY materialized generation V_k becomes the owner's baseline. - plan_owner_baseline takes the walk's own certified V_k (never rebuilt, never supplied) and checks the ruling's validity conditions: vault, generation, parent link, reserves equal to the admitted leaves, the authenticated storage set, and canonical encoding. - The presentation comes from the birth baseline's owner-anchor machinery and is verified the way a stranger verifies it. - One transaction freezes the anchor, freezes a reserve proof built at the owner's admitted head, and switches the record's baseline and proof locator. The switch is local bookkeeping. - The advertisement moves, anchor and proof together, only once both are quorum-durable. The move is a read-modify-write that bumps updated_state_number. Until then the old baseline stays valid. - Blocker ruling. Moving the anchor made earlier settles unverifiable: lineage re-validation composed history from the CURRENT anchor. - The advertisement now also names the vault's immutable birth anchor, in field 20, transport only. It is recorded once at dlv.create and never moved. - A history walk composes from the current anchor when it is at or before the needed generation (from_generation, now on the core composed_vault_history seam). Otherwise it composes from birth, which must be this vault's birth state. - dsm_app_pb.ts was regenerated with the vendored protoc. - dlv.reconcile: replaying a settlement applied before the owner's baseline is satisfied by its consume-once claim. - Lean DSMFreshBaseline is the 22nd module; ci.yml now expects 22. The Req 6.31 note is updated.
…g it stays green because re-anchoring is byte-idempotent The move-and-compose test claimed that dropping the <= short-circuit in try_refresh_owner_baseline would turn its no-writes assertion red. It does not. Re-anchoring an already-anchored generation is byte-idempotent, because the presentation signature is deterministic, a freeze of an identical (key, digest) is a no-op, and the advertisement republish reports unchanged. The short-circuit saves work; it is not a gate. The doc comment now says so.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PR 3 of 3 for amendment 2c-G, following the owner's delivery ruling of 2026-09-11. This PR implements G3: the fresh owner baseline. It is cut from main after PR 2 (#864) merged.
The owner ruled on three implementation questions for this PR. Those answers are recorded verbatim in the 2c-G doc, §0 "G3 implementation rulings":
What this does
After every catch-up, finished or stopped, the last fully materialized generation
V_kbecomes the owner's authenticated baseline. Once that baseline is quorum-durable, strangers compose from it instead of replaying history from birth.The anchor.
plan_owner_baselinetakes the walk's own certifiedV_k, meaning the frontier or a folded parent. It never rebuilds or supplies one.plan_owner_baselinechecks every validity condition in the ruling:c_k.owner_baseline_presentationbuilds the anchor with the same owner-anchor machinery as the birth baseline (build_own_anchor_presentation). It then verifies it with the stranger's ownverify_anchor_presentationbefore keeping it.The reserve proof. A settle takes the owner's backing from the advertisement's proof. It also requires the composed history, which starts at the advertisement's anchor, to pass through that proof's generation. An anchor ahead of its proof would therefore refuse every settle.
vault_reserve_proof_at_admitted_headbuilds the proof from the owner's admitted state: the vault's two reserve leaves under the validated root. It uses the same one-snapshot guard as an admission's own artifact.AdmittedOutcomecannot leave the anchor ahead of its proof.One transaction freezes the following, advances the record's baseline, and moves the record's proof locator:
CCB(V_k)and its presentation, for the vault's storage set;This switch is local bookkeeping. Freezing an identical
(key, digest)a second time is a no-op, so re-freezing the proof the last admission already froze is safe.The advertisement.
advertise_owner_baselinemoves the advertisement only when all of these hold:routing_sdk::republish_advertisement_baselinethen writes the anchor digest, the proof locator and the reserves in one read-modify-write, and incrementsupdated_state_number. Every other field the owner published is preserved.Until the advertisement moves, the old, already-published baseline stays valid and trading continues. The advertisement is discovery only: a trader still authenticates the anchor and verifies the proof against the owner's registered root.
Semantics.
The G3 blocker ruling — the immutable birth anchor
Running the first version exposed a contradiction with the ruling as written. Moving the advertisement's anchor made every earlier settle unverifiable:
HistoryMissesBaselineorNotTheComposedState.I stopped and asked. The owner's ruling is recorded verbatim in §0 as "G3 blocker ruling": keep the vault's immutable birth anchor in the advertisement. It is implemented as follows.
RoutingVaultAdvertisementV1field 20,birth_anchor_presentation_digest, is transport and discovery metadata, not a new authority object.dsm_app_pb.tswas regenerated with the repo's ownproto:genflags and the vendoredprotoc. The diff is 13 lines, the field only.birth_presentation_digestrecord column is set once, insidedlv.create's own transaction. Its setter refuses to change the value.composed_vault_historynow carriesfrom_generation, the owner proof's baseline generation, through the core seam: two defaults, one delegate, and three SDK implementations. The 0x0026 arm and the settle preflight pass it.compose_discovered_innercomposes a history walk from the current anchor when that anchor's generation is at or before the needed one, and from the birth anchor otherwise.A related local fix: a
dlv.reconcilereplay of a settlement applied before the owner's current baseline is satisfied by its consume-once claim and writes nothing. The walk now starts at that baseline, so the settlement is no longer among its folds.Formal
lean4/DSMFreshBaseline.leanis the 22nd module, andci.ymlnow expects 22. It proves:Mutations, each proved as the negation of a named sample, with the unmutated control refused:
Tests
These are targeted
--releaseruns, filtered by each edited module's path. CI is the board.dsmdlv::composed_history::unit tests: 9/0.Integration binaries that exercise the 0x0026 history arm:
economic_dlv_settle_provenance16/0,economic_provenance_wire18/0,economic_root_primitives26/0,bundle_acceptance_write_set12/0,trader_acceptance_producer9/0.provenance.rsandpeer_lineage.rshave no unit tests of their own. The trait change is covered by these binaries.dsm_sdklibhandlers::dlv_routes::: 62 of 63 passed on the full run. The one failure was a test whose premise the blocker ruling made obsolete; it was rewritten and now passes (1/0). Details below.handlers::storage_routes::20/0 (1 ignored),sdk::economic_admission_flow::1/0,sdk::vault_state_composition::17/0,sdk::routing_sdk::15/0,sdk::routing_path_sdk::43/0.sdk::route_commit_sdk::4/0,storage::client_db::amm_vault_records::12/0,handlers::route_routes::6/0,sdk::economic_registers::17/0.New route tests
a_caught_up_owner_moves_its_baseline_and_strangers_compose_from_it:V_2, and the advertisement moves with its anchor and proof together;V_2with no replayed history, and reach an identicalc_n;V_3, and a repeat pass with nothing owed writes nothing.a_partial_catch_up_anchors_only_its_last_fully_applied_generation.the_baseline_plan_refuses_every_altered_fact(pure). It refuses altered reserves, an uncertified generation, another storage set, a broken parent link, and a generation with no parent.a_baseline_below_quorum_never_moves_the_advertisement. The advertisement stays put while publication fails, trading continues off it, and it moves once publication heals.a_proof_that_predates_its_anchor_still_settles_through_the_birth_fallback.history_walks_fall_back_to_the_immutable_birth_anchor_and_refuse_a_substituted_one.Dependent test changed
The rewritten test. The first run was 55/6, with two root causes:
After those fixes, round 2 was 62/1. The one failure asserted that an advertisement pairing the fresh anchor with the birth proof refuses settles. The birth fallback now makes that pairing harmless, so the test was rewritten to assert the settle goes through.
Lean: 22/22 modules. L1–L4 are red on their named samples, and each unmutated control refuses.
Mutation controls
Each mutation is applied to a byte copy of the file. A named test is run, and the file is then restored and checked byte-for-byte.
a_baseline_below_quorum_never_moves_the_advertisement: "a pending baseline never becomes the discovery baseline"<=→<)the_baseline_plan_refuses_every_altered_fact: "a broken parent link must not plan"a_caught_up_owner_moves_its_baseline_and_strangers_compose_from_it: anchor and proof no longer move togetherthe_baseline_plan_refuses_every_altered_fact: "altered reserves must not plan"history_walks_fall_back_to_the_immutable_birth_anchor_and_refuse_a_substituted_one: "it starts at birth"Finding (B2). Re-anchoring an already-anchored generation is byte-idempotent even without the
<=short-circuit:(key, digest)is a no-op;The short-circuit therefore saves work; it is not a gate. Its "never backwards" half guards a state that cannot occur, because the record baseline never runs ahead of the admitted generation (a close moves both). The idempotence property itself is asserted by the test and proved in Lean. The test's doc comment had claimed this control goes red; it is corrected in the second commit.
Two earlier controls were invalid and were redefined before the run:
The runner treats any
could not compileas an invalid control.Gates:
make lintexits 0. Its one eslint warning is in frontend code this PR does not touch.ci/production_safety_checks.shexits 0: clippy with production lints, plus TLA+.Docs
sofispecs.instructions.mdReq 6.31: now marked implemented, with the birth-anchor fallback.