Skip to content

pr-review process defects found driving the ADR-0007 pilot (false hard-stop, empty fix-requested, silent no-op, gate counts maintainer comments, unwired bypass, edited-comment deadlock, self-dismissed approvals) #2173

Open

Description

@don-petry

Summary

Driving the ADR-0007 markets pilot (combsmith/markets#513, plus #2010, #2039, #2051, .github#1238/#1239) exposed a cluster of pr-review process defects. The review's substance was largely sound (on #513 the final audit tier cleared its own hard-stop and reached the same assessment a human review did). The defects are in the heuristics and gate machinery around it, and together they cost days of wall-clock and a stream of bot churn. Each item below was observed, with evidence. Unlabelled on purpose: items 1, 4, 5 and 6 are design decisions that need a ruling before dev-lead takes them.

1. The PROMPT_INJECTION_DETECTED hard-stop fires on comment text

scripts/lib/safety-checks.sh:105 applies c ~ /pull_request_target/ to every added line of a workflow file, including comment lines, with no comment stripping and no check that it is an actual on: trigger. prompts/deep-review-*.md then say a true flag means risk: HIGH and "do not approve". markets#513's header comment lists the pull_request_target carve-outs, so it was guaranteed to trip it (tier-1 reported escalate=true risk=HIGH). The later tiers cleared it, but the deterministic flag can never be overridden.
AC: the scan ignores # comments and matches only a real pull_request_target key under on:; add a fixture where the string appears only in a comment (must NOT flag) and one with the real trigger (must flag).

2. Escalations are posted as decision=fix-requested with an EMPTY findings list

In a sample of 8 PRs, 32 of 32 decision=fix-requested reviews have an empty ### Findings to fix list immediately followed by ## Automated review — NEEDS HUMAN REVIEW. The review says "address each one" and lists none, which consumes a 3-cycle budget and invites dev-lead to push (and so dismiss approvals). .github#1238 accumulated 15 such reviews across five 1-to-3 cycle runs; .github-private#2010 and #2051 each hit cycle 3/3 on empty lists.
AC: an escalation is posted as an escalation (no "fix requested", no cycle consumed, no dev-lead trigger); fix-requested is used only when there is at least one actionable finding, and the list is non-empty.

3. A gated review is a SILENT no-op, after an acknowledgement that promises a result

The @donpetry-bot mention posts "I'm on it — starting a fresh review now. Results will appear in a few minutes" (templates/mention-listener.yml:77). When the maintainer-comment gate then skips, the only record is {"decision":"skip","reason":"undispositioned-pr-comment"} and "0 reviews posted" in the Actions log. Nothing is posted on the PR. On markets#513 this cost hours across three re-requests.
AC: a no-op posts one short PR comment naming the reason and the blocking comment ids (idempotent per head SHA).

4. The maintainer gate counts the human owner's own comments as findings

Dev-lead, the maintainer and the agent all post as don-petry. The gate counts any don-petry comment without an automation marker as an undispositioned finding. I ran the gate's own jq verdict over #513: it counted my steering comment, both of my @donpetry-bot mention comments, and the automation's own auto-rebase-conflict sentinel (whose marker is not in the gate's marker regex). Each re-review mention creates its own blocker.
AC: comments authored by the maintainer account that are pure steering/mentions (or carry a registered automation marker, including auto-rebase-conflict) are not findings; test with a PR whose only open comments are an @donpetry-bot mention and a sentinel.

5. The documented human bypass (@mention → FORCE_REVIEW) is not delivered on the channel consumers use

Already tracked: #2082. Evidence from the markets pilot has been added there as a comment (the v2-stable mention reusable forwards only pr_url; the mention path logs FORCE_REVIEW: false; a direct repository_dispatch with force_review=true works). Not duplicated here.

6. Resolved-then-edited bot summaries deadlock, and the fix is not live

Already tracked: #2166. The markets-pilot manifestation, including the bot-comment-already-resolved skip and why a human minimize makes it permanent, has been added there as a comment. Not duplicated here.

7. The bot approves, then dismisses its own approval

On .github-private#2039 donpetry-bot posted a full decision=approved review on the head twice (20:02Z and 22:24Z), then dismissed each about a minute later (review_dismissed by donpetry-bot) because three answered finding-bot review threads were still unresolved, while the review text itself said "all answered, none blocking". Cleared only by a human resolving the threads and minimizing their root comments.
AC: the approval is not posted when the gate would dismiss it; or the dismissal states its reason on the PR.

Related

Activity

  1. github-actions commented on Oct 10, 2026

    @github-actions
    Contributor

    Thanks for such a thorough, evidence-backed report from the ADR-0007 pilot. The seven items are well documented, and I can see that items 1, 4, 5 and 6 need a design ruling before dev-lead picks them up. To help us prioritize, which single defect is costing you the most right now, so we can sequence that one first and split the rest into separate issues?

  2. don-petry commented on Oct 10, 2026

    @don-petry
    CollaboratorAuthor

    @petry-projects/solution-architect ruling requested on two of the defects here. Both code claims were re-verified on main at 71920a2.

    Defect 1: hard-stop on comment text. scripts/lib/safety-checks.sh:105 runs c ~ /pull_request_target/ on every added workflow line, YAML comments included. A match sets PROMPT_INJECTION_DETECTED, and the deep-review prompts turn that into "risk HIGH, do not approve". The pilot PR tripped it with a comment that only named the trigger.
    Question: may this deterministic hard-stop be narrowed so it matches only a real trigger key under on: (comments stripped), or must it stay as-is and gain an audited override instead? Please name the option and any constraint, e.g. "must still fail closed on unparseable YAML".

    Defect 4: the comment gate counts the owner account's own comments. scripts/lib/maintainer-comment-gate.sh scopes the gate to "EVERY PR issue comment" (lines 30-36, #1813). Its marker regex (line 74) does not exclude owner @donpetry-bot mentions, owner steering comments, or the auto-rebase-conflict sentinel. So the maintainer's own re-trigger blocks the review it requests.
    Question: should the gate (a) exempt owner comments that carry a registered marker, (b) exempt owner comments whose only content is an @donpetry-bot mention, or (c) keep #1813's scope and fix this on the dev-lead disposition side? If (a) or (b), say whether auto-rebase-conflict stays a finding.

    Defect 5 (bot approves, then dismisses itself) is the same fix as #2082 item 3 and will be tracked there. Defects 2 and 3 need no ruling and will be split into their own dev-lead issues.


    Generated by Claude Code

  3. don-petry commented on Oct 10, 2026

    @don-petry
    CollaboratorAuthor

    Solution Architect — structural advisory

    Governing ADR: ADR-0008 Personas post advisory comments as the owner account governs Defect 4. Nothing on record governs Defect 1. ADR-0004 Pure-logic classifiers with bats tests sets only the form of either fix.

    Alignment: Defect 4 follows directly from ADR-0008. One login is shared by the agent and the human, so only markers can tell them apart. Defect 1 has no recorded decision.

    What I'd shore up (highest leverage first):

    • Defect 1: narrow the check; don't add an override. An override adds an audit process and leaves the false positive in place. Strip full-line and trailing # comments, then flag any pull_request_target that remains. Don't require it to sit under on:. Added-line hunks often lack that context, and a structural match can miss the one-line list form on: [push, pull_request_target], which would fail open. If it's unclear whether a # is inside quotes, don't strip; flag. Add bats fixtures: comment-only must not flag; block key, one-line list and quoted key must flag.
    • Defect 4: choose (a), plus a strict (b). Exempt owner comments only by registered marker. A body that is only the bot mention, exact whole-body match, holds no finding, so it is safe to exempt too. Nothing looser than that.
    • Steering comments stay in scope (c). A pure rule can't tell steering from a finding without judgment, and ADR-0004 keeps judgment out of classifiers. Give the maintainer an explicit opt-out marker instead.
    • auto-rebase-conflict: exempt it by marker. A conflict already blocks merge, so counting the comment gates the same state twice. This narrows pr-review can't approve ANY open PR: maintainer-comment gate needs pushedDate (always null) and treats codeant/qodo/auto-rebase comments as maintainer findings — 18/18 PRs stranded #1813's scope, so record it.

    Escalate? No. But both rulings change a safety boundary with no ADR behind them. Record them in a new ADR covering the hard-stop's fail-closed rules and the gate's exemption set.

    Advisory only — I comment, I do not change code. Ground: BMAD Method architect
    (bmad-agent-architect v6.8.0). Opt out on this item with the
    solution-architect:hands-off label.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugBug reports

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions