State schema
RunState fields, check-run conclusions, and how to read the journal ref.
Two copies of the same run exist. The sticky pull-request comment is what you read in the browser. The journal git ref is the durable copy. They should match. When the ref is missing, only an operator reconcile can adopt the sticky. See Reconcile.
RunState
Schema version is 2. The payload is JSON inside an HTML comment, after the marker <!-- autofeat:state -->. The fence regex is a json object between ```json and ```.
| Field | Meaning |
|---|---|
schema_version | Literal 2. |
repo | owner/name, or null. |
pr | Pull request number, or null. |
sig | Signature over the document, or null. |
revision | Integer >= 0. |
claim | Current lease, or null. See below. |
started | Map of run id to a start record. |
completed | Map of run id to a completion record. |
history | List of audit events. |
legacy_uncertain_ids | Run ids copied from schema v1. They prove neither execution nor completion. |
state | One of the values below. |
turn | Integer >= 0. |
last_run_id | Latest run id, or null. The table prints - when null. |
seen_run_ids | Ids already observed. |
ledger | Cumulative spend. See below. |
head_sha | Head that this state belongs to, or null. |
base_sha | Base commit, or null. |
policy_sha | Default-branch commit whose config judged the run, or null. |
base_ref | Base ref name, or null. |
updated_at | Timestamp. |
claim: run_id, owner, claimed_at, expires_at.
started[run_id]: owner, at, head_sha, reserved (a ledger), admission_checkpointed.
completed[run_id]: at, outcome (object), published, next_run_id.
history[]: at, event, run_id, owner, head_sha.
Ledger
| Field | Default | Meaning |
|---|---|---|
usd | 0.0 | Cumulative estimated or measured USD. |
tokens | 0 | Cumulative tokens. |
minutes | 0.0 | Cumulative minutes. |
runs | 0 | Incremented by 1 on each accumulate. |
alert_fired | false | True after budget.alert_usd was reached. Stays true. |
usd_unmeasured | false | USD was not measured for a run. |
usd_estimated | true | USD is an estimate, not an invoice. |
no_progress_streak | 0 | Turns with no progress. |
The sticky table prints usd=, tokens=, minutes=, and runs=. It does not print the other ledger fields. Those are in the JSON.
Evidence for gate and merge is stored under the reserved key autofeat:evidence:v1.
States
| Value | Meaning |
|---|---|
queued | Admitted, not started. |
planning | Plan action in progress. |
specced | Plan finished. |
implementing | Implement action in progress. |
testing | Test action in progress. |
reviewing | Review action in progress. |
gating | Gate action in progress. |
waiting_for_author | Turn finished. Waiting for a new head. Not terminal. |
approved | Approve-only mode approved the pull request. |
merging | Merge in progress. |
ready | Stopped before merge. The autofeat:ready label is the human signal. |
merged | Merged. Terminal. |
done | Schema terminal. A v1 document that said done migrates to ready. The merge path records ready or merged. |
halted | Halted. Terminal. |
failed | Last turn failed. Not terminal. Same head does not retry unless the failure was infra. |
budget_exceeded | A budget.* ceiling was crossed. Terminal until the ceiling changes or an operator reconciles. |
Terminal set: done, merged, halted, budget_exceeded. No transition leaves a terminal state. Removing a label does not.
Schema v1 migration, when schema_version is absent: set version to 2, rename waiting_author to waiting_for_author, rename done to ready, and if ledger.runs is non-zero copy seen_run_ids into legacy_uncertain_ids.
Check runs
Name: autofeat/<action>. Conclusions:
| Stage outcome | Check conclusion |
|---|---|
succeeded | success |
dry_run | neutral |
waiting_for_author | neutral |
waiting_for_checks | neutral |
refused | failure |
crashed | failure |
stopped | cancelled |
A blocking budget alert forces failure regardless of the stage outcome.
review_only publishes autofeat/review, autofeat/gate, and autofeat/merge. Do not list any autofeat/* name in checks.required_checks. That is a self-wait and the config is invalid.
Read the journal
Replace OWNER, REPO, and N.
gh api "/repos/OWNER/REPO/git/ref/heads/autofeat-state/pr-N" --jq .object.sha
gh api "/repos/OWNER/REPO/git/commits/SHA" --jq .messageThe commit message is the sticky body, including the marker and the JSON. A 404 on the ref means the pull request never ran, or the ref was deleted. GitHub cuts a commit message at 65536 characters. Recovery is autofeat reconcile prepare --adopt-truncated and then apply, operator only.
The ruleset autofeat-state-refs targets refs/heads/autofeat-state/** and blocks deletion and non-fast-forward updates.