feat(2c-G): owner catch-up — admitted, ordered DlvOwnerApplyV2 behind storage.sync and dlv.reconcile (PR 1 of 3: G1 + G2) - #863
Merged
Conversation
… storage.sync and dlv.reconcile (rulings G1 + G2)
PR 1 of amendment 2c-G's three. An owner returning to an active DLV applies every certified market
settlement traders realized while it was away, oldest first, each as an ADMITTED DlvOwnerApplyV2.
Its reserve leaves then equal the composed frontier. Catch-up consumes certified history and never
creates any.
- 0x0027 producer. certify_market_evidence already proved the trader's 0x0021 leaf and its
256-sibling path under the validated R_T^+ (Req 21.16), then dropped the path. It is now carried on
the certified fold (CertifiedPayment). settlement_payment_producer projects it into
SettlementPaymentEvidenceV1 and mirrors the arm's leaf-equality and inclusion checks, plus its
strict decoder, before signing. The arm runs after the advance commits, so the mirror is what
keeps a refusal from stranding an admission.
- admitted_dlv_owner_apply uses the 0x0027 arm and shares admit_reserve_mutation with funded
create.
- One engine, catch_up_owner_vault_locked: consume-once by settlement identity, and an interruption
keeps every completed apply durable. It always returns a report: applied, already applied, and why
it stopped. owed_certified_folds is pure, and Through(x) is the prefix of Frontier ending at x.
- storage.sync runs resume_owner_catch_up last. It gates nothing, skips while a catch-up holds the
lock, and never calls sync.
- dlv.reconcile { vault_id, x } now catches up through x: it applies older settlements first where
it used to refuse them, and it waits on the lock.
- Core: advance refuses a DlvOwnerApplyV2 with no attached admission. The dependent tests use
advance_admitted.
- dlv.unlockRouted's displaced doc block moves back onto dlv_unlock_routed.
- Lean DSMOwnerCatchUp is the 20th module, and ci.yml expects 20. Docs: Req 4.5 implementation note,
Req 6.31 G3-owed status, and registry §5.37 producer.
Not here: G4, the admitted close (PR 2), and G3, the fresh baseline anchor (PR 3).
cryptskii
added a commit
that referenced
this pull request
Sep 11, 2026
…s every close fact from the composition (G4) (#864) PR 2 of amendment 2c-G's three, cut from main after PR 1 (#863). - The close is admitted. admitted_dlv_close runs on PR 1's admit_reserve_mutation with facts None, and the builder derives the two 0x0024 same-transition credits. Its write set drains exactly the owner's ADMITTED reserve leaves at the parent generation, so the proceeds exist in R_econ. Before, they existed on the head only. - The one close commit derives, and no longer trusts its caller. commit_canonical_close(vault_id, composed, close_commitment, op) reads the parent, generation, reserves, parent binding and storage set off the composition through the pure close_plan, and holds the signed operation to them. It refuses: - a close whose parent a realized market successor consumed (it may not erase it); - later generations realized, or a generation beyond the frontier (no skip); - altered amounts; - a parent another candidate holds unrealized (an uncertified predecessor). Below the route gate, a close of the owner's stale baseline would otherwise have passed both core and an admission, because R_econ is exactly as stale as the head. finish_prepared_close keeps its resume-specific identity checks. - Core: advance refuses a DlvClose with no attached admission. fence_allows runs first, so a fenced head still gets the fence's own refusal. The dependents move to advance_admitted, including the table-driven malformed-close test. - Close before catch-up still refuses. Authority, QuorumBind, accepted-successor and close-auth are unchanged. - Lean DSMAdmittedClose is the 21st module, and ci.yml now expects 21. Docs: a Req 6.30 implementation note, and the producers in registry §5.34. Not here: G3, the fresh baseline anchor (PR 3).
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 1 of 3 for amendment 2c-G (owner catch-up), per the owner's delivery ruling of 2026-09-11. This PR covers rulings G1 + G2: owner catch-up execution. PR 2 (G4, the admitted terminal close) and PR 3 (G3, the re-anchor) follow, each cut from main after its predecessor merges.
The rulings text is frozen in #862, which recovers #861's unmerged freeze commit. This PR does not touch that doc.
What this does
An owner returning to an active DLV now catches up on every certified market settlement traders realized while it was away. Each one is applied oldest first as an admitted
DlvOwnerApplyV2, and afterwards the owner's reserve leaves are exactly the composed frontier.The invariant is: catch-up consumes certified history; it never creates certified history.
0x0021receipt leaf and its 256-sibling path under the validatedR_T^+when it certified the fold (Req 21.16). It then discarded the path.certify_market_evidencenow carries the leaf, the path and the trader coordinates on the certified fold asCertifiedPayment, alongsidecertified_acceptance.settlement_payment_producerprojects that payment intoSettlementPaymentEvidenceV1. Nothing is fetched again.finish_admission, after the advance has committed, so evidence the arm refused would strand the admission.admitted_dlv_owner_apply(ineconomic_admission_flow) uses the0x0027 ValidatedDlvSettlementPaymentcredit arm, with the evidence frozen in the advance transaction.admit_reserve_mutationbody with funded create, so the staged advance andfinish_admissionare not duplicated per operation.catch_up_owner_vault_locked) serves both entrypoints:owed_certified_foldsis pure:Through(x)is exactly the prefix ofFrontierthat ends atx.storage.sync.resume_owner_catch_upis the last sync pass.dlv.reconcile { vault_id, x }now means "catch up throughx". It waits on the lock.xwhen an older settlement was still unapplied. Now it applies the older ones first, in order. The later settlement is still never applied over a state that lacks the earlier ones. The frontend's loop overpendingXis unchanged and still works.DlvOwnerApplyV2now requires its own attached Prepared admission inadvance.advance_admitted: six core tests,dlv_owner_apply_preservation,dlv_value_op_signing, and onevault_rehydrationtest.dlv.unlockRouted's doc block was displaced ontodlv_reconcile. It is moved back ontodlv_unlock_routed, which had none.What this does not do
DlvClose/DlvWithdraware still unadmitted. That is PR 2.AnchorPresentationV3is published after catch-up, and Req 6.31 is annotated as owed. That is PR 3.Formal
lean4/DSMOwnerCatchUp.leanis the 20th module;ci.ymlnow expects 20. It has no sorry, and its axioms arepropext/Quot.soundonly. It proves:Mutations, each executed as the kernel proving a named sample's negation, with the unmutated control refused:
Tests
Targeted
--releaseruns, each filtered by the edited module's path. These are not the board; CI is.dsmtypes::device_state::tests::: 67/0. This includes the newan_owner_apply_is_refused_without_its_own_attached_admission.dsm_sdkintegration binariesdlv_owner_apply_preservation(4/0) anddlv_value_op_signing(7/0). These are the chokepoint dependents.dsm_sdklib:handlers::dlv_routes::: 55/0handlers::storage_routes::: 20/0, 1 ignoredsdk::economic_admission_flow::: 1/0sdk::vault_state_composition::: 17/0sdk::sofi_receipt_publication::: 5/0sdk::vault_rehydration::tests::: 14/0New route tests:
an_offline_owner_catches_up_on_sync_to_exactly_the_composed_frontier. It runs through the realstorage.syncand checks that:c_nand every trader head are unchanged;an_interrupted_catch_up_resumes_without_applying_anything_twice.a_certified_fold_with_any_fact_altered_is_refused_and_writes_nothing. It covers the receipt leaf, the inclusion path, the parent reserves and the parent generation.a_sync_pass_skips_while_another_catch_up_runs.Updated tests:
…_lp_catches_up_oldest_first: an explicit catch-up through generation 1, then the sync pass finishes generation 2.The first focused run found a real bug. The engine's
?discarded partial progress, so an interrupted pass reported 0 of its durable applies. The engine now always returnsCatchUpReport { applied, already_applied, stopped }. The sync pass counts every durable apply, anddlv.reconcilereportsN applied, then stopped.Mutation controls
Each mutation is applied to a byte copy of the file. A named test is run against it, and the file is then restored and checked for byte equality.
DlvOwnerApplyV2arm of the core fencean_owner_apply_is_refused_without_its_own_attached_admission: the raw apply moved the reservesstorage.syncan_offline_owner_catches_up_on_sync_to_exactly_the_composed_frontiera_certified_fold_with_any_fact_altered_is_refused_and_writes_nothinga_certified_fold_with_any_fact_altered_is_refused_and_writes_nothinga_sync_pass_skips_while_another_catch_up_runs(timeout)an_offline_owner_catches_up_on_sync_to_exactly_the_composed_frontieran_offline_owner_catches_up_on_sync_to_exactly_the_composed_frontierThrough(x)truncationlp_offline_market_advances_three_generations_and_lp_catches_up_oldest_firstThe first MR2 was an invalid mutation and stayed green. It read
false && a || …, and operator precedence meant only the first two comparisons were disabled. The corrected control empties the refusal branch and goes red.Gates:
make lint: exit 0. The single eslint warning is in frontend code this PR does not touch.ci/production_safety_checks.sh: exit 0. This includes clippy with the production lints and TLA+.Docs
sofispecs.instructions.md: the Req 4.5 implementation note, and the Req 6.31 status (the baseline is owed by G3).ccb-object-registry.md§5.37: the producer and the chokepoint.