Skip to content

fix(intake): admit listed humans by id and triage dispatched issues - #7924

Merged
kyle-sexton merged 4 commits into
mainfrom
fix/7886-intake-author-ids
Oct 18, 2026
Merged

kyle-sexton merged 4 commits into
mainfrom
fix/7886-intake-author-ids

Conversation

@kyle-sexton

@kyle-sexton kyle-sexton commented Oct 18, 2026 •

Copy link
Copy Markdown
Member

Refs: #7886

Summary

The intake caller moves to ci-workflows v0.49.0 (ff8cd36), which gates intake by author id (ci-workflows#773) and triages an issue named by a workflow_dispatch run (ci-workflows#774). v0.49.0 drops the OWNER/MEMBER admit rule, so this caller now passes the listed humans' ids explicitly.

Fix

  • check-born-triaged emits human-ids: the trusted-actor list's kind: human ids, comma-joined, and empty when the list is unreadable.
  • intake-triage.yml passes allowed-author-ids (those ids) and allowed-bot-sender-ids (41898282, github-actions[bot], plus the lanes App id when AUTOMATION_LANES_APP_SENDER_ID is set). It also gains a workflow_dispatch trigger with issue-number and passes that number through. A dispatch runs only from main, so the trusted-actor list, models block and AGENTS.md always come from the default branch.
  • The lanes App id stays in allowed-bot-sender-ids. This is the owner's decision: issues the lanes App opens get intake.
  • The reusable is pinned to ff8cd36321423de961381e608e4a31563e74adb3 # v0.49.0. The repin also brings in v0.46.0 through v0.48.0. git diff --stat v0.45.0 v0.48.0 touches only pr-review.yml and a compose-args test, so none of those changes reach this caller. v0.49.0 changes intake-triage.yml and nothing that file uses (report-lane-outcome is unchanged).

Verification

  • node --test .github/actions/check-born-triaged/check-born-triaged.test.mjs .github/actions/resolve-config/callers.test.mjs: 31 pass, 0 fail, including the dispatch guard, the id wiring and the bot id list.
  • actionlint and zizmor --offline on the touched workflow and action: clean.
  • runner-policy.mjs --root .: passes after merging main, which carries the standards sync #8058 of the reviewed intake-triage contract (standards#802).
  • Not run live yet. Intake triage skips every issue: repin the author-id gate and add dispatch intake #7886 holds the post-merge checklist: the opened path, the dispatch path and the skipped-job path.

Related

🤖 Generated with Claude Code

https://claude.ai/code/session_019bQ8pL7fJgVy7YjQ7wK1Zw

kyle-sexton and others added 2 commits October 17, 2026 22:19
The reusable intake lane's author gate read author_association, which a
webhook reports as CONTRIBUTOR for the org owner's private membership,
so every intake run skipped triage. Pass allowed-author-ids from the
trusted-actor list's human ids, which check-born-triaged now emits.

Add workflow_dispatch with an issue-number input, since issues a job's
GITHUB_TOKEN files start no issues run. github-actions[bot] (41898282)
and the lanes App are admitted as dispatching bots.

Pin is a save point on an unreleased ci-workflows commit; repin to the
release that carries ci-workflows#773 and #774 before opening the PR.

Refs: #7886

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019bQ8pL7fJgVy7YjQ7wK1Zw
kyle-sexton added a commit to melodic-software/standards that referenced this pull request Oct 18, 2026
)

Refs: melodic-software/claude-code-plugins#7924

## Summary

Registers ci-workflows v0.49.0 `intake-triage.yml`
(`ff8cd36321423de961381e608e4a31563e74adb3`) in runner policy.
claude-code-plugins#7924 repins its intake caller to that SHA, and its
runner-policy gate fails today with `runner-target-contract: ... no
reviewed runner-input contract (auto-approval declined: inputs changed
since the previously reviewed ...intake-triage.yml@182aca3...)`.

**Owner sign-off pending.** The README rollout paragraph says "sign-off
pending"; replace that with "signed off by hand on <date>" when you sign
it off, then mark this ready.

## Fix

- `components/runner-policy/policy.json`: a new
`intake-triage.yml@ff8cd36` entry that copies the v0.45.0 entry
(`3c8a4b2`) and adds three inputs to `allowedInputs`: `issue-number`,
`allowed-author-ids` and `allowed-bot-sender-ids`. Secrets
(`CLAUDE_CODE_OAUTH_TOKEN`) and caller permissions (`contents: read`,
`issues: write`) are unchanged.
- `components/runner-policy/README.md`: the rollout paragraph for
v0.49.0, sign-off pending.
- `components/runner-policy/runner-policy.test.mjs`: a test that the
v0.49.0 entry is exactly the v0.45.0 entry plus those three inputs, and
that it is the only v0.49.0 key.

### Input diff, v0.45.0 to v0.49.0 `workflow_call`

Read from both files at their SHAs (ci-workflows#773 and #774 are the
only intake commits in the range):

| Input | v0.45.0 | v0.49.0 | Registered here |
|---|---|---|---|
| `issue-number` | absent | added | yes |
| `allowed-author-ids` | absent | added | yes |
| `skipped-label` | absent | added | no: no caller passes it (same rule
v0.45.0 applied to `allowed-bot-sender-ids`) |
| `allowed-bot-sender-ids` | declared, unregistered | declared | yes:
the #7924 caller now passes it |

The other seven inputs, the outputs (`triage-failed`, `failure-class`)
and the one secret are unchanged.

### Security note

- Each new input is a numeric-id allowlist or an issue number.
`allowed-author-ids` and `allowed-bot-sender-ids` are comma-separated
numeric user ids. The job `if:` rejects an empty list and a leading,
trailing or doubled comma, then matches `,<id>,` exactly. `issue-number`
is checked against `^[1-9][0-9]{0,9}$` in a "Resolve the issue" step,
read from the API, and refused when it is a pull request.
- The OWNER or MEMBER `author_association` gate is replaced by the id
allowlist. An `opened` issue runs only when its author opened it and the
author's id is listed (a bot's sender id in the bot list).
- The `workflow_dispatch` path is admitted only for an actor whose
`github.actor_id` is in one of the two lists. Only a listed dispatching
bot's login reaches `allowed_bots`.
- A new `skipped` job (`issues: write`, inside the caller's grant) posts
a notice and, when `skipped-label` is set, applies that label after
checking it exists. No secret reaches it.
- No secret, `runs-on` routing or caller-permission change.

## Verification

- Runner-policy tests: `ℹ tests 314`, `ℹ pass 314`, `ℹ fail 0`,
including `✔ intake-triage at the v0.49.0 tag copies its v0.45.0 entry
and adds only the author-gate inputs`.
- `node components/runner-policy/runner-policy.mjs --root .` in this
repository: `Runner policy passed.`
- Against the claude-code-plugins#7924 worktree
(`fix/7886-intake-author-ids`, `--repository-visibility public`):
- with its current vendored policy:
`.github/workflows/intake-triage.yml#intake-triage:
runner-target-contract: the reusable workflow path@SHA has no reviewed
runner-input contract (auto-approval declined: inputs changed since the
previously reviewed
melodic-software/ci-workflows@182aca3)`
- with `--policy` set to this branch's `policy.json`: `Runner policy
passed.`
  - with this branch's `runner-policy.mjs`: `Runner policy passed.`
  - That worktree was not modified (`--policy` flag, no file copy).
- Pre-commit hooks: typos, editorconfig, markdownlint, gitleaks and
biome clean.

## Related

- claude-code-plugins#7924 passes its runner-policy gate once this
merges and the policy syncs to that repository.
- Precedent: #797 (v0.45.0 entries).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
kyle-sexton and others added 2 commits October 18, 2026 03:25
A workflow_dispatch from another branch would read that branch's
trusted-actor list, models block and AGENTS.md. The born-triaged gate
now runs only for an issues event or a dispatch on refs/heads/main;
skipped, it leaves claude-args empty and intake does not run.

Also corrects the failed-gate text (the reusable now turns away every
person when human-ids is empty), quotes the human-ids output
description, and tests the id wiring, the bot id list and a
wrong-version trusted-actor list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kyle-sexton
kyle-sexton marked this pull request as ready for review October 18, 2026 07:30
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-18T07:31:55.130073Z 6fa815c Draft marked ready
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions

Copy link
Copy Markdown
Contributor

claude-security-review reviewed 6fa815c1bc820898726be576fb1369c1244dbf71: 0 finding(s), 0 as inline comments.

Security review: PR #7924 (head 6fa815c)

No security findings.

This change lets the intake lane admit listed humans by id and adds a workflow_dispatch entry point to intake-triage.yml. I read the full diff and the final intake-triage.yml and action.yml. I did not read the reusable workflow at ci-workflows v0.49.0, so its admission behavior is taken from the comments in this diff.

  • Dispatch from a branch. The born-triaged job now has if: github.event_name == 'issues' || github.ref == 'refs/heads/main'. On a dispatch from another ref that job is skipped. Its outputs are then empty, so claude-args is empty and the intake-triage job does not run. A branch-modified workflow therefore cannot run intake with its own trusted-actor list, models block or AGENTS.md, and it never reaches the secrets.
  • Injection. issue-number reaches the script only through an env: variable. The script accepts it only if it matches ^[1-9][0-9]*$, and the test for 7; echo confirms a bad value is rejected. LANES_APP_ID is also passed through env:. The bot-id step keeps it only if it is all digits.
  • Trusted ids. humanIds keeps only kind: human entries whose id is a positive integer. A list that cannot be read or has the wrong version gives an empty string, so the reusable turns away every person.
  • Bot admission. Listing github-actions[bot] (41898282) lets a dispatch sent with a repository GITHUB_TOKEN through. That is intended and limited to workflows in this repository. The comment correctly notes that an issue opened with a GITHUB_TOKEN starts no issues run, so the id does not admit issue events.
  • Residual trust. A dispatcher on the allowlist can ask for any issue number to be triaged, including one an outsider opened. That is by design: the dispatcher is trusted, and the lane's own untrusted-content handling covers the issue text. Nothing in this diff changes that.
  • Permissions. They are unchanged: permissions: {} at the top, contents: read and issues: read for the gate, and issues: write for the lane.
  • Pin. The reusable moves from v0.45.0 to v0.49.0, still pinned to a full SHA. Pin changes are zizmor's lane, so I did not review them further.

@github-actions

Copy link
Copy Markdown
Contributor

claude-review reviewed 6fa815c1bc820898726be576fb1369c1244dbf71: 0 finding(s), 0 as inline comments.

No findings on PR #7924 at 6fa815c.

What I checked

  • check-born-triaged.mjs: humanIds(list) returns an empty string when the list is unreadable. It keeps only kind: human actors with a positive integer id. main() computes it before the issue read, so human-ids is still emitted when the issue number is invalid or the issue is unreadable. writeOutputs handles the hyphenated key, and the CLI test regex now accepts it.
  • action.yml and the README add the human-ids output and document the new fall-through: a failed gate leaves skip and human-ids empty, so the reusable turns away every person.
  • intake-triage.yml:
    • The born-triaged job's if runs for issues events and for dispatches from refs/heads/main. A dispatch from another ref skips the job. intake-triage then also skips, because its claude-args guard is empty.
    • The gate takes issue-number from github.event.issue.number || inputs.issue-number.
    • The bot-id step composes 41898282 plus the lanes App id only when it matches ^[1-9][0-9]*$.
    • The concurrency group falls back to inputs.issue-number.
  • The reusable is repinned to ff8cd363… (v0.49.0), and the runner-policy policy.json already has an entry for that SHA.
  • The other consumer of the action, intake-label-needs-triage.yml, reads only skip, so the extra output does not affect it.
  • The new tests in callers.test.mjs have their imports (spawnSync, mkdtempSync and the rest). The evaluate helper supports the || and == operators the new if uses.

Not verified

  • I could not run node --test in this session because the sandbox required approval. The "31 pass" figure is the author's claim and I did not confirm it.
  • Whether the reusable (ci-workflows v0.49.0) checks the issue author on a dispatch is outside this diff and not confirmed. If it does not, a GITHUB_TOKEN workflow, listed as an admitted bot by id 41898282, could dispatch intake for an issue an untrusted person opened. That is a question for the security lane and the reusable, not this diff.

Closing issue
The PR uses Refs: #7886, not a closing keyword, and the context file has no closing-issue section, so there were no acceptance criteria to check.

@kyle-sexton
kyle-sexton added this pull request to the merge queue Oct 18, 2026
Merged via the queue into main with commit 6498194 Oct 18, 2026
69 of 81 checks passed
@kyle-sexton
kyle-sexton deleted the fix/7886-intake-author-ids branch October 18, 2026 08:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant