fix(#944): verified machine-readable origins for compiler-introduced object branches — the 9 were never unattributed, the real 33 now are attributed - #967
Merged
Conversation
…anches + cover 'if' as a source decision The 9 'only-in-synth' divergences in #944 are provenance ENTRIES for real source ops (6 unconditional br, 3 folded select) that witness's manifest never instruments — not object branches without WASM origin. The genuine compiler-introduced conditional branches on that module are the bulk-memory expansion guards, previously surfaced only with a guessing free-text note. - ObjectCondBranch gains additive 'origin': kebab-case, derived from the encode-time line_map (never guessed), only for disassembly-verified families: bulk-memory-fill-loop, bulk-memory-copy-loop, division-trap-guard. Unverified families stay declared-unattributed. - WasmOp::If is now a covered source decision (preserved entry, resolved branch) — its conditional branch was mis-bucketed as introduced. - Unit tests incl. the non-vacuity negative control (unverified family must NOT be labeled). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
…e API session limit Committed by the hub coordinator so the work survives the interruption. NOT reviewed and NOT complete: the lane stopped mid-task, so this is a restore point, not a claim that anything is verified. The lane resumes from here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ibuted floor 0, closed vocabulary Integration gate over scripts/repro/provenance_introduced_944.wat (cortex-m3 --all-exports --relocatable --emit-provenance): - pins EXACT per-origin counts (1 fill-loop, 3 copy-loop, 3+1+1+1 division guards) so an attribution loss OR an excuse-widening both turn red (mutation-verified: unmapping MemoryCopy -> 3 UNATTRIBUTED panics; blanket-labeling trips the synth-core negative control); - asserts the if decision is covered+resolved, not bucketed as introduced; - holds the module-wide unattributed floor at 0 with a closed, disassembly-verified origin vocabulary. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
This was referenced Aug 14, 2026
avrabe
added a commit
that referenced
this pull request
Aug 14, 2026
…MS (#970) (#972) Six artifacts still read `proposed` while their work is on main. Re-graded to `implemented`, so the release-readiness query means something: RQ-57-SKIPEXIT #952 (#960) declined REQUESTED export exits non-zero RQ-57-PROVGAP #944 (#967) verified origins for introduced branches RQ-57-DOCSWEEP #946 (#968) Tiers 2+3 honesty sweep RQ-57-SPWILD #946 (#969) writes_sp 175-of-222 wildcard expanded RQ-57-A64PARAM #851 (#971) aarch64 written-param homing RQ-561-ZEROMEM #953 (v0.56.1) shipped two releases ago, never re-graded NEW: RQ-57-COUNTPARAMS (#970). It has no artifact because nobody planned it — the #851 lane found it while fixing the aarch64 instance of the same shape. An unplanned finding with no artifact is invisible to the release query, which is exactly how #933 slipped a release, so it gets one now. What it records that the issue alone does not: * The severity differs by BACKEND and both halves are stated at the confidence they were established. RISC-V is EXECUTED-confirmed: with a poisoned stack the function returns 0xDEADBEEF — an UNINITIALISED stack slot, i.e. previous frame contents. That is information disclosure, not a wrong value. ARM's exact wrong value was INFERRED FROM DISASSEMBLY, never executed; the artifact says so, because the #851 lane's first draft stated it as measured and the advisor caught it. * ARM is only INCIDENTALLY correct on the simple shape (a merge-point `str r1,[sp]` catches the still-live param) and breaks once a call clobbers the param register — so "ARM looks fine" is not evidence. * Red-first must be per-backend BY EXECUTION. Assuming one backend's evidence transfers is what left this latent after the aarch64 fix. * The named residual survives: the `None` branch of `current_func_param_count` still uses the unsound heuristic — unreachable from the CLI, reachable via the direct `compile_function` API. Per the user's direction this ships in v0.57 rather than as a v0.56.3 patch. rivet: errors unchanged (50 before and after); warnings 164 -> 166, which is the two every artifact in this file carries (trailer-reference naming, and the `verifies` link that lands with the test) — verified identical for RQ-57-SPWILD and RQ-57-GPIO, so this is one more artifact of the same shape, not a new defect. claim_check 43/43. Refs #970 Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
avrabe
added a commit
that referenced
this pull request
Aug 14, 2026
* chore(release): v0.57.0 assembly — "the checkers were the defects" Nine artifacts. In five of them the bug was in the machinery that checks the compiled code, not in the compiled code: #975 ArmSemantics silently no-oped 87 of 222 ops — a Rocq-proved, default-on rotl rule was "validated" by a model that executed neither of its instructions #976 the gpio differential CANNOT discriminate the miscompile it guards — complementary conditions, so no input to that driver can #969 writes_sp claimed exhaustiveness over a wildcard absorbing 175 of 222 #967 the "9 unattributed branches" were manufactured by witness's own hardcoded divergence text #979 the prescribed release: back-fill would have written 32 false entries, with the one correct pre-existing value beside them as the disproof The unifying property is that each of those checks COULD NOT FAIL. This release makes them able to fail and proves it by making them fail on purpose. Also fixed, and the most severe item: #974 — a conditionally-written parameter was demoted to a zero-init local on ARM and RISC-V. Exit 0, no decline, wrong code; on RISC-V it reads an UNINITIALISED stack slot (0xDEADBEEF under a poisoned stack), an information-disclosure shape. ARM behaves identically — which the issue predicted otherwise, and only execution settled. Release surfaces, all four swept and checker-confirmed at 0.57.0: Cargo.toml [workspace.package] + 10 path-dep pins MODULE.bazel, npm/package.json, Cargo.lock (cargo metadata) scripts/check_version_pins.py: OK Derived artifacts regenerated (--emit-status): artifacts/status.json, docs/status/FEATURE_MATRIX.md. Claim gate: 43/43. Open by design, named not hidden: #973 (ARM select miscompile, found only because a lane compiled ARM fixtures — which CI never does), #977 (ELF-magic flake, second sighting), #938 (breaking object 0.x-minor bump, auto-merge disabled), #912 (open with four remaining: items). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L * fix(release): act on the v0.57.0 cold review — 8 accuracy defects, 4 of them mine Cold review of the assembled release. Nothing blocked the tag; everything below is accuracy. Four of the eight were errors in the CHANGELOG I had just written, which is the reason the review exists. THE GENERALIZABLE FINDING, and it is pointed given this release's theme: `check_generated_fresh` byte-compares the RENDERED FEATURE_MATRIX against the TEMPLATE. `render_feature_matrix` only substitutes `{{...}}` fields, so the gate proves the render is faithful to the template — and NEVER that the template is faithful to the code. Every stale number below lives in template prose no substitution touches. In a release titled "the checkers were the defects", that is the checker that cannot fail. Three independent stale numbers survived a green 43/43. USER-FACING FALSE, verified by compiling rather than by reading: FEATURE_MATRIX listed "writing a PARAM local in a LEAF function" as a LOUD DECLINE on aarch64. #971 shipped exactly that. A leaf `local.set` on a param compiles: 32 bytes of machine code, exit 0. Also corrected in the same row: homing is no longer non-leaf-only, and the float-param decline widened with it. Fixed in the TEMPLATE (the render is generated) + regen. MY CHANGELOG ERRORS: * "145 of the 175 pre-declined / 30 reachable" matched no partition. The shipped source (wcet_loops.rs:1232) says 142 give up with `true`, leaving 33. Re-derived: 142/33. Corrected. * "demoted to a zero-initialised local" is wrong for the two backends the entry is about — zero-init is gated on first-access-being-a-READ, and in the cond-write shape the first access IS the write, so nothing initialises the slot. That is WHY it reads poison; the old wording made an information-disclosure bug sound like a benign wrong value, and contradicted the entry's own next sentence. * "Nine artifacts" — there are ten, and RQ-57-DOCSWEEP (#946/#968) had NO CHANGELOG entry at all despite touching CLAUDE.md, coq/STATUS.md, PROJECT_STATUS.md, the matrix template and eight source files. Added. * "Five in-tree oracles took that opt-in" — eight scripts plus three Rust tests. All eight carry floors, but `i64_param_518_riscv_loudskip`'s is `compiles >= 1`, which is a floor and NOT the "tight" one the paragraph claimed for the set. Named rather than folded into the claim. STALE COUNTS (the template-prose class above): ORACLE_WIRING.md, the matrix template and claims.yaml all said "137 oracles / 295,621 emulator entries". Re-derived independently — and the reviewer's number and mine agree exactly: 144 oracles / 296,059. Both `count-min` pins moved 137 -> 144 with them (same `emulations >=` pattern, two sibling claims); the pinned verbatim texts moved too, or the ledger would have gone red against its own corrected doc. REVERSE STALENESS (a doc calling SHIPPED work missing): `synth verify` declines shift rules citing "SMT modeling of the variable-shift register encoding is an open gap". #975 CLOSED that gap — it modelled LslReg/LsrReg/AsrReg/RorReg as Rm<7:0> (ARMv7-M A7.7.68/70/12/117) and moved five lowerings Invalid -> Verified. Both comments corrected to say what is true: the modelling gap is closed, the remaining decline is a WIRING residual. Behaviour deliberately unchanged — rewiring the rule table is a verification-surface change, not release assembly. Filed as #981. ARTIFACT: RQ-57-PROVGAP still asserted "9 object branches with no WASM origin" as fact while its own PR disproved it. Outcome recorded, as RQ-57-BACKFILL already did. docs/architecture/CRATE_STRUCTURE.md said 18 crates; there are 19. A RECURRENCE — PROJECT_STATUS.md cites this exact drift as why it was gutted in the #946 sweep, and one file over it was live again. Gates after: claim_check 43/43, check_version_pins OK at 0.57.0, cargo check -p synth-cli rc=0. Refs #980, #981 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L * style: rustfmt the #975 decline-reason comment (indent 14 -> 12) My own miss: I ran cargo check on the edited file but not cargo fmt, and Format is a required context. The comment content is unchanged — only the indentation rustfmt wanted. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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.
#944 — the 9 "object branches with no WASM origin": triage, attribution, gate
The premise was false — in a good way
Reproducing the count from the committed evidence (
pulseengine/galebenches/gust/drivers/mcdc/evidence/switch-thin.{witness,provenance}.json, joined exactly aswitness object-dispositiondoes, on(func_index, instruction_offset)) gives exactly the 9 keys from the issue — and every one of them is a provenance entry for a real source WASM op, not an object branch without origin:wit_bindgen::rt::cabi_reallocbrpreserved…fsm::Guest>::tickbrpreserved…fsm::Guest>::run_switchbrpreserved…fsm::Guest>::run_switchselectfolded-predication…fsm::Guest>::run_switchbrpreserved…fsm::Guest>::mark_resumedselectfolded-predication_export_mark_swapped_cabiwrapperbrpreserved…fsm::Guest>::current_windowselectfolded-predicationbrpreservedCategory verdict: none of the 9 is a miscompile, and none is even an unattributed compiler-introduced branch. They are 6 unconditional
brs and 3selects folded to predication — source ops synth's map records faithfully, which witness never instruments (its manifest carries onlybr_if/br_table_target/br_table_default/if_then/if_else).only_in_synthin witness's reconciler means "synth entry with no witness branch record"; its hardcoded divergence text — "(object branch witness never instrumented)" — is what turned "uninstrumentedbr/select" into "object branch with no source", and the issue's framing followed from that message. The 9→8→4 movement across the source variants in the follow-up comment tracks whichbr/selectsource ops survive each source-level simplification — consistent, and the "irreducible 4" are thebr/selectops incabi_reallocand the export wrappers. Witness-side classification (filter/labelonly_in_synthby theopfield synth already emits) is witness#109 territory.The REAL unattributed population — and its attribution
The genuinely compiler-introduced conditional branches on
switch-thinare the 33resolved: falseentries inobject_cond_branches(invisible to witness's join, surfaced only with a free-text guessing note). All 33 trace, via the encode-timeline_map, tomemory.fill/memory.copylowerings. Reproduced and disassembly-verified on current main (ELF bytes via pyelftools+capstone, neversynth disasmtext), per introducing op:memory.fillcmp; bhs, zero-trip safe)memory.copycmp dst,src; bhi) + forward and backward copy-loop bound testsi32.div_sINT_MIN/-1overflow guard pair, each skipping audfi32.div_u/i32.rem_s/i32.rem_uifProbed-and-clean on the same path (no cond branches emitted):
call_indirect,memory.grow, i64 div/rem (software), i64 shifts/clz/eqz/compares. No category (b) — no branch that should not exist — was found anywhere in this lane.What this PR changes
crates/synth-core/src/provenance.rsObjectCondBranchgains an additiveoriginfield (absent whenNone; witness's serde ignores unknown fields, wire format stayssynth-provenance-v1): a kebab-case machine-readable origin derived from the op the branch'sline_mapentry traces to — never guessed. Closed, disassembly-verified vocabulary:bulk-memory-fill-loop,bulk-memory-copy-loop,division-trap-guard.origin: Nonewith the op named in the note — unexplained-but-declared beats a confident wrong label (the issue's own follow-up demonstrated the plausible label being wrong).ifis now a covered source decision ("If"preservedentry; its conditional branch resolves). Bonus for the witness join: witness does instrumentif_then/if_else, so coveredifentries can now match instead of falling intono-provenance.scripts/repro/provenance_introduced_944.wat+crates/synth-cli/tests/provenance_introduced_origin_944.rs— the gate:MemoryCopy→ 3UNATTRIBUTEDpanics; blanket-labeling (_ => Some("division-trap-guard")) trips the synth-core negative-control unit test.No CHANGELOG/version/status.json changes (release-hub constraint). No doc-claim changes;
claim_checkstays 43/43.Closes #944.
🤖 Generated with Claude Code
https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L