Skip to content

feat(map): stage 1c, the adversarial layer over the code map - #1053

Merged
cryptskii merged 1 commit into
mainfrom
feat/code-map-adversarial-layer
Sep 29, 2026
Merged

cryptskii merged 1 commit into
mainfrom
feat/code-map-adversarial-layer

Conversation

@cryptskii

Copy link
Copy Markdown
Collaborator

Stage 1c: the adversarial layer over the code map

The map stays the only source of truth. This layer attacks it and turns every weakness it confirms into a deterministic rule with a regression test. It never marks anything Reached, and it adds no exceptions.

Defects it found in the map (all fixed, each pinned by a fixture reading)

Defect Wrong reading Fix
A constructed value counted as dispatch evidence for an associated function probe::Built::Example::static_fn read Reached Only a type named where a call dispatches counts as evidence: a type argument or an associated item
self::, super:: and crate:: calls in a macro body fitted no candidate risked a false Dead (through_self) The call's site decides, or every candidate stays open
A call name the build also uses from outside the workspace was read as the outside item's false Dead Every workspace candidate stays undecided
Calls in code the shipped build does not compile counted as its calls inflated undecided readings (unattributed 38 → 0) Each call carries its cfg gates
The Android crate list was incomplete the checker found witnesses through unreached nodes Each build's crates come from its own cargo tree
A reference inside a statement the build's features turn off counted as an edge; an undecidable gate was read as decided false Reached (called_under_leak, undecided_twin, the gated JNI export) Edges carry every site they are written at, and each build keeps only the sites its cfg compiles. An edge into an undecided item is uncertain. Android has none today (uncompiled-edges 0)

What the layer is

  • Fixture crate (tools/requirement_map/fixture): 86 readings through the real pipeline, covering:
    • receivers, associated functions, UFCS, trait objects;
    • outside traits, macros, scopes, format captures, call cycles;
    • JNI roots: declared, renamed, undeclared, internal, gated;
    • cfg leakage.
  • Mutation cases (mutations.toml, 33 in all), each run in a temporary copy that is removed afterwards:
    • 14 fixture changes;
    • 1 real-tree declaration change;
    • 1 real-tree re-index, where the core router adapter is never built;
    • 17 faults planted in copies of the fixture map and the real map.
  • Check (ci/requirement_map.py check): 17 declared rules.
    • Contradictions in the map's own tables, including a new closure rule: a proven non-dispatch step from a reached definition must lead to a reached one.
    • 26 sentinels, the committed entry points, and reading counts. A jump in Indeterminate readings is refused.
  • Rule coverage (tools/requirement_map/rules.tsv, checked by src/rules.rs): every code, edge kind, entry-point kind and check rule, with its unit tests, fixture readings, mutation cases and sentinel. Where a column is empty, the row says why. The test fails when a rule has no row, a citation is missing, or a code's examples read another code.
  • CI: after the map, the check and every mutation case run. committed.tsv, the entry points and counts this map reads, is kept as an artifact for review.

Known before merge

  • The storage node is only indexed on Linux, so its entry-point and count rows are not committed yet. The first CI run of the check will name them as unexplained. I will commit them from that run's committed.tsv, after you review them.

Verification on 8b6abf0be (local, macOS; the node build is indexed only on Linux)

  • requirement_map unit tests (--release): 93 passed, including the rule-table test.
  • Lint: make lint exits 0, and cargo clippy -p requirement_map --all-targets -D warnings is clean.
  • Real-code guard against origin/main: exit 0.
  • make requirement-map-fixture: all 86 readings as expected.
  • make requirement-map + make requirement-map-check:
    • 0 contradictions;
    • 25 Android sentinels checked, 0 lost (the node sentinel is checked on Linux);
    • 70 Android entry points, 0 new, 0 lost;
    • counts unchanged.
  • make requirement-map-mutations: 33 cases run, 0 failed:
    • 14 fixture changes;
    • 1 declarations case;
    • 1 real-tree re-index;
    • 17 planted faults.
  • Mutation checks of the new rules. Each rule was removed in turn and its named test went red:
    • the site filter;
    • the undecided-item doubt;
    • associated-of dispatch;
    • the unread-declaration root;
    • the closure rule;
    • the rule table: a row removed, a sentinel of the wrong code, and an unlisted code.
  • Path handling change: after replacing the lossy decodes, all 22,146 readings outside the mapper crate are identical to before.
  • Gemini gate: satisfied in round 3.
    • Round 1 findings fixed: the root-package prefix bug, lossy UTF-8, the re-index mutation now a plain deletion, and a fixture default.
    • Round 1 findings rejected with evidence: the exit status (executed: 1), and the fixture's deliberate shapes and planted faults.
    • Round 2: every lossy decode in the crate removed.

The map stays the only source of truth. This layer tries to falsify it and
turns each weakness it confirms into a deterministic rule with a regression
test. It never marks anything Reached.

Defects it found in the map, each fixed and pinned by a fixture reading:
- An associated function read Reached from a constructed value; only a type
  named where a call dispatches (a type argument, an associated item) now
  counts as evidence (probe::Built::Example::static_fn).
- `self::`, `super::` and `crate::` calls in a macro body fitted no
  candidate, which risked a false Dead; the call's site decides, or every
  candidate stays open (probe::macros::through_self).
- A call name the build also uses from outside the workspace was read as the
  outside item's; every workspace candidate now stays undecided.
- Calls in code the shipped build does not compile (test modules, gated-off
  items) counted as its calls; each call carries its cfg gates.
- The Android crate list was incomplete. The checker found witnesses through
  unreached nodes in dsm-anchor-core and dsm-anchor-verifier; each build's
  crates now come from its own `cargo tree`.
- A reference in a statement the shipped build's features turn off counted as
  an edge (a false Reached), and a gate the map cannot decide was read as
  decided. Each edge now carries every site it is written at. A build keeps
  only the sites its cfg compiles, and an edge into an undecided item is
  uncertain (probe::gates::called_under_leak, undecided_twin,
  roots::Java_fixture_Probe_gated). The Android map has none today
  (uncompiled-edges 0).

The layer:
- The fixture crate (tools/requirement_map/fixture) covers these shapes,
  each read through the real pipeline (86 readings):
  - receivers, associated functions, generic dispatch, UFCS, trait objects;
  - outside traits, function-local impls, ambiguous and never-made types;
  - macros that define, write impls, call, use common names, or write two
    items over one extent;
  - block-item collisions, format captures, call cycles;
  - JNI roots: declared, renamed, undeclared, internal, gated;
  - cfg(test), debug_assertions, and dev-dependency feature leakage.
- Mutation cases (tools/requirement_map/fixture/mutations.toml, run by
  ci/requirement_map_mutations.py in temporary copies):
  - 14 fixture changes;
  - 2 on the real tree: a JNI declaration removed, and the core router
    adapter never built (a full Android re-index);
  - 17 faults planted in copies of the fixture's map and the real map.
- The check (ci/requirement_map.py check): 17 declared rules.
  - Contradictions in the map's own tables, including a new closure rule: a
    proven non-dispatch step from a reached definition must lead to a
    reached one.
  - The committed sentinels (26, one real example per rule where one
    exists), entry points and counts, with any jump in Indeterminate
    readings refused.
- tools/requirement_map/rules.tsv lists every code, edge kind, entry-point
  kind and check rule, with its unit tests, fixture readings, mutation cases
  and sentinel, or why one is missing. src/rules.rs fails when:
  - a rule has no row;
  - a citation does not exist;
  - a code's examples read another code.
- Root kinds are named constants (graph::root).
- Paths are written from their UTF-8 components. A name that is not UTF-8 is
  an error, never replaced, and a package at the repository root reads
  `src/`.
- CI runs the check and every mutation case after the map; committed.tsv
  (what the map reads) is kept for review.
@cryptskii
cryptskii merged commit 646aa4e into main Sep 29, 2026
25 of 26 checks passed
@cryptskii
cryptskii deleted the feat/code-map-adversarial-layer branch September 29, 2026 03:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant