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