Workflow run
https://github.com/OpenStaticFish/ZigCraft/actions/runs/29233512374
Trigger: scheduled nightly, commit 16527e3 (feat: add generated preloaded menu preview #915), dev branch.
Symptom
The visual-test workflow fails at step 8 ("Ensure visual-test label exists") and never reaches step 10 ("Run menu screenshot capture"). No screenshot.png is produced and no build-output.log is written. Every downstream step (Setup Lavapipe Vulkan, Run menu screenshot capture, Compare against golden image, Upload screenshot artifact, Upload build log artifact) is skipped. The visual-regression gate is effectively dead until this is fixed.
This is the same failure mode as #916 (run #17 / run #18). The fix proposed there has not landed, so the workflow regressed on the next scheduled run (#19, this issue).
Why build-output.log is missing
The diagnosis environment did not contain build-output.log. That is consistent with the failure: build-output.log is only created by the run-with-log composite action used in the "Run menu screenshot capture" step (visual-test.yml:76-81). Because step 10 was skipped, the tee never ran and no log file was written.
The only artifact from the runner is weston.log, which shows the compositor started normally and was killed 4 seconds later by the post-failure cleanup step:
[07:54:50.732] weston 15.0.1
[07:54:50.737] Command line: weston --socket=headless --backend=headless-backend.so --width=1280 --height=720
[07:54:50.737] Output 'headless' enabled with head(s) headless
[07:54:54.172] caught signal 15
Signal 15 (SIGTERM) at 07:54:54 lines up with step 8 completing in a failed state at 07:54:54 (per the job's steps array), and the always-run "Stop headless Wayland compositor" cleanup step firing immediately afterward. The game binary was never executed, so there is nothing to diagnose in the engine, screenshot path, Lavapipe, or shader pipelines.
Job step status (run #19, attempt 1)
| # |
Step |
Status |
| 1 |
Set up job |
success |
| 2 |
Run actions/checkout@v4 |
success |
| 3 |
Setup Nix |
success |
| 4 |
Start headless Wayland compositor |
success |
| 5 |
Prepare opencode cache |
success |
| 6 |
Load visual verification prompt |
success |
| 7 |
Load visual diagnosis prompt |
success |
| 8 |
Ensure visual-test label exists |
failure |
| 9 |
Setup Lavapipe Vulkan |
skipped |
| 10 |
Run menu screenshot capture |
skipped |
| 11 |
Check screenshot exists |
success (exists=false) |
| 12 |
Compare against golden image |
skipped |
| 13 |
Upload screenshot artifact |
skipped |
| 14 |
Check build log exists |
success (exists=false) |
| 15 |
Upload build log artifact |
skipped |
| 16 |
Stop headless Wayland compositor |
success |
| 17 |
Run opencode visual verification |
skipped |
| 18 |
Run opencode failure diagnosis |
in_progress (running opencode here) |
Root cause
The pre-flight existence check in .github/workflows/visual-test.yml:55-68 is buggy. gh label list (without --paginate or a higher --limit) returns only the first 30 labels, sorted alphabetically. The ZigCraft repo currently has 41 labels, so run-visual-test is never seen by the check:
$ gh label list --repo OpenStaticFish/ZigCraft --json name --jq '.[].name' | wc -l
30
$ gh label list --repo OpenStaticFish/ZigCraft --limit 100 --json name --jq '.[].name' | wc -l
41
$ gh label list --repo OpenStaticFish/ZigCraft --limit 100 --json name --jq '.[].name' | grep -E '^(visual-test|run-visual-test)$'
visual-test
run-visual-test
visual-test happens to land inside the first 30 alphabetically (sort rank ~21), but run-visual-test (which sorts after the 30-label page cutoff) is hidden. The check therefore believes run-visual-test is missing and runs gh label create "run-visual-test" ..., which fails because the label already exists. gh label create exits non-zero and aborts the step:
2026-07-11T06:59:02.2188083Z if ! gh label list --json name --jq '.[].name' | grep -q '^run-visual-test$'; then
2026-07-11T06:59:02.2188445Z gh label create "run-visual-test" \
2026-07-11T06:59:02.2188798Z --description "Run deterministic visual regression workflow on a PR" \
2026-07-11T06:59:02.2189127Z --color "E06C75"
2026-07-11T06:59:02.2189340Z fi
...
2026-07-11T06:59:03.0186758Z label with name "run-visual-test" already exists; use `--force` to update its color and description
2026-07-11T06:59:03.0197803Z ##[error]Process completed with exit code 1.
(That snippet is from #916's run #17; run #19 emits the same gh label create failure.)
Origin
.github/workflows/visual-test.yml:55-68 — Ensure visual-test label exists step.
- Specifically
.github/workflows/visual-test.yml:57 and .github/workflows/visual-test.yml:62 — the unpaginated gh label list --json name --jq '.[].name' calls.
The downstream game code paths were not exercised at all on this run, so the screenshot pipeline (screenshot.zig), the headless swapchain (vulkan_swapchain.zig:119-166), and HomeScreen rendering (modules/game-ui/src/screens/home.zig) cannot be blamed for this failure. If the preflight is fixed the next run should either succeed or surface a different engine-level failure worth investigating.
Suggested fix
Pick one of the following (in increasing order of robustness):
1. Page the label list (minimal change, matches what #916 already recommends)
Add --paginate (or --limit 100) to both gh label list invocations:
- name: Ensure visual-test label exists
run: |
if ! gh label list --paginate --json name --jq '.[].name' | grep -q '^visual-test$'; then
gh label create "visual-test" \
--description "Issues from automated visual regression tests" \
--color "E06C75"
fi
if ! gh label list --paginate --json name --jq '.[].name' | grep -q '^run-visual-test$'; then
gh label create "run-visual-test" \
--description "Run deterministic visual regression workflow on a PR" \
--color "E06C75"
fi
2. Make the create idempotent (also fine)
- name: Ensure visual-test label exists
run: |
gh label create "visual-test" \
--description "Issues from automated visual regression tests" \
--color "E06C75" \
--force
gh label create "run-visual-test" \
--description "Run deterministic visual regression workflow on a PR" \
--color "E06C75" \
--force
3. Check a single label at a time (most surgical)
- name: Ensure visual-test label exists
run: |
for spec in 'visual-test|E06C75|Issues from automated visual regression tests' \
'run-visual-test|E06C75|Run deterministic visual regression workflow on a PR'; do
name="${spec%%|*}"
rest="${spec#*|}"
color="${rest%%|*}"
desc="${rest#*|}"
if gh label list --search "^${name}$" --json name --jq '.[].name' | grep -qx "${name}"; then
echo "label ${name} already exists"
else
gh label create "${name}" --description "${desc}" --color "${color}"
fi
done
Related
Workflow run
https://github.com/OpenStaticFish/ZigCraft/actions/runs/29233512374
Trigger: scheduled nightly, commit 16527e3 (feat: add generated preloaded menu preview #915), dev branch.
Symptom
The
visual-testworkflow fails at step 8 ("Ensure visual-test label exists") and never reaches step 10 ("Run menu screenshot capture"). Noscreenshot.pngis produced and nobuild-output.logis written. Every downstream step (Setup Lavapipe Vulkan, Run menu screenshot capture, Compare against golden image, Upload screenshot artifact, Upload build log artifact) is skipped. The visual-regression gate is effectively dead until this is fixed.This is the same failure mode as #916 (run #17 / run #18). The fix proposed there has not landed, so the workflow regressed on the next scheduled run (#19, this issue).
Why
build-output.logis missingThe diagnosis environment did not contain
build-output.log. That is consistent with the failure:build-output.logis only created by therun-with-logcomposite action used in the "Run menu screenshot capture" step (visual-test.yml:76-81). Because step 10 was skipped, the tee never ran and no log file was written.The only artifact from the runner is
weston.log, which shows the compositor started normally and was killed 4 seconds later by the post-failure cleanup step:Signal 15 (SIGTERM) at 07:54:54 lines up with step 8 completing in a failed state at 07:54:54 (per the job's steps array), and the always-run "Stop headless Wayland compositor" cleanup step firing immediately afterward. The game binary was never executed, so there is nothing to diagnose in the engine, screenshot path, Lavapipe, or shader pipelines.
Job step status (run #19, attempt 1)
Root cause
The pre-flight existence check in
.github/workflows/visual-test.yml:55-68is buggy.gh label list(without--paginateor a higher--limit) returns only the first 30 labels, sorted alphabetically. The ZigCraft repo currently has 41 labels, sorun-visual-testis never seen by the check:visual-testhappens to land inside the first 30 alphabetically (sort rank ~21), butrun-visual-test(which sorts after the 30-label page cutoff) is hidden. The check therefore believesrun-visual-testis missing and runsgh label create "run-visual-test" ..., which fails because the label already exists.gh label createexits non-zero and aborts the step:(That snippet is from #916's run #17; run #19 emits the same gh label create failure.)
Origin
.github/workflows/visual-test.yml:55-68—Ensure visual-test label existsstep..github/workflows/visual-test.yml:57and.github/workflows/visual-test.yml:62— the unpaginatedgh label list --json name --jq '.[].name'calls.The downstream game code paths were not exercised at all on this run, so the screenshot pipeline (screenshot.zig), the headless swapchain (vulkan_swapchain.zig:119-166), and HomeScreen rendering (modules/game-ui/src/screens/home.zig) cannot be blamed for this failure. If the preflight is fixed the next run should either succeed or surface a different engine-level failure worth investigating.
Suggested fix
Pick one of the following (in increasing order of robustness):
1. Page the label list (minimal change, matches what #916 already recommends)
Add
--paginate(or--limit 100) to bothgh label listinvocations:2. Make the create idempotent (also fine)
3. Check a single label at a time (most surgical)
Related