| 1 | # `carlos run`: copy deck (approved by Paul, 2026-10-08) |
| 2 | |
| 3 | Every user-facing string the `carlos run` series writes: CLI output, console |
| 4 | refusals, the reasons a run is interrupted, usage lines and the docs prose. |
| 5 | The gate is `approved_run_copy_test.go` at the repo root (Plan 1 Task 1). It |
| 6 | reuses the moves gate's parser, so the format is the same: one table per |
| 7 | heading, columns id, file, text, status. |
| 8 | |
| 9 | The rules: |
| 10 | |
| 11 | - A row is `pending` until Paul approves it in the copy review (the |
| 12 | `copy-review` skill). His edits are applied to the text here verbatim and |
| 13 | the row becomes `approved`. |
| 14 | - No task writes a row's text into code or docs while the row is pending. |
| 15 | The task that writes it adds the id to `runCopyBuilt` in |
| 16 | `approved_run_copy_test.go` in the same commit; from then on the gate |
| 17 | fails unless the row is `approved` and its text is in the file it names. |
| 18 | - In a Go file the text is one string literal, or one `fmt.Sprintf`, |
| 19 | `fmt.Errorf` or `errors.New` format, equal to the row with `%s` or `%d` |
| 20 | where the row has a `<placeholder>`. Never a trailing newline inside the |
| 21 | literal: print with `Fprintln`. The one exception is a row whose file |
| 22 | cell says "a fragment": its text is a piece of a larger literal (the |
| 23 | top-level usage text), and the gate looks for it inside one. |
| 24 | - No em dashes. A full stop and a new sentence instead. Console and CLI |
| 25 | sentences start lower case after the first word, as the moves record's do. |
| 26 | |
| 27 | Placeholders: `<host>` the instance's hostname; `<app>` the app; `<id>` a |
| 28 | run id; `<by>` the caller's address; `<kind>` a route kind; `<fleet>` a |
| 29 | fleet name; `<name>` a file's base name; `<last>` and `<got>` chunk |
| 30 | sequence numbers; `<detail>` the box's member-safe wake error; `<reason>` |
| 31 | one of the interrupted reasons below; `<timeout>` a duration such as |
| 32 | `30m0s`; `<path>` a local file path; `<error>` the operating system's |
| 33 | error; `<url>` a console URL; `<role>` a quoted role word. |
| 34 | |
| 35 | ## Plan 1: box foundation |
| 36 | |
| 37 | | id | file | text | status | |
| 38 | |---|---|---|---| |
| 39 | | RUN-C1 | internal/activator/cmd.go | the instance hibernated | approved | |
| 40 | | RUN-C2 | internal/activator/cmd.go | the instance was restarted | approved | |
| 41 | | RUN-C3 | internal/activator/cmd.go | the instance moved to a new release | approved | |
| 42 | | RUN-C4 | internal/activator/cmd.go | the instance's process exited | approved | |
| 43 | | RUN-C5 | internal/activator/cmd.go | the instance lost its database lease | approved | |
| 44 | | RUN-C6 | internal/activator/cmd.go | the instance was stopped for a move or a layout change | approved | |
| 45 | | RUN-C7 | internal/activator/cmd.go | the instance was taken off this box | approved | |
| 46 | | RUN-C8 | internal/activator/cmd.go | the box's agent stopped | approved | |
| 47 | | RUN-C9 | internal/instanceexec/exec.go | usage: carlos instance-exec <run|seed|sidecar|cmd> <instance-id> | approved | |
| 48 | | RUN-C10 | internal/instanceexec/exit.go | usage: carlos instance-exit <run|seed|sidecar|cmd> <instance-id> | approved | |
| 49 | | RUN-C11 | docs/site/cli.md | `ExecStart=` of the four instance templates. | approved | |
| 50 | | RUN-C12 | docs/site/cli.md | `ExecStopPost=` of the four instance templates. | approved | |
| 51 | | RUN-C13 | internal/instanceexec/exec.go | instance-exec: role <role> is not run, seed, sidecar or cmd | approved | |
| 52 | | RUN-C14 | internal/instanceexec/exit.go | instance-exit: role <role> is not run, seed, sidecar or cmd | approved | |
| 53 | |
| 54 | ## Plan 2: record and console door |
| 55 | |
| 56 | | id | file | text | status | |
| 57 | |---|---|---|---| |
| 58 | | RUN-C15 | internal/console/apiruns.go | too many run requests. wait a minute and try again | approved | |
| 59 | | RUN-C16 | internal/console/apiruns.go | <host> is not an instance of <app> | approved | |
| 60 | | RUN-C17 | internal/console/apiruns.go | <host> is a <kind> route. carlos run works on instances | approved | |
| 61 | | RUN-C18 | internal/console/apiruns.go | <host> is served from its own address, so the platform does not run its process | approved | |
| 62 | | RUN-C19 | internal/console/apiruns.go | <host> does not sleep, so the platform does not run its process | approved | |
| 63 | | RUN-C20 | internal/console/apiruns.go | <host> is a pooled instance. carlos run works on instances with a process of their own | approved | |
| 64 | | RUN-C21 | internal/console/apiruns.go | <host> runs on your fleet <fleet>. carlos run works on platform boxes only | approved | |
| 65 | | RUN-C23 | internal/console/apiruns.go | <host> is moving. run the command once the move has finished | approved | |
| 66 | | RUN-C24 | internal/console/apiruns.go | <host> already has a run open: <id>, started by <by> | approved | |
| 67 | | RUN-C25 | internal/console/apiruns.go | name the command to run after -- | approved | |
| 68 | | RUN-C26 | internal/console/apiruns.go | a run takes at most 256 arguments | approved | |
| 69 | | RUN-C27 | internal/console/apiruns.go | the arguments come to more than 64 KB | approved | |
| 70 | | RUN-C28 | internal/console/apiruns.go | an argument holds a NUL byte | approved | |
| 71 | | RUN-C29 | internal/console/apiruns.go | the timeout must be between 1 second and 6 hours | approved | |
| 72 | | RUN-C30 | internal/console/apiruns.go | the files come to more than 64 MB | approved | |
| 73 | | RUN-C31 | internal/console/apiruns.go | <name> is not a plain file name | approved | |
| 74 | | RUN-C32 | internal/console/apiruns.go | two files are both named <name> | approved | |
| 75 | | RUN-C33 | internal/console/apiruns.go | <host> is an import that is not committed yet. finish or abort the import first | approved | |
| 76 | | RUN-C34 | internal/console/apiruns.go | the run request is malformed | approved | |
| 77 | | RUN-C35 | internal/console/apiruns.go | could not record the run. try again | approved | |
| 78 | | RUN-C36 | internal/console/apirunsfollow.go | <host> has no run <id> | approved | |
| 79 | | RUN-C37 | internal/console/apirunsfollow.go | could not read the run. try again | approved | |
| 80 | | RUN-C38 | internal/console/apirunsfollow.go | run <id> has not finished yet | approved | |
| 81 | | RUN-C39 | internal/console/apirunsfollow.go | run <id> ended at chunk <last>, not <got> | approved | |
| 82 | | RUN-C40 | internal/console/apirunsfollow.go | run <id> has already started | approved | |
| 83 | | RUN-C41 | internal/console/apirunsfollow.go | run <id> has already ended | approved | |
| 84 | | RUN-C42 | internal/console/apiruns.go | carlos run is not available on this console yet. it needs the host-claim launch protocol | approved | |
| 85 | | 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 | |
| 86 | | RUN-C44 | internal/runs/runs.go | its box stopped reporting | approved | |
| 87 | | RUN-C45 | internal/runs/runs.go | the box's agent restarted | approved | |
| 88 | | RUN-C46 | internal/runs/runs.go | the command's unit went away | approved | |
| 89 | | RUN-C82 | internal/runs/runs.go | the box could not start the command | approved | |
| 90 | |
| 91 | ## Plan 4: the CLI and the docs |
| 92 | |
| 93 | | id | file | text | status | |
| 94 | |---|---|---|---| |
| 95 | | RUN-C22 | run.go | carlos run: <error> | approved | |
| 96 | | RUN-C47 | run.go | usage: carlos run --app <app> --instance <host> [--file <path>]... [--timeout 30m] -- <arg>... | approved | |
| 97 | | RUN-C48 | run.go | carlos run --app <app> --instance <host> --follow <id> | approved | |
| 98 | | 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 | |
| 99 | | RUN-C50 | run.go | --app and --instance name the instance to run against | approved | |
| 100 | | RUN-C51 | run.go | --timeout must be between 1s and 6h | approved | |
| 101 | | RUN-C52 | run.go | --follow takes a run id and no command after -- | approved | |
| 102 | | RUN-C53 | run.go | could not read <path>: <error> | approved | |
| 103 | | RUN-C54 | run.go | run <id> on <host>. to follow it again: carlos run --app <app> --instance <host> --follow <id> | approved | |
| 104 | | RUN-C55 | run.go | <host>: not picked up within 60 seconds. the run did not start. try again | approved | |
| 105 | | RUN-C56 | run.go | <host>: the box is not on the nested layout yet, so carlos run cannot run there | approved | |
| 106 | | RUN-C57 | run.go | <host>: the box has host claims armed. carlos run cannot run there yet | approved | |
| 107 | | RUN-C58 | run.go | <host>: the instance did not wake. <detail> | approved | |
| 108 | | RUN-C59 | run.go | <host> was deleted and created again after this run was requested. the run did not start | approved | |
| 109 | | RUN-C60 | run.go | <host> is an import that is not committed yet. the run did not start | approved | |
| 110 | | RUN-C61 | run.go | <host>: the run was interrupted: <reason>. it may have done part of its work | approved | |
| 111 | | RUN-C62 | run.go | <host>: the command timed out after <timeout> | approved | |
| 112 | | RUN-C63 | run.go | <host>: the command was cancelled | approved | |
| 113 | | RUN-C64 | run.go | <host>: the output passed 64 MB and was cut off there. the command ran on to its end | approved | |
| 114 | | RUN-C65 | run.go | cancelling run <id>. press Ctrl-C again to stop waiting | approved | |
| 115 | | RUN-C66 | run.go | <host>: run <id> was cancelled before it started | approved | |
| 116 | | RUN-C67 | run.go | <host> has no run <id>, or this console has no carlos run yet | approved | |
| 117 | | RUN-C83 | main.go (a fragment of the usage text) | run one of your app's commands against one instance, as that instance | approved | |
| 118 | | RUN-C68 | docs/site/cli.md | ### carlos run | approved | |
| 119 | | 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 | |
| 120 | | RUN-C70 | docs/site/cli.md | Any person who is a member of the account can run a command. A service credential cannot. | approved | |
| 121 | | RUN-C71 | docs/site/cli.md | carlos run --app shop --instance shop.example.com --file orders.csv -- import orders.csv | approved | |
| 122 | | 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 | |
| 123 | | 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 | |
| 124 | | 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 | |
| 125 | | 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 | |
| 126 | | 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 | |
| 127 | | 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 | |
| 128 | | RUN-C78 | docs/site/cli.md | the instance's database, the path --db names when the instance starts | approved | |
| 129 | | RUN-C79 | docs/site/cli.md | this run's id | approved | |
| 130 | | 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 | |
| 131 | | 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 | |
| 132 | | RUN-C84 | run.go | not a regular file | approved | |
| 133 | | RUN-C85 | run.go | the file changed while carlos was reading it | approved | |
| 134 | | 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 | |
| 135 | | RUN-C87 | run.go | <host>: run <id> timed out before it started | approved | |
| 136 | |