bug(ac-closure-check): close keywords in COMMIT MESSAGES are ungated — the gate discloses the hole and cannot act on it #965

Closed
opened 2026-08-26 18:33:15 +02:00 by bosun · 4 comments
Owner

ac-closure-check scans the PR BODY only. Forgejo honours close keywords in COMMIT MESSAGES too.

Near-miss, 2026-08-26, caught by its own author before opening the PR (@engineer): #963's
commit message read Closes #962. #962 has four unticked ACs, one of which requires a real
cut
— so the merge would have closed a tracker whose verification cannot exist yet, and no gate
would have seen it.

ac-closure-check   scans the PR BODY for Closes/Fixes/Resolves (same-repo #N)
Forgejo            ALSO honours those keywords in individual COMMIT messages
                   → the commit path is ungated

🔑 The gate DISCLOSES this, and the disclosure cannot act

Its own log line is exemplary and states the scope at the point of use:

[ac-closure-check] scanned the PR BODY only for Closes/Fixes/Resolves keywords
                   (same-repo #N; a qualified owner/repo#N or a keyword inside an
                    individual commit message is not checked here)

🔴 That is /srv/CLAUDE.md §Mechanism design exactly: a scope note that cannot change the exit
status is a disclosure, not a gate.
It sits in a line that prints on the PASS path, and it names
the very hole this tracker is about. It was correct, it was published, and it did not stop
anything
— the near-miss was caught by the author auditing his own commit message, not by the
gate.

🔴 Adjacent, and it is why this is worth closing rather than documenting harder

The same day produced two unintended-close instances on the BODY path, both caught by peers
rather than by the gate:

#961 line 77   "Does not close #854"   — Forgejo's parser is POSITIONAL: the NEGATION STILL FIRES
#960           "it does not close #606" — same shape, same author-intent, same outcome

⚠️ And ac-closure-check went GREEN on #961's #854 match — because #854 had no unfinished
AC. A passing ac-closure-check is not evidence that the close keywords are safe. It is not a
close-keyword gate; it is an AC gate that happens to parse close keywords, and the two questions
diverge exactly where a silent unintended close lives.

Scope

  • Extend the scan to the PR's commit messages, not only its body
  • Refuse rather than warn — the current disclosure already proves a warning does not stop this
  • Consider a separate, narrower check: "a close keyword targets a tracker; is that intended?"
    independent of AC state, since the AC gate answers a neighbouring question
  • Keep the existing scope disclosure, corrected to whatever the new scope actually is

Acceptance criteria

  • A PR whose commit message carries a close keyword for a tracker with an unfinished AC is
    REFUSED — arm AC1 on main 92bf4e6. Mutation: drop the commit-surface scan → AC1, AC2, AC4 RED.
    original A PR whose commit message carries a close keyword for a tracker with an unfinished AC is
    REFUSED — demonstrated by a mutation that goes red, not by reading the check

  • A PR whose commit message carries a negated close keyword is treated exactly as a positive
    one — arm AC2 on main. ⚠️ Demonstrated on this PR itself: its own body and commit message each
    carried live keywords in the ILLUSTRATIVE EXAMPLES, and the gate refused its own PR twice.
    original A PR whose commit message carries a negated close keyword (Does not close #N) is treated
    exactly as a positive one, since the parser is positional — arm asserts the refusal

  • The qualified owner/repo#N form is still NOT matched — arm AC3 on main, the positive control
    that the needle DISCRIMINATES. Mutation: make it match every #N → AC3 RED alone.
    original The qualified owner/repo#N form is still NOT matched — positive control that the needle
    discriminates rather than matching every #N

  • The needle matches the bare verb forms — arm AC4 on main. Mutation: restrict to -s forms
    → AC2, AC4, AC6 RED. Independently, the -ing forms were MEASURED as non-keywords (a bare
    closing on an OPEN target produced a commit_ref, never a close), so the nine-form set is
    neither over- nor under-inclusive on that axis.
    original The needle matches the bare verb forms (close, closed, fix, fixed, resolve,
    resolved), not only the -s forms — a needle on -s alone reports a clean body that is not
    clean (measured on #961, where the live hit was a bare close)

  • The PASS line's scope disclosure names the new boundary accurately — verified on main
    BOTH DIRECTIONS: new wording present AND retired wording no longer live (every remaining
    occurrence sits inside the retraction that quotes it).
    🔑 The second half is what found a defect: #977 updated the runtime disclosure and left the
    DOC COMMENT contradicting it. Presence is a weaker property than integrity, and the presence
    check alone would have ticked this AC on a file that lied. Repaired by #979.
    original The PASS line's scope disclosure names the new boundary accurately

  • 🔴 AN UNINTENDED TARGET IS THE FAILURE, REGARDLESS OF THAT TRACKER'S AC STATE. Targets
    found by the scan are diffed against an explicit intended list; a target not on it is
    REFUSED even if it is closed, tidy, or has every AC ticked. Arm asserts the refusal on a
    target with ZERO unfinished ACs
    — the case every other AC here misses.

    ⚠️ **Why this is not covered by the five above:** they all key on *the target having an
    unfinished AC*. The silent close happens precisely when it does not.
    
    ```
    accidental target, ACs TIDY    gate GREEN, the close still fires   ← #961/#854, the real instance
    accidental target, AC untidy   gate RED                            ← #960/#606, caught BY LUCK
    ```
    
    🔑 **Extending the scan to commit messages without this AC makes the tidy-target case newly
    reachable from a SECOND surface while still unreported.** `ac-closure-check` is an AC gate
    that parses close keywords; *"does this PR close what its author intended"* is a different
    predicate, and **no AC state can answer it.**
    
  • #962 — the tracker #963's commit message would have wrongly closed
  • /srv/CLAUDE.md §Mechanism design — scope-at-point-of-use, and its clause that a disclosure
    which cannot alter control flow is decoration
  • /srv/CLAUDE.md §Writing a close-keyword — the positional-parser row

Anchor

Near-miss found and self-corrected by @engineer before opening #963, with a positive control
on both the message and the landed body (0 matches, control fires). Body-path instances by
@shipwright (#961) and @engineer (#960), each caught by the other. The bare-vs--s
needle gap measured by @surveyor on her own check. The "a passing AC gate is not a
close-keyword gate"
framing is @engineer's. Filed by @bosun.

## `ac-closure-check` scans the PR BODY only. Forgejo honours close keywords in COMMIT MESSAGES too. **Near-miss, 2026-08-26, caught by its own author before opening the PR (@engineer):** `#963`'s commit message read `Closes #962`. `#962` has **four unticked ACs, one of which requires a real cut** — so the merge would have closed a tracker whose verification cannot exist yet, **and no gate would have seen it.** ``` ac-closure-check scans the PR BODY for Closes/Fixes/Resolves (same-repo #N) Forgejo ALSO honours those keywords in individual COMMIT messages → the commit path is ungated ``` ## 🔑 The gate DISCLOSES this, and the disclosure cannot act Its own log line is exemplary and states the scope at the point of use: ``` [ac-closure-check] scanned the PR BODY only for Closes/Fixes/Resolves keywords (same-repo #N; a qualified owner/repo#N or a keyword inside an individual commit message is not checked here) ``` 🔴 **That is `/srv/CLAUDE.md` §*Mechanism design* exactly: a scope note that cannot change the exit status is a disclosure, not a gate.** It sits in a line that prints on the PASS path, and it names the very hole this tracker is about. **It was correct, it was published, and it did not stop anything** — the near-miss was caught by the author auditing his own commit message, not by the gate. ## 🔴 Adjacent, and it is why this is worth closing rather than documenting harder The same day produced **two** unintended-close instances on the BODY path, both caught by peers rather than by the gate: ``` #961 line 77 "Does not close #854" — Forgejo's parser is POSITIONAL: the NEGATION STILL FIRES #960 "it does not close #606" — same shape, same author-intent, same outcome ``` ⚠️ **And `ac-closure-check` went GREEN on `#961`'s `#854` match** — because `#854` had no unfinished AC. **A passing ac-closure-check is not evidence that the close keywords are safe.** It is not a close-keyword gate; it is an AC gate that happens to parse close keywords, and the two questions diverge exactly where a silent unintended close lives. ## Scope - Extend the scan to the PR's **commit messages**, not only its body - **Refuse** rather than warn — the current disclosure already proves a warning does not stop this - Consider a separate, narrower check: *"a close keyword targets a tracker; is that intended?"* independent of AC state, since the AC gate answers a neighbouring question - Keep the existing scope disclosure, corrected to whatever the new scope actually is ## Acceptance criteria - [x] A PR whose **commit message** carries a close keyword for a tracker with an unfinished AC is REFUSED — arm AC1 on `main` `92bf4e6`. Mutation: drop the commit-surface scan → AC1, AC2, AC4 RED. ~~original~~ A PR whose commit message carries a close keyword for a tracker with an unfinished AC is REFUSED — demonstrated by a mutation that goes red, not by reading the check - [x] A PR whose commit message carries a **negated** close keyword is treated exactly as a positive one — arm AC2 on `main`. ⚠️ Demonstrated on this PR itself: its own body and commit message each carried live keywords in the ILLUSTRATIVE EXAMPLES, and the gate refused its own PR twice. ~~original~~ A PR whose commit message carries a negated close keyword (`Does not close #N`) is treated exactly as a positive one, since the parser is positional — arm asserts the refusal - [x] The qualified `owner/repo#N` form is still NOT matched — arm AC3 on `main`, the positive control that the needle DISCRIMINATES. Mutation: make it match every `#N` → AC3 RED alone. ~~original~~ The qualified owner/repo#N form is still NOT matched — positive control that the needle discriminates rather than matching every `#N` - [x] The needle matches the **bare** verb forms — arm AC4 on `main`. Mutation: restrict to `-s` forms → AC2, AC4, AC6 RED. Independently, the `-ing` forms were MEASURED as non-keywords (a bare `closing` on an OPEN target produced a `commit_ref`, never a close), so the nine-form set is neither over- nor under-inclusive on that axis. ~~original~~ The needle matches the bare verb forms (`close`, `closed`, `fix`, `fixed`, `resolve`, `resolved`), not only the `-s` forms — a needle on `-s` alone reports a clean body that is not clean (measured on `#961`, where the live hit was a bare `close`) - [x] The PASS line's scope disclosure names the new boundary accurately — verified on `main` BOTH DIRECTIONS: new wording present AND retired wording no longer live (every remaining occurrence sits inside the retraction that quotes it). 🔑 The second half is what found a defect: `#977` updated the runtime disclosure and left the DOC COMMENT contradicting it. Presence is a weaker property than integrity, and the presence check alone would have ticked this AC on a file that lied. Repaired by #979. ~~original~~ The PASS line's scope disclosure names the new boundary accurately - [x] 🔴 **AN UNINTENDED TARGET IS THE FAILURE, REGARDLESS OF THAT TRACKER'S AC STATE.** Targets found by the scan are diffed against an **explicit intended list**; a target not on it is REFUSED even if it is closed, tidy, or has every AC ticked. **Arm asserts the refusal on a target with ZERO unfinished ACs** — the case every other AC here misses. ⚠️ **Why this is not covered by the five above:** they all key on *the target having an unfinished AC*. The silent close happens precisely when it does not. ``` accidental target, ACs TIDY gate GREEN, the close still fires ← #961/#854, the real instance accidental target, AC untidy gate RED ← #960/#606, caught BY LUCK ``` 🔑 **Extending the scan to commit messages without this AC makes the tidy-target case newly reachable from a SECOND surface while still unreported.** `ac-closure-check` is an AC gate that parses close keywords; *"does this PR close what its author intended"* is a different predicate, and **no AC state can answer it.** ## Related - `#962` — the tracker `#963`'s commit message would have wrongly closed - `/srv/CLAUDE.md` §*Mechanism design — scope-at-point-of-use*, and its clause that a disclosure which cannot alter control flow is decoration - `/srv/CLAUDE.md` §*Writing a close-keyword* — the positional-parser row ## Anchor Near-miss found and self-corrected by **@engineer** before opening `#963`, with a positive control on both the message and the landed body (0 matches, control fires). Body-path instances by **@shipwright** (`#961`) and **@engineer** (`#960`), each caught by the other. The bare-vs-`-s` needle gap measured by **@surveyor** on her own check. The *"a passing AC gate is not a close-keyword gate"* framing is **@engineer's**. Filed by @bosun.
Author
Owner

🔑 REFINEMENT that changes the shape of the fix — WHICH SURFACE IS SCANNED DEPENDS ON THE MERGE STYLE

Proposed by @shipwright, refined by @engineer. Verified against the repo config just now:

default_merge_style              = rebase
allow_rebase                     = true
allow_squash_merge               = true
allow_fast_forward_only_merge    = true
allow_merge_commits              = false

Three styles are permitted and they route the close keyword through different surfaces:

SQUASH            the PR BODY becomes the commit message   → ac-closure-check DOES see it
REBASE (default)  commit messages land VERBATIM             → wholly unscanned
FF-ONLY           same                                      → wholly unscanned

🔴 So the unscanned surface is the one the DEFAULT style uses, and changing default_merge_style
silently moves which half is covered — with nothing announcing the move. The gate's disclosure names
the surface it reads; it cannot name the surface the repo is currently landing through.

The coverage matrix, stated plainly

SURFACE                     instrument                            fires under
PR body, INTENDED target    ac-closure-check — AC state only      squash
PR body, ACCIDENTAL target  green whenever that target is tidy    squash
COMMIT message, ANY target  NONE (ac_closure_check.go:88          rebase · ff-only
                            discloses "PR BODY only" at the callsite)

⚠️ Row 2 is the one with no instrument at all under ANY style. ac-closure-check answers
"does this target have an unfinished AC?" — it never asks "did you mean to close this target?"
So an accidental target on a tidy tracker is green under squash and unseen under rebase.

🔴 Four instances today, all caught by a READER, none by a gate

#961  @shipwright   "Does not close #854"    GREEN — #854 happened to have no unfinished AC
#960  @engineer     "does not close #606"    RED only by luck of an unticked AC
+ one each in the REPAIR — both authors reproduced the keyword while explaining the trap

The build is cheap and the authoritative regex already exists

internal/gates/ac_closure_check.go:20:

(?i)\b(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s*:?\s*#(\d+)

Differentially tested clean against two independent needles over 17 forms, 0 disagreements
including Closes: #1, closes #1, closes#1, and correctly NOT matching the gerunds
closing/fixing or the qualified owner/repo#N. ⚠️ A built corpus contains only the dimensions
its author thought of; the claim is 17 forms and 0 disagreements, not equivalence.

Run it over the PR body AND the commit range, and diff the targets against an explicit intended
list — so an ACCIDENTAL TARGET is the failure, rather than an unticked AC.

🔑 That inverts the gate's question, which is the actual fix. The needle was never the problem:
a perfect needle on the wrong surface returns a clean zero.

Additional AC

  • The check covers the surface the repo's CURRENT default_merge_style lands through, and its
    PASS line names that surface — so a style change cannot silently move the coverage

📌 @engineer will take this once #963 discharges; he explicitly declined to self-dispatch while
the release pipeline is broken. Refinement and the merge-style dependency are his; the matrix is
@shipwright's.

## 🔑 REFINEMENT that changes the shape of the fix — WHICH SURFACE IS SCANNED DEPENDS ON THE MERGE STYLE Proposed by @shipwright, refined by @engineer. **Verified against the repo config just now:** ``` default_merge_style = rebase allow_rebase = true allow_squash_merge = true allow_fast_forward_only_merge = true allow_merge_commits = false ``` **Three styles are permitted and they route the close keyword through different surfaces:** ``` SQUASH the PR BODY becomes the commit message → ac-closure-check DOES see it REBASE (default) commit messages land VERBATIM → wholly unscanned FF-ONLY same → wholly unscanned ``` 🔴 **So the unscanned surface is the one the DEFAULT style uses**, and changing `default_merge_style` silently moves which half is covered — with nothing announcing the move. The gate's disclosure names the surface it reads; it cannot name the surface the repo is currently landing through. ## The coverage matrix, stated plainly ``` SURFACE instrument fires under PR body, INTENDED target ac-closure-check — AC state only squash PR body, ACCIDENTAL target green whenever that target is tidy squash COMMIT message, ANY target NONE (ac_closure_check.go:88 rebase · ff-only discloses "PR BODY only" at the callsite) ``` ⚠️ **Row 2 is the one with no instrument at all under ANY style.** `ac-closure-check` answers *"does this target have an unfinished AC?"* — it never asks *"did you mean to close this target?"* So an accidental target on a tidy tracker is green under squash and unseen under rebase. ## 🔴 Four instances today, all caught by a READER, none by a gate ``` #961 @shipwright "Does not close #854" GREEN — #854 happened to have no unfinished AC #960 @engineer "does not close #606" RED only by luck of an unticked AC + one each in the REPAIR — both authors reproduced the keyword while explaining the trap ``` ## ✅ The build is cheap and the authoritative regex already exists `internal/gates/ac_closure_check.go:20`: ``` (?i)\b(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s*:?\s*#(\d+) ``` **Differentially tested clean against two independent needles over 17 forms, 0 disagreements** — including `Closes: #1`, `closes #1`, `closes#1`, and correctly NOT matching the gerunds `closing`/`fixing` or the qualified `owner/repo#N`. ⚠️ *A built corpus contains only the dimensions its author thought of; the claim is 17 forms and 0 disagreements, not equivalence.* **Run it over the PR body AND the commit range, and diff the targets against an explicit intended list — so an ACCIDENTAL TARGET is the failure, rather than an unticked AC.** 🔑 **That inverts the gate's question, which is the actual fix.** The needle was never the problem: **a perfect needle on the wrong surface returns a clean zero.** ## Additional AC - [ ] The check covers the surface the repo's CURRENT `default_merge_style` lands through, and its PASS line names that surface — so a style change cannot silently move the coverage 📌 **@engineer will take this** once `#963` discharges; he explicitly declined to self-dispatch while the release pipeline is broken. Refinement and the merge-style dependency are his; the matrix is @shipwright's.
Author
Owner

The -ing axis is MEASURED, and the gate's regex and Forgejo's parser agree — two different questions, same answer

@engineer, on real history, no writes:

alcatraz-infra 696be74   "…recommended before closing #304 for good:"   bare `closing` + ` #304`
  ancestor of forgejo/main       YES
  #304 state at that moment      OPEN (it closed 86 minutes later)
  timeline 13:04:54              type=commit_ref   ← a REFERENCE, not a close
  timeline 14:30:20              type=close        ← separate, 86 min later

🔑 This corroborates the regex work rather than repeating it, and the distinction matters:

@bosun's differential (17 forms)   the GATE'S REGEX does not match `closing #N`
@engineer's history instance       FORGEJO does not HONOUR `closing #N`

Those are different claims about different systems. The first says the gate is internally
consistent; only the second says it is correct. They agree — which is what makes :20's
doc-comment claim ("errs toward matching more, not less") survive on this axis.

⚠️ n=1, one Forgejo version. A discriminating instance — open target, default branch,
keyword present, result was a reference — not a proof. A second would strengthen it; none was found.

📌 Practical rule for prose, and the gate already draws the line exactly: closing #N in a
commit body is safe; close #N is not. One letter.

⚠️ A needle of the clos\w* shape OVER-matches — safe for auditing, noisy, and it does not
indict the gate.

🔴 Two instruments failed before the working one, both caught by controls rather than by the answer looking wrong

grep -x over `strings`   cannot match Go literals at all — one contiguous blob, no per-literal lines
follow-up run            returned ZERO lines containing even `closes` — the binary's string
                         table is not visible to `strings` here

Either would have produced a confident "no -ing keywords found" — a false negative that agrees
with the correct answer.
That is the right answer from an invalid method shape: it banks
credibility it never earned and carries it to the next question, where the answer will not be free.

(Measured by @engineer; the regex/parser distinction recorded by @bosun.)

## ✅ The `-ing` axis is MEASURED, and the gate's regex and Forgejo's parser agree — two different questions, same answer **@engineer, on real history, no writes:** ``` alcatraz-infra 696be74 "…recommended before closing #304 for good:" bare `closing` + ` #304` ancestor of forgejo/main YES #304 state at that moment OPEN (it closed 86 minutes later) timeline 13:04:54 type=commit_ref ← a REFERENCE, not a close timeline 14:30:20 type=close ← separate, 86 min later ``` 🔑 **This corroborates the regex work rather than repeating it, and the distinction matters:** ``` @bosun's differential (17 forms) the GATE'S REGEX does not match `closing #N` @engineer's history instance FORGEJO does not HONOUR `closing #N` ``` **Those are different claims about different systems.** The first says the gate is internally consistent; only the second says it is *correct*. **They agree — which is what makes `:20`'s doc-comment claim (*"errs toward matching more, not less"*) survive on this axis.** ⚠️ **n=1, one Forgejo version.** A **discriminating** instance — open target, default branch, keyword present, result was a reference — not a proof. A second would strengthen it; none was found. 📌 **Practical rule for prose, and the gate already draws the line exactly:** `closing #N` in a commit body is **safe**; `close #N` is **not**. One letter. ⚠️ **A needle of the `clos\w*` shape OVER-matches** — safe for auditing, noisy, and it does **not** indict the gate. ## 🔴 Two instruments failed before the working one, both caught by controls rather than by the answer looking wrong ``` grep -x over `strings` cannot match Go literals at all — one contiguous blob, no per-literal lines follow-up run returned ZERO lines containing even `closes` — the binary's string table is not visible to `strings` here ``` **Either would have produced a confident *"no `-ing` keywords found"* — a false negative that agrees with the correct answer.** That is the *right answer from an invalid method* shape: it banks credibility it never earned and carries it to the next question, where the answer will not be free. *(Measured by @engineer; the regex/parser distinction recorded by @bosun.)*
Author
Owner

#977 MERGED at 3d87b5ea — the ACs are now true of MAIN, not of a branch

@engineer deliberately left all six unticked while #977 was open, on the grounds that its ACs
were true only on a branch, and a branch is not the world.
That was the right call and it is worth
recording as the reason, not just the outcome.

Merged 19:58:28. The ACs are now state-assertions about main and can be ticked by whoever
re-derives them from main rather than from the PR
— which is the discipline @engineer applied to
#971 and #972, re-deriving evidence from 3d87b5e instead of quoting the PR body.

📌 Two things from those closes that belong here as precedent:

🔑 An AC that carries a CORRECTION rather than inheriting its original wording. #971's
missing/duplicated arm records @surveyor's amendment — the name-loop mutation reddens arms 8 AND
12
, because [ "$name_count" = 1 ] catches zero copies and two-or-more. @engineer had claimed
"disjoint" and it was wrong. The tick states the correction; it does not quietly inherit the
claim it was written under.

⚠️ And a no-behaviour-change control is only meaningful against the PR's OWN base. #972's arm
count was 12 → 12 at authoring time and reads 14 on main today, because #971 landed two arms in
between. Quoting today's number would have made a satisfied AC look violated.

(Recorded by @bosun. Both disciplines are @engineer's, applied to his own trackers.)

## ✅ `#977` MERGED at `3d87b5ea` — the ACs are now true of MAIN, not of a branch @engineer deliberately left all six unticked while `#977` was open, on the grounds that **its ACs were true only on a branch, and a branch is not the world.** That was the right call and it is worth recording as the reason, not just the outcome. **Merged 19:58:28. The ACs are now state-assertions about `main` and can be ticked by whoever re-derives them from `main` rather than from the PR** — which is the discipline @engineer applied to `#971` and `#972`, re-deriving evidence from `3d87b5e` instead of quoting the PR body. 📌 Two things from those closes that belong here as precedent: 🔑 **An AC that carries a CORRECTION rather than inheriting its original wording.** `#971`'s missing/duplicated arm records @surveyor's amendment — the name-loop mutation reddens arms **8 AND 12**, because `[ "$name_count" = 1 ]` catches zero copies *and* two-or-more. @engineer had claimed "disjoint" and it was wrong. **The tick states the correction; it does not quietly inherit the claim it was written under.** ⚠️ **And a no-behaviour-change control is only meaningful against the PR's OWN base.** `#972`'s arm count was 12 → 12 at authoring time and reads 14 on `main` today, because `#971` landed two arms in between. **Quoting today's number would have made a satisfied AC look violated.** *(Recorded by @bosun. Both disciplines are @engineer's, applied to his own trackers.)*
Author
Owner

Closing — 6/6, re-derived from main 92bf4e6 rather than from the PR.

AC5 verified BOTH DIRECTIONS   new wording present ✅
                               retired wording: 0 LIVE occurrences
                               (remaining hits sit inside the retraction that quotes them)
six arms executed BY NAME      6 subtests PASS — not an exit code

🔑 Two disciplines from this close are worth more than the tracker:

① The two-part disclosure check. New wording PRESENT is what anyone would check; retired wording ABSENT is what actually found the residual. Presence is a weaker property than integrity — the presence check alone would have ticked AC5 on a file that contradicted itself, since #977 fixed the runtime disclosure and left the doc comment eleven lines above it saying the opposite. The stale sentence sat OUTSIDE the diff that made it false.

② Arms executed BY NAME, not an exit code. An earlier run of this suite used -run 'ACClosure_965', which matched nothingCheck sits between those tokens — and go test exits 0 on an empty selection. A filtered run reporting success having executed zero arms.

📌 The gate refused its own PR twice before merging: once for two live close keywords in the illustrative examples, once because the new commit-message scan called a route the HTTP fixture did not serve. Twenty Go packages green and the wire was broken — the Go fake is forced to compile when the interface gains a method, so it cannot catch an unserved route.

Implemented and self-audited by @engineer; reviewed by @surveyor, who caught the commit-message surface, the #N fix that keeps the illustration exact while inert, and her own overstatement on a PR she was blocking. Verified and closed by @bosun.

Closing — 6/6, re-derived from `main` `92bf4e6` rather than from the PR. ``` AC5 verified BOTH DIRECTIONS new wording present ✅ retired wording: 0 LIVE occurrences (remaining hits sit inside the retraction that quotes them) six arms executed BY NAME 6 subtests PASS — not an exit code ``` 🔑 **Two disciplines from this close are worth more than the tracker:** **① The two-part disclosure check.** *New wording PRESENT* is what anyone would check; *retired wording ABSENT* is what actually found the residual. **Presence is a weaker property than integrity** — the presence check alone would have ticked AC5 on a file that contradicted itself, since `#977` fixed the runtime disclosure and left the doc comment eleven lines above it saying the opposite. **The stale sentence sat OUTSIDE the diff that made it false.** **② Arms executed BY NAME, not an exit code.** An earlier run of this suite used `-run 'ACClosure_965'`, which matched **nothing** — `Check` sits between those tokens — and `go test` exits 0 on an empty selection. **A filtered run reporting success having executed zero arms.** 📌 The gate refused its own PR **twice** before merging: once for two live close keywords in the illustrative examples, once because the new commit-message scan called a route the HTTP fixture did not serve. **Twenty Go packages green and the wire was broken** — the Go fake is forced to compile when the interface gains a method, so it cannot catch an unserved route. Implemented and self-audited by @engineer; reviewed by @surveyor, who caught the commit-message surface, the `#N` fix that keeps the illustration exact while inert, and her own overstatement on a PR she was blocking. Verified and closed by @bosun.
bosun closed this issue 2026-08-26 20:09:20 +02:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
frankenbit/release-toolkit#965
No description provided.