fix(map): the node's committed rows; a reading names only definitions its build reads - #1054
Merged
Merged
Conversation
…s committed rows CI's first Linux run of the check found the storage node's sentinel `dsm_storage_node::main` reading two definitions. The second was the build script's `main` (dsm_storage_node/build.rs), which shares the Rust path but lies outside the node build's sources, so it has no node reading. A sentinel, and a fixture reading key, names a reading in one build. Only definitions that build reads are candidates now. Two such definitions under one path still fail, as naming two. - ci/requirement_map.py: the sentinel lookup ignores definitions with no reading in the sentinel's artifact. - `requirement_map fixture`: a key matches only definitions the fixture build reads. `absent` means the build reads none. - ci/requirement_map_mutations.py: two definitions that read alike stay two readings and are never folded into one. A map that is not clean before planting fails its own planted cases while every other case still runs (the first CI run stopped before any case). - The fixture gains the same shape: `probe::main`, one path shared with the fixture's build script. Its committed sentinel (tools/requirement_map/fixture/sentinels.tsv) is checked before planting. With the old lookup it reads ['None None', 'reached REACHED'], CI's failure exactly. - The node's entry point and counts, taken from CI's Linux map (committed.tsv): main is the one entry point. The counts are 283 reached (50 of them by dispatch), 5,394 dead, 299 indeterminate and 1,977 not in the build. 282 of the 289 IND_UNLINKED_IMPL readings flow from three seeds (impls of outside traits on `[u8; N]` and `dyn`) into core code the node otherwise never reaches. - An unlinked impl's reason names its whole type (`[u8; 32]`, not `[u8;`). Found while answering the gate: - `cargo tree` prints `dsm-anchor-core` and `dsm-anchor-verifier` with hyphens, and the features parser stored them under `_`. compiled_out read those crates' calls with no features, and exclusions skipped them. There is one normalization now (`cfgs::feature_key`), and `graph::package_features` returns an error for an unknown package instead of an empty set. The Android map had no instance (readings identical before and after), and both new tests went red first. - A `cargo tree` line whose source is not `(…)` is an error, not skipped. - compiled() sorts each compiled site once into certain and undecided (with the gate's reason). The only way it drops an edge is when no site compiles. - The fixture's own test asserted a sum `entry()` no longer returns, and it panicked when run. It is fixed, and `make requirement-map-fixture` now builds the fixture as shipped and runs its test. - The edge endpoints the map holds no definition of are counted per build and pinned in counts.tsv as `no-definition`: other crates' symbols, and the workspace's generated or macro-written code (Android 1917/1950, node 1838/1883). A jump fails like an Indeterminate jump, so the checks' skips are accounted for, never silent.
cryptskii
added a commit
that referenced
this pull request
Sep 29, 2026
CI's first run of #1055 (the first to index the node with the manifest's root questions) refused: `a root query's entry point dsm_storage_node::main names 2 definitions the node build compiles`. The node's index also holds its build script's `main`, which shares the binary's path and is not in the build. That is the class #1054 fixed for sentinel and fixture lookups. A root query now looks paths up only among definitions in the build's own crates. - The fixture gains the question: MR-FIX-0008 asks whether probe::main, a path the fixture's build script shares, is reached from the entry point. Before the fix the fixture refused with CI's exact error; it now reads SATISFIED. - ci/conformance_evidence.py requirement_statuses refuses a canonical ID §8 gives no row, where it used to map the ID to None. The comparator reports that refusal on stderr. A macOS host cannot index the node, which is why the local run missed this. The Android root answers are byte-identical before and after. The map check is clean, 40/40 mutation cases pass, and the fixture reads 90/90 with 0 unexpected intent outcomes.
cryptskii
added a commit
that referenced
this pull request
Sep 29, 2026
…ap, and the conformance it found (#1055) * feat(map): the intent comparator: the specifications' intent against the map The map establishes what the code does. It never decides what the code ought to do. The intent manifest (specs/requirements/INTENT_MANIFEST.tsv, the owner-authorized home) says that, one row per requirement, symbol and build. ci/intent_comparator.py maps each row's intent and the map's reading to an outcome through one table, and the outcome to an action. The owner's policy says which outcomes fail. - MUST_REACH is satisfied by REACHED and REACHED_VIA_DISPATCH alike: dispatch is evidence provenance, not a correctness level (owner ruling). The spec-only MUST_REACH_DIRECT accepts a direct path alone, and a dispatch-only reading is DIRECT_PATH_GAP. - The outcomes: - SATISFIED and CORRECT; - WIRING_GAP, ARTIFACT_GAP, UNDECIDED, DIRECT_PATH_GAP and ROOT_MISMATCH; - UNEXPECTED_LIVE_PATH and PRODUCTION_LEAK; - REMOVAL_CANDIDATE and DELETE; - MISSING_SYMBOL, and AMBIGUOUS_SYMBOL for a path several definitions share (two `From` impls on one type); - UNSPECIFIED, every production definition no row names. A gap fails unless its requirement is Partial, Missing or Violated in CONFORMANCE §8, where it is reported as the known hole. REMOVAL_CANDIDATE, DELETE and UNSPECIFIED are only reported. - A manifest that cannot be read is refused, never guessed at. It is refused for: - an unknown value; - a requirement MASTER does not define; - a missing exception for an exclusion, a deprecation or a row with no requirement; - a row with no requirement that demands reach; - test-only code that is not MUST_NOT_REACH; - a missing citation; - a duplicate row; - a root that is not an entry point. - `root` is the map's fact, never the comparator's. `requirement_map index` and `fixture` answer `--root-queries` (is this symbol reached from this one entry point?) with the same reach, direct-path and classify rules, and write root-queries.tsv. Asking nothing is `None`, never an empty answer. - The requirements come from MASTER and CONFORMANCE §8 through ci/conformance_evidence.py, still the only Markdown parser. It now exposes canonical_ids, status_rows and requirement_statuses, which its own checks use. Tests and gates: - The fixture gains an intent manifest with a row for every outcome, and the outcome each must produce (intent.tsv, requirements.tsv, intent-expected.tsv), run by `make requirement-map-fixture`. It also gains two `From` impls on one type. - Seven `manifest` mutation cases each change one thing in a copy of the manifest. The comparator must refuse the row, or move exactly the outcomes named, with every other row held to its expected outcome. - rules.tsv gains the `outcome` kind. src/rules.rs reads OUTCOMES from Python (as it reads CHECK_RULES) and checks that each outcome's positive fixture row has it and its negative does not. - `make requirement-map` asks the manifest's root questions. `make requirement-map-intent` runs the comparator, and CI runs it with both builds required. intent.tsv, unspecified.tsv and root-queries.tsv are kept. The manifest holds its header only. Its rows are the next chunk. From the gate: - The root answers come back in the order asked, whatever the builds' order. - A root is checked to be an entry point in each build this host indexed. - The harness checks every table's header and cell count, and tells a comparator crash (stderr) from a failing row. It writes the changed manifest and the report to separate places. * docs(requirements): the intent manifest's requirement rows; 16 Met citations traced (§6.39) The manifest's first rows declare the specifications' intent for the code §8 cites. - **What is covered.** Every canonical code reference of a Met or Partial row (Violated rows cite violating code, not code that must run) becomes a MUST_REACH row. - **Build.** A row goes in the build that reaches its code, preferring its requirement's family (storage to node, the rest to Android). It goes in the family's build only when nothing reaches the code, since storage-spec obligations the client carries run in the Android build. - **Evidence and source.** Each row names the §8 row's tests as its evidence and cites the §8 row. - **Node pins.** MR-SOFI-0070 and MR-STOR-0042 add node MUST_NOT_REACH rows for Route::of and Route::leader: a storage node never computes a cell's route or its leader, and the map shows the node's build reaches neither. Before landing, the comparator read those citations against the canonical Linux map (CI's map of main at 20f63cb). Sixteen Met rows, over fourteen requirements, cited code that no shipped build reaches. Each was traced again from the requirement's text (owner ruling): - 12 stay Met, citing the production code: - a leader comes from position_seed or storage_seed, through RoutedCell::new, Route::of (permute) and Route::leader; - SoFi signatures: verify_precommit and verify_fulfillment; - a setup's identity: setup_ref and verify_setup; - the conjunction: validate's Verdict; - waiting on an unread leader: evaluate; - determinism: dsm_domain_hasher. The dead helpers cited before (position_leader, first_member, verify_signed_object, SignedSofiBody::object_id, Validation::and/all, CanonicalEncode) are named in each note. - MR-STOR-0156 is now Partial. No shipped build checks a completion proof: production builds one and keeps its digest, and nothing reads a kept proof back or checks it. - MR-DSM-0100 is now Partial. The uniform CanonicalEncode is never called, and only per-type canonical encodings exist. CONFORMANCE §6.39 records all of it. Also recorded there: 101 references that name no definition the map holds (module paths, method shorthand, deleted code), which are Stage 3's to canonicalize and are not rows yet. The §7 totals are regenerated: Met 325, Partial 236. Against the canonical map the manifest reads 539 SATISFIED, 4 CORRECT, and 6 WIRING_GAPs that are Partial requirements' known holes: 0 failing rows. On a host that cannot index the node, its 76 rows read not-built (UNDECIDED), and CI requires both builds. ci/conformance_evidence.py passes. Also: ci/intent_comparator.py loses an unused `import csv` (it reads its tables with its own strict reader). * feat(map): entry points bound to their requirements; out-of-scope and test-only fates The manifest gains the owner's remaining first scope. Entry points are bound to what they serve, and production symbols no build reaches get the conservative class fates. The map's uncertainty never becomes intent. Root queries follow the process, not one call. - A path must start at the named entry point. A dispatch on it may rest on a value any entry point made, since the process holds it: the app router is built at startup (JNI_OnLoad, SDK start) and every later request dispatches to it through dispatchIngress. - `reach::reached_with` starts from known type evidence and returns what it gathered. A build's reach starts from none; a root query starts from the whole build's. - `reached` had no production caller left and is gone. - New unit test: a_root_query_dispatches_on_a_value_another_entry_point_made. - New fixture shape: a router JNI_OnLoad installs in a global and `Probe.query()` calls through a trait object (installed.rs). Its manifest row is SATISFIED from `query`. It reads UNDECIDED if the query starts from no evidence (mutation-checked). Entry points (owner ruling: bind each root to the requirements it serves). - A read-only trace of all 71, confirmed by the map: - dispatchIngress reaches the code of 208 of the 213 Android symbols the manifest names; the other 5 are known holes that no entry point reaches. - Those rows, 468 of them, take dispatchIngress as their root. - The node's 72 requirement rows take dsm_storage_node::main, its only entry point. - 67 more entry points get rows saying why each exists: - 36 offline BLE, 6 NFC recovery and 7 dBTC (the six undeclared bitcoin* exports and the builtins guard), as MAY_REACH with the scope exception; - 18 infrastructure (library load, SDK start, UI state and status), as MAY_REACH with its reason. - removeContact and dsm_init_runtime stay unspecified, with no stated purpose, for the owner. Unreached production symbols (owner ruling: conservative class rules). - Dead code in out-of-scope subsystems is MAY_REACH with the scope exception and stays active, never deprecated. That covers dBTC (§8.3 Deferred), emissions, recovery, the hardware anchor crates and the offline BLE transport. - The testing tags module, which declares itself never used by production protocol paths, is test-only MUST_NOT_REACH. - Everything else stays UNSPECIFIED. The comparator's report now gives the action by reading: - dead: a removal candidate pending review; - indeterminate: undecided; - reached: declare its intent. It is reported only. 83 paths that name several definitions in a build stay unspecified rather than become ambiguous rows. CONFORMANCE §6.39 records the rest of what the trace found: - the two ingress points, and removeContact and dsm_init_runtime; - the parked dBTC policy's load-time assert, which can abort the Android library's load; - a committed C header whose nine declarations have no Rust definition; - three §8 rows stale the other way: MR-SOFI-0255's sofi_routes.rs exists, and spool payloads are sealed (MR-DSM-0272, MR-STOR-0146). They are left until traced. Found while verifying: - The comparator counted a definition the build does not compile (another profile's host-only twin of the same path, which reads not-in-artifact) as a candidate. On macOS that made three anchor rows AMBIGUOUS_SYMBOL. - Only compiled definitions are candidates now. With none compiled, a row reads not-in-artifact: ARTIFACT_GAP under MUST_REACH, CORRECT under MUST_NOT_REACH. Against CI's Linux map of main, all 1,062 rows read 0 failing: - Android: 468 SATISFIED, 376 CORRECT, 5 known holes; - node: 71 SATISFIED, 141 CORRECT, 1 known hole. Locally the Android rows read the same, and the node's 92 read not-built, which CI's --built android,node evaluates. From the gate: the fixture's Probe.query() expects its installed router (JNI_OnLoad installs it first) instead of turning a missing one into 0. * docs(requirements): each open §6.39 finding carries its status; refusals reach the terminal Owner ruling (2026-09-29): a finding the investigation established stays in §6 even when its remediation is outside the change that found it, marked deferred. Unfinished implementations of active requirements are never deleted as dead code, and what awaits the owner is recorded as open, not settled. §6.39's Open list now says which each item is: - *confirmed, remediation deferred*: MR-STOR-0156, whose check_completion_proof and completion_proofs::get are kept to be wired; MR-DSM-0100, whose CanonicalEncode is kept; the stale C header; the 101 unresolved references (Stage 3); - *owner decision*: removeContact and dsm_init_runtime (keep, wire or delete), and whether the parked dBTC policy's mismatch should stop the library loading; - *unconfirmed*: the three Missing rows whose code exists, until traced; - *recorded, not a defect*: the entry points, which the manifest binds. ci/intent_comparator.py prints a refusal to stderr. `make` redirects `root-queries`' stdout into a file, and a malformed manifest's refusal disappeared into it while make aborted. ci/requirement_map_mutations.py reads an expected refusal from stderr, and fails a case on any other stderr. * fix(map): a root query names only definitions in its build's crates CI's first run of #1055 (the first to index the node with the manifest's root questions) refused: `a root query's entry point dsm_storage_node::main names 2 definitions the node build compiles`. The node's index also holds its build script's `main`, which shares the binary's path and is not in the build. That is the class #1054 fixed for sentinel and fixture lookups. A root query now looks paths up only among definitions in the build's own crates. - The fixture gains the question: MR-FIX-0008 asks whether probe::main, a path the fixture's build script shares, is reached from the entry point. Before the fix the fixture refused with CI's exact error; it now reads SATISFIED. - ci/conformance_evidence.py requirement_statuses refuses a canonical ID §8 gives no row, where it used to map the ID to None. The comparator reports that refusal on stderr. A macOS host cannot index the node, which is why the local run missed this. The Android root answers are byte-identical before and after. The map check is clean, 40/40 mutation cases pass, and the fixture reads 90/90 with 0 unexpected intent outcomes.
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 Code map job is red on
mainsince #1053; this makes it greenOn its first Linux run, #1053's adversarial check failed for four reasons. The first two are what this PR fixes; the other two are consequences:
ci/requirement_map.{roots,counts}.tsv. This PR adds them, taken from that run'scommitted.tsv:main;IND_UNLINKED_IMPLreadings flow from three seeds (impls of outside traits on[u8; N]anddyn).node dsm_storage_node::mainread['None None', 'reached REACHED']: the second definition was the build script'smain, which is outside the node build. Now:probe::main, beside its build script'smain) and a committed fixture sentinel. With the old lookup, it reproduces CI's failure exactly.Also fixed, found through the Gemini gate
cargo treeprintsdsm-anchor-coreanddsm-anchor-verifierwith hyphens, and the features parser stored them under_. So those crates' call gates were read with no features, and their feature-leak exclusions were skipped.cfgs::feature_key), and an unknown package is an error instead of an empty set.cargo treeline is an error, not skipped.compiled()drops an edge only when no site compiles.make requirement-map-fixturenow builds the fixture and runs its test.no-definition, so the checks' skips are accounted for. They are other crates' symbols, and the workspace's generated or macro-written code. The counts are Android 1,917 / 1,950 and node 1,838 / 1,883. A jump fails.[u8; 32], not[u8;).Verification
main(646aa4e3c).requirement_map: 96/96 (--release); clippy-D warnings, fmt and the guard are clean.make requirement-map-fixture: the fixture builds, its test passes, and all 87 readings are as expected.make requirement-map-check(local, Android): 0 contradictions, 25 sentinels, 70 entry points, counts unchanged.make requirement-map-mutations: 33/33 cases. The 17 planted cases also pass against the CI Linux map.