# `carlos run`: copy deck (approved by Paul, 2026-10-08)

Every user-facing string the `carlos run` series writes: CLI output, console
refusals, the reasons a run is interrupted, usage lines and the docs prose.
The gate is `approved_run_copy_test.go` at the repo root (Plan 1 Task 1). It
reuses the moves gate's parser, so the format is the same: one table per
heading, columns id, file, text, status.

The rules:

- A row is `pending` until Paul approves it in the copy review (the
  `copy-review` skill). His edits are applied to the text here verbatim and
  the row becomes `approved`.
- No task writes a row's text into code or docs while the row is pending.
  The task that writes it adds the id to `runCopyBuilt` in
  `approved_run_copy_test.go` in the same commit; from then on the gate
  fails unless the row is `approved` and its text is in the file it names.
- In a Go file the text is one string literal, or one `fmt.Sprintf`,
  `fmt.Errorf` or `errors.New` format, equal to the row with `%s` or `%d`
  where the row has a `<placeholder>`. Never a trailing newline inside the
  literal: print with `Fprintln`. The one exception is a row whose file
  cell says "a fragment": its text is a piece of a larger literal (the
  top-level usage text), and the gate looks for it inside one.
- No em dashes. A full stop and a new sentence instead. Console and CLI
  sentences start lower case after the first word, as the moves record's do.

Placeholders: `<host>` the instance's hostname; `<app>` the app; `<id>` a
run id; `<by>` the caller's address; `<kind>` a route kind; `<fleet>` a
fleet name; `<name>` a file's base name; `<last>` and `<got>` chunk
sequence numbers; `<detail>` the box's member-safe wake error; `<reason>`
one of the interrupted reasons below; `<timeout>` a duration such as
`30m0s`; `<path>` a local file path; `<error>` the operating system's
error; `<url>` a console URL; `<role>` a quoted role word.

## Plan 1: box foundation

| id | file | text | status |
|---|---|---|---|
| RUN-C1 | internal/activator/cmd.go | the instance hibernated | approved |
| RUN-C2 | internal/activator/cmd.go | the instance was restarted | approved |
| RUN-C3 | internal/activator/cmd.go | the instance moved to a new release | approved |
| RUN-C4 | internal/activator/cmd.go | the instance's process exited | approved |
| RUN-C5 | internal/activator/cmd.go | the instance lost its database lease | approved |
| RUN-C6 | internal/activator/cmd.go | the instance was stopped for a move or a layout change | approved |
| RUN-C7 | internal/activator/cmd.go | the instance was taken off this box | approved |
| RUN-C8 | internal/activator/cmd.go | the box's agent stopped | approved |
| RUN-C9 | internal/instanceexec/exec.go | usage: carlos instance-exec <run|seed|sidecar|cmd> <instance-id> | approved |
| RUN-C10 | internal/instanceexec/exit.go | usage: carlos instance-exit <run|seed|sidecar|cmd> <instance-id> | approved |
| RUN-C11 | docs/site/cli.md | `ExecStart=` of the four instance templates. | approved |
| RUN-C12 | docs/site/cli.md | `ExecStopPost=` of the four instance templates. | approved |
| RUN-C13 | internal/instanceexec/exec.go | instance-exec: role <role> is not run, seed, sidecar or cmd | approved |
| RUN-C14 | internal/instanceexec/exit.go | instance-exit: role <role> is not run, seed, sidecar or cmd | approved |

## Plan 2: record and console door

| id | file | text | status |
|---|---|---|---|
| RUN-C15 | internal/console/apiruns.go | too many run requests. wait a minute and try again | approved |
| RUN-C16 | internal/console/apiruns.go | <host> is not an instance of <app> | approved |
| RUN-C17 | internal/console/apiruns.go | <host> is a <kind> route. carlos run works on instances | approved |
| RUN-C18 | internal/console/apiruns.go | <host> is served from its own address, so the platform does not run its process | approved |
| RUN-C19 | internal/console/apiruns.go | <host> does not sleep, so the platform does not run its process | approved |
| RUN-C20 | internal/console/apiruns.go | <host> is a pooled instance. carlos run works on instances with a process of their own | approved |
| RUN-C21 | internal/console/apiruns.go | <host> runs on your fleet <fleet>. carlos run works on platform boxes only | approved |
| RUN-C23 | internal/console/apiruns.go | <host> is moving. run the command once the move has finished | approved |
| RUN-C24 | internal/console/apiruns.go | <host> already has a run open: <id>, started by <by> | approved |
| RUN-C25 | internal/console/apiruns.go | name the command to run after -- | approved |
| RUN-C26 | internal/console/apiruns.go | a run takes at most 256 arguments | approved |
| RUN-C27 | internal/console/apiruns.go | the arguments come to more than 64 KB | approved |
| RUN-C28 | internal/console/apiruns.go | an argument holds a NUL byte | approved |
| RUN-C29 | internal/console/apiruns.go | the timeout must be between 1 second and 6 hours | approved |
| RUN-C30 | internal/console/apiruns.go | the files come to more than 64 MB | approved |
| RUN-C31 | internal/console/apiruns.go | <name> is not a plain file name | approved |
| RUN-C32 | internal/console/apiruns.go | two files are both named <name> | approved |
| RUN-C33 | internal/console/apiruns.go | <host> is an import that is not committed yet. finish or abort the import first | approved |
| RUN-C34 | internal/console/apiruns.go | the run request is malformed | approved |
| RUN-C35 | internal/console/apiruns.go | could not record the run. try again | approved |
| RUN-C36 | internal/console/apirunsfollow.go | <host> has no run <id> | approved |
| RUN-C37 | internal/console/apirunsfollow.go | could not read the run. try again | approved |
| RUN-C38 | internal/console/apirunsfollow.go | run <id> has not finished yet | approved |
| RUN-C39 | internal/console/apirunsfollow.go | run <id> ended at chunk <last>, not <got> | approved |
| RUN-C40 | internal/console/apirunsfollow.go | run <id> has already started | approved |
| RUN-C41 | internal/console/apirunsfollow.go | run <id> has already ended | approved |
| RUN-C42 | internal/console/apiruns.go | carlos run is not available on this console yet. it needs the host-claim launch protocol | approved |
| RUN-C43 | internal/provision/run.go | a run is open on this instance: <id>. wait for it to finish or cancel it, then move the instance | approved |
| RUN-C44 | internal/runs/runs.go | its box stopped reporting | approved |
| RUN-C45 | internal/runs/runs.go | the box's agent restarted | approved |
| RUN-C46 | internal/runs/runs.go | the command's unit went away | approved |
| RUN-C82 | internal/runs/runs.go | the box could not start the command | approved |

## Plan 4: the CLI and the docs

| id | file | text | status |
|---|---|---|---|
| RUN-C22 | run.go | carlos run: <error> | approved |
| RUN-C47 | run.go | usage: carlos run --app <app> --instance <host> [--file <path>]... [--timeout 30m] -- <arg>... | approved |
| RUN-C48 | run.go | carlos run --app <app> --instance <host> --follow <id> | approved |
| RUN-C49 | run.go | carlos run works through a console, and this shell is in direct bucket mode. unset CARLOS_DEPLOYMENT_BUCKET and CARLOS_DEPLOYMENT_DIR, then run carlos auth login --console <url> | approved |
| RUN-C50 | run.go | --app and --instance name the instance to run against | approved |
| RUN-C51 | run.go | --timeout must be between 1s and 6h | approved |
| RUN-C52 | run.go | --follow takes a run id and no command after -- | approved |
| RUN-C53 | run.go | could not read <path>: <error> | approved |
| RUN-C54 | run.go | run <id> on <host>. to follow it again: carlos run --app <app> --instance <host> --follow <id> | approved |
| RUN-C55 | run.go | <host>: not picked up within 60 seconds. the run did not start. try again | approved |
| RUN-C56 | run.go | <host>: the box is not on the nested layout yet, so carlos run cannot run there | approved |
| RUN-C57 | run.go | <host>: the box has host claims armed. carlos run cannot run there yet | approved |
| RUN-C58 | run.go | <host>: the instance did not wake. <detail> | approved |
| RUN-C59 | run.go | <host> was deleted and created again after this run was requested. the run did not start | approved |
| RUN-C60 | run.go | <host> is an import that is not committed yet. the run did not start | approved |
| RUN-C61 | run.go | <host>: the run was interrupted: <reason>. it may have done part of its work | approved |
| RUN-C62 | run.go | <host>: the command timed out after <timeout> | approved |
| RUN-C63 | run.go | <host>: the command was cancelled | approved |
| RUN-C64 | run.go | <host>: the output passed 64 MB and was cut off there. the command ran on to its end | approved |
| RUN-C65 | run.go | cancelling run <id>. press Ctrl-C again to stop waiting | approved |
| RUN-C66 | run.go | <host>: run <id> was cancelled before it started | approved |
| RUN-C67 | run.go | <host> has no run <id>, or this console has no carlos run yet | approved |
| RUN-C83 | main.go (a fragment of the usage text) | run one of your app's commands against one instance, as that instance | approved |
| RUN-C68 | docs/site/cli.md | ### carlos run | approved |
| RUN-C69 | docs/site/cli.md | Runs one command of your app against one instance, as that instance: the same build, user, directories, database and environment it serves with. Output streams back to your terminal about a second behind, and carlos exits with the command's own status. | approved |
| RUN-C70 | docs/site/cli.md | Any person who is a member of the account can run a command. A service credential cannot. | approved |
| RUN-C71 | docs/site/cli.md | carlos run --app shop --instance shop.example.com --file orders.csv -- import orders.csv | approved |
| RUN-C72 | docs/site/cli.md | Each --file is uploaded with the request and placed, under its own name, in the directory CARLOS_RUN_DIR names. Together they can come to 64 MB. The directory is deleted when the run ends. | approved |
| RUN-C73 | docs/site/cli.md | The command runs for at most 30 minutes unless you pass --timeout, up to 6 hours. Ctrl-C cancels it on the box. A second Ctrl-C stops waiting, and the cancel still happens. If your terminal goes away the run carries on, and carlos run --app <app> --instance <host> --follow <id> picks its output up again. carlos prints that command when the run starts. Output is kept until carlos has read all of it, and for 24 hours at most. | approved |
| RUN-C74 | docs/site/cli.md | A hibernating instance is woken first. A live one keeps serving, and the command opens the same database beside it. One run at a time per instance. carlos run needs the instance's box on the nested layout, and it is refused while the instance is moving. | approved |
| RUN-C75 | docs/site/cli.md | carlos run exits with the command's own status, 128 plus the signal number when a signal ended it, 124 when it timed out, 125 when it was interrupted or the instance let go of it without a result, 130 when you cancelled it, 3 when the box refused it or did not pick it up within 60 seconds, and 2 when the console refused it. | approved |
| RUN-C76 | docs/site/cli.md | Arguments are kept in the account's audit log, with who ran them. Never put a secret in an argument. Pass it in a --file, or keep it in the instance's environment. | approved |
| RUN-C77 | docs/site/cli.md | A command started with carlos run gets the same environment, without CARLOS_CONTROL_SOCKET or CARLOS_PURGE, and three variables of its own: | approved |
| RUN-C78 | docs/site/cli.md | the instance's database, the path --db names when the instance starts | approved |
| RUN-C79 | docs/site/cli.md | this run's id | approved |
| RUN-C80 | docs/site/cli.md | a directory holding the files passed with --file, under their own names. Always set, even with no files. Deleted after the run | approved |
| RUN-C81 | docs/site/cli.md | Its arguments are exactly what followed --, after your binary. The platform adds nothing. It runs in the instance's data directory. On a cancel, a timeout or an instance stop it gets SIGTERM, then SIGKILL 20 seconds later. | approved |
| RUN-C84 | run.go | not a regular file | approved |
| RUN-C85 | run.go | the file changed while carlos was reading it | approved |
| RUN-C86 | run.go | <host>: the instance no longer holds run <id>, and no result came back. it may have done part of its work | approved |
| RUN-C87 | run.go | <host>: run <id> timed out before it started | approved |
