autofeat docs
Reference

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 ```.

FieldMeaning
schema_versionLiteral 2.
repoowner/name, or null.
prPull request number, or null.
sigSignature over the document, or null.
revisionInteger >= 0.
claimCurrent lease, or null. See below.
startedMap of run id to a start record.
completedMap of run id to a completion record.
historyList of audit events.
legacy_uncertain_idsRun ids copied from schema v1. They prove neither execution nor completion.
stateOne of the values below.
turnInteger >= 0.
last_run_idLatest run id, or null. The table prints - when null.
seen_run_idsIds already observed.
ledgerCumulative spend. See below.
head_shaHead that this state belongs to, or null.
base_shaBase commit, or null.
policy_shaDefault-branch commit whose config judged the run, or null.
base_refBase ref name, or null.
updated_atTimestamp.

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

FieldDefaultMeaning
usd0.0Cumulative estimated or measured USD.
tokens0Cumulative tokens.
minutes0.0Cumulative minutes.
runs0Incremented by 1 on each accumulate.
alert_firedfalseTrue after budget.alert_usd was reached. Stays true.
usd_unmeasuredfalseUSD was not measured for a run.
usd_estimatedtrueUSD is an estimate, not an invoice.
no_progress_streak0Turns 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

ValueMeaning
queuedAdmitted, not started.
planningPlan action in progress.
speccedPlan finished.
implementingImplement action in progress.
testingTest action in progress.
reviewingReview action in progress.
gatingGate action in progress.
waiting_for_authorTurn finished. Waiting for a new head. Not terminal.
approvedApprove-only mode approved the pull request.
mergingMerge in progress.
readyStopped before merge. The autofeat:ready label is the human signal.
mergedMerged. Terminal.
doneSchema terminal. A v1 document that said done migrates to ready. The merge path records ready or merged.
haltedHalted. Terminal.
failedLast turn failed. Not terminal. Same head does not retry unless the failure was infra.
budget_exceededA 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 outcomeCheck conclusion
succeededsuccess
dry_runneutral
waiting_for_authorneutral
waiting_for_checksneutral
refusedfailure
crashedfailure
stoppedcancelled

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 .message

The 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.

On this page