Skip to content

refactor(agent-workflows): pick the runner inside the gate job - #30

Merged
azlekov merged 1 commit into
mainfrom
refactor/agent-workflows-pick-inside-gate
Sep 12, 2026
Merged

azlekov merged 1 commit into
mainfrom
refactor/agent-workflows-pick-inside-gate

Conversation

@azlekov

@azlekov azlekov commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

What changed

Every agent workflow ran three hosted job starts before the agent step: gate,
then a pick-runner job (a call to the reusable pick-runner.yml), then the
agent job on the picked runner. Each hosted start bills a full minute, and the
picker itself does nothing gate couldn't host alongside it.

This folds the picker into the gate job as a step, using the composite action
actions/pick-runner (merged earlier today) directly instead of the reusable
pick-runner.yml wrapper. The picker step runs after the last step that decides
gate's proceed output, guarded by if: <that step>.outputs.proceed == 'true',
so it never runs for an event that will not proceed. gate gains a runner
output, and the agent job (triage, implement, assist, review, build in
deps-verify) reads runs-on: ${{ fromJson(needs.gate.outputs.runner) }} and drops
pick-runner from needs:.

Applies to issue-triage.yml, issue-implement.yml, claude-assist.yml,
pr-review.yml, deps-verify.yml.

Per workflow, the step guarding proceed comes from:

  • issue-triage.yml: steps.bots.outputs.proceed (covers the workflow_dispatch
    no-issue-number no-op path added earlier today, verified below)
  • issue-implement.yml: steps.check.outputs.proceed
  • claude-assist.yml: steps.gate.outputs.proceed
  • pr-review.yml: steps.level.outputs.proceed (narrows the gate's own verdict
    further: no changed files, docs only)
  • deps-verify.yml: steps.gate.outputs.proceed

gate's timeout-minutes moves from 5 (8 for pr-review) to 10, since the
picker calls the org runner API and may retry.

AGENTS.md's row for this rule is rewritten: the picker now runs inside the
gate job, after agent-gate, conditioned on proceed, so it cannot run for an
event that will not proceed, and it still never precedes the kill switch.
README.md's "Runners" section and the agent:implement field-comparison line
are updated to match; "When nothing happens" did not mention the picker job and
needed no change.

self-test.yml gains an assertion that every one of these five workflows picks
its runner as a step inside gate, guarded on a proceed-named output, with no
standalone pick-runner job left behind.

Status check contexts

<caller job> / pick-runner / Pick runner disappears from these five workflows,
since pick-runner is no longer a job of its own there. Nothing in this repo or
its consumers requires that context by name.

Verification

  • actionlint on all six changed workflow files: clean (only the two documented
    create-github-app-token ignores apply, and neither fired).
  • bash actions/agent-gate/test.sh: pass=30 fail=0.
  • bash actions/sticky-comment/test.sh: pass=10 fail=0 (unaffected by this
    change, run for completeness).
  • Self test on this PR: https://github.com/dodi-smart/.github/actions/runs/34698706699 — success.
  • Live exercise against the reference consumer (a private repo in this org):
    pointed its pr-review.yml and triage.yml callers at this branch on a
    throwaway branch and draft PR, adding a workflow_dispatch issue-number
    input to triage.yml for the dispatch test (throwaway change, not part of
    this PR).
    • Marked the throwaway PR ready for review to trigger a real pr-review
      run: run 34698942244, conclusion failure. The job list showed
      exactly one hosted job (gate, which includes the pick step) before
      review started on the picked runner — the fold-in worked. The run then
      failed for a documented, pre-existing reason unrelated to this change:
      claude-code-action self-skips whenever a PR changes a workflow file
      (this throwaway PR necessarily did), and the existing guard step correctly
      turns that skip into a failing job rather than a false green.
    • Dispatched triage with no issue-number on this branch: run
      34698826193, conclusion success — clean no-op, gate ran and
      triage was skipped, no pick step or picker job executed.
    • Dispatched triage with a throwaway issue number: run 34698804159,
      conclusion failure. Job list: exactly gate then triage, no
      standalone picker job — the agent job actually ran on the picked runner.
      It failed on the pre-existing "Verify triage recorded its state" check
      (the agent did not set Triage state on this throwaway issue), unrelated
      to this change.
    • Confirmed via gh run view --json jobs on all three runs above that each
      had exactly one hosted job (gate) before the agent job, never two.
    • Cleaned up: closed the throwaway PR (branch auto-deleted) and the
      throwaway issue.

Internal references stay pinned at @v1, and actions/pick-runner merged to
main earlier today, so no temporary re-pointing of the action itself was
needed — only the reference consumer's caller uses: pointed at this branch for
the exercise above.

Not verified

Could not exercise a real (non-self-skipped) claude-code-action agent comment
on pr-review in the live exercise, because any PR that points a caller's
uses: at a branch necessarily changes a workflow file, which makes
claude-code-action skip itself by design until the change reaches the default
branch. The infra-level behavior (gate picks the runner, exactly one hosted job,
agent job dispatched) is confirmed regardless.

🤖 Generated with Claude Code

Fold the runner picker into each agent workflow's gate job, as a step run
after actions/agent-gate and guarded on the same proceed output the agent
job already checks. A proceeding run now costs one hosted job start
instead of two: previously gate and pick-runner each billed a full
hosted minute before the agent job even began.

Applies to issue-triage.yml, issue-implement.yml, claude-assist.yml,
pr-review.yml and deps-verify.yml. Each drops its standalone pick-runner
job, adds a runner output to gate, and points its agent job's runs-on at
needs.gate.outputs.runner instead of needs.pick-runner.outputs.runner.
gate's timeout-minutes moves from 5 (8 for pr-review) to 10, since the
picker calls the org API and may retry.

issue-triage.yml's workflow_dispatch no-op path (added earlier today)
already sets proceed=false before the pick step, so a dispatch with no
issue-number still skips the picker.

self-test.yml gains an assertion that every agent workflow picks its
runner as a step inside gate, guarded on a proceed output, with no
standalone pick-runner job left behind. AGENTS.md and README.md are
updated to describe the new shape.

Status-check contexts: '<caller job> / pick-runner / Pick runner'
disappears for these five workflows, since pick-runner is no longer a
job of its own there.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@azlekov
azlekov merged commit 4391e9a into main Sep 12, 2026
1 check passed
@azlekov
azlekov deleted the refactor/agent-workflows-pick-inside-gate branch September 12, 2026 14:51
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 1.6.1 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

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