Skip to content

2c-C3 — freeze ValidDlvSuccessorCore, and land C2's owed registry edits - #786

Merged
cryptskii merged 5 commits into
mainfrom
docs/amendment-2c-c3-valid-dlv-successor
Sep 9, 2026
Merged

2c-C3 — freeze ValidDlvSuccessorCore, and land C2's owed registry edits#786
cryptskii merged 5 commits into
mainfrom
docs/amendment-2c-c3-valid-dlv-successor

Conversation

@cryptskii

Copy link
Copy Markdown
Collaborator

Three commits, in dependency order.

1. C2's owed registry edits (6dee3041)

Amendment 2c-C2 merged as #785 but its "Registry and cross-document edits" section never landed. All six of its markers returned zero against origin/main. C1's edits did land, so the asymmetry was easy to miss — and the sentence C2 declares false was still sitting in the registry telling implementers the opposite of the ruling.

Seven edits, one per obligation C2 recorded. The sharpest: signature_alg = 0x0001 is DSM BLAKE3-SPHINCS+-SPX256F, not FIPS-205 SLH-DSA-256f. The key and signature widths coincide exactly, which is what makes the substitution silent — a foreign verifier linking a standards-only SLH-DSA library gets byte-identical lengths and fails every signature with nothing to diagnose. §3.1's old closing sentence, "the algorithm and the key bytes stand or fall together", is replaced; it was false as written, and false in the direction that made the substitution look safe.

Also adds the retrieval obligation (new §2.11), BindingRecordWireV1 as the one named non-CCB grammar (§2.10), the dsm-testnet root-register profile (new §3.2), and the §15.3 reconciliation.

2. Amendment 2c-C3 (f6376ece)

Specification-first, on the owner's ordering. That ordering earned its keep twice — both findings were invisible to clause-by-clause transcription, and both change what the predicate must contain:

  • §8's Req 8.1 is a successor-validity condition, not background: "a claim is consumed at most once and its exact removal must be visible in the successor state." No earlier draft carried it.
  • Def 6.1's terminal close requires crediting released reserves to owner balance exactly once — a property of the owner's balance, not of V_{n+1}, so no predicate over the successor can express it. Declared, owner named, explicitly not discharged.

The expected "15-clause predicate" does not exist in the source. Def 6.1 is ten common checks plus kind-specific tails: sixteen items at its own granularity, ~34 atomically. Fifteen is the VaultStateV2 tuple arity and the revision number. That framing is revoked.

Four Rev 15 errata corrected, two of which made the predicate unformulable — most sharply D3: §7.1's fee_t is zero for the beta family, because the fee stays in the reserves rather than being extracted. A verifier applying §7.1 literally rejects every valid beta market successor.

successor_ccb resolved unconditionally. canon() is encode_to_vec(), so the field is frozen wire and is the bundle-shape discriminator — the opposite of the draft's assumption. It is not deleted from a frozen encoding; the encoding is superseded in full, and 2c-A already prohibits the field in as many words. Until that cut lands, b is computed over prost bytes, which §2.10 forbids. Recorded, not relied upon.

3. lean4/DSMValidDlvSuccessor.lean (facd9abe)

Strictly additive; CI Lean count 11 → 12.

market_never_derives_retired formalizes the whole argument for erratum D2. The encoding theorems show the §2.4 count prefix is load-bearing: without it, a state holding one claim and a state holding a live budget have byte-identical encodings, because the claim list borrows the presence marker's byte.

One theorem was wrong on the first pass and the compiler caught it. I had claimed correspondence's class always agrees with the derivation's class. It does not — a successful derivation whose bytes mismatch is invalid, so the derived arm downgrades valid to invalid. That asymmetry is the comparison's purpose. Replaced with the exact statement.

Ruling H is formalized rather than promised: core_does_not_discharge_token_policy exhibits an input satisfying the core predicate while the complete predicate fails.

Mutation controls executed, not asserted. Both produced the strongest available result — the kernel proving the negation of a named theorem:

mutation result
strip the count prefix canonVault_separates_the_samples proved FALSE; canonVault_injective falls back on sorryAx
remove the byte comparison preserved_field_mutation_is_rejected, encumbrance_introduction_is_rejected, budget_introduction_is_rejected, market_successor_not_accepted_under_close each proved FALSE

The encumbrance and budget controls matter most: beta's E is always empty and no Allocation type exists in Rust, so no beta-shaped test can exercise Req 8.1 at all.

Axioms reported per theorem. No result depends on sorryAx or Classical.choice; six depend on no axioms at all.

Verification

All 12 Lean modules pass lean -DwarningAsError=true (exit 0), run as the CI gate runs them.

No Rust, no proto, no tests changed — the only non-doc change is the CI module count.

What this does NOT do

Phase D (production Rust) is not in this PR and is owed by the adopting change. The amendment records the debt it lands on: vault_state_composition.rs:572 binds the bundle's declared successor and discards it with let _ = transition;, folding its own derived next_state instead. The comparison this amendment makes normative does not exist today. Also owed: replacing the BindingEvidenceUnavailable taxonomy collapse, and giving advance_validated a VaultStateV2 in its signature.

Two conjuncts are declared and undischarged — TokenPolicyValid and the terminal close's owner credit — named so "C3 is closed" can never be read as "DLV succession is fully verified."

C2 was merged as PR #785 but its "Registry and cross-document edits"
section never landed. All six of its markers returned zero against
origin/main. C1's edits did land, so the asymmetry was easy to miss --
and the sentence C2 declares false was still sitting in the registry
telling implementers the opposite of the ruling.

Seven edits, one per obligation C2 recorded:

  §3.1  signature_alg 0x0001 is DSM BLAKE3-SPHINCS+-SPX256F, NOT
        FIPS-205 SLH-DSA-256f. The widths coincide exactly, which is
        what makes the substitution silent: a foreign verifier linking
        a standards-only SLH-DSA library gets byte-identical key and
        signature lengths and fails every signature with nothing to
        diagnose. The old closing sentence -- "the algorithm and the
        key bytes stand or fall together" -- is replaced; it was false
        as written, and false in the direction that made the
        substitution look safe.

  §2.10 BindingRecordWireV1 recorded as the ONE named non-CCB grammar,
        with its Class N argument and an explicit statement that it is
        not permission for arbitrary protobuf-derived identities.

  §2.11 New: the retrieval obligation. Recompute the identity over the
        bytes returned and compare to the address asked for, BEFORE
        reading any field. Consumer's obligation, never the fetcher's.
        Must be discharged by a single named construct, not by
        open-coding.

  §15.3 Reconciled in §2.11: Rev 15's H(N ‖ P) names no hash function,
        has no separator and no length discipline, so it is not
        injective in N. The frozen form is H_dom(N, P). Injectivity is
        a property of the NUL-free tag AND the separator together --
        the separator alone would push the same ambiguity one byte
        deeper -- so §2.9's NUL-free condition is cited, not assumed.

  §3.2  New: normative network parameters. The dsm-testnet
        root-register profile -- three (member_id,
        register_incarnation_id) pairs, n = 3, q = 2, and the derived
        storage_set_id. A network absent from the table is unknown,
        not permissive.

  §5.2  Cross-references §3.2, so the frozen element encoding and the
        values instantiating it can be read against each other.

  §7    2c-C2 bullet added and marked WRITTEN, recording that
        authenticated retrieval is frozen as a rule and NOT met by the
        implementation. The decomposition's "P0-P6" citation is
        corrected to the authority-resolver contract; C2 ruling A
        declines to elevate P0-P6 into a SoFi interface.

§4's counts do not move: framework and namespace content only, no
field table added, changed or burned.

Documentation only. Prerequisite for 2c-C3, which cites the registry.
C2 froze how a verifier obtains and authenticates the substrate and
deliberately did not decide what makes a DLV continuation valid. C3
decides exactly that.

Specification-first, on the owner's ordering: complete the read-only
source reconciliation, freeze every clause, write the normative
predicate, then implement. That ordering earned its keep twice --
both findings were invisible to clause-by-clause transcription and
both change what the predicate must contain:

  - §8's Req 8.1 is a successor-validity condition, not background:
    "a claim is consumed at most once and its exact removal must be
    visible in the successor state". No earlier draft carried it.
  - Def 6.1's terminal close requires crediting released reserves to
    owner balance EXACTLY ONCE -- a property of the owner's balance,
    not of V_{n+1}, so no predicate over the successor can express it.
    Declared, owner named, explicitly not discharged.

The expected "15-clause predicate" does not exist in the source. Def
6.1 is ten common checks plus kind-specific tails: sixteen items at
its own granularity, ~34 atomically. FIFTEEN IS THE VaultStateV2 TUPLE
ARITY AND THE REVISION NUMBER. That framing is revoked; no clause
count is normative.

Four Rev 15 errata corrected as exact predicates, two of which made
the predicate unformulable as written:

  D1  the parent-reserves-digest operand does not exist -- it was part
      of p_v, which Req 6.6 burned. c_n commits the whole tuple.
  D2  Retired(V) := reserve_a = 0 ∧ reserve_b = 0. No new field;
      unambiguous BECAUSE market admissibility forbids a = 0, so a
      market successor can never zero both legs.
  D3  fee_t ≡ 0 in §7.1 for the beta market family. The fee stays in
      the reserves, so it is inside Σ R^(n+1), not subtracted from it.
      A verifier applying §7.1 literally REJECTS EVERY VALID BETA
      MARKET SUCCESSOR.
  D4  direction is carried nowhere and is derived from the policy-
      commit set equality. Total, because beta_constant_product
      refuses an unordered or equal pair, so the same check that
      establishes membership also refutes input = output.

Canon(expected) = Canon(supplied) is the SINGLE AUTHORITATIVE
SUCCESSOR-STATE CORRESPONDENCE test -- one conjunct, not the whole
predicate. Authority, signatures, the parent relationship, binding
finality and evidence validity stand alongside it. DeriveExpected is
typed and partial (Derived | Invalid | Incomplete | SafetyViolation);
the byte comparison happens only on the Derived arm, so failed
arithmetic and unavailable evidence cannot hide inside an apparently
total call.

successor_ccb resolved unconditionally. canon() is encode_to_vec(), so
the field IS frozen wire and IS the bundle-shape discriminator -- the
opposite of the earlier draft's assumption. It is not deleted from a
frozen encoding; the encoding is superseded in full, and 2c-A already
prohibits it: "do not carry both the complete successor and a second
independently encoded successor digest". Until that cut lands, b is
computed over prost bytes, which §2.10 forbids. Recorded, not relied
upon.

C3 receives typed prerequisite RESULTS, not success-only facts. A
component that owns INVALID / INCOMPLETE / SAFETY_VIOLATION cannot be
handed only established successes; it would be blind to the outcomes
it classifies.

Two conjuncts are declared and undischarged -- TokenPolicyValid and
the owner credit -- named so "C3 is closed" can never be read as "DLV
succession is fully verified".

Records the implementation debt this lands on: vault_state_composition
.rs:572 binds the bundle's declared successor and discards it with
`let _ = transition;`, folding its own derived next_state instead. The
comparison this amendment makes normative does not exist today.

Documentation only. No Rust, no proto, no tests.
…proved

Phase C of amendment 2c-C3: definitions and theorem statements for
ValidDlvSuccessorCore. Strictly additive -- new module, no existing
proof touched. CI Lean count 11 -> 12.

What it machine-checks:

  BRIDGE       canonVault_injective. Canonical equality implies
               structural equality, so "every preserved field equals
               V_n" derived from a byte comparison carries no unstated
               assumption. Ruling B names this obligation precisely
               because omitting it would hide one.

  ENCODING     The encumbrance set sits at field 10, in the MIDDLE of
               the tuple, so its §2.4 count prefix is load-bearing.
               Two genuinely different states -- one encumbered, one
               holding a live budget -- have byte-identical encodings
               without it, because the claim list borrows the presence
               marker's byte:

                   no claims, β = some 0  ->  [] ‖ (1 ‖ 0)  =  [1,0]
                   claim [1], β = none    ->  [1] ‖ (0)     =  [1,0]

               A verifier comparing those bytes accepts a successor
               that silently converted a budget into an encumbrance.

  D2           market_never_derives_retired. This is the whole
               argument for erratum D2, formalized: admissibility
               forbids a = 0, so the input leg is R_in + a > 0 and at
               least one leg stays strictly positive. Both-zero is
               therefore reachable ONLY by the close family, which is
               what makes Retired an unambiguous marker with no new
               tuple field.

  D4           direction_refutes_equal_commitments. The set-equality
               check that establishes membership also refutes
               input = output, because token_a < token_b strictly.
               The derivation is total.

  DERIVED      Preserved and mutated field equalities are obtained
               FROM correspondence, not checked beside it -- theorems,
               not acceptance conjuncts, so the independence
               obligation is satisfiable for them.

  TAXONOMY     Each failing arm propagates its EXACT class and reason.
               Stated per constructor, because "propagates its class"
               is the claim that must not be approximate.

One theorem was WRONG on the first pass and the compiler caught it: I
had claimed correspondence's class always agrees with the derivation's
class. It does not -- a successful derivation whose bytes mismatch is
invalid, so the derived arm downgrades valid to invalid. That
asymmetry IS the comparison's purpose. Replaced with
correspondence_valid_iff_bytes_match, which states it exactly.

Ruling H is formalized rather than promised:
core_does_not_discharge_token_policy exhibits an input satisfying the
core predicate while the complete predicate fails. The module cannot
be read as claiming C3 closed a dependency it does not own.

Mutation controls executed, not asserted. Both produced the strongest
available result -- the kernel proving the NEGATION of a named
theorem, not merely a broken proof:

  strip the count prefix   -> canonVault_separates_the_samples proved
                              FALSE; canonVault_injective falls back
                              on sorryAx
  remove the comparison    -> preserved_field_mutation_is_rejected,
                              encumbrance_introduction_is_rejected,
                              budget_introduction_is_rejected and
                              market_successor_not_accepted_under_close
                              each proved FALSE

The encumbrance and budget controls matter most: beta's E is always
empty and no Allocation type exists in Rust, so no beta-shaped test
can exercise Req 8.1 at all. Stated at the model level or not tested
anywhere.

Axioms reported per theorem, never as a blanket claim. No result
depends on sorryAx or Classical.choice; six depend on no axioms at
all. propext and Quot.sound are Lean's logic, not assumptions added
here.

Gate: all 12 modules pass lean -DwarningAsError=true.
A read-only survey of the Rust surface, run before any Phase D code was
written, found three defects in this amendment. Two of them were
confident, specific and wrong. Correcting them here rather than merging
and issuing an erratum: this is the freeze point, and a normative
document should not enter the record carrying a false factual claim.

1. THE PRODUCTION-DEBT STATEMENT WAS FALSE.

   The draft said vault_state_composition.rs:572 "binds the bundle's
   declared successor and discards it ... Both values are live at that
   point."

   There is no declared successor on the wire at all. successor_ccb is
   32 bytes that do not commit the successor state: production market
   bundles write the route-set commitment x (dlv_routes.rs:3249, whose
   own comment says "there is no such commitment to name, so it carries
   the trade identity and nothing reads it as a successor"), and
   production closes write x_close (settlement_bind.rs:161), a pure
   function of (vault_id, parent_generation). Both composition arms
   derive the successor locally.

   So VDS.COMMON.10.a is not merely unimplemented -- it is
   UNIMPLEMENTABLE against the current encoding, and is now marked:

       NORMATIVE               YES
       FORMALLY SPECIFIED      YES
       PRODUCTION IMPLEMENTED  NO
       BLOCKED ON              the 2c-A canonical encoder cut

   with the exact blocker, the owning prerequisite, and what unblocks
   it. Blocked must not mean accepted-without: production may not
   report ValidDlvSuccessorCore = VALID for any path whose validity
   requires the conjunct until the byte comparison is actually
   performed. That gap is implementation status
   (BlockedOnCanonicalSuccessorEncoding), never a protocol reason code
   -- the INVALID/INCOMPLETE/SAFETY_VIOLATION taxonomy is not
   contaminated because a prerequisite has not landed.

   Also recorded: successor_ccb cannot simply be repurposed.
   is_close_transition derives BundleShape from exactly that field, so
   writing a real successor commitment into it silently reclassifies
   every close bundle as Market. The shape discriminator must move
   first, and that is 2c-A's to do.

2. RULING J NAMED THE WRONG OBSERVATION TYPE.

   The draft called binding_observation "C2's four-valued
   CellObservation, unchanged". Two distinct types exist over two
   distinct key spaces:

       CellObservation     economic register cell     FOUR arms
       BindingObservation  DLV parent-binding slot    FIVE arms

   The settle path reads the five-valued one. All five arms are now
   mapped normatively:

       Free          -> INVALID          NO_BINDING_ESTABLISHED
       BoundFinal    -> proceeds to VDS.COMMON.10.a
       Conflict      -> SAFETY_VIOLATION DUPLICATE_BINDING_FINALITY
       Undetermined  -> INCOMPLETE       BINDING_UNDETERMINED
       Unavailable   -> INCOMPLETE       BINDING_EVIDENCE_UNAVAILABLE

   Two of those are easy to get backwards and the defining module says
   so. Undetermined is neither emptiness nor forgery -- two quorums
   intersect, but one READ need not see the intersection, so a value
   chosen behind a down member lands there; reading it as Free composes
   past a live bind, and reading it as invalid "would make every
   concurrent settle permanently invalid". Free is INVALID rather than
   INCOMPLETE because a quorum of authenticated explicit absences is
   positive evidence that nothing is chosen.

   The Lean module was corrected first and the document follows it. Two
   new theorems pin the arms that invert:
   undetermined_is_incomplete_never_valid_never_invalid and
   free_is_invalid_not_incomplete.

3. THE CORRESPONDENCE CONDITION NEEDED A BYTE-LEVEL STATEMENT.

   Semantic successor equality and canonical-byte equality come apart
   here. decode_vault_state NORMALIZES rather than refuses --
   StorageSetMembers::new and EncumbranceSet::new both sort, rejecting
   only duplicates, never bad order -- and CCB has no decode/re-encode
   equality check, though ~10 sibling modules apply exactly that
   discipline and one names it "the settlement-wire discipline". So two
   byte strings decode to one VaultStateV2 and a round-trip comparison
   launders non-canonical input.

   The acceptance condition is now stated as equality of canonical
   BYTES, with the round trip permitted only under the frozen normative
   encoder -- which does not exist: repo-wide there is exactly one
   `fn canon`, and it is protobuf. Phase D must build it.

Ruling E additionally now forbids mapping a FAILED SIGNATURE to an
absence, because the shipped code does it deliberately:
fetch_verified_receipt discards a failed SPHINCS+ receipt verification
with .ok()?, which becomes MarketRealization::Absent, breaks the fold
and returns Ok(...) -- so a forged receipt is not an error at all. Its
doc defends the collapse as sound about FOLDING, which it is; it is
wrong about CLASSIFICATION.

The amendment carries a "Corrections made before it merged" section
recording all three rather than silently rewriting a circulated draft.

All 12 Lean modules pass lean -DwarningAsError=true. No result depends
on sorryAx or Classical.choice.
@cryptskii
cryptskii merged commit 0e0f795 into main Sep 9, 2026
19 checks passed
@cryptskii
cryptskii deleted the docs/amendment-2c-c3-valid-dlv-successor branch September 9, 2026 00:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant