Repository navigation
fix: a device's held first admission resumes on its activation root - #1113
Merged
Merged
Conversation
…n path A first faucet claim held at its root claim (two links, never final) has already credited the head. Resuming it directly, by the next claim, and by storage.sync after a restart each refuse today with "cannot activate an economic lineage on a device that already holds value": the predecessor is activated against the head that carries the held claim's own credit.
The advance and the pending admission commit in one transaction, so a first admission held at any later step leaves its own credit on the head. Every resume path took the predecessor from validated_root_or_activate, which with nothing admitted activated against that head and refused it as already holding value — so a fresh wallet whose first claim was held was stuck. The activation precondition is about what the device held at its activation point: the head its first admission was built on. Core now reads it as the head less exactly that admission's own credit (EconomicActivationSnapshot::before_first_admission), taking the pending admission's frozen witness only when it binds the admission (operation digest, post-root, pre-root = activation root) and verifies from the activation root under the head's identity. activate() is unchanged: value the first admission did not credit, of the same asset or another, or an outstanding allocation, is still refused. The SDK reads the frozen witness through one helper shared with resume_pending_admission.
…on controls CONFORMANCE_GAPS §6.73: the finding, the predecessor the specification decides (the activation root, DSM Amendment A8), the fix, the tests and six mutation controls; MR-DSM-0030 names the new gate and tests. VERIFICATION_MATRIX: the gate, its negative tests and the mutations observed red.
The Code map check failed on 97 PIN_STALE rows: the fix changed code under pins whose evidence was sealed before it (economic admission, route seats, the SoFi facts and walk, peer lineage, the spool seal). They are re-pinned at 577f00d through intent_pins' own judge and seal, after the 71 evidence tests they name ran and passed at that commit (6 cargo runs, 0 failed). None moved beyond its code. make requirement-map-intent over the CI map for 577f00d reads 0 failing rows and 0 failing pins.
cryptskii
added a commit
that referenced
this pull request
Oct 5, 2026
…on (SoFi S21)
Core of escrow vaults, per SoFi section 19.9.
Release:
- B° gains the Release branch (class 0x0065) and its route digest
(0x0066): one leg, E in its single-leg form, naming the vault, the
verdict cell and the outcome; no verdict enters E.
- VaultTerms resolves a vault's slots to a market's policies or, when all
three name one object, to its escrow terms (authenticated under the
terms namespace). Close and Swap require a market, so against an escrow
vault they are Invalid (TermsAreNotAMarket); a Release requires escrow
terms (TermsAreNotEscrow).
- validate_release: one leg; the vault's own derivation, pinned set,
Active; the whole stake (reserve_a, reserve_b = 0); the cell the terms
derive; a branch for the outcome that pays the trader; the closed write
set with the vault retired; the realize root.
- market_legs_permitted becomes vault_tokens_permitted and checks the
escrow vault's one token too; close_vault_post becomes
retire_vault_post.
The verdict in resolution:
- RouteFacts, GroundFacts and EstablishedFacts carry a VerdictFact.
ConsumedRoute needs the verdict final on the release's outcome;
RouteImpossible gains arm (v), VerdictOnAnotherOutcome, which skips the
key without validation evidence; rung 5 voids a release that lost.
- The verifier reads a Release's verdict cell beside its legs, keeps its
completion proof when final, and reports an undecided cell as
NotEstablished::VerdictCell.
- An acquisition fetches a token policy the predicate named missing on
the next round, which is how an escrow vault's token is fetched.
Creation:
- Operation variant 38, EscrowVaultCreate {genesis_preimage, creation,
terms, signature}, signed over the operation like SofiVaultCreate;
egress, ClosedWriteSet, and verified in DeviceState::advance.
- Its write set: one debit of the stake of the terms' token and the
creation record, insert-only; the terms must be the object all three
slots name. Conservation holds the deltas to that one debit.
- GenesisAccepted has its escrow form (GenesisTerms::Escrow);
vaults_of_token passes over escrow vaults, and vaults_of_cell finds the
vaults bound to a verdict cell under escrow_cell_locator.
- Publication: the terms, the escrow genesis (indexed by vault and by
cell), and gathered verdicts (by statement).
History: TX_TYPE_ESCROW_LOCK and TX_TYPE_ESCROW_RELEASE, the SDK's
Realized kinds and the wallet's labels; the frontend proto regenerated.
Tests (release): dsm --lib sofi/economic/types 619 passed, then the new
escrow tests: 9 release validation, 3 ladder, 5 write set, 4 genesis
acceptance, 2 publication, 1 conservation; sofi_v8_operations covers tag
38's signature arms and round trip. dsm_sdk wallet_routes 26/26.
Frontend mapper and wallet tests 35/35, tsc clean. clippy -D warnings
clean on dsm and dsm_sdk; the real-code guard clean;
conformance_evidence clean. CONFORMANCE section renumbered 6.74 (6.72
and 6.73 are taken by #1112 and #1113).
…ission Conflicts: - economic_admission_flow.rs: both sides added a function after validated_root_or_activate; both kept (frozen_witness_of, then main's admitted_root_and_tree). - CONFORMANCE_GAPS.md: main's §6.72 (A11) then this branch's §6.73; §6.72's "filed separately" note now points at §6.73. - INTENT_PINS.tsv: the same rows were re-pinned on both sides; main's pins taken. The rows the merged code moved are re-pinned at the merge commit.
cryptskii
added a commit
that referenced
this pull request
Oct 5, 2026
Conflicts: - dsm/src/common/domain_tags/mod.rs: EXPECTED_TAG_COUNT 368 = 353 at the merge base + A11's 6 + S21's 9; both notes kept. - MASTER_REQUIREMENTS §1: the pins re-taken from the merged spec bytes (DSM explainer = main's, SoFi = this branch's); the pin history S21, then A11. §7.1 holds both entries, A11 then escrow. Counts: DSM 25, SoFi 55, storage 30, 973 canonical rows. - CONFORMANCE_GAPS: main's §6.72, then this branch's §6.74 (§6.73 is #1113). §7 totals regenerated by ci/conformance_evidence.py --write.
cryptskii
added a commit
that referenced
this pull request
Oct 5, 2026
…vaults CONFORMANCE: main's §6.73 (#1113) goes before this branch's §6.74; §7 totals regenerated from the merged rows (ci/conformance_evidence.py, static: 0 failures). The verification matrix merged cleanly. Checked on the merged tree: dsm economic_admission_lifecycle 18/0; dsm_sdk escrow_e2e_tests 5/0 and faucet_flow_tests 13/0; clippy clean; real-code guard clean.
This was referenced Oct 5, 2026
cryptskii
added a commit
that referenced
this pull request
Oct 5, 2026
…l-connection payment race) (#1115) * chore(code-map): re-pin the 97 rows #1113 moved on main main's Code map has been red since #1113 merged (8017c77): 97 PIN_STALE rows, every one a code-class change (production or evidence-test closures). Re-pinned at 8017c77 with ci/intent_pins.py repin, one key at a time, over CI's code map for 2c8fe4b, whose tree is main's exactly (3f1358e), after the 71 evidence tests they name passed on that tree (6 cargo runs, 0 failed). make requirement-map-intent: 0 failing rows, 0 failing pins, 635 pinned. * test(connect): the real-connection test reads a payment's outcome once it is answered Coverage on main has been red since #1112 (7e084d7): at real_connection.rs:845 the payment's outcome was Unspecified where the test asserted CarriedOut. The game's account establishes the Paid fact from the transfer it accepted (connect.app.status takes in the inbox first), and the wallet's answer reaches it separately over the relay; either can arrive first, and outcome is Unspecified exactly while no answer has. The test waited only for the fact. It now waits for the answer as well before reading the outcome. Only the Coverage job (instrumented debug build) was slow enough to hit it.
cryptskii
added a commit
that referenced
this pull request
Oct 5, 2026
… wait) into feat/escrow-vaults Clean: main brings INTENT_PINS.tsv (the 97 rows #1113 moved, re-pinned on main's tree) and a test-only wait in crates/dsm-app-host/tests/ real_connection.rs. This branch's own re-pin follows from this head's CI map.
cryptskii
added a commit
that referenced
this pull request
Oct 5, 2026
…commitment (#1114) * docs(specs): SoFi Amendment S21, escrow vaults released by a canonical verdict An application needed two parties to lock equal stakes against one agreed match, with the application as referee, and nothing in DSM could hold value under a condition other than a SoFi market. The owner ruled (2026-10-04) for a generic escrow vault and no wager logic in Core, and (2026-10-05) that one external commitment has one admissible verdict, made so by the protocol rather than by the referee's bookkeeping. SoFi section 19.9 specifies it on the vault machinery: - an escrow vault is a vault whose three policy slots name one EscrowTerms object (class 0x0063): a token, Y = H(DSM/external/v1 || X) and 1 to 16 branches, each an outcome, the exact set of signers that decides it, and a recipient; - it releases its whole amount once, by the recipient's own Release position (classes 0x0065 and 0x0066), and has no market and no owner close; - the verdict occupies K_verdict = H(DSM/escrow/verdict-cell/v1; Y || tau) first at its leader and proves its own authority from its bytes (EscrowVerdict, class 0x0064); tau is the digest of the outcome table, so only the agreed signers can occupy the cell; - every vault bound to the cell settles only on the outcome it holds: ConsumedRoute requires the verdict final on the release's outcome, and RouteImpossible arm (v) skips a release that lost it, which resolves Void; - creation is operation variant 38, EscrowVaultCreate. MR-SOFI-0363 to 0384 are added with source amendment, all Missing; CONFORMANCE section 6.72 records the finding; section 1 is re-pinned and the totals regenerated. The implementation follows after the owner's review of the amendment. * docs(requirements): number the escrow finding 6.73, after A11's 6.72 on #1112 * docs(specs): S21 links escrow vaults only by the cell their terms derive Review of S21 asked that linking be an explicit invariant, so that two parties cannot believe their vaults share a verdict when they do not. Section 19.9 now states it: - the outcome table has exactly one encoding, so the same outcomes with the same signer sets give the same tau, and the table is the verdict authority (there is no other); - two vaults are linked exactly when they name the same Y and byte-identical tables, and then they share K_verdict; a vault that differs in any byte is bound to another cell and is not linked; - a link is derived from each vault's accepted terms, never asserted by a match id, an index entry or an application, and is checked by whoever relies on it: escrow.create against a counterpart, and any reader waiting on both stakes; - discovery is by cell, so escrow_commitment_locator(Y) becomes escrow_cell_locator(K_verdict), and a vault bound to another cell is never found among the linked ones; - Core accepts each genesis on its own terms and compares no two vaults; the section says why, and what protects the party that locks first. MR-SOFI-0365, 0373 and 0384 rewritten; MR-SOFI-0385 and 0386 added, Missing; section 1 re-pinned and the totals regenerated. * feat(escrow): the terms, the verdict and the verdict cell (SoFi S21) The wire layer of escrow vaults (SoFi section 19.9), and nothing that spends yet: - CCB classes 0x0063 to 0x0066: EscrowTerms, EscrowVerdict, and the Release branch and its route digest, allocated for the next commit. - Nine domain tags in common/domain_tags/dsm/misc/escrow.rs: DSM/external/v1 and the eight DSM/escrow/* domains; the registry count goes from 353 to 362. - EscrowTerms, EscrowOutcome, OutcomeTable, EscrowBranch, EscrowSigner, EscrowVerdict and VerdictSignature, with strict codecs. A signer set is 1 to 4 signers strictly ascending by their canonical bytes, and an outcome table 1 to 16 entries strictly ascending by outcome, so a duplicate, a threshold or a second encoding of one table cannot be expressed. - sofi::escrow: Y, A_T, tau, K_verdict, the verdict seed, the statement m(o), the two locators, signing a statement, and verdict_authority: a verdict occupies its cell only when its Y and table derive the cell, its outcome is in the table, its signers are exactly that outcome's, and every signature verifies over m(o). The verdict cell is read with route_chain::evaluate; the read keeps why each value ahead of the occupant counts as nothing, and standing_for gives a release Final, Unsettled or Lost. Tests (dsm --lib, release): 8 in sofi::escrow, covering the derivations against an independent hasher, the field-table bytes with frozen digests, linked vaults sharing one cell exactly when Y and the table agree, every refusal of a malformed table, and a signature deciding only the cell it was made for. Domain tag registry 8/8. clippy clean. * feat(escrow): release by the canonical verdict, and the escrow creation (SoFi S21) Core of escrow vaults, per SoFi section 19.9. Release: - B° gains the Release branch (class 0x0065) and its route digest (0x0066): one leg, E in its single-leg form, naming the vault, the verdict cell and the outcome; no verdict enters E. - VaultTerms resolves a vault's slots to a market's policies or, when all three name one object, to its escrow terms (authenticated under the terms namespace). Close and Swap require a market, so against an escrow vault they are Invalid (TermsAreNotAMarket); a Release requires escrow terms (TermsAreNotEscrow). - validate_release: one leg; the vault's own derivation, pinned set, Active; the whole stake (reserve_a, reserve_b = 0); the cell the terms derive; a branch for the outcome that pays the trader; the closed write set with the vault retired; the realize root. - market_legs_permitted becomes vault_tokens_permitted and checks the escrow vault's one token too; close_vault_post becomes retire_vault_post. The verdict in resolution: - RouteFacts, GroundFacts and EstablishedFacts carry a VerdictFact. ConsumedRoute needs the verdict final on the release's outcome; RouteImpossible gains arm (v), VerdictOnAnotherOutcome, which skips the key without validation evidence; rung 5 voids a release that lost. - The verifier reads a Release's verdict cell beside its legs, keeps its completion proof when final, and reports an undecided cell as NotEstablished::VerdictCell. - An acquisition fetches a token policy the predicate named missing on the next round, which is how an escrow vault's token is fetched. Creation: - Operation variant 38, EscrowVaultCreate {genesis_preimage, creation, terms, signature}, signed over the operation like SofiVaultCreate; egress, ClosedWriteSet, and verified in DeviceState::advance. - Its write set: one debit of the stake of the terms' token and the creation record, insert-only; the terms must be the object all three slots name. Conservation holds the deltas to that one debit. - GenesisAccepted has its escrow form (GenesisTerms::Escrow); vaults_of_token passes over escrow vaults, and vaults_of_cell finds the vaults bound to a verdict cell under escrow_cell_locator. - Publication: the terms, the escrow genesis (indexed by vault and by cell), and gathered verdicts (by statement). History: TX_TYPE_ESCROW_LOCK and TX_TYPE_ESCROW_RELEASE, the SDK's Realized kinds and the wallet's labels; the frontend proto regenerated. Tests (release): dsm --lib sofi/economic/types 619 passed, then the new escrow tests: 9 release validation, 3 ladder, 5 write set, 4 genesis acceptance, 2 publication, 1 conservation; sofi_v8_operations covers tag 38's signature arms and round trip. dsm_sdk wallet_routes 26/26. Frontend mapper and wallet tests 35/35, tsc clean. clippy -D warnings clean on dsm and dsm_sdk; the real-code guard clean; conformance_evidence clean. CONFORMANCE section renumbered 6.74 (6.72 and 6.73 are taken by #1112 and #1113). * feat(escrow): the SDK flow: create, sign, adjudicate, verdict, release (SoFi S21) escrow_flow runs each escrow route over the vault machinery of sofi_flow: - create publishes the terms and the genesis Stored, checks a named counterpart vault is accepted, Active and bound to the same verdict cell, then admits the creation with its one debit; - sign puts this device's signature for an outcome under the cell's statement locator; adjudicate gathers the outcome's signatures there, assembles the verdict Core recognizes, writes it to the cell leader first and returns what the cell holds, which may be an earlier verdict; - release builds only on a final verdict whose branch pays this device, sets up with the vault, walks it to its head, retires it and exercises the release through Core; - locked and vaults list escrow vaults by cell and by this device's own creations. Core gains gathered_signatures and assemble_verdict, with their test. sofi_flow's VaultAtHead carries the vault's terms, so market routes take a market and escrow routes an escrow vault, and the helpers the flow shares are crate-visible. sofi_sdk gains build_escrow_vault_create and draft_release. * fix(sofi): a setup after a SoFi position names its resolved claim, and peers read it so A setup made right after a trader's SoFi position names the claim that position's resolution accepted. A peer validating the setup read the trader's claim there through validate_peer_lineage, whose target step refuses a conditional claim as Unresolved: so once a trader traded (or released) through one vault and then set up with another, nobody else could walk the second vault past that trader's exercise. peer_claim_at walks the segment as a payer's and takes the position itself as its last step, resolving a conditional claim through the S15 resolver exactly as an interior position, and accepted_claim_at reads another trader's claim through it (MR-SOFI-0347). Found by the escrow node test in which the winner releases two vaults in turn. * feat(escrow): the escrow routes, their wire, and the node tests (SoFi S21) escrow.party, .create, .sign, .adjudicate, .verdict, .release, .locked and .vaults reach escrow_flow through handlers/escrow_routes, with their requests and responses in dsm_app.proto (envelope payloads 129-133, after Connect's 128) and refused as requests by the Core bridge. Five node tests run them on the pinned set's nodes with two players and a referee: the winner takes both stakes once, and the loser's release of the winner's branch is refused by Core past its producer; a referee who signs both outcomes settles both vaults on the first, and a release built on the second is Void; a verdict signed outside the authority, or made for another cell, is passed over; a joint cancel and a referee result cannot both settle; an escrow vault has no market and no owner close, and a stake is not locked against a vault bound to another cell. escrow.release splits into its producer's pre-check and exercise_release, which drafts and exercises through Core. sofi_flow prices a named vault only once it has a market, so an escrow vault is refused for that and not for its token. * docs(requirements): escrow rows Met, their mutation controls, and the conditional-claim finding CONFORMANCE §6.74: S21 is implemented; MR-SOFI-0363 to 0386 are Met on their code and their unit and node tests, and the finding found on the way (a peer could not read a setup's claim at a conditional position) is recorded with MR-SOFI-0347's row, which gains peer_claim_at. §7 totals regenerated. VERIFICATION_MATRIX: ten escrow rows. Every gate was removed in a scratch worktree and its named test watched red: the release checks, the verdict's cell, signers and signatures, gathering, the ConsumedRoute conjunct, rung 5, arm (v), the release's standing at the cell, genesis acceptance, the creation write set and conservation, the counterpart's cell, and the conditional-claim arm. The Close/Swap/Release-by-kind refusal is the type and has no mutation. * fix(escrow): no secret-derived value in a test panic; every pub escrow fn has a caller CodeQL alert 750 (rust/cleartext-logging): the loser's-release test Debug-printed the PositionOutcome of a release signed with the device key. The panic now says only what happened. Rust gates (G1, ci/sofi_reachability.py) found five pub fns in sofi/escrow.rs with no production caller: - verdict_object_address duplicated Publication::address for a gathered verdict, and check_verdict_completion had no consumer at all: both deleted, with the MR-SOFI-0369 row's citation of the second. - sign_statement, gathered_signatures and assemble_verdict are called by escrow_flow, which imported the module as `escrow::{self, ..}`; it now imports `dsm::sofi::escrow` as a module of its own, the form G1 resolves. Local: ci/production_safety_checks.sh passes (G1: 128 reachable, the baseline unchanged); clippy and the real-code guard clean; conformance evidence static clean. * chore(pins): repin #1114 from its CI map at ce886f7 Code map run 37294345826 (head ce886f7) read 0 failing rows and 622 failing pins: 616 PIN_STALE, all code-class; 3 ORPHAN_PIN and 3 UNPINNED, the MR-SOFI-0304/0311/0318 rows renamed market_legs_permitted -> vault_tokens_permitted. - 616 repinned (0 moved beyond their code; 324 evidence tests); - the three old keys unpinned and the three renamed rows pinned. Board logs, all of ce886f7: CI's Rust tests (dsm) 1658 ok, workspace-rest 262 ok, Storage Node (Postgres) 101 ok; and 50 tests those logs do not attest, run targeted on a clean tree at ce886f7: 47 dsm_sdk (not yet reported by CI) and dsm::economic_lineage_register's two, whose CI log interleaves the next binary's header before their result line. All passed. make requirement-map-intent: 0 failing rows, 0 failing pins, 635 pinned.
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.
The defect
If a device's first economic admission was held, it could never be finished. This was found 2026-10-04 while building the DSM Connect real-connection test (#1112).
Why the head already held value. The advance and the pending admission commit in one transaction. So a first faucet claim held at any later step leaves its +100 ERA credit on the head, fenced: for example, its root claim was not yet final ("the claim is not final yet … the admission stays held for resume").
Why every resume failed. Every resume path takes its predecessor from
validated_root_or_activate:resume_pending_admission, reached from the next claim,stage_admission,wallet.send, andstorage.sync(Step 0 and the stranded-admission sweep).With nothing admitted yet, that function ran
activate()against the head as it stood. The head carries the held claim's own credit, soactivate()refused: "cannot activate an economic lineage on a device that already holds value".The effect. A fresh wallet whose first claim was held stayed stuck and fenced. The SDK suites' funding fixture hit the same refusal intermittently under load:
two_device.rs:234"funding claim 0: …".What decides the predecessor
The specification decides it. An identity's activation root is position 0 at the canonical empty root (DSM Amendment A8, MR-DSM-0274), and a first admission is built on it.
activate()'s refusal of a device that holds value is local protection against self-rooting, and it stays as it is. What changes is the head it is asked about. It must be the activation point: the head the first admission was built on. Before this fix it was the head after that admission's own advance.The fix
Core,
EconomicActivationSnapshot::before_first_admission(head, witness), indsm/src/economic/lineage.rs. It reads the activation point as the head less exactly the pending first admission's credits, taken from the admission's frozen witness. The witness counts only when it is that admission's own:The admission must be locally accepted, DSM-backed, and at position 1. Anything else is
ActivationPointUnreadable. An outstanding allocation is read off the head as it stands, because a DSM-backed admission moves none.SDK.
validated_root_or_activateuses it when the head carries a pending admission and nothing is admitted. The frozen witness is read by one helper (frozen_witness_of), now shared withresume_pending_admission.activate()is unchanged. Whatever the first admission did not credit was held at the activation point, and it is still refused: more of the same asset, another asset, or an outstanding allocation.Tests
Node-backed (
faucet_flow_tests, the storage node's own code on Postgres):a_held_first_claim_is_finished_by_resuming_ita_held_first_claim_is_finished_by_the_next_claima_held_first_claim_is_finished_by_the_sync_after_a_restartEach holds a fresh device's first claim by refusing
K_root(1)writes at the seats after the second, so the root claim reaches two links and is never final. The test asserts the held state: position 1 on the empty root, 100 ERA on the head, nothing admitted. Then it resumes the claim through one production path. All three were red before the fix (commit d8b85c9), with exactly the refusal above.Core (
economic_admission_lifecycle):a_held_first_admission_resumes_on_the_activation_rootvalue_the_first_admission_did_not_credit_still_blocks_activationthe_activation_point_is_read_only_through_the_first_admissions_own_witnessMutation controls
Each mutation was run on 217491e and then restored;
git diff --exit-codewas clean after each restore.a_held_first_claim_*testsvalue_the_first_admission_did_not_credit_still_blocks_activationvalue_the_first_admission_did_not_credit_still_blocks_activationthe_activation_point_is_read_only_through_the_first_admissions_own_witnessRecords
CONFORMANCE_GAPS.mdgets a new section, §6.73. §6.72 is held by A11: a Web2 application connects to a wallet (DSM Connect), proven with a game on three phones #1112; renumber at merge if needed.VERIFICATION_MATRIX.mdgets a new row.Not driven by a test
wallet.sendandstorage.syncStep 0 resume through the sameresume_pending_admission, but no test reaches either with a held first admission. Step 0 runs only with a staged inbound transfer, and a send needs a funded device.Board
Local, at 577f00d, which is this PR's head. The board is the exact CI command (
cargo test --locked --workspace --exclude dsm_storage_node --release -- --nocapture --test-threads=1), run on a private Postgres server.make lint: exit 0 (fmt, clippy--all-targets -D warnings, frontend eslint).ci/conformance_evidence.pypasses static checks. Run against this log alone, the only tests it reports as not run aredsm_storage_nodeanddsm_sphincstests, which this board does not build; CI's combined log covers them.🤖 Generated with Claude Code