# `carlos run` Implementation Plans: Index

> **For agentic workers:** this file is the map. Each plan it names is a
> self-contained implementation plan with the superpowers header; execute
> them in the order below with superpowers:subagent-driven-development or
> superpowers:executing-plans. Do not start a plan whose dependencies are
> not merged on `main`.

**Goal:** Build `carlos run` (amadan carlos/platform #259): a member runs one
of their app's commands against one platform-run instance, as that instance,
with its output streamed back through the console and its record kept.

**Spec:** `docs/superpowers/specs/2026-10-08-carlos-run-design.md` rev 4
(Astra-accepted in `CODEX-FINDINGS-RUN-AND-SEEDED-R4.md`, approved by Paul).
Every accepted finding in its §11 to §13 is a requirement; the plans name
the finding beside the task that answers it. The failure modes behind them
are in `CODEX-FINDINGS-RUN-AND-SEEDED-R1.md` to `R4.md`.

**Built against:** `main` at 4a51a6da. Every name the plans cite was read
there (file:line given where it matters).

**Copy:** `2026-10-08-carlos-run-copy.md` is the deck: every user-facing
string of the series, 83 rows, all pending. Plan 1 Task 1 puts the gate in
place and gets Paul's review before any other task writes a string.

## The series

| # | plan | produces | depends on |
|---|---|---|---|
| 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` |
| 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) |
| 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 |
| 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 |

Plans 1 and 2 have no dependency on each other's code, but Plan 2 uses
approved copy, so it starts after Plan 1 Task 1 is merged. Plan 4's code
needs only Plan 2's console; it can be built beside Plan 3.

```
main (4a51a6da) ─► Plan 1 ─► Plan 2 ─► Plan 3 ─► Plan 4 (dev-box checks)
                     │          └──────────────► Plan 4 (code)
                     └─ Task 1: copy review (Paul) gates every string
```

## Where the code differs from the spec, and what the plans do

Each of these is decided here once; the plans follow it.

1. **No "deadline-sweep loop" exists in the console** (spec §5.8). The
   console's background passes are claim workers (`registerClaimWorkers`,
   `internal/console/claim_workers.go:5`, started from `cmd/console/main.go`
   at about :424 to :467). Plan 2 adds a worker, `StartRunSweep`, every 30
   seconds.
2. **Reconcile does not run at agent start** (spec §5.2 "at agent start").
   `instancesLoop` sleeps a minute before its first pass
   (`internal/host/agent.go:2816`) and `fastConvergeLoop` only seeds its
   serial at arming. Plan 3 adds a boot pass at `fastConvergeLoop`'s arming
   that reads every record once (`provision.ListAllRecords`) and acts on
   runs for rows this box owns; later passes ride reconcile's own reads
   through a `RunSeen` hook, as the spec says.
3. **The record has no caller field on `RunState`**, but refusal 7 names the
   open run's caller. Plan 2 adds `By string \`json:"by,omitempty"\``
   (additive).
4. **The wake error has no field in `result.json`.** Plan 2 adds
   `Detail string \`json:"detail,omitempty"\`` for the member-safe wake
   error (`memberSafeWakeReason`, `internal/activator/logsink.go:49`).
5. **`Signal` is the decimal signal number as a string** ("9"), so the CLI
   can exit 128 + n; the spec shows the field but not its form.
6. **A cancelled run's exit code is not in the spec's table.** The CLI
   exits 130 (the shell's Ctrl-C status) for a run that was cancelled, before
   or after launch, and for the second Ctrl-C. Docs row RUN-C75 says so.
7. **`--follow` needs `--app` and `--instance`** (spec §3 shows
   `carlos run --follow <id>` alone). Every console route is under
   `/api/cli/apps/{account}/{app}/instances/{host}/`, and a bare run id
   carries none of that; the CLI prints the full follow command when a run
   starts (RUN-C54).
8. **The console spools each file part to a temp file before the bucket
   write.** `release.PutObjectFrom` needs an `io.ReadSeeker` and a size
   (`internal/release/store.go:266`); the bytes still go only through the
   console (F8). The 64 MB cap is enforced while spooling.
9. **The instance verbs' window is 60 a minute** (`cliInstanceBurst`,
   `internal/console/authcli.go:101`), which a once-a-second poll would
   exhaust. Only the create door spends it (spec refusal 3); the follow
   routes spend a new `runs` window of 600 a minute.
10. **The existing 429 text has an em dash** (`mainGateCopy().tooMany`,
    `internal/console/apicli.go:203`), so the run door has its own
    (RUN-C15).
11. **The Phase 1 move door says its generic sentence for "a run is open".**
    `phase1Conflict` (`internal/console/apiinstancemove.go:895`) is inside
    the moves copy scan (`movesScanFiles`), so a new sentence there needs a
    moves record row. The legacy door, the one live while
    `CARLOS_MOVES_PHASE1` is off, answers the provision error, which carries
    RUN-C43. The moves series adds the Phase 1 wording when its gate task
    next runs; this series does not touch the moves record.
12. **A host-claim console refuses every run route** with RUN-C42. Boxes with
    claim control armed refuse runs anyway (spec §3.1), and the
    claim-invariant census (`claim_invariant_test.go:258`) needs every
    route to be a door, a read or a listed refusal; a refusal is the honest
    one until #250 adds `cmd` to the reservation protocol.
13. **Files are written 0600 in a 0700 directory** (spec §5.2 says "mode
    0700" for the files): they are data, not programs.
14. **The import abort door does not exist yet.** Plan 2 gives the seeded
    plan `(*RunState).Open()` and `provision.ErrRunOpen`; its abort
    transition must refuse with them (spec §5.1 "an import's abort is
    refused while ActiveRun is non-terminal"). `SeedSource` here has `Kind`
    and `State` only, with the seeded spec's json tags; the seeded plan
    extends it.

## Handed to other work

- **#250 (claim activation):** add `cmd` to the reservation protocol beside
  `main`, `seed` and `sidecar`, settle the cmd unit's producer before a
  generation is published, and lift the RUN-C42 refusal and the box's
  `refused: "claim"`. Post a note on #250 when Plan 1 merges.
- **The seeded-instances plan:** its record plan (being written beside this
  series) defines the full `SeedSource`; whichever lands second extends the
  other rather than redefining it (Plan 2 Task 1 says how). Extend
  `provision.SeedSource`; make the
  import abort transition refuse with `provision.ErrRunOpen`; keep
  `(*Record).ImportUncommitted()`'s meaning (an import whose state is not
  `committed`).
- **The moves series:** the Phase 1 door's wording for a run that is open
  (item 11).
