The inline-comment MCP tool now requires confirmed=true to post (otherwise
calls are buffered). This structurally prevents subagent test/probe
comments from reaching customer PRs — subagents that inherit the tool and
probe it without confirmed=true see their calls harmlessly buffered.
Backward compatible: against older versions of claude-code-action that
don't know the param, the extra field is ignored and the comment posts
as before.
- Use allowlist for issue view (numeric issue numbers only)
- Enforce zero positional args for issue list / label list
- Pin GH_HOST and GH_REPO explicitly to avoid ambient state
- Add descriptive error messages with usage examples
Extract triage prompt into /triage-issue command and use a dedicated
edit-issue-labels.sh script for label operations instead of raw
gh issue edit. The script validates labels against the repo's existing
labels before applying them.
The sweep script was closing issues based solely on when a lifecycle label
was applied, ignoring any human comments posted after the label. This caused
active issues (like #11792) to be closed even when users responded to the
stale warning.
Three changes:
1. Teach the triage bot about `stale` and `autoclose` labels so it removes
them when a human comments on the issue.
2. Add a safety net in `closeExpired()` that checks for non-bot comments
posted after the lifecycle label was applied — if any exist, skip closing.
3. Extend the 10-upvote protection (which previously only applied to
enhancements) to all issue types, in both `markStale()` and
`closeExpired()`.
Fixes#16497
## Test plan
Trace through scenarios manually:
- Issue with stale label + human comment after → triage removes label;
sweep skips even if triage hasn't run yet (safety net)
- Issue with stale label + no human comment → closes as before
- Issue with 10+ upvotes of any type → never marked stale or closed
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When lifecycle labels (needs-info, needs-repro, invalid, stale, autoclose)
are applied to an issue, the author currently only sees a label change with
no explanation. They then get a closing comment days later without ever
being nudged to respond.
Add a GitHub Actions workflow that triggers on issues.labeled and runs a
new lifecycle-comment.ts script to post a comment explaining what's needed
and how long before auto-close.
Extract lifecycle config (labels, timeouts, close reasons, nudge messages)
into a shared issue-lifecycle.ts so the sweep script and comment script
stay in sync. Previously the timeouts were duplicated between the sweep
script and the comment messages.
- needs-info: asks for version, OS, error messages
- needs-repro: asks for steps to trigger the issue
- invalid: links to the Claude Code repo and Anthropic support
- stale/autoclose: explains inactivity auto-close
The script no-ops for non-lifecycle labels, so the workflow fires on every
label event and lets the script decide — single source of truth.
## Test plan
Dry-run all labels locally:
GITHUB_REPOSITORY=anthropics/claude-code LABEL=needs-info ISSUE_NUMBER=12345 bun run scripts/lifecycle-comment.ts --dry-run
GITHUB_REPOSITORY=anthropics/claude-code LABEL=needs-repro ISSUE_NUMBER=12345 bun run scripts/lifecycle-comment.ts --dry-run
GITHUB_REPOSITORY=anthropics/claude-code LABEL=invalid ISSUE_NUMBER=12345 bun run scripts/lifecycle-comment.ts --dry-run
GITHUB_REPOSITORY=anthropics/claude-code LABEL=stale ISSUE_NUMBER=12345 bun run scripts/lifecycle-comment.ts --dry-run
GITHUB_REPOSITORY=anthropics/claude-code LABEL=autoclose ISSUE_NUMBER=12345 bun run scripts/lifecycle-comment.ts --dry-run
Verified sweep.ts still works:
GITHUB_TOKEN=$(gh auth token) GITHUB_REPOSITORY_OWNER=anthropics GITHUB_REPOSITORY_NAME=claude-code bun run scripts/sweep.ts --dry-run
The sweep job (https://github.com/anthropics/claude-code/actions/runs/21983111029/job/63510453226)
was silently failing when closeExpired tried to comment on a locked issue,
causing a 403 from the GitHub API.
Two issues:
1. closeExpired didn't skip locked issues like markStale already does.
Adding the same `if (issue.locked) continue` guard fixes this.
2. The error was swallowed by `main().catch(console.error)` which logs
to stderr but exits 0, so CI reported success despite the crash.
Replaced the main() wrapper with top-level await so unhandled errors
properly crash the process with a non-zero exit code.
## Test plan
YOLO