carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1# `carlos run` Implementation Plans: Index
2
3> **For agentic workers:** this file is the map. Each plan it names is a
4> self-contained implementation plan with the superpowers header; execute
5> them in the order below with superpowers:subagent-driven-development or
6> superpowers:executing-plans. Do not start a plan whose dependencies are
7> not merged on `main`.
8
9**Goal:** Build `carlos run` (amadan carlos/platform #259): a member runs one
10of their app's commands against one platform-run instance, as that instance,
11with its output streamed back through the console and its record kept.
12
13**Spec:** `docs/superpowers/specs/2026-10-08-carlos-run-design.md` rev 4
14(Astra-accepted in `CODEX-FINDINGS-RUN-AND-SEEDED-R4.md`, approved by Paul).
15Every accepted finding in its §11 to §13 is a requirement; the plans name
16the finding beside the task that answers it. The failure modes behind them
17are in `CODEX-FINDINGS-RUN-AND-SEEDED-R1.md` to `R4.md`.
18
19**Built against:** `main` at 4a51a6da. Every name the plans cite was read
20there (file:line given where it matters).
21
22**Copy:** `2026-10-08-carlos-run-copy.md` is the deck: every user-facing
23string of the series, 83 rows, all pending. Plan 1 Task 1 puts the gate in
24place and gets Paul's review before any other task writes a string.
25
26## The series
27
28| # | plan | produces | depends on |
29|---|---|---|---|
30| 1 | [`2026-10-08-carlos-run-1-box-foundation.md`](2026-10-08-carlos-run-1-box-foundation.md) | the copy gate and Paul's review; `instancectl.RoleCmd` and `carlos-instance-cmd@.service` (equal to the seed template outside the named lines); `POST /cmd` and the `GET /cmd/{host}` long-poll; `instancefs.RemoveAllBeneath`; `cmdVars`, `launch.cmd.json`, the two stream listeners and `ExecBackend.StartCmd`; stop reasons on every stop path; the activator entry's `cmd` field, `Service.RunCmd`, the stop ordering in `finishStop` and the idle hold | `main` |
31| 2 | [`2026-10-08-carlos-run-2-record-and-console.md`](2026-10-08-carlos-run-2-record-and-console.md) | `provision.RunState` and `Record.ActiveRun` (carried by ordinary writers), the minimal `SeedSource`, the run transitions and the move transitions' refusal; `internal/runs` (ids, keys, wire shapes, validation, reasons); the console's five run routes in spec order, the run-end audit, the run sweep worker; the claim-invariant fixtures | Plan 1 merged (Task 1's gate and approved copy) |
32| 3 | [`2026-10-08-carlos-run-3-box-pickup-and-output.md`](2026-10-08-carlos-run-3-box-pickup-and-output.md) | `runs.Chunker` and `runs.StageFiles`; reconcile's `RunSeen` hook; the box's runs worker: pickup CAS, incarnation and import checks, agent-restart recovery, the boot sweep of `.carlos-run`; one run's life from wake to `result.json` and the `done` CAS; the agent wiring | Plans 1 and 2 merged |
33| 4 | [`2026-10-08-carlos-run-4-cli-and-docs.md`](2026-10-08-carlos-run-4-cli-and-docs.md) | `carlos run` and `--follow`, interleaving, the 60-second expiry, the ack, every exit code, Ctrl-C once and twice; `docs/site/cli.md`'s `carlos run` section and the contract section; the closing copy check; the dev-box checklist | Plan 2 merged for code; Plan 3 merged before the dev-box checks |
34
35Plans 1 and 2 have no dependency on each other's code, but Plan 2 uses
36approved copy, so it starts after Plan 1 Task 1 is merged. Plan 4's code
37needs only Plan 2's console; it can be built beside Plan 3.
38
39```
40main (4a51a6da) ─► Plan 1 ─► Plan 2 ─► Plan 3 ─► Plan 4 (dev-box checks)
41 │ └──────────────► Plan 4 (code)
42 └─ Task 1: copy review (Paul) gates every string
43```
44
45## Where the code differs from the spec, and what the plans do
46
47Each of these is decided here once; the plans follow it.
48
491. **No "deadline-sweep loop" exists in the console** (spec §5.8). The
50 console's background passes are claim workers (`registerClaimWorkers`,
51 `internal/console/claim_workers.go:5`, started from `cmd/console/main.go`
52 at about :424 to :467). Plan 2 adds a worker, `StartRunSweep`, every 30
53 seconds.
542. **Reconcile does not run at agent start** (spec §5.2 "at agent start").
55 `instancesLoop` sleeps a minute before its first pass
56 (`internal/host/agent.go:2816`) and `fastConvergeLoop` only seeds its
57 serial at arming. Plan 3 adds a boot pass at `fastConvergeLoop`'s arming
58 that reads every record once (`provision.ListAllRecords`) and acts on
59 runs for rows this box owns; later passes ride reconcile's own reads
60 through a `RunSeen` hook, as the spec says.
613. **The record has no caller field on `RunState`**, but refusal 7 names the
62 open run's caller. Plan 2 adds `By string \`json:"by,omitempty"\``
63 (additive).
644. **The wake error has no field in `result.json`.** Plan 2 adds
65 `Detail string \`json:"detail,omitempty"\`` for the member-safe wake
66 error (`memberSafeWakeReason`, `internal/activator/logsink.go:49`).
675. **`Signal` is the decimal signal number as a string** ("9"), so the CLI
68 can exit 128 + n; the spec shows the field but not its form.
696. **A cancelled run's exit code is not in the spec's table.** The CLI
70 exits 130 (the shell's Ctrl-C status) for a run that was cancelled, before
71 or after launch, and for the second Ctrl-C. Docs row RUN-C75 says so.
727. **`--follow` needs `--app` and `--instance`** (spec §3 shows
73 `carlos run --follow <id>` alone). Every console route is under
74 `/api/cli/apps/{account}/{app}/instances/{host}/`, and a bare run id
75 carries none of that; the CLI prints the full follow command when a run
76 starts (RUN-C54).
778. **The console spools each file part to a temp file before the bucket
78 write.** `release.PutObjectFrom` needs an `io.ReadSeeker` and a size
79 (`internal/release/store.go:266`); the bytes still go only through the
80 console (F8). The 64 MB cap is enforced while spooling.
819. **The instance verbs' window is 60 a minute** (`cliInstanceBurst`,
82 `internal/console/authcli.go:101`), which a once-a-second poll would
83 exhaust. Only the create door spends it (spec refusal 3); the follow
84 routes spend a new `runs` window of 600 a minute.
8510. **The existing 429 text has an em dash** (`mainGateCopy().tooMany`,
86 `internal/console/apicli.go:203`), so the run door has its own
87 (RUN-C15).
8811. **The Phase 1 move door says its generic sentence for "a run is open".**
89 `phase1Conflict` (`internal/console/apiinstancemove.go:895`) is inside
90 the moves copy scan (`movesScanFiles`), so a new sentence there needs a
91 moves record row. The legacy door, the one live while
92 `CARLOS_MOVES_PHASE1` is off, answers the provision error, which carries
93 RUN-C43. The moves series adds the Phase 1 wording when its gate task
94 next runs; this series does not touch the moves record.
9512. **A host-claim console refuses every run route** with RUN-C42. Boxes with
96 claim control armed refuse runs anyway (spec §3.1), and the
97 claim-invariant census (`claim_invariant_test.go:258`) needs every
98 route to be a door, a read or a listed refusal; a refusal is the honest
99 one until #250 adds `cmd` to the reservation protocol.
10013. **Files are written 0600 in a 0700 directory** (spec §5.2 says "mode
101 0700" for the files): they are data, not programs.
10214. **The import abort door does not exist yet.** Plan 2 gives the seeded
103 plan `(*RunState).Open()` and `provision.ErrRunOpen`; its abort
104 transition must refuse with them (spec §5.1 "an import's abort is
105 refused while ActiveRun is non-terminal"). `SeedSource` here has `Kind`
106 and `State` only, with the seeded spec's json tags; the seeded plan
107 extends it.
108
109## Handed to other work
110
111- **#250 (claim activation):** add `cmd` to the reservation protocol beside
112 `main`, `seed` and `sidecar`, settle the cmd unit's producer before a
113 generation is published, and lift the RUN-C42 refusal and the box's
114 `refused: "claim"`. Post a note on #250 when Plan 1 merges.
115- **The seeded-instances plan:** its record plan (being written beside this
116 series) defines the full `SeedSource`; whichever lands second extends the
117 other rather than redefining it (Plan 2 Task 1 says how). Extend
118 `provision.SeedSource`; make the
119 import abort transition refuse with `provision.ErrRunOpen`; keep
120 `(*Record).ImportUncommitted()`'s meaning (an import whose state is not
121 `committed`).
122- **The moves series:** the Phase 1 door's wording for a run that is open
123 (item 11).
124