Skip to content

feat(#396): synth-provenance-v1 branch-transformation map + reconciliation gate (VCR-DEC-003)#774

Merged
avrabe merged 7 commits into
mainfrom
feat/45-396-mcdc-provenance
Jul 16, 2026
Merged

feat(#396): synth-provenance-v1 branch-transformation map + reconciliation gate (VCR-DEC-003)#774
avrabe merged 7 commits into
mainfrom
feat/45-396-mcdc-provenance

Conversation

@avrabe

@avrabe avrabe commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

VCR-DEC-003 (#396, witness#130) — synth-provenance-v1 source-to-object branch-transformation map

synth's lowering changes the branch structure between the WASM source and the ARM object (cmp→select fusion → IT-block predication, br_table splitting, wide-branch expansion). witness measures MC/DC on the WASM component; to certify the OBJECT it must reconcile each object-level branch/condition back to its source condition. Without a provenance map that reconciliation is impossible — the #396 / witness#130 source-to-object traceability gap. This PR ships the emitter + the non-vacuous reconciliation gate.

Deliverable

1. Emitter (--emit-provenance) — writes a synth-provenance-v1 JSON sidecar (<output>.provenance.json). Frozen-safe: the ELF is byte-identical with and without the flag (verified on control_step.wasm). Derived post-hoc at the CLI from the compiled op stream + op_offsets + the ARM line_map + a new object-branch side-table BranchMap (crate::backend::BranchClass, captured at the ARM encode loop — the one datum post-hoc derivation couldn't recover: which emitted instruction IS a conditional branch vs a predicated move).

2. Reconciliation gate (provenance_reconciliation_396, CI job provenance-gate) — parses the sidecar as an independent consumer and asserts the two properties #396 exists to guarantee. Verified non-vacuous: mislabeling SelectMove as CondBranch turns the gate RED.

Schema synth-provenance-v1

{ schema, module, functions: [
  { func_index, name,
    entries: [ { instruction_offset,   // wasm byte offset — witness join key
                 wasm_op_index, op, kind, object_pcs, count?, scry_evidence? } ],
    object_cond_branches: [ { pc, wasm_op_index?, instruction_offset?, resolved, note? } ] } ] }

kind ∈ preserved | folded-predication | split-into-object-branches | eliminated-constant.

Fixture source→object correspondence (provenance_branches_396.wat)

source op (wasm off) kind object realization
br_if @47 preserved cond branch @0x12
select @70 folded-predication IT-block predicated move @0x32 (no branch)
br_table @81 split-into-object-branches (count 2) cond @0x3c, cond @0x40, uncond @0x42
br @95 preserved uncond branch @0x50

object_cond_branches: all 3 real object conditional branches (0x12, 0x3c, 0x40) reconcile back to their source condition — clause (a) satisfied non-vacuously. On a real fixture (msgq_put) an uncovered trap-guard branch is surfaced resolved:false with a note, never hidden.

Bounded v1 — named follow-ups

  • optimized ir_to_arm on some op shapes drops source_line; such functions are loud-declined from the sidecar (warn + skip) rather than mislabeled.
  • i64-expansion / div-mem trap-guard object branches are surfaced-unresolved, not reconciled.
  • RISC-V / AArch64 carry no branch_map (ARM-Thumb only in v1).
  • witness#130 object-disposition end-to-end compose.

Verification

  • 3 emitter unit tests (synth-core::provenance) + reconciliation gate; full workspace green (2278 tests). ELF byte-identity confirmed. rivet validate non-xref error count unchanged (0). Roadmap VCR-DEC-003 → implemented.

Coordinates conceptually with witness#130 (referenced); the map + parse-assert gate live entirely in synth.

🤖 Generated with Claude Code

https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L

avrabe and others added 7 commits July 16, 2026 00:01
…VCR-DEC-003)

Add the object-branch-class side-table the provenance emitter needs to
enumerate real object conditional branches (line_map records which wasm op an
instruction came from, not whether it IS a conditional branch). Captured at the
ARM encode loop alongside line_map; additive metadata, .text byte-identical.

- synth-core: BranchClass enum + BranchMap type + CompiledFunction.branch_map
- synth-backend/arm: classify_arm_branch + populate in the encode loop
- riscv/aarch64: empty branch_map (ARM-only in v1)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
synth-core::provenance — the branch-transformation map. Per-function derivation
from op stream + op_offsets + line_map + branch_map:
- preserved (br_if/br), folded-predication (select), split-into-object-branches
  (br_table), eliminated-constant (folded/fact-spec-dropped source branch).
- object_cond_branches carries clause (a): every REAL object conditional branch
  (from BranchMap) reconciled back to its source op; unresolved (i64/trap-guard)
  branches surfaced with a note, never hidden.
- 3 unit tests: preserved+folded, unresolved-surfaced, eliminated-recorded.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
…(VCR-DEC-003)

Wire the emitter into compile_all_exports: derive per-function provenance from
the compiled op stream + line_map/branch_map, write <output>.provenance.json.
Eliminated-constant entries derived from the fact-spec kept-set.

Demonstrated on control_step.wasm (SYNTH_CMP_SELECT_FUSE=1): 5 Select ops ->
folded-predication (object_pc = the IT-block predicated move, no branch), 2 BrIf
+ 1 Br -> preserved, and both real object conditional branches reconcile back to
their source BrIf conditions (clause a). ELF byte-identical with/without the flag
(frozen-safe).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
…C-003)

Non-vacuous gate: provenance_branches_396.wat exercises br_if (preserved),
select (folded-predication), br_table (split-into-object-branches) in one func.
The gate parses the sidecar as an independent consumer and asserts:
- (a) every REAL object conditional branch (from object_cond_branches, derived
  from BranchMap) resolves to a source WASM condition;
- (b) the folded select is recorded folded WITH its object realization, and no
  object branch traces back to it (proves a real fold, not a mislabel);
- split carries count + object PCs; source<->object correspondence both ways.

Verified non-vacuous: mislabeling SelectMove as CondBranch turns the gate RED
(the 'traces back to a folded select' assertion fires); reverted.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
- .github/workflows/ci.yml: dedicated provenance-gate job (reconciliation gate +
  emitter unit tests), named so a provenance regression is a visible red.
- artifacts/verified-codegen-roadmap.yaml: VCR-DEC-003 proposed -> implemented;
  document the shipped emitter, schema, gate, and the named v1 follow-ups
  (optimized ir_to_arm source_line, i64/trap-guard unresolved branches, RISC-V/
  AArch64 branch_map, witness#130 object-disposition compose).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
…chema

Advisor-caught gaps off the gate's happy path:
1. Loud-decline: a function whose lowering dropped source_line (line_map
   all-None) but emitted object branches can't reconcile — warn + skip that
   function rather than serialize a map that lies to witness (mirrors
   --debug-line's warn-on-unservable-path). Frozen-safe: sidecar-only.
2. eliminated-constant join key: was hardcoded instruction_offset=0 (all
   eliminated entries collided at 0). Now looks up the ORIGINAL-stream byte
   offset from func.op_offsets[orig_idx] (unfiltered) and threads it; unit test
   asserts the real offset. Signature: (usize,String) -> (usize,String,u32).
3. Roadmap ledger: rivet rejects sw-integration-verification verifies-linking a
   sw-req directly (needs an intermediate design artifact). Dropped the VER
   artifact that introduced the only new non-xref ERROR; VCR-DEC-003 now carries
   the same coverage WARN as every sibling VCR-* sw-req (established CI-passing
   pattern). NON_XREF error count stays 0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
… ir_to_arm

Claim-honesty pass (advisor + repo claim-gate ethos):
- eliminated-constant: move from 'covered' to 'wired in schema+emitter (correct
  join key) but not yet gate-exercised' — the reconciliation fixture exercises
  preserved/folded/split; fact-spec deletions today are pure condition slices +
  proven-dead if-regions, so a covered-branch-drop fixture is a v1 follow-up.
- ir_to_arm: soften 'still sets source_line: None' (this fixture keeps it on both
  paths) to 'may drop on some op shapes; such functions are loud-declined' — what
  was actually verified. Roadmap + provenance.rs module doc.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
@codecov

codecov Bot commented Jul 16, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 83.83459% with 43 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
crates/synth-cli/src/main.rs 66.19% 24 Missing ⚠️
crates/synth-core/src/provenance.rs 88.75% 19 Missing ⚠️

📢 Thoughts on this report? Let us know!

@avrabe
avrabe merged commit 27324fe into main Jul 16, 2026
39 of 40 checks passed
@avrabe
avrabe deleted the feat/45-396-mcdc-provenance branch July 16, 2026 00:40
avrabe added a commit that referenced this pull request Jul 16, 2026
… WasmCert anchor + VCR-ISA 41→50, A64 7→50, 12× beat-LLVM, MC/DC provenance) (#775)

Five oracle-gated lanes across semantics/capability/perf/assurance:
- VCR-WASM-001 (#771): WasmCert-Coq source anchor (i32.add refinement, 474→476)
- VCR-ISA-001 (#773): generate-not-mirror 41→50 ops (476→485 Qed)
- #538 (#769): AArch64 milestone 2 — 7→50 integer ops, honest div/rem decline
- #494 (#770): proof-carrying rem_u elision — 12× beat over clang -Os
- #396 (#774): synth-provenance-v1 MC/DC source-to-object map (VCR-DEC-003)

#757 stays OPEN (accepted residual — 7 reconstructions all green, needs reporter
module; #772 lands them as regression guards). i64.trunc_f64 + #761 residuals noted.


Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
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