The host delivers its own successor, and versions live side by side #46

Merged
mesh-admin merged 1 commits from feat/the-host-delivers-its-own-successor into main 2026-09-28 22:29:57 +00:00
Contributor

novox/hq ADR 0141, answering issue 142. This is the host's half; the delivery half — a module
carrying the next host — follows. Until that exists nothing delivers a version, every machine takes
the fallback, and behaviour is exactly what it is today.

The supervision was already correct: a clean exit means the host stood aside, and the launcher's
next turn runs whatever is on disk. Two things made all of it dead code. Nothing told the running
host a successor was waiting — Replaced() was called by its tests and nothing else. And the
rollback resolved its known-good version through pacman -U out of the package cache, which no
machine in this mesh uses, and which two of the three operating systems ADR 0005 builds separate
binaries for do not have.

That the record keeps a version rather than a path was the clue: keeping a version is only useful to
something that can choose between versions on the machine.

  • The launcher picks a version every time round the loop, never once — standing aside is a clean
    exit, and the next turn must run what is on disk now. Resolved once, the same binary would
    restart for ever and no upgrade would take.
  • Newest is when it arrived, never how the name sorts. "1.10" orders before "1.9"; ordering by
    name would start an older host and call it an upgrade.
  • A pin from a rollback beats the newest, or the rollback would start the binary it just
    rejected and flap. A pin naming an absent version is ignored rather than fatal.
  • A directory with no binary in it is not a version. An interrupted delivery leaves one, and
    treating it as the newest means running nothing.
  • The host stands aside between reconciles and nowhere else, and returns nil rather than the
    cancelled context's error — otherwise the launcher counts the upgrade as a crash and rolls the new
    host back before it has run once.
  • A completed reconcile retires what is older than the predecessor, keeping the predecessor
    because that is exactly what a rollback starts, and never the running version — a host that
    deleted its own image would run until it stopped and then be unstartable.
  • Rollback pins the predecessor instead of reinstalling a package. No package manager, no cache
    anyone else may clean, identical on every operating system.
  • The report says which host version produced it, without which nothing can tell a machine is
    behind.
  • Nothing delivered falls back to the host placed by hand, which is how every first host arrives
    — and how all four machines here run today.

Tests: six on version selection and retirement in Go; the rollback suite rewritten with no package
manager to stub, because the thing under test is the filesystem and a fake would only assert that
the fake behaves as expected (ADR 0017); five new launcher cases. Proved by reverting pick_host to
the old behaviour once — exactly the four selection tests fail and the rest pass. make check green.

novox/hq ADR 0141, answering issue 142. This is the host's half; the delivery half — a module carrying the next host — follows. Until that exists nothing delivers a version, every machine takes the fallback, and behaviour is exactly what it is today. The supervision was already correct: a clean exit means the host stood aside, and the launcher's next turn runs whatever is on disk. Two things made all of it dead code. Nothing told the running host a successor was waiting — `Replaced()` was called by its tests and nothing else. And the rollback resolved its known-good **version** through `pacman -U` out of the package cache, which no machine in this mesh uses, and which two of the three operating systems ADR 0005 builds separate binaries for do not have. That the record keeps a version rather than a path was the clue: keeping a version is only useful to something that can choose between versions on the machine. - **The launcher picks a version every time round the loop**, never once — standing aside is a clean exit, and the next turn must run what is on disk *now*. Resolved once, the same binary would restart for ever and no upgrade would take. - **Newest is when it arrived, never how the name sorts.** "1.10" orders before "1.9"; ordering by name would start an older host and call it an upgrade. - **A pin from a rollback beats the newest**, or the rollback would start the binary it just rejected and flap. A pin naming an absent version is ignored rather than fatal. - **A directory with no binary in it is not a version.** An interrupted delivery leaves one, and treating it as the newest means running nothing. - **The host stands aside between reconciles and nowhere else**, and returns nil rather than the cancelled context's error — otherwise the launcher counts the upgrade as a crash and rolls the new host back before it has run once. - **A completed reconcile retires what is older than the predecessor**, keeping the predecessor because that is exactly what a rollback starts, and never the running version — a host that deleted its own image would run until it stopped and then be unstartable. - **Rollback pins the predecessor** instead of reinstalling a package. No package manager, no cache anyone else may clean, identical on every operating system. - **The report says which host version produced it**, without which nothing can tell a machine is behind. - **Nothing delivered falls back to the host placed by hand**, which is how every first host arrives — and how all four machines here run today. Tests: six on version selection and retirement in Go; the rollback suite rewritten with no package manager to stub, because the thing under test is the filesystem and a fake would only assert that the fake behaves as expected (ADR 0017); five new launcher cases. Proved by reverting `pick_host` to the old behaviour once — exactly the four selection tests fail and the rest pass. `make check` green.
mesh-admin added 1 commit 2026-09-28 22:29:56 +00:00
The supervision was already right: a clean exit means the host stood aside, and
the launcher's next turn runs what is on disk. Two things made it dead code —
nothing told the running host a successor was waiting, and the rollback resolved
its known-good version through pacman, which no machine here uses and which two
of three operating systems do not have.

Keeping a version rather than a path was the clue. Versions now live in
directories named for them:

- the launcher picks the newest delivered one every time round the loop, or the
  one a rollback pinned, or the host placed by hand when nothing is delivered;
- the running host stands aside between reconciles, never inside one, by exiting
  cleanly — and returns nil so the launcher does not count it as a crash;
- a completed reconcile retires what is older than the predecessor, keeping the
  predecessor because that is what a rollback starts, and never the running one;
- rollback pins the predecessor instead of reinstalling a package: no package
  manager, no cache anyone may clean, same script on every operating system;
- the report says which host version produced it, so 'behind' is answerable.

Newest is when it arrived, never how the name sorts: '1.10' orders before '1.9',
and ordering by name would start an older host and call it an upgrade.

novox/hq ADR 0141. The delivery half — a module carrying the next host — follows;
until then nothing delivers a version and every machine takes the fallback, which
is what it does today.
mesh-admin merged commit 0c3928ad19 into main 2026-09-28 22:29:57 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: novox/mesh-host#46