Skip to content

fix(guard): R-2 close-issue guard now fails closed on the #3400 landmine - #3497

Merged
alfredodeza merged 1 commit into
mainfrom
fix/no-close-landmine-guard
Sep 18, 2026
Merged

alfredodeza merged 1 commit into
mainfrom
fix/no-close-landmine-guard

Conversation

@alfredodeza

Copy link
Copy Markdown
Contributor

Fixes #3400.

What was happening

GitHub's closing-keyword parser reads close: immediately inside no-close:
— the hyphen counts as a word boundary to it, so the negating prefix is
invisible. check_pr_closes_issue.sh (the R-2 guard, wired into ci.yml)
had the identical blind spot in its own CLOSE_RE: a body line starting
with the negating marker and naming an issue still parsed as a real closing
reference, so the guard printed PASS instead of catching the danger.

Measured cost so far: an issue tracked in this repo's Qwen3.5 CPU work was
closed twice by this mechanism before #3400 was filed. #3400's own remedy
list asked for a guard change and nothing else landed on it. Today the same
class of line, on PR #3484, closed both the GPU sub-ticket and its parent
epic a third time — despite the body explicitly carrying the negating marker
for each.

What changed

scripts/check_pr_closes_issue.sh:

  • New NOCLOSE_LANDMINE_RE: any hyphen-prefixed closing keyword (fix,
    close, resolve, and their variants) immediately followed by a colon and
    an issue reference. Checked first, unconditionally — a landmine anywhere in
    the body fails the whole check, even alongside an otherwise-correct closing
    line elsewhere.
  • The sanctioned "this stays open, here's why" marker is now keep-open:
    instead of the negating marker — a string neither GitHub's parser nor this
    guard's own CLOSE_RE reads as a closing keyword.
  • Self-test: 9 → 19 cases. Three new RED rows prove the landmine is caught,
    including a mixed body that also correctly closes a different issue (the
    landmine still fails the whole body).

Also fixed by hand (not in this diff)

Two open PRs (#3488, #3494) still carried the negating-marker line naming the
epic; edited their bodies in place to the new marker before they could merge
and reopen the same hole. The epic and the GPU sub-ticket, closed again
today by this mechanism, are reopened.

keep-open: #3477, #3090 — this PR lands the guard only; GPU device support
itself is unstarted, separate work, and stays open on milestone 0.68.2.

🤖 Generated with Claude Code

GitHub's closing-keyword parser reads "close: #N" inside "no-close: #N" --
the hyphen is a word boundary to it, the negating prefix is invisible. This
guard's own CLOSE_RE had the identical blind spot: `no-close: #3090` /
`no-close: #3477` both parsed as "PASS: every cited issue has a closing
keyword", agreeing with GitHub's mistake instead of catching it.

Measured cost: #3091 closed twice by this mechanism (2026-09-15/16, tracked
as #3400 when found). #3400's own "Required" list asked for exactly this
guard change (item 3) and was never implemented -- the pattern hit #3090 and
#3477 a third time today via PR #3484, which carried two explicit
"no-close:" lines naming them and still closed both.

check_pr_closes_issue.sh now fails any body containing a hyphen-prefixed
closing keyword next to a "#N" (no-close:, wont-fix:, skip-resolve:, ...),
unconditionally, before any other check. The sanctioned marker becomes
"keep-open: #N <reason>" -- a string neither GitHub's parser nor this
guard's CLOSE_RE reads as a closing keyword. Self-test grows from 9 to 19
cases: 3 new RED rows prove the landmine is caught, including one where a
body also correctly closes a different issue (the landmine still fails the
whole body, since a false PASS anywhere is the failure mode being fixed).

Also fixed the two live landmines this uncovered (#3488, #3494 PR bodies,
both still open, edited in place to keep-open: before merge) and reopened
#3090/#3477, closed a third time by PR #3484 via this exact mechanism.

Closes #3400.
keep-open: #3477, #3090 -- this lands the guard; GPU support itself is
unstarted and stays open on 0.68.2.
@github-actions

Copy link
Copy Markdown

§13.11 rung 1 — quorum shadow verdict

S13-SHADOW pr=3497 head=58f6185291351401ebd2ba126b71fef23770596c verdict=REFUSE class=Q1 arm_rc=1

Shadow mode: this records a verdict and merges nothing. A refusal
to arm is not a block (§13 adds zero rows to §7) — the pull request is
exactly as green as it was.

@alfredodeza
alfredodeza added this pull request to the merge queue Sep 18, 2026
Merged via the queue into main with commit e9962a9 Sep 18, 2026
22 of 23 checks passed
@alfredodeza
alfredodeza deleted the fix/no-close-landmine-guard branch September 18, 2026 19:37
@alfredodeza alfredodeza mentioned this pull request Sep 18, 2026
@alfredodeza

Copy link
Copy Markdown
Contributor Author

pr-review v2.1.0 | verdict=FINDINGS | consultations: pmat=consulted cuda=not-triggered crux=not-triggered mutation=consulted agy=consulted | findings=4 (cited=0 measured=4 asserted=0) | index=58f6185 ancestor=true | agy=gemini-3.1-pro-high advisory | divergence: agreed=1 agy-only=0 primary-only=2 contradicted=0 | receipt=evidence/pr-review/3497/58f6185291351401ebd2ba126b71fef23770596c/receipt.intoto.jsonl (L1-self, unsigned pending CI)

The signature proves this receipt was produced in the CI environment. It does not prove the review was honest or complete — attestation_level is L1-self.
Verify it yourself: bash scripts/check_pr_review_receipt.sh evidence/pr-review/3497/58f6185291351401ebd2ba126b71fef23770596c

Signature note: this receipt is committed unsigned by design (PR-REVIEW-SKILL-002 v2 §4.3, §12): the reviewing session has no access to the escrowed PR_REVIEW_SIGNING_KEY_B64 secret, and this repo's pr-review-sign CI job attaches the .minisig and pushes it back to this branch automatically. A local run of the guard against the committed (unsigned) receipt correctly REJECTs on [B1] receipt is unsigned, and that is the only rejection reason — every other check (schema, findings_ref.sha256, actor separation, base_sha = git merge-base origin/main HEAD, per-consultation completeness) was independently confirmed to ACCEPT by re-validating a copy of this exact receipt signed with a disposable throwaway keypair (PR_REVIEW_PUBKEY override — a guard-supported extension point, not a bypass).

Findings (4 total, this PR's own diff — scripts/check_pr_closes_issue.sh only):

  1. [measured, warning, advisory] landmine-colon-optional-gap — NOCLOSE_LANDMINE_RE (line 64) requires a literal :, but CLOSE_RE (line 56) and GitHub's own parser (per this PR's own account) treat the colon as optional. A body reading no-fixes #123 tracked elsewhere (no colon) is not caught by the new landmine check and is then read by CLOSE_RE as a valid closing reference — the guard prints PASS: every cited issue has a closing keyword. for exactly the class of bug GitHub parses 'no-close: #N' as 'close: #N' — #3091 was closed twice by PRs whose bodies said no-close (D0 of the pmat 3.41.0 run); #3351 carries the same line for #3347 #3400 is about, just without the colon. Reproduced live (bash scripts/check_pr_closes_issue.sh --body <(printf 'no-fixes #123 tracked elsewhere') → exit 0, PASS). Independently found by the antigravity (agy, gemini-3.1-pro-high) consultation with a different literal (no-close #123) — divergence: agreed.
  2. [measured, warning, advisory] doc-drift-apr-release-001-no-close — docs/specifications/APR-RELEASE-001-train-and-build-kaizen.md:566, the spec this script's own header cites as its authority ("APR-RELEASE-001 section 6, predicate R-2"), still documents no-close: as the sanctioned marker for Refs #N. This diff renames the sanctioned marker to keep-open: in the guard and its self-test but does not update this spec line — a contributor following the spec as written will be hard-failed by the very guard it describes.
  3. [measured, note, advisory] self-test-vacuity-floor-stale — the self-test's vacuity floor (cases -lt 9) was written for the pre-PR 9-row table and was not raised after this diff grew it to 19 rows (confirmed: self-test OK: 19 case(s).). Minor; the file's own comments already acknowledge the floor doesn't catch row deletion.
  4. [measured, warning, advisory] missing-optional-colon-in-landmine-re (antigravity/agy) — the same defect as Feature Request: Decision Tree & Random Forest for Classification Tasks #1, found independently by the cross-vendor arm.

None of these fire any §7 mechanical blocking class (B1–B6): mutation kill rate on the new guard logic is 5/5 = 100% (no dedicated mutate_*.sh harness exists for this script, so 5 mutants were hand-built per the skill's fallback — invert the landmine condition, delete the landmine block, break the regex, revert the keep-open:/no-close: rename, and flip the landmine branch's return 1→return 0; all 5 turned the script's own 19-row --self-test RED). No comparative claims in the diff. reviewer_actor ≠ author_actor. Index is an ancestor of HEAD. CUDA and CRUX triggers were evaluated via the guard's own --match-path/--match-message/--match-crux-surface predicates (not by eye) and did not fire. Verdict is FINDINGS, not BLOCK — these are real, reproduced completeness gaps worth a follow-up, not reasons to hold the merge.

🤖 Generated with Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant