Sync claude/fix-all-issues-cSPLB with main#7
Conversation
…ew-comment-update fix: Do not comment if --comment is not present
Introduce a simple, mechanical daily sweep that closes issues with lifecycle labels past their timeout: - needs-repro: 7 days - needs-info: 7 days - needs-votes: 30 days - stale: 30 days The sweep checks when the label was last applied via the events API, and closes the issue if the timeout has elapsed. No AI, no comment checking — if the label is still there past its timeout, close it. Removing a label (by a triager, slash command, or future AI retriage) is what prevents closure. Each close message directs the reporter to open a new issue rather than engaging with the closed one. The script supports --dry-run for local testing: GITHUB_TOKEN=$(gh auth token) \ GITHUB_REPOSITORY_OWNER=anthropics \ GITHUB_REPOSITORY_NAME=claude-code \ bun run scripts/sweep.ts --dry-run ## Test plan Ran --dry-run against anthropics/claude-code. Correctly identified 3 issues past their timeouts (1 needs-repro at 12d, 2 needs-info at 14d and 26d). No false positives.
…lifecycle-labels Add daily sweep to enforce issue lifecycle label timeouts
…pics#25352) * Unify issue lifecycle labeling and sweep into a single system Consolidate issue triage, stale detection, and lifecycle enforcement into two components: a Claude-powered triage workflow and a unified sweep script. Triage workflow changes: - Add issue_comment trigger so Claude can re-evaluate lifecycle labels when someone responds to a needs-repro/needs-info issue - Add concurrency group per issue with cancel-in-progress to avoid pile-up - Filter out bot comments to prevent sweep/dedupe triggering re-triage - Hardcode allowed label whitelist to prevent label sprawl (was discovering labels via gh label list, leading to junk variants like 'needs repro' vs 'needs-repro') - Replace MCP GitHub server with gh CLI — simpler, no Docker dependency, chaining is caught by the action so permissions are equivalent - Add lifecycle labels (needs-repro, needs-info) for bugs missing info - Add invalid label for off-topic issues (Claude API, billing, etc.) - Add anti-patterns to prevent false positives (don't require specific format, model behavior issues don't need traditional repro, etc.) Sweep script changes: - Absorb stale issue detection (was separate stale-issue-manager workflow) - Mark issues as stale after 14 days of inactivity - Skip assigned issues (team is working on it internally) - Skip enhancements with 10+ thumbs up (community wants it) - Add invalid label with 3-day timeout - Add autoclose label support to drain 200+ legacy issues - Drop needs-votes (stale handles inactive enhancements) - Unify close messages into a single template with per-label reasons - Run 2x daily instead of once Delete stale-issue-manager.yml — its logic is now in sweep.ts. ## Test plan Dry-run sweep locally: GITHUB_TOKEN=$(gh auth token) GITHUB_REPOSITORY_OWNER=anthropics GITHUB_REPOSITORY_NAME=claude-code bun run scripts/sweep.ts --dry-run Triage workflow will be tested by opening a test issue after merge. * Update .github/workflows/claude-issue-triage.yml Co-authored-by: Ashwin Bhat <ashwin@anthropic.com> --------- Co-authored-by: Ashwin Bhat <ashwin@anthropic.com>
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
…entry Fix changelog
…hropics#61584) Replace the static ANTHROPIC_API_KEY secret with Workload Identity Federation inputs in claude.yml, claude-issue-triage.yml, and claude-dedupe-issues.yml. The federation rule, organization, service account, and workspace IDs are read from repository variables.
Update security-guidance plugin
Update security-guidance plugin
There was a problem hiding this comment.
Pull request overview
This PR syncs the branch with main by updating repository security/reporting docs, adding/adjusting GitHub automation for issue lifecycle management, and landing a major security-guidance plugin v2 update (hooks + supporting Python modules). It also adds MDM deployment templates and updates several workflows to authenticate to Anthropic via Workload Identity Federation (OIDC) instead of a static API key.
Changes:
- Replace stale-issue GitHub Script automation with Bun-based lifecycle tools (
scripts/sweep.ts,scripts/lifecycle-comment.ts) and new workflows. - Introduce
security-guidanceplugin v2 (pattern warnings + Stop-hook LLM diff review + agentic commit review) with new hook plumbing and utilities. - Add MDM deployment examples (macOS + Windows) and update security/reporting documentation and workflow auth configuration.
Reviewed changes
Copilot reviewed 43 out of 46 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| SECURITY.md | Updates HackerOne submission URL and renames disclosure section. |
| scripts/sweep.ts | Adds Bun script to mark issues stale and auto-close lifecycle-labeled issues. |
| scripts/lifecycle-comment.ts | Adds Bun script to post a warning comment when lifecycle labels are applied. |
| scripts/issue-lifecycle.ts | Defines lifecycle labels, timeouts, nudges, and upvote threshold. |
| scripts/gh.sh | Adds a restricted gh wrapper to constrain allowed subcommands/flags. |
| scripts/edit-issue-labels.sh | Adds workflow-bound label editor (issue number from event payload). |
| scripts/comment-on-duplicates.sh | Changes duplicate-commenting to bind base issue to the triggering event payload. |
| plugins/security-guidance/README.md | Documents the security-guidance plugin layers, configuration, and data handling. |
| plugins/security-guidance/hooks/sg-python.sh | Adds Python interpreter shim (esp. for Windows Git Bash python3 stub). |
| plugins/security-guidance/hooks/session_state.py | Adds per-session state file management with optional file locking and GC. |
| plugins/security-guidance/hooks/review_api.py | Adds importable public API for agentic review prompt building/filtering/formatting. |
| plugins/security-guidance/hooks/patterns.py | Defines built-in security patterns and stable numeric RuleId mapping. |
| plugins/security-guidance/hooks/hooks.json | Updates hook wiring to include SessionStart bootstrap and async Stop/commit/push reviews. |
| plugins/security-guidance/hooks/gitutil.py | Adds git/diff helpers and diff parsing/source classification logic. |
| plugins/security-guidance/hooks/extensibility.py | Adds project/user/local guidance + custom pattern rule loading/validation. |
| plugins/security-guidance/hooks/ensure_agent_sdk.py | Adds SessionStart bootstrap to ensure the agent SDK is available via a persisted venv. |
| plugins/security-guidance/hooks/diffstate.py | Adds git-derived diff/review state helpers and reviewed-SHA tracking. |
| plugins/security-guidance/hooks/_base.py | Adds shared helpers (debug logging, plugin version, token/cost usage metrics). |
| plugins/security-guidance/.claude-plugin/plugin.json | Bumps plugin version to 2.0.0 and updates metadata/homepage. |
| plugins/code-review/commands/code-review.md | Updates code-review command instructions (terminal summary + conditional commenting). |
| feed.xml | Adds Atom feed for CHANGELOG release entries. |
| examples/settings/README.md | Fixes grammar and adds a pointer to MDM templates. |
| examples/mdm/windows/Set-ClaudeCodePolicy.ps1 | Adds Intune script example to deploy managed-settings.json on Windows. |
| examples/mdm/windows/en-US/ClaudeCode.adml | Adds Group Policy resource strings for Claude Code managed settings. |
| examples/mdm/windows/ClaudeCode.admx | Adds Group Policy definition for managed settings JSON registry value. |
| examples/mdm/README.md | Adds README for MDM templates across platforms. |
| examples/mdm/managed-settings.json | Adds minimal managed-settings.json template. |
| examples/mdm/macos/com.anthropic.claudecode.plist | Adds Jamf/Kandji custom settings plist template. |
| examples/mdm/macos/com.anthropic.claudecode.mobileconfig | Adds macOS configuration profile template for managed settings. |
| .github/workflows/sweep.yml | Adds scheduled workflow to run scripts/sweep.ts. |
| .github/workflows/stale-issue-manager.yml | Removes old GitHub Script-based stale issue manager. |
| .github/workflows/oncall-triage.yml | Removes old oncall triage workflow. |
| .github/workflows/non-write-users-check.yml | Adds workflow to warn when allowed_non_write_users is introduced/modified. |
| .github/workflows/issue-lifecycle-comment.yml | Adds workflow to post lifecycle comments on issues:labeled. |
| .github/workflows/claude.yml | Switches Claude action auth to Workload Identity Federation inputs. |
| .github/workflows/claude-issue-triage.yml | Updates triage to run on issue open + human comments, uses slash command + WIF auth. |
| .github/workflows/claude-dedupe-issues.yml | Switches to WIF auth and adds script caps + improved Statsig logging inputs. |
| .github/workflows/backfill-duplicate-comments.yml | Pins Bun setup action by SHA. |
| .github/workflows/auto-close-duplicates.yml | Pins Bun setup action by SHA. |
| .claude/commands/triage-issue.md | Adds triage slash command instructions using restricted scripts. |
| .claude/commands/oncall-triage.md | Removes oncall triage slash command content. |
| .claude/commands/dedupe.md | Updates dedupe command to use the restricted scripts/gh.sh wrapper. |
| .claude-plugin/marketplace.json | Updates marketplace schema URL. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (!response.ok) { | ||
| if (response.status === 404) return {} as T; | ||
| const text = await response.text(); | ||
| throw new Error(`GitHub API ${response.status}: ${text}`); |
| const events = await githubRequest<any[]>(`${base}/events?per_page=100`); | ||
|
|
||
| const labeledAt = events | ||
| .filter((e) => e.event === "labeled" && e.label?.name === label) | ||
| .map((e) => new Date(e.created_at)) | ||
| .pop(); | ||
|
|
||
| if (!labeledAt || labeledAt > cutoff) continue; |
| const comments = await githubRequest<any[]>( | ||
| `${base}/comments?since=${labeledAt.toISOString()}&per_page=100` | ||
| ); | ||
| const hasHumanComment = comments.some( | ||
| (c) => c.user && c.user.type !== "Bot" | ||
| ); | ||
| if (hasHumanComment) { |
| xy, path = e[:2], e[3:] | ||
| if xy == "??": | ||
| untracked.add(path) | ||
| else: | ||
| tracked.add(path) | ||
| # Rename/copy entries are XY old\0new\0 — second NUL field is | ||
| # the origin path; consume it so it isn't misparsed as a new | ||
| # 2-char-status entry. | ||
| if "R" in xy or "C" in xy: | ||
| i += 1 | ||
| i += 1 |
| # ─── push-sweep reviewed-commit tracking ──────────────────────────────────── | ||
| # | ||
| # Repo-local (not session-local) record of which commits the commit-review | ||
| # hook has already reviewed, so the push-sweep can advance its diff base past | ||
| # the contiguous reviewed prefix and skip entirely when everything pushed was | ||
| # already covered. Lives under `.git/` (same precedent as CC's | ||
| # `.git/claude-trailers`) so it survives across sessions and is per-clone. | ||
| # | ||
| # Format: one line per reviewed sha, append-only: | ||
| # <40-hex-sha>\t<unix-ts>\t<pv>\t<vulns_found> | ||
| # | ||
| # The trailing columns are observability only — load reads just the sha set. | ||
| # GC keeps the last _REVIEWED_SHAS_CAP entries; the file is small (~64 bytes | ||
| # per line) so even at the cap it's ~32KB. | ||
|
|
||
|
|
| try: | ||
| state_dir = os.path.dirname(state_file) | ||
| if state_dir: | ||
| os.makedirs(state_dir, exist_ok=True) | ||
|
|
||
| with open(state_file, "w") as f: | ||
| json.dump(state, f) |
| lock_fd = None | ||
| try: | ||
| lock_fd = os.open(lock_file, os.O_RDWR | os.O_CREAT) | ||
| fcntl.flock(lock_fd, fcntl.LOCK_EX) |
|
|
||
| REPO="anthropics/claude-code" | ||
| BASE_ISSUE="" | ||
|
|
| - `./scripts/gh.sh` — wrapper for `gh` CLI. Only supports these subcommands and flags: | ||
| - `./scripts/gh.sh label list` — fetch all available labels | ||
| - `./scripts/gh.sh label list --limit 100` — fetch with limit |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4087798616
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| permissions: | ||
| issues: write |
There was a problem hiding this comment.
Grant checkout read access for the sweep
This workflow explicitly restricts GITHUB_TOKEN to only issues: write, so the later actions/checkout@v4 step has no contents: read permission to fetch the repository before running scripts/sweep.ts. Add contents: read here, as the other checkout-based workflows do, otherwise the scheduled/manual sweep can fail before enforcing lifecycle timeouts.
Useful? React with 👍 / 👎.
| permissions: | ||
| issues: write |
There was a problem hiding this comment.
Grant checkout read access for lifecycle comments
This workflow also narrows GITHUB_TOKEN to only issues: write, but it checks out the repo before running scripts/lifecycle-comment.ts. Without contents: read, actions/checkout@v4 can fail before any lifecycle label comment is posted, so labels such as needs-info/stale would not notify users about the timeout.
Useful? React with 👍 / 👎.
| (github.event_name == 'issue_comment' && !github.event.issue.pull_request && github.event.comment.user.type != 'Bot') | ||
| concurrency: | ||
| group: issue-triage-${{ github.event.issue.number }} | ||
| cancel-in-progress: true |
There was a problem hiding this comment.
Avoid cancelling the initial category triage
When a reporter adds a human comment shortly after opening an issue, this shared concurrency group cancels the still-running issues triage run and replaces it with the issue_comment run. The comment-mode prompt explicitly says not to add or remove category labels, so the issue can be left without the required bug/enhancement/question/invalid/duplicate label from the opened flow.
Useful? React with 👍 / 👎.
| - If the issue has `needs-repro` or `needs-info` and the missing information has now been provided, remove the label: | ||
| `./scripts/edit-issue-labels.sh --remove-label "needs-repro"` |
There was a problem hiding this comment.
Remove the matching lifecycle label
For a needs-info issue where the user supplies the requested information, the only concrete command shown here removes needs-repro instead, leaving needs-info in place and allowing the sweep to auto-close the issue despite the requested response being provided. The example should remove the actual satisfied label, or show both cases explicitly.
Useful? React with 👍 / 👎.
|
|
||
| const base = `/repos/${owner}/${repo}/issues/${issue.number}`; | ||
|
|
||
| const events = await githubRequest<any[]>(`${base}/events?per_page=100`); |
There was a problem hiding this comment.
Paginate label events before skipping closure
For long-running issues with more than 100 issue events before a lifecycle label is applied, this fetch only reads the first events page, so the later labeled event is never seen and labeledAt remains empty. Those issues will be skipped forever by the sweep even after the timeout expires unless the events API is paginated or queried via a source that includes the relevant label event.
Useful? React with 👍 / 👎.
| ) | ||
| if r.returncode == 0: | ||
| full_shas.append(r.stdout.strip()) | ||
| _append_reviewed_shas(repo_root, full_shas, vulns_found=len(vulns or [])) |
There was a problem hiding this comment.
Don’t mark failed reviews as covered
When the LLM review fails transiently (for example, the API call returns no result and analyze_code_security yields (None, [])), this still writes the commit SHA to sg-reviewed-shas. A later push sweep will treat that SHA as already reviewed and advance past it, so the commit never gets retried after the API recovers; only append the SHA after a successful review/no-finding response, not after an API failure.
Useful? React with 👍 / 👎.
No description provided.