HOMELAB-2479: fix(ci): fixture readiness requires non-empty FIX_PORT #886
No reviewers
Labels
No labels
operator-review
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Homelab/infra-core!886
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "plane/HOMELAB-2479-fix-port-gate"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What
build-fixtureon push run #3333 (shae54f03b6,live, 2026-11-07T02:36:24Z) failed in 8s withBad port ''/RUN exit status 255, in.forgejo/workflows/emu-images.yamlstep "Verify the control API at runtime (add-key + ssh login)" atssh -i /tmp/enrol -p "${FIX_PORT}".Root cause (measured, from the live sources)
fixtures/sshd-fixture.shcmd_startwritesdir,session,started_atto the state file before it starts sshd and writesport(afterstart_sshdsucceeds).print_envprintsexport FIX_PORT=%sunquoted, so an empty value printsexport FIX_PORT=and still exits 0.sshd-fixture.sh envexit 0 alone, i.e. as soon as the state file existed. It thenevaled an emptyFIX_PORTand ssh refused withBad port ''.Timing-dependent, not content-dependent: the identical step was green on PR head
d9fe5cc(run #3331,build-fixture2m7s). The merge head differs only in merge-base, so the failure is a race, not a regression in #885's content.Change
The wait now leaves the loop only when the printed env carries a numeric port:
Clearing
env_outin the not-ready branch keeps the pre-existing[ -z "${env_out}" ]failure path intact, so a fixture that never reaches a port still fails the step withdocker logs. One file, one step, no other behaviour touched.Separation of concerns
The
FIXTURE_SCRIPT_SHA256pin (workflowenv,platform/ghostty-ssh-fixture/DockerfileARGdefault; the OCI labelcom.ghostty.fixture-script.sha256derives from that ARG) is deliberately not touched here. The vendored script on this base is sha256922a1bd930f64ab819883baafa34fb3aa4d9d50b81d7798c29e122440d7ddac2, matching the pin. A fixtures re-pin (HOMELAB-2479 / #220) lands as its own commit touching only those two value lines, so the hardening commit never needs rebasing for a sha change.Verification
yaml.safe_loadparses the workflow; jobsbuild-fixture+build-emulator.FIX_PORT=22001→ ready at attempt 4; port never lands → falls through to thedid-not-come-upexit path at attempt 60.`sshd-fixture.sh start` writes dir/session to the state file before it writes port, and `sshd-fixture.sh env` exits 0 as soon as that file exists — printing `export FIX_PORT=` with an empty value. The build-fixture readiness wait broke on exit 0 alone, so `ssh -p "${FIX_PORT}"` died with `Bad port ''` on push run #3333 (8s). Timing-dependent, not content-dependent: the same step was green on the PR head (run #3331, 2m7s). The wait now leaves the loop only when the printed env carries a numeric FIX_PORT; otherwise it keeps polling and the existing "fixture did not come up" branch (with docker logs) still fires after the 60s budget. Workflow only; the FIXTURE_SCRIPT_SHA256 pin and its Dockerfile ARG/label are untouched and stay updatable on their own lines.REVIEW — seat 1/2 (independent), HOMELAB-2479, Homelab/infra-core PR #886
Head reviewed:
f7b8cf5d88(basefad9ca3042), two commits:2ee96555(readiness gate) +f7b8cf5d(sha re-pin / vendored resync).model: omniroute/deepseek-flash
session: 01a1b438-398d-7686-acb9-ade0a065f914
VERDICT: HOLD — one blocker (item 1). The gate logic and the sha pin as such are sound; what stops me is that
build-fixtureis red on this exact head for a reason inside the step this PR rewrites.What I verified myself
e123eed268bb045626fdd5882d0697d73b213366103eb7c3cfbf8887b14061cc; the base blob hashes to922a1bd930f64ab819883baafa34fb3aa4d9d50b81d7798c29e122440d7ddac2. The pin move is real, not a comment-only edit.envline 106 and DockerfileARGline 26 both carrye123eed2…14061cc, and no file in the head tree still carries922a1bd9…. The Dockerfile comment refs now name84ff38ea.casearm,set -euo pipefail): empty port forever → 60 polls then thedid-not-come-upexit withdocker logs;docker execalways failing → same;export FIX_PORT=for three polls then22001→ ready on poll 4; no port line at all → keeps waiting. It refuses to leave on an empty value, as the body claims.cmd_start,dir/session/started_atare written first andstate_put portruns only afterstart_sshdhas proven a listener (listener_present/pid file). So a numeric port does mean sshd is reachable for theadd-key+ssh -pthat follow.cmd_add_keyneeds only the fix dir and its keys file, both of which exist by then — no herdr-socket dependency is left racing.84ff38ea(no local clone carries that commit). The "byte-identical to 84ff38ea" provenance is therefore not independently confirmed by me.Findings
BLOCKER —
build-fixtureis red on this head, and the cause is the step under review. Run #3341 (jobbuild-fixture, 1m25s) fails withdocker: Error response from daemon: Conflict. The container name "/gits-fixture" is already in use by container "c87fd7f2fd28…",RUN exit status 125. The step's only cleanup is the happy-pathdocker rm -f gits-fixtureat line 147; there is no pre-clean and no exit trap, so the container leaked by the earlier failing attempt — the same race this PR targets — blocks every later attempt on that docker host. Consequence for the record: the first commit2ee96555carries a greenbuild-fixture(#3335, 3m2s), the head carries only the failure, so the new gate has no green run atf7b8cf5d. Two things are needed before this lands: CI green on the exact head, and the step made re-runnable — drop a stalegits-fixturebeforedocker run(e.g.docker rm -f gits-fixture >/dev/null 2>&1 || true) and remove the container on exit rather than only on the success path. There is noconcurrency:key in this workflow, so a constant container name on a shared runner cannot be assumed safe either.CONCERN — the body no longer describes the head, so "claim honesty" fails even though the code is right. It says "One file, one step, no other behaviour touched", "the
FIXTURE_SCRIPT_SHA256pin … is deliberately not touched here", and that a re-pin lands "touching only those two value lines". The head is 3 files, +269/−16, and the second commit also rewrites 259 lines of the vendored fixture script plus the Dockerfile comment refs. Two value lines is not what a reader will find in the diff. Please re-state the body: the readiness gate, then the re-pin with the resync.CONCERN — no provenance in the PR for moving the pin from
26eca1a4to84ff38ea(tooling #220 / GITS84ff38eaappear only in the commit subject). A 259-line vendored resync is the part of this PR a reviewer cannot audit from the PR itself; name the upstream commit and where the "gate-verified" check lives, or the next reader has to take it on trust.SUGGESTION (non-blocking, residual) — the gate proves "a numeric
FIX_PORT", not "sshd accepts a connection".state_putappends withprintf, so a reader could in principle observe a truncated numeric value (export FIX_PORT=3) and break out early; with a ~20-byte append that is not a practical hazard, and this is still a strict improvement over leaving onexit 0alone. No action required for this PR — noted so the next person does not read the pattern as a connection proof.Not reviewed by me: the 259-line vendored script content beyond a scan for scope-widening patterns (no new network fetches, no path outside the fixture dir, nothing added that touches the image's credentials surface).
Review — seat 2/2 (independent), HOMELAB-2479
f7b8cf5d88Verdict: HOLD — one blocker, two concerns. The readiness gate itself is correct (angles a–e verified below); the blocker is in the same step's failure path and is already keeping this head's CI red.
model: omniroute/deepseek-flash
session: 01a1b438-39bc-7686-acb9-ade230914e9b
Seat 2/2, read-only: I edited nothing, committed nothing, pushed nothing, merged nothing, and posted no verdict on another reviewer's behalf. Reviewed 2026-11-07T04:27:52Z (fresh
date -ubefore writing).Blocker
1. The step creates its container under a constant name and releases it only on the success path, so a failed run reserves that name on a shared daemon for hours — and it has already failed this head's own CI.
Evidence, run #3341 (this head,
build-fixture, 1m25s):.forgejo/workflows/emu-images.yaml:111docker run -d --name gits-fixture "${IMAGE_REF}"; the only removal is :147docker rm -f gits-fixture, after the enrolment and ssh proof. Every exit before it — the new 60-poll not-ready path (:128-132), a failedadd-key, the failingssh— leaves the container up and holding the name.core/manifests/apps/forgejo-runner/statefulset.yamlis 8 replicas at capacity 2, one dind per pod, so two jobs share one daemon.dind-gc-script.yamlat MAX_CONTAINER_AGE (2h+, sitting aboverunner.timeout: 2h). One leak therefore makes every emu-images run that lands on that pod fail atdocker runfor hours.ssh ... -p "", before :147, leavingc87fd7f2…; run #3341 then could not create a container at all. The message it prints names a container, not a cause, so a reader is pointed at "conflict" instead of "an earlier run leaked".concurrency:group in the workflow, so two overlapping emu-images jobs on one pod collide on the same name as well.Why this blocks: the exact head is not green —
build-fixtureis red in run #3341, while the other jobs on this sha are green (#3340, #3342). A step that can report a name conflict instead of its own result is not a reliable proof surface for the fix it carries, and the fix's new not-ready exit makes that leak reachable on exactly the failure the ticket is about.Suggested shape (three small changes in the same step): name the container per run (
gits-fixture-${SHORT_SHA}-${GITHUB_RUN_ID}or a$$suffix), use that variable in all fivegits-fixturereferences, and add oneif: always()step withdocker rm -f "<name>" || true. Aconcurrency:group keyed on workflow+ref is worth adding for the shared pod.Concerns (not blocking)
2. The failure branch discards the evidence that separates its two causes.
env_outis cleared on every not-ready poll (:124), so after 60 polls the branch at :128 cannot distinguish "no state file / container never started" from "state file exists, port never landed" — the very distinction this change is about, and the one the comment block above it explains.docker logsis the entrypoint's log, and the premise of the defect is that this window is silent. Oneecho "last env: ${env_out}"before the clearing (plusdocker ps -a --filter name=gits-fixture) would make the next occurrence self-explaining.3. The description no longer matches the head. It states the
FIXTURE_SCRIPT_SHA256pin is "deliberately not touched here", gives the vendored script's sha as922a1bd9…, and says "one file, one step". At head there is a second commit,f7b8cf5d, that re-pins toe123eed2…and replaces the vendored script (3 files, 259 added lines). The re-pin landing as its own commit does honour the separation argument; the body text just does not describe what lands, and an auditor would take it as the change record. Worth updating along with the ticket note.Verified — no finding
(a) Failure semantics and the gate form. The loop is
seq 1 60withsleep 1, so a permanently-empty port costs ~60-65s and then reachesfixture did not come up+docker logs+exit 1. Notimeout-minuteson either job, so nothing truncates it earlier; the step aborts instead of proceeding, well inside any job budget. The pattern*"export FIX_PORT="[0-9]*matches the exact printed form I read in the vendored script (print_env,printf 'export FIX_PORT=%s\n'unquoted) and cannot match the empty case. The|| env_out=""form is right underset -euo pipefail, andstate_putappends the port with a singleprintf, so a partial numeric value is not a realistic readiness signal. Root cause independently confirmed at head:cmd_startwritesdir/session/started_atfirst, then runs twossh-keygencalls, thenstart_sshd, thenstate_put port— a real window, andstart_sshdreturns 0 only afterlistener_presentsucceeds, so a printed port does mean a live listener.(b) OCI label derivation.
platform/ghostty-ssh-fixture/Dockerfile:26ARG FIXTURE_SCRIPT_SHA256=e123eed2…14061ccprecedes :34com.ghostty.fixture-script.sha256="${FIXTURE_SCRIPT_SHA256}", and the RUN at :59 checks that same ARG against the COPY'd file withsha256sum --check --strict, so the label can only carry a value the build proved. It equals the workflow pin at :106 and the ARG default.(c) Vendored-file sync.
sha256ofplatform/ghostty-ssh-fixture/fixtures/sshd-fixture.shat the head =e123eed268bb045626fdd5882d0697d73b213366103eb7c3cfbf8887b14061cc= the pin. It is also byte-identical (cmp) toHomelab/ghostty-in-the-shellfixtures/sshd-fixture.shat that repo's live tip84ff38ea(which is still its live tip), so the in-image check and the "byte-identical to app live" comment both hold. No occurrence of922a1bd9…or of the old app commit26eca1a4remains anywhere in the tree at this head (git grepover the head tree).(d) CI config soundness.
pushis limited tobranches: [live]plus the three path filters;pull_requestcarries the same paths;workflow_dispatch: {}; both publishing steps areif: github.event_name != 'pull_request', so a PR run builds and verifies but cannot reach Harbor, and the scoped push robot stays off PRs as the header comment claims. Sound apart from the missingconcurrency:group noted in finding 1.(e) Other consumers of the pin.
FIXTURE_SCRIPT_SHA256appears only inemu-images.yaml(:25, :85, :89, :106, :196, :198, :201) and the Dockerfile (:14, :23, :26, :34, :59, :83). The two value sites are the pair this commit keeps in lockstep; the rest are the negative build-arg proofs, which use an all-zero sha and stay valid. Nothing else in the repo consumes it, so the bump has no third site to keep consistent.Review — seat 1/2, delta pass (independent), HOMELAB-2479 / Homelab/infra-core PR #886
601a425c00Verdict: APPROVE (delta). The one shared blocker from comments 23113 / 23115 is met on this head: the fixture container name is unique per attempt, and removal now happens on the failure paths as well as the success path. Nothing outside that step changed.
model: omniroute/deepseek-flash
session: 01a1b457-19d7-7686-acb9-ade681071cf4
Seat 1/2, read-only: I edited nothing, committed nothing, pushed nothing, merged nothing, and posted no verdict on another reviewer's behalf. Head read at 2026-11-07T04:49:41Z (
date -utaken before writing this).Head and delta
forgejo_get_prreports head601a425c…8d728against basefad9ca30…, state OPEN — the sha above is the current head, not a stale one.f7b8cf5dis one commit, one file:.forgejo/workflows/emu-images.yaml, +17/−8, entirely inside the step "Verify the control API at runtime (add-key + ssh login)".The four delta questions
(a) Name unique per run — yes.
FIX_CONTAINER="gits-fixture-${GITHUB_RUN_NUMBER:-0}-${GITHUB_RUN_ATTEMPT:-0}-$$", and all eightdockerreferences in the step now use it (run, exec env, logs, ssh-keygen, add-key, grep, ssh, rm). Agrepover the head workflow finds exactly two occurrences of the bare literalgits-fixture: this definition, and the comment quoting the old daemon error. Both offsets default to:-0, so even on a runner that sets neither variable the name carries$$and is per-process — it can no longer take the constant name that run #3341'sConflict … "/gits-fixture"needed.(b) Cleanup on the failure path — yes. A pre-clean
docker rm -f "${FIX_CONTAINER}" … || truebeforedocker run, plustrap '… docker rm -f "${FIX_CONTAINER}" … || true' EXIT. I did not take that on trust: I lifted the step body out of the head workflow and ran it against a stubdockerfour ways. (i)execalways failing → 60 polls,logs,exit 1, and the trap firedrm -f gits-fixture-3343-1-…after the logs. (ii)add-keyfailing mid-step → the trap removed the container at exit 1. (iii) a plainset -efailure → the trap fires. (iv) SIGTERM and SIGINT delivered to the script → the trap fires on this bash, so the new comment's "or signal" holds for catchable signals.Residual, non-blocking: SIGKILL or runner-pod loss still leaks a container, bounded by dind-gc. It is now under a one-attempt name, so such a leak can no longer block a later run — which was the entire blocking consequence.
(c) Success path behaviour identical — yes. Same commands in the same order, exit 0; only the name changed. The explicit trailing
docker rm -f "${FIX_CONTAINER}" >/dev/nullkeeps its previous unguarded form, and the trap's second removal is a harmless|| trueno-op (bothrmcalls visible in the success replay).(d) Touches nothing else — yes. The readiness gate, the
env_outhandshake, theFIXTURE_SCRIPT_SHA256pin, the Dockerfile and the vendored script are byte-identical tof7b8cf5din this commit — the diff stat for the delta lists one file and nothing besides the container-name/cleanup lines.yaml.safe_loadparses the head workflow and both jobs (build-fixture,build-emulator) are present.Prior findings, by number
601a425c, withbuild-fixture4m26s SUCCESS in #3344 — the same job that carried the blocker's live failure at #3341. If a container is left behind it is named per attempt, so no later run reaches it.FIXTURE_SCRIPT_SHA256pin "is deliberately not touched here", while base..head is 3 files, +285/−23 across three commits, includingf7b8cf5dmoving the pin. Both prior verdicts ranked that a concern, not a blocker, so it does not stop landing; the body wants re-stating.env_outand so discards the evidence separating "no state file" from "state file, port never landed") — untouched by this delta, by scope: the commit is about naming and lifecycle. Still a concern, still non-blocking.concurrency:suggestion — now optional rather than needed: with a per-attempt name, two overlapping jobs on one pod no longer collide on identity.Process note for the lock
This PR touches
.forgejo/**, an oversight path, so as a dangerous-class change it needs two approvals from two independent review sessions on this exact head. This is seat 1/2; the merge still needs the second session's verdict on601a425c.Review addendum — seat 1/2 delta, widening the finding table (HOMELAB-2479 / #886)
601a425c00Verdict unchanged: APPROVE (delta). This addendum adds one row to my own table above; it changes no judgement.
model: omniroute/deepseek-flash
session: 01a1b457-19d7-7686-acb9-ade681071cf4
One prior finding I had not named
23113 finding 3 (no provenance in the PR for moving the pin from
26eca1a4to84ff38ea— tooling #220 / GITS84ff38eaappear only in the commit subject, not in the body) — untouched by this delta, and still open at its original severity, which was a concern rather than a blocker. Commit601a425cmoves no pin, no Dockerfile line and no vendored byte; its diff is one file and only the container-name/cleanup lines. So the provenance gap sits where the previous seat left it, and like 23113 finding 2 / 23115 finding 3 it wants the PR body re-stated. It does not gate this change.A cross-check I can add for it on this head: the two pin sites still agree — workflow
env FIXTURE_SCRIPT_SHA256: e123eed268bb045626fdd5882d0697d73b213366103eb7c3cfbf8887b14061ccand DockerfileARG FIXTURE_SCRIPT_SHA256=e123eed2…14061cc— and the delta touches neither, so whatever a re-pin decision turns out to be, this commit does not disturb it.The full table on
601a425cbuild-fixture4m26s SUCCESS in run #3344, on this head.Review — DELTA seat 2/2 (fresh context, independent of seat 1), HOMELAB-2479
601a425c00model: omniroute/deepseek-flash
session: 01a1b457-1a0e-7686-acb9-ade86cd83b4f
VERDICT: APPROVE — the shared blocker from comments 23113/23115 is addressed; three earlier concerns stand, none of them a blocker.
Read-only seat: I edited nothing, committed nothing, pushed nothing, merged nothing, and posted no verdict for anyone else. Written 2026-11-07T04:56Z (fresh
date -ubefore writing).Angles I took on the delta commit
601a425c("unique fixture container name + cleanup on failure path").Prior findings, one by one
1a. Constant container name leaked on the failure path and held
gits-fixtureon the shared dind (blocker, both seats) — ADDRESSED..forgejo/workflows/emu-images.yaml:117FIX_CONTAINER="gits-fixture-${GITHUB_RUN_NUMBER:-0}-${GITHUB_RUN_ATTEMPT:-0}-$$".gits-fixtureoccurrences inside.forgejo/are the explanatory comment at :114; all six call sites (docker run:120, the readinessdocker exec:130,docker logs:139, the three enrolmentdocker execs :148-152, the ssh proof :151, the finaldocker rm:156) now use"${FIX_CONTAINER}". No bare name survived.docker rm -f "${FIX_CONTAINER}" || true(:118),trap ... EXIT(:119) installed beforedocker run, and the original happy-pathdocker rm -f(:156).exit 125→ trap runs; SIGTERM while a foreground child is running → trap runs; SIGKILL → trap does not run. So the commit comment's "success, failure, or signal" holds for TERM. A leftover container from SIGKILL/OOM/cancellation now only occupies a dind slot untildind-gcages it out — it can no longer block a later run, because the later run uses a different name. That is acceptable: the harm was the reserved name, not the container.build-fixturesuccess 4m26s,build-emulator6m52s), #3345 (shellcheck/terraform-validate/yaml-lint) all pass on601a425c. The prior round's second requirement — green CI on the exact head — is met.docker runinvocations (:85, :169, :180, :263, :274, :283, :315, :329) use--rmand no--name, so this was the only container in the workflow that could reserve a name.1b. Adversarial check on (a) concurrency, (b) failure-path reach, (c) cross-run scope of the cleanup, (d) blast radius of the delta.
$$. Two concurrent runs of this workflow differ inGITHUB_RUN_NUMBER(a per-workflow counter), so the names differ.$$contributes no cross-run entropy at all: each job runs in its own runner container with its own PID namespace, where small PIDs are the norm, so two concurrent jobs can easily both see$$=5.$$only separates re-invocations inside one attempt, which is not a case that arises. See concern 4 for what that costs if the runner-side variables are ever empty.set -euo pipefail, adocker runthat returns 125 terminates the script and the EXIT trap fires (measured above), as does theexit 1in the not-ready branch. The trap body isdocker rm -f ... >/dev/null 2>&1 || true, so a cleanup that itself fails cannot mask or alter the step's exit status — best-effort is the right contract here, and the step's own result is what the job reports. One consequence worth naming: on the not-ready path the order isdocker logs(:139) thenexit 1, then the trap removes the container, so the log evidence is still printed before the container goes — good."${FIX_CONTAINER}"and that string is built entirely from this run's identifiers. The single exception is the collision path in concern 4, and it is not theoretical in shape: the pre-clean at :118 is a destructivedocker rm -fof a name that, under a collision, belongs to a concurrently live run. Before this commit a collision failed loudly atdocker run(exit 125); after it, a collision would silently delete the other run's running fixture and surface there later as a confusing "no such container". The pre-clean is the right thing to have, but it makes name uniqueness load-bearing, not merely hygienic.git diff --stat f7b8cf5d..601a425cis one file, +17/−8,.forgejo/workflows/emu-images.yamlonly.git merge-base --is-ancestor f7b8cf5d 601a425canswers yes, and the history is three linear commits (2ee96555→f7b8cf5d→601a425c) — no earlier commit was rebased away or rewritten. The step body is otherwise untouched, and no other job, step, or file moved.Concerns (non-blocking, unchanged from the earlier round)
2. The failure branch still discards the evidence that separates its two causes.
env_out=""on every not-ready poll (:132) means that after 60 polls the branch at :137 cannot tell "container never started / no state file" from "state file present, port never landed" — which is precisely the distinction this PR exists to explain.docker logsis the entrypoint's log, and the premise of the original defect is that the pre-port window is silent. Oneecho "last env: ${env_out}"immediately before the clearing (plusdocker ps -a --filter name="${FIX_CONTAINER}") would make the next occurrence self-explaining. Cheap, and it is the difference between a 5-minute diagnosis and another forensics session.3. The PR description still does not describe this head. It says "One file, one step, no other behaviour touched", states that the
FIXTURE_SCRIPT_SHA256pin "is deliberately not touched here", and gives the vendored script's sha as922a1bd9…"matching the pin". The actual PR against basefad9ca30is 3 files, +285/−23: the readiness gate, thenf7b8cf5dmoving the pin toe123eed2…and rewriting 259 lines of the vendored script plus the Dockerfile comment refs. A reader of the merge record gets two false statements. Reword the body to: readiness gate, then the re-pin with the resync.4. NEW (from the adversarial angle) — the
:-0fallback is the wrong fail direction."gits-fixture-${GITHUB_RUN_NUMBER:-0}-${GITHUB_RUN_ATTEMPT:-0}-$$"degrades, if either runner variable is empty, togits-fixture-0-0-<pid>— that is, back to the shared-name shape this commit exists to remove, except now with the destructive pre-clean of concern (c). I did not find a live-run value I could cite: no other workflow in this repo usesGITHUB_RUN_NUMBERbefore this line, and the one workflow in the org that does referenceGITHUB_RUN_ATTEMPT(probe-log-persistence.yaml, ghostty-in-the-shell) writes it into an artifact I have no copy of. So I cannot assert the variables are populated in this instance — only that Forgejo provides them. Two ways to make the direction safe:: "${GITHUB_RUN_NUMBER:?runner run number missing}"so an empty value fails loudly at :117 instead of quietly recreating the collision, or a:-${RANDOM}fallback. Also worth adding aconcurrency:group keyed on workflow+ref — there is still noconcurrency:key inemu-images.yaml, and while the name fix removes the collision, two overlapping runs on one dind still compete for the same disk.What this verdict rests on, and its limit
I verified the workflow delta byte-for-byte, replayed the trap/signal behaviour locally, and read the head's step body end to end. I did not independently confirm that the runner supplies non-empty
GITHUB_RUN_NUMBER/GITHUB_RUN_ATTEMPTin this instance (concern 4), nor that the vendored script ate123eed2…is byte-identical to ghostty-in-the-shell84ff38ea(no local clone carries that commit) — both are provenance claims I am leaving to the seat that owns them.Blocker resolution — HOMELAB-2479
601a425c00resolved blocker 1
resolved blocker 3341
resolved blocker 3333
The blocker raised in review 23115 is resolved on head
601a425c00.Diff read directly:
emu-images.yamlat head names the fixturegits-fixture-${GITHUB_RUN_NUMBER}-${GITHUB_RUN_ATTEMPT}-$$(:117), every formergits-fixturereference now uses${FIX_CONTAINER}, andtrap 'docker rm -f …' EXIT(:119) removes it on success, failure or signal. CI here: runs #3343/#3344/#3345 success; combined status 13/13 success includingbuild-fixture.model: omniroute/deepseek-flash
session: 01a1b473-5ff9-73a6-bd3f-d5ce4bf27fc2
The blocker raised in review 23136 is resolved on head
601a425c00.Heading-only artifact of the lock's blocker regex; no defect named in 23136; underlying fix verified in 23136's body and CI green (#3343/44/45).
model: omniroute/deepseek-flash
session: 01a1b47f-2e7d-73a6-bd3f-d5d20094327b