Prepare deploy writes before stopping the service #748

Closed
kayg wants to merge 11 commits from fix/deploy-688-round5 into dev
Owner

Deploys could stop the only healthy process before image-store and rollback writes had finished. This completes preparation under one target lock before stopping, then commits a prepared image record by rename.

  • Receive the artifact in the detached worker under the deploy lock; clean partial receipts and bound FIFO connection waits when a caller disappears.
  • Check free space from image size, prepare and sync unit/pointer bytes, reserve separate trial and recovery startup space, and discard archives before stop. Podman stop also uses reserved runtime space. Current/rollback identities now live in one atomic record; tags are bootstrap inputs. No image load or tag write follows stop.
  • Fix fixture relocation under /home and inject persistent disk exhaustion at every deploy, rollback and fallback step.

Validation: bash syntax, shellcheck, deploy regression gate (2,402 injected cases), updated transport checks with real user systemd, relay checks, and 768 additional persistent fallback cases pass: 3,170 injected cases total. All fixture paths are under /home. The final real staging replay passes 13 rows, including persistent ENOSPC, failed health, real generator-output loss and an SSH disconnect while the old service is stopped. Staging ends healthy on A with rollback B; its unit bytes and mount identities are preserved.

Final logs and rehearsal source. Related to #688; replacement for #733 because this forge ignores pushes to an open PR branch. Head d2ade1fb93. No production contact, merge, or production deploy.

Deploys could stop the only healthy process before image-store and rollback writes had finished. This completes preparation under one target lock before stopping, then commits a prepared image record by rename. - Receive the artifact in the detached worker under the deploy lock; clean partial receipts and bound FIFO connection waits when a caller disappears. - Check free space from image size, prepare and sync unit/pointer bytes, reserve separate trial and recovery startup space, and discard archives before stop. Podman stop also uses reserved runtime space. Current/rollback identities now live in one atomic record; tags are bootstrap inputs. No image load or tag write follows stop. - Fix fixture relocation under /home and inject persistent disk exhaustion at every deploy, rollback and fallback step. Validation: bash syntax, shellcheck, deploy regression gate (2,402 injected cases), updated transport checks with real user systemd, relay checks, and 768 additional persistent fallback cases pass: 3,170 injected cases total. All fixture paths are under /home. The final real staging replay passes 13 rows, including persistent ENOSPC, failed health, real generator-output loss and an SSH disconnect while the old service is stopped. Staging ends healthy on A with rollback B; its unit bytes and mount identities are preserved. [Final logs and rehearsal source](https://git.kayg.org/attachments/45696784-6fb8-4f31-8309-0ef3c10660ba). Related to #688; replacement for #733 because this forge ignores pushes to an open PR branch. Head d2ade1fb931b4ac74fb698015fcc95c3e7cf69c5. No production contact, merge, or production deploy.
kayg added 11 commits 2026-10-28 22:07:54 +00:00
Author
Owner

Independent sixth-round review started. Branch fix/deploy-688-round5; review head d2ade1fb93; merge base 3d0c6e33d5; dev at review start d83e54dbe5. I will inspect every commit and touched deploy file, run local failure injection and gates, and rehearse only against the configured staging target on 8by64-compute01. No push, merge, production contact, or userdata operations.

Independent sixth-round review started. Branch fix/deploy-688-round5; review head d2ade1fb931b4ac74fb698015fcc95c3e7cf69c5; merge base 3d0c6e33d588335c745a660163bf8bf5cdc8412d; dev at review start d83e54dbe571b625504001a8b6add90d44ca6161. I will inspect every commit and touched deploy file, run local failure injection and gates, and rehearse only against the configured staging target on 8by64-compute01. No push, merge, production contact, or userdata operations.
Author
Owner

Review base: merge-base 3d0c6e33d5; current dev reported by PR API d83e54dbe5. Head remains d2ade1fb93. Staging rehearsal is currently blocked: NetBird SSH to agent@8by64-compute01.vpn.kayg.org fails DNS lookup (no such host). NetBird management/signal are connected and list that peer. I am checking its reported VPN address while continuing local checks. The initial gate stopped at missing shellcheck; a private extracted shellcheck 0.10.0 is now supplied on PATH for the unmodified gate.

Review base: merge-base 3d0c6e33d588335c745a660163bf8bf5cdc8412d; current dev reported by PR API d83e54dbe571b625504001a8b6add90d44ca6161. Head remains d2ade1fb931b4ac74fb698015fcc95c3e7cf69c5. Staging rehearsal is currently blocked: NetBird SSH to agent@8by64-compute01.vpn.kayg.org fails DNS lookup (no such host). NetBird management/signal are connected and list that peer. I am checking its reported VPN address while continuing local checks. The initial gate stopped at missing shellcheck; a private extracted shellcheck 0.10.0 is now supplied on PATH for the unmodified gate.
Author
Owner

P2 — First normal deployment after bootstrap requires an undocumented predecessor
Location: deploy/next/switch.sh:102–104; deploy/next/README.md:166–169.

When the image record is absent, preparation unconditionally requires localhost/$NAME:rollback to exist and differ from the current image. A fresh installation, or the documented supervised bootstrap, can have one labelled healthy current image and no rollback tag. Its next normal deploy fails with bootstrap predecessor missing, before stopping. The new transaction already has the healthy current digest needed for candidate recovery and for the successor rollback record, so requiring a still older image prevents the documented first normal deployment. The supplied fixtures always seed two distinct tags and miss this case. Support an absent entry predecessor, or explicitly provision/document a safe bootstrap record without pretending the current image is its own predecessor.

Repro: python3 review-artifacts/independent-probe.py, case no-predecessor: baseline is active, reports full SHA a40, current digest is sha256:164, no pointer file and no rollback tag; submit valid revision b*40 bundle and unit. Exit 1; output bootstrap predecessor missing then transaction failed before switch; old service unchanged; zero stop calls. Same harness's normal case with a predecessor succeeds, rerun keeps the record and performs no stop/start/tag, and code rollback succeeds. Unlabelled baseline case refuses with bootstrap first, with zero stop calls, as documented.

Reviewer probe source and output.

P2 — First normal deployment after bootstrap requires an undocumented predecessor Location: deploy/next/switch.sh:102–104; deploy/next/README.md:166–169. When the image record is absent, preparation unconditionally requires `localhost/$NAME:rollback` to exist and differ from the current image. A fresh installation, or the documented supervised bootstrap, can have one labelled healthy current image and no rollback tag. Its next normal deploy fails with `bootstrap predecessor missing`, before stopping. The new transaction already has the healthy current digest needed for candidate recovery and for the successor rollback record, so requiring a still older image prevents the documented first normal deployment. The supplied fixtures always seed two distinct tags and miss this case. Support an absent entry predecessor, or explicitly provision/document a safe bootstrap record without pretending the current image is its own predecessor. Repro: `python3 review-artifacts/independent-probe.py`, case `no-predecessor`: baseline is active, reports full SHA a*40, current digest is sha256:1*64, no pointer file and no rollback tag; submit valid revision b*40 bundle and unit. Exit 1; output `bootstrap predecessor missing` then `transaction failed before switch; old service unchanged`; zero stop calls. Same harness's normal case with a predecessor succeeds, rerun keeps the record and performs no stop/start/tag, and code rollback succeeds. Unlabelled baseline case refuses with `bootstrap first`, with zero stop calls, as documented. [Reviewer probe source and output](https://git.kayg.org/attachments/2773ac9b-0b04-43e6-9d1c-3daf4050a04a).
Author
Owner

Independent checks update: 13 reviewer-written scenarios completed. Candidate load/tag/unit-copy/reservation/stop-after-effect/unit-rename/reload/start/health/pointer-commit failures preserve the entry image and userdata sentinel. Success, same-SHA rerun and code rollback pass; unlabelled baseline refuses before stopping. The real user-systemd transport checks also pass (abandoned FIFO, normal, failed transaction, closed output, dropped caller, locked receipt). A private extracted Quadlet 5.4.2 passes real generation and invalid-candidate rejection. These local checks do not satisfy the required staging rehearsal: hostname lookup still fails; both NetBird IPv4 and IPv6 attempts time out. No staging mutation has been confirmed; no production contact. The unmodified exhaustive gate is still running in one build slot.

Independent checks update: 13 reviewer-written scenarios completed. Candidate load/tag/unit-copy/reservation/stop-after-effect/unit-rename/reload/start/health/pointer-commit failures preserve the entry image and userdata sentinel. Success, same-SHA rerun and code rollback pass; unlabelled baseline refuses before stopping. The real user-systemd transport checks also pass (abandoned FIFO, normal, failed transaction, closed output, dropped caller, locked receipt). A private extracted Quadlet 5.4.2 passes real generation and invalid-candidate rejection. These local checks do not satisfy the required staging rehearsal: hostname lookup still fails; both NetBird IPv4 and IPv6 attempts time out. No staging mutation has been confirmed; no production contact. The unmodified exhaustive gate is still running in one build slot.
Author
Owner

P1 — The documented first-use bootstrap can strand the old service on a full disk
Location: deploy/next/README.md:160–174 (preparation at 140–159).

Normal deploy and rollback correctly refuse an unlabelled running image, so this supervised procedure is the documented route to the first provenance-aware production image. Unlike the automated switch, it does not reserve candidate/recovery runtime space, and step 6 stops the service before writing the new current tag. If storage fills at that boundary, tagging fails and both primary and pinned fallback startup can fail because Podman still needs runtime/container metadata space. There is no protected recovery allocation to release. The earlier storage-reservation section describes space.sh's automated transaction; the bootstrap steps never invoke or reproduce that preparation. Calling the procedure supervised does not preserve the old process under this failure.

Repro: python3 review-artifacts/bootstrap-probe.py encodes README steps 6–8, with local mocks only. The control switches successfully. With persistent ENOSPC injected immediately after the first stop, new tagging fails, restoration tagging fails, primary startup fails, and fallback startup fails. Output: bootstrap recovery failed; old process is stopped; exit 28; running image is null. The model also attempts the pinned fallback after a failed primary start, giving recovery that additional chance. This is a model of the documented procedure, not execution of a repository bootstrap command (none exists), and it has not been confirmed on staging because SSH is blocked. The runtime-write premise is also acknowledged in space.sh:4–7.

Apply the same preparation and reserved-space recovery guarantees to first-use bootstrap, including immutable unit identities prepared before stop, and rehearse that route on staging. The unlabelled refusal itself is safe and is not the finding.

Reviewer probe source and output.

P1 — The documented first-use bootstrap can strand the old service on a full disk Location: deploy/next/README.md:160–174 (preparation at 140–159). Normal deploy and rollback correctly refuse an unlabelled running image, so this supervised procedure is the documented route to the first provenance-aware production image. Unlike the automated switch, it does not reserve candidate/recovery runtime space, and step 6 stops the service before writing the new current tag. If storage fills at that boundary, tagging fails and both primary and pinned fallback startup can fail because Podman still needs runtime/container metadata space. There is no protected recovery allocation to release. The earlier storage-reservation section describes space.sh's automated transaction; the bootstrap steps never invoke or reproduce that preparation. Calling the procedure supervised does not preserve the old process under this failure. Repro: `python3 review-artifacts/bootstrap-probe.py` encodes README steps 6–8, with local mocks only. The control switches successfully. With persistent ENOSPC injected immediately after the first stop, new tagging fails, restoration tagging fails, primary startup fails, and fallback startup fails. Output: `bootstrap recovery failed; old process is stopped`; exit 28; running image is null. The model also attempts the pinned fallback after a failed primary start, giving recovery that additional chance. This is a model of the documented procedure, not execution of a repository bootstrap command (none exists), and it has not been confirmed on staging because SSH is blocked. The runtime-write premise is also acknowledged in space.sh:4–7. Apply the same preparation and reserved-space recovery guarantees to first-use bootstrap, including immutable unit identities prepared before stop, and rehearse that route on staging. The unlabelled refusal itself is safe and is not the finding. [Reviewer probe source and output](https://git.kayg.org/attachments/2773ac9b-0b04-43e6-9d1c-3daf4050a04a).
Author
Owner

Verdict: fix first. One P1 and three P2 findings.

Independent sixth-round review of head d2ade1fb93 on fix/deploy-688-round5, into dev. Merge base: 3d0c6e33d5. All 11 commits, every touched deploy file and added test were inspected.

  • P1, deploy/next/README.md:160–174: the documented bootstrap for an unlabelled running image stops before retagging and lacks the automated path's protected runtime reserves. In the reviewer model of steps 6–8, persistent ENOSPC after stop rejects new tagging and both old-image startup paths, leaving the old process stopped. This is a model of the written procedure, not execution of a repository bootstrap command; none exists. The ordinary command's unlabelled refusal is safe. Finding and reproduction.

  • P2, deploy/next/switch.sh:102–104: a healthy labelled baseline without an image record or older rollback tag cannot perform its first normal update. The actual Bash transaction returns bootstrap predecessor missing, with zero stops. The guide does not provision that predecessor, and the supplied fixtures always seed two tags. Finding and reproduction.

  • P2, deploy/next/target.sh:35–46: the detached receiver does not enforce umask 077. The actual worker under real user systemd creates its receipt with mode 0664 (manager umask 0002), while the enclosing directory remains 0700. A private positive control adding only the worker umask creates mode 0600. The parent umask does not cross this boundary. Finding and real systemd reproduction.

  • P2, deploy/next/target.sh:43–59: if the receiver fails before opening the FIFO and exits before the producer opens it, ordinary > creates a regular .stream file. The real-systemd probe retained all 262144 artifact bytes in the control directory after failure, with the worker gone and lock free. Finding and real systemd reproduction.

Reviewer probe source and outputs. Fifteen independent transaction scenarios cover success, same-SHA rerun, code rollback, failure recovery, pinned fallback, unlabelled refusal and the missing-predecessor defect. Userdata sentinels remain unchanged. Two separate bootstrap-guide model cases cover the control and persistent ENOSPC.

Local validation is complete. Bash syntax and shellcheck pass. The original unmodified gate reached its 2400-second watchdog and exited 124, confirming 1,488 injection positions; it is not a full gate pass. A separate resumed run executed the remaining 1,682 positions through the original injection function and three-worker limit, completing all 3,170 combined cases with userdata unchanged. Remaining relay, artifact-build and next/cloud dry-run checks pass. The Rust HTTP health test passes with the reviewed full SHA supplied at compile time: 1 passed, 0 failed. That SHA is present in the passed test binary.

The supplied real user-systemd checks and private Quadlet 5.4.2 generation checks pass. The additional real-systemd probes reproduce the two transport defects above. Passing mocks do not resolve those findings. The separate committed-diff whitespace check reports one cosmetic blank line at space.sh:46.

The required real staging rehearsal remains blocked. Initial hostname attempts failed DNS lookup; the final bounded attempts timed out on agent@8by64-compute01.vpn.kayg.org and its current peer VPN IPv4 and IPv6 addresses. No remote command or mutation was confirmed. No real staging Podman/application deploy, rollback, first-use or full-disk rehearsal was completed. Resolve staging access and rehearse those paths before using this deploy route.

The reviewed head and PR head remain d2ade1fb93. No production contact, push, merge, deployment or issue closure was performed. Product files and the index are unchanged. Findings are in the job's untracked findings.md; evidence is attached here and is not committed.

Final verification, verbatim gate output and evidence.

Verdict: fix first. One P1 and three P2 findings. Independent sixth-round review of head d2ade1fb931b4ac74fb698015fcc95c3e7cf69c5 on fix/deploy-688-round5, into dev. Merge base: 3d0c6e33d588335c745a660163bf8bf5cdc8412d. All 11 commits, every touched deploy file and added test were inspected. - P1, deploy/next/README.md:160–174: the documented bootstrap for an unlabelled running image stops before retagging and lacks the automated path's protected runtime reserves. In the reviewer model of steps 6–8, persistent ENOSPC after stop rejects new tagging and both old-image startup paths, leaving the old process stopped. This is a model of the written procedure, not execution of a repository bootstrap command; none exists. The ordinary command's unlabelled refusal is safe. [Finding and reproduction](https://git.kayg.org/kayg/calternal/pulls/748#issuecomment-68518). - P2, deploy/next/switch.sh:102–104: a healthy labelled baseline without an image record or older rollback tag cannot perform its first normal update. The actual Bash transaction returns `bootstrap predecessor missing`, with zero stops. The guide does not provision that predecessor, and the supplied fixtures always seed two tags. [Finding and reproduction](https://git.kayg.org/kayg/calternal/pulls/748#issuecomment-68471). - P2, deploy/next/target.sh:35–46: the detached receiver does not enforce umask 077. The actual worker under real user systemd creates its receipt with mode 0664 (manager umask 0002), while the enclosing directory remains 0700. A private positive control adding only the worker umask creates mode 0600. The parent umask does not cross this boundary. [Finding and real systemd reproduction](https://git.kayg.org/kayg/calternal/pulls/748#issuecomment-68531). - P2, deploy/next/target.sh:43–59: if the receiver fails before opening the FIFO and exits before the producer opens it, ordinary `>` creates a regular .stream file. The real-systemd probe retained all 262144 artifact bytes in the control directory after failure, with the worker gone and lock free. [Finding and real systemd reproduction](https://git.kayg.org/kayg/calternal/pulls/748#issuecomment-68592). [Reviewer probe source and outputs](https://git.kayg.org/attachments/2773ac9b-0b04-43e6-9d1c-3daf4050a04a). Fifteen independent transaction scenarios cover success, same-SHA rerun, code rollback, failure recovery, pinned fallback, unlabelled refusal and the missing-predecessor defect. Userdata sentinels remain unchanged. Two separate bootstrap-guide model cases cover the control and persistent ENOSPC. Local validation is complete. Bash syntax and shellcheck pass. The original unmodified gate reached its 2400-second watchdog and exited 124, confirming 1,488 injection positions; it is not a full gate pass. A separate resumed run executed the remaining 1,682 positions through the original injection function and three-worker limit, completing all 3,170 combined cases with userdata unchanged. Remaining relay, artifact-build and next/cloud dry-run checks pass. The Rust HTTP health test passes with the reviewed full SHA supplied at compile time: 1 passed, 0 failed. That SHA is present in the passed test binary. The supplied real user-systemd checks and private Quadlet 5.4.2 generation checks pass. The additional real-systemd probes reproduce the two transport defects above. Passing mocks do not resolve those findings. The separate committed-diff whitespace check reports one cosmetic blank line at space.sh:46. The required real staging rehearsal remains blocked. Initial hostname attempts failed DNS lookup; the final bounded attempts timed out on agent@8by64-compute01.vpn.kayg.org and its current peer VPN IPv4 and IPv6 addresses. No remote command or mutation was confirmed. No real staging Podman/application deploy, rollback, first-use or full-disk rehearsal was completed. Resolve staging access and rehearse those paths before using this deploy route. The reviewed head and PR head remain d2ade1fb931b4ac74fb698015fcc95c3e7cf69c5. No production contact, push, merge, deployment or issue closure was performed. Product files and the index are unchanged. Findings are in the job's untracked findings.md; evidence is attached here and is not committed. [Final verification, verbatim gate output and evidence](https://git.kayg.org/kayg/calternal/pulls/748#issuecomment-68617).
Author
Owner

P2 — Detached receipt worker does not enforce private file modes
Location: deploy/next/target.sh:35–46.

The caller sets umask 077, but systemd starts the detached worker with the service manager's own umask. The worker sets no umask and no UMask property before cat creates $stage/input. Its receipt file therefore does not reliably have mode 0600. The real user-systemd probe measured worker umask 0002, receipt mode 0664 and directory mode 0700. A private copy with only umask 077 added inside the worker measured receipt mode 0600. No tracked product file was edited. The enclosing directory stays private, so no direct data exposure was observed; the explicit 0700/0600 staging contract is nevertheless broken.

Repro: python3 review-artifacts/receipt-mode-probe.py. This executes the actual target.sh receiver via real user systemd, with filesystem prefixes and the required root owner relocated to this account's private fixture. Only systemd-run/systemctl wrappers select the user manager. The payload records umask and stat metadata, with no application, network or secret access. The second run is an explicitly labelled positive control with the worker umask added to the private script copy. It returns receiver umask=0002 receipt mode=664 directory mode=700 for the PR and receiver umask=0077 receipt mode=600 directory mode=700 for the control.

Set umask 077 inside the detached worker or set its systemd UMask property. Add receipt-mode assertions to the real-systemd transport test; the ordinary process mock inherits the caller's umask and misses this boundary. Production/staging manager defaults have not been measured because target SSH is blocked.

Real systemd probe source and output.

P2 — Detached receipt worker does not enforce private file modes Location: deploy/next/target.sh:35–46. The caller sets umask 077, but systemd starts the detached worker with the service manager's own umask. The worker sets no umask and no UMask property before cat creates $stage/input. Its receipt file therefore does not reliably have mode 0600. The real user-systemd probe measured worker umask 0002, receipt mode 0664 and directory mode 0700. A private copy with only `umask 077` added inside the worker measured receipt mode 0600. No tracked product file was edited. The enclosing directory stays private, so no direct data exposure was observed; the explicit 0700/0600 staging contract is nevertheless broken. Repro: `python3 review-artifacts/receipt-mode-probe.py`. This executes the actual target.sh receiver via real user systemd, with filesystem prefixes and the required root owner relocated to this account's private fixture. Only systemd-run/systemctl wrappers select the user manager. The payload records umask and stat metadata, with no application, network or secret access. The second run is an explicitly labelled positive control with the worker umask added to the private script copy. It returns `receiver umask=0002 receipt mode=664 directory mode=700` for the PR and `receiver umask=0077 receipt mode=600 directory mode=700` for the control. Set umask 077 inside the detached worker or set its systemd UMask property. Add receipt-mode assertions to the real-systemd transport test; the ordinary process mock inherits the caller's umask and misses this boundary. Production/staging manager defaults have not been measured because target SSH is blocked. [Real systemd probe source and output](https://git.kayg.org/attachments/093ec5f0-7c60-40bc-83ab-2b991b561fe5).
Author
Owner

Validation update: the unmodified gate reached its 2400-second watchdog and exited 124. No assertion failure was printed. Its log explicitly confirms 1,488 injection positions. This is not a full gate pass. I am preserving the original output, running the targeted added Rust health test, then resuming only unconfirmed positions through the original probe with its unchanged three-worker limit. The final comment will include the original exit status and gate output verbatim, plus separate resumed coverage. Staging SSH remains blocked; the verdict stays fix first.

Validation update: the unmodified gate reached its 2400-second watchdog and exited 124. No assertion failure was printed. Its log explicitly confirms 1,488 injection positions. This is not a full gate pass. I am preserving the original output, running the targeted added Rust health test, then resuming only unconfirmed positions through the original probe with its unchanged three-worker limit. The final comment will include the original exit status and gate output verbatim, plus separate resumed coverage. Staging SSH remains blocked; the verdict stays fix first.
Author
Owner

The targeted added Rust HTTP health test passes with CALTERNAL_REVISION=d2ade1fb931b4ac74fb698015fcc95c3e7cf69c5: 1 passed, 0 failed, 5 filtered out. Cargo ran with --locked -j3 through the local build slot. Resuming the remaining 1,682 unconfirmed injection positions now, using the original probe, with its three-worker limit; relay/build/dry-run checks follow. The original gate exit 124 and its output remain preserved separately.

The targeted added Rust HTTP health test passes with CALTERNAL_REVISION=d2ade1fb931b4ac74fb698015fcc95c3e7cf69c5: 1 passed, 0 failed, 5 filtered out. Cargo ran with --locked -j3 through the local build slot. Resuming the remaining 1,682 unconfirmed injection positions now, using the original probe, with its three-worker limit; relay/build/dry-run checks follow. The original gate exit 124 and its output remain preserved separately.
Author
Owner

P2 — A receiver setup failure can turn the FIFO into a retained artifact file outside the lock
Location: deploy/next/target.sh:43–46 and 59; cleanup at 16–19.

The worker's EXIT trap removes stream when setup fails before opening the FIFO. If it finishes before the caller reaches line 59, cat > "$job/stream" creates a regular file at that now-missing path. The worker has already released the transaction lock. The producer writes the artifact into the control directory under /run, then the caller returns failure and retains it because started=1. No worker remains to remove the recreated file. This breaks the locked-upload and bounded-control-storage guarantees; image bytes can consume /run rather than the intended receipt filesystem.

Repro: python3 review-artifacts/receiver-exit-probe.py runs actual target.sh through real user systemd, with private filesystem/owner relocation. The normal control transfers and removes stream. The failure case leaves the private fixture Quadlet directory absent to force receiver mktemp failure, then uses an inotify handshake after the caller unlocks to schedule producer opening after the worker exits. This controls the race ordering without changing transaction logic or adding fixed sleeps. Before producer open: result=001, stream absent, lock available. Afterward: caller exit=1, transaction never entered, stream is a regular file retaining all 262144 input bytes. No app or target was contacted, and no service outage is claimed from this small fixture.

Open the FIFO without O_CREAT so a vanished receiver cannot redirect the artifact to a regular control file. A status/type check followed by ordinary > still has an unlink race. Cover receiver failure before FIFO open, as well as caller disappearance, and remove any failed upload residue.

Real systemd probe source and output.

P2 — A receiver setup failure can turn the FIFO into a retained artifact file outside the lock Location: deploy/next/target.sh:43–46 and 59; cleanup at 16–19. The worker's EXIT trap removes stream when setup fails before opening the FIFO. If it finishes before the caller reaches line 59, `cat > "$job/stream"` creates a regular file at that now-missing path. The worker has already released the transaction lock. The producer writes the artifact into the control directory under /run, then the caller returns failure and retains it because started=1. No worker remains to remove the recreated file. This breaks the locked-upload and bounded-control-storage guarantees; image bytes can consume /run rather than the intended receipt filesystem. Repro: `python3 review-artifacts/receiver-exit-probe.py` runs actual target.sh through real user systemd, with private filesystem/owner relocation. The normal control transfers and removes stream. The failure case leaves the private fixture Quadlet directory absent to force receiver mktemp failure, then uses an inotify handshake after the caller unlocks to schedule producer opening after the worker exits. This controls the race ordering without changing transaction logic or adding fixed sleeps. Before producer open: result=001, stream absent, lock available. Afterward: caller exit=1, transaction never entered, stream is a regular file retaining all 262144 input bytes. No app or target was contacted, and no service outage is claimed from this small fixture. Open the FIFO without O_CREAT so a vanished receiver cannot redirect the artifact to a regular control file. A status/type check followed by ordinary `>` still has an unlink race. Cover receiver failure before FIFO open, as well as caller disappearance, and remove any failed upload residue. [Real systemd probe source and output](https://git.kayg.org/attachments/2aac8efe-5c1d-4a2f-8eeb-8055373256ee).
Author
Owner

Independent review finished. Verdict: fix first. One P1 and three P2 findings are in the review and attached findings.md.

Head: d2ade1fb93. Branch: fix/deploy-688-round5 into dev. Tracked worktree and index are unchanged. No push, merge, deployment, issue closure, production contact or evidence commit was performed by this job.

Final findings, reviewer probe source and validation evidence.

The original unmodified gate exited 124 after its 2400-second watchdog, with 1488 injection positions explicitly confirmed. Its output below is verbatim and its saved hash is unchanged. It is not a full gate pass. The separately labelled resumed run executed the remaining 1682 positions through the original injection function and three-worker limit, completing all 3170 combined cases, then passed relay/build/dry-run checks. The Rust HTTP health test passed with the reviewed full SHA supplied at compile time.

The required real staging rehearsal remains blocked: initial hostname attempts failed DNS lookup; final attempts to the hostname and current peer VPN IPv4/IPv6 addresses timed out. No remote command or mutation was confirmed. Real user-systemd fixture probes were run locally and reproduce two transport findings; they do not establish application behavior on staging.

Original gate output verbatim (exit 124):

bash -n: PASS
shellcheck: PASS
diff whitespace: PASS
Quadlet unit transformation: PASS (generator unavailable; compile check skipped)
target caller lost before FIFO open cleans upload and releases lock: PASS
target partial receipt failure cleans private upload: PASS
target normal: PASS
target failed-transaction: PASS
target closed-output: PASS
target dropped-caller: PASS
target locked-receipt: PASS
remote malformed-pointer: PASS
remote multiline-pointer: PASS
remote equal-pointers: PASS
remote symlink-pointer: PASS
remote invalid-generator: PASS
remote closed-output: PASS
remote generator-fails: PASS
remote byte-exact-recovery: PASS
remote normal: PASS
remote stale-recovery-definition: PASS
remote stale-primary-recovery: PASS
remote post-start-unit-rename: PASS
remote retained-recovery: PASS
remote same-revision different digest: PASS
remote disk-full: PASS
remote tag-fails: PASS
remote pointer-commit-fails: PASS
remote start-fails: PASS
remote health-fails: PASS
remote stale-response: PASS
remote inactive-systemd: PASS
remote wrong-container-image: PASS
remote stop-fails: PASS
remote reload-fails: PASS
remote unit-install-fails: PASS
remote unit-move-fails: PASS
remote load-fails: PASS
remote checksum-mismatch: PASS
remote label-mismatch: PASS
remote id-mismatch: PASS
remote pre-unhealthy: PASS
remote open-secret: PASS
remote missing-secret: PASS
remote symlink-secret: PASS
remote unlabelled-baseline: PASS
remote persistent-daemon-reload: PASS
remote persistent-unit-rename: PASS
remote stop-returns-error-after-stopping: PASS
remote post-stop-podman-error: PASS
remote recovery-start-fails: PASS
remote spaced-open-secret: PASS
remote spaced-new-open-secret: PASS
remote multiple-open-secret: PASS
remote optional-open-secret: PASS
remote service-open-secret: PASS
remote continued-open-secret: PASS
remote quoted-open-secret: PASS
remote unsupported-specifier: PASS
remote unsupported-escape: PASS
remote repeated-open-secret: PASS
remote spaced-safe-secret: PASS
remote multiple-safe-secret: PASS
remote optional-safe-secret: PASS
remote service-safe-secret: PASS
remote continued-safe-secret: PASS
remote quoted-safe-secret: PASS
remote home-safe-secret: PASS
remote percent-safe-secret: PASS
remote signal: PASS
remote rerun: PASS
remote manual-rollback: PASS
remote concurrent upload/switch serialized: PASS
injection deploy/normal/before: 40/91 PASS
injection deploy/normal/before: 80/91 PASS
injection deploy/normal/after: 40/91 PASS
injection deploy/normal/after: 80/91 PASS
state machine deploy/normal: 91 steps, before/after failures PASS
persistent disk deploy/normal/before: 40/91 PASS
persistent disk deploy/normal/before: 80/91 PASS
persistent disk deploy/normal/after: 40/91 PASS
persistent disk deploy/normal/after: 80/91 PASS
persistent full disk deploy/normal: every step before/after PASS (including recovery)
state machine same-sha/normal: 34 steps, before/after failures PASS
state machine same-image/normal: 34 steps, before/after failures PASS
injection rollback/normal/before: 40/78 PASS
injection rollback/normal/after: 40/78 PASS
state machine rollback/normal: 78 steps, before/after failures PASS
persistent disk rollback/normal/before: 40/78 PASS
persistent disk rollback/normal/after: 40/78 PASS
persistent full disk rollback/normal: every step before/after PASS (including recovery)
state machine bootstrap/normal: 8 steps, before/after failures PASS
state machine bootstrap-missing-unit/normal: 5 steps, before/after failures PASS
injection deploy/health-fails/before: 40/106 PASS
injection deploy/health-fails/before: 80/106 PASS
injection deploy/health-fails/after: 40/106 PASS
injection deploy/health-fails/after: 80/106 PASS
state machine deploy/health-fails: 106 steps, before/after failures PASS
persistent disk deploy/health-fails/before: 40/106 PASS
persistent disk deploy/health-fails/before: 80/106 PASS
persistent disk deploy/health-fails/after: 40/106 PASS
persistent disk deploy/health-fails/after: 80/106 PASS
persistent full disk deploy/health-fails: every step before/after PASS (including recovery)
injection rollback/health-fails/before: 40/93 PASS
injection rollback/health-fails/before: 80/93 PASS
injection rollback/health-fails/after: 40/93 PASS
injection rollback/health-fails/after: 80/93 PASS
state machine rollback/health-fails: 93 steps, before/after failures PASS
persistent disk rollback/health-fails/before: 40/93 PASS

Resumed validation output verbatim (exit 0; includes explicitly confirmed earlier positions):

RESUMED validation: only explicitly confirmed original-gate positions are skipped.
Confirmed prefix: [{"path": "deploy", "mode": "normal", "when": "before", "disk_full": false, "through_step": 91}, {"path": "deploy", "mode": "normal", "when": "after", "disk_full": false, "through_step": 91}, {"path": "deploy", "mode": "normal", "when": "before", "disk_full": true, "through_step": 91}, {"path": "deploy", "mode": "normal", "when": "after", "disk_full": true, "through_step": 91}, {"path": "same-sha", "mode": "normal", "when": "before", "disk_full": false, "through_step": 34}, {"path": "same-sha", "mode": "normal", "when": "after", "disk_full": false, "through_step": 34}, {"path": "same-image", "mode": "normal", "when": "before", "disk_full": false, "through_step": 34}, {"path": "same-image", "mode": "normal", "when": "after", "disk_full": false, "through_step": 34}, {"path": "rollback", "mode": "normal", "when": "before", "disk_full": false, "through_step": 78}, {"path": "rollback", "mode": "normal", "when": "after", "disk_full": false, "through_step": 78}, {"path": "rollback", "mode": "normal", "when": "before", "disk_full": true, "through_step": 78}, {"path": "rollback", "mode": "normal", "when": "after", "disk_full": true, "through_step": 78}, {"path": "bootstrap", "mode": "normal", "when": "before", "disk_full": false, "through_step": 8}, {"path": "bootstrap", "mode": "normal", "when": "after", "disk_full": false, "through_step": 8}, {"path": "bootstrap-missing-unit", "mode": "normal", "when": "before", "disk_full": false, "through_step": 5}, {"path": "bootstrap-missing-unit", "mode": "normal", "when": "after", "disk_full": false, "through_step": 5}, {"path": "deploy", "mode": "health-fails", "when": "before", "disk_full": false, "through_step": 106}, {"path": "deploy", "mode": "health-fails", "when": "after", "disk_full": false, "through_step": 106}, {"path": "deploy", "mode": "health-fails", "when": "before", "disk_full": true, "through_step": 106}, {"path": "deploy", "mode": "health-fails", "when": "after", "disk_full": true, "through_step": 106}, {"path": "rollback", "mode": "health-fails", "when": "before", "disk_full": false, "through_step": 93}, {"path": "rollback", "mode": "health-fails", "when": "after", "disk_full": false, "through_step": 93}, {"path": "rollback", "mode": "health-fails", "when": "before", "disk_full": true, "through_step": 40}]
injection deploy/normal/before: 40/91 PASS
injection deploy/normal/before: 80/91 PASS
injection deploy/normal/after: 40/91 PASS
injection deploy/normal/after: 80/91 PASS
state machine deploy/normal: 91 steps, before/after failures PASS
persistent disk deploy/normal/before: 40/91 PASS
persistent disk deploy/normal/before: 80/91 PASS
persistent disk deploy/normal/after: 40/91 PASS
persistent disk deploy/normal/after: 80/91 PASS
persistent full disk deploy/normal: every step before/after PASS (including recovery)
state machine same-sha/normal: 34 steps, before/after failures PASS
state machine same-image/normal: 34 steps, before/after failures PASS
injection rollback/normal/before: 40/78 PASS
injection rollback/normal/after: 40/78 PASS
state machine rollback/normal: 78 steps, before/after failures PASS
persistent disk rollback/normal/before: 40/78 PASS
persistent disk rollback/normal/after: 40/78 PASS
persistent full disk rollback/normal: every step before/after PASS (including recovery)
state machine bootstrap/normal: 8 steps, before/after failures PASS
state machine bootstrap-missing-unit/normal: 5 steps, before/after failures PASS
injection deploy/health-fails/before: 40/106 PASS
injection deploy/health-fails/before: 80/106 PASS
injection deploy/health-fails/after: 40/106 PASS
injection deploy/health-fails/after: 80/106 PASS
state machine deploy/health-fails: 106 steps, before/after failures PASS
persistent disk deploy/health-fails/before: 40/106 PASS
persistent disk deploy/health-fails/before: 80/106 PASS
persistent disk deploy/health-fails/after: 40/106 PASS
persistent disk deploy/health-fails/after: 80/106 PASS
persistent full disk deploy/health-fails: every step before/after PASS (including recovery)
injection rollback/health-fails/before: 40/93 PASS
injection rollback/health-fails/before: 80/93 PASS
injection rollback/health-fails/after: 40/93 PASS
injection rollback/health-fails/after: 80/93 PASS
state machine rollback/health-fails: 93 steps, before/after failures PASS
persistent disk rollback/health-fails/before: 40/93 PASS
persistent disk rollback/health-fails/before: 80/93 PASS
persistent disk rollback/health-fails/after: 40/93 PASS
persistent disk rollback/health-fails/after: 80/93 PASS
persistent full disk rollback/health-fails: every step before/after PASS (including recovery)
injection deploy/stale-primary-recovery/before: 40/104 PASS
injection deploy/stale-primary-recovery/before: 80/104 PASS
injection deploy/stale-primary-recovery/after: 40/104 PASS
injection deploy/stale-primary-recovery/after: 80/104 PASS
state machine deploy/stale-primary-recovery: 104 steps, before/after failures PASS
persistent disk deploy/stale-primary-recovery/before: 40/104 PASS
persistent disk deploy/stale-primary-recovery/before: 80/104 PASS
persistent disk deploy/stale-primary-recovery/after: 40/104 PASS
persistent disk deploy/stale-primary-recovery/after: 80/104 PASS
persistent full disk deploy/stale-primary-recovery: every step before/after PASS (including recovery)
injection rollback/stale-primary-recovery/before: 40/91 PASS
injection rollback/stale-primary-recovery/before: 80/91 PASS
injection rollback/stale-primary-recovery/after: 40/91 PASS
injection rollback/stale-primary-recovery/after: 80/91 PASS
state machine rollback/stale-primary-recovery: 91 steps, before/after failures PASS
persistent disk rollback/stale-primary-recovery/before: 40/91 PASS
persistent disk rollback/stale-primary-recovery/before: 80/91 PASS
persistent disk rollback/stale-primary-recovery/after: 40/91 PASS
persistent disk rollback/stale-primary-recovery/after: 80/91 PASS
persistent full disk rollback/stale-primary-recovery: every step before/after PASS (including recovery)
injection deploy/post-start-unit-rename/before: 40/101 PASS
injection deploy/post-start-unit-rename/before: 80/101 PASS
injection deploy/post-start-unit-rename/after: 40/101 PASS
injection deploy/post-start-unit-rename/after: 80/101 PASS
state machine deploy/post-start-unit-rename: 101 steps, before/after failures PASS
persistent disk deploy/post-start-unit-rename/before: 40/101 PASS
persistent disk deploy/post-start-unit-rename/before: 80/101 PASS
persistent disk deploy/post-start-unit-rename/after: 40/101 PASS
persistent disk deploy/post-start-unit-rename/after: 80/101 PASS
persistent full disk deploy/post-start-unit-rename: every step before/after PASS (including recovery)
injection rollback/post-start-unit-rename/before: 40/88 PASS
injection rollback/post-start-unit-rename/before: 80/88 PASS
injection rollback/post-start-unit-rename/after: 40/88 PASS
injection rollback/post-start-unit-rename/after: 80/88 PASS
state machine rollback/post-start-unit-rename: 88 steps, before/after failures PASS
persistent disk rollback/post-start-unit-rename/before: 40/88 PASS
persistent disk rollback/post-start-unit-rename/before: 80/88 PASS
persistent disk rollback/post-start-unit-rename/after: 40/88 PASS
persistent disk rollback/post-start-unit-rename/after: 80/88 PASS
persistent full disk rollback/post-start-unit-rename: every step before/after PASS (including recovery)
exhaustive failure injection: PASS (3170 cases; userdata unchanged)
Combined injection coverage: {"previously_confirmed": 1488, "executed_now": 1682}
relay success: PASS
relay save-fails: PASS
relay target-fails: PASS
non-dev revision and mutable tags rejected for next/cloud: PASS
data rollback options rejected: PASS
build normal: PASS
build dev-moved: PASS
build build-fails: PASS
build bad-id: PASS
build wrong-label: PASS
build save-fails: PASS
build export-wrong-label: PASS
build multiple-images: PASS
build root-owned-iid: PASS
Remaining relay/build and next/cloud dry-run checks: PASS
Resumed deploy regression checks: PASS (combined coverage; original gate status is reported separately)

Targeted Rust health test:

running 1 test
test health_answers_without_signing_in ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 5 filtered out; finished in 0.00s
Independent review finished. Verdict: fix first. One P1 and three P2 findings are in the review and attached findings.md. Head: d2ade1fb931b4ac74fb698015fcc95c3e7cf69c5. Branch: fix/deploy-688-round5 into dev. Tracked worktree and index are unchanged. No push, merge, deployment, issue closure, production contact or evidence commit was performed by this job. [Final findings, reviewer probe source and validation evidence](https://git.kayg.org/attachments/fc6d657b-3fbf-4285-8b91-05facb7efa7f). The original unmodified gate exited 124 after its 2400-second watchdog, with 1488 injection positions explicitly confirmed. Its output below is verbatim and its saved hash is unchanged. It is not a full gate pass. The separately labelled resumed run executed the remaining 1682 positions through the original injection function and three-worker limit, completing all 3170 combined cases, then passed relay/build/dry-run checks. The Rust HTTP health test passed with the reviewed full SHA supplied at compile time. The required real staging rehearsal remains blocked: initial hostname attempts failed DNS lookup; final attempts to the hostname and current peer VPN IPv4/IPv6 addresses timed out. No remote command or mutation was confirmed. Real user-systemd fixture probes were run locally and reproduce two transport findings; they do not establish application behavior on staging. Original gate output verbatim (exit 124): ```text bash -n: PASS shellcheck: PASS diff whitespace: PASS Quadlet unit transformation: PASS (generator unavailable; compile check skipped) target caller lost before FIFO open cleans upload and releases lock: PASS target partial receipt failure cleans private upload: PASS target normal: PASS target failed-transaction: PASS target closed-output: PASS target dropped-caller: PASS target locked-receipt: PASS remote malformed-pointer: PASS remote multiline-pointer: PASS remote equal-pointers: PASS remote symlink-pointer: PASS remote invalid-generator: PASS remote closed-output: PASS remote generator-fails: PASS remote byte-exact-recovery: PASS remote normal: PASS remote stale-recovery-definition: PASS remote stale-primary-recovery: PASS remote post-start-unit-rename: PASS remote retained-recovery: PASS remote same-revision different digest: PASS remote disk-full: PASS remote tag-fails: PASS remote pointer-commit-fails: PASS remote start-fails: PASS remote health-fails: PASS remote stale-response: PASS remote inactive-systemd: PASS remote wrong-container-image: PASS remote stop-fails: PASS remote reload-fails: PASS remote unit-install-fails: PASS remote unit-move-fails: PASS remote load-fails: PASS remote checksum-mismatch: PASS remote label-mismatch: PASS remote id-mismatch: PASS remote pre-unhealthy: PASS remote open-secret: PASS remote missing-secret: PASS remote symlink-secret: PASS remote unlabelled-baseline: PASS remote persistent-daemon-reload: PASS remote persistent-unit-rename: PASS remote stop-returns-error-after-stopping: PASS remote post-stop-podman-error: PASS remote recovery-start-fails: PASS remote spaced-open-secret: PASS remote spaced-new-open-secret: PASS remote multiple-open-secret: PASS remote optional-open-secret: PASS remote service-open-secret: PASS remote continued-open-secret: PASS remote quoted-open-secret: PASS remote unsupported-specifier: PASS remote unsupported-escape: PASS remote repeated-open-secret: PASS remote spaced-safe-secret: PASS remote multiple-safe-secret: PASS remote optional-safe-secret: PASS remote service-safe-secret: PASS remote continued-safe-secret: PASS remote quoted-safe-secret: PASS remote home-safe-secret: PASS remote percent-safe-secret: PASS remote signal: PASS remote rerun: PASS remote manual-rollback: PASS remote concurrent upload/switch serialized: PASS injection deploy/normal/before: 40/91 PASS injection deploy/normal/before: 80/91 PASS injection deploy/normal/after: 40/91 PASS injection deploy/normal/after: 80/91 PASS state machine deploy/normal: 91 steps, before/after failures PASS persistent disk deploy/normal/before: 40/91 PASS persistent disk deploy/normal/before: 80/91 PASS persistent disk deploy/normal/after: 40/91 PASS persistent disk deploy/normal/after: 80/91 PASS persistent full disk deploy/normal: every step before/after PASS (including recovery) state machine same-sha/normal: 34 steps, before/after failures PASS state machine same-image/normal: 34 steps, before/after failures PASS injection rollback/normal/before: 40/78 PASS injection rollback/normal/after: 40/78 PASS state machine rollback/normal: 78 steps, before/after failures PASS persistent disk rollback/normal/before: 40/78 PASS persistent disk rollback/normal/after: 40/78 PASS persistent full disk rollback/normal: every step before/after PASS (including recovery) state machine bootstrap/normal: 8 steps, before/after failures PASS state machine bootstrap-missing-unit/normal: 5 steps, before/after failures PASS injection deploy/health-fails/before: 40/106 PASS injection deploy/health-fails/before: 80/106 PASS injection deploy/health-fails/after: 40/106 PASS injection deploy/health-fails/after: 80/106 PASS state machine deploy/health-fails: 106 steps, before/after failures PASS persistent disk deploy/health-fails/before: 40/106 PASS persistent disk deploy/health-fails/before: 80/106 PASS persistent disk deploy/health-fails/after: 40/106 PASS persistent disk deploy/health-fails/after: 80/106 PASS persistent full disk deploy/health-fails: every step before/after PASS (including recovery) injection rollback/health-fails/before: 40/93 PASS injection rollback/health-fails/before: 80/93 PASS injection rollback/health-fails/after: 40/93 PASS injection rollback/health-fails/after: 80/93 PASS state machine rollback/health-fails: 93 steps, before/after failures PASS persistent disk rollback/health-fails/before: 40/93 PASS ``` Resumed validation output verbatim (exit 0; includes explicitly confirmed earlier positions): ```text RESUMED validation: only explicitly confirmed original-gate positions are skipped. Confirmed prefix: [{"path": "deploy", "mode": "normal", "when": "before", "disk_full": false, "through_step": 91}, {"path": "deploy", "mode": "normal", "when": "after", "disk_full": false, "through_step": 91}, {"path": "deploy", "mode": "normal", "when": "before", "disk_full": true, "through_step": 91}, {"path": "deploy", "mode": "normal", "when": "after", "disk_full": true, "through_step": 91}, {"path": "same-sha", "mode": "normal", "when": "before", "disk_full": false, "through_step": 34}, {"path": "same-sha", "mode": "normal", "when": "after", "disk_full": false, "through_step": 34}, {"path": "same-image", "mode": "normal", "when": "before", "disk_full": false, "through_step": 34}, {"path": "same-image", "mode": "normal", "when": "after", "disk_full": false, "through_step": 34}, {"path": "rollback", "mode": "normal", "when": "before", "disk_full": false, "through_step": 78}, {"path": "rollback", "mode": "normal", "when": "after", "disk_full": false, "through_step": 78}, {"path": "rollback", "mode": "normal", "when": "before", "disk_full": true, "through_step": 78}, {"path": "rollback", "mode": "normal", "when": "after", "disk_full": true, "through_step": 78}, {"path": "bootstrap", "mode": "normal", "when": "before", "disk_full": false, "through_step": 8}, {"path": "bootstrap", "mode": "normal", "when": "after", "disk_full": false, "through_step": 8}, {"path": "bootstrap-missing-unit", "mode": "normal", "when": "before", "disk_full": false, "through_step": 5}, {"path": "bootstrap-missing-unit", "mode": "normal", "when": "after", "disk_full": false, "through_step": 5}, {"path": "deploy", "mode": "health-fails", "when": "before", "disk_full": false, "through_step": 106}, {"path": "deploy", "mode": "health-fails", "when": "after", "disk_full": false, "through_step": 106}, {"path": "deploy", "mode": "health-fails", "when": "before", "disk_full": true, "through_step": 106}, {"path": "deploy", "mode": "health-fails", "when": "after", "disk_full": true, "through_step": 106}, {"path": "rollback", "mode": "health-fails", "when": "before", "disk_full": false, "through_step": 93}, {"path": "rollback", "mode": "health-fails", "when": "after", "disk_full": false, "through_step": 93}, {"path": "rollback", "mode": "health-fails", "when": "before", "disk_full": true, "through_step": 40}] injection deploy/normal/before: 40/91 PASS injection deploy/normal/before: 80/91 PASS injection deploy/normal/after: 40/91 PASS injection deploy/normal/after: 80/91 PASS state machine deploy/normal: 91 steps, before/after failures PASS persistent disk deploy/normal/before: 40/91 PASS persistent disk deploy/normal/before: 80/91 PASS persistent disk deploy/normal/after: 40/91 PASS persistent disk deploy/normal/after: 80/91 PASS persistent full disk deploy/normal: every step before/after PASS (including recovery) state machine same-sha/normal: 34 steps, before/after failures PASS state machine same-image/normal: 34 steps, before/after failures PASS injection rollback/normal/before: 40/78 PASS injection rollback/normal/after: 40/78 PASS state machine rollback/normal: 78 steps, before/after failures PASS persistent disk rollback/normal/before: 40/78 PASS persistent disk rollback/normal/after: 40/78 PASS persistent full disk rollback/normal: every step before/after PASS (including recovery) state machine bootstrap/normal: 8 steps, before/after failures PASS state machine bootstrap-missing-unit/normal: 5 steps, before/after failures PASS injection deploy/health-fails/before: 40/106 PASS injection deploy/health-fails/before: 80/106 PASS injection deploy/health-fails/after: 40/106 PASS injection deploy/health-fails/after: 80/106 PASS state machine deploy/health-fails: 106 steps, before/after failures PASS persistent disk deploy/health-fails/before: 40/106 PASS persistent disk deploy/health-fails/before: 80/106 PASS persistent disk deploy/health-fails/after: 40/106 PASS persistent disk deploy/health-fails/after: 80/106 PASS persistent full disk deploy/health-fails: every step before/after PASS (including recovery) injection rollback/health-fails/before: 40/93 PASS injection rollback/health-fails/before: 80/93 PASS injection rollback/health-fails/after: 40/93 PASS injection rollback/health-fails/after: 80/93 PASS state machine rollback/health-fails: 93 steps, before/after failures PASS persistent disk rollback/health-fails/before: 40/93 PASS persistent disk rollback/health-fails/before: 80/93 PASS persistent disk rollback/health-fails/after: 40/93 PASS persistent disk rollback/health-fails/after: 80/93 PASS persistent full disk rollback/health-fails: every step before/after PASS (including recovery) injection deploy/stale-primary-recovery/before: 40/104 PASS injection deploy/stale-primary-recovery/before: 80/104 PASS injection deploy/stale-primary-recovery/after: 40/104 PASS injection deploy/stale-primary-recovery/after: 80/104 PASS state machine deploy/stale-primary-recovery: 104 steps, before/after failures PASS persistent disk deploy/stale-primary-recovery/before: 40/104 PASS persistent disk deploy/stale-primary-recovery/before: 80/104 PASS persistent disk deploy/stale-primary-recovery/after: 40/104 PASS persistent disk deploy/stale-primary-recovery/after: 80/104 PASS persistent full disk deploy/stale-primary-recovery: every step before/after PASS (including recovery) injection rollback/stale-primary-recovery/before: 40/91 PASS injection rollback/stale-primary-recovery/before: 80/91 PASS injection rollback/stale-primary-recovery/after: 40/91 PASS injection rollback/stale-primary-recovery/after: 80/91 PASS state machine rollback/stale-primary-recovery: 91 steps, before/after failures PASS persistent disk rollback/stale-primary-recovery/before: 40/91 PASS persistent disk rollback/stale-primary-recovery/before: 80/91 PASS persistent disk rollback/stale-primary-recovery/after: 40/91 PASS persistent disk rollback/stale-primary-recovery/after: 80/91 PASS persistent full disk rollback/stale-primary-recovery: every step before/after PASS (including recovery) injection deploy/post-start-unit-rename/before: 40/101 PASS injection deploy/post-start-unit-rename/before: 80/101 PASS injection deploy/post-start-unit-rename/after: 40/101 PASS injection deploy/post-start-unit-rename/after: 80/101 PASS state machine deploy/post-start-unit-rename: 101 steps, before/after failures PASS persistent disk deploy/post-start-unit-rename/before: 40/101 PASS persistent disk deploy/post-start-unit-rename/before: 80/101 PASS persistent disk deploy/post-start-unit-rename/after: 40/101 PASS persistent disk deploy/post-start-unit-rename/after: 80/101 PASS persistent full disk deploy/post-start-unit-rename: every step before/after PASS (including recovery) injection rollback/post-start-unit-rename/before: 40/88 PASS injection rollback/post-start-unit-rename/before: 80/88 PASS injection rollback/post-start-unit-rename/after: 40/88 PASS injection rollback/post-start-unit-rename/after: 80/88 PASS state machine rollback/post-start-unit-rename: 88 steps, before/after failures PASS persistent disk rollback/post-start-unit-rename/before: 40/88 PASS persistent disk rollback/post-start-unit-rename/before: 80/88 PASS persistent disk rollback/post-start-unit-rename/after: 40/88 PASS persistent disk rollback/post-start-unit-rename/after: 80/88 PASS persistent full disk rollback/post-start-unit-rename: every step before/after PASS (including recovery) exhaustive failure injection: PASS (3170 cases; userdata unchanged) Combined injection coverage: {"previously_confirmed": 1488, "executed_now": 1682} relay success: PASS relay save-fails: PASS relay target-fails: PASS non-dev revision and mutable tags rejected for next/cloud: PASS data rollback options rejected: PASS build normal: PASS build dev-moved: PASS build build-fails: PASS build bad-id: PASS build wrong-label: PASS build save-fails: PASS build export-wrong-label: PASS build multiple-images: PASS build root-owned-iid: PASS Remaining relay/build and next/cloud dry-run checks: PASS Resumed deploy regression checks: PASS (combined coverage; original gate status is reported separately) ``` Targeted Rust health test: ```text running 1 test test health_answers_without_signing_in ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 5 filtered out; finished in 0.00s ```
Author
Owner

Starting deploy-fix-6 on staging 8by64-compute01. Base d2ade1fb93, fetched fix/deploy-688-round5. Working branch fix/deploy-688-round6: the job's hard rule requires a new branch and PR because this forge ignores pushes to an open PR branch. I will link the replacement here and keep #748 open.

Scope: scripted bootstrap with all image/unit writes and free-space checks before stop; missing first predecessor; worker umask; FIFO producer race. Gates: bash -n, shellcheck, all deploy tests, real staging bootstrap/full-disk/deploy/same-SHA/rollback rehearsal. No production contact, merge, or production deploy. Evidence stays untracked and in issue comments.

Starting deploy-fix-6 on staging 8by64-compute01. Base d2ade1fb931b4ac74fb698015fcc95c3e7cf69c5, fetched fix/deploy-688-round5. Working branch fix/deploy-688-round6: the job's hard rule requires a new branch and PR because this forge ignores pushes to an open PR branch. I will link the replacement here and keep #748 open. Scope: scripted bootstrap with all image/unit writes and free-space checks before stop; missing first predecessor; worker umask; FIFO producer race. Gates: bash -n, shellcheck, all deploy tests, real staging bootstrap/full-disk/deploy/same-SHA/rollback rehearsal. No production contact, merge, or production deploy. Evidence stays untracked and in issue comments.
Author
Owner

Finding → fix → test, round 6 in progress:

  1. P1 bootstrap: README steps 6–8 stop before tagging; the review's persistent ENOSPC model strands recovery. Replacing the manual procedure with deploy.sh bootstrap, routed through the existing detached lock, free-space admission, runtime reserves, scratch generator, prepared unit/pointer files and recovery. The running legacy digest is explicitly recorded before stop; only that digest permits status-only health, without fabricating a revision. New images still require the dev SHA. README becomes a dry-run and one command.

  2. P2 first update: switch_prepare required :rollback even when a healthy labelled baseline had no predecessor. The image record now represents an absent predecessor as -; commit still records the old baseline as the real predecessor. Regression paths exercise the first update and its failures.

  3. P2 worker permissions: the parent umask does not cross systemd-run. Worker now sets umask 077 itself. Real user-systemd tests under UMask=0002 pass and verify receipt 0600 and enclosing directory 0700.

  4. P2 FIFO race: shell > recreates the path after an early receiver exit. Producer now opens without O_CREAT, refuses symlinks and verifies the opened descriptor is a FIFO. A handshake forces receiver completion before producer open; mocks and real user systemd both pass with no retained artifact from a 262144-byte payload. Existing disconnect, abandoned upload, partial receipt, failure result and lock tests pass as well.

Evidence: .job-evidence/round6/transport.log (not committed). Full fault injection and real app rehearsals remain to run.

Finding → fix → test, round 6 in progress: 1. P1 bootstrap: README steps 6–8 stop before tagging; the review's persistent ENOSPC model strands recovery. Replacing the manual procedure with `deploy.sh bootstrap`, routed through the existing detached lock, free-space admission, runtime reserves, scratch generator, prepared unit/pointer files and recovery. The running legacy digest is explicitly recorded before stop; only that digest permits status-only health, without fabricating a revision. New images still require the dev SHA. README becomes a dry-run and one command. 2. P2 first update: switch_prepare required `:rollback` even when a healthy labelled baseline had no predecessor. The image record now represents an absent predecessor as `-`; commit still records the old baseline as the real predecessor. Regression paths exercise the first update and its failures. 3. P2 worker permissions: the parent umask does not cross systemd-run. Worker now sets umask 077 itself. Real user-systemd tests under UMask=0002 pass and verify receipt 0600 and enclosing directory 0700. 4. P2 FIFO race: shell `>` recreates the path after an early receiver exit. Producer now opens without O_CREAT, refuses symlinks and verifies the opened descriptor is a FIFO. A handshake forces receiver completion before producer open; mocks and real user systemd both pass with no retained artifact from a 262144-byte payload. Existing disconnect, abandoned upload, partial receipt, failure result and lock tests pass as well. Evidence: .job-evidence/round6/transport.log (not committed). Full fault injection and real app rehearsals remain to run.
Author
Owner

Real rehearsal progress on staging only: installed next normal deploy, same-SHA redeploy (same MainPID), and rollback pass. Disposable real Podman/systemd bootstrap from an unlabelled image passes; failed candidate health recovers the legacy image and original unit bytes. Both full-disk bootstrap admission and exhaustion after admission retain the exact old MainPID at zero available blocks.

The extra legacy rollback run correctly refused free-space admission in the 1 GiB fixture: legacy image size 115693123 bytes requires about 523 MiB headroom while about 461 MiB remained. This is a fixture sizing failure, not a service outage or bypass. The old candidate stayed healthy; cleanup completed. First-attempt logs are retained. Repeating the entire rehearsal with a dedicated 2 GiB filesystem, including explicit proof that the after-admission exhaustion hook completed before proceeding.

Real rehearsal progress on staging only: installed next normal deploy, same-SHA redeploy (same MainPID), and rollback pass. Disposable real Podman/systemd bootstrap from an unlabelled image passes; failed candidate health recovers the legacy image and original unit bytes. Both full-disk bootstrap admission and exhaustion after admission retain the exact old MainPID at zero available blocks. The extra legacy rollback run correctly refused free-space admission in the 1 GiB fixture: legacy image size 115693123 bytes requires about 523 MiB headroom while about 461 MiB remained. This is a fixture sizing failure, not a service outage or bypass. The old candidate stayed healthy; cleanup completed. First-attempt logs are retained. Repeating the entire rehearsal with a dedicated 2 GiB filesystem, including explicit proof that the after-admission exhaustion hook completed before proceeding.
Author
Owner

Round-six real staging rehearsal: PASS. All 12 rows below ran on 8by64-compute01 using real rootless Podman, real user systemd, the installed Quadlet generator and the real app. Existing A/B images and the original unlabelled image were reused; this script-only change requires no app rebuild. Normal deploy/rollback used the installed next service. Bootstrap and ENOSPC used a separate disposable real app and a bounded dedicated 2 GiB ext4 image store; no shared filesystem was filled.

Run Result Evidence
real staging normal deploy PASS mount roots: unchanged
real staging same SHA, same MainPID PASS mount roots: unchanged
real staging rollback PASS mount roots: unchanged
bootstrap full filesystem admission PASS old service: same MainPID; available blocks: 0
bootstrap full filesystem after admission PASS old service: same MainPID; available blocks: 0
bootstrap candidate failure recovers unlabelled image PASS unit bytes: original
bootstrap from unlabelled running image PASS exact image and pointer identities verified
rollback to recorded legacy image PASS health: status and exact digest
normal deploy after bootstrap removes legacy exception PASS exact image and pointer identities verified
same SHA after bootstrap PASS old service: same MainPID
normal rollback after bootstrap PASS exact image and pointer identities verified
final staging state PASS mount roots: unchanged

Both full-disk rows verified zero available blocks with the original MainPID continuously alive. The after-admission hook separately recorded zero blocks before image-load/tag/preparation could complete. Failed candidate health restored the exact unlabelled digest and original unit bytes. Explicit legacy rollback verified status and digest; normal deploy subsequently removed the legacy exception. Staging finishes at A with rollback B and byte-identical original installed unit; mount roots match entry. Disposable service/store/data were cleaned. Per-run logs and rehearsal source are retained untracked and will be attached with the final gate output.

Full gate now running through one local build slot, with three injection workers. No production contact, merge, or production deploy.

Round-six real staging rehearsal: PASS. All 12 rows below ran on 8by64-compute01 using real rootless Podman, real user systemd, the installed Quadlet generator and the real app. Existing A/B images and the original unlabelled image were reused; this script-only change requires no app rebuild. Normal deploy/rollback used the installed next service. Bootstrap and ENOSPC used a separate disposable real app and a bounded dedicated 2 GiB ext4 image store; no shared filesystem was filled. | Run | Result | Evidence | | --- | --- | --- | | real staging normal deploy | PASS | mount roots: unchanged | | real staging same SHA, same MainPID | PASS | mount roots: unchanged | | real staging rollback | PASS | mount roots: unchanged | | bootstrap full filesystem admission | PASS | old service: same MainPID; available blocks: 0 | | bootstrap full filesystem after admission | PASS | old service: same MainPID; available blocks: 0 | | bootstrap candidate failure recovers unlabelled image | PASS | unit bytes: original | | bootstrap from unlabelled running image | PASS | exact image and pointer identities verified | | rollback to recorded legacy image | PASS | health: status and exact digest | | normal deploy after bootstrap removes legacy exception | PASS | exact image and pointer identities verified | | same SHA after bootstrap | PASS | old service: same MainPID | | normal rollback after bootstrap | PASS | exact image and pointer identities verified | | final staging state | PASS | mount roots: unchanged | Both full-disk rows verified zero available blocks with the original MainPID continuously alive. The after-admission hook separately recorded zero blocks before image-load/tag/preparation could complete. Failed candidate health restored the exact unlabelled digest and original unit bytes. Explicit legacy rollback verified status and digest; normal deploy subsequently removed the legacy exception. Staging finishes at A with rollback B and byte-identical original installed unit; mount roots match entry. Disposable service/store/data were cleaned. Per-run logs and rehearsal source are retained untracked and will be attached with the final gate output. Full gate now running through one local build slot, with three injection workers. No production contact, merge, or production deploy.
Author
Owner

Additional finding during this fix's verification: the first absence-handling implementation conflated an image-inspection error with an absent rollback tag. A targeted injection at the rollback image inspection, followed by candidate health failure, reproduced old-image recovery with the older predecessor erased to -. Evidence: .job-evidence/round6/predecessor-red.log. This did not affect the earlier real staging rehearsal, which had no runtime inspection errors.

I stopped only this job's recorded timeout PID 1452960 after verifying its exact command and owner. The partial sweep is retained with exit 143; it is not a full gate pass. No other process was signalled.

Fix: distinguish Podman's explicit image-exists result 1 (absent) from result 0 (present, inspection must succeed) and runtime errors (abort before switch). This keeps real absence supported while preserving an existing predecessor on errors. Targeted before/after injections and explicit runtime/inspection error tests are being run, then the full real rehearsal and gate will be repeated from the start.

Additional finding during this fix's verification: the first absence-handling implementation conflated an image-inspection error with an absent rollback tag. A targeted injection at the rollback image inspection, followed by candidate health failure, reproduced old-image recovery with the older predecessor erased to `-`. Evidence: .job-evidence/round6/predecessor-red.log. This did not affect the earlier real staging rehearsal, which had no runtime inspection errors. I stopped only this job's recorded timeout PID 1452960 after verifying its exact command and owner. The partial sweep is retained with exit 143; it is not a full gate pass. No other process was signalled. Fix: distinguish Podman's explicit image-exists result 1 (absent) from result 0 (present, inspection must succeed) and runtime errors (abort before switch). This keeps real absence supported while preserving an existing predecessor on errors. Targeted before/after injections and explicit runtime/inspection error tests are being run, then the full real rehearsal and gate will be repeated from the start.
Author
Owner

After correcting the predecessor inspection/absence edge case, the entire real staging rehearsal was repeated from the start and all 12 rows passed again. Actual Podman image-exists results were explicitly checked: present image = 0, absent image = 1. Both zero-block bootstrap cases keep the old MainPID unchanged; failed bootstrap health recovers the exact unlabelled image; legacy rollback, normal deploy, same-SHA and normal rollback all pass. Staging again returns to A with rollback B, original installed unit bytes and unchanged mount identities. Disposable resources are removed.

Targeted predecessor existence and inspection failures before/after each command now preserve the older rollback identity through failed health. A fresh full gate is running from the beginning. The prior interrupted partial sweep remains recorded as exit 143 and is not claimed as a pass. Final evidence will include both the reproduced edge case and its passing tests, plus every rehearsal run log.

After correcting the predecessor inspection/absence edge case, the entire real staging rehearsal was repeated from the start and all 12 rows passed again. Actual Podman image-exists results were explicitly checked: present image = 0, absent image = 1. Both zero-block bootstrap cases keep the old MainPID unchanged; failed bootstrap health recovers the exact unlabelled image; legacy rollback, normal deploy, same-SHA and normal rollback all pass. Staging again returns to A with rollback B, original installed unit bytes and unchanged mount identities. Disposable resources are removed. Targeted predecessor existence and inspection failures before/after each command now preserve the older rollback identity through failed health. A fresh full gate is running from the beginning. The prior interrupted partial sweep remains recorded as exit 143 and is not claimed as a pass. Final evidence will include both the reproduced edge case and its passing tests, plus every rehearsal run log.
Author
Owner

Resuming after host reboot on fix/deploy-688-round5, base/head 768dbacbb6. The coordinator already pushed the WIP to PR #748; this task explicitly requires updates only to that branch and PR, overriding the earlier generic new-branch instruction. Rechecking staging, runner and fixtures; rerunning the interrupted full gate and real staging rehearsals. No production access or merge. Evidence remains untracked.

Resuming after host reboot on fix/deploy-688-round5, base/head 768dbacbb6b25773af560b5e10f69a9fbd88f1da. The coordinator already pushed the WIP to PR #748; this task explicitly requires updates only to that branch and PR, overriding the earlier generic new-branch instruction. Rechecking staging, runner and fixtures; rerunning the interrupted full gate and real staging rehearsals. No production access or merge. Evidence remains untracked.
Author
Owner

Post-reboot evidence on 8by64-compute01: boot ID 3e68dc6c-c253-49bd-a643-7dc507439df6. calternal-next.service active under user systemd; container running image sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb. Health revision 8dc52337a5, status ok. Saved pointer current matches that image; rollback sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108. Both forgejo-runner.service and forgejo-runner-web.service active since 06:43:49 CET with NRestarts=0. No leftover disposable round6/round6-extra stores. A/B archives and bundle fixtures remain present. Root fixture access works. Previous gate stopped without gate.exit at bootstrap-health-fails after-command 80/106; not a completed gate. Additional real regression evidence directory contains fixture symlinks only; those rehearsals have not run. Rerunning all gates and real rehearsals through one build slot, without changing production or filling a shared filesystem.

Post-reboot evidence on 8by64-compute01: boot ID 3e68dc6c-c253-49bd-a643-7dc507439df6. calternal-next.service active under user systemd; container running image sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb. Health revision 8dc52337a566158e86b0234bcfb082331d76d1cd, status ok. Saved pointer current matches that image; rollback sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108. Both forgejo-runner.service and forgejo-runner-web.service active since 06:43:49 CET with NRestarts=0. No leftover disposable round6/round6-extra stores. A/B archives and bundle fixtures remain present. Root fixture access works. Previous gate stopped without gate.exit at bootstrap-health-fails after-command 80/106; not a completed gate. Additional real regression evidence directory contains fixture symlinks only; those rehearsals have not run. Rerunning all gates and real rehearsals through one build slot, without changing production or filling a shared filesystem.
Author
Owner

Fresh post-reboot bootstrap rehearsal: all 12 rows PASS, command exit 0. Real next deploy, same-SHA with same MainPID, rollback, unlabelled bootstrap, legacy rollback, failed bootstrap health recovery, normal post-bootstrap deploy/same-SHA/rollback and final restoration passed. Both full-disk bootstrap cases (before admission and after admission before image load) observed zero available blocks and the same original service MainPID. Staging returns to A/rollback B, byte-identical installed unit and unchanged mount roots. Per-run logs are retained untracked and will be attached with final gates. Additional real regression rehearsal is running, then the interrupted exhaustive gate will rerun from its start.

{"case": "real staging normal deploy", "outcome": "PASS", "current": "sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108", "rollback": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "revision": "5b0dd8d009", "mount_roots": "unchanged"}
{"case": "real staging same SHA, same MainPID", "outcome": "PASS", "current": "sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108", "rollback": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "revision": "5b0dd8d009", "mount_roots": "unchanged"}
{"case": "real staging rollback", "outcome": "PASS", "current": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "rollback": "sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108", "revision": "8dc52337a5", "mount_roots": "unchanged"}
real Podman image-exists present=0 absent=1: PASS
{"case": "bootstrap full filesystem admission", "outcome": "PASS", "current": "sha256:983a8a530cb4330dadeb8c3f125ea2cfec9b242ae21c571d4fc6e964b5a1e17d", "old_service": "same MainPID", "available_blocks": 0}
{"case": "bootstrap full filesystem after admission", "outcome": "PASS", "current": "sha256:983a8a530cb4330dadeb8c3f125ea2cfec9b242ae21c571d4fc6e964b5a1e17d", "old_service": "same MainPID", "available_blocks": 0}
{"case": "bootstrap candidate failure recovers unlabelled image", "outcome": "PASS", "current": "sha256:983a8a530cb4330dadeb8c3f125ea2cfec9b242ae21c571d4fc6e964b5a1e17d", "rollback": "-", "unit_bytes": "original"}
{"case": "bootstrap from unlabelled running image", "outcome": "PASS", "current": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "rollback": "sha256:983a8a530cb4330dadeb8c3f125ea2cfec9b242ae21c571d4fc6e964b5a1e17d", "revision": "8dc52337a5"}
{"case": "rollback to recorded legacy image", "outcome": "PASS", "current": "sha256:983a8a530cb4330dadeb8c3f125ea2cfec9b242ae21c571d4fc6e964b5a1e17d", "rollback": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "health": "status and exact digest"}
{"case": "normal deploy after bootstrap removes legacy exception", "outcome": "PASS", "current": "sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108", "rollback": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "revision": "5b0dd8d009"}
{"case": "same SHA after bootstrap", "outcome": "PASS", "current": "sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108", "rollback": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "old_service": "same MainPID"}
{"case": "normal rollback after bootstrap", "outcome": "PASS", "current": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "rollback": "sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108", "revision": "8dc52337a5"}
{"case": "final staging state", "outcome": "PASS", "current": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "rollback": "sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108", "revision": "8dc52337a5", "mount_roots": "unchanged"}
round six real staging rehearsal: PASS

Fresh post-reboot bootstrap rehearsal: all 12 rows PASS, command exit 0. Real next deploy, same-SHA with same MainPID, rollback, unlabelled bootstrap, legacy rollback, failed bootstrap health recovery, normal post-bootstrap deploy/same-SHA/rollback and final restoration passed. Both full-disk bootstrap cases (before admission and after admission before image load) observed zero available blocks and the same original service MainPID. Staging returns to A/rollback B, byte-identical installed unit and unchanged mount roots. Per-run logs are retained untracked and will be attached with final gates. Additional real regression rehearsal is running, then the interrupted exhaustive gate will rerun from its start. {"case": "real staging normal deploy", "outcome": "PASS", "current": "sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108", "rollback": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "revision": "5b0dd8d00916eed389a47b7aee1e4d2186240ad6", "mount_roots": "unchanged"} {"case": "real staging same SHA, same MainPID", "outcome": "PASS", "current": "sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108", "rollback": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "revision": "5b0dd8d00916eed389a47b7aee1e4d2186240ad6", "mount_roots": "unchanged"} {"case": "real staging rollback", "outcome": "PASS", "current": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "rollback": "sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108", "revision": "8dc52337a566158e86b0234bcfb082331d76d1cd", "mount_roots": "unchanged"} real Podman image-exists present=0 absent=1: PASS {"case": "bootstrap full filesystem admission", "outcome": "PASS", "current": "sha256:983a8a530cb4330dadeb8c3f125ea2cfec9b242ae21c571d4fc6e964b5a1e17d", "old_service": "same MainPID", "available_blocks": 0} {"case": "bootstrap full filesystem after admission", "outcome": "PASS", "current": "sha256:983a8a530cb4330dadeb8c3f125ea2cfec9b242ae21c571d4fc6e964b5a1e17d", "old_service": "same MainPID", "available_blocks": 0} {"case": "bootstrap candidate failure recovers unlabelled image", "outcome": "PASS", "current": "sha256:983a8a530cb4330dadeb8c3f125ea2cfec9b242ae21c571d4fc6e964b5a1e17d", "rollback": "-", "unit_bytes": "original"} {"case": "bootstrap from unlabelled running image", "outcome": "PASS", "current": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "rollback": "sha256:983a8a530cb4330dadeb8c3f125ea2cfec9b242ae21c571d4fc6e964b5a1e17d", "revision": "8dc52337a566158e86b0234bcfb082331d76d1cd"} {"case": "rollback to recorded legacy image", "outcome": "PASS", "current": "sha256:983a8a530cb4330dadeb8c3f125ea2cfec9b242ae21c571d4fc6e964b5a1e17d", "rollback": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "health": "status and exact digest"} {"case": "normal deploy after bootstrap removes legacy exception", "outcome": "PASS", "current": "sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108", "rollback": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "revision": "5b0dd8d00916eed389a47b7aee1e4d2186240ad6"} {"case": "same SHA after bootstrap", "outcome": "PASS", "current": "sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108", "rollback": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "old_service": "same MainPID"} {"case": "normal rollback after bootstrap", "outcome": "PASS", "current": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "rollback": "sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108", "revision": "8dc52337a566158e86b0234bcfb082331d76d1cd"} {"case": "final staging state", "outcome": "PASS", "current": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "rollback": "sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108", "revision": "8dc52337a566158e86b0234bcfb082331d76d1cd", "mount_roots": "unchanged"} round six real staging rehearsal: PASS
Author
Owner

Fresh additional real staging rehearsal: 13 rows PASS, command exit 0. Together with the bootstrap rehearsal, all 25 rows now pass after reboot. Persistent ENOSPC remained at zero blocks at prepared, candidate_started and recovery_health; the filler stayed until recovery was verified. Real invalid Quadlet output loss recovered via the precompiled static unit. Terminating only this job's recorded loopback SSH client after stop did not abort detached failed-health recovery; result 001 was persisted and the original A image/unit with rollback B restored. Fixtures cleaned. Full gate has restarted from the beginning in one local build slot. Evidence below; final archive and verbatim gates follow on completion.

{"case": "real staging deploy", "outcome": "PASS", "current": "sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108", "rollback": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "revision": "5b0dd8d009", "mounts_and_data_roots": "unchanged"}
{"case": "real staging same SHA", "outcome": "PASS", "current": "sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108", "rollback": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "revision": "5b0dd8d009", "mounts_and_data_roots": "unchanged"}
{"case": "real staging rollback", "outcome": "PASS", "current": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "rollback": "sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108", "revision": "8dc52337a5", "mounts_and_data_roots": "unchanged"}
{"case": "real full receipt filesystem cleans partial upload", "outcome": "PASS", "current": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "rollback": "sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108", "revision": "8dc52337a5", "mounts_and_data_roots": "unchanged"}
{"case": "real staging invalid generator input", "outcome": "PASS", "current": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "rollback": "sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108", "revision": "8dc52337a5", "mounts_and_data_roots": "unchanged"}
{"case": "real staging failed candidate recovery", "outcome": "PASS", "current": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "rollback": "sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108", "revision": "8dc52337a5", "mounts_and_data_roots": "unchanged"}
{"case": "isolated store paths verified", "outcome": "PASS", "graph_root": "/home/calternal/.deploy-round6-extra/graph", "run_root": "/run/user/30033/calternal-round6-extra-run"}
{"case": "isolated real Podman deploy and rollback", "outcome": "PASS", "current": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "rollback": "sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108", "store": "dedicated ext4"}
{"case": "persistent full image store through recovery", "outcome": "PASS", "current": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "rollback": "sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108", "zero_free_blocks_at": ["candidate_started", "prepared", "recovery_health"], "filler": "retained until recovery verified"}
{"case": "free-space margin refuses next deploy", "outcome": "PASS", "current": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "rollback": "sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108", "old_service": "same MainPID throughout", "available_bytes": 0}
{"case": "final real staging check", "outcome": "PASS", "current": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "rollback": "sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108", "revision": "8dc52337a5", "mounts_and_data_roots": "unchanged"}
real staging rehearsal: PASS
{"case": "real generator output loss and static fallback", "outcome": "PASS", "current": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "rollback": "sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108", "revision": "8dc52337a5", "details": "Actual invalid Quadlet removed primary generator output; a precompiled static unit served A. Original unit and primary A restored under the deploy lock."}
{"case": "real SSH disconnect mid-switch", "outcome": "PASS", "current": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "rollback": "sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108", "revision": "8dc52337a5", "details": "Killed the recorded loopback SSH client after the old service stopped; released candidate into failed health. PID 1 completed A recovery and persisted result 001 with original unit bytes and rollback B."}
real generator-loss and SSH-disconnect rehearsal: PASS

Fresh additional real staging rehearsal: 13 rows PASS, command exit 0. Together with the bootstrap rehearsal, all 25 rows now pass after reboot. Persistent ENOSPC remained at zero blocks at prepared, candidate_started and recovery_health; the filler stayed until recovery was verified. Real invalid Quadlet output loss recovered via the precompiled static unit. Terminating only this job's recorded loopback SSH client after stop did not abort detached failed-health recovery; result 001 was persisted and the original A image/unit with rollback B restored. Fixtures cleaned. Full gate has restarted from the beginning in one local build slot. Evidence below; final archive and verbatim gates follow on completion. {"case": "real staging deploy", "outcome": "PASS", "current": "sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108", "rollback": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "revision": "5b0dd8d00916eed389a47b7aee1e4d2186240ad6", "mounts_and_data_roots": "unchanged"} {"case": "real staging same SHA", "outcome": "PASS", "current": "sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108", "rollback": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "revision": "5b0dd8d00916eed389a47b7aee1e4d2186240ad6", "mounts_and_data_roots": "unchanged"} {"case": "real staging rollback", "outcome": "PASS", "current": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "rollback": "sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108", "revision": "8dc52337a566158e86b0234bcfb082331d76d1cd", "mounts_and_data_roots": "unchanged"} {"case": "real full receipt filesystem cleans partial upload", "outcome": "PASS", "current": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "rollback": "sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108", "revision": "8dc52337a566158e86b0234bcfb082331d76d1cd", "mounts_and_data_roots": "unchanged"} {"case": "real staging invalid generator input", "outcome": "PASS", "current": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "rollback": "sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108", "revision": "8dc52337a566158e86b0234bcfb082331d76d1cd", "mounts_and_data_roots": "unchanged"} {"case": "real staging failed candidate recovery", "outcome": "PASS", "current": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "rollback": "sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108", "revision": "8dc52337a566158e86b0234bcfb082331d76d1cd", "mounts_and_data_roots": "unchanged"} {"case": "isolated store paths verified", "outcome": "PASS", "graph_root": "/home/calternal/.deploy-round6-extra/graph", "run_root": "/run/user/30033/calternal-round6-extra-run"} {"case": "isolated real Podman deploy and rollback", "outcome": "PASS", "current": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "rollback": "sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108", "store": "dedicated ext4"} {"case": "persistent full image store through recovery", "outcome": "PASS", "current": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "rollback": "sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108", "zero_free_blocks_at": ["candidate_started", "prepared", "recovery_health"], "filler": "retained until recovery verified"} {"case": "free-space margin refuses next deploy", "outcome": "PASS", "current": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "rollback": "sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108", "old_service": "same MainPID throughout", "available_bytes": 0} {"case": "final real staging check", "outcome": "PASS", "current": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "rollback": "sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108", "revision": "8dc52337a566158e86b0234bcfb082331d76d1cd", "mounts_and_data_roots": "unchanged"} real staging rehearsal: PASS {"case": "real generator output loss and static fallback", "outcome": "PASS", "current": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "rollback": "sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108", "revision": "8dc52337a566158e86b0234bcfb082331d76d1cd", "details": "Actual invalid Quadlet removed primary generator output; a precompiled static unit served A. Original unit and primary A restored under the deploy lock."} {"case": "real SSH disconnect mid-switch", "outcome": "PASS", "current": "sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb", "rollback": "sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108", "revision": "8dc52337a566158e86b0234bcfb082331d76d1cd", "details": "Killed the recorded loopback SSH client after the old service stopped; released candidate into failed health. PID 1 completed A recovery and persisted result 001 with original unit bytes and rollback B."} real generator-loss and SSH-disconnect rehearsal: PASS
Author
Owner

Resuming round six on fix/deploy-688-round5 at base/head 768dbacbb6. Previous saved real staging and extra regression rehearsals exited 0 after reboot; exhaustive gate has no exit marker and is incomplete. Rechecking services and fixtures, then rerunning gates from the beginning under one build slot. The explicit job instruction to push only this branch and rehearse staging takes precedence over the general new-branch/no-deploy boilerplate. No production access or merge.

Resuming round six on fix/deploy-688-round5 at base/head 768dbacbb6b25773af560b5e10f69a9fbd88f1da. Previous saved real staging and extra regression rehearsals exited 0 after reboot; exhaustive gate has no exit marker and is incomplete. Rechecking services and fixtures, then rerunning gates from the beginning under one build slot. The explicit job instruction to push only this branch and rehearse staging takes precedence over the general new-branch/no-deploy boilerplate. No production access or merge.
Author
Owner

Current resumption health check: calternal-next.service is active and its running digest is 60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb. /healthz returns {"revision":"8dc52337a566158e86b0234bcfb082331d76d1cd","status":"ok","version":"0.1.0"}. The private mode-0600 image record agrees and preserves rollback 26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108. Both forgejo-runner.service and forgejo-runner-web.service are active since 06:43:49 CET with NRestarts=0. There are no failed system units or leftover round6 disposable stores.

The saved post-reboot real staging rehearsals have both exit markers 0 and all 25 rows PASS. Their results were already posted at comments 69160 and 69169, and remain valid on the unchanged 768dbacb head and the same boot. No staging rerun is necessary. The interrupted exhaustive gate has no completed result; a fresh full run is now active in one build slot, with output kept separately from the interrupted log. No product edits are needed from this resumption's diff review so far.

Current resumption health check: calternal-next.service is active and its running digest is 60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb. /healthz returns {"revision":"8dc52337a566158e86b0234bcfb082331d76d1cd","status":"ok","version":"0.1.0"}. The private mode-0600 image record agrees and preserves rollback 26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108. Both forgejo-runner.service and forgejo-runner-web.service are active since 06:43:49 CET with NRestarts=0. There are no failed system units or leftover round6 disposable stores. The saved post-reboot real staging rehearsals have both exit markers 0 and all 25 rows PASS. Their results were already posted at comments 69160 and 69169, and remain valid on the unchanged 768dbacb head and the same boot. No staging rerun is necessary. The interrupted exhaustive gate has no completed result; a fresh full run is now active in one build slot, with output kept separately from the interrupted log. No product edits are needed from this resumption's diff review so far.
Author
Owner

Evidence audit found the complete pre-reboot log still available on unchanged product/test source, with explicit PASS positions through bootstrap-health-fails after 80/106. Those results were not lost. To follow the instruction to resume completed work, stopped only the current resumption's recorded timeout PID 534890, verified by /proc command and owner. Its exit 143 is retained and is not a passing gate.

The remaining gate uses an untracked resume harness. It reads the original matrix list from the committed test's AST, runs each baseline again, checks retained step counts, and calls the original inject function on every position without an explicit retained PASS. It keeps the original three-worker maximum and one build slot. Syntax, shellcheck, Quadlet, transport, ordinary transaction, bootstrap, relay, artifact and dry-run gates run fresh. Final evidence will state retained and fresh counts, include both original logs and resume source, and will not describe this as an uninterrupted full rerun. All 25 real staging rows already passed on this boot and unchanged head.

Evidence audit found the complete pre-reboot log still available on unchanged product/test source, with explicit PASS positions through bootstrap-health-fails after 80/106. Those results were not lost. To follow the instruction to resume completed work, stopped only the current resumption's recorded timeout PID 534890, verified by /proc command and owner. Its exit 143 is retained and is not a passing gate. The remaining gate uses an untracked resume harness. It reads the original matrix list from the committed test's AST, runs each baseline again, checks retained step counts, and calls the original inject function on every position without an explicit retained PASS. It keeps the original three-worker maximum and one build slot. Syntax, shellcheck, Quadlet, transport, ordinary transaction, bootstrap, relay, artifact and dry-run gates run fresh. Final evidence will state retained and fresh counts, include both original logs and resume source, and will not describe this as an uninterrupted full rerun. All 25 real staging rows already passed on this boot and unchanged head.
Author
Owner

Round-six verification checkpoint on unchanged head 768dbacbb6: 2802 injection positions are now fully accounted PASS from retained explicit results plus fresh missing positions. Bootstrap health-failure and persistent-full-disk sweeps are complete, as are failed deploy and failed rollback ordinary and persistent-full-disk sweeps. The original matrix continues through stale-primary recovery and post-start unit-rename recovery. No failed test or new product defect has appeared. This is still a running gate, not a final gate pass. All 25 real staging rehearsal rows remain recorded PASS after this boot. No product file changes, production access or merge.

Round-six verification checkpoint on unchanged head 768dbacbb6b25773af560b5e10f69a9fbd88f1da: 2802 injection positions are now fully accounted PASS from retained explicit results plus fresh missing positions. Bootstrap health-failure and persistent-full-disk sweeps are complete, as are failed deploy and failed rollback ordinary and persistent-full-disk sweeps. The original matrix continues through stale-primary recovery and post-start unit-rename recovery. No failed test or new product defect has appeared. This is still a running gate, not a final gate pass. All 25 real staging rehearsal rows remain recorded PASS after this boot. No product file changes, production access or merge.
Author
Owner

Resuming deploy-fix-8 on fix/deploy-688-round5, base/head 768dbacbb6. Work only in /home/agent/wt/deploy-fix. The explicit task-specific instruction to push this branch overrides the older generic new-branch instruction. No production contact, merges, or production deployment. Post-reboot staging calternal-next and both CI runners active with zero restarts. Health reports expected 8dc52337a5; real running image 60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb. Both runners have active jobs; no failed system units. RAM available 58GiB; root free 124GiB; systemd-oomd active. Product/test source is unchanged from WIP. Retained explicit passing injection positions will be combined with fresh remaining positions; all other gates and real rehearsals will run on this boot, one build slot, maximum three workers. Evidence remains untracked.

Resuming deploy-fix-8 on fix/deploy-688-round5, base/head 768dbacbb6b25773af560b5e10f69a9fbd88f1da. Work only in /home/agent/wt/deploy-fix. The explicit task-specific instruction to push this branch overrides the older generic new-branch instruction. No production contact, merges, or production deployment. Post-reboot staging calternal-next and both CI runners active with zero restarts. Health reports expected 8dc52337a566158e86b0234bcfb082331d76d1cd; real running image 60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb. Both runners have active jobs; no failed system units. RAM available 58GiB; root free 124GiB; systemd-oomd active. Product/test source is unchanged from WIP. Retained explicit passing injection positions will be combined with fresh remaining positions; all other gates and real rehearsals will run on this boot, one build slot, maximum three workers. Evidence remains untracked.
Author
Owner

Verification checkpoint on unchanged head 768dbacbb6: 3498 injection positions are fully accounted PASS from retained explicit results plus fresh positions. Stale-primary rollback recovery now passes every ordinary before/after failure and persistent-full-disk before-command failure; persistent after-command sweep is running. Fresh syntax, shellcheck, Quadlet, transport, transaction and bootstrap checks passed. No new product defect or code change. This is still a running gate, not a final gate pass. Fresh real staging rehearsals follow in the same single-slot sequence.

Verification checkpoint on unchanged head 768dbacbb6b25773af560b5e10f69a9fbd88f1da: 3498 injection positions are fully accounted PASS from retained explicit results plus fresh positions. Stale-primary rollback recovery now passes every ordinary before/after failure and persistent-full-disk before-command failure; persistent after-command sweep is running. Fresh syntax, shellcheck, Quadlet, transport, transaction and bootstrap checks passed. No new product defect or code change. This is still a running gate, not a final gate pass. Fresh real staging rehearsals follow in the same single-slot sequence.
Author
Owner

Regression gate completed on unchanged head 768dbacbb6, exit 0. Exhaustive failure injection: 4,354 cases = 3,314 retained explicit passes + 1,040 fresh passes; userdata unchanged. Fresh syntax, shellcheck, all other deploy probes, dry runs and real user-systemd checks pass. Fresh real staging rehearsal is running next in one build slot. Final completion comment will include verbatim gate output and all real rehearsal rows. No code changes, production access or merge.

Regression gate completed on unchanged head 768dbacbb6b25773af560b5e10f69a9fbd88f1da, exit 0. Exhaustive failure injection: 4,354 cases = 3,314 retained explicit passes + 1,040 fresh passes; userdata unchanged. Fresh syntax, shellcheck, all other deploy probes, dry runs and real user-systemd checks pass. Fresh real staging rehearsal is running next in one build slot. Final completion comment will include verbatim gate output and all real rehearsal rows. No code changes, production access or merge.
Author
Owner

Completed deploy-fix-8 on fix/deploy-688-round5, head 768dbacbb6b25773af560b5e10f69a9fbd88f1da. The WIP commit already contained all four fixes; review and verification found no additional product changes necessary. No new or empty commits were made. Pushed only the requested branch. No production contact, merges, or issue closure.

Finding → fix → test:

  • P1 bootstrap: scripted deploy.sh bootstrap replaces manual stop-before-tag steps. Verified image, unit and pointer preparation, free-space admission, static recovery and independent runtime reserves finish before stop. Real unlabelled bootstrap, candidate failure, legacy rollback and full disk before/after admission passed. Both bootstrap full-disk cases observed zero available blocks and preserved the exact old MainPID.
  • P2 first update: an absent predecessor is recorded as -; successful update records the old baseline. Runtime/inspection errors refuse before stop. Regression checks verify first update, failed first update, absent rollback refusal and preservation of older predecessors.
  • P2 worker permissions: receiver applies umask 077 itself. Mock and real user-systemd checks under UMask=0002 verify directory 0700 and receipt 0600.
  • P2 FIFO race: producer opens without O_CREAT, uses O_NOFOLLOW and checks FIFO type. Deterministic early receiver failure passes with mocks and real systemd, retaining none of the 262144 payload bytes.

Post-reboot health was verified before any rehearsal: both staging services (calternal-next and calternal-preview) active, zero restarts; next health and exact image/pointer identities matched expectations. Both CI runners active with zero restarts and running jobs. No failed system or calternal user units. CI slice MemoryMax=32 GiB and agent slice MemoryMax=24 GiB, with oomd enabled. All fresh heavy work used one build slot and no more than three workers. No build was needed.

Verification accounting: unchanged committed product/test source, with SHA-256 manifest attached. Explicit passing injection positions retained from the two interrupted runs were combined with freshly executed missing positions using the original injection function and matrix. Every baseline was rerun and boundary counts checked. This is a completed combined sweep, not an uninterrupted full rerun. Other gates and all 25 real rehearsal rows ran fresh on this boot. Evidence is untracked; no artifacts were committed.

Real staging rehearsal (fresh this boot):

Run Result
real staging normal deploy PASS
real staging same SHA, same MainPID PASS
real staging rollback PASS
bootstrap full filesystem admission PASS
bootstrap full filesystem after admission PASS
bootstrap candidate failure recovers unlabelled image PASS
bootstrap from unlabelled running image PASS
rollback to recorded legacy image PASS
normal deploy after bootstrap removes legacy exception PASS
same SHA after bootstrap PASS
normal rollback after bootstrap PASS
final staging state PASS
real staging deploy PASS
real staging same SHA PASS
real staging rollback PASS
real full receipt filesystem cleans partial upload PASS
real staging invalid generator input PASS
real staging failed candidate recovery PASS
isolated store paths verified PASS
isolated real Podman deploy and rollback PASS
persistent full image store through recovery PASS
free-space margin refuses next deploy PASS
final real staging check PASS
real generator output loss and static fallback PASS
real SSH disconnect mid-switch PASS

Rehearsals restored original staging unit bytes, image pointers and mount identities. Full disk cases used bounded dedicated filesystems; shared filesystems were never filled. Disposable fixtures were cleaned.

Complete evidence, retained output, resume source, source hashes and per-run logs.

Final health output, verbatim:

NRestarts=0
Id=forgejo-runner.service
ActiveState=active
SubState=running

NRestarts=0
Id=forgejo-runner-web.service
ActiveState=active
SubState=running
MemoryHigh=30064771072
MemoryMax=34359738368
ManagedOOMMemoryPressure=kill
Id=ci.slice

MemoryHigh=21474836480
MemoryMax=25769803776
ManagedOOMMemoryPressure=kill
Id=user-1002.slice
active
NRestarts=0
Id=calternal-next.service
ActiveState=active
SubState=running

NRestarts=0
Id=calternal-preview.service
ActiveState=active
SubState=running
calternal-next running=true image=60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb
  UNIT LOAD ACTIVE SUB DESCRIPTION

0 loaded units listed.
  UNIT LOAD ACTIVE SUB DESCRIPTION

0 loaded units listed.
image record: sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108
disposable leftovers: []
{"revision":"8dc52337a566158e86b0234bcfb082331d76d1cd","status":"ok","version":"0.1.0"}

Gate output, verbatim:

bash -n: PASS
shellcheck: PASS
diff whitespace: PASS
real Quadlet generation (main and immutable recovery): PASS
real Quadlet invalid candidate rejected before install: PASS
target receiver fails before producer open; no retained artifact: PASS
target caller lost before FIFO open cleans upload and releases lock: PASS
target partial receipt failure cleans private upload: PASS
target normal: PASS
target failed-transaction: PASS
target closed-output: PASS
target dropped-caller: PASS
target locked-receipt: PASS
remote predecessor-runtime-error: PASS
remote predecessor-inspection-error: PASS
remote malformed-pointer: PASS
remote multiline-pointer: PASS
remote foreign-legacy-pointer: PASS
remote equal-pointers: PASS
remote symlink-pointer: PASS
remote invalid-generator: PASS
remote closed-output: PASS
remote generator-fails: PASS
remote byte-exact-recovery: PASS
remote normal: PASS
remote stale-recovery-definition: PASS
remote stale-primary-recovery: PASS
remote post-start-unit-rename: PASS
remote retained-recovery: PASS
remote same-revision different digest: PASS
remote disk-full: PASS
remote tag-fails: PASS
remote pointer-commit-fails: PASS
remote start-fails: PASS
remote health-fails: PASS
remote stale-response: PASS
remote inactive-systemd: PASS
remote wrong-container-image: PASS
remote stop-fails: PASS
remote reload-fails: PASS
remote unit-install-fails: PASS
remote unit-move-fails: PASS
remote load-fails: PASS
remote checksum-mismatch: PASS
remote label-mismatch: PASS
remote id-mismatch: PASS
remote pre-unhealthy: PASS
remote open-secret: PASS
remote missing-secret: PASS
remote symlink-secret: PASS
remote unlabelled-baseline: PASS
remote persistent-daemon-reload: PASS
remote persistent-unit-rename: PASS
remote stop-returns-error-after-stopping: PASS
remote post-stop-podman-error: PASS
remote recovery-start-fails: PASS
remote spaced-open-secret: PASS
remote spaced-new-open-secret: PASS
remote multiple-open-secret: PASS
remote optional-open-secret: PASS
remote service-open-secret: PASS
remote continued-open-secret: PASS
remote quoted-open-secret: PASS
remote unsupported-specifier: PASS
remote unsupported-escape: PASS
remote repeated-open-secret: PASS
remote spaced-safe-secret: PASS
remote multiple-safe-secret: PASS
remote optional-safe-secret: PASS
remote service-safe-secret: PASS
remote continued-safe-secret: PASS
remote quoted-safe-secret: PASS
remote home-safe-secret: PASS
remote percent-safe-secret: PASS
remote signal: PASS
remote rerun: PASS
remote manual-rollback: PASS
remote concurrent upload/switch serialized: PASS
bootstrap legacy rollback verifies exact digest and status: PASS
failed first update preserves baseline; absent rollback refuses before stop: PASS
predecessor existence/inspection errors preserve older rollback through failed health: PASS
injection deploy/normal/before: retained 92/92 explicit PASS positions from unchanged source
injection deploy/normal/before: 92/92 accounted PASS
injection deploy/normal/after: retained 92/92 explicit PASS positions from unchanged source
injection deploy/normal/after: 92/92 accounted PASS
persistent disk deploy/normal/before: retained 92/92 explicit PASS positions from unchanged source
persistent disk deploy/normal/before: 92/92 accounted PASS
persistent disk deploy/normal/after: retained 92/92 explicit PASS positions from unchanged source
persistent disk deploy/normal/after: 92/92 accounted PASS
injection same-sha/normal/before: retained 34/34 explicit PASS positions from unchanged source
injection same-sha/normal/before: 34/34 accounted PASS
injection same-sha/normal/after: retained 34/34 explicit PASS positions from unchanged source
injection same-sha/normal/after: 34/34 accounted PASS
injection same-image/normal/before: retained 34/34 explicit PASS positions from unchanged source
injection same-image/normal/before: 34/34 accounted PASS
injection same-image/normal/after: retained 34/34 explicit PASS positions from unchanged source
injection same-image/normal/after: 34/34 accounted PASS
injection rollback/normal/before: retained 79/79 explicit PASS positions from unchanged source
injection rollback/normal/before: 79/79 accounted PASS
injection rollback/normal/after: retained 79/79 explicit PASS positions from unchanged source
injection rollback/normal/after: 79/79 accounted PASS
persistent disk rollback/normal/before: retained 79/79 explicit PASS positions from unchanged source
persistent disk rollback/normal/before: 79/79 accounted PASS
persistent disk rollback/normal/after: retained 79/79 explicit PASS positions from unchanged source
persistent disk rollback/normal/after: 79/79 accounted PASS
injection first-update/normal/before: retained 91/91 explicit PASS positions from unchanged source
injection first-update/normal/before: 91/91 accounted PASS
injection first-update/normal/after: retained 91/91 explicit PASS positions from unchanged source
injection first-update/normal/after: 91/91 accounted PASS
persistent disk first-update/normal/before: retained 91/91 explicit PASS positions from unchanged source
persistent disk first-update/normal/before: 91/91 accounted PASS
persistent disk first-update/normal/after: retained 91/91 explicit PASS positions from unchanged source
persistent disk first-update/normal/after: 91/91 accounted PASS
injection bootstrap-upgrade/normal/before: retained 91/91 explicit PASS positions from unchanged source
injection bootstrap-upgrade/normal/before: 91/91 accounted PASS
injection bootstrap-upgrade/normal/after: retained 91/91 explicit PASS positions from unchanged source
injection bootstrap-upgrade/normal/after: 91/91 accounted PASS
persistent disk bootstrap-upgrade/normal/before: retained 91/91 explicit PASS positions from unchanged source
persistent disk bootstrap-upgrade/normal/before: 91/91 accounted PASS
persistent disk bootstrap-upgrade/normal/after: retained 91/91 explicit PASS positions from unchanged source
persistent disk bootstrap-upgrade/normal/after: 91/91 accounted PASS
injection bootstrap-health-fails/normal/before: retained 106/106 explicit PASS positions from unchanged source
injection bootstrap-health-fails/normal/before: 106/106 accounted PASS
injection bootstrap-health-fails/normal/after: retained 106/106 explicit PASS positions from unchanged source
injection bootstrap-health-fails/normal/after: 106/106 accounted PASS
persistent disk bootstrap-health-fails/normal/before: retained 106/106 explicit PASS positions from unchanged source
persistent disk bootstrap-health-fails/normal/before: 106/106 accounted PASS
persistent disk bootstrap-health-fails/normal/after: retained 106/106 explicit PASS positions from unchanged source
persistent disk bootstrap-health-fails/normal/after: 106/106 accounted PASS
injection bootstrap/normal/before: retained 8/8 explicit PASS positions from unchanged source
injection bootstrap/normal/before: 8/8 accounted PASS
injection bootstrap/normal/after: retained 8/8 explicit PASS positions from unchanged source
injection bootstrap/normal/after: 8/8 accounted PASS
injection bootstrap-missing-unit/normal/before: retained 5/5 explicit PASS positions from unchanged source
injection bootstrap-missing-unit/normal/before: 5/5 accounted PASS
injection bootstrap-missing-unit/normal/after: retained 5/5 explicit PASS positions from unchanged source
injection bootstrap-missing-unit/normal/after: 5/5 accounted PASS
injection deploy/health-fails/before: retained 107/107 explicit PASS positions from unchanged source
injection deploy/health-fails/before: 107/107 accounted PASS
injection deploy/health-fails/after: retained 107/107 explicit PASS positions from unchanged source
injection deploy/health-fails/after: 107/107 accounted PASS
persistent disk deploy/health-fails/before: retained 107/107 explicit PASS positions from unchanged source
persistent disk deploy/health-fails/before: 107/107 accounted PASS
persistent disk deploy/health-fails/after: retained 107/107 explicit PASS positions from unchanged source
persistent disk deploy/health-fails/after: 107/107 accounted PASS
injection rollback/health-fails/before: retained 94/94 explicit PASS positions from unchanged source
injection rollback/health-fails/before: 94/94 accounted PASS
injection rollback/health-fails/after: retained 94/94 explicit PASS positions from unchanged source
injection rollback/health-fails/after: 94/94 accounted PASS
persistent disk rollback/health-fails/before: retained 94/94 explicit PASS positions from unchanged source
persistent disk rollback/health-fails/before: 94/94 accounted PASS
persistent disk rollback/health-fails/after: retained 94/94 explicit PASS positions from unchanged source
persistent disk rollback/health-fails/after: 94/94 accounted PASS
injection deploy/stale-primary-recovery/before: retained 105/105 explicit PASS positions from unchanged source
injection deploy/stale-primary-recovery/before: 105/105 accounted PASS
injection deploy/stale-primary-recovery/after: retained 105/105 explicit PASS positions from unchanged source
injection deploy/stale-primary-recovery/after: 105/105 accounted PASS
persistent disk deploy/stale-primary-recovery/before: retained 105/105 explicit PASS positions from unchanged source
persistent disk deploy/stale-primary-recovery/before: 105/105 accounted PASS
persistent disk deploy/stale-primary-recovery/after: retained 105/105 explicit PASS positions from unchanged source
persistent disk deploy/stale-primary-recovery/after: 105/105 accounted PASS
injection rollback/stale-primary-recovery/before: retained 92/92 explicit PASS positions from unchanged source
injection rollback/stale-primary-recovery/before: 92/92 accounted PASS
injection rollback/stale-primary-recovery/after: retained 0/92 explicit PASS positions from unchanged source
injection rollback/stale-primary-recovery/after: resumed 40/92 PASS
injection rollback/stale-primary-recovery/after: resumed 80/92 PASS
injection rollback/stale-primary-recovery/after: 92/92 accounted PASS
persistent disk rollback/stale-primary-recovery/before: retained 0/92 explicit PASS positions from unchanged source
persistent disk rollback/stale-primary-recovery/before: resumed 40/92 PASS
persistent disk rollback/stale-primary-recovery/before: resumed 80/92 PASS
persistent disk rollback/stale-primary-recovery/before: 92/92 accounted PASS
persistent disk rollback/stale-primary-recovery/after: retained 0/92 explicit PASS positions from unchanged source
persistent disk rollback/stale-primary-recovery/after: resumed 40/92 PASS
persistent disk rollback/stale-primary-recovery/after: resumed 80/92 PASS
persistent disk rollback/stale-primary-recovery/after: 92/92 accounted PASS
injection deploy/post-start-unit-rename/before: retained 0/102 explicit PASS positions from unchanged source
injection deploy/post-start-unit-rename/before: resumed 40/102 PASS
injection deploy/post-start-unit-rename/before: resumed 80/102 PASS
injection deploy/post-start-unit-rename/before: 102/102 accounted PASS
injection deploy/post-start-unit-rename/after: retained 0/102 explicit PASS positions from unchanged source
injection deploy/post-start-unit-rename/after: resumed 40/102 PASS
injection deploy/post-start-unit-rename/after: resumed 80/102 PASS
injection deploy/post-start-unit-rename/after: 102/102 accounted PASS
persistent disk deploy/post-start-unit-rename/before: retained 0/102 explicit PASS positions from unchanged source
persistent disk deploy/post-start-unit-rename/before: resumed 40/102 PASS
persistent disk deploy/post-start-unit-rename/before: resumed 80/102 PASS
persistent disk deploy/post-start-unit-rename/before: 102/102 accounted PASS
persistent disk deploy/post-start-unit-rename/after: retained 0/102 explicit PASS positions from unchanged source
persistent disk deploy/post-start-unit-rename/after: resumed 40/102 PASS
persistent disk deploy/post-start-unit-rename/after: resumed 80/102 PASS
persistent disk deploy/post-start-unit-rename/after: 102/102 accounted PASS
injection rollback/post-start-unit-rename/before: retained 0/89 explicit PASS positions from unchanged source
injection rollback/post-start-unit-rename/before: resumed 40/89 PASS
injection rollback/post-start-unit-rename/before: resumed 80/89 PASS
injection rollback/post-start-unit-rename/before: 89/89 accounted PASS
injection rollback/post-start-unit-rename/after: retained 0/89 explicit PASS positions from unchanged source
injection rollback/post-start-unit-rename/after: resumed 40/89 PASS
injection rollback/post-start-unit-rename/after: resumed 80/89 PASS
injection rollback/post-start-unit-rename/after: 89/89 accounted PASS
persistent disk rollback/post-start-unit-rename/before: retained 0/89 explicit PASS positions from unchanged source
persistent disk rollback/post-start-unit-rename/before: resumed 40/89 PASS
persistent disk rollback/post-start-unit-rename/before: resumed 80/89 PASS
persistent disk rollback/post-start-unit-rename/before: 89/89 accounted PASS
persistent disk rollback/post-start-unit-rename/after: retained 0/89 explicit PASS positions from unchanged source
persistent disk rollback/post-start-unit-rename/after: resumed 40/89 PASS
persistent disk rollback/post-start-unit-rename/after: resumed 80/89 PASS
persistent disk rollback/post-start-unit-rename/after: 89/89 accounted PASS
exhaustive failure injection resumed: PASS (4354 cases = 3314 retained + 1040 fresh; userdata unchanged)
relay success: PASS
relay save-fails: PASS
relay target-fails: PASS
relay bootstrap: PASS
non-dev revision and mutable tags rejected for next/cloud: PASS
data rollback options rejected: PASS
build normal: PASS
build dev-moved: PASS
build build-fails: PASS
build bad-id: PASS
build wrong-label: PASS
build save-fails: PASS
build export-wrong-label: PASS
build multiple-images: PASS
build root-owned-iid: PASS
deploy and code rollback dry-run without transport (next/cloud): PASS
deploy regression gates: PASS (completed with retained injection positions; local mocks; no production contact)
target receiver fails before producer open; no retained artifact: PASS (real user systemd)
target caller lost before FIFO open cleans upload and releases lock: PASS (real user systemd)
target normal: PASS (real user systemd)
target failed-transaction: PASS (real user systemd)
target closed-output: PASS (real user systemd)
target dropped-caller: PASS (real user systemd)
target locked-receipt: PASS (real user systemd)

Gate exit: 0. Real bootstrap/staging rehearsal exit: 0. Additional real regression and SSH-disconnect rehearsal exit: 0. Remote branch and PR head both verified as 768dbacbb6b25773af560b5e10f69a9fbd88f1da.

Completed deploy-fix-8 on `fix/deploy-688-round5`, head `768dbacbb6b25773af560b5e10f69a9fbd88f1da`. The WIP commit already contained all four fixes; review and verification found no additional product changes necessary. No new or empty commits were made. Pushed only the requested branch. No production contact, merges, or issue closure. Finding → fix → test: - P1 bootstrap: scripted `deploy.sh bootstrap` replaces manual stop-before-tag steps. Verified image, unit and pointer preparation, free-space admission, static recovery and independent runtime reserves finish before stop. Real unlabelled bootstrap, candidate failure, legacy rollback and full disk before/after admission passed. Both bootstrap full-disk cases observed zero available blocks and preserved the exact old MainPID. - P2 first update: an absent predecessor is recorded as `-`; successful update records the old baseline. Runtime/inspection errors refuse before stop. Regression checks verify first update, failed first update, absent rollback refusal and preservation of older predecessors. - P2 worker permissions: receiver applies umask 077 itself. Mock and real user-systemd checks under UMask=0002 verify directory 0700 and receipt 0600. - P2 FIFO race: producer opens without O_CREAT, uses O_NOFOLLOW and checks FIFO type. Deterministic early receiver failure passes with mocks and real systemd, retaining none of the 262144 payload bytes. Post-reboot health was verified before any rehearsal: both staging services (calternal-next and calternal-preview) active, zero restarts; next health and exact image/pointer identities matched expectations. Both CI runners active with zero restarts and running jobs. No failed system or calternal user units. CI slice MemoryMax=32 GiB and agent slice MemoryMax=24 GiB, with oomd enabled. All fresh heavy work used one build slot and no more than three workers. No build was needed. Verification accounting: unchanged committed product/test source, with SHA-256 manifest attached. Explicit passing injection positions retained from the two interrupted runs were combined with freshly executed missing positions using the original injection function and matrix. Every baseline was rerun and boundary counts checked. This is a completed combined sweep, not an uninterrupted full rerun. Other gates and all 25 real rehearsal rows ran fresh on this boot. Evidence is untracked; no artifacts were committed. Real staging rehearsal (fresh this boot): | Run | Result | | --- | --- | | real staging normal deploy | PASS | | real staging same SHA, same MainPID | PASS | | real staging rollback | PASS | | bootstrap full filesystem admission | PASS | | bootstrap full filesystem after admission | PASS | | bootstrap candidate failure recovers unlabelled image | PASS | | bootstrap from unlabelled running image | PASS | | rollback to recorded legacy image | PASS | | normal deploy after bootstrap removes legacy exception | PASS | | same SHA after bootstrap | PASS | | normal rollback after bootstrap | PASS | | final staging state | PASS | | real staging deploy | PASS | | real staging same SHA | PASS | | real staging rollback | PASS | | real full receipt filesystem cleans partial upload | PASS | | real staging invalid generator input | PASS | | real staging failed candidate recovery | PASS | | isolated store paths verified | PASS | | isolated real Podman deploy and rollback | PASS | | persistent full image store through recovery | PASS | | free-space margin refuses next deploy | PASS | | final real staging check | PASS | | real generator output loss and static fallback | PASS | | real SSH disconnect mid-switch | PASS | Rehearsals restored original staging unit bytes, image pointers and mount identities. Full disk cases used bounded dedicated filesystems; shared filesystems were never filled. Disposable fixtures were cleaned. [Complete evidence, retained output, resume source, source hashes and per-run logs](https://git.kayg.org/attachments/41c05e95-7549-44d8-9b76-889b74a237c4). Final health output, verbatim: ```text NRestarts=0 Id=forgejo-runner.service ActiveState=active SubState=running NRestarts=0 Id=forgejo-runner-web.service ActiveState=active SubState=running MemoryHigh=30064771072 MemoryMax=34359738368 ManagedOOMMemoryPressure=kill Id=ci.slice MemoryHigh=21474836480 MemoryMax=25769803776 ManagedOOMMemoryPressure=kill Id=user-1002.slice active NRestarts=0 Id=calternal-next.service ActiveState=active SubState=running NRestarts=0 Id=calternal-preview.service ActiveState=active SubState=running calternal-next running=true image=60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb UNIT LOAD ACTIVE SUB DESCRIPTION 0 loaded units listed. UNIT LOAD ACTIVE SUB DESCRIPTION 0 loaded units listed. image record: sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb sha256:26ec8a7decf4c161acbabf8c99ebb2ba868075dcd4697fe08df356295aab8108 disposable leftovers: [] {"revision":"8dc52337a566158e86b0234bcfb082331d76d1cd","status":"ok","version":"0.1.0"} ``` Gate output, verbatim: ```text bash -n: PASS shellcheck: PASS diff whitespace: PASS real Quadlet generation (main and immutable recovery): PASS real Quadlet invalid candidate rejected before install: PASS target receiver fails before producer open; no retained artifact: PASS target caller lost before FIFO open cleans upload and releases lock: PASS target partial receipt failure cleans private upload: PASS target normal: PASS target failed-transaction: PASS target closed-output: PASS target dropped-caller: PASS target locked-receipt: PASS remote predecessor-runtime-error: PASS remote predecessor-inspection-error: PASS remote malformed-pointer: PASS remote multiline-pointer: PASS remote foreign-legacy-pointer: PASS remote equal-pointers: PASS remote symlink-pointer: PASS remote invalid-generator: PASS remote closed-output: PASS remote generator-fails: PASS remote byte-exact-recovery: PASS remote normal: PASS remote stale-recovery-definition: PASS remote stale-primary-recovery: PASS remote post-start-unit-rename: PASS remote retained-recovery: PASS remote same-revision different digest: PASS remote disk-full: PASS remote tag-fails: PASS remote pointer-commit-fails: PASS remote start-fails: PASS remote health-fails: PASS remote stale-response: PASS remote inactive-systemd: PASS remote wrong-container-image: PASS remote stop-fails: PASS remote reload-fails: PASS remote unit-install-fails: PASS remote unit-move-fails: PASS remote load-fails: PASS remote checksum-mismatch: PASS remote label-mismatch: PASS remote id-mismatch: PASS remote pre-unhealthy: PASS remote open-secret: PASS remote missing-secret: PASS remote symlink-secret: PASS remote unlabelled-baseline: PASS remote persistent-daemon-reload: PASS remote persistent-unit-rename: PASS remote stop-returns-error-after-stopping: PASS remote post-stop-podman-error: PASS remote recovery-start-fails: PASS remote spaced-open-secret: PASS remote spaced-new-open-secret: PASS remote multiple-open-secret: PASS remote optional-open-secret: PASS remote service-open-secret: PASS remote continued-open-secret: PASS remote quoted-open-secret: PASS remote unsupported-specifier: PASS remote unsupported-escape: PASS remote repeated-open-secret: PASS remote spaced-safe-secret: PASS remote multiple-safe-secret: PASS remote optional-safe-secret: PASS remote service-safe-secret: PASS remote continued-safe-secret: PASS remote quoted-safe-secret: PASS remote home-safe-secret: PASS remote percent-safe-secret: PASS remote signal: PASS remote rerun: PASS remote manual-rollback: PASS remote concurrent upload/switch serialized: PASS bootstrap legacy rollback verifies exact digest and status: PASS failed first update preserves baseline; absent rollback refuses before stop: PASS predecessor existence/inspection errors preserve older rollback through failed health: PASS injection deploy/normal/before: retained 92/92 explicit PASS positions from unchanged source injection deploy/normal/before: 92/92 accounted PASS injection deploy/normal/after: retained 92/92 explicit PASS positions from unchanged source injection deploy/normal/after: 92/92 accounted PASS persistent disk deploy/normal/before: retained 92/92 explicit PASS positions from unchanged source persistent disk deploy/normal/before: 92/92 accounted PASS persistent disk deploy/normal/after: retained 92/92 explicit PASS positions from unchanged source persistent disk deploy/normal/after: 92/92 accounted PASS injection same-sha/normal/before: retained 34/34 explicit PASS positions from unchanged source injection same-sha/normal/before: 34/34 accounted PASS injection same-sha/normal/after: retained 34/34 explicit PASS positions from unchanged source injection same-sha/normal/after: 34/34 accounted PASS injection same-image/normal/before: retained 34/34 explicit PASS positions from unchanged source injection same-image/normal/before: 34/34 accounted PASS injection same-image/normal/after: retained 34/34 explicit PASS positions from unchanged source injection same-image/normal/after: 34/34 accounted PASS injection rollback/normal/before: retained 79/79 explicit PASS positions from unchanged source injection rollback/normal/before: 79/79 accounted PASS injection rollback/normal/after: retained 79/79 explicit PASS positions from unchanged source injection rollback/normal/after: 79/79 accounted PASS persistent disk rollback/normal/before: retained 79/79 explicit PASS positions from unchanged source persistent disk rollback/normal/before: 79/79 accounted PASS persistent disk rollback/normal/after: retained 79/79 explicit PASS positions from unchanged source persistent disk rollback/normal/after: 79/79 accounted PASS injection first-update/normal/before: retained 91/91 explicit PASS positions from unchanged source injection first-update/normal/before: 91/91 accounted PASS injection first-update/normal/after: retained 91/91 explicit PASS positions from unchanged source injection first-update/normal/after: 91/91 accounted PASS persistent disk first-update/normal/before: retained 91/91 explicit PASS positions from unchanged source persistent disk first-update/normal/before: 91/91 accounted PASS persistent disk first-update/normal/after: retained 91/91 explicit PASS positions from unchanged source persistent disk first-update/normal/after: 91/91 accounted PASS injection bootstrap-upgrade/normal/before: retained 91/91 explicit PASS positions from unchanged source injection bootstrap-upgrade/normal/before: 91/91 accounted PASS injection bootstrap-upgrade/normal/after: retained 91/91 explicit PASS positions from unchanged source injection bootstrap-upgrade/normal/after: 91/91 accounted PASS persistent disk bootstrap-upgrade/normal/before: retained 91/91 explicit PASS positions from unchanged source persistent disk bootstrap-upgrade/normal/before: 91/91 accounted PASS persistent disk bootstrap-upgrade/normal/after: retained 91/91 explicit PASS positions from unchanged source persistent disk bootstrap-upgrade/normal/after: 91/91 accounted PASS injection bootstrap-health-fails/normal/before: retained 106/106 explicit PASS positions from unchanged source injection bootstrap-health-fails/normal/before: 106/106 accounted PASS injection bootstrap-health-fails/normal/after: retained 106/106 explicit PASS positions from unchanged source injection bootstrap-health-fails/normal/after: 106/106 accounted PASS persistent disk bootstrap-health-fails/normal/before: retained 106/106 explicit PASS positions from unchanged source persistent disk bootstrap-health-fails/normal/before: 106/106 accounted PASS persistent disk bootstrap-health-fails/normal/after: retained 106/106 explicit PASS positions from unchanged source persistent disk bootstrap-health-fails/normal/after: 106/106 accounted PASS injection bootstrap/normal/before: retained 8/8 explicit PASS positions from unchanged source injection bootstrap/normal/before: 8/8 accounted PASS injection bootstrap/normal/after: retained 8/8 explicit PASS positions from unchanged source injection bootstrap/normal/after: 8/8 accounted PASS injection bootstrap-missing-unit/normal/before: retained 5/5 explicit PASS positions from unchanged source injection bootstrap-missing-unit/normal/before: 5/5 accounted PASS injection bootstrap-missing-unit/normal/after: retained 5/5 explicit PASS positions from unchanged source injection bootstrap-missing-unit/normal/after: 5/5 accounted PASS injection deploy/health-fails/before: retained 107/107 explicit PASS positions from unchanged source injection deploy/health-fails/before: 107/107 accounted PASS injection deploy/health-fails/after: retained 107/107 explicit PASS positions from unchanged source injection deploy/health-fails/after: 107/107 accounted PASS persistent disk deploy/health-fails/before: retained 107/107 explicit PASS positions from unchanged source persistent disk deploy/health-fails/before: 107/107 accounted PASS persistent disk deploy/health-fails/after: retained 107/107 explicit PASS positions from unchanged source persistent disk deploy/health-fails/after: 107/107 accounted PASS injection rollback/health-fails/before: retained 94/94 explicit PASS positions from unchanged source injection rollback/health-fails/before: 94/94 accounted PASS injection rollback/health-fails/after: retained 94/94 explicit PASS positions from unchanged source injection rollback/health-fails/after: 94/94 accounted PASS persistent disk rollback/health-fails/before: retained 94/94 explicit PASS positions from unchanged source persistent disk rollback/health-fails/before: 94/94 accounted PASS persistent disk rollback/health-fails/after: retained 94/94 explicit PASS positions from unchanged source persistent disk rollback/health-fails/after: 94/94 accounted PASS injection deploy/stale-primary-recovery/before: retained 105/105 explicit PASS positions from unchanged source injection deploy/stale-primary-recovery/before: 105/105 accounted PASS injection deploy/stale-primary-recovery/after: retained 105/105 explicit PASS positions from unchanged source injection deploy/stale-primary-recovery/after: 105/105 accounted PASS persistent disk deploy/stale-primary-recovery/before: retained 105/105 explicit PASS positions from unchanged source persistent disk deploy/stale-primary-recovery/before: 105/105 accounted PASS persistent disk deploy/stale-primary-recovery/after: retained 105/105 explicit PASS positions from unchanged source persistent disk deploy/stale-primary-recovery/after: 105/105 accounted PASS injection rollback/stale-primary-recovery/before: retained 92/92 explicit PASS positions from unchanged source injection rollback/stale-primary-recovery/before: 92/92 accounted PASS injection rollback/stale-primary-recovery/after: retained 0/92 explicit PASS positions from unchanged source injection rollback/stale-primary-recovery/after: resumed 40/92 PASS injection rollback/stale-primary-recovery/after: resumed 80/92 PASS injection rollback/stale-primary-recovery/after: 92/92 accounted PASS persistent disk rollback/stale-primary-recovery/before: retained 0/92 explicit PASS positions from unchanged source persistent disk rollback/stale-primary-recovery/before: resumed 40/92 PASS persistent disk rollback/stale-primary-recovery/before: resumed 80/92 PASS persistent disk rollback/stale-primary-recovery/before: 92/92 accounted PASS persistent disk rollback/stale-primary-recovery/after: retained 0/92 explicit PASS positions from unchanged source persistent disk rollback/stale-primary-recovery/after: resumed 40/92 PASS persistent disk rollback/stale-primary-recovery/after: resumed 80/92 PASS persistent disk rollback/stale-primary-recovery/after: 92/92 accounted PASS injection deploy/post-start-unit-rename/before: retained 0/102 explicit PASS positions from unchanged source injection deploy/post-start-unit-rename/before: resumed 40/102 PASS injection deploy/post-start-unit-rename/before: resumed 80/102 PASS injection deploy/post-start-unit-rename/before: 102/102 accounted PASS injection deploy/post-start-unit-rename/after: retained 0/102 explicit PASS positions from unchanged source injection deploy/post-start-unit-rename/after: resumed 40/102 PASS injection deploy/post-start-unit-rename/after: resumed 80/102 PASS injection deploy/post-start-unit-rename/after: 102/102 accounted PASS persistent disk deploy/post-start-unit-rename/before: retained 0/102 explicit PASS positions from unchanged source persistent disk deploy/post-start-unit-rename/before: resumed 40/102 PASS persistent disk deploy/post-start-unit-rename/before: resumed 80/102 PASS persistent disk deploy/post-start-unit-rename/before: 102/102 accounted PASS persistent disk deploy/post-start-unit-rename/after: retained 0/102 explicit PASS positions from unchanged source persistent disk deploy/post-start-unit-rename/after: resumed 40/102 PASS persistent disk deploy/post-start-unit-rename/after: resumed 80/102 PASS persistent disk deploy/post-start-unit-rename/after: 102/102 accounted PASS injection rollback/post-start-unit-rename/before: retained 0/89 explicit PASS positions from unchanged source injection rollback/post-start-unit-rename/before: resumed 40/89 PASS injection rollback/post-start-unit-rename/before: resumed 80/89 PASS injection rollback/post-start-unit-rename/before: 89/89 accounted PASS injection rollback/post-start-unit-rename/after: retained 0/89 explicit PASS positions from unchanged source injection rollback/post-start-unit-rename/after: resumed 40/89 PASS injection rollback/post-start-unit-rename/after: resumed 80/89 PASS injection rollback/post-start-unit-rename/after: 89/89 accounted PASS persistent disk rollback/post-start-unit-rename/before: retained 0/89 explicit PASS positions from unchanged source persistent disk rollback/post-start-unit-rename/before: resumed 40/89 PASS persistent disk rollback/post-start-unit-rename/before: resumed 80/89 PASS persistent disk rollback/post-start-unit-rename/before: 89/89 accounted PASS persistent disk rollback/post-start-unit-rename/after: retained 0/89 explicit PASS positions from unchanged source persistent disk rollback/post-start-unit-rename/after: resumed 40/89 PASS persistent disk rollback/post-start-unit-rename/after: resumed 80/89 PASS persistent disk rollback/post-start-unit-rename/after: 89/89 accounted PASS exhaustive failure injection resumed: PASS (4354 cases = 3314 retained + 1040 fresh; userdata unchanged) relay success: PASS relay save-fails: PASS relay target-fails: PASS relay bootstrap: PASS non-dev revision and mutable tags rejected for next/cloud: PASS data rollback options rejected: PASS build normal: PASS build dev-moved: PASS build build-fails: PASS build bad-id: PASS build wrong-label: PASS build save-fails: PASS build export-wrong-label: PASS build multiple-images: PASS build root-owned-iid: PASS deploy and code rollback dry-run without transport (next/cloud): PASS deploy regression gates: PASS (completed with retained injection positions; local mocks; no production contact) target receiver fails before producer open; no retained artifact: PASS (real user systemd) target caller lost before FIFO open cleans upload and releases lock: PASS (real user systemd) target normal: PASS (real user systemd) target failed-transaction: PASS (real user systemd) target closed-output: PASS (real user systemd) target dropped-caller: PASS (real user systemd) target locked-receipt: PASS (real user systemd) ``` Gate exit: 0. Real bootstrap/staging rehearsal exit: 0. Additional real regression and SSH-disconnect rehearsal exit: 0. Remote branch and PR head both verified as `768dbacbb6b25773af560b5e10f69a9fbd88f1da`.
Author
Owner

Independent round-seven verification started at 768dbacbb6 in fresh worktree /home/agent/wt/review-748g, tests branch review/748g-tests. I will test bootstrap, space failures, interruption/rerun, health recovery, concurrency and documentation independently. Required prior brief ~/codex-jobs/review-748/prompt.md is absent on this host; available round-six fix briefs and PR/issue history were read. No production contact or merge.

Independent round-seven verification started at 768dbacbb6b25773af560b5e10f69a9fbd88f1da in fresh worktree /home/agent/wt/review-748g, tests branch review/748g-tests. I will test bootstrap, space failures, interruption/rerun, health recovery, concurrency and documentation independently. Required prior brief ~/codex-jobs/review-748/prompt.md is absent on this host; available round-six fix briefs and PR/issue history were read. No production contact or merge.
Author
Owner

Round-seven staging access limitation: the public staging health endpoint at http://10.70.3.14:8080/healthz reports status ok, revision 8dc52337a5. This review session runs as agent (uid 1002), not root. sudo -n true and sudo -n -l require a password. NetBird SSH to the configured root@8by64-compute01.vpn.kayg.org endpoint fails with connection refused at 10.69.69.147:22; loopback SSH also refuses. The private credential request timed out. Therefore installed calternal-user services and a real root-mounted limited image store cannot currently be exercised. Isolated review fixtures will use this host's actual rootless Podman/user-systemd where possible, clearly distinguished from the installed service. Production remains untouched.

Round-seven staging access limitation: the public staging health endpoint at http://10.70.3.14:8080/healthz reports status ok, revision 8dc52337a566158e86b0234bcfb082331d76d1cd. This review session runs as agent (uid 1002), not root. sudo -n true and sudo -n -l require a password. NetBird SSH to the configured root@8by64-compute01.vpn.kayg.org endpoint fails with connection refused at 10.69.69.147:22; loopback SSH also refuses. The private credential request timed out. Therefore installed calternal-user services and a real root-mounted limited image store cannot currently be exercised. Isolated review fixtures will use this host's actual rootless Podman/user-systemd where possible, clearly distinguished from the installed service. Production remains untouched.
Author
Owner

Independent review round 7 — PR #748

Head: 768dbacbb6. Worktree: /home/agent/wt/review-748g.

Required previous brief /home/agent/codex-jobs/review-748/prompt.md is absent. Read available deploy-fix-6/7/8 briefs and fetched PR #748 plus issues #688/#733 and comments. No production access.

P1 — SIGKILL after stop strands the service; rerun cannot recover it

Locations: deploy/next/switch.sh:140–153, deploy/next/remote.sh:188–210; README explicitly excludes this at its SIGKILL/manual-recovery paragraph.

Reproducer: run deploy/tests/staging-review-probe.py as root on 8by64-compute01 with the existing A/B artifact directory and original unlabelled image ID. Its killed-after-stop case adds only kill -KILL $$ immediately after the successful stop_old call in the private transaction copy. It then invokes the unchanged transaction again, against the same service, unit and pointer files. No production connection is used.

Evidence: live/results.jsonl records active_before_rerun=inactive, active_after_rerun=inactive, refused_precheck=true. live/rerun-after-kill.log reports pre-deploy health failed; service was not changed and transaction failed before switch; old service unchanged. A real systemd detached worker does not rescue its killed transaction child. The prepared recovery files and reservations are stranded; a rerun performs the normal precheck instead of reconciling the interrupted switch. The independent 200-point seeded SIGKILL/rerun sweep is still running and will be appended separately. This violates the requested abrupt-stop/rerun and abort availability contract. The test manually repaired and removed its own disposable service; existing staging remained healthy at revision 8dc52337a5.

P2 — Exact data rollback is not provided (explicit contract mismatch)

Locations: deploy/next/switch.sh:155–214; deploy/next/README.md code-only rollback sections.

Reproducer: the same real staging test's data-mutation case creates a synthetic mounted-data sentinel containing before. The candidate unit writes after in ExecStartPre and listens on the wrong internal HTTP port. Candidate health fails, and the transaction automatically restores the exact prior image, unit bytes and pointer. It does not restore the sentinel. This models a startup migration or other candidate write before health verification; it does not claim the current application performs that specific migration.

Evidence: live/results.jsonl records image_recovered=true, data_restored=false, sentinel=after. live/data-mutation.log confirms automatic old-image recovery. Earlier PR history intentionally chose code-only rollback to avoid replacing live userdata, and the README accurately disclaims data restoration. Under this review's explicit requirement that rollback restore prior image and data exactly, that is an unresolved contract gap, not a claim that the script itself deletes data. Resolve the contract or provide an upgrade/data strategy that meets it; blindly restoring live data would conflict with the earlier safety rationale.

# Independent review round 7 — PR #748 Head: 768dbacbb6b25773af560b5e10f69a9fbd88f1da. Worktree: /home/agent/wt/review-748g. Required previous brief /home/agent/codex-jobs/review-748/prompt.md is absent. Read available deploy-fix-6/7/8 briefs and fetched PR #748 plus issues #688/#733 and comments. No production access. ## P1 — SIGKILL after stop strands the service; rerun cannot recover it Locations: deploy/next/switch.sh:140–153, deploy/next/remote.sh:188–210; README explicitly excludes this at its SIGKILL/manual-recovery paragraph. Reproducer: run `deploy/tests/staging-review-probe.py` as root on 8by64-compute01 with the existing A/B artifact directory and original unlabelled image ID. Its `killed-after-stop` case adds only `kill -KILL $$` immediately after the successful `stop_old` call in the private transaction copy. It then invokes the unchanged transaction again, against the same service, unit and pointer files. No production connection is used. Evidence: live/results.jsonl records `active_before_rerun=inactive`, `active_after_rerun=inactive`, `refused_precheck=true`. live/rerun-after-kill.log reports `pre-deploy health failed; service was not changed` and `transaction failed before switch; old service unchanged`. A real systemd detached worker does not rescue its killed transaction child. The prepared recovery files and reservations are stranded; a rerun performs the normal precheck instead of reconciling the interrupted switch. The independent 200-point seeded SIGKILL/rerun sweep is still running and will be appended separately. This violates the requested abrupt-stop/rerun and abort availability contract. The test manually repaired and removed its own disposable service; existing staging remained healthy at revision 8dc52337a566158e86b0234bcfb082331d76d1cd. ## P2 — Exact data rollback is not provided (explicit contract mismatch) Locations: deploy/next/switch.sh:155–214; deploy/next/README.md code-only rollback sections. Reproducer: the same real staging test's `data-mutation` case creates a synthetic mounted-data sentinel containing `before`. The candidate unit writes `after` in ExecStartPre and listens on the wrong internal HTTP port. Candidate health fails, and the transaction automatically restores the exact prior image, unit bytes and pointer. It does not restore the sentinel. This models a startup migration or other candidate write before health verification; it does not claim the current application performs that specific migration. Evidence: live/results.jsonl records `image_recovered=true`, `data_restored=false`, `sentinel=after`. live/data-mutation.log confirms automatic old-image recovery. Earlier PR history intentionally chose code-only rollback to avoid replacing live userdata, and the README accurately disclaims data restoration. Under this review's explicit requirement that rollback restore prior image and data exactly, that is an unresolved contract gap, not a claim that the script itself deletes data. Resolve the contract or provide an upgrade/data strategy that meets it; blindly restoring live data would conflict with the earlier safety rationale.
Author
Owner

P2 — Free-space admission runs after receiving and duplicating the artifact (target.sh:43–47, remote.sh:196–209). Independent python3 deploy/tests/preflight-review-probe.py observes the actual bootstrap at its first disk_preflight call. Five files already exist: bundle.tar=10240 bytes, image.tar=27, image.id=72, image.sha256=76, revision=41; the receiver wrote a separate input copy first. Abort preserves the running process in checked cases, but artifact writes can exhaust the target filesystem before admission. This violates the explicit “free-space preflight runs first” contract. The regression uses real files and the existing isolated service adapters; no production contact.

P2 — Free-space admission runs after receiving and duplicating the artifact (target.sh:43–47, remote.sh:196–209). Independent `python3 deploy/tests/preflight-review-probe.py` observes the actual bootstrap at its first disk_preflight call. Five files already exist: bundle.tar=10240 bytes, image.tar=27, image.id=72, image.sha256=76, revision=41; the receiver wrote a separate input copy first. Abort preserves the running process in checked cases, but artifact writes can exhaust the target filesystem before admission. This violates the explicit “free-space preflight runs first” contract. The regression uses real files and the existing isolated service adapters; no production contact.
Author
Owner

P1 — Actual inode exhaustion strands both primary and fallback recovery. In the independent real Podman/systemd/ext4 rehearsal, after normal preparation and the successful old-service stop, the test creates zero-length files until f_favail reaches zero, then stops creating files. Data blocks remain available. The unchanged switch and EXIT recovery cannot start either service: primary_state=failed, fallback_state=failed, and the transaction reports recovery failed; inspect service locally; private recovery files retained beside the quadlet. Locations: space.sh:19–36 and switch.sh:133–185. The runtime reserve allocates bytes in two files per root, but releasing them does not secure enough inodes for Podman's metadata. This is one-time ENOSPC after admission, with no continuous writer, SIGKILL or read-only filesystem. Reviewer test: deploy/tests/staging-review-probe.py, inode-exhaustion case; evidence: ~/codex-jobs/review-748g/live3/{inode-events.jsonl,inode-exhaustion.log,results.jsonl}. The disposable service was repaired after removing its own filler; installed staging remained healthy. No production contact.

P1 — Actual inode exhaustion strands both primary and fallback recovery. In the independent real Podman/systemd/ext4 rehearsal, after normal preparation and the successful old-service stop, the test creates zero-length files until f_favail reaches zero, then stops creating files. Data blocks remain available. The unchanged switch and EXIT recovery cannot start either service: primary_state=failed, fallback_state=failed, and the transaction reports `recovery failed; inspect service locally; private recovery files retained beside the quadlet`. Locations: space.sh:19–36 and switch.sh:133–185. The runtime reserve allocates bytes in two files per root, but releasing them does not secure enough inodes for Podman's metadata. This is one-time ENOSPC after admission, with no continuous writer, SIGKILL or read-only filesystem. Reviewer test: deploy/tests/staging-review-probe.py, inode-exhaustion case; evidence: ~/codex-jobs/review-748g/live3/{inode-events.jsonl,inode-exhaustion.log,results.jsonl}. The disposable service was repaired after removing its own filler; installed staging remained healthy. No production contact.
Author
Owner

Independent real staging checks completed using this account's existing Docker-group access and a disposable host helper, without changing SSH or sudo configuration. The fixture used actual Podman, user systemd, Quadlet, a 2 GiB ext4 filesystem, saved unlabelled staging image 983a8a530cb4330dadeb8c3f125ea2cfec9b242ae21c571d4fc6e964b5a1e17d, and the existing verified A/B artifacts. The current production image digest was not queried, because production contact is forbidden.

Passed: scripted bootstrap from the unlabelled image; no-predecessor baseline; same-SHA deploy; rollback to exact unlabelled image; real zero-block admission with the same old MainPID; automatic old-image/unit/pointer recovery after candidate health failure; real full-block filesystem retained through recovery; concurrent complete deploy invocations serialized; actual loopback SSH client killed after old-service stop with detached automatic recovery.

Confirmed defects: SIGKILL of the transaction after old-service stop leaves it inactive and normal rerun refuses; one-time inode exhaustion leaves primary and fallback failed despite 1,286,885,376 available bytes; candidate startup data write persists after successful image recovery (the exact-data contract gap). First-preflight ordering is separately reproduced with real files and isolated command adapters.

Disposable units, container, limited disk mount/image, synthetic data, runtime, generated SSH keys and listener were removed. Existing staging health is still ok at 8dc52337a5. No production contact, merge or product-code change. The unmodified deploy gate and seeded 200-point interruption sweep remain running; this is not yet a gate-pass claim.

Independent real staging checks completed using this account's existing Docker-group access and a disposable host helper, without changing SSH or sudo configuration. The fixture used actual Podman, user systemd, Quadlet, a 2 GiB ext4 filesystem, saved unlabelled staging image 983a8a530cb4330dadeb8c3f125ea2cfec9b242ae21c571d4fc6e964b5a1e17d, and the existing verified A/B artifacts. The current production image digest was not queried, because production contact is forbidden. Passed: scripted bootstrap from the unlabelled image; no-predecessor baseline; same-SHA deploy; rollback to exact unlabelled image; real zero-block admission with the same old MainPID; automatic old-image/unit/pointer recovery after candidate health failure; real full-block filesystem retained through recovery; concurrent complete deploy invocations serialized; actual loopback SSH client killed after old-service stop with detached automatic recovery. Confirmed defects: SIGKILL of the transaction after old-service stop leaves it inactive and normal rerun refuses; one-time inode exhaustion leaves primary and fallback failed despite 1,286,885,376 available bytes; candidate startup data write persists after successful image recovery (the exact-data contract gap). First-preflight ordering is separately reproduced with real files and isolated command adapters. Disposable units, container, limited disk mount/image, synthetic data, runtime, generated SSH keys and listener were removed. Existing staging health is still ok at 8dc52337a566158e86b0234bcfb082331d76d1cd. No production contact, merge or product-code change. The unmodified deploy gate and seeded 200-point interruption sweep remain running; this is not yet a gate-pass claim.
Author
Owner

Reviewer tests are pushed only to review/748g-tests, head 0ab7098b2416dd4b4dd62cd8953742af14983238. The author branch remains 768dbacbb6b25773af560b5e10f69a9fbd88f1da. Three Python reproducer/rehearsal scripts were committed; no evidence or product changes.

The real Podman/systemd sweep completed 200 distinct seeded (path, Bash command line) stop points, each with a confirmed SIGKILL exit 137 and a normal rerun before manual fixture repair. 16 cases left the primary service non-active; 85 reruns returned failure. The separate adapter sweep confirmed 200 SIGKILLs, with 13 stopped after rerun and 91 refused reruns. These are separate sweeps, not reused author results.

The final source rehearsal exited 0 after reproducing both P1s again. Actual inode exhaustion left primary and fallback failed with 1,286,844,416 bytes still free. Same-SHA deploy preserved MainPID, unit and pointer bytes; real concurrent invocations serialized; an actual SSH disconnect after stop recovered the old image. Synthetic mounted data remained changed after failed-candidate image rollback, as recorded in the exact-data contract finding.

Disposable resources and owned controls are removed. Installed staging is still healthy on exact image sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb, revision 8dc52337a566158e86b0234bcfb082331d76d1cd; CI runner active. No production contact or merge.

The original, unmodified bash deploy/tests/gate.sh remains running through the rollback/failed-health matrices. This is not a full gate-pass claim. I will post its exit status and complete output verbatim when it finishes. Verdict remains fix first, with two P1s and two P2 contract/order findings already recorded above and in ~/codex-jobs/review-748g/findings.md.

Reviewer tests are pushed **only** to [review/748g-tests](https://git.kayg.org/kayg/calternal/src/branch/review/748g-tests), head `0ab7098b2416dd4b4dd62cd8953742af14983238`. The author branch remains `768dbacbb6b25773af560b5e10f69a9fbd88f1da`. Three Python reproducer/rehearsal scripts were committed; no evidence or product changes. The real Podman/systemd sweep completed **200 distinct seeded (path, Bash command line) stop points**, each with a confirmed SIGKILL exit 137 and a normal rerun before manual fixture repair. **16 cases left the primary service non-active; 85 reruns returned failure.** The separate adapter sweep confirmed 200 SIGKILLs, with 13 stopped after rerun and 91 refused reruns. These are separate sweeps, not reused author results. The final source rehearsal exited 0 after reproducing both P1s again. Actual inode exhaustion left primary and fallback failed with 1,286,844,416 bytes still free. Same-SHA deploy preserved MainPID, unit and pointer bytes; real concurrent invocations serialized; an actual SSH disconnect after stop recovered the old image. Synthetic mounted data remained changed after failed-candidate image rollback, as recorded in the exact-data contract finding. Disposable resources and owned controls are removed. Installed staging is still healthy on exact image `sha256:60f680ae778350c332b127e477c3db3590927d4040007f0d9446c4d5d07c57eb`, revision `8dc52337a566158e86b0234bcfb082331d76d1cd`; CI runner active. No production contact or merge. The original, unmodified `bash deploy/tests/gate.sh` remains running through the rollback/failed-health matrices. **This is not a full gate-pass claim.** I will post its exit status and complete output verbatim when it finishes. Verdict remains **fix first**, with two P1s and two P2 contract/order findings already recorded above and in `~/codex-jobs/review-748g/findings.md`.
Author
Owner

Verdict: fix first. Independent round-seven review of 768dbacbb6b25773af560b5e10f69a9fbd88f1da (PR #748).

  • P1 — Inode exhaustion strands recovery. After normal preparation and successful old-service stop, one-time inode exhaustion on the real private ext4 image store leaves both primary and fallback failed. Data blocks remain available (about 1.28 GB). Byte reserves release too few inodes for Podman metadata. Reproducer: staging-review-probe.py, inode-exhaustion case. Evidence: final rehearsal inode-events.jsonl, inode-exhaustion.log and results.jsonl.
  • P1 — SIGKILL/rerun strands the service. Kill the actual transaction after stop_old: the service stays inactive, and a normal rerun refuses at pre-health instead of reconciling prepared recovery. Reproducer: staging probe's killed-after-stop/rerun-after-kill cases and its seeded real-runtime interruption sweep. The separate adapter sweep completed 200 confirmed SIGKILLs: 13 remained stopped after rerun; 91 reruns refused. The independent real Podman/systemd sweep also completed 200 distinct seeded stop points, all confirmed SIGKILL exit 137: 16/200 left the primary non-active after rerun; 85/200 reruns returned nonzero.
  • P2 — Space admission happens after artifact writes. The detached receiver input, a second bundle copy and extracted image already exist at the first disk_preflight. python3 deploy/tests/preflight-review-probe.py observes the real file writes before admission (10240-byte bundle plus extracted image/metadata for its small fixture). This fails the explicit preflight-first requirement, although checked early aborts keep the process serving.
  • P2 — Exact data rollback is unsupported. A real candidate startup hook changes a synthetic mounted-data sentinel, then candidate health fails. The previous image/unit/pointer recover, but the sentinel remains changed. This models an upgrade write, not a claim that today's candidate performs that migration. Earlier history intentionally selected code-only rollback; README accurately describes that choice. It conflicts with this review's exact-data contract and must be resolved explicitly, without blindly replacing live user data.

The README bootstrap invocation matches the script. Its SIGKILL/manual-repair and code-only-data limits match implementation; those are explicit differences from the requested contract.

Real rehearsal: saved unlabelled staging image → labelled A; exact legacy rollback; same-SHA no-op; full-block admission and exhaustion immediately after admission with unchanged old MainPID; failed candidate health; full-block image-store exhaustion retained through recovery; concurrent complete deploy invocations; and an actual SSH client killed after old-service stop. Real Podman, Quadlet and systemd were used on 8by64-compute01, with a unique disposable service and private 2 GiB ext4 storage. The synthetic unit uses a one-second stop timeout and Restart=no to bound repeated trials; the existing next service was not switched. Shared filesystems were not filled. The current production digest was not queried. Exhaustive per-command full-disk/failure coverage below is the repository's adapter matrix; physical exhaustion checks cover the explicitly listed boundaries, including the newly discovered inode failure.

Reviewer tests only: review/748g-tests, head 0ab7098b2416dd4b4dd62cd8953742af14983238. No product changes, production contact, merge or issue closure. Detailed reproducers and evidence are in findings.md inside the attached archive. Evidence archive; SHA-256 fddaf3fb013428e1f19fb34d659d6bbfde9ccf56a60d3ec3b162055991f66531. Test sources.

Cleanup: disposable service, fallback, container, mounted disk, synthetic data, runtime, owned controls and generated loopback SSH credentials/listener removed. Existing staging healthy at 8dc52337a566158e86b0234bcfb082331d76d1cd; CI runner active.

Gate command: bash deploy/tests/gate.sh. Exit 0. Output verbatim:

bash -n: PASS
shellcheck: PASS
diff whitespace: PASS
real Quadlet generation (main and immutable recovery): PASS
real Quadlet invalid candidate rejected before install: PASS
target receiver fails before producer open; no retained artifact: PASS
target caller lost before FIFO open cleans upload and releases lock: PASS
target partial receipt failure cleans private upload: PASS
target normal: PASS
target failed-transaction: PASS
target closed-output: PASS
target dropped-caller: PASS
target locked-receipt: PASS
remote predecessor-runtime-error: PASS
remote predecessor-inspection-error: PASS
remote malformed-pointer: PASS
remote multiline-pointer: PASS
remote foreign-legacy-pointer: PASS
remote equal-pointers: PASS
remote symlink-pointer: PASS
remote invalid-generator: PASS
remote closed-output: PASS
remote generator-fails: PASS
remote byte-exact-recovery: PASS
remote normal: PASS
remote stale-recovery-definition: PASS
remote stale-primary-recovery: PASS
remote post-start-unit-rename: PASS
remote retained-recovery: PASS
remote same-revision different digest: PASS
remote disk-full: PASS
remote tag-fails: PASS
remote pointer-commit-fails: PASS
remote start-fails: PASS
remote health-fails: PASS
remote stale-response: PASS
remote inactive-systemd: PASS
remote wrong-container-image: PASS
remote stop-fails: PASS
remote reload-fails: PASS
remote unit-install-fails: PASS
remote unit-move-fails: PASS
remote load-fails: PASS
remote checksum-mismatch: PASS
remote label-mismatch: PASS
remote id-mismatch: PASS
remote pre-unhealthy: PASS
remote open-secret: PASS
remote missing-secret: PASS
remote symlink-secret: PASS
remote unlabelled-baseline: PASS
remote persistent-daemon-reload: PASS
remote persistent-unit-rename: PASS
remote stop-returns-error-after-stopping: PASS
remote post-stop-podman-error: PASS
remote recovery-start-fails: PASS
remote spaced-open-secret: PASS
remote spaced-new-open-secret: PASS
remote multiple-open-secret: PASS
remote optional-open-secret: PASS
remote service-open-secret: PASS
remote continued-open-secret: PASS
remote quoted-open-secret: PASS
remote unsupported-specifier: PASS
remote unsupported-escape: PASS
remote repeated-open-secret: PASS
remote spaced-safe-secret: PASS
remote multiple-safe-secret: PASS
remote optional-safe-secret: PASS
remote service-safe-secret: PASS
remote continued-safe-secret: PASS
remote quoted-safe-secret: PASS
remote home-safe-secret: PASS
remote percent-safe-secret: PASS
remote signal: PASS
remote rerun: PASS
remote manual-rollback: PASS
remote concurrent upload/switch serialized: PASS
bootstrap legacy rollback verifies exact digest and status: PASS
failed first update preserves baseline; absent rollback refuses before stop: PASS
predecessor existence/inspection errors preserve older rollback through failed health: PASS
injection deploy/normal/before: 40/92 PASS
injection deploy/normal/before: 80/92 PASS
injection deploy/normal/after: 40/92 PASS
injection deploy/normal/after: 80/92 PASS
state machine deploy/normal: 92 steps, before/after failures PASS
persistent disk deploy/normal/before: 40/92 PASS
persistent disk deploy/normal/before: 80/92 PASS
persistent disk deploy/normal/after: 40/92 PASS
persistent disk deploy/normal/after: 80/92 PASS
persistent full disk deploy/normal: every step before/after PASS (including recovery)
state machine same-sha/normal: 34 steps, before/after failures PASS
state machine same-image/normal: 34 steps, before/after failures PASS
injection rollback/normal/before: 40/79 PASS
injection rollback/normal/after: 40/79 PASS
state machine rollback/normal: 79 steps, before/after failures PASS
persistent disk rollback/normal/before: 40/79 PASS
persistent disk rollback/normal/after: 40/79 PASS
persistent full disk rollback/normal: every step before/after PASS (including recovery)
injection first-update/normal/before: 40/91 PASS
injection first-update/normal/before: 80/91 PASS
injection first-update/normal/after: 40/91 PASS
injection first-update/normal/after: 80/91 PASS
state machine first-update/normal: 91 steps, before/after failures PASS
persistent disk first-update/normal/before: 40/91 PASS
persistent disk first-update/normal/before: 80/91 PASS
persistent disk first-update/normal/after: 40/91 PASS
persistent disk first-update/normal/after: 80/91 PASS
persistent full disk first-update/normal: every step before/after PASS (including recovery)
injection bootstrap-upgrade/normal/before: 40/91 PASS
injection bootstrap-upgrade/normal/before: 80/91 PASS
injection bootstrap-upgrade/normal/after: 40/91 PASS
injection bootstrap-upgrade/normal/after: 80/91 PASS
state machine bootstrap-upgrade/normal: 91 steps, before/after failures PASS
persistent disk bootstrap-upgrade/normal/before: 40/91 PASS
persistent disk bootstrap-upgrade/normal/before: 80/91 PASS
persistent disk bootstrap-upgrade/normal/after: 40/91 PASS
persistent disk bootstrap-upgrade/normal/after: 80/91 PASS
persistent full disk bootstrap-upgrade/normal: every step before/after PASS (including recovery)
injection bootstrap-health-fails/normal/before: 40/106 PASS
injection bootstrap-health-fails/normal/before: 80/106 PASS
injection bootstrap-health-fails/normal/after: 40/106 PASS
injection bootstrap-health-fails/normal/after: 80/106 PASS
state machine bootstrap-health-fails/normal: 106 steps, before/after failures PASS
persistent disk bootstrap-health-fails/normal/before: 40/106 PASS
persistent disk bootstrap-health-fails/normal/before: 80/106 PASS
persistent disk bootstrap-health-fails/normal/after: 40/106 PASS
persistent disk bootstrap-health-fails/normal/after: 80/106 PASS
persistent full disk bootstrap-health-fails/normal: every step before/after PASS (including recovery)
state machine bootstrap/normal: 8 steps, before/after failures PASS
state machine bootstrap-missing-unit/normal: 5 steps, before/after failures PASS
injection deploy/health-fails/before: 40/107 PASS
injection deploy/health-fails/before: 80/107 PASS
injection deploy/health-fails/after: 40/107 PASS
injection deploy/health-fails/after: 80/107 PASS
state machine deploy/health-fails: 107 steps, before/after failures PASS
persistent disk deploy/health-fails/before: 40/107 PASS
persistent disk deploy/health-fails/before: 80/107 PASS
persistent disk deploy/health-fails/after: 40/107 PASS
persistent disk deploy/health-fails/after: 80/107 PASS
persistent full disk deploy/health-fails: every step before/after PASS (including recovery)
injection rollback/health-fails/before: 40/94 PASS
injection rollback/health-fails/before: 80/94 PASS
injection rollback/health-fails/after: 40/94 PASS
injection rollback/health-fails/after: 80/94 PASS
state machine rollback/health-fails: 94 steps, before/after failures PASS
persistent disk rollback/health-fails/before: 40/94 PASS
persistent disk rollback/health-fails/before: 80/94 PASS
persistent disk rollback/health-fails/after: 40/94 PASS
persistent disk rollback/health-fails/after: 80/94 PASS
persistent full disk rollback/health-fails: every step before/after PASS (including recovery)
injection deploy/stale-primary-recovery/before: 40/105 PASS
injection deploy/stale-primary-recovery/before: 80/105 PASS
injection deploy/stale-primary-recovery/after: 40/105 PASS
injection deploy/stale-primary-recovery/after: 80/105 PASS
state machine deploy/stale-primary-recovery: 105 steps, before/after failures PASS
persistent disk deploy/stale-primary-recovery/before: 40/105 PASS
persistent disk deploy/stale-primary-recovery/before: 80/105 PASS
persistent disk deploy/stale-primary-recovery/after: 40/105 PASS
persistent disk deploy/stale-primary-recovery/after: 80/105 PASS
persistent full disk deploy/stale-primary-recovery: every step before/after PASS (including recovery)
injection rollback/stale-primary-recovery/before: 40/92 PASS
injection rollback/stale-primary-recovery/before: 80/92 PASS
injection rollback/stale-primary-recovery/after: 40/92 PASS
injection rollback/stale-primary-recovery/after: 80/92 PASS
state machine rollback/stale-primary-recovery: 92 steps, before/after failures PASS
persistent disk rollback/stale-primary-recovery/before: 40/92 PASS
persistent disk rollback/stale-primary-recovery/before: 80/92 PASS
persistent disk rollback/stale-primary-recovery/after: 40/92 PASS
persistent disk rollback/stale-primary-recovery/after: 80/92 PASS
persistent full disk rollback/stale-primary-recovery: every step before/after PASS (including recovery)
injection deploy/post-start-unit-rename/before: 40/102 PASS
injection deploy/post-start-unit-rename/before: 80/102 PASS
injection deploy/post-start-unit-rename/after: 40/102 PASS
injection deploy/post-start-unit-rename/after: 80/102 PASS
state machine deploy/post-start-unit-rename: 102 steps, before/after failures PASS
persistent disk deploy/post-start-unit-rename/before: 40/102 PASS
persistent disk deploy/post-start-unit-rename/before: 80/102 PASS
persistent disk deploy/post-start-unit-rename/after: 40/102 PASS
persistent disk deploy/post-start-unit-rename/after: 80/102 PASS
persistent full disk deploy/post-start-unit-rename: every step before/after PASS (including recovery)
injection rollback/post-start-unit-rename/before: 40/89 PASS
injection rollback/post-start-unit-rename/before: 80/89 PASS
injection rollback/post-start-unit-rename/after: 40/89 PASS
injection rollback/post-start-unit-rename/after: 80/89 PASS
state machine rollback/post-start-unit-rename: 89 steps, before/after failures PASS
persistent disk rollback/post-start-unit-rename/before: 40/89 PASS
persistent disk rollback/post-start-unit-rename/before: 80/89 PASS
persistent disk rollback/post-start-unit-rename/after: 40/89 PASS
persistent disk rollback/post-start-unit-rename/after: 80/89 PASS
persistent full disk rollback/post-start-unit-rename: every step before/after PASS (including recovery)
exhaustive failure injection: PASS (4354 cases; userdata unchanged)
relay success: PASS
relay save-fails: PASS
relay target-fails: PASS
relay bootstrap: PASS
non-dev revision and mutable tags rejected for next/cloud: PASS
data rollback options rejected: PASS
build normal: PASS
build dev-moved: PASS
build build-fails: PASS
build bad-id: PASS
build wrong-label: PASS
build save-fails: PASS
build export-wrong-label: PASS
build multiple-images: PASS
build root-owned-iid: PASS
deploy and code rollback dry-run without transport (next/cloud): PASS
deploy regression gates: PASS (local mocks; no production contact)
Verdict: **fix first**. Independent round-seven review of `768dbacbb6b25773af560b5e10f69a9fbd88f1da` (PR #748). - **P1 — Inode exhaustion strands recovery.** After normal preparation and successful old-service stop, one-time inode exhaustion on the real private ext4 image store leaves both primary and fallback failed. Data blocks remain available (about 1.28 GB). Byte reserves release too few inodes for Podman metadata. Reproducer: `staging-review-probe.py`, `inode-exhaustion` case. Evidence: final rehearsal inode-events.jsonl, inode-exhaustion.log and results.jsonl. - **P1 — SIGKILL/rerun strands the service.** Kill the actual transaction after `stop_old`: the service stays inactive, and a normal rerun refuses at pre-health instead of reconciling prepared recovery. Reproducer: staging probe's killed-after-stop/rerun-after-kill cases and its seeded real-runtime interruption sweep. The separate adapter sweep completed 200 confirmed SIGKILLs: 13 remained stopped after rerun; 91 reruns refused. The independent real Podman/systemd sweep also completed 200 distinct seeded stop points, all confirmed SIGKILL exit 137: **16/200** left the primary non-active after rerun; **85/200** reruns returned nonzero. - **P2 — Space admission happens after artifact writes.** The detached receiver input, a second bundle copy and extracted image already exist at the first disk_preflight. `python3 deploy/tests/preflight-review-probe.py` observes the real file writes before admission (10240-byte bundle plus extracted image/metadata for its small fixture). This fails the explicit preflight-first requirement, although checked early aborts keep the process serving. - **P2 — Exact data rollback is unsupported.** A real candidate startup hook changes a synthetic mounted-data sentinel, then candidate health fails. The previous image/unit/pointer recover, but the sentinel remains changed. This models an upgrade write, not a claim that today's candidate performs that migration. Earlier history intentionally selected code-only rollback; README accurately describes that choice. It conflicts with this review's exact-data contract and must be resolved explicitly, without blindly replacing live user data. The README bootstrap invocation matches the script. Its SIGKILL/manual-repair and code-only-data limits match implementation; those are explicit differences from the requested contract. Real rehearsal: saved unlabelled staging image → labelled A; exact legacy rollback; same-SHA no-op; full-block admission and exhaustion immediately after admission with unchanged old MainPID; failed candidate health; full-block image-store exhaustion retained through recovery; concurrent complete deploy invocations; and an actual SSH client killed after old-service stop. Real Podman, Quadlet and systemd were used on 8by64-compute01, with a unique disposable service and private 2 GiB ext4 storage. The synthetic unit uses a one-second stop timeout and Restart=no to bound repeated trials; the existing next service was not switched. Shared filesystems were not filled. The current production digest was not queried. Exhaustive per-command full-disk/failure coverage below is the repository's adapter matrix; physical exhaustion checks cover the explicitly listed boundaries, including the newly discovered inode failure. Reviewer tests only: `review/748g-tests`, head `0ab7098b2416dd4b4dd62cd8953742af14983238`. No product changes, production contact, merge or issue closure. Detailed reproducers and evidence are in `findings.md` inside the attached archive. [Evidence archive](https://git.kayg.org/attachments/2ad2461e-591c-49ae-833d-4e0ac25c1691); SHA-256 `fddaf3fb013428e1f19fb34d659d6bbfde9ccf56a60d3ec3b162055991f66531`. [Test sources](https://git.kayg.org/kayg/calternal/src/branch/review/748g-tests/deploy/tests). Cleanup: disposable service, fallback, container, mounted disk, synthetic data, runtime, owned controls and generated loopback SSH credentials/listener removed. Existing staging healthy at `8dc52337a566158e86b0234bcfb082331d76d1cd`; CI runner active. Gate command: `bash deploy/tests/gate.sh`. Exit 0. Output verbatim: ```text bash -n: PASS shellcheck: PASS diff whitespace: PASS real Quadlet generation (main and immutable recovery): PASS real Quadlet invalid candidate rejected before install: PASS target receiver fails before producer open; no retained artifact: PASS target caller lost before FIFO open cleans upload and releases lock: PASS target partial receipt failure cleans private upload: PASS target normal: PASS target failed-transaction: PASS target closed-output: PASS target dropped-caller: PASS target locked-receipt: PASS remote predecessor-runtime-error: PASS remote predecessor-inspection-error: PASS remote malformed-pointer: PASS remote multiline-pointer: PASS remote foreign-legacy-pointer: PASS remote equal-pointers: PASS remote symlink-pointer: PASS remote invalid-generator: PASS remote closed-output: PASS remote generator-fails: PASS remote byte-exact-recovery: PASS remote normal: PASS remote stale-recovery-definition: PASS remote stale-primary-recovery: PASS remote post-start-unit-rename: PASS remote retained-recovery: PASS remote same-revision different digest: PASS remote disk-full: PASS remote tag-fails: PASS remote pointer-commit-fails: PASS remote start-fails: PASS remote health-fails: PASS remote stale-response: PASS remote inactive-systemd: PASS remote wrong-container-image: PASS remote stop-fails: PASS remote reload-fails: PASS remote unit-install-fails: PASS remote unit-move-fails: PASS remote load-fails: PASS remote checksum-mismatch: PASS remote label-mismatch: PASS remote id-mismatch: PASS remote pre-unhealthy: PASS remote open-secret: PASS remote missing-secret: PASS remote symlink-secret: PASS remote unlabelled-baseline: PASS remote persistent-daemon-reload: PASS remote persistent-unit-rename: PASS remote stop-returns-error-after-stopping: PASS remote post-stop-podman-error: PASS remote recovery-start-fails: PASS remote spaced-open-secret: PASS remote spaced-new-open-secret: PASS remote multiple-open-secret: PASS remote optional-open-secret: PASS remote service-open-secret: PASS remote continued-open-secret: PASS remote quoted-open-secret: PASS remote unsupported-specifier: PASS remote unsupported-escape: PASS remote repeated-open-secret: PASS remote spaced-safe-secret: PASS remote multiple-safe-secret: PASS remote optional-safe-secret: PASS remote service-safe-secret: PASS remote continued-safe-secret: PASS remote quoted-safe-secret: PASS remote home-safe-secret: PASS remote percent-safe-secret: PASS remote signal: PASS remote rerun: PASS remote manual-rollback: PASS remote concurrent upload/switch serialized: PASS bootstrap legacy rollback verifies exact digest and status: PASS failed first update preserves baseline; absent rollback refuses before stop: PASS predecessor existence/inspection errors preserve older rollback through failed health: PASS injection deploy/normal/before: 40/92 PASS injection deploy/normal/before: 80/92 PASS injection deploy/normal/after: 40/92 PASS injection deploy/normal/after: 80/92 PASS state machine deploy/normal: 92 steps, before/after failures PASS persistent disk deploy/normal/before: 40/92 PASS persistent disk deploy/normal/before: 80/92 PASS persistent disk deploy/normal/after: 40/92 PASS persistent disk deploy/normal/after: 80/92 PASS persistent full disk deploy/normal: every step before/after PASS (including recovery) state machine same-sha/normal: 34 steps, before/after failures PASS state machine same-image/normal: 34 steps, before/after failures PASS injection rollback/normal/before: 40/79 PASS injection rollback/normal/after: 40/79 PASS state machine rollback/normal: 79 steps, before/after failures PASS persistent disk rollback/normal/before: 40/79 PASS persistent disk rollback/normal/after: 40/79 PASS persistent full disk rollback/normal: every step before/after PASS (including recovery) injection first-update/normal/before: 40/91 PASS injection first-update/normal/before: 80/91 PASS injection first-update/normal/after: 40/91 PASS injection first-update/normal/after: 80/91 PASS state machine first-update/normal: 91 steps, before/after failures PASS persistent disk first-update/normal/before: 40/91 PASS persistent disk first-update/normal/before: 80/91 PASS persistent disk first-update/normal/after: 40/91 PASS persistent disk first-update/normal/after: 80/91 PASS persistent full disk first-update/normal: every step before/after PASS (including recovery) injection bootstrap-upgrade/normal/before: 40/91 PASS injection bootstrap-upgrade/normal/before: 80/91 PASS injection bootstrap-upgrade/normal/after: 40/91 PASS injection bootstrap-upgrade/normal/after: 80/91 PASS state machine bootstrap-upgrade/normal: 91 steps, before/after failures PASS persistent disk bootstrap-upgrade/normal/before: 40/91 PASS persistent disk bootstrap-upgrade/normal/before: 80/91 PASS persistent disk bootstrap-upgrade/normal/after: 40/91 PASS persistent disk bootstrap-upgrade/normal/after: 80/91 PASS persistent full disk bootstrap-upgrade/normal: every step before/after PASS (including recovery) injection bootstrap-health-fails/normal/before: 40/106 PASS injection bootstrap-health-fails/normal/before: 80/106 PASS injection bootstrap-health-fails/normal/after: 40/106 PASS injection bootstrap-health-fails/normal/after: 80/106 PASS state machine bootstrap-health-fails/normal: 106 steps, before/after failures PASS persistent disk bootstrap-health-fails/normal/before: 40/106 PASS persistent disk bootstrap-health-fails/normal/before: 80/106 PASS persistent disk bootstrap-health-fails/normal/after: 40/106 PASS persistent disk bootstrap-health-fails/normal/after: 80/106 PASS persistent full disk bootstrap-health-fails/normal: every step before/after PASS (including recovery) state machine bootstrap/normal: 8 steps, before/after failures PASS state machine bootstrap-missing-unit/normal: 5 steps, before/after failures PASS injection deploy/health-fails/before: 40/107 PASS injection deploy/health-fails/before: 80/107 PASS injection deploy/health-fails/after: 40/107 PASS injection deploy/health-fails/after: 80/107 PASS state machine deploy/health-fails: 107 steps, before/after failures PASS persistent disk deploy/health-fails/before: 40/107 PASS persistent disk deploy/health-fails/before: 80/107 PASS persistent disk deploy/health-fails/after: 40/107 PASS persistent disk deploy/health-fails/after: 80/107 PASS persistent full disk deploy/health-fails: every step before/after PASS (including recovery) injection rollback/health-fails/before: 40/94 PASS injection rollback/health-fails/before: 80/94 PASS injection rollback/health-fails/after: 40/94 PASS injection rollback/health-fails/after: 80/94 PASS state machine rollback/health-fails: 94 steps, before/after failures PASS persistent disk rollback/health-fails/before: 40/94 PASS persistent disk rollback/health-fails/before: 80/94 PASS persistent disk rollback/health-fails/after: 40/94 PASS persistent disk rollback/health-fails/after: 80/94 PASS persistent full disk rollback/health-fails: every step before/after PASS (including recovery) injection deploy/stale-primary-recovery/before: 40/105 PASS injection deploy/stale-primary-recovery/before: 80/105 PASS injection deploy/stale-primary-recovery/after: 40/105 PASS injection deploy/stale-primary-recovery/after: 80/105 PASS state machine deploy/stale-primary-recovery: 105 steps, before/after failures PASS persistent disk deploy/stale-primary-recovery/before: 40/105 PASS persistent disk deploy/stale-primary-recovery/before: 80/105 PASS persistent disk deploy/stale-primary-recovery/after: 40/105 PASS persistent disk deploy/stale-primary-recovery/after: 80/105 PASS persistent full disk deploy/stale-primary-recovery: every step before/after PASS (including recovery) injection rollback/stale-primary-recovery/before: 40/92 PASS injection rollback/stale-primary-recovery/before: 80/92 PASS injection rollback/stale-primary-recovery/after: 40/92 PASS injection rollback/stale-primary-recovery/after: 80/92 PASS state machine rollback/stale-primary-recovery: 92 steps, before/after failures PASS persistent disk rollback/stale-primary-recovery/before: 40/92 PASS persistent disk rollback/stale-primary-recovery/before: 80/92 PASS persistent disk rollback/stale-primary-recovery/after: 40/92 PASS persistent disk rollback/stale-primary-recovery/after: 80/92 PASS persistent full disk rollback/stale-primary-recovery: every step before/after PASS (including recovery) injection deploy/post-start-unit-rename/before: 40/102 PASS injection deploy/post-start-unit-rename/before: 80/102 PASS injection deploy/post-start-unit-rename/after: 40/102 PASS injection deploy/post-start-unit-rename/after: 80/102 PASS state machine deploy/post-start-unit-rename: 102 steps, before/after failures PASS persistent disk deploy/post-start-unit-rename/before: 40/102 PASS persistent disk deploy/post-start-unit-rename/before: 80/102 PASS persistent disk deploy/post-start-unit-rename/after: 40/102 PASS persistent disk deploy/post-start-unit-rename/after: 80/102 PASS persistent full disk deploy/post-start-unit-rename: every step before/after PASS (including recovery) injection rollback/post-start-unit-rename/before: 40/89 PASS injection rollback/post-start-unit-rename/before: 80/89 PASS injection rollback/post-start-unit-rename/after: 40/89 PASS injection rollback/post-start-unit-rename/after: 80/89 PASS state machine rollback/post-start-unit-rename: 89 steps, before/after failures PASS persistent disk rollback/post-start-unit-rename/before: 40/89 PASS persistent disk rollback/post-start-unit-rename/before: 80/89 PASS persistent disk rollback/post-start-unit-rename/after: 40/89 PASS persistent disk rollback/post-start-unit-rename/after: 80/89 PASS persistent full disk rollback/post-start-unit-rename: every step before/after PASS (including recovery) exhaustive failure injection: PASS (4354 cases; userdata unchanged) relay success: PASS relay save-fails: PASS relay target-fails: PASS relay bootstrap: PASS non-dev revision and mutable tags rejected for next/cloud: PASS data rollback options rejected: PASS build normal: PASS build dev-moved: PASS build build-fails: PASS build bad-id: PASS build wrong-label: PASS build save-fails: PASS build export-wrong-label: PASS build multiple-images: PASS build root-owned-iid: PASS deploy and code rollback dry-run without transport (next/cloud): PASS deploy regression gates: PASS (local mocks; no production contact) ```
Author
Owner

The simpler deploy design is ready in PR #765 — Plan: simpler production deploy (#684), into dev from docs/deploy-plan.

The three-page document covers the single commit point, recovery before pre-health, the failure matrix and all seven rounds of findings, unlabelled bootstrap, precise state rollback, operator commands, acceptance/rehearsal tests, deletion estimates and a five-risk second pass. Failed trials restore stopped app state; later rollback preserves acknowledged edits. It uses 768dbacb and the reviewer sources as reference only.

Documentation only. Whitespace/coverage checks and the three-page print check pass; no builds, deploys, staging mutation or production contact.

The simpler deploy design is ready in [PR #765 — Plan: simpler production deploy (#684)](https://git.kayg.org/kayg/calternal/pulls/765), into dev from docs/deploy-plan. The three-page document covers the single commit point, recovery before pre-health, the failure matrix and all seven rounds of findings, unlabelled bootstrap, precise state rollback, operator commands, acceptance/rehearsal tests, deletion estimates and a five-risk second pass. Failed trials restore stopped app state; later rollback preserves acknowledged edits. It uses 768dbacb and the reviewer sources as reference only. Documentation only. Whitespace/coverage checks and the three-page print check pass; no builds, deploys, staging mutation or production contact.
Author
Owner

Closing per #931: deploy now uses the simple script in #786.

Closing per #931: deploy now uses the simple script in #786.
kayg closed this pull request 2026-11-04 17:37:43 +00:00
Some checks failed
CI / server (pull_request) Successful in 1m17s
CI / rust (pull_request) Successful in 17m44s
CI / web (pull_request) Has been cancelled

Pull request closed

Sign in to join this conversation.
No reviewers
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
kayg/calternal!748
No description provided.