Skip to content
This repository was archived by the owner on Jul 24, 2026. It is now read-only.

feat(restart): convoy restart — the SAFE restart (stop the host, agents survive, re-adopt) - #107

Draft
myobie wants to merge 2 commits into
mainfrom
safe-restart
Draft

feat(restart): convoy restart — the SAFE restart (stop the host, agents survive, re-adopt)#107
myobie wants to merge 2 commits into
mainfrom
safe-restart

Conversation

@myobie

@myobie myobie commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

What & why

Part 3 of 4 of the decoupling-hardening task. convoy restart exists so nobody reaches for convoy down + convoy up to restart a live network — down kills every agent (it is the only teardown), which is the mass-outage footgun that started the 2026-07-22 incident.

restart:

  1. STOPS the running host PROCESS with SIGTERM — agents keep running (the Nomad decoupling: up's handler just sets stop, and its exit path leaves every session up; proven by test(up): the DECISIVE decoupling test — killing convoy up must not kill its agents #105).
  2. Waits for it to release the host lock (so a fresh up never refuses or double-hosts).
  3. Becomes a fresh convoy up that RE-ADOPTS the still-running agents (reconcile skips live ones) and, via the fresh-supervisor un-park (fix(up): a fresh foreground convoy up UN-PARKS its members — restore the FULL fleet after an outage #106), restores any parked members.

With no host running, it simply starts one.

Changes

  • up.ts: restart() + RestartOptions.
  • cli.ts: cmdRestart + dispatch + a help line ("use this, never down + up").
  • command-table.ts: the restart entry — keeps completions + dispatch in parity (the nix parity check).
  • Test: a process-level proof — a real agent supervised by host A survives a real convoy restart at the same pid while the restart process takes over as the new host; plus the no-host-running case.

Full suite green.

Stacking: this branches on #106 (the fresh-supervisor un-park), since restart's re-adopt reuses that up() path. Until #106 merges, this PR shows both commits; after, only the restart commit. Reviewing/merging #106 first keeps the diff clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_014gbfntB6cu21sL4YBp21LF

myobie and others added 2 commits July 22, 2026 02:24
…e the FULL fleet after an outage

Parking-recovery for the 2026-07-22 incident: a supervisor bring-up after a mass outage brought
back only part of the fleet; the rest stayed PARKED from a prior supervisor's give-up and had to
be hand-launched.

Root cause: `strategy.status=flapping` + the fast-fail counter PERSIST to a session's tags (the
on-disk supervision contract), so they outlive the supervisor that wrote them. An outage drives the
cap to its limit → the agents park → and a fresh `convoy up`, reading those stale tags, hits
classify's `isFlapping → skip` and never relaunches them.

Fix: a foreground `convoy up` is a DELIBERATE bring-up — the operator gesture that says "restore the
fleet" — so at startup it clears the park AND zeroes the fast-fail counter for permanent members
(regardless of prior fail count), giving each a fresh cap budget. The cap still re-accrues
tick-to-tick WITHIN this supervisor's watch (the real crash-loop protection). The `--once` shepherd
cron does NOT un-park: it runs every few minutes, so un-parking there would relaunch a genuinely
broken agent every tick — parking must stay durable for it.

- flapping-cap.ts: `clearParkForFreshSupervisor` (pure) — the reset decision, unit-tested.
- up.ts: the FRESH-SUPERVISOR UN-PARK startup pass (foreground only), which clears the on-disk park
  (removes the status tag — updateTags MERGES) and seeds the in-memory classifier state.
- Tests: pure cases for the reset + a process-level proof (a parked, gone-but-recorded agent is
  UN-PARKED and RELAUNCHED by a fresh foreground up, while `--once` leaves it parked).

Part 2 of 4 of the decoupling-hardening task.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014gbfntB6cu21sL4YBp21LF
…ents survive, re-adopt)

Part 3 of 4 of the decoupling-hardening task. `convoy restart` exists so nobody reaches for
`convoy down` + `convoy up` to restart a live network — `down` KILLS every agent (it is the only
teardown), which is the mass-outage footgun that started the 2026-07-22 incident.

`restart` instead STOPS the running host PROCESS with SIGTERM (agents keep running — the Nomad
decoupling: up's handler just sets `stop`, and its exit path leaves every session up), waits for it
to release the host lock (so a fresh up never refuses or double-hosts), then becomes a fresh
`convoy up` that RE-ADOPTS the still-running agents (reconcile skips live ones) and, via the
fresh-supervisor un-park, restores any parked members. With no host running it simply starts one.

- up.ts: `restart()` + `RestartOptions`.
- cli.ts: `cmdRestart` + dispatch + help line ("use this, never `down` + `up`").
- command-table.ts: the `restart` entry (keeps completions + dispatch in parity).
- Test: a process-level proof — a real agent supervised by host A survives a real `convoy restart`
  at the SAME pid while the restart process takes over as the new host; plus the no-host-running case.

Stacks on #106 (the fresh-supervisor un-park) since restart's re-adopt reuses that up() path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014gbfntB6cu21sL4YBp21LF
@myobie
myobie marked this pull request as draft July 22, 2026 00:36
@myobie

myobie commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator Author

On hold per CoS: parts 3 & 4 (convoy hardening) are paused pending Nathan's morning call on swap-to-st2-and-retire-convoy vs keep-hardening. This was already built + green before the hold landed; marking draft so it's not requesting review. Reopen/merge only if the decision is to keep hardening convoy. (Stacks on #106.)

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant