RQ-64-CFOBLIG (#1057): Block/End — WASM half landed, ARM obligation stated with its obstruction kernel-pinned (done-when branch B) - #1187
Merged
Conversation
…and the structured executor exec_wasm_blocks (WASM half, 11 Qed, no ARM claim) The WASM side of the Block/End correspondence obligation. Four constructors mirror the shipped WasmOp::Block / Loop / Br(u32) / End in the FLAT op-stream form the compiler consumes (End_ because `End` is a reserved Rocq keyword — it names WasmOp::End and nothing else). exec_wasm_instr declines all four via its catch-all (a block boundary has no per-instruction meaning; a Br is always a transfer — #615 class); compile_wasm_to_arm gets LOUD-DECLINE arms ([UDF 255]) with the reason written down. WasmBlocks.v: split_block (nesting-aware forward scan to the matching End_), unwind_to (a taken branch restores the target block's entry stack — the arity-0 value-dimension half of the #509/#930 class), and the fuel-bounded structured executor exec_wasm_blocks: Block consumes WBranch 0 and resumes after End_, Loop re-enters, deeper branches propagate decremented, Br is an unconditional WBranch, BrIf is exec_wasm_seq's decision, a stray End_ is None. Kernel-checked: - exec_wasm_blocks_structured_free: on programs without the four new constructors the structured executor IS exec_wasm_seq — the executor the 600+ existing theorems and brif_correct are stated against is unchanged by construction; - split_block_block_free + the Block/Loop/Br unfolding lemmas the ARM-side obligation is stated against; - computed non-vacuity: the SAME block, differing only in the branch constant, lands with and without the body's tail (0-length unwound stack vs a 7 on top); a counting loop iterates to local 0 = 0; and with fuel for one pass it declines rather than reporting a one-iteration result. Deliberately WASM-only: nothing here is a correspondence claim, no theorem is named *_correct, no Admitted/admit. bazel test //coq:verify_proofs: 2/2 PASSED after the constructor addition rebuilt the 16 downstream files. Refs #1057 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
…nd its obstruction PINNED in the kernel (BlockEndObligation.v, 4 Qed, no correctness claim)
Branch B of the artifact's done-when: the correspondence cannot be
discharged against ArmSemantics.v today, and this file says exactly why in
terms the checker holds rather than a status file asserts. Nothing here is
named *_correct; nothing is admitted.
Kernel-checked model-gap pins (each stops compiling when VCR-ISA-001 closes
its gap — the intended red-first signal):
- exec_program_pc_B_not_taken / exec_program_pc_BL_not_taken (O1): the
index executor intercepts only BCondOffset; B/BL write the PC REGISTER
while the executor advances its INDEX by one — two disjoint program
counters, the shipped `Br` lowers to `B label`, and gale's 31 `Call`
rows share the shape.
- exec_program_pc_bcond_backward_falls_through (O2): for off < 0 the taken
and not-taken targets coincide at pc + 1 — a loop back-edge is a silent
fall-through, whatever the condition.
- arm_count_loop_body_runs_once (O2, concrete): ADD;CMP #10;BNE -3 from
R0 = 0 exits with R0 = 1 under exec_program_br — the model runs a
counting loop's body exactly once, where WasmBlocks.v's
ex_loop_counts_down_to_zero shows the WASM side iterating.
Written down in the file header as VCR-ISA-001 input, each tied to a lemma
or a measured shipped fact: O3 index-granular pc vs byte-resolved labels
(arm_backend.rs sums the real encoder's byte sizes, Label = 0 bytes; the
closed #483 mid-instruction landing and #740 halved-offset miscompiles are
invisible at index granularity), O4 no compositional state relation
(state_correspondence is defined in Compilation.v and used by zero theorem
statements), O5 the arity-1 join register is an allocator decision
(result_reg allocated lazily at the first br edge; #509/#930 lived there).
And the supply list, in dependence order: byte-addressed executor with an
encoder-derived size function admitting negative offsets (O1+O2+O3 are one
decision), step-counted fuel with the 59 exec_program_pc sites re-checked,
a cross-instruction simulation relation, blocktype arity + join register.
The forward/void/index-granular fragment IS statable and is deliberately
NOT proven: a green Qed that could not have caught #483, #500, #509, #740
or #930 is the overclaim this artifact forbids.
bazel test //coq:verify_proofs: rocq_proofs PASSED (executed, not cached),
vcr_sel_rules_coverage PASSED. Tree: 645 Qed / 2 Admitted.
Refs #1057
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
…from "unmodeled" to "absent" (modeled, unproven); the join gains its one Rocq-keyword escape, unit-driven
proof-inventory.json now says, per row, what the tree establishes for the
four structured control-flow ops: modeled:true with the real constructor
name and status "absent" — the WASM side has executable semantics
(WasmBlocks.v) and no theorem relates them to ARM code, because the ARM
model cannot state the obligation (BlockEndObligation.v pins why). That is
one tier above "unmodeled" and still uncovered for a consumer; nothing in
the row can be read as coverage. summary: modeled 138 -> 142, unmodeled
141 -> 137, absent 0 -> 4; qed 138 and result_correspondence 63 unchanged;
the BrIf row is byte-identical.
`End` is a reserved Rocq vernacular keyword, so the model spells the
constructor `End_`. The exact-name join keeps refusing every other
mismatch: `constructor_op` strips exactly one trailing underscore from a
stem in ROCQ_RESERVED_WORDS = {End} and nothing else — `Foo_` for a
non-reserved `Foo` still refuses (red-first: the unpatched generator
refused `End_` with exit 1 on this tree). The row keeps
"constructor": "End_" under "op": "End". scripts/test_proof_inventory.py
drives both branches plus the real tree's End/Block/Loop/Br and BrIf rows
(7 tests) and is wired beside the freshness check in the claim-check job.
The ledger pins on the Qed total and the modeled:false count move in the
next commit together with the docs, as CLAUDE.md requires.
Refs #1057
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
…630 -> 645 Qed, modeled:false 141 -> 137), the obstruction record pinned red-capable, VCR-ISA-001 given its input, artifact implemented on branch B claims.yaml: rocq_qed count-eq pins and the CLAUDE.md/STATUS.md headline text 630 -> 645; the proof-inventory `"modeled": false` count 141 -> 137; new SYNTH-BLOCKEND-OBSTRUCTION-1057 — exactly 4 Qed in BlockEndObligation.v, zero `*_correct` names and zero admits across the two new files, exactly 4 `absent` manifest rows, and the decision quoted from STATUS.md — so the obstruction record cannot rot silently in either direction. Mutation-verified, each planted then restored: a planted `Lemma planted_block_correct` -> red; a planted extra `Qed.` -> red (the fields-equal cross-check caught it independently); the End row hand- flipped absent -> qed -> red (`derived 3 != documented 4`); restored -> 62/62 hold. status.json + FEATURE_MATRIX.md regenerated by --emit-status. coq/STATUS.md: dated header entry, Total 645 / 2 (the pattern appears exactly twice, as pinned), per-file rows for WasmBlocks.v (11, Infra, WASM-only) and BlockEndObligation.v (4, model-gap pins), the DSL-coverage control-flow row (br_if is model-only via brif_correct; block/loop/br/end modeled WASM-side, ARM obligation blocked; column totals 82/13, the pinned DSL-served 60 untouched), and a new section "Block/End correspondence — obligation stated, obstruction pinned" with the O1-O5 table (pinned-by / would-have-missed) and the VCR-ISA-001 supply list in dependence order. artifacts/verified-codegen-roadmap.yaml: VCR-ISA-001 gains "INPUT FROM RQ-64-CFOBLIG (2026-09-07, #1057)" — the five gaps and what the item must supply; no moving Qed/Admitted figure cited, so the S-rule citation count is unchanged (34, re-derived). rivet validate: 40 cross-repo errors with and without the paragraph, 0 ours under CI's filter. artifacts/release-v0.64/RQ-64-CFOBLIG.yaml: proposed -> implemented on done-when branch B, verified-by written from the measurements (kernel results per file, the generator's exit-1 refusal before the keyword escape, the 7 join tests, the three pin mutations); names no PR and no live pinned value. status_evidence_check: 0 failures, 6/6 window commits attributed, floor-prose restated 0 times. Refs #1057 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
avrabe
force-pushed
the
proof/cfoblig-1057
branch
from
September 7, 2026 13:10
4b650dd to
2c0e725
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 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.
RQ-64-CFOBLIG (#1057) — done-when branch B, taken deliberately
The artifact's
done-whenhas two acceptable branches: a kernel-checked Block/End correspondence obligation reflected semantically inproof-inventory.json, OR the obstruction stated precisely enough to be VCR-ISA-001 input — with no existence-only theorem named*_correct. This PR takes branch B, and lands the half of the obligation that was never blocked.Why B. The correspondence cannot be discharged against
ArmSemantics.vas it stands, for reasons that are now kernel-checked facts rather than sentences. The fragment that IS statable today (forward, void, index-granular) would be blind to every closed control-flow miscompile (#483, #500, #509, #740, #930) — a green Qed there is exactly the overclaim the artifact forbids, so it was deliberately not written.What the kernel checked (
bazel test //coq:verify_proofs:rocq_proofsPASSED executed after each step,vcr_sel_rules_coveragePASSED; 630 -> 645 Qed / 2 Admitted, zeroadmit., nothing named*_correctin the new files)WasmInstructions.vgainsBlock/Loop/Br : nat/End_, one constructor per shippedWasmOpvariant in the FLAT op-stream form the compiler consumes.End_only becauseEndis a reserved Rocq keyword.exec_wasm_instrdeclines all four via its catch-all;compile_wasm_to_arm(the one exhaustive match the addition broke, per the kernel) gets LOUD-DECLINE[UDF 255]arms with the reason written down.WasmBlocks.v, 11 Qed, WASM-only by design.split_block(nesting-aware scan to the matchingEnd_),unwind_to(a taken branch restores the target block's entry stack —bris stack-polymorphic, the arity-0 half of the Direct selector miscompiles value-returning br_table — carried value dropped (affects --relocatable/shipped path) #509/thumb-2: br/br_if out of anifis not taken when its value operand is a block that branches (labels.wast) — silent wrong value, exit 0 #930 value class), the fuel-bounded structured executorexec_wasm_blocks(Block consumesWBranch 0and resumes afterEnd_, Loop re-enters, deeper branches propagate decremented,Bris an unconditionalWBranch, a strayEnd_isNone).exec_wasm_blocks_structured_freeproves it ISexec_wasm_seqon programs without the four constructors, so the executor every existing theorem andbrif_correctare stated against is unchanged by construction. Computed non-vacuity: the SAME block, differing only in the branch constant, lands with an unwound empty stack or with a 7 on top; a counting loop reaches local 0 = 0; with fuel for one pass it declines rather than reporting a one-iteration result.BlockEndObligation.v, 4 Qed, NO correctness theorem. Model-gap pins, each of which stops compiling when VCR-ISA-001 closes its gap:exec_program_pc_B_not_taken/_BL_not_taken(the index executor runs an unconditional branch / a call as a fall-through, writing only the PC register),exec_program_pc_bcond_backward_falls_through(foroff < 0the taken and not-taken targets coincide), andarm_count_loop_body_runs_once(ADD;CMP #10;BNE -3from R0 = 0 exits with R0 = 1 underexec_program_br— whereWasmBlocks.v's executor iterates).The obstruction, as VCR-ISA-001 input (file header,
coq/STATUS.mdsection, roadmap entry)exec_program_pcintercepts onlyBCondOffset;B/BLwrite the PC REGISTER while the executor advances its INDEX — two disjoint program counters. ShippedBr=B label; gale's 31Callrows share the shapebr, every callpc + 1 + Z.to_nat off,off < 0->pc + 1: a loop back-edge is a silent fall-through; fuel assumes forward-onlylooparm_backend.rsresolves labels to BYTE offsets by summing the real encoder's sizes (Label = 0 B) until 16/32-bit widths convergeB<c>.W)state_correspondence(Compilation.v:692) is defined and used by ZERO theorem statements; every Qed is one instruction under fixed-register hypothesesresult_reg,edge_value_move);Endemits aMOVonly then — an atomiccompile_wasm_to_arm End_ = []would be the #1021 popcnt shapeselect_with_stack.rsSupply list, in dependence order: (a) a byte-addressed executor with an encoder-derived size function admitting negative offsets, in which
B/BL/BCondOffsetare all executor-visible — O1, O2, O3 are ONE decision, not three; (b) step-counted fuel and the re-check of everyexec_program_pcsite (ArmSemantics 20, CorrectnessI32 27, CorrectnessBrIf 11, Compilation 1); (c) a simulation relation preserved across instructions; (d) blocktype arity and the join register in the model.Reflected semantically in
proof-inventory.jsonBlock/Loop/Br/End move from
unmodeledtoabsent(modeled, unproven): summary modeled 138 -> 142, unmodeled 141 -> 137, absent 0 -> 4; qed 138 and result_correspondence 63 unchanged; the BrIf row byte-identical. One tier up, still uncovered for a consumer — nothing in the row can be read as coverage.The join gained its ONE Rocq-keyword escape (
End_->End). Red-first: the unpatched generator refusedEnd_with exit 1 on this tree.constructor_opstrips exactly one underscore from a stem inROCQ_RESERVED_WORDS = {End}and nothing else —Foo_still refuses, so the escape cannot grow into the alias table the join exists to forbid.scripts/test_proof_inventory.py(7 tests) drives both branches plus the real tree's rows and is wired beside the freshness check in the claim-check job.Ledger and docs moved together
claims.yaml: rocq_qed 630 -> 645 (with CLAUDE.md and coq/STATUS.md),"modeled": false141 -> 137, and newSYNTH-BLOCKEND-OBSTRUCTION-1057— exactly 4 Qed in the obstruction file, zero*_correctnames and zero admits across the two new files, exactly 4absentrows, the decision quoted from STATUS.md. Mutation-verified, each planted then restored: a plantedLemma planted_block_correct-> red; a planted extraQed.-> red (the fields-equal cross-check caught it independently); the End row hand-flippedabsent->qed-> red (derived 3 != documented 4); restored -> 62/62 hold.coq/STATUS.md: dated header entry, Total 645 / 2, per-file rows, the DSL-coverage control-flow row corrected (br_if is model-only viabrif_correct; the pinned DSL-served total untouched), and a new "Block/End correspondence — obligation stated, obstruction pinned" section.artifacts/verified-codegen-roadmap.yaml: VCR-ISA-001 gains the dated input paragraph (no moving Qed figure cited; S-rule citations unchanged at 34).RQ-64-CFOBLIG.yaml:proposed->implementedon branch B,verified-bywritten from measurements, names no PR and no live pinned value.Gates re-derived on the merged tree (after rebasing over #1186)
claim_check.py claims.yaml62/62 ·proof_inventory.py --check0 ·test_proof_inventory.py7/7 ·status_evidence_check.py0 failures, 7/7 window commits attributed, floor-prose restated 0 times ·oracle_wiring_check.py0 ·rivet validate40 cross-repo errors with AND without the roadmap paragraph, 0 ours under CI's filter ·bazel test //coq:verify_proofs2/2.Not claimed
Any Block/End correspondence with ARM code; value-carrying blocks; If/Else/Return/BrTable/Call (unmodeled rows, unchanged). Deferring the ARM half a second time, now with the obstruction kernel-pinned, is the outcome the artifact named as acceptable.
Refs #1057
🤖 Generated with Claude Code
https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L