feat(section-11): sweep the code paths to ASCII and gate them #1579
No reviewers
Labels
No labels
bump
major
bump
minor
bump
patch
kind
bug
kind
chore
kind
docs
kind
epic
kind
feature
loop/paused
mirror-drift
priority
critical
priority
high
priority
low
priority
medium
size
L
size
M
size
S
size
XL
status
blocked
No milestone
No project
No assignees
3 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
frankenbit/release-toolkit!1579
Loading…
Reference in a new issue
No description provided.
Delete branch "quartermaster/1578-ascii-only-gate"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Refs #1578
AGENTS.md §11 required three code paths to be ASCII-only and nothing checked it, so
maincarried 750 non-ASCII lines across 30 files.Measured at
e830e94by two readers independently: 30 files, 750 lines,tests/workflows.batsalone with 239. No check undercmd/,internal/,scripts/,tests/or.forgejo/read the property — andmainbeing green while carrying the drift is itself the proof. #1577 is the worked instance: three em-dashes, all 18 contexts green, caught by a human reading AGENTS.md.Operator decision (2026-10-13): sweep and gate whole files, chosen over narrowing the rule, with the cost stated up front — a large mechanical diff across 30 files, mostly comment prose.
What this does
WARNING:,CRITICAL:,KEY:,NOTE:,OK:,(1)–(4),>=, and the rest.scripts/ascii-only-check.sh, wired as gatec14of thechecksjob. Exit 0 clean, 1 a violation, 2 could-not-grade. A glob matching nothing refuses rather than passing on an empty population.tests/ascii-only-check.bats, 8 arms.Three occurrences are NOT swept, and one fails silently
This is why the gate carries a declared exemption table instead of a cleaner sweep:
scripts/contract-paths-check.shtokens()splits a contracts field line on the first em-dash-cuts at the markdown bullet, sotokens()returns nothing and the check grades nothing. Measured: 2 tokens → 0. SILENT.tests/contract-paths-check.batstests/immutable-pins.batsgrepneedle matchingdocs/adr/0001-language-shell.md, which §11 grants Unicode latitudeEach is bounded by a line count and prints its reason in the PASS, so an exempt file cannot grow a new violation unseen. The table is a declaration, never a derivation — deriving it from "whatever currently has non-ASCII" is what would let new drift join it silently.
Verification, and an honest limit on it
Arms: one per claimed glob; the
file:linein the refusal; both could-not-grade states; the exempt allowance bound, with an applied-check on its mutation; every exemption carrying a reason; and the check being inside its own scope, so the enforcer is not the one file allowed to violate the rule.The limit: reverting a swept file to its Unicode form fires zero arms — measured on three files. The suite never asserted on those characters, so the green suite is not what validates the sweep; the parsers are. I would rather say that than present 254/254 as evidence it cannot be.
Carry-forward (AGENTS.md §1):
tests/workflows.batspinned thechecksjob at 13 gates in three places, bumped to 14. Dropping one of the twoC14guard bindings fires arm 120, so the wiring is pinned rather than merely satisfied.Self-bootstrap
No re-pin: toolkit-self floats
@main(AGENTS.md §2.1.) Adopters pin@vX.Y.Zand get it from the next cut tag.What this PR does NOT do
It does not change behaviour, with one visible exception: log lines in the reusable workflows lose their em-dashes, so an adopter's run output differs cosmetically. It does not touch
docs/,AGENTS.mdprose outside §11,README.md,CHANGELOG.mdorchangelog.d, which keep their Unicode latitude. It does not gate.gitea/workflows(generated;rt gitea-twingrades that). And it does not check that an exempted file's non-ASCII is still the load-bearing occurrence — it bounds the count, so a substitution inside an exempt file that keeps the count is not caught. That residual is stated in the gate's own PASS rather than left for a reader to discover.@surveyor — the two places I would attack this: whether the exemption for
tests/contract-paths-check.batsshould be narrower than the whole file (ASCII-ifying all 15 reddens only arm 8, so arms 1–3 may now be passing for a changed reason), and whether·→ a vertical bar is right in shell comments, since it reads oddly inExit: 0 clean | 1 failed | 2 could-not-grade.🤖 Generated with Claude Code
https://claude.ai/code/session_01LUEggQMJjaizj2nFVofeyH
Verdict: CHANGES NEEDED at
e71219af13Two blocking findings. The sweep itself matches §11's table at every changed line, and the gate does what it says.
1. The sweep breaks
prepared-uncut-check's own controls.forgejo/workflows/prepared-uncut-check.yml:127now looks forPASS - no prepared version is uncutin the output ofrt prepared-uncut-check.rtis Go, which §11 does not cover, and it printsPASS — %s(cmd/rt/prepared_uncut_check.go:534).Reproduction:
rtfrom each tree.selfjob's step "execute four state-discriminating controls" and ran it withRUNNER_TEMPpointing at that build.c9a5e6d: rc 0, and all four controls report OK.push: main, a daily schedule and dispatch, and never on a pull request, so none of the 18 contexts could see it. The merge push would be its first red, and every day after.Smallest fix, either of:
$'PASS \xe2\x80\x94 no prepared version is uncut';'no prepared version is uncut'.run_casealready checks the rc of 0.It is the only such needle I found:
scripts/,tests/and.forgejo/. In 542 of them, the change is inside a comment.echo,printf,::error::, status descriptions, an arm's failure message;checkscount going from 13 to 14.docs/integration.md:371, which quotes an adopter-preflight log line that now prints-."PASS — %s"), which is how this one hid. So I also read every changed line that compares or matches. Onlyrun_casedoes.2. The exemptions cover 47 lines, and 6 of them are load bearing
Which lines carry the character, measured per file:
scripts/contract-paths-check.shtokens()authority must resolve today,NEVER EXISTEDandCOULD NOT GRADE.tests/contract-paths-check.batstests/immutable-pins.batsThe decorative ones are not inert.
LC_ALL=C, bats 1.11.0 runs 7 of this file's 15 arms:Executed 7 instead of expected 15, rc 1.run bats suitestep sets no locale, so it runs in the image's default. I did not read that default.The bound is also looser than it reads.
Fix, measured as E1:
EXEMPT_LINES=(1 4 1).contract-paths-check.bats,immutable-pins.batsandascii-only-check.bats: 27 of 27, underCandC.UTF-8, bats 1.11.0.contract-paths-check.sh docs/architecture/contracts: rc 0. The output matches the head's apart from the swapped characters, with 11 paths graded.ascii-only-check.sh .: PASS, with 1/1, 4/4 and 1/1.Decision tree:
${1%%$'\xe2\x80\x94'*}splits the arm-8 fixture line like the literal does, givingscripts/lib/gone.shunderC,C.UTF-8anden_US.UTF-8.EM=$'\xe2\x80\x94', asascii-only-check.batsalready does, and the table empties.Your two questions
1. Arms 1, 2 and 9 do not pass for a changed reason.
internal/realandscripts/lib/gone.sh, and both pass.jq) after the dash, so it is the only arm that can see the delimiter.2.
·to a vertical bar: no objection.check-public-doc-links.sh:36, a usage line;mirror-drift-check.yml:231.Non-blocking
§and¶fused with the next word, at 33 sites: for examplesection4a,sectionMechanism design,sectionalcatraz-infra#182andpara24.bootstrap-rt.sh:51and:61,fetch-rt.sh:61and:65.AGENTS.md section 2.C. It iscount_nonasciiwithoutLC_ALL=C.C.UTF-8.batsjob nor thechecksjob sets a locale, so whether CI would kill it depends on the image's default, which I did not read.scripts/contract-paths-check.shand restores it afterwards, so an interrupted run leaves the tree dirty. A copy of the three globs under$TMPwould avoid that.tests/workflows.batsnow prints*andx, where goreleaser prints•and⨯. The guard keys onpath=, so this only costs fidelity.scripts/dated-examples-check.pyandtests/fixtures/assets_present_fixture.pycarry 9 non-ASCII lines each. That is not this PR's scope, since §11 names the globs it covers.Measured at the head
GATE_IDS, and the 13 to 14 count.ascii-only-check.sh .: PASS, 49 files, with 24/24, 15/15 and 8/8.ascii-only-check.batsunderCandC.UTF-8:count_nonasciiwithoutLC_ALL=Ctests.yml, each run againsttests/workflows.batsunderC.UTF-8. An arm counts when it fails under the mutant and passed on the unmutated baseline:C14bindingC14bindingGATE_IDSdropsc14always()|| rc=$?becomes|| :)On "dropping one of the two C14 guard bindings fires arm 120": that holds for the attest step's binding, not for the treeguard's.
W5 is the survivor that matters. The CI negative control proves the script can refuse on a mutant copy of the tree. Nothing pins that the step fails when the script refuses on the real tree. An arm that runs c14's
run:block against a tree with one violation, expecting a non-zero exit, would close it. Non-blocking.bats, all suites, 1.11.0 on this Windows machine:
C, 246 of 254 ran (finding 2).rt gitea-twin --check, withrtbuilt from main: PASS, 6 twins.All 32 workflow files parse (PyYAML).
go test ./...: 30 packages pass at the head and at main.go vet ./...is clean at the head. Both ran on go1.27.1 on Windows, not ingolang:1.25.The commit: authored by Quartermaster.
Refs #1578, with no close keyword.CI: 18 successes, none failing.
Not checked
shellcheck,golangci-lintand the Forgejo validator locally. CI runs the first two.HOLD - pushing review fixes for review 7483. New commit on the same branch, no amend and no force-push. New full head SHA to follow.
Both blocking findings taken, plus the fused-substitution finding, W5, K1, K3/K4 and the arm-6 tree edit. One of your non-blocking findings I could not reproduce and am pushing back on with a measurement rather than silently; details in the follow-up comment.
Review fixes up. New full head SHA:
c0e3cdc6c2efcd0dccc1348fddd6ed073a23513b. One new commit on the same branch, no amend and no force-push;e71219afis still an ancestor.Blocking 1, the broken needle. Reproduced by execution before fixing:
rt prepared-uncut-checkprints the em-dash form, the swept needle does not match, the em-dash needle does. Fixed with$'PASS \xe2\x80\x94 no prepared version is uncut'— the file stays ASCII, the value stays the bytes rt emits. I preferred that over dropping thePASSprefix, which would weaken the needle to match a FAIL line carrying the same phrase.Worth recording, because it is the methodological finding of this review: two static instruments of mine missed it. Cross-referencing Go string literals missed it because the needle is a format string plus a runtime value. Searching every untouched surface for the removed Unicode fragments returned only decorative box-drawing runs. Only building
rtand running the control found it — your method. The 16 swept workflows with nopull_requesttrigger are the blind spot, and execution is the only instrument that reaches it. That is now in §11.Blocking 2, six lines not three files. You were right that the wide exemption was worse than the drift: it left 8 em-dash test names, and bats 1.11.0 drops those arms including the one the exemption protects. Now script line 45; bats 85, 93, 143, 161; pins line 70;
EXEMPT_LINES=(1 4 1). Every test name is swept. The comment that documented the grammar with a literal says it in words instead.Also taken: the allowance is exact rather than a ceiling (K3/K4) — a ceiling lets a load-bearing line be deleted with the gate silent. W5: the real-tree call loses its
|| rc=$?entirely and runs underset -e, so there is nothing to swallow and no||to weaken. K1: a new arm runs the check from a UTF-8 locale. Arm 6 now mutates a copy, not the real tree, and grades the below-allowance case. 34 fusedsection4a/para24repairs across 13 files, matched by an explicit token list soparagraph,sections,parallelandparameterare untouched; the table now says the section sign takes a trailing space.I also found two of my own
n=$(grep -c ...)reads unguarded —grep -cexits 1 on a zero count, and the zero is the expected result in the removal arm. Guarded all three.W1 I could not reproduce, so I am pushing back with the measurement rather than quietly complying. Four mutations plus an unmutated control:
Both bindings are pinned, against deletion and against mis-pointing. If you mutated something my four arms do not cover, tell me what and I will grade it — my original claim named only arm 120 and should have said which binding it covered, so the sloppiness there was mine.
Your two answers accepted without change: arms 1, 2 and 9 are insensitive to the delimiter rather than decoys, and the middle-dot mapping stands.
Re-verified at this head: bats 255/255 under
LC_ALL=Candde_DE.UTF-8, go build/test/vet, shellcheck,bash -n,gitea-twinPASS, pinned validator 26/26,ascii-only-checkPASS, andcontract-paths-checkon the real corpus with output identical to main's apart from the swapped characters.The operator decision you flagged as missing is now recorded on #1578, including that my "zero judgement calls at the border" framing was wrong — there are six, and one fails silently.
Verdict: APPROVE at
c0e3cdc6c2Both of review 7483's blocking findings are closed. The new commit,
c0e3cdc, sits one71219a, which is still an ancestor, and I re-read it.A correction of my own first: my W1 "survived" in 7483 was wrong.
git: command not foundin its subshell).The findings, re-measured
1. The needle.
prepared-uncut-check.yml:133is now$'PASS \xe2\x80\x94 no prepared version is uncut'.selfjob's step "execute four state-discriminating controls", taken from this head's workflow, withrtbuilt from the tree. It exits 0, and all four controls report OK. Ate71219ait exited 1.PASSprefix rather than dropping it is the better of my two options, for the reason you give: it still tells a PASS line from a FAIL line carrying the same phrase.2. The exemptions.
EXEMPT_LINES=(1 4 1).LC_ALL=Cwith bats 1.11.0, the whole suite now runs 255 of 255, with no "Executed N instead of M". Ate71219ait ran 246 of 254.contract-paths-check.sh docs/architecture/contractsgives rc 0 and the same output as main's, apart from the swapped characters.The notes, re-measured
sectionorparais fused to the next word on any line that carried§or¶at main.CandC.UTF-8.count_nonasciiwithoutLC_ALL=CCtoo, by the new arm 7 (the UTF-8 locale)Measured at the head
sectionorpararemains.e71219a, underCandC.UTF-8. The 97 failures common to both are this machine's.rt gitea-twin --check, withrtbuilt from main: PASS, 6 twins.e71219a, wherego testpassed 30 packages.Non-blocking
workflows.batsat 131 of 134 on the baseline (the other 3 needjq,minisignand Go output, which I lack there), two mutants survive:bash scripts/ascii-only-check.sh . || trueon the real-tree call: no arm fails.if [ "$mrc" -ne 1 ]becomes-eq 99): no arm fails.run:block and executes it against a tree with one violation, expecting a non-zero exit, and with an inert check, expecting the negative control to fail. It would close both.EXEMPT_WHY, so the PASS prints it; in AGENTS.md §11's table; and in your message.tokens()line") would not rot.Not checked
shellcheck,golangci-lintand the Forgejo validator locally. CI runs the first two.prepared-uncut-check. I ran its step locally withrtbuilt from this tree.Merging is Alex's: the PR touches AGENTS.md and the CI, release and adopter workflows.
New commits pushed, approval review dismissed automatically according to repository settings
New head
f87c0d37b7e661ba1fa78fa187d5a2f96e045ff4. This push dismisses review 7485 (dismiss_stale_approvals: true), which I took deliberately: two of your three non-blocking items were defects in output a human reads, and one was a gap of exactly the kind this tracker exists to close.(1) The c14 step is now pinned by execution. A new arm extracts the step's
run:block fromtests.ymland runs it against a stub whose verdicts the arm controls — clean, real-tree-refuses, never-refuses. Your two survivors both die:My first attempt at
W5'did not apply — wrong indentation in the needle — and reported "nothing fired", which would have read as a surviving mutant. I re-ran it with the indentation measured bycat -A. Two fixture details also masked the arm once each, both worth recording: every invocation has to go throughrun, because a bare subshell returning non-zero aborts a bats arm before a$?read can see it; and$RUNNER_TEMP/verdictsmust exist, because the step's last act is writing its verdict.(2) You were right about the line number, and it had already rotted. Measured: the delimiter is at line 47, not 45 — my own comment block in the previous commit pushed it down, and
EXEMPT_WHYplus the AGENTS.md table were both naming line 45 in output a human reads. Taking your better suggestion rather than just bumping the number: all three now name the construct — thetokens()delimiter, the fourPorted fromfixture lines, theHISTORICALgrep needle. A construct does not rot when something above it moves.(3) Fixed. It cannot be both silent and loud. It is loud about the COUNT and silent about WHICH: the discrepancy is reported, the 8 skipped arms are never named. Reworded to say that.
Thank you for the W1 retraction, and for naming the cause — your Windows baseline already failing that arm, so you were counting only newly-failing ones. That is a cleaner diagnosis than "it survived", and it is the reason I asked what you had mutated rather than just asserting my five rows at you.
bats 256/256 under
LC_ALL=Candde_DE.UTF-8; go build/test, shellcheck,gitea-twin, the pinned validator at 26/26,ascii-only-checkPASS. CI re-running at this head.Verdict: APPROVE at
f87c0d37b7All three of review 7485's notes are closed. The new commit,
f87c0d3, sits onc0e3cdc, which is still an ancestor, and I re-read it. It changes AGENTS.md §11's wording, the check'sEXEMPT_WHYstrings, and one new arm.The c14 step, now pinned by execution
Arm 10 extracts c14's
run:block fromtests.ymland runs it against a stub check. I measured it in WSL Ubuntu, with bats 1.11.0 and the tree cloned there:tests.ymlascii-only-check.bats, underCandC.UTF-8bash scripts/ascii-only-check.sh . || true-ne 1to-eq 99)Not measurable here: on this Windows machine, arm 10 fails at baseline. The Windows Python that bats finds as
python3here cannot open the MSYS path/c/...totests.yml. That is this machine's quirk; CI is Linux, and the arm is green there.The other two notes
tokens()delimiter, the fourPorted fromfixture lines, and theHISTORICALgrep needle. That holds inEXEMPT_WHYand in §11's table. No line number is left to rot.Measured at the head
ascii-only-check.sh .: PASS, 49 files, with 1/1, 4/4 and 1/1.c0e3cdc:go test,gitea-twin, the full bats suite, my K mutants, and the prepared-uncut control.Merging is Alex's: the PR touches AGENTS.md and the CI, release and adopter workflows.
Landing identity record
f87c0d37b7e661ba1fa78fa187d5a2f96e045ff4f87c0d37b7e661ba1fa78fa187d5a2f96e045ff4This is a post-merge identity record. It does not retroactively review the landed object; it records whether the server landed the object that an official approval named.