0.25.3-rvnxx.4
Fork-only change on upstream 0.25.3. Follows 0.25.3-rvnxx.3.
Reconcile orphaned runtime containers at boot
The runtime registry is a Swoole\Table — process-local, and gone the moment the executor restarts. The containers it describes are not; they keep running. Nothing reconciled the two, so every restart orphaned that node's runtimes permanently.
Two consequences, both measured in production:
The reaper cannot see an orphan, because it walks the registry. Nodes were carrying build runtimes up to nine days old, accumulating since 2026-07-16 — container-worker-2 alone had ten.
Name collisions break every build on the node. createRuntime() hits Docker's "name already in use" for a name the registry reports as free, and the build dies with a bare Internal server error. Exactly what the rvnxx.3 rollout triggered:
build container revenexx-tag-3e5d0118… created 12:13:36 UTC, still running
executor restarted (rvnxx.3) 12:49:45 UTC
→ every subsequent app build: createRuntime failed
Eight of thirteen apps could not build. Production was never at risk — only ready builds are activated — but nothing could ship.
The fix
Maintenance::reconcileOrphans(), called from onStart() before the reaper starts. Node-local, filtered on the openruntimes-executor=<hostname> label. A listing failure logs and returns rather than blocking boot; a container that vanishes between listing and removal counts as success.
Trade-off
A restart now also drops that node's warm runtimes, so the next request to each pays a cold start. Those containers were unusable anyway — the registry no longer knew them — and warmup (PE-258 / PE-259, both deployed) re-warms opted-in apps shortly after.
Note
The first attempt injected the runner into onStart and made the executor start unhealthy — the runner depends on networks, which is registered inside that same hook. CI's E2E suite caught it. Reconciliation moved to Maintenance, which already holds the only two dependencies it needs.