Skip to content

Nightly triage: sweep unboarded issues onto the board and run the board audit #2463

Open

Description

@cliffhall

Problem

New issues reach the board only when a maintainer runs /issue-triage by hand, currently every day or two. Until then, a user-submitted issue has no card. It is invisible to the board, to its priority ordering and to every Status-filtered view, whether or not it is urgent. The same gap delays the dependabot and SDK-watch issues, which leave boarding to triage.

Goal

A nightly workflow that runs /issue-triage's pass 1 and its board audit, so that every open issue has a card by morning. Pass 2 (assigning a milestone, which is the approval) stays human, as the skill requires: "Pass 2 is a human judgment call and is never done unprompted."

Blocked on

#2462's credential. Board writes need org Projects write access, which GITHUB_TOKEN cannot hold. The GitHub App described there (Projects read & write, Issues read & write) covers this workflow too.

Design

The work splits into a deterministic half and a judgment half. That split, together with AGENTS.md's rule that no model runs in a write-capable job, gives the workflow the same three-job shape as sdk-watch.yml.

1. sweep — deterministic, holds the credential, no model.

2. analyze — the model, contents: read only.

  • Score each new v2 issue with the priority rubric (two axes plus the listed bonuses), and suggest a type label where the form did not determine one.
  • Return structured output only (--json-schema). Restrict the available tools (--tools, not only --allowedTools), give it no Bash and no WebFetch, and let it post nothing. These are the rules AGENTS.md lays down for sdk-watch's analysis job.
  • ⚠️ Issue bodies are untrusted, third-party input. Unlike sdk-watch, whose upstreams are first-party, anyone with a GitHub account can write the text this model reads. That is the case AGENTS.md says would change the trade-off: "Point the analysis at an upstream outside this org, or feed it third-party content, and the trade changes." So this job must not have ANTHROPIC_API_KEY readable through a tool it needs. Use workload identity federation, or a dedicated CI-scoped key with a spend cap, as AGENTS.md names for exactly this case. Decide which before building it.

3. apply — holds the credential, no model.

  • Validate every value analyze returned before writing it:
    • the Priority must be one of the four options
    • the type must be one of the five labels
    • each axis must be an integer from 1 to 5
    • the total must match its parts, recomputed here rather than trusted
  • Then set Priority and post the score comment in the format /issue-triage specifies.
  • A value that fails validation is left unset and reported, never coerced.

4. The board audit — report only.

  • Run the audit query from /issue-triage and fail the job when any check is non-zero, so drift becomes a red run instead of something the next manual pass has to notice.
  • ⚠️ Actions logs on this public repo are public, and the boards are private. The audit output (issue numbers per defect class, Status, Priority) and anything else read from the board must go only to a place with the board's own audience. Print counts only to the log, or write the detail somewhere private. The same applies to the sweep's own logging.

Out of scope

Acceptance

  • Scheduled workflow with sweep → analyze → apply (+ audit), permissions scoped per job as above, with tests pinning:
    • analyze's permissions and tool whitelist
    • that analyze contains no write step
    • that apply validates before it writes
  • Deterministic label/board classification in a scripts/*.mjs helper, with a sibling *.test.mjs, covering:
    • both forms
    • both boards
    • the already-milestoned → Todo exception
    • the incomplete-listing guard
  • Credential decision for the model key recorded (federation, or a dedicated capped key).
  • Nothing read from the private boards reaches a public log beyond counts.
  • /issue-triage and AGENTS.md updated to say the sweep runs nightly, and what is still manual (pass 2).

Activity

  1. added this to the v2.10.0 milestone on Sep 23, 2026
  2. added
    v2Issues and PRs for v2
    choreMaintenance: deps, build tooling, CI, cleanup — no user-facing behavior change
    on Sep 23, 2026
  3. cliffhall commented on Sep 23, 2026

    @cliffhall
    MemberAuthor

    Triage: Priority Medium (total 6)

    Board: #28, Status Todo, milestone v2.10.0.

  4. modified the milestones: v2.10.0, v2.9.0 on Sep 23, 2026
  5. cliffhall commented on Sep 23, 2026

    @cliffhall
    MemberAuthor

    Milestone moved to v2.9.0 (the milestone due dates were corrected). The priority is unchanged; the milestone bonus applies either way.

  6. removed this from the v2.9.0 milestone on Sep 24, 2026
  7. added this to the v2.10.0 milestone on Sep 30, 2026
  8. cliffhall commented on Sep 30, 2026

    @cliffhall
    MemberAuthor

    Blocked on #2544 (the GitHub App), which now has its own card. This issue is a sub-issue of tracker #2543, milestoned v2.10.0. Its separate open decision is unchanged: analyze reads third-party issue text, so the model's key must not be readable through a tool it needs (workload identity federation, or a CI-scoped key with a spend cap).

  9. modified the milestones: v2.10.0, v2.11.0, v2.12.0 on Oct 7, 2026
  10. cliffhall commented on Oct 20, 2026

    @cliffhall
    MemberAuthor

    Credential decision: no tools for the model, and the existing ANTHROPIC_API_KEY stays.

    This settles the open decision above, and with it the "credential decision recorded" item in Acceptance. No federation rule and no separate capped key are needed.

    Why. The risk this issue names is a model that can read the key "through a tool it needs". Unlike sdk-watch, triage does not need to read the repo. So analyze gets no tools at all:

    • A deterministic step in analyze collects each new card's issue title, body and form fields, plus the priority rubric from /issue-triage, and puts them into the prompt.
    • The model runs with --tools "": nothing reads files, no Bash, no WebFetch. It returns structured output (--json-schema) and nothing else.
    • With no tool, there is nothing that can read /proc/self/environ, so the key being in the environment is no longer reachable from the model.

    Everything else in the design is unchanged. That covers the three-job shape, analyze at contents: read, and apply checking every returned value and working out the total itself. It also covers the scan before the comment is posted.

    Checks for the implementing PR

    • Confirm that --tools "" really leaves the model with no tools under anthropics/claude-code-action at the pinned SHA. Do this with a real run, not by reading the docs. If it does not, deny every built-in tool by name and add a test that pins it.
    • Add a test pinning analyze's tool list as empty, next to the existing tests on permissions and on there being no write step.
    • Record the reason in AGENTS.md, next to the sdk-watch residual-channel paragraph. Whether this trade holds depends on the model having no tools; adding one back (Read included) re-opens this decision.
  11. modified the milestones: v2.12.0, v2.13.0 on Oct 20, 2026
  12. modified the milestones: v2.13.0, v2.14.0 on Oct 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    blockedchoreMaintenance: deps, build tooling, CI, cleanup — no user-facing behavior changev2Issues and PRs for v2

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions