[AUTOMATED] feat(p8): guardarm - break the ruleBlockIfNoExit arm TIE by code layout (+297 GED-perfect / 32,339 O0 functions) - #281
Merged
Merged
Conversation
This was referenced Aug 11, 2026
…by code layout, not by out-index (+297 GED-perfect over 32,339 O0 functions) `CollapseStructure::rule_block_if_no_exit` folds the condition block and ONE terminal out-arm into a `BlockIf`; the other arm survives as the fall-through. Upstream walks the out-edges in index order. Usually only one arm is eligible and there is no choice -- but when the condition guards a fatal no-return call and the other side is the function's own `return`, BOTH arms are eligible and index order decides a coin flip. A `KUNA_RS_DEBUG` decision trace added to the rule (also in this commit) shows the mechanism: `negateCondition`'s `swapEdges` re-orients the block between the two `collapse_all` runs the action pool performs, so the arm at out(0) in the deciding run is not the arm the branch was assembled with. `dpkg-query control_list` has both outcomes in ONE function -- its two guards are the same shape and the halt lands on out(0) for one and out(1) for the other, so one comes out flat and the other inverted. `option guardarm on` (default-off opt-in) resolves the tie by code layout: of the two eligible arms, the one whose front leaf lies earlier in the address space becomes the clause, because an unoptimized compiler emits the taken clause of `if (c) A; B;` in front of `B`. Ties only; a single eligible arm is byte-identical to upstream. The predicate deliberately does NOT read the `op_mark_halt` no-return bit. Preferring the no-return arm fixes `scp xcalloc` and `control_list` but inverts `make-prime-list xalloc`, whose source really is `if (p) return p; ...; exit(1);` Layout order gets all three right; `tail xlseek` and `xalloc` are byte-identical with the option on. Measured bidirectionally over the whole decbench O0 slice (32,339 scored functions, 265 slices): 6,821 bodies change, GED 230,291 -> 227,646 (-2,645), perfect 14,785 -> 15,082 (+297), 316 moved to perfect against 19 moved off (16.6:1, McNemar z = +16.2). All 6,821 changed bodies audited: identical callee and string multisets, gotos 1,021 -> 1,019, labels 620 -> 619, `while( true )` and `// no-return` counts unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C8UQbPqALzdUQ3cLLjUeKH
…try rebase The rebase onto #278 (poolentry) landed guardarm's three GUARDARM assertions and the KUNA-CATALOG #7 rename on top of main's BRANCHFLIP-ARMSWAP six, so the hand-merged data_footer was stale. Re-recorded from a live run rather than hand-merged: 413 -> 416, +3 GUARDARM keys and the #7 text update, nothing removed. Every shared counter was likewise re-derived from the BUILT artifact rather than by arithmetic, which caught two the rebase auto-merged at the wrong value with no conflict marker: catalog_bytecompat.rs (97, must be 98) and xml.rs's corpus file count (195, must be 196). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C8UQbPqALzdUQ3cLLjUeKH
mahaloz
added a commit
that referenced
this pull request
Aug 11, 2026
… from guardarm #283 was branched on top of #281, whose commit is now on main as a squash. The child was replayed with `git rebase --onto origin/main ee14e46`, so only loopcondhoist's own commit applies -- but the shared counters still needed re-deriving from the BUILT artifact rather than by arithmetic. Two auto-merged at the wrong value with no conflict marker, both because main and the branch had made the identical N -> N+1 edit: catalog_bytecompat.rs (98, must be 99) and xml.rs's corpus file count (196, must be 197). Live catalog 99, phases.toml 99, tiers (20, 44, 35), `},\n` 98, phase_catalog.json 99 (recaptured), docs/options.md 99 (regenerated), kuna-catalog.xml structure-recovery 25. Stages baseline re-recorded on the merged base: 416 -> 419. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C8UQbPqALzdUQ3cLLjUeKH
mahaloz
added a commit
that referenced
this pull request
Aug 11, 2026
…it scan so ruleBlockWhileDo keeps the head test (+669 GED-perfect / 32,339 O0 functions) (#283) * [AUTOMATED] feat(p8): loopcondhoist - let the deferred ifNoExit scan pass over a loop head so ruleBlockWhileDo keeps the head test (+669 GED-perfect over 32,339 O0 functions) `CollapseStructure::collapse_internal`'s deferred `ruleBlockIfNoExit` scan walks the live components in order and folds the FIRST one whose terminal arm qualifies, then restarts the cascade. A head-tested loop whose exit arm is a `return` is such a component -- once ActionReturnSplit/returndup has given that return in-degree 1 the rule matches the loop head -- and the head sits ahead of its own body in component order. So the head folds to `if (!C) return X;` and drops to ONE out-edge, after which `rule_block_while_do` can never match it and the loop is emitted as `while( true ) { if (!C) return X; BODY; }` where the source and IDA write `while (C) { BODY }`. The `KUNA_RS_DEBUG` trace added in the parent commit shows the alternative is already in the same scan: on coreutils od read_char and libacl getfacl walk_tree_visited the loop head is the first candidate and the body block carrying the `break` is the second. Folding the body first is strictly better -- its clause is the loop follower, which the rule ALREADY requires be reached from nowhere else (`size_in() == 1`), so absorbing it into the break arm is semantics-preserving by the rule's own precondition; the body then collapses to a single back-edge clause and the head test hoists into the `while`/`for` header on the next cascade pass, with no new machinery and no relocated statement. `option loopcondhoist on` (default-off opt-in) gives the non-heads one pass of the scan, falling back to the unrestricted upstream pass when it finds nothing. A function with no loop-head candidate is byte-identical, and the scan still terminates on the same fixpoint. Measured bidirectionally over the whole decbench O0 slice (32,339 scored functions, 265 slices): 2,281 bodies change, GED 230,291 -> 224,670 (-5,621), perfect 14,785 -> 15,454 (+669), 680 moved to perfect against 11 moved off (61.8:1, McNemar z = +25.5). Every architecture gains: x86-64 +542, ARM Cortex-M +123, i386 PE +4. Over the changed set `while( true )` goes 2,087 -> 223, gotos 621 -> 409, labels 399 -> 251, `// no-return` unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C8UQbPqALzdUQ3cLLjUeKH * [AUTOMATED] chore(p0): re-derive the shared counters after unstacking from guardarm #283 was branched on top of #281, whose commit is now on main as a squash. The child was replayed with `git rebase --onto origin/main ee14e46`, so only loopcondhoist's own commit applies -- but the shared counters still needed re-deriving from the BUILT artifact rather than by arithmetic. Two auto-merged at the wrong value with no conflict marker, both because main and the branch had made the identical N -> N+1 edit: catalog_bytecompat.rs (98, must be 99) and xml.rs's corpus file count (196, must be 197). Live catalog 99, phases.toml 99, tiers (20, 44, 35), `},\n` 98, phase_catalog.json 99 (recaptured), docs/options.md 99 (regenerated), kuna-catalog.xml structure-recovery 25. Stages baseline re-recorded on the merged base: 416 -> 419. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C8UQbPqALzdUQ3cLLjUeKH --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
mahaloz
added a commit
that referenced
this pull request
Aug 11, 2026
…e stages baseline Rebased onto #278/#281/#283. Every shared counter re-derived from the BUILT artifact rather than by arithmetic: live catalog 100, phases.toml 100, catalog_bytecompat.rs 100, kuna_phases/tests.rs 100 / tiers (21, 44, 35) / `},\n` 99, phase_catalog.json 100 (recaptured), docs/options.md 100 (regenerated), xml.rs corpus 198. `kuna-catalog.xml` structure-recovery stays 25 because calloverlap is a correctness-fix, not a structure-recovery row. Stages baseline re-recorded on the merged base: 419 -> 425, +6 CALLOVERLAP keys, none removed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C8UQbPqALzdUQ3cLLjUeKH
mahaloz
added a commit
that referenced
this pull request
Aug 11, 2026
…call-overlap guards (GH-275) (#284) * [AUTOMATED] fix(p3): calloverlap - complete the two stubbed upstream call-overlap guards (GH-275) Squashed from the branch's four commits (the fix, the two catalog-count bumps, the flip-guidance prose and the post-funcboundflow reconciliation) so the merge train replays one commit instead of four rounds of the same shared-counter edit. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C8UQbPqALzdUQ3cLLjUeKH * [AUTOMATED] chore(p0): re-derive the shared counters and re-record the stages baseline Rebased onto #278/#281/#283. Every shared counter re-derived from the BUILT artifact rather than by arithmetic: live catalog 100, phases.toml 100, catalog_bytecompat.rs 100, kuna_phases/tests.rs 100 / tiers (21, 44, 35) / `},\n` 99, phase_catalog.json 100 (recaptured), docs/options.md 100 (regenerated), xml.rs corpus 198. `kuna-catalog.xml` structure-recovery stays 25 because calloverlap is a correctness-fix, not a structure-recovery row. Stages baseline re-recorded on the merged base: 419 -> 425, +6 CALLOVERLAP keys, none removed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C8UQbPqALzdUQ3cLLjUeKH --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
mahaloz
added a commit
that referenced
this pull request
Aug 11, 2026
…e stages baseline Rebased onto #278/#281/#283/#284. Every shared counter re-derived from the BUILT artifact rather than by arithmetic: live catalog 101, phases.toml 101, catalog_bytecompat.rs 101, kuna_phases/tests.rs 101 / tiers (21, 45, 35) / `},\n` 100, phase_catalog.json 101 (recaptured), docs/options.md 101 (regenerated), xml.rs corpus 199, kuna-catalog.xml structure-recovery 26. Stages baseline re-recorded on the merged base: 425 -> 433, +8 orchain keys and the #7 text update, nothing removed. DIV-69 re-checked against origin/main immediately before pushing: main's highest row is DIV-68 (#277), so 69 stands unrenumbered. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C8UQbPqALzdUQ3cLLjUeKH
mahaloz
added a commit
that referenced
this pull request
Aug 11, 2026
…chain of a short-circuit (DIV-69) (#285) * [AUTOMATED] feat(p8): orchain - returndup must not split the operand chain of a short-circuit (DIV-69) DIV-54 flipped `returndup` on evidence from two of the three optimisation levels. Extended to all three (795 slices / 85,195 scored functions) the pass is +640 GED-perfect corpus-wide but -192 at -O0, and one sub-shape carries essentially all of that harm and almost none of the optimized benefit: the split that permanently blocks `CollapseStructure::rule_block_or`. `returndup` gives every predecessor of a shared bare-epilogue RETURN its own private return. For a guard clause that recovers the source; but the operand blocks of a short-circuit expression are predecessors of that same block, and the shared out-target IS ruleBlockOr's entire precondition. ActionReturnDup runs in fullloop's `returnsplit` group and collapse_conditions runs later, so the fold is lost for good and one source `return a || (b && (c || d));` prints as a five-return constant-guard cascade (iproute2 ip::sci_complete, GED 21 against 0). New option `orchain on|off` (default ON, DIV-69, ELEM 4122). `kuna_orchain.rs (shortcircuit_shared_targets)` replays collapse_conditions read-only on the bblocks CFG, mirroring ruleBlockOr's own admission test rather than approximating it (one in-edge, two out-edges, not a switch, not BlockBasic::isComplex, a shared out-target, sibling's other target is not the head), and `returndup_apply` declines the whole function's splits when one of its own candidates is a recorded target. It also protects a block joining two or more single-exit arms that are themselves fold targets, because the first returndup invocation of the fullloop still sees the constant-materialisation blocks in between. Sweep over the same corpus (base = shipped defaults at merge-base 82dd39a): O0 +611 GED-perfect / -2,816 GED, O2 -13 / +1,232, O2-noinline -15 / +617 -- +583 net and -967 aggregate, 781 functions to perfect against 198 off (3.94 : 1). That is 92% of the +630 the post-hoc "a short-circuit was lost" signal prices as a ceiling, at a smaller optimized-level cost than the ceiling itself (-28 vs -35). Predicate fidelity to that signal: 2,933 of 2,951 O0 positives recognised (99.4% recall) at 126 further firings (95.9% precision); 94.2% of firings reproduce the `option returndup off` body byte for byte. Default-ON rather than an `aggressive` preset member because `returndup` is a shipped default in every mode and 45% of the corpus (38,216 of 85,195 functions, 36 of 795 slices) is over the 500 KiB `auto` threshold and runs `reliable`, where a preset-only gate never runs -- measured: with the gate in the preset only, the no-option arm reproduced the ungated body on iproute2 ip (76 functions), bash (366), sshd (201). 675/675 PARITY OK with the raw flip, zero assertion movement and no per-test opt-out; stages 415/415 with the new two-pass tests/stages/ghdec-orchain.xml; rust-test 4,540 passed / 0 failed; check-spec OK lenient + strict; catalog --check OK. Speed worst +1.01% (two of four probes are net speedups). Rebased onto #280 (funcboundflow, DIV-67): every shared counter re-derived from the built artifact on the rebased tree (97 settables, tiers 20/43/34, xml corpus 195), and DIV-68 was renumbered to 69 after Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C8UQbPqALzdUQ3cLLjUeKH * [AUTOMATED] chore(p0): re-derive the shared counters and re-record the stages baseline Rebased onto #278/#281/#283/#284. Every shared counter re-derived from the BUILT artifact rather than by arithmetic: live catalog 101, phases.toml 101, catalog_bytecompat.rs 101, kuna_phases/tests.rs 101 / tiers (21, 45, 35) / `},\n` 100, phase_catalog.json 101 (recaptured), docs/options.md 101 (regenerated), xml.rs corpus 199, kuna-catalog.xml structure-recovery 26. Stages baseline re-recorded on the merged base: 425 -> 433, +8 orchain keys and the #7 text update, nothing removed. DIV-69 re-checked against origin/main immediately before pushing: main's highest row is DIV-68 (#277), so 69 stands unrenumbered. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C8UQbPqALzdUQ3cLLjUeKH --------- Co-authored-by: Claude Opus 5 (1M context) <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.
Closes symptom A of the round-4
ifnoexitcluster(
docs/decbench/features-round4.mdRank 2). The filed predicate there isrefuted (Killed K13) and is not what this ships.
Phase 1 — the decision trace, and its verdict
Three independent refuters asked for one experiment: a decision trace inside
CollapseStructure::rule_block_if_no_exit. It ships in this PR(
p8_structure/kuna_ifnoexit.rs+ theKUNA_RS_DEBUGprints inblockaction.rs), and the verdict is the deliverable, independent of the twofixes. Reproduce with
KUNA_RS_DEBUG=1 kuna decompile-all <bin> --addr 0x...— notekuna decompilespawns
decomp_dbgwith piped stderr and swallows the trace, sodecompile-allis the surface that shows it.
Per candidate it prints
bl's type / degree / start address, both arms'size_in,size_out,is_decision_outand exit-leaf halt type (testing thepcodeop_flags::noreturnbit,0x1000000, not thereturn(#0x1:4)shape —badinstruction/unimplemented/missing/haltare identical in shape),whether
blis a live loop head, whetherrule_block_while_dowould fire rightnow, the whole deferred-scan order, and the chosen
i. Theapply#Ncounter isin every header because
collapse_allruns twice per function.1. Is A an arm predicate, a scan-order predicate, or both? — a pure arm
predicate, and only in a tie. On
dpkg-query control_listboth guards arein=1 out=0 dec=trueon both arms, the rule takesi=0in both, and the onlydifference between the guard that is right and the guard that is wrong is which
side the halt happened to land on:
When only one arm is eligible the
for i in 0..2loop already picks it, which iswhy ~84% of the filed signature population is already right and is untouched by
any tie-break. The scan order is irrelevant to A: the same block is the only
candidate either way.
Why out-index carries no source information. The trace also settles the
"filed root cause predicts the opposite of the output" contradiction the refuters
logged on
scp xcalloc(objdumpputs the fatal block on out(0), yet kunaemits the inverse).
apply#0picks the halt arm ati=0correctly — andthat pick negates the condition,
take_pending_flips->block_basic_negate_lastop->swap_edgesre-orients the block, andapply#1sees the arms swapped and picks the
return. The second run undoes the first.The orientation at the rule is not the disassembly orientation.
2. Is B the same site, and does fixing the scan order alone fix A? — same
60 lines, different predicate, and no. On
coreutils od read_charandlibacl getfacl walk_tree_visitedthe trace showsloophead=yes,whiledo_would=no, and exactly one eligible arm — the tie-break never runs,so nothing an arm predicate does can touch B. Conversely the guards in A are
loophead=noand are the only candidate in their scan, so nothing a scan-orderpredicate does can touch A. They are disjoint on every witness.
Two further facts fall out of that dump. The better candidate is already in
the same scan, one position behind:
#2@0x5340, the block carrying thesource's
break, is itself ifNoExit-eligible (its arm#3@0x53a4isin=1 out=0) — first-match in component order simply reaches the head first.And the rule only becomes eligible on the head after the return split: in
apply#0the sharedreturn v3block isin=2, the rule declines, and nothingfires;
ActionReturnSplit/returndupthen gives it in-degree 1 andapply#1folds the head. That is why B is an -O0 population, and it is also why the filed
region_structurer.rs:1788 cb.size_out() != 1diagnosis (Killed K7) could neverhave been the site.
3. Which must land first, and does either regress the other? —
guardarmfirst, and no. They are separate predicates on separate inputs, so they are
two PRs.
guardarmis the narrower and more obviously symmetric change (the tieis between two folds that are both legal;
new_block_ifis symmetric in the armit takes), so it goes first and
loopcondhoiststacks on it. Measured, notassumed: the four named counter-cases are byte-identical under each option
alone and under both together, and the two options' changed-function sets
were measured separately on the same corpus.
4. What separates the ~84% already-correct panes from the inverted ones?
The tie itself, plus code layout. It must distinguish
control_list's twoguards from each other, and the halt bit cannot — the trace above shows both
guards carry
halt=noreturnon one arm. What differs is which arm, and thecompiler's own answer to that is the address: an unoptimised compiler emits the
taken clause of
if (c) A; B;in front ofB, and thethenside of anif/else in front of the
elseside. So: in a tie, the arm whose front leaflies earlier in the address space becomes the clause. This deliberately does
NOT read
op_mark_halt— preferring the no-return arm fixesscp xcallocandcontrol_listand invertscoreutils make-prime-list xalloc, whose sourcereally is
if (p) return p; fprintf(...); exit(1);(
O0/coreutils/compiled/make-prime-list.i:3645). Layout gets all three right.5. For B form C, what is the exact in-code follower assertion? — it is
already in the rule:
size_in() == 1. The round required "the loop'simmediate structural follower, in-degree exactly 1" and warned that a matcher
keyed on "a
return Xreachable after the loop" freeslibacl getfacl get_list's list and returns NULL. No new predicate is needed, because the fixis not a hoist-and-relocate:
rule_block_if_no_exitfolds the body blockwhose clause is the follower, and its own
if size_in() != 1 { continue; }is exactly that assertion.
get_list's follower issub_27de(v9); return NULL;— not a bare return, reached only by the
break— so it moves into the breakarm and the normal loop exit returns the list, which is the baseline's semantics
exactly. Verified on the rebased tree:
bzip2 mainGtUis dropped from the witness list as the round instructed: itis a non-member (its source is a genuine
do/while).Phase 2 — what
guardarmdoesIn a tie, the arm whose front leaf lies EARLIER in the address space becomes
the
ifclause. Ties only: a block with a single eligible arm isbyte-identical to upstream, so the great majority of guards are untouched. The
change is symmetric by construction — both arms are legal folds and
new_block_ifdoes not care which it takes — so nothing can be dropped orduplicated by it.
guardarm ondpkg-query control_list@0x6c1copenssh scp xcalloc@0x23fb9if (v3) return v3; fatal();if (!v3) fatal(); return v3;coreutils [ posixtest@0x495fif (1 <= a0) return expr(); abort();if (a0 <= 0) abort(); return expr();Named counter-cases, verified on the rebased tree
Every one re-run on
feat/guardarm@ee14e461(rebased onto11f40f46), withdiffagainst the default arm:coreutils/tail xlseek@0x4804coreutils/make-prime-list xalloc@0x1906bash/mksyntax main@0x19f7libacl/getfacl get_list@0x281axlseekandxallocare the two functions the filed predicate ("prefer thearm whose component ends in an artificial no-return halt") was measured to
invert;
mksyntax mainandget_listare symptom B's counter-cases, checkedhere too because the two options land as a stack.
Measurement — both directions
scripts/decbench/optsweep.py, which scores both arms of the flip against thetree's own source CFGs and reports functions moved to and off perfect.
Full O0 slice (pre-rebase,
82dd39a7base)265 slices, 32,339 functions scored in both arms. Controls clean: 25,518
unchanged bodies, 0 scoring differently; baseline arm agrees with the
published verdict on 32,263/32,339.
Re-measured on the rebased tree (#280
funcboundflowis upstream of P8)funcboundflowchanges function boundaries, so the sweep was re-run onbb05b83eover a representative 7-project slice spanning all threearchitectures and containing every previously-recorded mover:
openssh-portable, betaflight, coreutils, dpkg, mydoom, libacl, zlib —
139 slices, 16,019 functions.
The off-perfect set is identical function-for-function, and so is the
to-perfect count. The boundary change did not disturb this result; only three
functions moved by a point or two inside
worsened. Post-rebase headline forthe slice: 3,575 bodies changed, GED 123,837 -> 121,961 (-1,876), perfect
6,817 -> 7,016 (+199), 209 / 10 (20.9 : 1, z = +13.45).
The mirror population, by name
All 19 full-corpus off-perfect functions (
moved.csvin the sweep output):Two were read line by line and are the metric, not the option. With
guardarm on,betaflight cliDumpPrintLinefandzlib test_inflatematchtheir preprocessed source literally —
if (!((dumpMask & DO_DIFF) && equalsDefault)) { ...; return true; } return false;(
O0/betaflight/compiled/cli.i:17169) andif (strcmp(...)) { fprintf(stderr,"bad inflate\n"); exit(1); } else { printf(...); }(
O0/zlib/compiled/example.i:4170) — and GED still charges 8 and 6. Thataccounts for the whole ARM column.
One is a real limit and is written into the option's catalog row:
e2fsprogs preenhalt's source isif (!(ctx->options & E2F_OPT_PREEN)) return;and the option inverts it, because that arm is a return-split clone whose
start address is the shared epilogue's, not the source position of the
return.When the arm is a clone the layout signal is destroyed.
Requirement 8 — every changed function, not the witness
All 6,821 changed bodies audited mechanically (callee multiset, string
multiset, numeric multiset, statement count, plus shape totals):
while( true )// no-return1,036 of the 1,045 flagged functions differ only in the comparison constant
a negated condition renders (
v4 <= 0xb->10 <= v4). The rest were read:coreutils pr skip_readflipsv3 = v4 == 0xctov3 = v4 != 0xcand flipsits single use (equivalent);
gnutls certtool process_optionsandopenssh ssh mux_client_request_terminateare de-nestings that drop redundantv7 = v1copy shadows.Speed
Interleaved min-of-N whole-binary
decompile-all, re-measured on the rebasedbuild:
guardarm oncoreutils ls(min-of-5)openssh sshd(min-of-3)Inside measurement noise and far within the 5% budget. (Per-sample variance on
this box is +-20% under load, which is why only interleaved minima are quoted;
an earlier loaded run of the same pair read +1.14% / +6.01% and did not
reproduce.)
Gates — run on the REBASED tree (
ee14e461, base11f40f46)Exit codes captured directly (
make ... > log 2>&1; echo $?), never through apipe to
tail— a pipeline returnstail's status and hides a failure.Every hard-coded catalog count re-derived from the built artifact, not by
arithmetic, and then grepped back out of the file to prove the edit is not a
silent no-op. (A clean rebase is not evidence of a correct count: #280 and
#278 made the identical
95 -> 96edit at four sites, git auto-merged themwithout a conflict marker, and
catalog_bytecompat.rsdid not even appear ingit status.) Sources: livekuna catalog --json,grep -c '^\[\[settable\]\]' phases.toml, a freshphase_catalog.jsonrecapture, and
ls tests/{datatests,stages}/*.xml | wc -l:kuna catalog --jsonoptionskuna_phases/tests.rskuna_num_settables/SETTABLE_TABLE.lenemit_catalog_json},\ncountcatalog_bytecompat.rsfixture"option"/"tier"/"symptoms"kuna-catalog.xml#7structure-recoverykuna-base/src/xml.rscorpus filesphase_catalog.json"option"entriesRead the assertions, not the test names: on
mainthe function is calledsettable_count_is_95while its body asserts96. This branch's function nameswere updated too, but the numbers above are the contract.
docs/options.mdandtests/fixtures/phase_catalog.jsonregenerated from thatsame build;
docs/baseline-stages.jsonre-recorded, not hand-merged.Scope
Default-OFF opt-in,
tier = transform. No DIV row: a new default-offoption changes no default. Listed in
modes.rs'sUNEVALUATEDnext toparamcopyhoist— preset membership would make it the default output(
auto->aggressiveunder 500 KiB) and that is the DIV-recorded change,wanting its own PR whose only remaining work is the 0/675 ablation with the flag
forced on plus a preset-level speed number. The corpus half of that argument is
already done above.
Spec prose:
docs/spec/08-structuring.mdsection 8.1.Merge order
mainloopcondhoist, symptom B, stacked on this branch) -> retarget tomain, then mergedelete_branch_on_mergeis set repo-wide, so merging this PR deletesfeat/guardarmand auto-closes #283. Retarget #283 tomainbeforemerging this one.
🤖 Generated with Claude Code
https://claude.ai/code/session_01C8UQbPqALzdUQ3cLLjUeKH