guard: a run reports failure while its only job's log ends in success #36

Open
opened 2026-08-13 21:11:37 +00:00 by PerishFire · 1 comment
Owner

PerishLab/codehull run 120 (task id 6511, head 1207405ea6658ded853a1fdf1c1a2937c133ddc3) reports failure after 15m29s. Its log contains no failure of any kind, and ends by declaring success.

The contradiction

$ runseal :perish @forgejo --json job list 120
{"jobs":[{"index":0,"name":"guard","status":"failure"}],"version":1}

$ runseal :perish @forgejo job log 120 0 | tail -1
🏁  Job succeeded

One job. API says failure, its own log says succeeded.

The step boundaries are incomplete

Every ⭐ Run X in the 1274-line log:

⭐ Run Pre Install stable Ectropy    ✅ Success
⭐ Run Pre Install stable Plumb      ✅ Success
⭐ Run Main Checkout                 ← no matching Success
⭐ Run Post Install stable Plumb     ✅ Success
⭐ Run Post Install stable Ectropy   ✅ Success
⭐ Run Post Checkout                 ✅ Success
🏁  Job succeeded

guard.yml declares a step named Guard (line 33). No ⭐ Run Guard marker appears anywhere. The ~1200 lines between Main Checkout and Post Install carry the whole guard body — cargo fmt, clippy, test, release check, deno, plumb doctor, ectropy, act — all of it succeeding, with act: clean as its last output. Those lines belong to no delimited step.

Why it matters to a caller

plumb land refuses to merge on guard failure, correctly, and leaves the pull open. Diagnosing that refusal means reading the log. Here the log cannot explain the verdict: it shows no failure and ends in success. The caller has no way to distinguish a real failure whose cause is unlogged from a reporting defect.

What was verified locally

The same tree passes every guard step on a workstation, following guard.yml line by line: cargo fmt --all --check, clippy --locked --workspace --all-targets -- -D warnings, test --locked, check --locked --release, deno fmt, deno check, helm lint charts/codehull --quiet, plumb doctor . (true to the skeleton, on plumb 0.18.26 matching CI's stable), ectropy . (clean), and cargo run -p codehull -- act (act: clean).

The previous run on the same branch, 119 (c20b1c38), failed in 16s on a genuine cargo fmt diff and its log showed it plainly. So short failures do surface; this one does not.

Not asking for

No change to what guard runs. The claim is only that a job's reported status and its log disagree, and that one declared step leaves no boundary marker.

PerishLab/codehull run 120 (task id 6511, head `1207405ea6658ded853a1fdf1c1a2937c133ddc3`) reports `failure` after 15m29s. Its log contains no failure of any kind, and ends by declaring success. ## The contradiction ``` $ runseal :perish @forgejo --json job list 120 {"jobs":[{"index":0,"name":"guard","status":"failure"}],"version":1} $ runseal :perish @forgejo job log 120 0 | tail -1 🏁 Job succeeded ``` One job. API says failure, its own log says succeeded. ## The step boundaries are incomplete Every `⭐ Run X` in the 1274-line log: ``` ⭐ Run Pre Install stable Ectropy ✅ Success ⭐ Run Pre Install stable Plumb ✅ Success ⭐ Run Main Checkout ← no matching Success ⭐ Run Post Install stable Plumb ✅ Success ⭐ Run Post Install stable Ectropy ✅ Success ⭐ Run Post Checkout ✅ Success 🏁 Job succeeded ``` `guard.yml` declares a step named `Guard` (line 33). **No `⭐ Run Guard` marker appears anywhere.** The ~1200 lines between Main Checkout and Post Install carry the whole guard body — cargo fmt, clippy, test, release check, deno, plumb doctor, ectropy, act — all of it succeeding, with `act: clean` as its last output. Those lines belong to no delimited step. ## Why it matters to a caller `plumb land` refuses to merge on guard failure, correctly, and leaves the pull open. Diagnosing that refusal means reading the log. Here the log cannot explain the verdict: it shows no failure and ends in success. The caller has no way to distinguish a real failure whose cause is unlogged from a reporting defect. ## What was verified locally The same tree passes every guard step on a workstation, following `guard.yml` line by line: `cargo fmt --all --check`, `clippy --locked --workspace --all-targets -- -D warnings`, `test --locked`, `check --locked --release`, `deno fmt`, `deno check`, `helm lint charts/codehull --quiet`, `plumb doctor .` (true to the skeleton, on plumb 0.18.26 matching CI's stable), `ectropy .` (clean), and `cargo run -p codehull -- act` (act: clean). The previous run on the same branch, 119 (`c20b1c38`), failed in 16s on a genuine `cargo fmt` diff and its log showed it plainly. So short failures do surface; this one does not. ## Not asking for No change to what guard runs. The claim is only that a job's reported status and its log disagree, and that one declared step leaves no boundary marker.
Author
Owner

Three more runs: the status contradiction did not reproduce, the missing step boundary did

Re-checked 2026-08-14 against the same branch.

run event head verdict log
120 pull_request 1207405 failure, 15m29s wall 🏁 Job succeeded, no failure of any kind
121 workflow_dispatch 1207405 failure, 2m03s plumb doctor 2 out of true, then ⚙️ [runner]: exitcode '1': failure, then 🏁 Job failed
122 pull_request d7324df success, 2m22s normal
123 push (main) d7324df success normal

The headline claim now rests on one sample

Run 121 failed for a real reason and said so plainly: plumb doctor went out of true because plumb 0.18.27 shipped between 20:12 and 22:00 while the pin stayed at ^0.18.26. Its log carries ⚙️ [runner]: exitcode '1': failure and ends 🏁 Job failed. Runs 122 and 123 succeeded and reported success.

So status and log agree in three of four runs, including one genuine failure. Run 120 stays unexplained, but nothing since reproduces it.

One timing detail the original report did not have

Run 120's log stops at 20:12:42 with 🏁 Job succeeded; the run record's updated_at is 20:25:48. The work took 2m23s, then nothing happened for 13m06s before the verdict landed. The 15m29s quoted originally is wall clock, not job duration. Runs 121 and 122 have no such gap — they finish and report immediately.

Job body completes, result never posted, verdict arrives 13 minutes later: that shape points at the runner's task connection rather than at guard.

The step-boundary half reproduces every time

Unchanged across 120, 121, 122 and 123: guard.yml declares a step named Guard, and no ⭐ Run Guard marker appears in any log. Main Checkout never gets a matching Success line. The entire guard body sits between them, belonging to no delimited step.

That half is reproducible and independent of any verdict, including on successful runs. Suggest re-scoping this issue to it, and tracking run 120 separately — or closing that half as unreproduced unless it recurs.

## Three more runs: the status contradiction did not reproduce, the missing step boundary did Re-checked 2026-08-14 against the same branch. | run | event | head | verdict | log | |---|---|---|---|---| | 120 | pull_request | `1207405` | failure, 15m29s wall | `🏁 Job succeeded`, no failure of any kind | | 121 | workflow_dispatch | `1207405` | failure, 2m03s | `plumb doctor` 2 out of true, then `⚙️ [runner]: exitcode '1': failure`, then `🏁 Job failed` | | 122 | pull_request | `d7324df` | success, 2m22s | normal | | 123 | push (main) | `d7324df` | success | normal | ### The headline claim now rests on one sample Run 121 failed for a real reason and said so plainly: `plumb doctor` went out of true because plumb 0.18.27 shipped between 20:12 and 22:00 while the pin stayed at `^0.18.26`. Its log carries `⚙️ [runner]: exitcode '1': failure` and ends `🏁 Job failed`. Runs 122 and 123 succeeded and reported success. So status and log agree in three of four runs, including one genuine failure. Run 120 stays unexplained, but nothing since reproduces it. ### One timing detail the original report did not have Run 120's log stops at 20:12:42 with `🏁 Job succeeded`; the run record's `updated_at` is 20:25:48. The work took 2m23s, then nothing happened for 13m06s before the verdict landed. The 15m29s quoted originally is wall clock, not job duration. Runs 121 and 122 have no such gap — they finish and report immediately. Job body completes, result never posted, verdict arrives 13 minutes later: that shape points at the runner's task connection rather than at guard. ### The step-boundary half reproduces every time Unchanged across 120, 121, 122 and 123: `guard.yml` declares a step named `Guard`, and no `⭐ Run Guard` marker appears in any log. `Main Checkout` never gets a matching Success line. The entire guard body sits between them, belonging to no delimited step. That half is reproducible and independent of any verdict, including on successful runs. Suggest re-scoping this issue to it, and tracking run 120 separately — or closing that half as unreproduced unless it recurs.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
PerishLab/actions#36
No description provided.