Repository navigation
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
Description
Summary
pr-review can no longer auto-approve any open PR in this repo. The maintainer issue-comment gate (scripts/lib/maintainer-comment-gate.sh, #1290) fails closed on every PR:
- It depends on
pushedDate, which GitHub now always returns asnull.maintainer_gate_head_committer_date()returns empty without it, andcheck_maintainer_commentsfails closed on an empty push time whenever any unanswered comment exists. So a push can never clear the gate, even though the skip verdict claims one would. - It judges a comment "addressed" by comparing timestamps, not by whether anyone answered it. A push that happens after a comment is only a proxy. It says nothing about whether the comment's finding was read, checked, or acted on. And every open PR today carries comments that nobody has answered:
codeant-aireview-status comments,qodo-code-review's trial-ended notice, andauto-rebase's merge-conflict reports.
The fix is NOT to exclude those authors from the gate. Excluding comments because a bot wrote them makes ignoring them the official process. Every comment must be researched, analyzed, answered with evidence, and resolved. The gate withholds approval until that has happened, for bots and humans alike.
This also regresses a known failure class: #577 removed the pushedDate dependency from advisory-review-gate.sh in June, and #1290 reintroduced it here.
Revision — 2026-09-13 (supersedes the original "exclude automation authors" approach)
The first version of this issue said to add codeant-ai / qodo-code-review to MAINTAINER_GATE_EXCLUDED_BOTS and add auto-rebase-conflict to _MAINTAINER_GATE_AGENT_MARKERS, so the gate would ignore their comments. The maintainer rejected that direction: comments must not be ignored; their findings must be considered, answered, and resolved.
The dev-lead implementation plan posted on this issue at 2026-09-13T13:43Z is superseded and must not be implemented. That plan's excluded-bots registry derivation, the auto-rebase-conflict agent-marker addition, and tests asserting "a codeant-ai / qodo comment → gate passes" are the rejected design. The run executing it was cancelled.
Evidence (executed 2026-09-13 ~13:20Z)
pushedDate is null on PR heads, via the gate's own GraphQL query:
#1789 head=13482110 pushedDate=NULL committer.date=2026-09-13T07:08:46-05:00
#1772 head=f5cd9dc1 pushedDate=NULL committer.date=2026-09-12T23:08:11-05:00
The real gate, run against PR #1789's live snapshot (sourced from origin/main):
helper head date=[]
[maintainer-gate] WARNING: maintainer issue comment present but head-commit push time is unknown — failing closed (blocking approval)
gate rc=1
What it counted as maintainer findings on #1789, all automation:
04:23:06Z qodo-code-review <!-- qodo:billing-blocked --> Qodo reviews are paused because your trial has ended…
04:23:06Z codeant-ai ## 🤖 CodeAnt AI — Review Status …
04:23:07Z codeant-ai Thanks for using CodeAnt! …
04:28:56Z don-petry <!-- auto-rebase-conflict:56d8ccc7… --> Auto-rebase failed — merge conflict …
05:06:24Z don-petry <!-- auto-rebase-conflict:675cb4aa… --> Auto-rebase failed — merge conflict …
pr-review's verdict on a targeted run (34759124454):
{"pr":".../pull/1789","sha":"13482110…","decision":"skip","reason":"unaddressed-maintainer-comment",
"would_change":"the maintainer comment is addressed by a new push, or an @mention (FORCE_REVIEW) overrides the gate"}
Scope: 18 of 18 open PRs trip the gate (same filter, same exclusion list and marker regex):
PR#1804 PR#1802 PR#1801 PR#1800 PR#1799 PR#1798 PR#1796 PR#1794 PR#1793
PR#1792 PR#1790 PR#1789 PR#1788 PR#1783 PR#1782 PR#1770 PR#1742 PR#1730
— every one carries codeant-ai ×2–3 and qodo-code-review ×1; most also carry auto-rebase-conflict comments
Every one is BLOCKED / REVIEW_REQUIRED. The last PR to merge on an automated approval before this was #1772, at 2026-09-13T04:23:27Z, just before the first Qodo trial-ended notice (04:23:06Z on #1789) appeared.
The fix PR for this issue will hit the same gate. It needs a human approval or the @donpetry-bot FORCE_REVIEW break-glass.
Design
Addressed means a verified disposition on the comment, not a timestamp.
- Scope: every PR issue comment, from human maintainers and bots alike (
codeant-ai,qodo-code-review,auto-rebase, and so on). The only exclusion is our own automation's disposition and acknowledgement replies, so the agent never answers itself. No author is exempt for being a bot. - For each comment without a disposition, dev-lead:
- Researches it: reads it fully, follows its links, referenced files, check runs and CI logs, and reproduces its claims against the current PR head.
- Analyzes it: is there an actionable finding? Is the claim correct? Is it in scope for this PR? If it's informational, does it have an operational consequence?
- Responds once, with evidence and one disposition:
fixed: the finding was valid and a commit on the PR fixes it. Cite the commit.invalid: the claim is wrong. Show the evidence, the way false-positive review threads are answered today.out-of-scope: valid, but tracked elsewhere. Cite the issue.answered: the comment asked a question, and this reply answers it.informational: no code finding. State the operational consequence, and route it if there is one (AC6).
- Leaves resolution to the harness. The model never resolves or minimizes a comment itself, matching harness-only resolution for review threads (dev-lead resolves review threads without addressing them — unfixed findings merge through the thread-resolution gate #1621 S1).
- How a comment gets resolved: GitHub has no "resolved" state for issue comments. So resolving means the harness verifies the disposition and then minimizes the original comment with classifier
RESOLVED(GraphQLminimizeComment). Verification:fixed: the cited commit is on the PR head and touches what the comment is about. Reuse the [#1621 S2] Verify the addressed-marker against the pushed diff + honour maintainer dispositions #1692 addressed-claim verifier.out-of-scope: the referenced issue exists.invalid/answered/informational: a reply with non-empty evidence exists.
- Human maintainer comments: always answered. Auto-resolved only on a verified
fixed. Any other disposition on a human's comment stays open until that human resolves or accepts it, so an agent can never dismiss a person's finding by arguing with it (consistent with [Phase 4] Close the maintainer review-gate gap — dev-lead PRs cannot be blocked by the owner (shared identity + agent-resolved threads) #1415 and Harness cannot resolve a bot review thread whose correct response is 'no change needed' — false positives block merge permanently #1743).
Answer to the triage question (committer.date vs a tamper-resistant server-side time): once "addressed" means a verified disposition, head push time is no longer what decides whether a comment is answered. That removes the tamper-resistance concern at the root, because a spoofed commit date cannot manufacture a verified disposition. So: remove the pushedDate dependency entirely. Where a head time is still needed (for example, noticing new commits after a disposition), use the head commit's committer.date, matching advisory-review-gate.sh.
Acceptance criteria
- No
pushedDatedependency. Nothing in the gate or its helpers readspushedDate. Any head time still needed comes fromcommitter.date, as in advisory-bot gate strands PRs forever: pushedDate=null disables both absent-bot timeout fallbacks #577. - The gate withholds approval while any non-agent PR issue comment lacks a verified disposition. This applies to bots and humans alike. Do not add
codeant-aiorqodo-code-reviewto an excluded-authors list, and do not addauto-rebase-conflictto the ignorable agent markers. A conflict report is a finding, and it isfixedby the rebase commit. The only exclusion is our own disposition and ack replies. - dev-lead handles PR issue comments, not only review threads. Today fix-reviews walks
reviewThreadsalone, which is the gap PR comments from maintainers are mechanically inert — they neither block merge nor reach dev-lead #1290 describes. For each undispositioned comment it researches, analyzes, and posts exactly one evidence-backed reply carrying<!-- dev-lead:comment-disposition id=<comment_id> disposition=<fixed|invalid|out-of-scope|answered|informational> [sha=<sha>] [ref=#<n>] -->. - The harness resolves after verifying: it checks the disposition as described in Design, then minimizes the original comment as
RESOLVED. It never resolves an unverified disposition, and the model never minimizes anything itself. - Human maintainer comments are always answered, auto-resolved only on a verified
fixed, and otherwise left open for the human. The gate keeps blocking until they are resolved. - Informational comments with consequences are routed, not merely acknowledged. From the evidence above: Qodo's
qodo:billing-blockednotice means Qodo reviews have stopped. The response states that, checks whether the advisory gate's required-bot set still expects Qodo, and raises the billing decision for a human, with one tracking issue per repo linked from each PR's reply rather than a new issue per PR. - Loop safety (feat: implement issue #846 — [Phase 2] Cache the LSP index and enforce the 30s cold-start SLA with auto-skip #860): our disposition and ack replies never count as comments needing a response. A bot's reply to our disposition is answered only if it raises a new finding, so boilerplate never starts a reply chain. One disposition per comment id (idempotency key = comment id). Everything stays within the existing per-PR automation budget (
MAX_PR_AUTOMATION_CYCLES). - An undeterminable gate state is diagnosable: a distinct verdict reason, never disguised as
unaddressed-maintainer-comment, so this outage cannot recur looking like a legitimate hold. - Regression bats:
- (a)
pushedDatenull, every comment dispositioned and verified → passes. - (b) An undispositioned
codeant-ai,qodo-code-review, orauto-rebase-conflictcomment → blocks. - (c) A
fixeddisposition citing a sha that is not on the head → still blocks. - (d) A human comment with a non-
fixeddisposition → blocks until the human resolves it. - (e) Our own disposition reply is never treated as needing a response.
- (f) A bot's boilerplate reply to our disposition does not trigger another reply.
- (a)
- After merge, the stranded PRs are processed: dev-lead dispositions the existing
codeant-ai,qodoandauto-rebasecomments on the open PRs, then pr-review re-reviews them (see pr-review sweep reports success while silently omitting an eligible open PR from the candidate pool #1744 for the sweep caveat).
Notes for implementation
- This fix's own PR will be blocked by the currently merged, broken gate, so it needs a human approval or the
@donpetry-botFORCE_REVIEW break-glass. That PR's own bot comments must also be dispositioned under the new rule. - If this is too large for one PR, split it into (i) the gate plus harness verification and resolution (AC1, 2, 4, 5, 8, 9) and (ii) the dev-lead comment-disposition pass (AC3, 6, 7, 10). Land (ii) first, or together with (i). Landing the gate alone would block every PR until comments are dispositioned.
Architect review — folded in 2026-09-13 (these are acceptance criteria)
The Solution Architect's advisory on this issue found the design mostly aligned with ADR-0004 (the harness verifies, then does the write) and ADR-0008 (personas post as the owner account). It escalated one gap and flagged that no ADR records this contract. Its escalation premise was verified against main:
- dev-lead's model posts its own trust markers today.
prompts/dev-lead/fix-reviews.md(lines 63–73) instructs the model to post thread replies ending in<!-- dev-lead:addressed -->and<!-- dev-lead:claim {…} -->viagh api graphql. The model step runs withGH_TOKEN = GH_PAT_DON_PETRY, the owner account's write token (dev-lead-reusable.yml:141). The harness only verifies claims against the pushed diff ([#1621 S2] Verify the addressed-marker against the pushed diff + honour maintainer dispositions #1692) and resolves. That is the reverse of the persona runner's read/write split, which ADR-0008 relies on: there the agent cannot post at all, and the workflow enforces the marker and posts. - The harness issues a blanket bot-resolve command.
scripts/dev-lead-fix-reviews.sh:324posts@coderabbitai resolve <!-- dev-lead -->, which makes CodeRabbit resolve all of its threads with no per-finding verification. This was observed closing a half-fixed finding on PR feat: implement issue #1727 — [Phase 2] Retarget run attribution to job/role level so the collapse doesn't blind the fleet monitor #1789: the fleet-monitor half of [Phase 2] Retarget run attribution to job/role level so the collapse doesn't blind the fleet monitor #1727 AC4 parity was never applied, and the thread closed anyway.
- Dispositions are workflow-enforced, not prompt-enforced. The model must not post disposition replies itself. It emits each disposition as structured output (a sentinel block, as
persona-runner-reusable.ymluses for advisories). The harness validates the structure, runs the verification in AC4, and only then posts the reply with the disposition marker. The marker carries provenance the gate can check: at minimum the Actionsrun_idof the dev-lead run that posted it. The gate accepts a disposition only if that run exists, is adev-lead-reusable.ymlrun on this repo, and completed. A marker typed by the model outside that path, or pasted by a human or any holder of the owner token, must not clear the gate. Forinvalid/answered/informationalthere is no diff to verify, so provenance is the only thing standing between a model's assertion and a merge. - Record the contract as ADR-0009, alongside this change (same PR, or merged before it), not after. It must cover what "addressed" means for the merge gate (verified disposition), the harness-posted provenance requirement (AC11) and why marker-only trust is insufficient under ADR-0008, the human-comment rule (auto-resolve only on verified
fixed), and theminimizeCommentauditability trade-off (AC14). No existing ADR governs this gate's contract. - Retire blanket bot-resolve commands. Remove the
@coderabbitai resolvepost fromdev-lead-fix-reviews.sh, and any equivalent "resolve all" instruction to another review bot. Resolution happens only through verified per-finding dispositions. A bot resolving its own threads wholesale is exactly the "ignore the comment" behaviour this issue exists to remove. - Minimizing never erases the audit trail. A resolved comment stays discoverable: the harness-posted disposition reply quotes or links the original finding and records the verification evidence (commit sha and verified files for
fixed, the referenced issue forout-of-scope, the evidence summary otherwise). A minimized finding is always reachable from a visible reply, and a count of minimized-as-resolved findings is available to the reviewer report, so nobody has to un-hide comments to audit what was dismissed.
Note on the in-flight dev-lead pass (run 34760944872): it started before this section was added. If its PR does not meet ACs 11–14, those gaps will be raised as review findings on the PR rather than by restarting the pass.
Added 2026-09-13 — review of dev-lead's 13:58 plan (acceptance criterion)
- A disposition that needs no commit must still be resolvable. The 13:58 plan wires
resolve_dispositioned_comments()into the existing fix-reviews resolution-gate blocks, which "never resolve without a commit" (the Follow-up to #1567: threads resolved with NO commit at all — the shipped evidence bar gates status=applied, not thread resolution (near-miss: fleet-breaking merge on .github#1024) #1609 / Follow-up to #1609 (slice 2): gate dev-lead thread resolution on the pass having advanced the PR head #1617 no-commit resolution gate). Most comments this issue targets produce no commit by nature: Qodo'sqodo:billing-blockednotice (informational), CodeAnt's review-status tables (informational), a wrong finding (invalid), a question (answered), work tracked elsewhere (out-of-scope). If minimizing is gated on a commit, those comments are never minimized. The new gate blocks on every unminimized comment, so every open PR would be stranded again, recreating this outage in a new form. It is the Harness cannot resolve a bot review thread whose correct response is 'no change needed' — false positives block merge permanently #1743 deadlock ("a bot finding whose correct response is no change can never be resolved") generalized to issue comments. Required: minimizing a verifiedinvalid/answered/informational/out-of-scopedisposition does not require a commit-producing pass. Onlyfixedis tied to a commit, verified via the cited sha (AC4). A bats case covers it: a verifiedinformationaldisposition on aqodo:billing-blockedcomment, from a pass with no commit, is minimized and the gate passes.
Note on the in-flight pass: its 13:58 plan covers ACs 1–10 but none of 11–15 (workflow-enforced disposition provenance, ADR-0009, retiring the blanket @coderabbitai resolve, the minimize audit trail, and no-commit resolution). Those will be raised as review findings on the PR.
Added 2026-09-13 — found while verifying PR #1816 (acceptance criterion)
- Nothing dev-lead posts may be marker-less. On PR feat: implement issue #1813 — 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 #1816, dev-lead's model posted a free-form "Phase 0 note —
template-driftfailure is the pre-existing [bug] seed-repo-template fetches standards unpinned — pin to published stable, compliance accepts N-1 (continues #1436) #1448 known-red…" comment (2026-09-13T14:21:26Z) carrying no agent marker. It comes from no harness template: nothing inscripts/orprompts/contains that text, so the model wrote and posted it on its own. It is authored bydon-petry, andauthor.__typenameis"User", so both the current gate and feat: implement issue #1813 — 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 #1816's new gate read it as a human maintainer's finding. Consequences: dev-lead blocks its own PR; under AC5 a "human" comment auto-resolves only on a verifiedfixed, which a note can never have; and the disposition pass would be asked to research and answer dev-lead's own note, the self-reply loop AC7 forbids. Required: every comment the model or harness posts carries an agent marker, enforced by the harness rather than the prompt (the same mechanism as AC11: the model emits content, the harness posts it with the marker). A lint or bats check fails if any dev-lead prompt instructs posting a comment by any path other than that one.
Activity
github-actions commentedon Sep 13, 2026
Thanks for the thorough write-up — the evidence trail, live gate output, and acceptance criteria make the scope very clear. Before diving in: since AC #1 calls out a tamper-resistance trade-off between using
committer.date(simple, already proven in the advisory gate) versus a server-side source like the PR timeline'shead_ref_force_pushedevent or a check-suitecreated_at(harder to spoof but more complex to fetch), do you have a preference, or should the fix default tocommitter.dateparity withadvisory-review-gate.shand leave the tamper-resistant path as a follow-up?don-petry commentedon Sep 13, 2026
Dev-Lead Implementation Plan
Issue: #1813 — 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
Scope
Repair the #1290 maintainer issue-comment gate so it (a) derives head push-time from the head commit's
committer.dateinstead of the always-nullpushedDate, (b) never classifies org automation (advisory bots + auto-rebase-conflict) as a human maintainer, and (c) gives an undeterminable push-time a distinct, diagnosable verdict — while still blocking a genuine post-head human maintainer comment.Implementation Checklist
scripts/lib/maintainer-comment-gate.sh— AC1: rewritemaintainer_gate_head_committer_date()to return the head commit'scommitter.date(droppushedDate); record the tamper-resistance trade-off in the header.scripts/lib/maintainer-comment-gate.sh— AC2: deriveMAINTAINER_GATE_EXCLUDED_BOTSfrom the reviewer-source registry (reviewer-sources.sh/reviewer-sources.tsv, socodeant-ai/qodo-code-review/graphite-appand any future bot are excluded automatically) unioned with the generic automation accounts; keep an in-sync built-in fallback.scripts/lib/maintainer-comment-gate.sh— AC2: addauto-rebase-conflictto_MAINTAINER_GATE_AGENT_MARKERS(posted asdon-petry); dev-lead*/dependency-advisory already covered by the existing alternation.scripts/lib/maintainer-comment-gate.sh— AC4: return a distinct rc=3 when a maintainer comment exists but the head push-time is undeterminable (was folded into rc=1).scripts/review-one-pr.sh— AC4: map rc=3 to a distinct verdict reason (maintainer-comment-gate-indeterminate-pushtime), notunaddressed-maintainer-comment.Tests to Write (
tests/dev-lead/unit/test_maintainer_comment_gate.bats)qodo:billing-blockedcomment (authorqodo-code-review) → 0;auto-rebase-conflictmarker (authordon-petry) → 0codeant-aiissue comment → 0maintainer_gate_head_committer_datereadscommitter.date(does not depend onpushedDate)review-one-pr.shmaps rc=3 tomaintainer-comment-gate-indeterminate-pushtimeTest Command
bats tests/dev-lead/unit/test_maintainer_comment_gate.bats(full suite via the repobatsjob); lint viabash .dev-lead/scripts/dev-lead-lint.shdon-petry commentedon Sep 13, 2026
Two responses: an answer to the triage question from 13:18, and a note that the dev-lead plan from 13:43 is superseded.
Triage question:
committer.dateparity vs. a tamper-resistant server-side time. The scope has changed, and that changes the answer. The issue body is revised: a comment now counts as "addressed" only once it has a verified disposition, meaning an evidence-backed reply that the harness checks before resolving the comment. Whether a push came after the comment no longer decides it. Under that model the head push time is not what judges a comment answered, so the tamper-resistance concern goes away at the root: a spoofed commit date cannot produce a verified disposition. The decision: removepushedDateentirely, and wherever a head time is still needed, usecommitter.datefor parity withadvisory-review-gate.sh. No separate tamper-resistant follow-up is needed for this gate.The 13:43 implementation plan is superseded. Do not implement it. It excluded
codeant-ai,qodo-code-reviewand graphite through the reviewer-sources registry, addedauto-rebase-conflictto the ignorable agent markers, and tested that those comments pass the gate. The maintainer has directed the opposite: comments are not to be ignored. Every comment, from a bot or a human, must be researched, analyzed, answered, and resolved. The run implementing the old plan was cancelled, and dev-lead will be re-dispatched against the revised body. The Design and Acceptance criteria sections of the body are now the source of truth.don-petry commentedon Sep 13, 2026
@petry-projects/solution-architect please measure the revised design in this issue's body against our recorded ADRs before it lands. It changes the contract of the merge gate that every PR in this repo passes through.
What changed (see the body's Revision, Design and Acceptance criteria sections): the maintainer-comment gate (#1290) currently treats a comment as "addressed" when a push lands after it. That depends on GitHub's
pushedDate, which is always null, so the gate strands every open PR. The maintainer rejected the obvious fix, excluding bot authors, because comments must not be ignored. The revised design:fixed,invalid,out-of-scope,answeredorinformational.RESOLVED(GraphQLminimizeComment). The model never resolves anything itself, matching harness-only thread resolution (dev-lead resolves review threads without addressing them — unfixed findings merge through the thread-resolution gate #1621 S1).fixed. Any other disposition waits for the human (consistent with [Phase 4] Close the maintainer review-gate gap — dev-lead PRs cannot be blocked by the owner (shared identity + agent-resolved threads) #1415 and Harness cannot resolve a bot review thread whose correct response is 'no change needed' — false positives block merge permanently #1743).Where your lens matters most:
don-petry, so the gate can tell an agent's disposition from a human's comment only by HTML markers. Is a marker-only trust boundary structurally sound for a merge gate? Does the harness minimizing comments authored by the owner account introduce an identity ambiguity ADR-0008 did not anticipate?minimizeComment. Is hiding the original comment the right structural representation of "resolved" for issue comments, or does it trade away auditability (a minimized finding is easy to miss later) that the decision record would care about?Time-sensitive: dev-lead is already implementing this issue (run 34760944872), so structural concerns are cheapest to act on now.
don-petry commentedon Sep 13, 2026
Solution Architect — structural advisory
Governing ADR: ADR-0008 (personas post as the owner account) for the identity question raised in point 2; ADR-0004 (pure-logic classifiers + bats) for the verify/resolve split. No ADR governs the merge gate's "addressed" contract itself.
Alignment: mostly aligned, with one gap ADR-0008 didn't anticipate.
main(), judgment-only-where-needed split, and AC1/AC9's pure-classifier-plus-bats requirement is its stated boundary. But no ADR records what "addressed" means for this gate; record one alongside this change, not after.persona-runner-reusable.yml's post step enforces it workflow-side, not prompt-side. This design reuses the same marker-as-trust-boundary pattern fordev-lead's disposition comments, butdev-leadruns throughdev-lead-reusable.yml(ADR-0001), not the persona runner. Confirm the disposition marker is equally workflow-enforced there before the gate trusts it to distinguish agent from human — otherwise ADR-0008's residual-risk warning applies unverified.minimizeCommentas "resolved": consistent with ADR-0004's harness-does-the-write pattern; the auditability trade-off (a minimized finding is easy to miss) isn't covered by any ADR — worth a line in the new record.Escalate? yes — confirm dev-lead's disposition-marker enforcement is workflow-side before this becomes a merge-gate trust boundary.
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.
don-petry commentedon Sep 13, 2026
Responding to the Solution Architect's advisory above. All four points are accepted and folded into this issue's body as ACs 11–14 (section "Architect review — folded in").
The escalation is confirmed, not hypothetical. I checked it against
main:prompts/dev-lead/fix-reviews.mdlines 63–73 instruct the model to post its own thread replies ending in<!-- dev-lead:addressed -->and<!-- dev-lead:claim {…} -->viagh api graphql.GH_TOKEN = GH_PAT_DON_PETRY(dev-lead-reusable.yml:141).For
fixed, the diff verification limits the risk. Forinvalid,answeredandinformationalthere is nothing to verify against, so the gate would be trusting a model-authored marker outright. AC11 therefore requires dispositions to be emitted as structured output and posted by the harness, carrying the dev-leadrun_idas provenance the gate checks.A related finding the advisory did not have:
scripts/dev-lead-fix-reviews.sh:324posts@coderabbitai resolve, which makes CodeRabbit resolve all of its threads wholesale with no per-finding verification. This is how a half-applied finding on PR #1789 (#1727 AC4 parity) closed. AC13 retires it.On the missing ADR: agreed, and AC12 requires ADR-0009 to land alongside the change, not after, covering the "addressed" contract, the provenance requirement and why marker-only trust is insufficient under ADR-0008, the human-comment rule, and the
minimizeCommentauditability trade-off. AC14 makes that trade-off concrete: a minimized finding is always reachable from a visible, harness-posted reply that records the verification evidence.On sequencing: kept as advised. The comment-disposition pass lands first or together with the gate.
coderabbitai commentedon Sep 13, 2026
@don-petry, the revised acceptance criteria address the structural risks.AC11 removes the marker-only trust boundary. The harness becomes the disposition author and binds each disposition to the dev-lead
run_id.AC12 correctly requires ADR-0009 to land with the implementation. The ADR should define the verified-disposition contract and the audit trail for minimized comments.
AC13 removes an unsafe bulk-resolution path. Per-finding verification must precede every resolution action.
AC14 preserves auditability. Each minimized original comment has a visible, harness-posted reply with the disposition and verification evidence.
The required landing order remains correct: land the disposition pass before, or together with, the merge-gate change.
🐇
✏️ Learnings added
You are interacting with an AI system.
don-petry commentedon Sep 13, 2026
Dev-Lead Implementation Plan
Issue: #1813 — 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
Scope
Replace the maintainer issue-comment gate's
pushedDate/timestamp "addressed" proxy with a verified-disposition model: "addressed" is a comment minimized server-side with classifierRESOLVED(mirrors review-threadisResolved); dev-lead posts one evidence-backed disposition reply per undispositioned non-agent comment, and the harness verifies it and minimizes the original.Implementation Checklist
scripts/lib/maintainer-comment-gate.sh— remove allpushedDatereads (AC1);maintainer_gate_head_committer_datereturnscommitter.date(kept for the review-thread gate); rewritecheck_maintainer_comments <snapshot> [bot_user](drophead_date) to block any non-agent issue comment not minimized asRESOLVED(AC2) — the only exclusions are our bot login and our own agent-marked disposition/ack/note replies; no bot-author exclusion list. Distinct verdict path so an undeterminable snapshot (rc=2) is never disguised as the legitimate hold (AC8).scripts/lib/comment-disposition-verify.sh— new pure lib: parse<!-- dev-lead:comment-disposition id=<id> disposition=<fixed|invalid|out-of-scope|answered|informational> [sha=<sha>] [ref=#<n>] -->;cdv_authorizeencodes AC4/AC5 (human comment auto-resolves only on a verifiedfixed; bots on any verified disposition). Reusesaddressed-claim-verify.shfor thefixedsha-on-head check.scripts/review-one-pr.sh— call the new gate signature; emit a distinct reason (undispositioned-pr-commentfor the hold,maintainer-comment-gate-errorfor undeterminable, AC8); keep FORCE_REVIEW bypass + prior-approval dismissal.scripts/dev-lead-fix-reviews.sh— newresolve_dispositioned_comments(): for each undispositioned non-agent issue comment, find our disposition reply (idempotent per comment id, AC7), verify per disposition type, andminimizeCommentclassifierRESOLVED(AC4/AC5). Wire it into the fix-reviews / fix-bot-comment resolution-gate blocks (never resolves without a commit).prompts/dev-lead/fix-reviews.md— instruct dev-lead to research/analyze/respond-once to every undispositioned PR issue comment (not just review threads), emit the disposition marker, route informational comments with one tracking issue per repo (AC3/AC6), and never answer its own replies (AC7).scripts/lib/consumer-manifest.json+.github/workflows/lint.yml— register the new lib and bats files (also adds the pre-existingtest_maintainer_comment_gate.bats, currently unregistered).Tests to Write
tests/dev-lead/unit/test_maintainer_comment_gate.bats(rewrite): nopushedDate; undispositionedcodeant-ai/qodo-code-review/auto-rebase-conflictcomment → blocks (AC9b); all comments minimizedRESOLVED→ passes (AC9a); our disposition reply never blocks (AC9e); malformed → rc=2 (AC8).tests/dev-lead/unit/test_comment_disposition_verify.bats(new): marker parse (valid/missing/multiple/bad);fixedciting a sha not on head → not authorized (AC9c); human comment + non-fixeddisposition → not authorized/blocks (AC9d); a bot boilerplate reply to our disposition is not a new finding (AC9f).Test Command
scripts/run-bats.sh tests/dev-lead/unit/test_maintainer_comment_gate.bats tests/dev-lead/unit/test_comment_disposition_verify.batsandbash .dev-lead/scripts/dev-lead-lint.shdon-petry commentedon Sep 13, 2026
Dev-Lead: Implementation Complete — PR #1816
The implementation for issue #1813 is durable: commits are pushed and a pull request is open. This record is backed by verifiable artifacts you can confirm in one click.
cd327acfddeccd8afc218976b43bb875e8f76525Review happens on the PR. Acceptance criteria and test results are evidenced by the PR's diff and CI checks — not asserted here. If a later stage fails, this claim is retracted in place.
feat: implement issue #1813 — pr-review can't approve ANY open PR: ma…
fix(pr-review): re-evaluate at the same head after its own gate dismi…
fix(lint): ADR-0012 line began with #1813, parsed as an atx heading (…
feat: implement issue #2209 — pr-review comment gate counts the owner…