Repository navigation
ci: auto-close-duplicates cron — 14-day window, dry-run mode (#289) - #291
Conversation
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pohc8xLkdx4gwXkW3xd7up
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
WalkthroughAdds a scheduled and manually triggered workflow plus a Node.js CLI that detects marked duplicate issues, applies age and objection safeguards, supports dry-run mode, and closes eligible issues with GitHub API fallbacks. ChangesDuplicate issue auto-closure
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant GitHubActions
participant DuplicateCLI
participant GitHubAPI
GitHubActions->>DuplicateCLI: Start with repository and effective mode
DuplicateCLI->>GitHubAPI: Fetch open issues and comments
GitHubAPI-->>DuplicateCLI: Return issue and deduplication data
DuplicateCLI->>GitHubAPI: Comment on and close eligible issue
GitHubAPI-->>DuplicateCLI: Return request status
Possibly related issues
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Preview DeploymentPreview URL: https://1b492ee6.bestax.pages.dev |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
scripts/auto-close-duplicates.mjs (1)
209-338: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winOne failing issue aborts the entire scan.
The per-issue body has no local error handling, so a single transient/permanent error (e.g., a transferred duplicate target returning
301, which is not inokStatuses, or a one-off5xx) throws to the outer catch and stops processing every remaining issue for that run. A persistently problematic issue would block all other closures on every daily run.Consider wrapping the per-issue work in a
try/catchthat logs andcontinues (tracking afailedcount), reserving the outer hard-fail forRateLimitAbortand unexpected fatal errors.♻️ Sketch: isolate per-issue failures
for (const issue of issues) { if (issue.pull_request || isBot(issue.user)) continue; counts.scanned++; - checkRateBudget(); - ... - counts.closed++; + try { + checkRateBudget(); + // ... existing per-issue logic ... + counts.closed++; + } catch (err) { + if (err instanceof RateLimitAbort) throw err; + console.error(`#${issue.number}: skipped after error: ${err.message}`); + counts.failed++; + } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/auto-close-duplicates.mjs` around lines 209 - 338, Wrap each iteration of the issues loop around the per-issue workflow in a local try/catch so transient or issue-specific API failures are logged and processing continues with subsequent issues. Add and increment a failed count for these skipped issues, while rethrowing RateLimitAbort and preserving the outer catch for unexpected fatal errors; ensure the final summary includes the new failed count.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@scripts/auto-close-duplicates.mjs`:
- Around line 124-150: The api() helper must handle GitHub secondary rate limits
and request timeouts. Add an AbortSignal.timeout(...) to each fetch request, and
retry 403/429 responses using the Retry-After header as the backoff delay before
retrying; preserve existing okStatuses handling and error reporting for
non-retriable failures.
---
Nitpick comments:
In `@scripts/auto-close-duplicates.mjs`:
- Around line 209-338: Wrap each iteration of the issues loop around the
per-issue workflow in a local try/catch so transient or issue-specific API
failures are logged and processing continues with subsequent issues. Add and
increment a failed count for these skipped issues, while rethrowing
RateLimitAbort and preserving the outer catch for unexpected fatal errors;
ensure the final summary includes the new failed count.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: e9ca0027-6708-4fba-8807-c896dd005609
📒 Files selected for processing (2)
.github/workflows/auto-close-duplicates.ymlscripts/auto-close-duplicates.mjs
There was a problem hiding this comment.
Deep review — 1 finding
| No. | Severity | Area | Finding | Location |
|---|---|---|---|---|
| 1 | 🟡 Minor | Correctness | No concurrency group — a manual workflow_dispatch can race the daily cron and double-comment/double-close the same issue | .github/workflows/auto-close-duplicates.yml:41 |
Overall: The change is sound and unusually well-guarded. The veto stack (age gate → human-comment-after → 👎 reaction → reopened timeline event → target-exists/self-reference), the fail-closed AI_TRIAGE_AUTOCLOSE gate, the de-escalate-always / never-escalate-to-on dispatch logic, and the state_reason=duplicate → not_planned 422 fallback all check out. Scanning only state=open and materializing the full issue list up front avoids pagination-shift bugs, and the polite rate-limit floor exits 0 as documented. The riskiest area is purely operational — overlapping runs — and it only yields a redundant comment, not a wrong close. Minor edges worth a glance but not blocking: a Duplicate-of-#PR target passes the existence check (PRs resolve via /issues/{n}), and there are no unit tests for the exported helpers, though that matches the existing scripts/*.mjs (which have none either).
🏄 Clean set, dude — this cron paddles out with reef shoes on: fails closed, vetoes politely, and will not wipe out on a 422. Only ripple is two boards catching the same wave if a manual run drops in on the cron, so toss a concurrency leash on it and it is all-time. Good to send.
#289) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pohc8xLkdx4gwXkW3xd7up
Preview DeploymentPreview URL: https://910da6b5.bestax.pages.dev |
|
🎉 This PR is included in version 3.2.1 🎉 The release is available on: Your semantic-release bot 📦🚀 |
|
🎉 This PR is included in version 5.4.1 🎉 The release is available on: Your semantic-release bot 📦🚀 |
|
🎉 This PR is included in version 1.0.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
|
🎉 This PR is included in version 1.0.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
Pull Request
Description
PR B of #289: the zero-Claude daily cron that closes silence-approved duplicate issues, ported from oven-sh/bun's
scripts/auto-close-duplicates.tswith the objection window widened from 3 to 14 days.scripts/auto-close-duplicates.mjs— dependency-free (node: builtins + globalfetchagainst the GitHub REST API,GITHUB_TOKENauth). Scans open issues for a latest claude[bot] comment carrying the<!-- ai-triage:dedupe -->marker plus a parseableDuplicate of #Nline; closes only when the marker is ≥14 days old and none of the three vetoes hit: a non-bot comment after the marker, a 👎 reaction on the marker comment, or areopenedevent in the issue timeline (a reopened issue is never auto-closed again). Closes withstate_reason: duplicate, falling back tonot_plannedon a 422.--mode=dry-runonly logs the would-close list. Rate-limit courtesy: aborts politely below 20 remaining requests. Exit codes: 0 clean / 1 API failure / 2 usage; every action/skip line names the issue and the reason; summary counts at the end..github/workflows/auto-close-duplicates.yml— cron37 6 * * *(non-colliding slot) +workflow_dispatch;permissions: {}top-level, the single job gets exactlyissues: write+contents: read; pinned checkout/setup-node, no pnpm/cache (the script is dep-free). Deliberately NOT gated onAI_LOOP_ENABLED— zero-Claude workflows ignore the AI-spend kill switch (house rule, same asauto-label-claude-prs.yml). Aworkflow_dispatchmode input may de-escalateon → dry-runbut can never escalatedry-run → onunless the repo variable itself ison.The job never runs until the
AI_TRIAGE_AUTOCLOSErepo variable is set (dry-runoron) — unset fails closed. Per the #289 rollout plan: start withdry-run, flip toonafter a week of sane logs.Docs for #289 (ai-development guide + root CLAUDE.md) live in PR A, which also ships the ai-triage v2 workflow and slash commands that produce the dedupe markers this cron consumes.
@allxsmith/bestax-bulma)create-bestax)@allxsmith/bestax-docs)scripts/,.github/workflows/)Related Issue(s)
Refs #289 (PR A + the rollout checklist complete it)
Type of Change
Checklist
node -efixture asserts over the exported pure helpers: argv parsing, marker matching, age math, veto logic; plus a live dry-run against this repo — 20 issues scanned, 0 candidates, clean exit)CLAUDE.mdfiles are updated — CLAUDE.md update is PR A's scopeScreenshots / Demos
Live dry-run output (read-only) against
allxsmith/bestax:Additional Context
Verification performed:
node --check,python3 yaml.safe_loadon the workflow, prettier + eslint clean, fixture asserts, live dry-run.🤖 Generated with Claude Code
https://claude.ai/code/session_01Pohc8xLkdx4gwXkW3xd7up
Generated by Claude Code
Summary by CodeRabbit