Skip to content
Proud to collaborate with Microsoft for Startups

github.workflow-job-handle ​

Route workflow_job webhooks: in_progress → claim-watch; queued → runner.dispatch-from-job-queued; completed → dispatches runner.execution-stop-<backend> (which frees the cap slot when it lands; this step does not wait for it).

Handle a workflow_job event.

Three cases are handled:

  1. action=in_progress — signal runner.execution-claim-watch so it transitions from WATCHING to CLAIMED, confirming the runner registered with GitHub. For retry attempts (run_attempt > 1) the claim-watch ID includes the attempt number: claim_watch_{workflow_run_id}_{run_attempt}.

  2. action=queued (any run_attempt) — start runner.dispatch-from-job-queued with the webhook payload. The runners library picks the best matching active group (label superset, repository scope > org scope), honors max_count, and starts the backend-specific runner.execution-launch-*. Outcome (launched / no_match / cap_reached) is observable on the dispatch workflow's state_data; we don't surface failures here.

  3. action=completed — match the RunnerExecution by github_runner_name and call runner.execution-stop-<backend>. The stop workflow chains runner.scale-check-group on completion, which launches a fresh runner if there are still queued jobs needing the freed slot. This per-job handling is what lets a multi-job workflow_run drain its matrix without waiting for workflow_run:completed (which only fires once the entire run finishes).

If no claim-watch workflow exists for the run_id (e.g. pre-feature execution, or github_workflow_run_ref was unavailable at launch time), the missing workflow is silently ignored.

Inputs:

  • action: Job action (queued/in_progress/completed)
  • github_workflow_run_ref: GitHub workflow run reference the job belongs to
  • github_job_ref: GitHub workflow job reference
  • runner_name: Name of the runner that claimed the job (in_progress)
  • runner_labels: Labels requested for the job
  • run_attempt: Run attempt number (1 for first, >1 for retries)
  • repository_full_name: Full name "owner/repo"
  • organization_uuid: Public organization UUID

Outputs (terminal state_data):

  • claim_watch_signaled: True if claim-watch was successfully advanced
  • claim_watch_workflow_ref: Workflow reference of the signaled claim-watch (if any)
  • dispatch_started: True if a runner.dispatch-from-job-queued was PUBLISHED. The dispatch runs in the runners image, so this does not mean a runner group was picked -- follow dispatch_workflow_ref for that outcome.
  • dispatch_workflow_ref: Workflow reference of the dispatch (if any)
  • execution_stopped: True if a stop workflow was DISPATCHED for the runner. Not a confirmation that the runner stopped. The stop runs on its own workflow (it is owned by a per-cloud library this image does not carry, so it must be handed to the bus), and it may still be in flight — or have failed — while this field reads True. A runner reported here as stopped can therefore still be alive, still billing, and still holding its runner group's max_count slot. The pool reconcile nudged alongside this dispatch is what actually reclaims the slot; reconcile, not this field, is the source of truth for execution state (P5.T2).
  • stop_workflow_ref: Workflow id of that dispatched stop — poll it for the real outcome.
  • stopped_runner_execution_uuid: RunnerExecution the stop was dispatched for

Overview ​

PropertyValue
Workflow typeAtomic
LibraryApp-github
Version1.0

Input Schema ​

FieldTypeRequiredDefaultDescription
actionstringYes—Job action
github_workflow_run_refintegerNo—Workflow run ID
github_job_refintegerNo—Workflow job ID
runner_namestringNo—Runner name
runner_labelslistNo—Runner labels
run_attemptintegerNo—Run attempt number (1 = first, >1 = retry)
repository_full_namestringYes—Repository full name
organization_uuidstringNo—Organization UUID (public)
github_installation_refintegerNo—GitHub App installation reference — used to flush the LTIP queued-jobs cache so demand-aware reconcile refetches on the next tick
workflow_run_idstringNo—Parent DAG run ID

Output Schema ​

FieldTypeRequiredDefaultDescription
claim_watch_signaledbooleanNo—True if claim-watch was advanced
claim_watch_workflow_refstringNo—Reference of the signaled claim-watch
dispatch_startedbooleanNo—True if a dispatch was published to the runners image. Not proof a group was picked; follow dispatch_workflow_ref.
dispatch_workflow_refstringNo—Reference of the dispatch workflow
execution_stoppedbooleanNo—True if a stop was DISPATCHED — not a confirmation it stopped
warm_pool_handledbooleanNo—True if handled via warm-pool path
instance_transitioned_tostringNo—Terminal instance state for warm-pool path
stop_workflow_refstringNo—Workflow id of the dispatched stop; poll it for the outcome
stopped_runner_execution_uuidstringNo—UUID of the execution the stop was dispatched for
actionstringNo—Job action
github_workflow_run_refintegerNo—Workflow run ID
github_job_refintegerNo—Workflow job ID
runner_namestringNo—Runner name
runner_labelslistNo—Runner labels
run_attemptintegerNo—Run attempt number
repository_full_namestringNo—Repository full name
organization_uuidstringNo—Organization UUID (public)
github_installation_refintegerNo—GitHub App installation reference
workflow_run_idstringNo—Parent DAG run ID
failure_reasonstringNo——
failure_typestringNo——
failed_actionstringNo——
failed_at_statestringNo——
failed_stepstringNo——
failed_layerstringNo——
errorstringNo——
error_typestringNo——

States ​

StateInitialTerminalSuccessAuto-advanceDescription
pendingYesNo—execute—
completedNoYesYes——
failedNoYesNo——

State Diagram ​

Transitions ​

FromActionToDescription
pendingexecutecompleted—
* (any state)failfailed—

API Usage ​

bash
POST /api/workflows/start
Content-Type: application/json

{
  "workflow_type": "github.workflow-job-handle",
  "initial_data": {
    "action": "value",
    "repository_full_name": "value"
  }
}