Repository navigation
0.70.3: docs-only fast path redesign: one total verdict function, not hand-picked shell excuses (#4717) #4752
Description
Parked to 0.70.3: #4717 hit the same review finding (B3) in two rounds, so it gets this redesign instead of a third round. Owner aprender-w4472. Refs #4678, #4717.
The finding (B3, guard_mutation_below_100, both rounds)
#4717 lets a docs-only PR skip the Rust-only fat sections. To keep the required checks green,
both verdict steps (ci / gate and gate) gain an excuse: a missing sov.gate /
determinism-compare is accepted when CLASS_JOB:CLASS is success:docs
(ci.yml case "$CLASS_JOB:$CLASS:$sec" in success:docs:sov.gate | success:docs:determinism-compare)).
The guard scripts/check_ci_gate_docs_class_rule.sh runs the two steps' run: bodies against
fixture rows.
-
Round 1 (head 78551b0): 22 mutants, 17 killed, 5 survived. One was real: the
ci / gate
docs loop could dropguard-cargoand every guard stayed green. -
Round 2 (head d7a6b3c): all 5 of round 1's survivors were killed. 5 new non-equivalent,
in-scope mutants of thegatestep survived:- m07:
X86:sov.gateis dropped from the loop; - m22: the excuse is widened to
success:*:sov.gate; - m08:
sov.gateis excused regardless ofCLASS_JOB; - m23:
determinism-compareis excused regardless ofCLASS_JOB; - m24: an empty
CLASS_JOBsatisfies the excuse.
Each one lets a FAILED
sov.gatemerge on a class=full PR, or on a failed classifier, while
the guard and its self-test stay green. - m07:
-
Also open, outside the mutants: 4 job-wiring mutants (m18–m21):
- m18/m19:
needs: change-classis dropped; - m20/m21:
CLASS_JOBorX86_RESULTis hard-wired inenv:.
The guard supplies the env itself, so it cannot see these.
- m18/m19:
Why it recurs
- The rule is an open shell surface, not a closed table. The decision depends on
(event × CLASS_JOB × CLASS × section × result), but it is written ascasepatterns,for
lists andifs, separately in two steps. The guard samples that space with rows chosen by
the author.- Each review plants a new family of mutants and finds the cells no row visits.
- Killing the named survivors adds rows for those cells only, so the next round finds the
next unvisited cell. Round 2's rows coveredci / gatefailure cases but never fedgate
a failingsov.gatewith a failed or empty classifier.
- There are two copies of the rule.
ci / gateandgateeach encode the excuse, so every
fixture row must exist twice. Round 2's survivors are all in the second copy. - The wiring is outside the guard's reach. The table runs only the
run:bodies, so
needs:/env:edits (m18–m21) can change what the steps read without any row moving.
Fixing more survivors, row by row, does not converge. The redesign has to make the decision
space finite and fully checked.
Option A: one verdict function plus an exhaustive truth table (keeps the excuse)
-
Move the excuse into one sourced bash library (option-neutral, no
set), for example
scripts/ci_gate_verdict.sh. It exposesverdict <event> <class_job> <class> <section> <result>,
which printspass,failorexcused. Both verdict steps call it. No rule text stays inline. -
Commit the oracle as data: a TSV over the full product of the domains, including the out-of-
domain values.- event: pull_request, merge_group, push.
- CLASS_JOB: success, failure, cancelled, skipped, empty.
- CLASS: docs, full, empty, other.
- section: every gated section.
- result: success, failure, cancelled, skipped, missing.
This is a few hundred cells. The guard checks every cell, and the self-test proves that each
planted widening or narrowing flips at least one cell. This makes the mutation score 100% by
construction: any change to the function's behaviour changes a cell. -
Add structural rows that read ci.yml itself:
- both verdict jobs
needs: change-class; CLASS_JOBis exactly${{ needs.change-class.result }};- no
env:literal overrides it.
These kill m18–m21.
- both verdict jobs
-
Cost: medium. It is still a gate loosening, so it needs a new sign-off row, but the signed
intent (row 4) is unchanged.
Option B: no excuse in the gate; the docs path produces real results (gate unchanged)
- Leave both verdict steps exactly as on main: every gated section must report
success. - On class=docs, the producer job runs a docs variant of each gated section instead of skipping
it. The variant is a cheap step that re-runs the classifier on the merge commit and asserts
the diff is still docs-only, then reportssuccessunder the same section name, with the
class and the sha in its log.- A failed or absent classifier means no docs variant, so the full sections run.
- It fails closed by construction.
- The gate gains no new code, so there are no gate mutants. All risk moves to the classifier,
which gets the hardening below. - Cost: lower gate risk and some producer plumbing in ci/sections.yml (no fat_driver growth).
The minimum skipped work is one runner start-up per variant. - Risk: a
successfrom a section that did not run its full body has to stay visibly
distinguishable in the log, and in Σ-executed where it applies (L25: report it as
not-triggeredin the section log, never as a pass of the full body).
Recommendation: B. It deletes the surface that failed twice instead of fencing it. Fall
back to A if a docs variant cannot satisfy the required-check names.
Carried items (either option)
- The classifier misses split macros. agy, re-measured by the primary:
include_str!with
the.mdliteral 4 or more lines below the macro reads class=docs. Replace the fixed context
window with a token-level scan (or arustc-freeinclude_(str|bytes)!(…)multi-line
match), plus a case-table row for 1, 3, 4 and 8 line splits. - There is a duplicate diff derivation.
ci_change_class.shre-implementsderive_diff
fromscripts/ci_gpu_touched.shand a seconddocs_only()fromscripts/ci_test_tier.sh.
Use one library. - The PR's own receipt defeats the fast path. A docs PR that commits its receipt under
evidence/pr-review/<pr>/is class=full, so the fast path never fires for it. Counting the
receipt as docs widens the classifier: that is its own sign-off row, and nothing is built
for it before signing. - The mutants rule waits on fix(ci): RESTORE mutants gate rules after 0.70 (#4646) #4648. Once fix(ci): RESTORE mutants gate rules after 0.70 (#4646) #4648 removes the
exit 0above GATE-MUTANTS-RULE,
a docs PR readsmutantsas missing. Under A it needs a table cell; under B, a docs variant
ofmutants.
Acceptance
- A case table RED on origin/main and GREEN on the branch.
- Under A: every cell of the oracle is checked, and the self-test kills 100% of the planted
mutants, including m07, m08, m22, m23, m24 and m18–m21 by name. - Under B: the gate diff is empty, and the classifier table includes the split-macro rows.
- Mutants reported as killed/total, receipts on the CI image, bash only (no Python, C301), and a
quorum of two non-author models.
Activity
noahgift commentedon Oct 8, 2026
Parked by APR-EPIC-001: over E1 budget (rank 28/37). Reopen = pull.
Claim: owner:aprender-w4472. Branch heads: none. C334 item 2 (#4678's open children to E1 #3998); nothing deleted.