---
name: gh-run-status-field-freezes
description: "gh run view --json status can read `queued` while jobs are executing — the run-level field froze at createdAt; and startedAt is populated on queued jobs, so it is not an execution-start signal"
metadata: 
  node_type: memory
  type: feedback
  originSessionId: c894998a-574a-4157-8d2f-f28105092845
  modified: 2026-08-03T14:26:52.912Z
---

**Watching a golden run for a hold lift: poll `--json jobs`, NEVER `--json status`.** Two separate
instrument traps in the same document, both measured by me at exit 0 on golden run `30509109149`
@`d26a2b2` (2026-07-30, `gh run view --repo BigscreenVR/spt-bs-core`), after chert flagged the first:

## ⭐⭐ TRAP 1 — the run-level `status` never updated at all
`--json status,conclusion,updatedAt,createdAt` returned
`{"status":"queued","conclusion":"","createdAt":"2026-07-30T02:40:38Z","updatedAt":"2026-07-30T02:40:38Z"}`
while `--json jobs` showed **changes=completed/success, traceability=completed/success, both test legs
in_progress**. ⭐ **`updatedAt` == `createdAt` to the second** — the run-level document had not been
touched since creation despite two jobs completing. So this is not lag: the field is not tracking its
jobs. A poller reading `status` would report "queued, nothing started" indefinitely, at exit 0, with a
timestamp that *looks* like a fresh measurement. **`updatedAt == createdAt` is the tell** — check it
whenever a run-level field looks implausibly static.

## ⭐⭐ TRAP 2 — `startedAt` is present on QUEUED jobs, so it orders nothing
Same document: `n1-gate (…, kitsubito)` **status=queued** with **startedAt=02:40:48Z**, *earlier* than
the `in_progress` Windows test leg's 02:40:50Z. ⇒ **`startedAt` is not "began executing"** and cannot
date when a leg actually started, nor order legs against each other.
⚠ **This killed a sharper claim I was one step from sending.** I was about to correct doyle with "the
test legs started 12s after run creation, so 'test not started' was already false when you read it" —
built entirely on reading `startedAt` as execution start. **Wrong instrument, and the refutation was
sitting in the same output I had already printed.** chert's more generous "true when you read it"
stands; mine would have been a confident correction resting on a field that does not mean what its
name says. See [[a-predicate-without-its-tool-is-not-evidence]] and
[[verdict-from-probe-competence]] — and note this one was self-inflicted at the *reading* step, not
the tool-choice step.

## ⭐⭐ TRAP 3 — `.conclusion // "fallback"` NEVER fires in jq (doyle, and it fired in MY command)
**gh emits `""`, not `null`, for an incomplete job's `conclusion`.** jq's `//` only falls through on
`null`/`false`, so `""` passes straight through and **every running job renders BLANK where you expect
your fallback string.** doyle nearly opened a false "runner online but deaf" incident on the
combination of this and Trap 1.
⚠ **I ran `--jq '… \(.conclusion // "-") …'` in the very measurement above and it silently did nothing** —
the in_progress rows printed two adjacent tabs, not `-`. I read past my own dead fallback because I had
printed `.status` in the same row. ⭐ **Both doyle and I were rescued by a redundant field, not by
noticing the defect** (his: runners showed `busy=true`). Print `status` AND `conclusion` side by side and
let them cross-check; a fallback you never saw render is not a fallback. Use
`(.conclusion // "" | if . == "" then "-" else . end)` if you want a real placeholder.

## ⚠ SCOPE — this is a `gh run view` rule, and `gh run list` does NOT take `jobs`
Measured 2026-08-03 (todlando raised it, I re-ran it): `gh run list --limit 1 --json jobs` exits with
**`Unknown JSON field: "jobs"`** — `jobs` is a `gh run view` field only. So at the LIST level `status`
is the field that exists and is the right one for picking WHICH run to look at; the "never `--json
status`" rule above is about the per-RUN document, where the rollup froze. Stating the rule without
naming the verb is what made this ambiguous — a builder reading it as universal gets a hard error and
no explanation.

**How to apply:** for every hold-lift / golden watch, one command —
`gh run view <id> --repo <r> --json jobs --jq '.jobs[] | "\(.name)\t\(.status)\t\(.conclusion // "-")"'`.
Report `status`+`conclusion` per job and nothing else. Never quote a run-level rollup as run progress,
and never quote `startedAt` as evidence of when work began. Related:
[[verdict-from-exit-not-from-silence]] · [[measure-what-costs-one-command]] ·
[[intermittent-green-is-zero-information]].
