fix(dev-lead): idempotent comment dispositions and duplicate recovery (#1992) - #2006
Conversation
…e timeout — unreviewed, do not merge)
…osition Finishes the checkpoint 1b957e3, which timed out at 2700s. It is now reviewed and complete. The checkpoint's prompts told the engine to skip any comment that already carried a disposition. That is wrong for `fixed`: the resolver verifies a `fixed` sha only when the pass citing it produced the commit, so a `fixed` reply orphaned by a failed or timed-out pass can never clear on its own. Skipping it would block the PR permanently, the same deadlock this issue removes. - fix-reviews.md: skip only comments with an existing non-`fixed` disposition. A still-unminimized `fixed` one must be re-checked and given a fresh disposition; the resolver keeps the latest and marks older ones OUTDATED. - fix-bot-comment.md: the idempotency check ignores `disposition=fixed` replies, and the BOT_USER fallback is don-petry, the account that posts dispositions. - test_fix_reviews.bats: new case with #1952's exact shape (fixed, then invalid twice). It resolves on the latest invalid and marks both earlier replies OUTDATED. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015fdDZDb4mhT65MU1izeA4R
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
ⓘ Qodo reviews are paused because your trial has ended. Ask your workspace admin to add credits to resume reviews. Manage billing |
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. Note Currently processing new changes in this PR. This may take a few minutes, please wait... ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Advanced Run ID: 📒 Files selected for processing (6)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
CodeAnt Nitpicks4 code suggestions1. An earlier valid
|
There was a problem hiding this comment.
Code Review
This pull request implements duplicate recovery and idempotency for bot comment dispositions, resolving issues #1992, #1952, and #1953. It introduces a deterministic selection helper cdv_select_disposition to collapse multiple authorized replies to the latest one, minimizes superseded replies as outdated, and ensures the resolver runs on failed passes. The feedback suggests fixing an inconsistent default value for BOT_USER in the documentation, removing a redundant newline check when reading from a here-string, and tightening test assertions in BATS to check for specific non-zero exit codes instead of generic non-zero statuses.
There was a problem hiding this comment.
All reported issues were addressed across 6 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
…ed`, strict markers and metadata cubic, CodeAnt and Gemini findings on #2006: - Failed pass never certifies `fixed` (cubic P2). The engine may commit locally and then fail before commit_and_push, so local HEAD advances while the PR head does not. resolve_dispositioned_comments takes a "failed" flag on all three failure paths and refuses `fixed` there. - Selector fails closed on unreadable metadata (cubic P2 x2). It accepts only an explicit isMinimized == false and a well-formed ISO-8601 createdAt, so a missing or garbled field can't count or win "latest". - fix-bot-comment idempotency check: - (CodeAnt major, cubic P2) it matches only a well-formed non-`fixed` marker, the shape the parser accepts, so quoted or malformed text never suppresses the reply; - (cubic P3) an unreadable check fails closed and posts nothing; - (cubic P2) it reads the newest 100 comments (`last:100`), where an earlier pass's reply lives; - (Gemini) the BOT_USER fallback matches the resolver's (donpetry-bot). - fix-reviews prompt (cubic P2, CodeAnt): an unverified `fixed` whose fix is already on the branch gets an `answered` disposition citing that commit. A fresh `fixed` from a pass with no commit can never verify. - Tests: the disposition repo lives under BATS_TEST_TMPDIR (no leak); the failure-path tests assert exit 1, not just nonzero; new cases cover failed-pass `fixed`, a missing isMinimized and a malformed createdAt (each fails without the change). The redundant here-string `|| [[ -n ]]` guard is gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015fdDZDb4mhT65MU1izeA4R
There was a problem hiding this comment.
All reported issues were addressed across 6 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
…check cubic P2: $id was concatenated into the test() regex, so a regex metacharacter in an id would break the match (re-posting a duplicate) or abort jq (read as unreadable, suppressing a legitimate reply). The body is now split on the exact marker prefix, and only the disposition tail is a regex. Checked against an id containing '+': the matching reply counts, while fixed, other-id and quoted cases do not. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015fdDZDb4mhT65MU1izeA4R
Dev-Lead — review-changes (no-changes)No changes were needed for this PR. |
|
Advisory bots were rate-limited; auto-approval is withheld until they recover. pr-review-sweep will re-review this PR after 2026-10-01T22:16:41Z. |
Dev-Lead — fix-bot-comment (no-changes)Agent reasoning |
|
CI checks on this PR are still running. The PR-review sweep re-reviews this PR automatically once the checks complete — no action is needed. Posted by the donpetry-bot PR-review cascade. |
donpetry-bot
left a comment
There was a problem hiding this comment.
Automated review — APPROVED ✓
Risk: MEDIUM
Reviewed commit: f50278111066dbdc0851fcc2fb06731663ba6af3
Review mode: triage-approved (single reviewer)
Summary
Fixes the Dev-Lead disposition deadlock (#1992). It adds a pure cdv_select_disposition selector. The selector collapses multiple authorized BOT_USER dispositions to the latest by createdAt (node-id tie-break). The resolver minimizes the superseded replies OUTDATED once the original is RESOLVED. The resolver now also runs on the failure path of all three intents, with fixed never certified there. Both prompts gain idempotency guidance.
Linked issue analysis
Closes #1992. All acceptance criteria are substantively addressed:
- Idempotent posting: prompt-level skip in both paths. The harness backstop is duplicate convergence after the engine returns, and the code comment records this choice (the model posts through its own shell).
- Duplicate recovery: deterministic latest-wins selection. Superseded replies are minimized OUTDATED. Unparseable or non-
BOT_USERreplies still fail closed. - No orphans on a failed pass:
resolve_dispositioned_comments <intent> failedruns on the fix-reviews, fix-bot-comment and review-changes failure branches.fixedis refused there because the commit may be unpushed. - Tests: behavioural unit tests for the selector, and end-to-end resolver tests with failing engine stubs, covering the #1952 shape, the CWE-863 attacker case and
fixedon a failed pass.
Findings
No blocking findings.
- Authorization is preserved: the CWE-863 author gate (
BOT_USERor its[bot]-stripped login) is unchanged. Only the cardinality rule is relaxed, and only among replies the bot itself authored. The new selector also tightens input handling:isMinimized == falsemust be explicit, andcreatedAtmust be strict ISO-8601. - The resolver's comment query already fetches
createdAtandisMinimized, so the selector gets real data in production. - Fail-closed edge case: if the latest reply is a stale
fixedfrom an earlier pass, the comment stays open. The new prompt exception (postanswerednaming the existing commit) lets the next pass converge. - Non-blocking: a failed OUTDATED minimization is only warned, not retried. The author's rationale holds: once the original is RESOLVED it leaves the candidate set, so a leftover reply can't re-block the gate.
- All 17 review threads (CodeAnt, Gemini, cubic) are resolved, with fixes in 41ede87 and 42ef721.
- MCP secret scan:
run_secret_scanningwas not available in this run. The gitleaks CI check passed.
CI status
All required checks are green: bats, unit-tests, shellcheck, CodeQL, SonarCloud (Quality Gate passed), gitleaks, AgentShield, actionlint and others. dev-lead / dispatch|ci-relay|resume show CANCELLED (superseded concurrency runs, not test failures). Dependency-audit language jobs were skipped as not applicable.
Reviewed automatically by the PR-review agent (single-reviewer mode: opus 5.5 [opus 4.8, opus 4.7]). Reply if you need a human review.
|
pr-review approved on PARTIAL advisory evidence: 4/5 required advisory bots reported before the gate's head-age-timeout fallback proceeded. Recorded for the miss-rate metric (#1596). |
Dev-Lead — fix-bot-comment (no-changes)Agent reasoning |
|
|
Advisory bots were rate-limited; auto-approval is withheld until they recover. pr-review-sweep will re-review this PR after 2026-10-01T23:04:45Z. |
donpetry-bot
left a comment
There was a problem hiding this comment.
Automated review — APPROVED ✓
Risk: MEDIUM
Reviewed commit: e0cc2cf886d607efeea0df043a69400bea190e10
Review mode: triage-approved (single reviewer)
Summary
Fixes the Dev-Lead disposition deadlock (#1992). It adds a pure cdv_select_disposition selector, which picks the latest authorized BOT_USER disposition by createdAt (node-id tie-break). The resolver then minimizes the superseded replies OUTDATED once the original comment is RESOLVED. The resolver also runs on the failure path of fix-reviews, fix-bot-comment and review-changes, and fixed is never certified there. Both prompts gain idempotency guidance. Since the prior approved review (f502781), the only new commits are two merges from main, and neither touches this PR's six files.
Linked issue analysis
Closes #1992 (and #1934 item 1). The acceptance criteria are substantively addressed:
- Duplicate recovery: latest-wins selection is deterministic. Superseded replies are minimized OUTDATED only after the original is RESOLVED, so the count converges to one.
- Authorization unchanged: only
BOT_USER(or its[bot]-stripped login) counts (CWE-863). Minimized, unparseable, mis-targeted and malformed-createdAt replies fail closed. - No orphans on a failed pass:
resolve_dispositioned_comments <intent> failedruns on all three failure branches.fixedis refused there because the commit may be unpushed. - Idempotent posting: the prompts skip an existing non-
fixeddisposition. An unverifiedfixedis re-checked rather than skipped (the 0a639c3 fix). The fix-bot-comment check matches the node id literally and fails closed when the check can't be read. - Tests: selector unit tests, plus end-to-end resolver tests covering #1952's exact
fixed, invalid, invalidshape and a non-BOT_USERdisposition.
Findings
No blocking findings.
- All 18 review threads (CodeAnt, Gemini, cubic) are resolved; cubic reports every issue addressed.
- The resolver's comment query already fetches
id,isMinimized,minimizedReasonandcreatedAt, which is everything the selector needs. - Non-blocking: a failed OUTDATED minimize is only logged as a warning. This is self-healing: the next pass reselects, and an already-RESOLVED original stops being a candidate.
- The secret-scan MCP tool is not available in this run. The gitleaks CI check passed.
CI status
All code checks pass on e0cc2cf: shellcheck, bats, unit, unit-tests, CodeQL, SonarCloud (Quality Gate passed), gitleaks, actionlint, AgentShield, prompt-coverage, and the stub and standards validators. The only non-success jobs are three CANCELLED dev-lead orchestration jobs (dispatch, ci-relay, resume), which were superseded runs and are not code checks.
Reviewed automatically by the PR-review agent (single-reviewer mode: opus 5.5 [opus 4.8, opus 4.7]). Reply if you need a human review.



Summary
Dev-Lead no longer stacks duplicate
dev-lead:comment-dispositionreplies, and the resolver recovers PRs that are already stuck on duplicates.This finishes the Dev-Lead checkpoint 1b957e3, which timed out at the 2700s action budget. It adds a fix for one defect in that checkpoint and a regression test with #1952's exact shape.
scripts/lib/comment-disposition-verify.sh): new purecdv_select_disposition. Among the parseable, non-minimized dispositions authored byBOT_USER(CWE-863: nobody else's ever count), it picks the latest bycreatedAt, with a node-id tie-break, and reports the rest as superseded.scripts/dev-lead-fix-reviews.sh): uses the selector instead of failing closed onauth_count != 1. After the original comment is minimized RESOLVED, it minimizes the superseded replies OUTDATED, so the count converges to one.resolve_dispositioned_commentsnow also runs on the failure path of fix-reviews, fix-bot-comment and review-changes. Each disposition is still verified on its own terms, so an unpushedfixedfails closed.fixeddisposition from the bot account.fixed: a still-unminimizedfixeddisposition is never verified, because the resolver only accepts afixedsha produced by the pass that cites it. The model must re-check it and post a fresh disposition. Skipping it, as the checkpoint did, would block the PR permanently. That is the defect fixed in 0a639c3.Problem
Closes #1992. It also covers #1934 item 1 (the fix-bot-comment path).
Dev-Lead posted a new disposition on every pass until the comment was minimized. The resolver required exactly one per comment, so any comment dispositioned but not minimized in the same pass stayed blocked for good:
fixed, theninvalidtwice).Risk
Medium. This changes how the maintainer comment gate clears dispositioned comments.
BOT_USERreplies count, and unparseable markers fail closed.fixedverification is unchanged: it is still bound to the pass that made the commit.Test plan
cdv_select_dispositionunit tests intests/dev-lead/unit/test_comment_disposition_verify.bats. They cover a single disposition, three converging to the latest, a non-BOT_USERreply never winning, minimized and unparseable replies being excluded, zero replies failing closed, and the tie-break.tests/dev-lead/unit/test_fix_reviews.bats, with failing engine stubs:fixed,invalid,invalid) resolves on the latestinvalid;BOT_USERdisposition can't resolve the comment.bats tests/dev-lead/unit/test_comment_disposition_verify.bats, plusbats tests/dev-lead/unit/test_fix_reviews.bats(161 tests). The only failure isreview-changes evidence: whitespace/cosmetic-only change, which fails identically onmain.test_agent_marker_convention,test_channel_*_guardrail,test_duplicate_decl_gate; 66 tests) andtests/dev-lead/integration/test_prompt_coverage.shpass.shellcheck --severity=warningandscripts/dev-lead-lint.share clean.Rollback
Revert this PR. There are no non-revertible side effects. Replies minimized OUTDATED stay minimized but are still visible, and can be un-minimized by hand.
Monitoring
selecting latest … marking N superseded reply(ies) OUTDATED, and the gate'sundispositioned-pr-commenthold should clear.expected exactly one authorized dispositionnotice. That message no longer exists, so seeing it means the old code is still running.Interaction contract
Checklist
shellcheck scripts/*.shpasses for any changed shell scripts.🤖 Generated with Claude Code
https://claude.ai/code/session_015fdDZDb4mhT65MU1izeA4R
Generated by Claude Code