Skip to content

feat(select-runner): admit issues event under generalized ancillary opt-in - #225

Merged
kyle-sexton merged 2 commits into
mainfrom
feat/select-runner-issues-event-optin
Jul 23, 2026
Merged

feat(select-runner): admit issues event under generalized ancillary opt-in#225
kyle-sexton merged 2 commits into
mainfrom
feat/select-runner-issues-event-optin

Conversation

@kyle-sexton

@kyle-sexton kyle-sexton commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

No related issue: no tracked issue exists for this gap — it is the residual of #224 (which scoped only comment/review events) and part of the 2026-07-22 private-repo CI/CD fleet-audit runner-routing remediation.

Related

The routing gap

#224 closed the hosted-fallback gap for issue_comment / pull_request_review / pull_request_review_comment via the per-caller admits-comment-events opt-in. It did not cover the issues event, which is in neither the unconditional LOCAL_EVENT_ALLOWLIST nor the opt-in set. So under self-hosted-only, a private-repo caller triggering on issues gets hostedResult(..., "hosted-only") on selector success and its work job runs on a GitHub-hosted (billable) runner — violating the $0-budget / 100%-self-hosted mandate for private repos.

Confirmed live consumer: medley issue-labeling.yml triggers on issues: [opened], routes through select-runner under ${{ vars.CI_RUNNER_POLICY }} (self-hosted-only), and its auto-label job is a pure actions/github-script job with zero checkout. Today it routes hosted and then trips its own require-self-hosted sentinel, failing the run.

The fix

Generalize the single opt-in to admit issues under the identical no-checkout, fork-safe, fail-closed gate the comment/review events already use:

  • Input admits-comment-eventsadmits-ancillary-events; admitted set → ANCILLARY_EVENTS = {issue_comment, pull_request_review, pull_request_review_comment, issues}.
  • The rename makes the name honest for the broadened, heterogeneous set (comment + review + issue-lifecycle). It is safe to rename now because a fleet-wide audit confirmed no caller sets the flag yet (the feat(select-runner): opt-in local routing for comment/review events #224 consumer opt-in is still deferred pending a medley repin), so there is zero breakage.
  • issues is strictly lower-risk than the events already admitted: it carries no PR head-ref, so the fork guard has nothing to protect; the caller's no-checkout attestation is the whole gate.

Fork-safety, fail-closed, public preservation (unchanged)

  • Fork-origin pull_request / pull_request_target still route hosted (FORK_GUARDED_EVENTS untouched).
  • self-hosted-only config errors still throw and yield the ci-runner-selection-failed sentinel — no hosted fail-open.
  • Public repos still route hosted regardless of event/opt-in.
  • The observer-token mint step widens for issues only inside the admits-ancillary-events-gated clause; the unconditional token-mint event set is unchanged (test-locked).

Blast radius

select-runner.yml is a shared reusable pinned by SHA across every repo (medley, github-iac, provisioning, dotfiles). This change is additive and default-off: callers that do not pass admits-ancillary-events: true are unaffected; public callers are unaffected. Because the input was renamed while still unadopted, any future caller adopts the new name directly.

Companion event-routing audit (all private repos) found this is the ONLY remaining gap event; github-iac, provisioning, and dotfiles have zero gap-class triggers. A separate, consumer-side gap remains out of scope here: ~84 || 'ubuntu-24.04' fail-open fallbacks in medley/github-iac/provisioning lanes (dotfiles already uses the correct || 'melodic-ubuntu-24.04-x64' / sentinel pattern) — tracked separately.

Consumer follow-up (not in this PR)

After this merges and cuts a tag, medley repins select-runner.yml and then adds admits-ancillary-events: true to issue-labeling.yml's selector call (and to comment-review-gate.yml for the comment/review events #224 enabled). Note the generalized flag: any caller setting it true now also admits issues under the same no-checkout attestation.

Verification

  • node --test .github/scripts/*.test.cjs251/251 (selector suite 116/116; +4 issues cases: opt-in true/false × prefer-self-hosted/self-hosted-only). The workflow_run negative test confirms a non-admitted event stays hosted even with the flag true.
  • node .github/scripts/render-select-runner-workflow.cjs --check — embedded block byte-in-sync with source.
  • actionlint, zizmor, typos, editorconfig-checker on all changed files — clean.
  • Independent fresh-context review — all invariants hold, no CRITICAL/IMPORTANT findings.

🤖 Generated with Claude Code

https://claude.ai/code/session_015rYiP56ofZpx7oCWPkJ6No

…pt-in

The comment/review opt-in added in #224 (admits-comment-events) closed
the hosted-fallback gap for issue_comment / pull_request_review /
pull_request_review_comment but left the `issues` event uncovered: it is
in neither the unconditional local allowlist nor the opt-in set, so a
private-repo caller triggering on `issues` under `self-hosted-only`
routes HOSTED (billable) on selector SUCCESS. Confirmed live consumer:
medley `issue-labeling.yml` (on: issues) whose auto-label job is a pure
github-script no-checkout job that then trips its own require-self-hosted
sentinel.

Generalize the single opt-in to admit `issues` under the identical
no-checkout, fork-safe, fail-closed gate:

- Rename input `admits-comment-events` -> `admits-ancillary-events` and
  the admitted event set to ANCILLARY_EVENTS = {issue_comment,
  pull_request_review, pull_request_review_comment, issues}. The name now
  honestly covers the issue-lifecycle event; safe to rename because no
  caller sets the flag yet (fleet audit confirmed zero adopters).
- `issues` is strictly lower-risk than the already-admitted events: it
  carries no PR head-ref, so there is nothing for the fork guard to
  protect, and the caller's no-checkout attestation is the whole gate.

Preserved unchanged: fork-origin PRs route hosted; self-hosted-only
config errors yield the ci-runner-selection-failed sentinel; public
repos route hosted; the observer-token mint surface widens for `issues`
ONLY inside the opt-in-gated clause, never as an unconditional route.

Regenerated the embedded selector block via
render-select-runner-workflow.cjs. Full suite 251/251 (selector 116/116,
+4 issues cases), actionlint / zizmor / typos / editorconfig-checker
clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015rYiP56ofZpx7oCWPkJ6No
@claude

claude Bot commented Jul 23, 2026

Copy link
Copy Markdown

Claude encountered an error after 0s —— View job


I'll analyze this and get back to you.

@claude

claude Bot commented Jul 23, 2026

Copy link
Copy Markdown

Claude encountered an error after 0s —— View job


I'll analyze this and get back to you.

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown

Warning

Automated security review did not complete — this is an infrastructure failure, not a review verdict.

Treat any Claude comment on this PR (including a placeholder like "I'll analyze this and get back to you") as incomplete, not "no findings."

Re-running the job, or pushing a new commit, will retry the review.

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown

Warning

Automated review did not complete — this is an infrastructure failure, not a review verdict.

Treat any Claude comment on this PR (including a placeholder like "I'll analyze this and get back to you") as incomplete, not "no findings."

Re-running the job, or pushing a new commit, will retry the review.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1e4a266d57

ℹ️ 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".

Comment thread .github/workflows/select-runner.yml
@kyle-sexton

Copy link
Copy Markdown
Contributor Author

🔒 babysit-prs lane claiming this PR for on-branch fix work this cycle (amendment-round: 16, safe tier). Will fix clear branch-owned findings and push; will not resolve threads or merge (safe tier).

…alias

Renaming the opt-in input to admits-ancillary-events is a breaking change
to the workflow_call input contract: a caller that repins to this
revision while still passing the former name (including anyone following
#224's merged guidance, which documented `admits-comment-events: true`)
would hit a hard unknown-input rejection rather than a hosted fallback.
The audit found zero current adopters, but out-of-tree and mid-flight
consumer branches cannot be verified away, and this reusable is pinned by
SHA across every repo.

Retain `admits-comment-events` as a deprecated alias, ORed with
admits-ancillary-events at the yml input boundary so either name opts a
caller in under both policies:

- Env passthrough feeds the single flag the selector reads from the OR of
  both inputs (`inputs.admits-ancillary-events || inputs.admits-comment-events`).
- The prefer-self-hosted observer-token mint gate admits either input.
- The tested select-runner.cjs core stays single-flag; the shim lives
  only in the yml input layer, so removing it later (once no caller sets
  it) reverts one file.

Tests: token-mint guard now locks both inputs and the OR'd env mapping; a
new test asserts the deprecated alias input remains declared. Full suite
252/252; render --check in sync; actionlint / zizmor / typos /
editorconfig-checker clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015rYiP56ofZpx7oCWPkJ6No
@claude

claude Bot commented Jul 23, 2026

Copy link
Copy Markdown

Claude encountered an error after 1s —— View job


I'll analyze this and get back to you.

@claude

claude Bot commented Jul 23, 2026

Copy link
Copy Markdown

Claude encountered an error after 1s —— View job


I'll analyze this and get back to you.

@kyle-sexton
kyle-sexton merged commit e77f012 into main Jul 23, 2026
41 checks passed
@kyle-sexton
kyle-sexton deleted the feat/select-runner-issues-event-optin branch July 23, 2026 00:28

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5e29f396ec

ℹ️ 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".

github.event.pull_request.head.repo.full_name != github.repository }}
API_TIMEOUT_SECONDS: ${{ inputs.api-timeout-seconds }}
ADMITS_COMMENT_EVENTS: ${{ inputs.admits-comment-events }}
ADMITS_ANCILLARY_EVENTS: ${{ inputs.admits-ancillary-events || inputs.admits-comment-events }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Limit the deprecated opt-in to its original events

When an existing caller has a multi-event workflow that already passes admits-comment-events: true for comment/review handling and also triggers on issues, this OR turns that old comment-only attestation into admission for every ANCILLARY_EVENTS member, including issues. On an issues run the selector now sees ADMITS_ANCILLARY_EVENTS=true and can return a self-hosted label even though the caller never opted its issue-triggered job into the new broader contract; keep the alias scoped to the three original comment/review events or pass both booleans separately.

Useful? React with 👍 / 👎.

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