docs: carlos system, the env contract, and the roll script's last two lines
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015QCUCpfxbCyEAVQ2m51dH5
4 files changed,
+54
−0
AGENTS.md+7 −0docs/site/cli.md+26 −0docs/superpowers/plans/2026-09-06-carlos-system-update-pinfra.md+17 −0docs/superpowers/specs/2026-09-06-carlos-system-update-design.md+4 −0
diff --git a/AGENTS.md b/AGENTS.md| index f50e5801a..4b85e4bbc 100644 |
| --- a/AGENTS.md |
| +++ b/AGENTS.md |
| @@ -278,6 +278,13 @@ anything. |
| PR #10). Hibernating instances are never woken. Static routes carry |
| `X-Carlos-Version` from the same roll (#112), so `carlos deploy -kind |
| static -host <site>` verifies static sites too. |
| +- `carlos-system-update.path` relays the agent's platform self-update request |
| + to `carlos system apply` (root; spec 2026-09-06-carlos-system-update-design.md |
| + §6). `carlos-system-recover.service` runs once per boot, before the edge, |
| + and on the apply's failure. Both must be armed; the last run of |
| + `update-platform-binary.sh` does it, and the hourly `carlos adopt` pass |
| + installs and arms them if it did not. A box follows `CARLOS_SYSTEM_CHANNEL` |
| + (canary|stable, default stable) in host.env. |
| - **`carlos restart --host` / `--environment` / `--dry-run`** (discussion |
| #16): the same verb narrowed to one route, or to everything reading one |
| config bundle — the shape that follows `carlos secrets set --environment |
diff --git a/docs/site/cli.md b/docs/site/cli.md| index 3b017d0d6..9ae89d76a 100644 |
| --- a/docs/site/cli.md |
| +++ b/docs/site/cli.md |
| @@ -929,6 +929,32 @@ fixture escaped. Run it before you ever point this binary at a real bucket. |
| Neither mode publishes anything: the public status page is written by each |
| box's own agent tick. |
| +### carlos system |
| + |
| +The platform's own release channel. `update` promotes a public release (or, |
| +with `--file`, a binary you built) to `canary` or `stable`; every box on that |
| +channel fetches it through its normal store, verifies it against the signed |
| +pointer, installs it, restarts its edge, and rolls back to the previous |
| +binary if the new one does not come up and stay up. `status` shows each box's |
| +platform, channel, running build, the channel's target, and the last apply |
| +outcome with its reason. `rollback` points a channel back at the release |
| +before this one, and every box on it rolls back the same way it rolled |
| +forward. All three talk to your console; you need to be an owner of the ops |
| +account. |
| + |
| +```sh |
| +carlos system update --channel canary |
| +carlos system status --channel canary |
| +carlos system update --channel stable --spread 10m |
| +carlos system rollback --channel stable |
| +``` |
| + |
| +A box is put on canary with `CARLOS_SYSTEM_CHANNEL=canary` in its host.env. |
| +Boxes that have not reported a platform yet count as stable. `carlos system |
| +apply` is the root oneshot behind `carlos-system-update.service`; it is not |
| +for operators. The console is an app — update it with `carlos deploy --app |
| +console`. |
| + |
| ## On the host |
| These are what systemd starts. You will rarely type them, but knowing what |
diff --git a/docs/superpowers/plans/2026-09-06-carlos-system-update-pinfra.md b/docs/superpowers/plans/2026-09-06-carlos-system-update-pinfra.md| new file mode 100644 |
| index 000000000..25d87c79c |
| --- /dev/null |
| +++ b/docs/superpowers/plans/2026-09-06-carlos-system-update-pinfra.md |
| @@ -0,0 +1,17 @@ |
| +# update-platform-binary.sh: the last two lines it needs |
| + |
| +After the `carlos-restart.path` block, guarded the same way: |
| + |
| + if [ -f /etc/systemd/system/carlos-system-update.path ]; then |
| + systemctl enable --now carlos-system-update.path |
| + systemctl enable carlos-system-recover.service |
| + fi |
| + |
| +And to the post-conditions at the end: |
| + |
| + systemctl is-active carlos-system-update.path |
| + systemctl is-enabled carlos-system-recover.service |
| + |
| +Same change in amadan.net/tito/carlos-infrastructure/scripts/update-platform-binary.sh. |
| +After every deployment has been hand-rolled once onto a binary carrying the |
| +system-update spec, the script is retired (spec §10). |
diff --git a/docs/superpowers/specs/2026-09-06-carlos-system-update-design.md b/docs/superpowers/specs/2026-09-06-carlos-system-update-design.md| index 447bd811b..b44999bc4 100644 |
| --- a/docs/superpowers/specs/2026-09-06-carlos-system-update-design.md |
| +++ b/docs/superpowers/specs/2026-09-06-carlos-system-update-design.md |
| @@ -853,6 +853,10 @@ reach GitHub uses `--file`. |
| - A box that is asleep or dark during the promote rolls when it next |
| ticks. `status` shows it on the old version until then, which is the |
| truth. |
| +- A permanently failing recovery re-runs every minute (the agent re-touches |
| + the marker while its request is unanswered) and never writes |
| + `result.json`, so the fleet sees a stale result; that is the needs-a-human |
| + state, and it is fleet-invisible. |
| --- |