feat(#777): VCR-VER-003 — per-compilation static-data addressing validation (the #757 miscompile class made unrepresentable)#785
Merged
Conversation
…first gate) New synth-verify::addr module: per-compilation concrete byte-equality that every static-data reloc resolves to the runtime-correct byte (active data segments applied in declaration order, later-wins). This lifts the post-hoc #757 differential oracle (scripts/repro/mem757_gale_differential.py) into a per-compilation validator so a wrong-segment resolution FAILS by construction. Invariant (per compiled relocatable object, N data segments): for every emitted RelocResolution (seg_index K, addend A), the byte the packed .data serves (seg[K].bytes[A]) EQUALS the byte the RUNTIME image holds at the original access address (seg[K].off + A). The runtime image is reconstructed independently of K (apply all segments in declaration order, later-wins), so the truth side never mirrors the code under test — the VCR-ORACLE mirror-pinning vacuity mode is structurally excluded. Non-vacuous red-first gate is a PERMANENT parameterized unit test (red_on_first_match_green_on_last_match): the tie-break policy is an argument (resolve_owner last_wins), so the SAME validator goes RED on .position() (first-match, the #757 miscompile) and GREEN on .rposition() (last-match, correct) for a 3-segment overlap with DISTINCT bytes. No source revert needed. Plus partial-overlap-tail-wins, non-overlapping-both-consistent, out-of-range-mismatch. Concrete byte-equality, not fabricated SMT: mirrors VCR-VER-002 structure (verdict enum + per-compilation gate) but does the honest direct comparison. Backend-agnostic, always-compiled (not arm-gated). Part of VCR-VER-003 (epic #242). Wiring into the reloc emission path follows. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
…o main.rs Move the validator from synth-verify into synth-core::static_data_addr (single source; synth-verify::addr re-exports it) and call it in the mixed-split reloc retargeting path (main.rs). It runs on EVERY compilation — the shipping build is `--features riscv`, NOT `verify`, so a verify-gated check would stay dormant in exactly the build that shipped #757 four times. The retarget loop now records the EMITTED (seg_index k, addend) per static-data reloc — never recomputed — and after the loop validate_reloc_resolutions() proves each resolves to the runtime-correct byte (segments applied in declaration order, later-wins). A wrong-segment resolution hard-errors the compile (anyhow::bail!) with the offending symbol/addend/served/runtime bytes. Red-first demonstration (end-to-end through main.rs), captured for the PR: * `.rposition()` (shipped): gale's real loom.wasm compiles clean. * revert to `.position()`: compile FAILS — func 9 reloc @ 0x250: __synth_wasm_seg_0+0x8 -> linmem 0x100008 serves 0x02 but the runtime image owns 0x67 ('g') exactly gale's historical got=[2,0,0,0,..] = __synth_wasm_seg_0+8 (#757). * restore `.rposition()`: clean again. Durable regression gate: crates/synth-cli/tests/vcr_ver_003_addr_777.rs compiles the REAL gale module and asserts exit 0 — because the validator is wired unconditionally, this GREEN goes RED the instant someone reverts the fix. Honours the "the real module must live in CI; 7 synthetic shapes were all green" lesson. The synthetic 3-overlap DISCRIMINATION (same validator RED on .position(), GREEN on .rposition()) is the permanent unit test in synth-core. Concrete byte-equality, not fabricated SMT: the truth image is reconstructed independently of the chosen segment, so mirror-pinning vacuity is structurally excluded. Part of VCR-VER-003 (epic #242). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
… CLAUDE.md Add the VCR-VER-003 roadmap artifact (status: implemented), a CI-gated claims.yaml entry (SYNTH-VCR-VER-003-STATUS, 20/20 claims hold), and a Track C North Star note in CLAUDE.md. Claim wording matches what is actually checked: "per-compilation concrete validation that every static-data relocation resolves to the runtime-correct byte (overlapping active segments later-wins)" — NOT "SMT-proved addressing". The gate re-derives status=implemented + the two evidence files (synth-core/src/static_data_addr.rs, synth-cli tests/vcr_ver_003_addr_777.rs). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
Clarify the roadmap description: the validator runs on the native-pointer relocatable mixed-split path (the only path that emits __synth_wasm_seg_K retargets and thus the only path the #757 class can arise on), so "catches #757 on every module" holds without implying it runs on every compile in general. Name the single-resolved-byte scope (matches the reference oracle) — multi-byte span-boundary reads are a named follow-up alongside all-addressing + RV/AArch64 bases. Wording-only; claims 20/20 unaffected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
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.
VCR-VER-003 (#777, epic #242) — Wave-1 Lane 1
Lifts the post-hoc #757 differential oracle (
scripts/repro/mem757_gale_differential.py) into a per-compilation validator, so the overlapping-active-segment wrong-segment miscompile FAILS at compile time on any module instead of shipping silently. #757 survived four releases because value-differential oracles are coverage-limited (7 synthetic reconstructions were all green; only gale's real fusedgust:osnode hit it).The invariant (concrete byte-equality, not SMT)
For a compiled relocatable object with N active data segments, for every emitted static-data relocation
(symbol = __synth_wasm_seg_K, addend A): the byte the packed.dataserves (seg[K].bytes[A]) EQUALS the byte the runtime linear-memory image holds at the original access address (seg[K].off + A), where the runtime image applies every active segment in WASM declaration order, later-wins.The runtime image is reconstructed independently of the chosen K — it is derived only from the segment list — so the truth side never mirrors the code under test. The mirror-pinning vacuity failure mode is structurally excluded.
There are no symbolic operands (unlike VCR-VER-002's trap VCs), so this is an honest direct byte-comparison, not fabricated SMT. Claim wording matches.
Unconditional (runs in the shipping build)
The validator lives in
synth_core::static_data_addrand is called directly in the mixed-split reloc retargeting path (synth-cli/src/main.rs). It runs on every compilation — the shipping/release build is--features riscv, notverify(confirmedrelease.ymlL33-39), so a verify-gated check would have stayed dormant in exactly the build that shipped #757 four times.synth-verify::addrre-exports it (VCR-VER-002 structure parity), single source, no mirror drift.Trigger scope: the check fires on the native-pointer relocatable mixed-split path (
--native-pointer-abi --relocatable+ themixed_separablegeometry) — the only path that emits per-segment__synth_wasm_seg_Kretargets, and thus the only path the #757 class can arise on. On aMismatchthe compile hard-errors (anyhow::bail!) naming the offending symbol/addend/served/runtime bytes.Non-vacuous, red-first gate
Permanent discrimination unit test (
synth_core::static_data_addr::red_on_first_match_green_on_last_match): the SAME validator, on a synthetic 3-overlap module with distinct bytes, returnsMismatchon the.position()(first-match, the #757 bug) resolution andConsistenton the.rposition()(last-match, correct) resolution. The tie-break is an argument (resolve_owner(last_wins)), so this is a permanent test, not a one-time source revert. Plus partial-overlap-tail-wins, non-overlapping-both-consistent, out-of-range-mismatch.Durable end-to-end gate (
synth-cli/tests/vcr_ver_003_addr_777.rs): compiles gale's realloom.wasmand asserts exit 0. Because the validator is wired unconditionally, this GREEN goes RED the instantmain.rsreverts.rposition()→.position(). Empirically confirmed both directions:0x02is exactly gale's historicalgot=[2,0,0,0,..]=__synth_wasm_seg_0+8;0x67='g', the string's real first byte. The gale GREEN is non-vacuous — the RED demo proved gale actually reaches the retarget path (func 9 reloc @ 0x250fired), so GREEN means "validator ran and passed," not "never executed." (Honours the "the real module must live in CI; synthetic shapes were all green" lesson.)Ledger / status
claims.yaml: newSYNTH-VCR-VER-003-STATUS(20/20 claims hold).artifacts/verified-codegen-roadmap.yaml:VCR-VER-003added, status implemented.CLAUDE.md: Track C North Star note.Verification
cargo test --workspacegreen (default config — includes both new gates viaci.yml:53).cargo test -p synth-verifygreen (225 tests).cargo fmt --check+cargo clippy --workspace --all-targets -- -D warningsclean.Bounded (named follow-up)
Reloc/overlap class only. The validator checks the resolved byte per reloc (matching the reference oracle); multi-byte accesses spanning an owning segment's packed-length boundary, all-addressing coverage, and the AArch64 / RV32 static bases are named follow-ups.
🤖 Generated with Claude Code
https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L