c8ctl-plugin-nano 1.64.0 → 1.65.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1060,6 +1060,8 @@ c8ctl nano supervisor add reviewer # add + spawn a worker (forw
1060
1060
  c8ctl nano supervisor add reviewer --name reviewer-2 # a SECOND reviewer, named so it stays distinct
1061
1061
  c8ctl nano supervisor add reviewer --instances 3 # add 3 distinct auto-named reviewers in one call
1062
1062
  c8ctl nano supervisor restart reviewer # by worker id or profile name
1063
+ c8ctl nano supervisor reload # adopt new code fleet-wide (rolling drain+respawn, zero downtime)
1064
+ c8ctl nano supervisor reload reviewer # reload just one worker/profile
1063
1065
  c8ctl nano supervisor remove coder # stop + drop a worker (also: `all`)
1064
1066
  c8ctl nano supervisor logs reviewer --follow # tail a worker's log (or the daemon's)
1065
1067
  c8ctl nano supervisor stop # stop the daemon and every worker
@@ -1109,6 +1111,73 @@ How it works and where things live:
1109
1111
  - Stopping is SIGTERM → grace → SIGKILL, per worker and for the daemon; `stop`
1110
1112
  always clears `supervisor.json` so a stale marker never wedges a future start.
1111
1113
 
1114
+ ### Hot code reload: `supervisor reload` / `workforce reload`
1115
+
1116
+ When you update the plugin on a machine that's already running a fleet
1117
+ (`c8ctl nano update`, or otherwise replacing the installed `c8ctl-plugin-nano`),
1118
+ `supervisor reload` adopts the new code **without stopping the fleet**:
1119
+
1120
+ ```bash
1121
+ c8ctl nano update # pull the new harness onto disk
1122
+ c8ctl nano supervisor reload # roll it into the running fleet, zero downtime
1123
+ ```
1124
+
1125
+ - Each supervised worker is a **separate `nano work` process** that reads the
1126
+ plugin from disk when it starts, so the daemon adopts new code by **rolling
1127
+ through the workers one at a time** — gracefully draining each (the same
1128
+ `SIGUSR2` quiesce as `stop`: it stops leasing new jobs, finishes the ones in
1129
+ flight, and exits) and respawning it, which re-reads the updated
1130
+ `c8ctl-plugin.js`, its sidecars, and `supervisor.dist.js`. Because only one
1131
+ worker is down at a time, the rest of the fleet keeps serving — **zero fleet
1132
+ downtime**. To keep it genuinely one-at-a-time, after respawning a worker the
1133
+ daemon **waits for the replacement to report ready** (its activation loop is up
1134
+ and leasing) before draining the next one — a bare spawn/PID is not readiness,
1135
+ so on the normal readiness path a slow replacement can never leave two workers
1136
+ down at once. That wait is
1137
+ **bounded** (`NANO_SUPERVISOR_RELOAD_READY_TIMEOUT_MS`, default 30s): a
1138
+ never-ready replacement can't wedge the roll — the daemon advances anyway. That
1139
+ timeout fallback is the one exception to the guarantee above: when a
1140
+ replacement never reports ready the daemon drains the next worker while the
1141
+ previous one is still unready, so **two (or more) workers can be temporarily
1142
+ unavailable** until the slow replacement catches up. This
1143
+ is a *fleet-level* guarantee: a worker is drained **before** its replacement
1144
+ spawns, so a **single-worker fleet** (or a job type served by only one worker)
1145
+ does lose that capacity for the drain+boot window. Run more than
1146
+ one worker for a type if you need it served continuously across a reload.
1147
+ - It **never kills in-flight work**: a reload waits indefinitely for each
1148
+ worker's jobs to finish (adopting new code is never worth losing a running
1149
+ job). The command **streams progress** and **Ctrl-C detaches** — the daemon
1150
+ keeps rolling in the background (rerun `supervisor status` to check). A reload
1151
+ is refused while another is already in progress.
1152
+ - It **stops the roll on a failed reload** (a canary): if a worker's replacement
1153
+ crashes, fails to spawn, or is swapped out from under the roll — i.e. it has no
1154
+ confirmed serving child — the daemon aborts the remaining pass rather than drain
1155
+ the next worker on top of that gap (which would both break one-at-a-time and
1156
+ risk rolling a broken replacement across the whole fleet). The remaining workers
1157
+ are reported **skipped** and the terminal frame reports a partial failure
1158
+ (`ok:false`), so automation sees it. A readiness *timeout* on a still-live
1159
+ replacement is **not** a failure — it counts as reloaded and the roll continues.
1160
+ - `reload [target]` defaults to the whole fleet; pass a worker id or profile to
1161
+ reload just those. `workforce reload` rolls only the workers a manifest owns.
1162
+ - **Not supported on Windows.** The graceful drain relies on `SIGUSR2` to quiesce
1163
+ each worker, which Windows cannot deliver (Node maps a non-zero signal there to
1164
+ a forceful, SIGKILL-like termination, so the child's drain handler never fires).
1165
+ The daemon therefore **rejects `supervisor reload` / `workforce reload` on
1166
+ Windows** rather than hang or hard-kill in-flight work — use
1167
+ `supervisor restart <target>`, or a full `supervisor stop` + `start`, to adopt
1168
+ new code there.
1169
+ - **Scope — workers, not the daemon.** A reload adopts all **worker-side** code
1170
+ (job running, agent instances, git/container provisioning, the agentic
1171
+ connection, the Effect runtime workers load — the bulk of the harness). The
1172
+ supervisor **daemon** keeps running the code it started with: its workers are
1173
+ its children and watch its pid, so re-exec'ing the daemon would take the fleet
1174
+ down with it. To adopt new **supervisor** code, do a full restart
1175
+ (`c8ctl nano supervisor stop && c8ctl nano supervisor start`) — a rare event,
1176
+ since the daemon is a thin process manager. `supervisor status` shows an
1177
+ `on disk:` line flagging "update available" whenever the code on disk has
1178
+ advanced past the running daemon, so you know when a reload (or restart) is
1179
+ worthwhile.
1180
+
1112
1181
  ### Surviving SSH logout: `supervisor install` / `uninstall`
1113
1182
 
1114
1183
  On **macOS**, a supervisor started over SSH is bound to your SSH login session's
@@ -1216,6 +1285,7 @@ c8ctl nano workforce list # print the manifest (+ --json)
1216
1285
  c8ctl nano workforce start # ensure the daemon is up, then reconcile
1217
1286
  c8ctl nano workforce status # desired vs actual, per worker (+ --json)
1218
1287
  c8ctl nano workforce stop # remove this manifest's workers (+ stop an empty daemon)
1288
+ c8ctl nano workforce reload # hot-adopt new code into this manifest's workers (rolling, zero downtime)
1219
1289
  c8ctl nano workforce remove qwen # drop an entry ("all" clears the manifest)
1220
1290
  ```
1221
1291