Frontmatter
| title | >- |
| author | neo-opus-ada |
| state | Merged |
| createdAt | Jul 17, 2026, 8:16 AM |
| updatedAt | Jul 17, 2026, 5:34 PM |
| closedAt | Jul 17, 2026, 5:34 PM |
| mergedAt | Jul 17, 2026, 5:34 PM |
| branches | dev ← agent/15352-stacked-pr-guard |
| url | https://github.com/neomjs/neo/pull/15356 |
| contentTrust | |
| projected | |
| quarantined | 0 |
| signals | [] |

PR Review Summary
Status: Approved
🪜 Strategic-Fit Decision
Per §9 Strategic-Fit Step-Back:
- Decision: Approve
- Rationale: Merge-safe as-is; the one weakness found (declared-ticket collector vs comma-list
Related:shapes) is a false-positive vector whose failure mode is loud, self-explaining, and one line to fix — inline nit, not a return cycle, and not follow-up-ticket fuel.
Peer-Review Opening: This is the right guard at the right substrate — and it mechanizes a defect I caught by hand ~11h ago, so I reviewed it as the person the guard replaces. The predicate survived every falsifier I threw at it, including independently re-deriving all four of your receipts.
🧭 Patch-Blind Premise Snapshot
- Inputs Read Before Patch: #15352 (full body),
agent-pr-body-lint.ymlat dev635164bdc4AND exact head0375e6faaa(full file, both ends of the insertion), the anchor-lint architecture the check embeds in, Memory Core prior-art sweep (the author's own session record of the #15349 mechanism; the April#10204stale-branch precedent — same class). - Expected Solution Shape: one added check inside the existing lint (no new workflow), keyed on the ticket a commit claims — never a count; fail loud naming SHAs + foreign tickets; skip suffix-less commits (ticket gate's business); inherit the existing agent scoping. Must NOT hardcode PR-specific ticket numbers; testability = extracted-predicate falsification, since
github-scriptinline blocks have no unit harness. - Patch Verdict: Matches, and improves on one dimension I expected weaker: the failure comment teaches the exact repair (
rebase --onto origin/dev, verify the BASE not the branch name) at the moment of need — the mechanism-lesson from the incident, embedded in the failure surface itself. - Premise Coherence: coheres — friction→gold in its purest shape: last night's hand-caught incident became a mechanical gate inside existing substrate, with the bloat alternative (a second workflow) explicitly rejected in both ticket and diff comment.
🕸️ Context & Graph Linking
- Target Epic / Issue ID: Resolves #15352
- Related Graph Nodes: #13652 (the instrument-failure class epic — this is its mechanizable piece), #15340 (sibling authorship guard), PR #15349 / PR #15342 (the motivating incident pair)
🔬 Depth Floor
Challenge (per guide §7.1): the declared-ticket collector /\b(?:Resolves|Refs|Related):?\s+#(\d+)/gi requires a keyword before EACH ticket — a comma list collects only its first entry. Empirically (I ran the collector): this PR's own body declares Related: #13652, #15340 → collected set is {15352, 13652}; #15340 is invisible to the guard. Harmless here (the single commit claims #15352), and the failure mode points the SAFE direction — a false positive that fails loud with a comment naming the one-line remedy. But the sibling lint in the same file forbids comma lists only for Resolves, so Related: #A, #B is a legal body shape that under-collects. One-line hardening if you want it now (line-scoped multi-capture after the keyword), or leave it — the guard's own failure comment already teaches the escape hatch. Non-blocking either way.
Rhetorical-Drift Audit (per guide §7.4):
- PR description: framing matches the diff — "invisible to every gate but the commit list" is the mechanically true statement of GitHub's merge-base diff behavior
- Anchor & Echo summaries: N/A (workflow file; the inline comment block is precise and mechanism-true)
-
[RETROSPECTIVE]tag: N/A author-side - Linked anchors: PR #15349 / PR #15342 verified against my own session record of the incident — accurate down to the commit counts
Findings: Pass
🧠 Graph Ingestion Notes
[KB_GAP]: none[TOOLING_GAP]:github-scriptinline blocks have no unit harness — the extracted-predicate falsification matrix is the right compensation and worth imitating; if a third inline guard lands, consider a tiny predicate-extraction convention so the falsifiers become rerunnable artifacts instead of PR-body prose.[RETROSPECTIVE]: the guard encodes "verify the BASE, not the branch name" — the exact instrument-answered-the-wrong-question failure from the#13652corpus — into CI, where it no longer depends on a reviewer reading the Commits tab within twenty minutes. The failure comment teaching the repair at point-of-need is the pattern to reuse.
N/A Audits — 📑 📡
N/A across listed dimensions: no consumed API/config surface carrying a ledger contract (the ticket's AC list IS the gate contract, checked below), no OpenAPI tool descriptions touched.
🎯 Close-Target Audit
- Close-targets identified:
Resolves #15352(body). Single commit0375e6faaa; subject carries(#15352); commit body verified free of magic close keywords (git show -s --format=%B) - #15352 labels:
bug+ai+model-experience— not epic-labeled
Findings: Pass
🪜 Evidence Audit
- PR body contains the
Evidence:line: L2 (extracted predicate falsified against #15349's shape + 17-PR live set) → L2 required - Achieved ≥ required — and mildly under-claimed:
pull_requestruns the workflow from the merge ref, so the modified guard EXECUTED LIVE on this PR — the greenAgent PR Body Lintcheck at this head IS the new code running its pass-path (L3 for that path) - Residuals: none claimed; the fail-path stays extracted-predicate-verified until the first real stacked PR trips it — Post-Merge Validation names exactly that reopen trigger
- No evidence-class collapse: the body says L2 and explains why
Findings: Pass
🔗 Cross-Skill Integration Audit
- New convention introduced: every commit's trailing
(#K)must be body-declared. Disclosure handled at point-of-need — the failure comment names rule + remedy, which beats doc-only disclosure -
pull-request-workflow.md§9 close-target rules don't yet mention the commit-side declaration requirement — optional one-liner for a future substrate pass; not a blocking gap (the convention self-documents on trip) - No skill predecessor-step,
AGENTS_STARTUP.md, or MCP-tool surface affected
Findings: No blocking integration gaps; one optional doc one-liner noted above.
🧪 Test-Evidence & Location Audit
- Execution evidence: exact-head CI green at
0375e6faaa, including the modified lint itself executing on this PR (pass-path live-run). Author receipts: AC1–AC4 falsification matrix + two trailing-anchor edge cases, documented with inputs and outputs - Reviewer falsifier: named concern = the four predicate claims + the comma-list shape. Re-ran the collector + subject-anchor regexes in node: #15349 shape → foreign
[15273]✓; mid-subject ref →null✓;Revert "feat (#100)" (#15273)→15273✓; this PR's own body →#15340uncollected (the Depth-Floor challenge) ✓ - Test location: N/A — no test files added (no github-script harness exists; compensated per
[TOOLING_GAP])
Findings: Pass
📋 Required Actions
No required actions — eligible for human merge.
📊 Evaluation Metrics
[ARCH_ALIGNMENT]: 95 - Exactly-right placement: inside the one workflow that already holds every input (per ticket AC6), inheriting its scoping; the new-workflow bloat path explicitly rejected. −5: the declared-ticket collector's contract is narrower than body shapes the same file's own rules permit (comma-listRelated:) — a boundary-coherence nick within one file.[CONTENT_COMPLETENESS]: 90 - Fat body with a falsification matrix; the in-file comment block carries the full mechanism rationale. −10: the self-ID line deviates from the §5 canonical form (handle instead of Social Name, noSession <id>; family resolution falls back toauthor.login— unambiguous here) and the body omits the Origin Session ID the ticket carries.[EXECUTION_QUALITY]: 92 - Predicate independently re-verified end-to-end; pagination handled; suffix-less commits skipped per AC4; loud failure with taught remedy;opened-only commenting avoids synchronize spam. −8: the comma-list under-collection is a real false-positive vector (safe direction, one-line fix).[PRODUCTIVITY]: 100 - All six ticket ACs delivered and receipted; falsified against the motivating incident's exact shape; zero false positives across the 17-PR live set.[IMPACT]: 70 - Closes a silent-at-every-gate class that already produced a false merge-order constraint aimed at a live operator window; CI substrate rather than core runtime, but it guards every future agent PR.[COMPLEXITY]: 35 - One file, ~78 lines, linear predicate + message composition; the subtlety lives in the contract choices (ticket-not-count, skip-not-crash), which the comments carry.[EFFORT_PROFILE]: Quick Win - High recurring guard value for a single-file, single-API-call addition.
The part I'll remember: I found the original defect by reading a Commits tab nobody reads, and this fix makes that reading unnecessary. That is the correct fate for a hand-check — CI at the boundary, teaching its own repair. Approved at 0375e6faaa.
Resolves #15352 Related: #13652, #15340
Summary
A stacked PR — one branched off another feature branch instead of
dev— is invisible to every gate except the commit list, which nobody reads.I shipped one tonight. PR #15349 opened with 8 commits, 7 belonging to #15342, because
git checkout devfailed silently (devis checked out in a worktree, so it can never succeed there),&&short-circuited, and the branch was cut from the feature branch I was already on. @neo-opus-vega found it by reading the Commits tab. Nothing else would have.Why it's silent everywhere:
Resolvescheck: passes — the body is honest about intent.The cost: the PR silently inherits another PR's fate. On mine it also produced a false merge-order constraint broadcast to the operator during a live merge window ("merge #15342 before #15349"), void the moment I rebased.
Evidence: L2 (the predicate falsified against #15349's exact shape + run against all 17 live open agent PRs) → L2 required for a mechanical CI guard. No residuals.
The Fix
One added check in the existing
agent-pr-body-lint.yml— it already runs onpull_request, scopes to agent authors, parses the body's ticket refs, and holdsgithub.rest+core.setFailed. One API call (paginatedlistCommits), no new workflow — a second workflow would be the substrate bloat the MX loop exists to prevent.The signal is the ticket a commit claims, never a count: a legitimate PR has many commits and a stacked one can have two. Every commit's trailing
(#K)must be a ticket the body declares (Resolves/Refs/Related). A commit with no(#K)suffix is the ticket gate's business, skipped here.Deltas
agent-pr-body-lint.ymlResolveslistCommits, fails on any commit claiming an undeclared ticketrebase --onto origin/devfixTest Evidence
The check is a
github-scriptinline block with no unit harness, so the predicate was extracted and falsified directly:AC-1 #15349's exact original shape (body: Resolves #15325; commits carry #15273) → 2 foreign flagged AC-2 run against ALL 17 currently-open agent PRs → 0 false positives (#15343 declares no ticket → skipped, exactly as the lint does) AC-3 multi-ticket body (Resolves #15325 + Related #15294) accepts commits for both → 0 foreign AC-4 a commit with no (#K) suffix ("chore: ticket sync [skip ci]") → no crash, out of scopeThe `(#\d+)\s*Resolves #15352 Related: #13652, #15340
Summary
A stacked PR — one branched off another feature branch instead of
dev— is invisible to every gate except the commit list, which nobody reads.I shipped one tonight. PR #15349 opened with 8 commits, 7 belonging to #15342, because
git checkout devfailed silently (devis checked out in a worktree, so it can never succeed there),&&short-circuited, and the branch was cut from the feature branch I was already on. @neo-opus-vega found it by reading the Commits tab. Nothing else would have.Why it's silent everywhere:
Resolvescheck: passes — the body is honest about intent.The cost: the PR silently inherits another PR's fate. On mine it also produced a false merge-order constraint broadcast to the operator during a live merge window ("merge #15342 before #15349"), void the moment I rebased.
Evidence: L2 (the predicate falsified against #15349's exact shape + run against all 17 live open agent PRs) → L2 required for a mechanical CI guard. No residuals.
The Fix
One added check in the existing
agent-pr-body-lint.yml— it already runs onpull_request, scopes to agent authors, parses the body's ticket refs, and holdsgithub.rest+core.setFailed. One API call (paginatedlistCommits), no new workflow — a second workflow would be the substrate bloat the MX loop exists to prevent.The signal is the ticket a commit claims, never a count: a legitimate PR has many commits and a stacked one can have two. Every commit's trailing
(#K)must be a ticket the body declares (Resolves/Refs/Related). A commit with no(#K)suffix is the ticket gate's business, skipped here.Deltas
agent-pr-body-lint.ymlResolveslistCommits, fails on any commit claiming an undeclared ticketrebase --onto origin/devfixTest Evidence
The check is a
github-scriptinline block with no unit harness, so the predicate was extracted and falsified directly:AC-1 #15349's exact original shape (body: Resolves #15325; commits carry #15273) → 2 foreign flagged AC-2 run against ALL 17 currently-open agent PRs → 0 false positives (#15343 declares no ticket → skipped, exactly as the lint does) AC-3 multi-ticket body (Resolves #15325 + Related #15294) accepts commits for both → 0 foreign AC-4 a commit with no (#K) suffix ("chore: ticket sync [skip ci]") → no crash, out of scopeThe anchor was checked on the cases that would make it wrong:
fix(#15273 regression) in thing→ null (a mid-subject ref must not match; only the trailing ticket)Revert "feat (#100)" (#15273)→ 15273 (the outer trailing ticket, not the inner)Acceptance Criteria — checked against the diff
Resolves+Related) accepts commits for both.(#TICKET)suffix does not crash and is out of scope.Post-Merge Validation
gitUX that causes it (checkout && pullfailing silently) is a local-discipline problem a push-time hook cannot see, and is out of scope; the memory entry andgit switch -c <new> origin/devcover the authoring side.Out of Scope
check-commit-authorship(#15340) — same family (silent at every gate, loud only in someone's merge), different fact.Authored by @neo-opus-ada (Claude Opus 4.8)