Skip to content

Beta completion: DSM core, SoFi and offline software, with §6.95–§6.116 records - #1158

Open
cryptskii wants to merge 204 commits into
mainfrom
docs/conformance-recheck-2026-10-26
Open

cryptskii wants to merge 204 commits into
mainfrom
docs/conformance-recheck-2026-10-26

Conversation

@cryptskii

@cryptskii cryptskii commented Oct 26, 2026 •

Copy link
Copy Markdown
Collaborator

Owner, 2026-10-26: "ALL remaining beta work = ONE PR: mass code first, CI once." This PR carries §6.95 (the recheck it opened with) and all the beta completion work built on it: the DSM core, SoFi and offline software that needs no further owner ruling. Nothing here was left as a stand-in; where something is not built, the record says so.

What is in it

Requirements and records. CONFORMANCE_GAPS.md §6.95–§6.116, VERIFICATION_MATRIX.md, and pin updates in MASTER_REQUIREMENTS.md. Totals after this PR: 662 Met, 90 Partial, 82 Missing, 1 Violated, 70 Not code, 154 Deferred, of 1,059 rows.

SoFi and DSM core.

  • A7, §6.113: a producer publishes F only once its conformance is Valid (SoFi L1738). A trader fulfillment is frozen withheld, and the republish sweep never puts it out early (client schema 35).
  • A6, §6.111: every root the SDK used now comes from Core: the setup body, the leaf cache behind a resolved root, the producer's tree, the witness roots, the owner's vault tree, and DSM Connect's holdings proofs.
  • MR-SOFI-0260: a producer refuses a route that does not chain, tested on the nodes.
  • Question 9, §6.100: Core's write set refuses a Transfer or Burn its token's policy forbids.
  • MR-DSM-0248, §6.112: a field the 4-byte length prefix cannot frame is refused and never hashed.
  • MR-DSM-0332, §6.104: a spool copy consumes a message id only for what this device verified and its content names.
  • §6.106: reserve finality is re-proven from kept reads. A vault chain is extended only by Core's walk, the device's record follows Core and reports every replacement by name, and the latest authenticated owner baseline is adopted (MR-DSM-0331).

Offline account, phases 2–4 (§6.103).

  • The appliance on a software board, the payer's chain of spends, and receipts held pending and settled online.
  • The offline door (MR-DSM-0219): Core's door is an exhaustive match that admits only the bearer Transfer.
  • Noop: refused in Core's advance (MR-DSM-0201, its Noop half).
  • Bearer evidence: mandatory in decide_confirm.
  • The trace harness: its confirm cases now run as SDK tests on real bearer pairs. The Tripwire and DSM.tla replays into the code are removed and recorded as gaps; DSM.tla's NetDeliver had been replayed through the offline confirm rather than the online path it models.

Verification debt paid.

  • Mutation controls for the shared-lineage, vault-history, baseline and contacts gates (§6.106), for DSM core gates checked only by reading (§6.108), and for properties §6.96 left untested (§6.110).
  • Named tests for rows that had none (§6.107), and an item on a previous-tip route never taken in (§6.114).
  • Core-alone bounds: Core refuses every token bound the SDK checks, at its edge and one past it (§6.115). Five of those bounds had no test before.
  • Key derivation: K_root's position and genesis components (§6.116).

Frontend. The generated dsm_app_pb.ts now matches the proto (four messages had been missing). Jest tests count as conformance evidence (§6.102).

How it was verified (locally, before this push)

  • Core: cargo test --release -p dsm --no-fail-fast, 1,888+ passed, 0 failed over 23 binaries.
  • SDK, by module, one test at a time on Postgres:
    • every SoFi node module: node_e2e_tests 77, frontier_verification_tests 13, escrow_e2e_tests 10, computed_escrow_e2e_tests 6, connect_escrow_e2e_tests 5, connect_duel_e2e_tests 2;
    • bluetooth 162, storage::client_db 320, recipient_dispatch 23, and the offline modules;
    • bilateral_reject_tests, ble_smoke and source_rules.
  • Trace harness: tla-check passes all 95 specs, plus property-tests (seed 42, 5 iterations), implementation-traces and adversarial.
  • Android: cargo ndk arm64 release of the JNI library; :app:testDebugUnitTest 274/0; assembleDebug and assembleDebugAndroidTest.
  • Frontend: type-check, lint, Jest 967/967, and the production build.
  • Checks: make lint, ci/production_safety_checks.sh, python3 ci/conformance_evidence.py, and the real-code guard against origin/main.
  • Mutation controls: every new security gate was run with one DSM_MUTANT build in a scratch worktree, with the unmutated control green and the named test red.

The full SDK suite was not run locally, per the standing rule; this PR's CI board is that run. The intent pins are refreshed from this run's code map afterwards.

Not in this PR: waiting on the owner

  • The 13 §6.105 rulings and the 7 §6.101 storage decisions. The exact spec text for each is drafted, unapplied.
  • The ten offline decisions in §6.103, and whether walks get an in-process cache.
  • Six dead Bitcoin JNI exports (Bitcoin is out of this round).
  • Where the wallet shows value received offline before it settles.
  • Then: the fleet redeploy and the full protocol run on phones. Hardware is last.

… (§6.95)

Owner, 2026-10-26: recheck all 314 rows against current main first
(read-only), update the record, then open one session per area on what
is actually left. 791 of §8's 914 rows carried no date.

All 323 rows recorded Partial, Missing or Violated were rechecked at main
ad831f5 by read-only agents, one per batch of spec sections. A test counts
only if it passed in main's last green CI run (6b2eb2d, the same Rust
code). Every cited test was checked against that run, and every name was
converted to the canonical names ci/conformance_evidence.py resolves. The
gate passes statically and against that board log.

Before: Partial 204, Missing 116, Violated 3. After: Met 90, Partial 148,
Missing 82, Violated 3. One row was recorded better than main
(MR-DSM-0202, cited a deleted module). Of the 233 not Met: 143 are out of
beta by the owner's 2026-09-23 rulings or out of scope; 20 need code in
beta; 38 need only a test; 14 need an owner ruling; 10 await the owner's
confirmation under the G3 ruling; 8 are blocked by Open storage-spec items.

§5A's SoFi routes row said close fails after a trade; main closes a
traded-through vault on the nodes. The row is corrected and stays
unticked on the relay.
…tip check before any read, drop unused CanonicalEncode

MR-DSM-0191: Core's validated advance refuses a Burn of a token whose
committed policy does not let it be burned (AssetNotBurnable), as it already
refuses a non-transferable Transfer; one helper reads the committed policy
for both conjuncts.

MR-DSM-0018: a refused countersign artifact records nothing. The
awaiting_valid_reply status, its setter and its store tests are removed; the
step stays submitted and a valid artifact for the same commitment finalizes
it.

MR-DSM-0029: the receiver checks the signed parent against the sender head it
pinned (the genesis seed before any step) before prevalidation reads the
sender's register; a pinned head equal to the signed child is a duplicate the
apply reports.

MR-DSM-0100: the CanonicalEncode trait had one implementation, on the legacy
State compat type, and no caller; deleted.
…canonical_rebuild

The realization predicate is the only way a device head comes into
existence. StateMachine now holds and installs only a RealizedHead, a
wrapper with a private field that only Core builds: the genesis head
(DeviceState::genesis_head), a head restored from this device's own
persisted encoding (restore now refuses unless the rebuilt root is the
stored root), and the transitions on a realized head (advance,
establish_relationship, the pending-admission fence, a resolved SoFi
position, opening an offline account, an offline boundary). DeviceState's
own state-producing methods are crate-private, so the SDK cannot build a
head and install it.

Removed with it: StateMachine::set_state (it only re-seeded a compat
anchor and its one caller ran before any head existed), the &mut setters
bootstrap_legacy_root and set_genesis_digest, and the SDK's genesis patch
of an existing head (a head under another genesis is now refused). The
canonical_rebuild module had no caller outside its own tests and is
deleted (MR-SOFI-0281's second half).

dsm_sdk/tests/one_door_for_device_heads.rs reads Core's source and fails
if the door is reopened.
…dead items, relay rebuilds an exercise

- MR-SOFI-0312: Core releases a creator's supply only to a device the policy's
  recipient allowlist admits (TokenPolicy::admits_issuance_to, checked in
  verify_genesis_release); token.create refuses an allowlist that leaves the
  creator out before anything is written. Tests: Core refusal and acceptance,
  free trade once issued, the SDK creation refusal.
- MR-SOFI-0337: the reserve starts at ERA's committed genesis supply
  (reserve_genesis_supply; ERA_RESERVE_GENESIS_SUPPLY removed), and the faucet
  asks Core's faucet_claim_admissible at the reserve head before it signs or
  freezes a release (ReleaseRefusal::Exhausted). Tests: a reserve drained to
  zero refuses the claim; the claim flow asks before it signs (source order).
- MR-DSM-0191: a counterfeit burn of a burn-disabled token, built by the
  payer's own builders, is refused by a new receiver's walk.
- MR-DSM-0029: a transfer forked from a head the receiver moved past is
  refused before any cell of the sender's register is read.
- MR-SOFI-0268/0273: verify_signed_object, SignedSofiBody,
  PolicyFulfillmentAuxRef, last_head and STORAGE_MEMBER_COUNT deleted (no
  production caller, rule T1); G1's baseline holds no entries.
- MR-SOFI-0281: a source test holds every SoFi producer to admitted_advance.
- MR-SOFI-0266: the relay rebuilds an exercise no leg key holds from the
  position's public objects once its pair is registered (Amendment S25), and
  carries it; RecognizedExercise keeps its bytes.
…ence; fork and seed tests

- MR-SOFI-0241: a leg's parent the vault's chain does not name is placed at
  its generation by the state leaf the exercise's own core proves under the
  parent root (validation::generation_at, the leaf read by its value), so the
  ground facts refute an orphaned parent before any validation evidence is
  acquired. Test: Orphaned on the proven leaf; Unavailable on an unproven
  leaf or none.
- MR-DSM-0029: the forked-parent test forks two steps deep and signs with
  A's own wallet signer, so its receipt chains to the key B holds; B refuses
  it on the pinned head before any cell read.
- MR-DSM-0082, MR-SOFI-0078, MR-SOFI-0126: position seed, root register key,
  reserve seed and the escrow seeds pinned against an independent hasher and
  b3sum digests.
- MR-SOFI-0105: no tag reuses a retired domain (registry and source scan).
- MR-SOFI-0075: the draw counter's step is its own function; exhaustion at
  u32::MAX is a defined error.
- MR-DSM-0020/0102/0245/0095: establishment order is not state, and a
  relationship's step depends only on its own chain, however late it lands.
… the SoFi and storage invariants

- MR-SOFI-0017: NativeReserveState's fields are Core's. A reserve state at
  generation g is derived from its genesis (at_generation: every release is
  exactly the claim policy's payout), and the SDK's memo names generations
  and refuses a row whose remaining supply the lineage does not hold there.
  FundedCredit's fields are Core's, read through accessors.
- dsm_sdk/tests/source_rules.rs, one binary for every source rule:
  the faucet asks before it signs (MR-SOFI-0337); SoFi producers advance
  only through admitted_advance (MR-SOFI-0281); no SoFi rule reads a clock,
  height or duration (MR-DSM-0218, MR-SOFI-0056); evidence is never
  defaulted (MR-SOFI-0274, MR-SOFI-0018); every evidence field is consumed
  by its predicate (MR-SOFI-0020); no Core state has a public field
  (MR-SOFI-0001/0017/0023); only route_chain reads a cell's seats
  (MR-STOR-0020).
- dsm_storage_node/tests/storage_is_blind.rs: the node names only framing
  modules, signs nothing and names no application or authority concept,
  with a negative control (MR-DSM-0051, MR-STOR-0011, MR-SOFI-0286).
- MR-DSM-0216: a value the set saw first holds nothing unless first at the
  leader. MR-DSM-0050: a step of one relationship never advances another.
- MR-SOFI-0284: setup, vault creation and fulfillment are each one
  deterministic transition with one entropy, driven through advance.
- MR-SOFI-0276 (Gate G4): every sampled evidence byte changed is refused, and
  a changed P(E) byte recomputes another E. MR-SOFI-0275: the vault-leaf row's
  missing half.
- MR-DSM-0101: route entries, the online transfer request and a transfer's
  canonical bytes match independent encoders.
- MR-DSM-0215: the same vault states choose the same route in any order.
- MR-SOFI-0092: a position's two cells go to each seat in one put.
- MR-SOFI-0254/0260/0272: source rules hold the producer to Core's check
  before it signs, freezes or publishes, and PrecommitDraft to check_draft.
- MR-SOFI-0261: a trade whose objects cannot be Stored stops before its
  transition (node harness: refuse_object_writes).
- MR-SOFI-0346: resolving another trader's position reads nothing of its
  lineage at or below the verifier's frontier but the named setup position.
…nterpart that does not mirror, and a locked wallet (MR-SOFI-0396)
…eneration (MR-SOFI-0410)

The owner's duty in SoFi Amendment S24: every sync walks each vault this
device created on from what it recorded and publishes a baseline at the head
when none is published at that generation, as Connect's offer already did on
demand. A baseline left unpublished is reported in the sync's errors; it
costs a fresh reader speed and never an admission. The read-set test now
names every position of the trader the verifier read.
…lk does not reproduce (MR-SOFI-0401)

Step 4 of a fresh reader (SoFi Amendment S23): once the walk has
established a checkpoint's segment, the checkpoint is compared with the
digest and root the walk computed at its end. One the walk does not bear
out is dropped and its roots name nothing more; a hint keeps naming its
root, and a checkpoint whose segment is not established yet is left
standing. The reserve and vault walks settle their checkpoints before
owing anything. GenerationChain::head_at gives the head at a pushed
generation.
…he receiver's record; vault checkpoints (MR-SOFI-0346/0347, MR-SOFI-0399)

Frontier (MR-SOFI-0346, MR-SOFI-0347; DSM Amendments A8, A19):
- resolving_another_traders_position_reads_only_its_segment found that a
  receiver whose frontier for a trader stood past the trader's setup
  position replayed the trader's history from its activation root to read
  the setup's claim (peer_claim_at looked only for a frontier strictly below
  the position). A walk now records a frontier at every position it
  validates, each with the digest of the claim it accepted there, and
  peer_claim_at answers a position the receiver validated from that record,
  reading nothing behind the frontier. AcceptedClaim::from_validated_coordinate
  is the constructor for such a recorded coordinate. peer_frontier gains
  claim_ref; client schema 34 (beta does not migrate).
- Test: a_claim_recorded_at_the_position_answers_and_nothing_is_read.

Vault checkpoints (MR-SOFI-0399, SoFi Amendment S23 "who writes"):
- vault_post_states takes F and each VaultPostState carries its generation's
  bundle step (attempt F fixed for the vault, E, the trader's (G, DevID, q),
  the route's other legs), derived by Core from (P, F); an F of another P or
  one fixing no attempt for a leg is Invalid.
- The step is recorded with the generation (sofi_vault_step). A vault walk
  owes the checkpoint and bundle of every complete segment nobody published;
  the trader whose position realizes generation g owes g's hint and, at a
  multiple of 32, the segment's checkpoint and bundle, after the position is
  committed and never on its path.
- Tests: each_vault_generation_carries_the_bundle_step_its_exercise_derives,
  a_generations_bundle_step_is_recorded_once_and_read_back_exactly,
  the_trader_that_realizes_generation_32_publishes_its_segments_checkpoint.

Test fix: an_online_owner_publishes_a_baseline_after_each_realized_generation
recognized candidates under the frontier commitment instead of the locator
the frontier names, so it kept none; it now recognizes as the wallet's reader
does.

Each touched test passed in its own targeted --release run.
…under another Y (MR-SOFI-0396)

Mutation found the lock test weak: dropping the commitment comparison from
mirrors left it green, because its other-match counterpart differed in the
table too. A counterpart's terms are its own, so one that keeps this match's
table and branches under another Y binds another match cell. The test now
locks against such a vault and is refused. create's vault creation is its own
function, create_vault, which create calls and the test uses as a hostile
counterpart would.

Each computed escrow test passed in its own targeted --release run.
…zed heads; lint

make lint was red. The one-door change made DeviceState's state-producing
methods crate-private, and tools/vertical_validation still built heads with
them: the tool now holds RealizedHead, starts from DeviceState::genesis_head
and steps through the realized head's own transitions. Clippy: a Copy leaf
is copied, not cloned; the SoFi transition cases have a named type; the
one-door source test unwraps with let-else, not Option::expect.

make lint passes. vertical-validation implementation-traces and
property-tests --iterations 5 --seed 42 pass. The touched tests passed in
their own targeted --release runs.
…ame commitment (MR-DSM-0185)

DSM §60, three parties, two chains, one commitment: Alice and Bob each fund
a vault for Carol under the same delivery Y, each released by its own
funder's acknowledgement. Bob refuses; Alice's vault binds its own verdict
cell, its verdict is final and Carol is paid from it, while Bob's cell holds
nothing and his vault releases nothing until his own refund settles it.

The test passed in its own targeted --release run.
…s it (MR-SOFI-0007/0110/0134)

The test these rows cited went with verify_signed_object, which had no
production caller. The production recognizers now carry the evidence: each
names a setup, a precommit and a fulfillment by the identity its body
derives, and an envelope over the same body with another signature is not
recognized at all, so no envelope names another object.

The test passed in its own targeted --release run.
ci/sofi_reachability_negative_control.sh copies G1 and its helper, unchanged,
into a scratch tree with the workspace's layout whose one public SoFi
function has no production caller, and requires G1 to fail naming it; once
production code calls it, G1 must pass. production_safety_checks.sh runs it
after G1. Run here: the control holds, and G1 passes on the real tree (180
public functions reachable, an empty baseline).
…dent encoder (MR-DSM-0100/0101)

The receipt's canonical ReceiptCommit bytes, which both parties sign and the
receipt commitment hashes, are encoded here from the proto definition with no
production encoder and compared byte for byte: the seven roots and tips,
dev_proof, the transition entropy and the step's writes, and none of the
wire-only signatures, certificates, ephemeral keys or Kyber ciphertexts.

The test passed in its own targeted --release run.
The lock test now covers every mirror check: a counterpart that is B's own
vault under A's terms (not the opponent's), one whose a-wins branch pays B,
one in another token A holds, and a stake A withdrew and took back (no longer
Active), beside the other match, the other Y and the other amount. No refused
lock moves B's ERA.

The test passed in its own targeted --release run.
…nd mutation controls (§6.96)

61 §8 rows rewritten. Of §6.95's 20 code rows, 13 are Met, 3 Partial with
the remainder named and 4 await an owner question; of its 38 named-test
rows, 36 are Met and 2 Partial. 42 mutation controls, each a named test
observed red with its gate removed, are in the verification matrix gate by
gate. Found on the way: a verifier replaying a trader's history to read a
setup's claim (fixed; MR-SOFI-0347 had been Met), a lock test green without
its commitment check (strengthened), MR-SOFI-0275 overclaimed by §6.95, and
make lint red on tools/vertical_validation (fixed).

ci/conformance_evidence.py passes; totals regenerated.
…the reserve (MR-STOR-0045)

What a reserve-funded credit decides from its descriptor alone — the
network's canonical reserve, a generation past the genesis — is decided
before the reserve's lineage is read: a counting resolver shows no read for
either refusal, and one read for the canonical release, which funds.

The test passed in its own targeted --release run; reading the release
before the canonical-id check turns it red (mutation run, scratch tree).
…e checks first (§6.96)

Transfer acceptance, SoFi route checks, vault walks and now reserve-funded
credits each decide what they can in hand before reading a cell; the
mutation of the reserve credit's order is in the verification matrix.
ci/conformance_evidence.py passes; totals regenerated.
Run on the node harness: one online transfer, every object and cell each
node newly held listed. Besides the sealed spool, each side publishes its
root claim, manifest, witness and successor evidence, with the receiver's
authority evidence, acceptance bundle and release, and an ek-cert step each;
every one has a reader in verification, and the memo, nonce and ticker are
in none. Together they reveal balances, counterparties, amounts, tokens and
relationship tips to anyone walking an identity's lineage: that meets A7's
spool-scoped privacy and not the explainer's general sentence, recorded as
owner question 7 (§6.96).
…te chains (G15)

The reserve and successor-cell models, in TLA+ and Lean, still proved the
copy rule: Final was the leader's value held by two other members. A
further link is now a copy carrying the value's leader record, so a copy
written before the value held the leader counts for nothing.

- DSM_NativeReserveRelease and DSM_SofiSuccessorCells count chain links;
  each gains FinalRequiresChainLinks and a _CopyRule falsification that
  counts early copies and must fail it. TLC: all 96 registered specs pass.
- DSMNativeReserve.lean and DSMSofiSuccessorCells.lean count only links;
  a bare copy is none. Counting one turns the new theorems red.
- DSMRouteChain.lean proves final_survives_two_losses, which had been
  only stated. Three lost seats, or Final at two links, turn it red.
- DSMTokenIssuance.lean had the creation fee as 10 where Core charges
  1,000 base units, and accepted any fee; it now refuses every fee but
  the protocol's.
- Records: §5A G15 done, MR-STOR-0122-0127/0143/0144 notes say what is
  proved and what has no model, two matrix rows, and the axiom ledger.
…ands its units (MR-SOFI-0311)

A run over the real nodes showed the owner's own device accepting a vault
creation over a token whose policy forbids transfer: both reserves are
debited, then every verifier refuses the vault's genesis, the owner's
own close included. MR-SOFI-0311's note said vault creation was checked;
it now says what the run showed, and where the owner's creation is
refused is owner question 8.

The SoFi routes row's two backend reasons are closed (the relay carries
a route that is not final; MR-SOFI-0266 Met); it stays unticked for the
frontend screens it names.
…ed before anything is debited (MR-SOFI-0311)

An owner's market or escrow vault over a token whose committed policy
forbids transfer used to be admitted on the owner's own device: both
reserves were debited, then every verifier refused the vault's genesis,
the owner's close included, and the units were stranded.

SofiVaultCreate and EscrowVaultCreate now carry the committed
TokenPolicyV3 bytes of each funded token that is not pre-rooted, under
the owner's signature. Core's write set re-hashes each to its token and
applies SoFi §49's market-leg rule before any write set exists, so the
device's own admission and every verifier refuse the creation before a
debit, whoever produced it. The SDK asks the same Core check first,
before anything is published or signed.

Tests run the defect end to end on the nodes, through the route and past
the SDK's check, for market and escrow vaults. Seven mutants (the Core
gate, its re-hash and count, the signature coverage, the SDK's early
check, and the existing genesis and route refusals) each turn named
tests red. Records the owner's rulings on questions 1-8 and a question 9
from the reserve-locking review.
…olds one token per ticker (SoFi Amendment S28, MR-SOFI-0305)

Owner ruling 1 (2026-10-27): keep SA-148's device-local protection and
amend the specification to tell global ticker non-uniqueness apart from
device-local enforcement. Token identity stays the policy commitment.

SoFi Amendment S28 states both rules. MR-SOFI-0421..0423 are derived
from it, the SoFi pin moves, and MR-SOFI-0305 is Met: the SDK's refusal
of a second token under a held ticker is the device-local rule, not a
violation of display-only tickers.

A new test has two devices each create TWIN; both stand as two tokens,
and each device refuses the other's TWIN and a second creation of its
own, with its ERA and admitted position unchanged. Removing either
refusal turns the named tests red. MR-SOFI-0423 stays Partial for the
BLE receiver's ticker check (offline, done last).
…y its secret's source differs (MR-DSM-0272)

Owner ruling 2 (2026-10-27): check whether the per-payload encapsulation
already satisfies A7 before changing any wire. The step's Kyber exchange
and the spool seal encapsulate to the same recipient identity key, read
from the same contact field, and the recipient opens both with one
secret. End-to-end confidentiality, a domain-tagged key never reused, an
authenticated cipher and ciphertext-only nodes all hold; the per-payload
secret is finer than per step and keeps the fixed nonce safe under a
re-seal. No wire change. MR-DSM-0272 stays Partial until A7's wording
is settled (owner question 10).
…tate; the established head is for lineages with no owner (MR-DSM-0315)

Owner ruling 3 (2026-10-27): treat owned vault lineages as refined out
of A15's established-head requirement by S24, document the scope
precisely, and keep the cryptographic validity and lineage requirements.

DSM Amendment A15 gains that scope in the owner's words: SoFi market,
escrow and computed escrow vaults read S24's composed state from the
latest authenticated owner baseline, holding and fetching no generation
below it; the established head stays the boundary for the native
reserve. Induction above the baseline, discovery without authority, the
walk from the genesis without a baseline, and quarantine on differing
frontiers are kept.

MR-DSM-0315 is restated for lineages with no owner and stays Partial
(the faucet builds on the walk's stop, not on the head type);
MR-DSM-0331 is added, Met on the S24 and S26 end-to-end tests, each run
alone. Also orders §6.96's questions, the owner's rulings and what was
done under each.
…hods and items (MR-SOFI-0273, MR-SOFI-0268)

Owner ruling 5 (2026-10-27): extend G1 to public SoFi methods using
compiler-resolved identity, not text matching, and prove the gate fails
on a method that lacks a production caller.

ci/sofi_reachability.py now indexes the workspace with the pinned
toolchain's rust-analyzer (SCIP, read by a small hand-written reader)
and counts a caller only where name resolution and type inference
resolve a reference to the function. It checks every public SoFi
function, inherent method, struct, enum, const, static, type alias and
trait; uses inside an item's own definition and impl blocks do not
count. The whole tree: 812 of 812, baseline empty.

The 41 public functions and methods it found with no production caller
are resolved: 21 referenced nowhere are deleted; accessors only tests
read are gone (the tests read the fields), PreEClosureIndex::with is
cfg(test), EscrowKind::encode and Validation::and/all are deleted; the
11 standalone decoders of objects production parses only inside a
parent are deleted, their round trips moved onto the production parsers
(sofi::wire::objects::parse_tests); VaultChain::roots is deleted and the
SDK tests read the accepted genesis root and the chain's head.

The negative control plants an uncalled public SoFi method beside a
called SDK method of the same name, and an unused public type; the gate
names both and passes once they are used. It runs as a cargo test, and
mutants that skip methods, skip items or credit references by name each
turn it red. rust-analyzer and rust-src join the toolchain file and the
CI jobs that run the gate or the test.
…its debit behind a registered claim (question 9)

Ran on the real nodes, in a scratch build whose only change removes the
SDK's pre-transition policy check: A's send of a non-transferable token
commits the debit, registers A's root claim on all five seats and
publishes its evidence, and only then is refused by Core's
advance_validated. The admission stays held and A's next send is
refused. Question 9 now carries the run in place of a reading.
cryptskii and others added 29 commits November 1, 2026 00:53
…nd Bluetooth stubs are gone

SA-178, the lesser items left after the firmware and appliance fixes.

- boundary::contain_core1 replaces the no-op assert_core1_contained. It
  writes PSM.FRCE_OFF.PROC1 (rp235x-pac: bit 24), reads it back, and waits a
  bounded number of reads for PSM.DONE.PROC1 to clear. The monitor halts
  before the boundary, and before anything Non-secure, if either is not
  confirmed. Built for the RP2350 with and without `production`, 0
  warnings; not run on silicon.
- The SIGMA_CHIP_SELFTEST switch, its branch in the reset path and
  tropic.rs with its fixed dev seed [0x11; 32] are deleted. The pin map's
  source of truth is anchor_glue.rs; the README and wiring sheet point
  there. The deleted file's nine baseline lines go with it.
- The bench banner prints the harness build's own debug_assertions and
  commit; hw=production, which the host could not know, is gone.
- post_bilateral_event_to_webview_jni exists only where the JNI bridge
  does, and the event callback is installed only there. The non-JNI Android
  and non-Android variants, which answered Ok(()) and delivered nothing, are
  deleted; a delivery failure is logged as one.

resolve_prepared_on_reattach, the raw-SPI MISO relay and the Pico image went
in bcfd259, 3585aad and 163101c. CONFORMANCE section 6.130.

Runs: monitor firmware builds (release, plain and production); dsm_sdk
cargo ndk check for arm64-v8a with jni,bluetooth; make lint passes.
… the Start

SA-140 (owner's choices 2026-10-31: "Rules-checked + commit-reveal", and
"Start carries reveals"), as SoFi Amendment S33. The duel's tiebreak seed
was H(nonce, key_a, key_b): the side supplying its key second could pick
keys until the seed favoured it.

Core (SoFi section 19.10):
- The computed table carries each side's start commitment
  H(DSM/escrow/start-commit/v1; r). ComputedEscrowTerms, TranscriptOutcome,
  EquivocationProof and MatchStart go to schema 2; schema 1 is burned.
- A Start carries both reveals and is recognized only when each opens its
  side's commitment; assemble_start refuses otherwise
  (ComputedRefusal::RevealOpensNothing). A transcript outcome carries both
  under the same check, so the match cell is still recognized from its own
  bytes.
- OutcomeProgram::outcome(setup, reveals, opened).
- Tags DSM/escrow/start-commit/v1 and DSM/escrow/start-entropy/v1 (430).

Wallet:
- escrow_start_entropy: an Expand under the wallet root beside the session
  key, derived when used, never stored. connect.duelSessionKey answers its
  commitment; the lock refuses a setup whose commitment for its side is not
  its own; ready hands out the entropy with the ready signature; the side
  readying second refuses an opponent reveal that opens nothing before it
  signs, and writes the Start with both.
- A kept match's rules progress starts when its Start is final (client DB
  schema 41).

Program (wildstate-duel, rules version 2):
- DuelSetupV2 (schema 2) carries both commitments and no seed;
  match_of(r_a, r_b) checks the openings and derives
  H(DSM/wildstate-duel/tiebreak/v2; nonce, r_a, r_b, keys).
- Each conformance vector carries both reveals and must be seeded by them,
  so the seed rule is in what P pins. Vectors refrozen (the even/odd parity
  vectors on reveal bytes that give each parity); P re-pinned. The SDK runs
  every vector through the program as Core calls it.
- The wasm bindings take commitments and reveals; startCommitment added for
  matches with no stake.

The side readying second sees the other's entropy before the Start and may
decline: then either side withdraws and both stakes are refunded.

Tests: computed::tests::a_start_or_an_outcome_whose_reveal_opens_nothing_is_not_recognized;
golden::a_staked_matchs_seed_is_what_both_opened_reveals_derive;
computed_flow::tests::a_start_commitment_is_the_wallet_roots_expand_for_the_match;
computed_escrow_e2e_tests::a_lock_is_refused_for_a_foreign_session_key_or_a_counterpart_that_does_not_mirror
(another wallet's commitment refused at the lock).
Runs: dsm sofi::computed, ccb, key_schedule, domain_tags, write_set,
publication, lineage, validation 234/0; wildstate-duel 37/0; dsm_sdk
outcome_programs, computed_flow, computed_escrow_e2e, connect_duel_e2e,
connect::grant, duel_matches 52/0 over two runs; outcome_program_bounds 3/0;
root workspace check clean; conformance evidence gate passes; make lint
passes.
…im is registered

SA-94. finish_locked registered the root claim, which cannot be undone, and
only then ran the verifier, whose credit checks read the paying side live; a
resume ran it again. A read that stopped answering for good after the
registration would leave the admission unfinishable and the lineage fenced.

- advance_validated runs, through the live resolver, on the exact frozen
  claim envelope before it is registered. A refusal registers nothing.
- Once it accepts, the admission is EconomicAdmissionState::Validated,
  durably (store code 5), between EvidencePublished and Registered, and
  fencing like them. Registration, finality and admission follow from the
  frozen claim; the admitted position, root and claim reference are the
  registered claim's.
- finish_locked reads how far the admission got from the stored row, never
  the head's copy (which keeps the state the fence engaged at), so a resume
  in the same process skips validation as one after a restart does. A row
  naming another admission is a stop.
- No state moves back on a resume.
- NodeSet::erase_cell: a set whose stores all lost a cell.

The live resolver reads content-addressed evidence and leader-held claims,
so no way was found for a sender to change a position it paid from; the fix
does not rest on that.

SA-112 recorded Fixed: concurrent copies are stopped by the durable
compare-and-swap (two_copies_of_a_bearer_response_taken_together_release_once),
and a copy after a failed release settles through recovery with the counter
moving once (a_release_cut_short_is_settled_live_when_the_response_comes_again).

Test faucet_flow_tests::an_admission_the_verifier_accepted_lands_without_reading_its_source_again:
a claim held at the root cell's leader is Validated; the release it consumes
is erased from every member and the device forgets its walk; the resume still
admits position 1, credited once.
Runs: dsm_sdk faucet held-claim tests 4/0; frontier, recipient_dispatch,
sender_admission, counterfeit, offline_step_tests, economic_admission 129/0;
conformance evidence gate passes; make lint passes.
…s sign for real

SA-175: acceptance steps 13, 16 and 17 checked nothing. delivers_to_receiver
repeated step 6, is_genesis was false at every construction site, and
anchor_uncompromised was set on every pin. The steps, their flags, the
uncompromised column (client DB schema 42) and their errors are deleted.
DSM Amendment A24 states trust-on-first-use for the pinned appliance and
what offline acceptance cannot know; the pk_chip binding (SA-26) stays open.

SA-179: the anchor-core unit and integration tests signed with hashes anyone
holding the public key could make. They now sign with Ed25519 and
BLAKE3-SPHINCS+ through verifiers that check for real.
…tinct value

SA-25: a claim or release naming another cell is now dropped for the cost of
a decode, before any SPHINCS+ verify, and a keeper recognizes each distinct
value at a cell once, so a value copied at every seat or written many times
costs one verify and then a lookup.
SA-171: the monitor hashes a fixed 64 KiB window before every authority op,
and the Non-secure image laid its writable data inside it, so a static write
changed the measured bytes. The image now fills the window with code,
read-only data and a fixed trapping fill, starts .data at the window's end,
and both linker scripts assert it.
The owner's choice of 2026-11-01, "Keep the frontier, state it": a peer
frontier a receiver validated is not withdrawn by later evidence off its
chain, and a contradiction at one cell fails closed where a walk reads it,
without rolling back what was accepted. SA-92's process memo went in
0b98dbf and the vault record is never a walk's start, so the row is Fixed.
…point

SA-75 (owner's choice 2026-11-01, "Keep verified head", as DSM Amendment
A26): every load re-established each memoised release since R_0, about four
SPHINCS+ verifies a generation, and the memo kept every release.

- Core: ReserveLineage::checkpoint seals each generation's step under a
  reader's key over (reserve_id, n, d_n, R_n); ReserveLineage::resume
  rebuilds every root and digest from the steps and takes the result only
  when the seal holds.
- SDK: the key is derived under the at-rest key; a load starts from the
  resumed checkpoint and verifies only the releases after it; a refused
  checkpoint is verified again from the memo. Keeping a checkpoint lets go
  of the other devices' releases it covers, and release_at reads such a
  release back from its cell against the sealed step (client DB schema 42).
… history

SA-03 (owner's choice 2026-11-01, "Tie to history", as SoFi Amendment
S34): a reader adopted a baseline on its signature, owner and frontier hash
alone, so an owner could sign a root no trade built.

- Core: Verifier::tie_baseline checks the committed history head is the
  vault's at g, holds R_0 at 0 and the frontier's root at g, that the
  exercise final at the history's root at g-1 builds that root, and that no
  contradiction published for the baseline holds when checked again.
  history::leaf_root_at reads a history's root at a generation by its path.
- TiedBaseline (one construction, pinned by CI gate [7]) is what a witness
  is built from, so nothing is adopted untied.
- VaultBaselineContradictionV1 (class 0x0076) under one locator per vault;
  a reader that finds a contradiction publishes it and quarantines the vault.
- S34 states the limit: roots invented between 0 and g-1 are refused once a
  walker publishes the step.
…dio carries nothing in the clear

SA-35 (owner's choices 2026-11-01, as DSM Amendment A27): the GATT identity
characteristic gave any scanner a device's permanent identity, and bilateral
frames, terms and salt included, crossed the radio in the clear. No existing
channel could be reused.

- Core (dsm::crypto::ble_link): a post-quantum KK handshake over the ML-KEM-768
  keys each contact pinned from the other's AK-signed entry (Hello, Reply,
  Confirm; Decline for a Hello not sealed for this wallet or from a
  non-contact), then fixed-length XChaCha20-Poly1305 records with strictly
  increasing counters per direction.
- SDK (bluetooth::link_session): radio bytes are handshake fragments or sealed
  records only; the client tries its target, then every contact; pairing
  completes on the Confirm and on the server's ready record, through
  PairingOrchestrator::link_paired. Rust chooses the link: answers and owed
  frames are sealed on it, and with no paired link Kotlin is asked to connect.
- Deleted: the identity and pairing characteristics (service UUID bumped), the
  identity read and the three-phase pairing (Rust, JNI, Kotlin, protos), the
  bridge's fallback that paired a contact from an unauthenticated identity, the
  raw chunking exports, and the coordinator's reach routing.
- Tests: Core handshake and record tests; the two-device harness now seals
  every BLE frame, with tests that the radio carries no identity, that a
  replayed or old-link record opens on nothing, and that a non-contact's Hello
  is declined. BLE between phones is not yet run (hardware stage).
… and built

Every row 6.89 listed as waiting on the owner has been decided and fixed in
its own section since; the paragraph now says where each stands, and that
SA-117 (Partial) and SA-80 (deferred by the owner) remain.
…pts change; tests build against the tie

The Non-secure crate's build script told cargo nothing of its linker scripts,
so a change to dsm-ns-sram.x left the monitor embedding an image laid out
before it (the window-fill ASSERT caught it); the monitor did not track its
own dsm-secure-sram.x either. Both now rerun on their scripts.

The frontier unit tests check a witness under an authenticated frontier, and
now call that check (VaultWitness::under_frontier) rather than building a
witness from a baseline that was never tied; the link tests' contact book
names its lifetime.
…alls

Rust asks the app that hosts it for a few things: hand the page an event,
write sealed records on exactly the link it chose, connect for a contact,
start or stop the radio, seal the wallet seed under the device's key, put a
count on the icon. Those calls were JNI up-calls scattered behind
cfg(target_os = "android") gates, with no-op twins on every other build.
They are now one trait, host::HostBridge, installed once per process:
Android's implementation wraps the same JNI up-calls; iOS registers C
callbacks. The gated event sites (ingress, inbox poller, identity
publication, money waiting, token and storage routes, the pairing
orchestrator, the bilateral event callback) call the host, and the pairing
orchestrator's radio calls are platform-neutral.

The seed vault seals through the host on a device build (Android over JNI,
iOS with the ffi feature) and falls back to its software key only where no
device host exists (host tests); an iOS build has no software fallback.

The C ABI (feature ffi) mirrors the JNI exports one for one and terminates in
the same neutral code; the bridge's protobuf shapes are decoded and encoded
in Rust so the Swift shell interprets no byte. Rust's log goes to the host.
cbindgen writes include/dsm_sdk.h; the stale header, which named functions
that no longer existed, is replaced.

The unified JNI bridge is untouched: its radio functions move to the neutral
module in the extraction that follows the first Android compile.
…ftovers are gone

The first cargo ndk check of the link-session JNI code stopped on four dead-code lints the
pairing deletion left: two unused imports, ble_frame_type_from_i32 and
frame_classify::ble_frame_needs_chunking, which only the deleted chunking exports called.

cargo ndk -t arm64-v8a --platform 23 check, lib and tests, both exit 0. frame_classify's two
remaining tests pass.
…nd the host seam under every platform gate

The iOS shell calls dsm_ffi_* (ffi/{mod,bridge,codec,host_bridge,ble}.rs): the
same startup, ingress, envelope, identity, session and radio entry points the
JNI exports give Android, over DsmBytes buffers the caller frees. Protobuf never
leaves Rust: ffi/codec.rs decodes bridge requests, host events and preferences
and encodes their answers, so Swift carries bytes only. The host's callbacks
(DsmHostCallbacks: post_event, write_on_link, request_connect, radio start and
stop, seal and open the seed, show the money count, log a line) implement
host::HostBridge, and every site that was gated on android+jni now calls the
host: ingress, identity publication, the inbox poller, money waiting, the seed
vault, the token, storage, wallet, system and contacts routes, the bilateral
handlers, the BLE runtime slots and init. Rust log lines reach the host's
logger (os_log on iOS).

bluetooth/radio_link.rs is the radio logic the JNI exports held: a link opened
or closed, bytes in on a link, a send to a contact or a reply on a link as a
Delivery (sent or not now), owed frames, accept/reject/cancel of a proposal,
and the last seen address as a connect hint. wallet.sendOffline runs through
it on both platforms.

The header is generated by cbindgen (usize as size_t, so Swift reads Int); the
hand-written header is gone. Targeted: 27 tests under ffi::, host:: and
bluetooth::pairing_orchestrator pass; clippy is clean in these modules.
… wire, and the simulator build

dsm_client/ios holds the iPhone app beside the Android one, hosting the same
React bundle: WKWebView serves it from the dsm-app://wallet/assets origin, a
document-start script installs window.DsmBridge with the Android keys, requests
go over a replying script message handler and events over evaluateJavaScript,
bytes as Base32 Crockford at the boundary and binary after it. BridgeDispatcher
answers the 24 names Kotlin answers (ci/bridge_rpc_names.py now requires the
Swift arms to equal Kotlin's); the identity, preferences, session facts,
diagnostics (OSLogStore + share sheet), phone contacts, QR scanner and connect
links are ported one to one; the seed vault is the Keychain (AES-256-GCM key,
WhenUnlockedThisDeviceOnly, no software fallback); screen guards cover the app
in the switcher and while captured, and say that screenshots cannot be stopped.

CoreBluetooth speaks the Android wire: the service and characteristic UUIDs,
roles client=1 server=2, 501-byte records, the [0xFF nonce] first notification,
the 0xFF count ACK every 10 notifications, 3 attempts, and the MTU gates (>=501
to write, >=503 to notify) under which a link does not open. Rust chooses the
link and the records; Swift moves bytes on the link it is told and reports a
vanished link closed. Bluetooth runs only while the app is open.

Build: make ios-libs (xcframework + header), the frontend's build:ios (bundle
into DsmWallet/www), make ios-project (the generator writes the Xcode project;
one executable, arm64 simulator) and make ios-simulator. The project's last
phase runs the real-code guard over dsm_client/ios, red until the owner lands
the guard's Swift extension on main.

Proven in the iOS 26.5 simulator: the page renders through Swift into Rust,
genesis v2, the seed sealed in the Keychain, the identity published 5/5 to the
fleet, wallet_ready, and the identity restored on relaunch. Nothing on the
radio is proven here.
…ftovers are gone

The first cargo ndk check of the link-session JNI code stopped on four dead-code lints the
pairing deletion left: two unused imports, ble_frame_type_from_i32 and
frame_classify::ble_frame_needs_chunking, which only the deleted chunking exports called.

cargo ndk -t arm64-v8a --platform 23 check, lib and tests, both exit 0. frame_classify's two
remaining tests pass.
…pies of its logic are gone

bleLinkOpened, bleLinkClosed, processIncomingBleData and the accept, reject
and cancel exports read their JNI arguments and call radio_link, the one copy
of the radio logic the C ABI also calls. Deleted from the bridge: its own
deliver_owed_frames, owed_frames_on, owed_frame_type, radio_write, Delivery,
send_ble_chunks_via_unified, send_ble_reply_on_link, dispatch_on_link,
request_gatt_connect and build_chunk_array (the Android host's chunk_array
and write_on_link/request_connect are the JNI up-calls now). A radio error
reaches Kotlin under RadioWriteError's code, the same JniErrorCode numbers.

cargo ndk arm64-v8a check, lib and tests, exit 0; host: 29 targeted tests
pass, fmt clean, the whole-tree guard exits 0.
…arget, and make ios-device builds for a connected device

The device slice linked at the 10.0 default and failed on ___chkstk_darwin,
which the C objects built for the SDK's own iOS version reference; the cargo
build now carries IPHONEOS_DEPLOYMENT_TARGET (17.0, the project's). ios-device
runs xcodebuild for generic/platform=iOS with the owner's team and Xcode's
automatic provisioning (IOS_TEAM=<team id>).

Proven on the owner's iPad (iPad13,4, iOS 18.5): installed over USB, genesis
created, the seed sealed through the Keychain (92-byte blob in recovery_prefs),
the identity published (identity_publication: published, quorum 3).
…next event; deliveries reach the bridge log; the rig onboards today's wizard

The event relay flushed only on markReady: one delivery the page did not
take left every later event queued until the page was torn down and
re-attached. A post now drains the queue in order when it cannot deliver
directly, so the kept event goes first and the new one follows; the relay
test covers it. Each delivery is logged to the bridge log as its topic or
kind, size and whether the page took it, never its bytes: on a device the
unified log cannot be read from the Mac without root, and this is the only
record of what reached the page.

scripts/rig_onboard.py walks the current wizard on a phone: the beta
acknowledgement, the look chooser, the intro, INITIALIZE, the 24 words read
off the page, the three checks answered from them, and the lock and tour
prompts. Proven on RFGYB0PQ8XK (fresh install, 2026-11-01).
The Send section is a ScreenTabs button and the form submits through its
primary button; the old tab-button and send-button selectors found nothing.
Proven on RFGYB0PQ8XK: 1 ERA sent online to the iPad (2026-11-01).
…r waits on its own queue; radio and Rust outcomes reach the bridge log

Two defects the iPad-as-GATT-client run exposed, both in the Swift shell:

- The foreground fact came from UIApplication.applicationState, which still
  reads inactive inside sceneDidBecomeActive, so every publish told Rust
  `foreground 0`, pairing_may_run stayed false and the pairing loop never
  started after a relaunch. The fact is now the scene's activationState; at
  resign-active the resigning scene is excluded (it still reads
  foregroundActive in that callback), so the pause publishes `foreground 0`
  and Rust answers may_run=false (proven on the iPad: advertising 0 on the
  next publish).
- openClientLink did `queue.sync` while already on `queue`: the BLE queue
  stalled, refreshAdvertising then blocked the main thread on it, the app
  froze and the link dropped right after TX_RESPONSE subscribed. The target
  is read directly under a dispatchPrecondition.

The device's unified log is unreadable from the Mac, so the bridge log now
carries RADIO: lines (discovery, connect, subscribe/capacity, link opened /
refused / failed, host start_scan / stop_scan / start_advertising /
request_connect codes, central state, the facts each publish sends) and
RUST: lines mirrored from dsm_sdk::bluetooth*, dsm_sdk::sdk::session_manager
and every Rust error line. pairing_follows logs its decision.

Both CoreBluetooth roles are now proven against an Android phone with this
build (server with RFGYB0PQ8XK, client with RFGYB0PQ9FF: PAIRING_COMPLETE on
both sides each time).

Tests: dsm_sdk --features ffi,bluetooth --lib bluetooth:: host:: on Postgres,
--test-threads=1: 157/0 (the 3 that fail under parallel threads share the
durable offline_spend_in_flight row of one device id; serial they pass).
Guard over the tree: exit 0. Parked Swift guard: only the 4 platform literals
awaiting the owner's ruling.
…layer (owner ruling 2026-11-01)

The owner ruled on 2026-11-01 that the NFC ring backup, encrypted recovery
capsules written to NTAG216 rings, is obsolete: remove the ring and the
capsule from the tree and never port it to iOS. This removes the ring
transport and the capsule artifact at every layer, in one change.

The ruling's scope, in its words: the tombstone/succession/resume/activation
pipeline, its recovery.* routes, recovery_impl.rs and RecoveryPipelineScreen
stay as a visible hole with no staging source. Recovery is unruled this
round, and protocol-level recovery code is not in the ruling's words. The
pipeline's staging input is gone: the capsule a recovering phone read from
a ring was the only writer of the staged device, root, rollup and tips it
reads, so the pipeline screen now shows that nothing is staged, and nothing
navigates to it (the two screens that did were the ring's). What stages a
recovery next is the recovery specification's to define. The KS1
derivations derive_recovery_key and derive_recovery_authority_seed stay,
and so does everything reached from the BLE re-establish, the dBTC asset
locks, the node tests and the seed/mnemonic cache.

Core: recovery/capsule.rs is deleted; the contact-set commitment moves to
recovery/contact_set.rs, its only remaining user, and the two KS1
derivations to recovery/recovery_keys.rs (K_R now has no consumer; the
authority seed feeds the recovery authority derived at unlock); the capsule
arms of the Core bridge, the recovery-nonce and recovery-challenge domain
tags (count 434 -> 432) and the capsule AAD KAT go with it. CLAIM_TRACE C68
is repinned to the new module.

Wire: NfcRecoveryCapsule, RecoveryCapsuleDecryptRequest/Response and the
NfcTag* messages are removed; UniversalOp 22, Envelope 15 and 96,
SdkEventKind 13/14, NativeHostRequestKind 8/9/105/106 and
NativeHostEventKind 4/5 are reserved by number and name. The frontend
bindings are regenerated.

SDK: capsule creation, persistence, pending marker, refresh, status,
enable/disable, auto-write and decrypt; the cache of the capsule key K_R
(the recovery authority keypair stays, derived at unlock); the
accepted-state and capsule-state indexes and the egress refusal on a stale
capsule; the six JNI exports; the recovery.status, capsulePreview, enable,
disable, cacheMnemonic, createCapsule, inspectCapsule, decryptCapsule and
setAutoWrite routes and nfc.ring.read/write; their ingress tests.

Android: reader mode, tag read and write, the NTAG216 capability-container
fix and its vibration, the host-bridge arms, the post-ingress refresh hook,
the NFC and VIBRATE permissions and the android.hardware.nfc feature.

Frontend: NfcRecoveryScreen, RecoveryScreen, the ring half of
nfcRecoveryService, nfcBackupUi, the Settings card, three tour steps, the
auto-backup toast, the restore entry points, the host-bridge functions and
event topics, the practice-gate rows. RecoveryPipelineScreen stays on route
recovery_pipeline without its ring text; its routes move to
services/recovery/recoveryPipelineService.ts, which reads the SDK's
key=value answers strictly (a missing key is an error, not a default) and
takes "every counterparty has acknowledged" from the SDK's empty pending
list.

Also gone: the nfc-backup skill and its evaluations, the code-map roots
and manifest rows of the deleted symbols, and the stray mentions in
scripts, TLA comments and skill docs. CONFORMANCE_GAPS 6.139 records the
ruling, closes S-NONCE, S-RING and D-F13 as removed, and names what stands
as a hole: the pipeline with no staging source, and the recovery-authority
anchor with no production publisher.

Checks: cargo check (dsm; dsm_sdk jni+bluetooth, with tests), cargo fmt,
clippy on both crates (two lints that predate this change, in ble_link.rs
and native_reserve.rs, allowed to reach the rest), the targeted Core and
SDK tests, scripts/real_code_guard.py against origin/main,
scripts/check_claim_trace.py, ci/bridge_rpc_names.py (24/24), the frontend
type check, lint and the touched jest suites, and
./gradlew :app:compileDebugKotlin.
…m check is gone and three gates get their first tests

The Confirm's nonce comparison could never refuse on its own: the key it is
sealed under has a transcript that already names the Reply's nonce. It is
gone, the Confirm is an empty message under that key, and its opening is the
whole check (DSM Amendment A27 reworded).

Checks no test reached now have one: six in accept_offline_release, the link's
clear-byte, turn, ready and early-record refusals, and a release read back
after its memo row is let go. Clippy's large-variant and as_chunks findings on
this code are fixed.
… reader walks the last trader's lineage

SoFi Amendment S34's step check ran Core's whole walk, so a reader that never
traded walked the last trader's lineage: 2828, 4481 and 7134 GET requests
across five members at generations 2, 4 and 6. Owner, 2026-11-01: "S34 must
not reintroduce history-linear verification indirectly by forcing a fresh
reader to walk the last trader's entire lineage."

facts::last_link_holds decides a step over the objects at its key: every leg
at its first key and final on the exercise, the position pair registered, a
Release's verdict final, conformance Valid, route validation Valid short of
the trader's lineage, and Core's fold builds the root. The same reader now
reads 1751, 1756 and 1756.

A link that does not hold decides nothing, since anyone can take a first key:
only the full check can say a step fails, publish a Step or quarantine. A Step
published at any generation up to the frontier's takes the full check.

What it gives up, as S34 states: a last-link liar no longer needs a trader
whose lineage is valid. A griefer can still force the full check, and refuses
nothing by it.

The contradiction test now publishes the state leaves its steps read and
asserts the middle lie is really adopted; before, its control failed at setup
and the adoption's Ok hid it.
MR-SOFI-0426 and 0428 restated, MR-SOFI-0429 to 0431 added, pins moved. The
matrix's pending mutation cells hold their results and gains rows for sections
6.132, 6.133, 6.136 and 6.137.
… wire, over a C ABI onto the same Rust as JNI

The conflict was in jni/unified_protobuf_bridge.rs: the send functions this
branch kept had moved to bluetooth::radio_link on the port's side, so the moved
code stands and the JNI shim's copies are gone.
ble_events.rs kept an import and a helper nothing reads once the radio exports
moved to bluetooth::radio_link, which breaks the Android build. The iOS bridge's
tests take expect_err. The matrix named a test that went with the NFC ring
backup; its row goes too.
Comment on lines 252 to 254
"/tmp/x",
"--ingress-secret-dir",
"/tmp/x-ingress",
"--env-config",
Comment on lines +536 to +539
match self {
Self::Initiator => 1,
Self::Responder => 2,
}
Comment on lines +2620 to +2622
"[sofi verifier] vault {}: the last link at generation {generation} takes \
the full check: {why}",
short_id(vault_id)
let sid_a = connect(&r, &p.a, &relay, &code_a).await;
let code_b = offer(&r, &relay, &creatures).await;
let sid_b = connect(&r, &p.b, &relay, &code_b).await;
let nonce = [0x43; 32];
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.

2 participants