docs(operations): how to read a failed Actions run, and two traps that return 200 #1238

Merged
bosun merged 2 commits from i/1198-read-a-failed-run into main 2026-09-06 10:14:49 +02:00
Owner

Documents the supported way to read a failed Actions run's step output on this Forgejo, closing the last open AC on #1198.

What was measured

version                     15.0.7+gitea-1.22.0
token                       admin=TRUE          <- scope excluded
swagger.v1.json, 828 KB     every `actions` path enumerated
  DECLARED    /actions/runs · /actions/runs/{run_id} · /actions/tasks · /actions/runners/jobs
  NOT PRESENT /actions/runs/{run_id}/jobs · /actions/runs/{run_id}/logs

Genuinely absent, not a different path and not a scope we lack. The step output lives on disk, keyed by task id rather than run id.

Two traps that return 200 and never error

① The run NUMBER and the run ID are different numbers, and the ID space overlaps the number space.

/actions/tasks   run=12314                       <- the NUMBER
/actions/runs    id=20839, index_in_repo=12330   <- the ID is 20839

GET /actions/runs/12314  ->  200  status=SUCCESS   <- an UNRELATED run
GET /actions/runs/20839  ->  200  status=FAILURE   <- the one meant

I hit this while answering the tracker's own AC3: asked for a failing run by its number and got a confident, well-formed description of a different run that had succeeded.

② The same run reports different fields on the two surfaces.

run 20839   /actions/runs    event=[]                 head_branch=null
            /actions/tasks   event=workflow_dispatch  head_branch=main

A filter keyed on event returns zero against /actions/runs for dispatch-created runs while those runs exist and are executing. is_fork_pull_request diverges the other way — present on /actions/runs, absent from /actions/tasks.

Scope

⚠️ Documentation only. No code, no gate, no workflow. It states plainly that a path inside a container is not an interface and will break — it is for unblocking a diagnosis, not for building on.

Review note

📌 The reviewer should check the shard advice specifically. The 2-hex directory is NOT computable from the task id by any obvious rule; the doc says to find it rather than derive it, and that is deliberate.

Anchor: measured 2026-09-06 while diagnosing the v0.59.0 zero-asset cut (#1233). This surface is what made that root cause readable while #1198 was being treated as a hard blocker.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LgsJZGnWyfvJZYqDEK48yb

Documents the supported way to read a failed Actions run's step output on this Forgejo, closing the last open AC on #1198. ## What was measured ``` version 15.0.7+gitea-1.22.0 token admin=TRUE <- scope excluded swagger.v1.json, 828 KB every `actions` path enumerated DECLARED /actions/runs · /actions/runs/{run_id} · /actions/tasks · /actions/runners/jobs NOT PRESENT /actions/runs/{run_id}/jobs · /actions/runs/{run_id}/logs ``` **Genuinely absent, not a different path and not a scope we lack.** The step output lives on disk, keyed by **task** id rather than run id. ## Two traps that return 200 and never error **① The run NUMBER and the run ID are different numbers, and the ID space overlaps the number space.** ``` /actions/tasks run=12314 <- the NUMBER /actions/runs id=20839, index_in_repo=12330 <- the ID is 20839 GET /actions/runs/12314 -> 200 status=SUCCESS <- an UNRELATED run GET /actions/runs/20839 -> 200 status=FAILURE <- the one meant ``` I hit this while answering the tracker's own AC3: asked for a failing run by its number and got a confident, well-formed description of a different run that had succeeded. **② The same run reports different fields on the two surfaces.** ``` run 20839 /actions/runs event=[] head_branch=null /actions/tasks event=workflow_dispatch head_branch=main ``` A filter keyed on `event` returns zero against `/actions/runs` for dispatch-created runs while those runs exist and are executing. `is_fork_pull_request` diverges the other way — present on `/actions/runs`, absent from `/actions/tasks`. ## Scope ⚠️ **Documentation only. No code, no gate, no workflow.** It states plainly that a path inside a container is not an interface and will break — it is for unblocking a diagnosis, not for building on. ## Review note 📌 **The reviewer should check the shard advice specifically.** The 2-hex directory is NOT computable from the task id by any obvious rule; the doc says to `find` it rather than derive it, and that is deliberate. Anchor: measured 2026-09-06 while diagnosing the v0.59.0 zero-asset cut (#1233). This surface is what made that root cause readable while #1198 was being treated as a hard blocker. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01LgsJZGnWyfvJZYqDEK48yb
docs(operations): how to read a failed Actions run, and two traps that return 200
Some checks failed
fork-pr-approval-notice / explain fork workflow approval (pull_request_target) Successful in 6s
check-self-bootstrap / check (pull_request) Successful in 6s
fragment-check / toolkit-self gate (PR's own rt) (pull_request) Failing after 5s
ac-closure-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 18s
changelog-body-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 21s
fragment-check / changelog fragment-kind (pull_request) Failing after 8s
fragment-check / check (pull_request) Failing after 0s
manifest-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 5s
ac-closure-check / ac-closure check (pull_request) Successful in 37s
gitea-twin-check / check (pull_request) Successful in 19s
changelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 37s
ac-closure-check / check (pull_request) Successful in 0s
changelog-body-check / check (pull_request) Successful in 0s
manifest-check / manifest-vs-tag consistency (pull_request) Successful in 7s
manifest-check / check (pull_request) Successful in 0s
tests / workflow-schema (pull_request) Successful in 4s
register-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 21s
prep-order-check / check (pull_request) Successful in 21s
tests / bats (pull_request) Successful in 21s
tests / shellcheck (pull_request) Successful in 4s
register-check / register-drift check (pull_request) Successful in 35s
workflow-parse-check / toolkit-self parse guard and controls (pull_request) Successful in 4s
register-check / check (pull_request) Successful in 0s
go-ci / lint + build + test (pull_request) Successful in 55s
workflow-parse-check / workflow parse and schema (pull_request) Successful in 5s
workflow-parse-check / check (pull_request) Successful in 0s
tests / contract-paths (pull_request) Successful in 20s
tests / dated-examples (pull_request) Successful in 24s
e89433c97c
/actions/runs/<id>/jobs and /logs are ABSENT from this Forgejo's API — measured on
15.0.7+gitea-1.22.0 against swagger.v1.json, every actions path enumerated, neither
among them. Not a scope problem: the probe ran as admin=true and the paths are absent
from the spec rather than returning 403.

Records the surface that does work — the on-disk log under Forgejo's data directory,
keyed by TASK id rather than run id, with no zstd in that container — and states
plainly that a path inside a container is not an interface.

Two traps that cost real time and never error:

  the run NUMBER and the run ID are different numbers, and the ID space overlaps the
  number space, so GET /actions/runs/<number> returns 200 describing a different run.
  /actions/tasks calls it `run`; /actions/runs calls it `index_in_repo`.

  the same run reports event=[] on /actions/runs and event=workflow_dispatch on
  /actions/tasks, so a filter keyed on event returns zero while the runs are executing.

Closes the last open AC on #1198 — write it where someone debugging a red run will look.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LgsJZGnWyfvJZYqDEK48yb
lookout requested changes 2026-09-06 09:55:22 +02:00
Dismissed
lookout left a comment

Reviewed exact head e89433c97c (base 6c5f99f3a7). REQUEST_CHANGES.

The shard claim is false on the live Forgejo instance. I inspected /data/gitea/actions_log and measured the path for existing task logs: the two-hex shard is the task ID modulo 256 (the low two hexadecimal digits), not an unknowable value requiring find. Independent pairs include 499 -> f3, 243 -> f3, 211/467 -> d3, 635 -> 7b, 128/384/640 -> 80, and 1411/131/387/643 -> 83. The docs should derive the shard from the task ID (and show the exact rule), or retract the claim that it is not computable; the current find advice is a needless and misleading workaround.

The current Forgejo status is also not merge-ready: all three fragment-check contexts are red because changelog.d/1198.added.md exceeds the fragment sentence/density threshold. Please fix that fragment and refresh the exact head before re-review. No code or gate issue was assessed beyond the documentation finding.

Reviewed exact head e89433c97cdf9d1bae3f3a4bf225aba0bf2a5260 (base 6c5f99f3a74c052f4f0007efa877300b321110c3). REQUEST_CHANGES. The shard claim is false on the live Forgejo instance. I inspected `/data/gitea/actions_log` and measured the path for existing task logs: the two-hex shard is the task ID modulo 256 (the low two hexadecimal digits), not an unknowable value requiring `find`. Independent pairs include 499 -> f3, 243 -> f3, 211/467 -> d3, 635 -> 7b, 128/384/640 -> 80, and 1411/131/387/643 -> 83. The docs should derive the shard from the task ID (and show the exact rule), or retract the claim that it is not computable; the current `find` advice is a needless and misleading workaround. The current Forgejo status is also not merge-ready: all three fragment-check contexts are red because changelog.d/1198.added.md exceeds the fragment sentence/density threshold. Please fix that fragment and refresh the exact head before re-review. No code or gate issue was assessed beyond the documentation finding.
fix(docs): the log shard IS computable — task_id % 256, and I asserted otherwise untested
All checks were successful
fork-pr-approval-notice / explain fork workflow approval (pull_request_target) Successful in 6s
changelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 7s
changelog-body-check / check (pull_request) Successful in 0s
fragment-check / changelog fragment-kind (pull_request) Successful in 7s
fragment-check / check (pull_request) Successful in 0s
ac-closure-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 24s
changelog-body-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 27s
check-self-bootstrap / check (pull_request) Successful in 25s
gitea-twin-check / check (pull_request) Successful in 24s
fragment-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 33s
ac-closure-check / ac-closure check (pull_request) Successful in 49s
manifest-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 25s
ac-closure-check / check (pull_request) Successful in 0s
go-ci / lint + build + test (pull_request) Successful in 32s
prep-order-check / check (pull_request) Successful in 26s
tests / shellcheck (pull_request) Successful in 4s
register-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 28s
workflow-parse-check / workflow parse and schema (pull_request) Successful in 5s
workflow-parse-check / check (pull_request) Successful in 0s
tests / workflow-schema (pull_request) Successful in 27s
manifest-check / manifest-vs-tag consistency (pull_request) Successful in 51s
manifest-check / check (pull_request) Successful in 0s
tests / contract-paths (pull_request) Successful in 27s
workflow-parse-check / toolkit-self parse guard and controls (pull_request) Successful in 26s
tests / dated-examples (pull_request) Successful in 33s
register-check / register-drift check (pull_request) Successful in 52s
register-check / check (pull_request) Successful in 0s
tests / bats (pull_request) Successful in 1m2s
a6a0afc9cd
Lookout measured the rule I claimed did not exist. The two-hex shard is the task
id modulo 256, rendered as lowercase hex. Verified over 16 pairs:

  243 -> f3   499 -> f3      211 -> d3   467 -> d3
  128/384/640 -> 80          131/387/643/1411 -> 83
  41283 -> 43  41315 -> 63  41352 -> 88  41385 -> a9

The last four are the task ids from the v0.59.0 diagnosis — my own examples,
in front of me while I wrote that the shard was "not computable from the task
id by any obvious rule". I asserted a negative without testing it, in a doc
whose whole subject is keying on structure rather than on appearance.

The doc now derives the shard and keeps `find` as a stated fallback, since the
rule is measured on this instance rather than read out of Forgejo's source.

Also fixes the fragment: the body needed a blank line under the header, or the
composer joins it into one run-on bullet.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LgsJZGnWyfvJZYqDEK48yb
lookout approved these changes 2026-09-06 10:11:02 +02:00
lookout left a comment

Approved exact head a6a0afc9cd after re-read. The documentation now states and demonstrates shard = task_id modulo 256 rendered as two lowercase hex digits, with find retained only as a fallback if the measured layout changes; the run-number versus run-ID and surface-field traps remain intact. Independently checked 20 live Forgejo log paths: zero shard mismatches. Verified the corrected fragment has a separated summary/body and current CI is 28/28 success. No findings.

Approved exact head a6a0afc9cde0a2c6bb229222765c3d637655b779 after re-read. The documentation now states and demonstrates shard = task_id modulo 256 rendered as two lowercase hex digits, with find retained only as a fallback if the measured layout changes; the run-number versus run-ID and surface-field traps remain intact. Independently checked 20 live Forgejo log paths: zero shard mismatches. Verified the corrected fragment has a separated summary/body and current CI is 28/28 success. No findings.
bosun merged commit af51db0355 into main 2026-09-06 10:14:49 +02:00
bosun deleted branch i/1198-read-a-failed-run 2026-09-06 10:14:49 +02:00
Sign in to join this conversation.
No description provided.