Skip to content

feat(verify): VCR-VER-002 Phase B — float→int trunc trap class via ordeal 0.9.1 (#166) - #742

Merged
avrabe merged 3 commits into
mainfrom
feat/42-vcr-ver-002-phase-b
Jul 15, 2026
Merged

avrabe merged 3 commits into
mainfrom
feat/42-vcr-ver-002-phase-b

Conversation

@avrabe

@avrabe avrabe commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

VCR-VER-002 Phase B — the float→int truncation trap class (#166, ordeal#59/TR-020)

Follows #723 (Phase A). ordeal 0.9.1 adds trap_trunc: the WASM iN.trunc_fM_s/u trap predicate (NaN ∨ ±∞ ∨ out-of-range) classified purely over the float operand's bit pattern — exponent-field patterns + sign-split monotonic magnitude threshold compares. Floats enter as BV32/BV64; no FP theory, pure QF_BV, LRAT-certified pipeline unchanged. This is exactly the #709 soundness surface: ARM VCVT saturates where WASM must trap, so a lowering that keeps the saturated value and drops the guard is value-equal and invisible to a value-only VC.

Wrapper (crates/synth-verify/src/trap.rs)

  • trunc_op(WasmOp) → Option<(FpFmt, IntTarget, bool)> — maps all six decoder-reachable trunc variants (i32/i64.trunc_f{32,64}_{s,u}; i64.trunc_f32_* are not WasmOp variants, the raw builder still covers those shapes).
  • trap_trunc(bits, fmt, target, signed) — ordeal's classifier over synth BV/Bool, loud width assert. Trap-clause-only VC class (synth models no float→int value function).
  • Cargo pin 0.9 → 0.9.1 (trap_trunc is 0.9.1-only; the lockfile already resolved 0.9.1 since feat(verify): VCR-VER-002 — trap-preservation gate over ordeal::trap (#166) #723 — no lock change).

Red-first gate (tests/trap_preservation.rs, 6 → 11 tests) — which drops are caught

direction result
guard-dropping lowering (may_trap=false), all six variants CAUGHT — Dropped/Sat, counterexample validated as genuinely trapping against the real-float reference
signedness-confused guard (unsigned range check on i32.trunc_f32_s — NaN/∞ kept, thresholds wrong: the partial-drop VCVT shape) CAUGHT — Sat, counterexample separates signed from unsigned trapping
guard-preserving lowering, all six variants accepted — Preserved/Unsat, LRAT certificate re-checked
wrong-expectation control (claiming NaN doesn't trap at a ground input) CAUGHT — non-vacuity of the ground gate

Classifier-encoding check (the ordeal#59 ask; synth's #709 table is the reference oracle)

The 31-row #709 boundary table evaluated two ways, every row self-checked against the real-float reference so the table cannot drift:

  1. directly via ordeal::eval on the classifier — i32.trunc_f32_s: NaN / +∞ / −∞ / 3e9 / −3e9 trap, 2^31 (0x4F000000) TRAPS, −2^31 (0xCF000000) in-range, 2.5 in-range; i32.trunc_f32_u: −1.0 traps, 0.5 / −0.0 in-range, 2^32 traps; plus f64 and i64-target boundary anchors (largest-below-threshold floats, ±2^63, 2^64, −(2^31+1), …).
  2. through the certificate-checked pipeline on the synth wrapper: at ground inputs, trap_trunc(const) ⇔ expected proven valid per pattern (certified Unsat).

Roadmap / claims

VCR-VER-002 title + description updated (Phase B landed, LANDED item 5); status stays proposed — the live-validator wiring gap is unchanged (trunc, like load/store / call_indirect / unreachable, is gated at the unit level pending the exec-model trap flag). claims.yaml SYNTH-VCR-VER-002-STATUS text updated verbatim; claim_check 18/18.

Gates

  • cargo test -p synth-verify + --test trap_preservation (11/11) green
  • cargo test --workspace green (119 test binaries, 0 failures)
  • cargo clippy --workspace --all-targets -- -D warnings clean, cargo fmt --check clean
  • python3 scripts/claim_check.py claims.yaml — 18/18 claims hold

Refs #166, #242, ordeal#59. Follows #723.

🤖 Generated with Claude Code

https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L

avrabe and others added 3 commits July 14, 2026 21:11
… (VCR-VER-002 Phase B, #166)

ordeal 0.9.1 (ordeal#59/TR-020) adds the float→int truncation trap
CLASSIFIER: the trap predicate (NaN ∨ ±∞ ∨ out-of-range) is built purely
over the float operand's BIT PATTERN — exponent-field patterns plus
sign-split monotonic magnitude threshold compares. Floats enter as
BV32/BV64; no FP theory, pure QF_BV, soundness unchanged (LRAT-certified
pipeline).

synth_verify::trap Phase B:
- `trunc_op(WasmOp) -> Option<(FpFmt, IntTarget, bool)>` maps all six
  decoder-reachable trunc variants (i32/i64.trunc_f32/f64_s/u;
  i64.trunc_f32_* are not WasmOp variants — the raw builder still covers
  those shapes).
- `trap_trunc(bits, fmt, target, signed)` wraps ordeal's classifier over
  synth's BV/Bool; loud width assert (bits must be fmt.total_bits()).
- Re-export FpFmt/IntTarget; docs: trunc is trap-clause-only (synth
  models no float→int value function) — exactly the #709 surface (ARM
  VCVT saturates where WASM traps).

Cargo.toml pin 0.9 → 0.9.1 (trap_trunc is 0.9.1-only; lock already
resolves 0.9.1 since #723 — no lock change).

Refs #166, #242, ordeal#59

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
…val (VCR-VER-002 Phase B, #166)

Extends the red-first trap-preservation gate (6 → 11 tests) with the
float→int trunc class:

- trunc_all_six_ops_catch_the_drop_and_accept_preservation: for every
  variant a guard-dropping lowering (may_trap=false, the #709
  VCVT-saturate shape) is CAUGHT (Dropped/Sat) with the counterexample
  validated as a genuinely trapping input against the real-float
  reference; an identical-guard lowering is accepted (Preserved/Unsat,
  LRAT re-checked).
- trunc_signedness_confused_guard_is_caught: an unsigned range guard on
  i32.trunc_f32_s (partial drop — NaN/∞ kept, thresholds wrong) is Sat,
  counterexample separates signed from unsigned trapping.
- trunc_classifier_eval_matches_709_boundary_table: the encoding-
  correctness check ordeal#59 called for — ordeal's classifier evaluated
  via ordeal::eval at the exact #709 bit patterns (2^31/0x4F000000 traps
  signed, -2^31/0xCF000000 in-range, 3e9/-3e9 trap, NaN/±∞ trap, 2.5
  in-range; -1.0 traps unsigned, 0.5/-0.0 in-range, 2^32 traps; plus f64
  and i64-target anchors — 31 rows), every row self-checked against the
  real-float reference so the table cannot drift.
- trunc_gate_decides_709_boundary_table_through_certified_pipeline: the
  same table at GROUND inputs through the synth wrapper —
  trap_trunc(const) ⇔ expected proven valid per pattern (certified
  Unsat), with a wrong-expectation non-vacuity control (claiming NaN
  does not trap is rejected).
- trunc_op mapping test for all six WasmOp variants + None cases.

Refs #166, #242, ordeal#59

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
…166)

Roadmap entry: title + description gain the float→int trunc class
(LANDED item 5: ordeal 0.9.1 trap_trunc bit-pattern classifier, six
WasmOp variants mapped, red-first gate + #709 boundary-table eval both
via ordeal::eval and through the certified pipeline); documented-gap and
steps/preconditions updated. Status stays `proposed` — the live-
validator wiring gap is unchanged (trunc, like load/store /
call_indirect / unreachable, is gated at the unit level pending the
exec-model trap flag).

claims.yaml SYNTH-VCR-VER-002-STATUS: claim text updated to the new
title verbatim; evidence unchanged (status=proposed + file-exists).
claim_check 18/18.

Refs #166, #242, ordeal#59

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

codecov Bot commented Jul 14, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.11765% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
crates/synth-verify/src/trap.rs 94.11% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@avrabe
avrabe merged commit 1f0707d into main Jul 15, 2026
37 checks passed
@avrabe
avrabe deleted the feat/42-vcr-ver-002-phase-b branch July 15, 2026 02:41
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