Skip to content

fix: a replacement supervisor keeps the pipe it was serving - #446

Merged
zcsizmadia merged 1 commit into
mainfrom
fix/preserve-custom-pipe
Sep 20, 2026
Merged

zcsizmadia merged 1 commit into
mainfrom
fix/preserve-custom-pipe

Conversation

@zcsizmadia

Copy link
Copy Markdown
Collaborator

Closes #429. This is the last thing making the nightly acceptance run red, so a red nightly means something new again.

skrog restart --supervisor rebuilds the supervisor's arguments rather than forwarding them, and the served pipe was not among what it carried. A supervisor started with --pipe <custom> came back on the default, DOCKER_HOST stopped working, and the error named a missing file rather than a moved pipe. The watchdog path had the same gap — skrogw relaunches through spawnSupervisor too, so a crash lost the pipe the same way.

spawnSupervisor is the single choke point for both, so the fix lives there and needs no new CLI surface.

Only a custom pipe is preserved, and the exclusions are the fix

  • The default is not pinned. Normal selection takes it again when free and falls back correctly when Docker Desktop has it. Pinning would turn that graceful fallback into a hard failure to bind.
  • The fallback is not pinned either, because selection re-derives it — it is only ever chosen when the default is taken. Pinning would make the fallback sticky, so a machine that stopped running Desktop would never take the default pipe back, and "plain docker just works" is what the default pipe buys.

What is left is a pipe someone asked for by name — exactly what was being lost. Ordinary installs see no change at all.

Two mistakes worth recording

The comparison was dead. The first version trimmed \\.\pipe\ off the recorded value and compared it against DefaultPipeName — which already contains that prefix. Nothing ever matched, so both exclusions were inert and the fallback would have become sticky. The table test caught it; pipeEq now normalises both sides.

The helper was tested, not the wiring. The --pipe append started out inline in spawnSupervisor, and TestCustomPipeToPreserve passed with that append deleted — a correct helper that nothing called, which is the shape of three other defects in this release. The argument list is now built by supervisorCommand and asserted directly.

Negative control, wiring removed:

args [supervise --state-dir ...] carry no --pipe; the replacement supervisor
would re-select and DOCKER_HOST would stop working

The helper-only test still passes in that state, which is precisely why it is not the one that matters.

Closes #429. This is the last thing making the nightly acceptance run
red, so a red nightly means something new again.

`skrog restart --supervisor` rebuilds the supervisor's arguments rather
than forwarding them, and the served pipe was not among what it
carried. A supervisor started with `--pipe <custom>` came back on the
default, DOCKER_HOST stopped working, and the error named a missing
FILE rather than a moved pipe. The watchdog path had the same gap:
skrogw relaunches through spawnSupervisor too, so a crash lost the pipe
the same way.

spawnSupervisor is the single choke point for both, so the fix lives
there and needs no new CLI surface.

Only a CUSTOM pipe is preserved, and the two exclusions are the fix
------------------------------------------------------------------
  - the DEFAULT is not pinned. Normal selection takes it again when
    free and falls back correctly when Docker Desktop has taken it;
    pinning would turn that graceful fallback into a hard failure to
    bind.
  - the FALLBACK is not pinned either, because selection re-derives it
    -- it is only ever chosen when the default is taken. Pinning would
    make the fallback STICKY, so a machine that stopped running Desktop
    would never take the default pipe back, and "plain docker just
    works" is the thing the default pipe buys.

What is left is a pipe someone asked for by name, which is exactly what
was being lost. Ordinary installs see no change at all.

Two mistakes worth recording
-----------------------------
The first comparison trimmed `\\.\pipe\` off the recorded value and
compared it against DefaultPipeName -- which ALREADY CONTAINS that
prefix. Nothing ever matched, so both exclusions were dead and the
fallback would have become sticky. The table test caught it; pipeEq now
normalises both sides.

The second is the one this release keeps finding. The --pipe append
started out inline in spawnSupervisor, and TestCustomPipeToPreserve
passed with that append deleted: a correct helper that nothing called.
So the argument list itself is now built by supervisorCommand and
asserted directly. Verified as a negative control -- with the wiring
removed:

	args [supervise --state-dir ...] carry no --pipe; the replacement
	supervisor would re-select and DOCKER_HOST would stop working

The helper-only test still passes in that state, which is precisely why
it is not the one that matters.
@zcsizmadia
zcsizmadia merged commit f83660d into main Sep 20, 2026
5 checks passed
@zcsizmadia
zcsizmadia deleted the fix/preserve-custom-pipe branch September 20, 2026 13:58
zcsizmadia added a commit that referenced this pull request Sep 20, 2026
)

Reopens and actually fixes #429. #446 did not, and the nightly said so.

#446 had spawnSupervisor read the recorded endpoint to decide whether
to forward --pipe. But runSupervise clears that record on a clean exit:

	defer func() { supervise.ClearEndpoint(opts.StateDir) }()

and `restart --supervisor` exits the old supervisor CLEANLY before
spawning the replacement. So the sequence was:

  1. old supervisor exits -> endpoint record deleted
  2. spawnSupervisor -> ReadEndpoint -> nothing
  3. replacement re-selects from scratch

The fix read the record after the thing that writes it had removed it.
It worked for a hard kill, where the record survives -- which is why
the test passed. That test wrote an endpoint and called
supervisorCommand directly, modelling the crash path rather than the
restart sequence: the right assertion about the wrong scenario.

runRestart now captures the pipe BEFORE recycling and carries it
through runStartPreserving to spawnSupervisor. The recorded read stays
as the fallback, because for a supervisor that died hard it is the only
thing that remembers.

runStartPreserving rather than a `--pipe` flag on `start`: this is not
something a user asks for, it is one command carrying a value across a
teardown that would otherwise erase it, and a CLI flag would document a
decision the caller never makes.

Verification
------------
TestRestartSequencePreservesTheCustomPipe walks the real ordering --
record written, captured, CLEARED, then the replacement built -- and
fails against the exact code #446 shipped:

	args [supervise --state-dir ...] carry pipe "", want
	"\\.\pipe\skrog-e2e-suite"

TestCrashRestartUsesTheRecordedPipe covers the other direction, so the
fallback cannot rot.

Not claiming this closes #429 until the acceptance suite says so. That
claim is what was wrong last time.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

restart --supervisor loses the served pipe; the replacement re-selects from scratch

1 participant