---
name: in-flight-run-logs-are-gated-read-steps-and-annotations
description: "gh run view --log-failed refuses while a run is in_progress, but the RAW jobs endpoint serves a completed job's full log mid-run — one CLI's refusal is not the data being unavailable."
metadata: 
  node_type: memory
  type: reference
  originSessionId: 96b2ffa6-824d-4728-90df-e6fc882aa798
  modified: 2026-08-19T11:22:05.692Z
---

Measured 2026-08-19 triaging two reds in golden run 32243884768 while its twohost jobs were still running.

`gh run view -R <repo> --job <id> --log-failed` refuses with `run <run-id> is still in progress; logs will be available when it is complete` — **even for a job that has itself already CONCLUDED failure**. That refusal is a property of the `run view` command, NOT of log availability.

**The log IS available. Use the raw jobs endpoint:**
```
gh api repos/<owner>/<repo>/actions/jobs/<jobid>/logs > job.log
```
Served the complete 511KB log of a completed job while the run was still `in_progress`. I nearly filed "logs are gated until the run completes" as a fact and deferred the whole discriminator to run-end; doyle handed me this route and the triage closed immediately. **A tool refusing is evidence about the tool. Before concluding data is unavailable, try the layer under the CLI.** See [[empty-inbound-is-never-material]], [[filters-that-read-as-absent]].

Other in-flight surfaces, and what each settles:
- `gh api repos/<o>/<r>/actions/jobs/<jobid> --jq '.steps[] | "\(.number) \(.conclusion) \(.name)"'` — per-STEP conclusions. Enough to say *which leg* died ("both nextest phases green, only the clippy step failed ⇒ not a test victim"). Not enough for any assertion text.
- `gh api repos/<o>/<r>/check-runs/<jobid>/annotations` — failure line + `Process completed with exit code N` (101 = clippy `-D warnings`/assertion, 1 = terminated), plus warnings worth naming.
- The **source at the head sha**, read locally — a DETERMINISTIC defect is provable from the tree with no log at all (ungated `use` at :10, both call sites under `#[cfg(windows)]`). Only a nondeterministic one needs the log.

**Step-table traps:**
1. Every step AFTER the failing one reads `skipped`. `skipped` did NOT run and did NOT pass — never score the sibling-platform leg (e.g. "Clippy — windows: skipped") as evidence about that platform. Only cfg/source structure says that; carry the claim as cfg-derived, not run-derived.
2. `skipped` has TWO causes in one table — matrix-gated (by design, both jobs list both OS variants) and post-failure. Read step NUMBER against the failing step before concluding which.

**A same-file red is not a same-cell red.** The failing panic line landed 51 lines above the cell everyone expected; the expected cell had PASSED in the same run. Map panic line → enclosing `fn` (grep `^fn ` boundaries) and diff that range against the suspect commit's `@@` hunks before attributing. Same discipline clears a fix by WRITE-SET: the family-B fix touched `sid_after`, the failing predicate was `spawned` from an untouched 45s poll — non-intersecting write-sets beat "it's a different face" as an exoneration.

## SECOND INSTANCE, 2026-08-24 — and I authored the first, with the same specimen

Golden v0.62.0 run 32790838399 @`5a9f9bdc`, Linux test leg job 97631953443. I used the raw-jobs route
above correctly (the CLI refused mid-run, exactly as documented), read the clippy step's log, and then
wrote into an RCA sent to the gate authority: **"nothing else in the log; no test ever ran."** False.
deployah checked the steps API: step 18 Test Phase A linux SUCCESS, 20 Phase B linux SUCCESS, 22
Doctests linux SUCCESS, and the clippy red at 24 was the FIRST failure in the job. I had the step
ledger in hand — my own earlier query had listed the non-success steps, and 18/20/22 were absent from
it, i.e. green. I asserted against data I had already pulled.

⭐⭐ **The sentence that did it: `error: could not compile spt (bin "spt")` inside a CLIPPY step's log
is a LINT REFUSAL, not a build break.** Clippy recompiles the crate under `-D warnings`, so a
`dead_code` warning becomes `error: could not compile` there while the very same crate compiled and
ran its full test suite clean in the steps ABOVE. "Could not compile" reads as a hard build failure —
that is the whole trap — and it invites generalizing one step's death backwards over the entire job.

**Instrument split, in deployah's words and worth keeping verbatim: the steps API is the instrument for
WHAT EXECUTED; the failing step's log is only the instrument for WHY IT DIED, and it is silent about
everything above it.** Line 22 of this file already said this, with a clippy-vs-nextest example, and
line 24 already described the same cfg-gated dead-code class from 2026-08-19. **Knowing the rule did
not fire the rule** — same shape as [[open-the-subindex-before-running-a-gate]]. The trigger has to be
the ACT: any sentence of the form "no X ran" / "nothing else happened" is a claim about EXECUTION, so
it is answered by the step ledger, never by a log. Read the ledger before writing that sentence.

**Direction of the error matters for who catches it.** Mine understated my OWN lane — the units had in
fact executed on Linux for the first time and passed, which was new coverage I was throwing away. A
self-deprecating error gets no scrutiny from its author and is caught only by a peer with the artifact
open; deployah had it open because he was driving the release. **Correct it back at the author level
yourself** rather than letting the catcher carry it onward: the wrong sentence was in MY report to
doyle, so the retraction had to reach doyle from me. See [[correct-by-replacement-not-annotation]],
[[dont-take-a-diagnosis-as-measured]].
