carlos / platform Public
Moves Phase 1: copy record (pending Paul's review)
Every operator-facing string the Phase 1 plans introduce, with the id the copy gate (approved_moves_copy_test.go, Plan 1 Task 15, completed by Plan 6 Task 7) checks. Rows are pending until Paul approves them. A pending string may be wired while moves.DefaultEnabled is false (D13), and the gate keeps the default off while any row is pending. Spec §20 ids (M-C1 to M-C22) keep their text as drafted there until the review changes it. No em dashes anywhere.
The Text cell is the sentence and nothing else. A note goes in the File cell, in parentheses after the path. A row whose text ends in <element>, ... is a list the code builds at run time; the gate's notation for it, and for %q, is in the Review of 2026-10-09 below (Paul, 2026-10-10).
Each plan's Task "Gate and copy" appends its rows under its heading. Columns: id, file, text, status.
Spec §20 drafts
| id | file | text | status | |---|---|---|---| | M-C1 | internal/moves/operatorcopy.go | Moving <host> from <from> to <to>. | approved | | M-C2 | move.go | Copying data. <host> keeps serving while this runs. | approved | | M-C3 | move.go | Checking that <to> can run <host>. | approved | | M-C4 | move.go | <to> cannot take this move yet: <check>. | approved | | M-C5 | internal/moves/operatorcopy.go | <to> is ready. Starting the cut. <host> will be offline for about <estimate>. | approved | | M-C6 | internal/moves/operatorcopy.go | Stopped in <from>. Starting in <to>. | approved | | M-C7 | internal/moves/operatorcopy.go | Moved. <host> is served from <to>. It was offline for <seconds> seconds. | approved | | M-C8 | internal/moves/operatorcopy.go | <to> could not start <host>: <reason>. Moving it back to <from>. | approved | | M-C9 | internal/moves/operatorcopy.go | <host> is back in <from> and serving. Nothing was lost. | approved | | M-C10 | move.go | Waiting for <box> in <to> to confirm it has stopped <host>. <host> is offline. | approved | | M-C11 | move.go | <host> could not start in <to> or in <from>. It is offline. Run carlos move resolve --host <host> --retry once the cause is fixed. | approved | | M-C12 | move.go | Ready to cut. Run carlos move cut --host <host> when you are ready. | approved | | M-C13 | internal/console/apiinstancemove.go | This move has started its cut. It will finish or move back on its own. | approved | | M-C14 | internal/host/movesphase1.go | <host> keeps a file this move cannot copy: <path>. Only regular files and folders move. | approved | | M-C15 | internal/host/movesphase1.go | <host> keeps more than 256 MiB of files beside its database. Keep large files in object storage. | approved | | M-C16 | internal/host/movesphase1.go | <host> does not answer its readiness path. Declare moves.ready_path for this app. | approved | | M-C17 | internal/console/apiinstancemove.go | <host> uses private storage. Instances with private storage cannot move yet. | approved | | M-C18 | move.go | This declares <box> lost. Anything <host> wrote on <box> after <time> is lost. Type the host name to confirm. | approved | | M-C19 | internal/console/apiinstancemove.go | <host> is being moved. Delete it after the move finishes, or abort the move first. | approved | | M-C20 | internal/host/movespaging.go | <box> is running a process for <host> that this move cannot account for. An operator has been paged. | approved | | M-C21 | internal/console/apiinstancemove.go | <region> has more than one box. Moves between regions with several boxes are not supported yet. | approved | | M-C22 | move.go | <host> is waiting for a box in <region>. Its last box was terminated before the move finished. Its data up to the last completed upload is kept in <region>. It will start when a box joins the region, and any writes after that upload will be reported then. | approved |
Plan 1
| id | file | text | status | |---|---|---|---| | MV-N1 | internal/console/apiinstancemove.go | a home with replicas does not move. remove its replicas first | approved | | MV-N2 | internal/console/apiinstancemove.go | region <to> is not one of this app's regions: <list> | approved | | MV-N3 | internal/console/apiinstancemove.go | the instance has no host claim yet. it is adopted on its next reconcile pass | approved | | MV-N4 | internal/console/apiinstancemove.go | the instance is under a <kind> operation. wait for it to finish | approved | | MV-N5 | internal/console/apiinstancemove.go | a box in <from> or <to> is below the moves protocol floor. roll the platform first | approved | | MV-N6 | internal/console/apiinstancemove.go | cut must be "auto" or "manual" | approved | | MV-N7 | internal/console/apiinstancemove.go | this move was started by an older console and has no id. abort it and request it again | approved | | MV-N8 | internal/console/apiinstancemove.go | the fleet's edges files could not all be read. try again | approved | | MV-N9 | internal/host/movesreport.go | the staging lane holds another move's move.json | approved | | MV-N10 | internal/host/systemapply.go | moves protocol floor: <version> is below <minrelease> | approved | | MV-N11 | internal/console/apiinstancemove.go | declare-lost is not enabled in this deployment yet | approved | | MV-N12 | internal/provision/provision.go | no journaled move in flight | approved | | MV-N13 | internal/provision/provision.go | the record's move is <id> for incarnation <incarnation> | approved | | MV-N14 | internal/provision/provision.go | malformed move start | approved | | MV-N15 | internal/provision/provision.go | past the point of no return. the cut completes forward or moves back on its own | approved | | MV-N16 | internal/console/apiinstancemove.go | the instance is already in <region> | approved | | MV-N17 | internal/console/apiinstancemove.go | could not read the app's instances manifest. try again | approved | | MV-N18 | internal/console/apiinstancemove.go | could not read this instance's storage binding. try again | approved | | MV-N19 | internal/console/apiinstancemove.go | this deployment publishes no armed regions. nothing to move to | approved | | MV-N20 | internal/console/apiinstancemove.go | region <to> cannot take a move in this deployment. it publishes no staging bucket. Regions that can: <list> | approved | | MV-N22 | internal/console/apiinstancemove.go | could not read the moves protocol floor. try again | approved | | MV-N23 | internal/console/apiinstancemove.go | no box in <to> publishes an edges file yet | approved | | MV-N24 | internal/console/apiinstancemove.go | no eligible box in <from> publishes an edges file | approved | | MV-N25 | internal/console/apiinstancemove.go | the record's identity is malformed | approved | | MV-N26 | internal/console/apiinstancemove.go | could not read the host claim | approved | | MV-N27 | internal/console/apiinstancemove.go | the host claim names another incarnation of this instance | approved | | MV-N28 | internal/console/apiinstancemove.go | the source box <box> is not the eligible box of <from> | approved | | MV-N29 | internal/console/apiinstancemove.go | could not mint a move id | approved | | MV-N30 | internal/console/apiinstancemove.go | the instance has unfinished cleanup in <to>. wait for it to finish | approved | | MV-N31 | internal/console/apiinstancemove.go | the host claim changed under this request. try again | approved | | MV-N32 | internal/console/apiinstancemove.go | could not write the host claim | approved | | MV-N33 | internal/console/apiinstancemove.go | could not record the move. try again | approved | | MV-N34 | internal/console/apiinstancemove.go | could not write the move. try again | approved | | MV-N35 | internal/console/apiinstancemove.go | nothing to resolve yet | approved | | MV-N36 | internal/console/apiinstancemove.go | could not confirm whether the move was recorded. read its state before trying again | approved | | MV-N37 | internal/console/apiinstancemove.go | this console is below the moves protocol floor. roll the console first | approved | | MV-N38 | internal/console/apiinstancemove.go | the move changed since this request read it. read its state and try again | approved | | MV-N39 | internal/console/apiinstancemove.go | past the point of no return. the cut completes forward | approved | | MV-N40 | internal/provision/provision.go | cut is only legal from ready | approved | | MV-N41 | internal/provision/provision.go | already aborting | approved | | MV-N42 | internal/provision/provision.go | a move in state "<state>" cannot be aborted | approved | | MV-N43 | internal/provision/provision.go | a move to <to> is already <state> | approved | | MV-N44 | internal/provision/provision.go | a <role> does not move. delete it and create one in the new region | approved | | MV-N45 | internal/provision/provision.go | no move in flight | approved | | MV-N46 | internal/console/apiinstancemove.go | this move's state does not allow that. read its state and try again | approved | | MV-N47 | internal/console/apiinstancemove.go | only exec-backed instances move. this route is kind <kind> | approved | | MV-N48 | internal/console/apiinstancemove.go | an addr-backed route's process is not platform-managed. there is nothing to move | approved | | MV-N49 | internal/console/apiinstancemove.go | fleet-placed instances keep state-does-not-follow semantics. moves are for platform regions | approved | | MV-N50 | internal/console/apiinstancemove.go | pooled instances do not move yet. their data lives on the pool's own box | approved | | MV-N51 | internal/console/apiinstancemove.go | a non-hibernating instance runs outside the activator. nothing manages its data, so nothing can move it | approved | | MV-N52 | internal/console/apiinstancemove.go | too many instance requests. wait a minute and try again | approved | | MV-N53 | internal/console/apiinstancemove.go | this console has no deployment bucket, so it cannot answer this | approved | | MV-N54 | internal/console/apiinstancemove.go | account "<segment>" is ambiguous. pass --account-id with one of: <sqid> ("<name>"), ... | approved | | MV-N55 | internal/moves/readiness.go | the last round moved <bytes> bytes and the bar is <bytes> | approved | | MV-N56 | internal/moves/readiness.go | the target has not published a readiness report yet | approved | | MV-N57 | internal/moves/readiness.go | the target's readiness report is stale | approved | | MV-N58 | internal/moves/readiness.go | <check> was not reported | approved | | MV-N59 | internal/moves/readiness.go | <check>: did not pass | approved | | MV-N60 | internal/moves/readiness.go | T11: the cut budget admits no probe attempt | approved | | MV-N61 | internal/moves/readiness.go | T11: the cut budget was priced for a final round of <MiB> MiB and the bar is <MiB> MiB | approved | | MV-N62 | internal/moves/readiness.go | the source has not published a readiness report yet | approved | | MV-N63 | internal/moves/readiness.go | the source's readiness report is stale | approved | | MV-N64 | internal/moves/readiness.go | S1: the source did not answer | approved | | MV-N65 | internal/moves/readiness.go | S2: the source could not confirm its spawn state | approved | | MV-N66 | internal/moves/readiness.go | the target checked files <digest> and the source now publishes <digest> | approved | | MV-N67 | internal/moves/readiness.go | the target checked launch environment <digest> and the source now reports <digest> | approved | | MV-N68 | internal/moves/readiness.go | no fresh rate measurement for <which> | approved | | MV-N69 | internal/moves/readiness.go | the database size could not be measured | approved | | MV-N70 | internal/moves/readiness.go | the cut window is not a usable number of seconds | approved | | MV-N71 | internal/moves/readiness.go | the cut needs about <seconds> seconds with one probe attempt and the window is <seconds> seconds | approved | | MV-N72 | internal/host/movesphase1.go | nested instances cannot move yet | approved | | MV-N73 | internal/host/movesreport.go | the source has published no archive yet | approved | | MV-N74 | internal/host/movesreport.go | the host claim has not touched <region> yet | approved | | MV-N75 | internal/host/movesreport.go | waiting for the root stage pass | approved | | MV-N76 | internal/host/movesreport.go | the source has not reported its release yet | approved | | MV-N77 | internal/moves/archive/archive.go | archive: only regular files and directories move: <path> | approved | | MV-N78 | internal/moves/archive/archive.go | archive: the files directory is over 256 MiB | approved | | MV-N79 | internal/host/movesphase1.go | archive: the files directory holds more than 131072 entries | approved | | MV-N80 | internal/moves/archive/archive.go | archive: a file changed while it was being archived: <path> | approved | | MV-N81 | internal/host/movesphase1.go | archive: refused path: <path> is longer than 4096 bytes | approved | | MV-N82 | internal/moves/readiness.go | T11: the target's report carries a rate that is not a usable number | approved | | MV-N83 | internal/moves/readiness.go | T11: the target's report claims <n> probe attempts and its measurements admit <n> | approved | | MV-N84 | internal/moves/readiness.go | the target's readiness report is dated ahead of this clock | approved | | MV-N85 | internal/moves/readiness.go | the source's readiness report is dated ahead of this clock | approved | | MV-N86 | internal/moves/archive/archive.go | archive: only regular files and directories move: <path> is a hard link | approved | | MV-N87 | internal/host/systemapply.go | moves protocol floor: carlos.prev records no version, so it is below <minrelease> | approved | | MV-N88 | internal/host/systemapply.go | moves protocol floor: the floor on this box cannot be read: <error> | approved | | MV-N89 | internal/host/systemapply.go | a participant in a held operation does not downgrade: <version> is below the running <release> | approved | | MV-N90 | internal/host/systemapply.go | a participant in a held operation does not downgrade, and the registry cannot say whether this box is one: <error> | approved | | MV-N91 | internal/host/systemapply.go | a participant in a held operation does not downgrade, and the running release is unknown | approved | | MV-N92 | internal/host/systemapply.go | moves protocol floor: could not write it on this box: <error> | approved | | MV-N93 | internal/host/systemapply.go | <refusal>; the root passes paused for the install could not be started again: <error>. the next apply or recovery starts them | approved | | MV-N94 | internal/host/systemapply.go | <refusal>; the root passes paused for the install could not be started again: <error>, and the retry could not be recorded: <error>. the journal stays for the next recovery | approved | | MV-N95 | internal/host/systemapply.go | <refusal>; the root passes paused for the restore could not be started again: <error> | approved | | MV-N96 | internal/activator/backendexec.go | activator: a host in a move spawns only under strict containment | approved | | MV-N97 | internal/activator/backendexec.go | S2: the next spawn of this host is not strict | approved | | MV-N98 | internal/activator/backendexec.go | S2: this box has no delegated cgroup subtree, so it cannot spawn strictly | approved | | MV-N99 | internal/activator/backendexec.go | S2: this box cannot spawn strictly | approved | | MV-N100 | internal/release/materializeheld.go | release: this process cannot write the version store, so the root stage pass stages this release | approved | | MV-N101 | internal/release/materializeheld.go | release: held stage request no longer matches its move | approved | | MV-N102 | internal/release/materializeheld.go | release: held rows are not staged while CARLOSHOSTCLAIM is on | approved | | MV-N103 | internal/appconfig/renderheld.go | config: not a held row of this move | approved | | MV-N104 | internal/appconfig/renderheld.go | config: held rows are not rendered while CARLOSHOSTCLAIM is on | approved | | MV-N105 | internal/host/systemapply.go | moves protocol floor: another writer holds the floor lock on this box; this waits for the next run | approved | | MV-N106 | internal/host/systemapply.go | <error>; the root passes paused for the restore could not be started again: <error>, and the retry could not be recorded: <error> | approved | | MV-N107 | internal/host/systemapply.go | <error>; the journal stays: <error> | approved | | MV-N108 | internal/activator/backendexec.go | S2: the agent is not placed in its delegated cgroup subtree, so it cannot spawn strictly | approved | | MV-N109 | internal/activator/backendexec.go | S2: this host's last strict group has not been cleaned up yet | approved | | MV-N110 | internal/release/materializeheld.go | release: a held publication needs its authority | approved | | MV-N111 | internal/activator/backendexec.go | activator: a strict child of this host is still running in its group | approved | | MV-N112 | internal/activator/backendexec.go | activator: this host's last strict group is still being cleaned up | approved | | MV-N113 | internal/moves/readiness.go | the archive's file count could not be measured | approved | | MV-N114 | internal/host/movesphase1.go | this deployment has no deployment bucket for the staging lease | approved | | MV-N115 | internal/host/movesphase1.go | this box has no secrets key, so it cannot report the launch environment or seal the archive | approved | | MV-N116 | internal/host/movesphase1.go | this box cannot probe the instance yet | approved | | MV-N117 | internal/host/movesphase1.go | <host> is not an exec-backed instance, so it cannot move | approved | | MV-N118 | internal/host/movesphase1.go | <host> does not hibernate, so it cannot move | approved | | MV-N119 | internal/host/movesphase1.go | <host> has a sidecar environment file, so it cannot move | approved | | MV-N120 | internal/host/movesphase1.go | <host> has <column> set in the registry, so it cannot move | approved | | MV-N121 | internal/host/movesreport.go | the moves protocol floor could not be read | approved | | MV-N122 | internal/host/movesreport.go | this box cannot read its own copy of the moves protocol floor | approved | | MV-N123 | internal/host/movesreport.go | this box has not armed the moves protocol floor yet | approved | | MV-N124 | internal/host/movesreport.go | this box speaks moves protocol <n>, below the floor's <n> | approved | | MV-N125 | internal/host/movesreport.go | the console has not published its secrets key fingerprint yet | approved | | MV-N126 | internal/host/movesreport.go | this box has no secrets key | approved | | MV-N127 | internal/host/movesreport.go | this box's secrets key is not the console's | approved | | MV-N128 | internal/host/movesreport.go | this box could not compose the launch environment of the held row | approved | | MV-N129 | internal/host/movesreport.go | the launch environment differs from the source's: <NAME (kind)>, ... | approved | | MV-N130 | internal/host/movesreport.go (a T3 note, not a failure) | <NAME> in the carried .env overrides the rendered configuration | approved | | MV-N131 | internal/host/movesreport.go (a T3 note for an advisory variable) | <NAME> (<kind>) | approved | | MV-N132 | internal/host/movesreport.go | the store bound as <PREFIX> did not answer the probe from this box | approved | | MV-N133 | internal/host/movesreport.go | this box could not read the archive the source published | approved | | MV-N134 | internal/host/movesreport.go | the archive does not open with this box's key or does not match its manifest | approved | | MV-N135 | internal/host/movesreport.go | this box could not measure its free disk space | approved | | MV-N136 | internal/host/movesreport.go | this box needs <GiB> GiB free for the move without filling its disk past 85% | approved | | MV-N137 | internal/host/movesreport.go | this box could not stage the source's release | approved | | MV-N138 | internal/host/movesreport.go | no box publishes <region> | approved | | MV-N139 | internal/host/movesreport.go | <n> boxes publish <region>, and a move needs exactly one | approved | | MV-N140 | internal/host/movesreport.go | the host claim is not held by this move | approved | | MV-N141 | internal/host/movesreport.go | the staging lane has no move.json yet | approved | | MV-N142 | internal/host/movesreport.go | the source region publishes no data bucket | approved | | MV-N143 | internal/host/movesreport.go | this box cannot write the return lane in the source's bucket | approved | | MV-N144 | internal/host/movesreport.go | no replica has been staged yet | approved | | MV-N145 | internal/host/movesreport.go | the instance left <region> recently and can move back after <RFC 3339 time> | approved | | MV-N146 | internal/moves/readiness.go (the <which> of MV-N68) | the disk's per-file sync | approved | | MV-N147 | internal/console/apiinstancemove.go | invalid region | approved | | MV-N148 | internal/host/movesphase1.go | the app's readiness path could not be read | approved | | MV-N149 | internal/moves/readiness.go | the target has not checked a release yet | approved | | MV-N150 | internal/moves/readiness.go | the target checked release <release> and the source now runs <release> | approved | | MV-N151 | internal/host/movesphase1.go | the files beside the database could not be archived | approved | | MV-N152 | internal/moves/readiness.go | the cut budget could not be priced | approved | | MV-N153 | internal/host/movesreport.go | this box requires a proof for the source's release and has not accepted one | approved | | MV-N154 | internal/host/movesreport.go | the source staged its archive without a fixed version id | approved | | MV-N155 | internal/moves/readiness.go | T11: the database has grown to <MiB> MiB since the target priced <MiB> MiB. waiting for the target's next report | approved | | MV-N156 | internal/console/apiinstancemove.go | the source box is not eligible to move from | approved | | MV-N157 | internal/console/apiinstancemove.go | could not read the host roster. try again | approved | | MV-N158 | internal/provision/provision.go | stop evidence budget | approved | | MV-N159 | internal/stopevidence/reconstructlinux.go | the instance's processes could not be stopped | approved | | MV-N160 | internal/stopevidence/reconstructlinux.go | pid <pid> in the agent's cgroup is not the agent | approved | | MV-N161 | internal/stopevidence/reconstructlinux.go | <count> process(es) hold the database open | approved | | MV-N162 | internal/stopevidence/reconstructlinux.go | lock.json still held | approved | | MV-N163 | internal/provision/provision.go | the cut ran past its deadline | approved | | MV-N164 | internal/provision/provision.go | the final copy kept finding changes at the source | approved | | MV-N165 | internal/provision/provision.go | the stopped files could not be archived | approved | | MV-N166 | internal/host/movesphase1.go | <region> has more than one box. Moves between regions with several boxes are not supported yet. | approved | | MV-N167 | internal/provision/provision.go | the instance keeps its data under a per-incarnation path, which moves cannot cut yet | approved |
Plan 2
(Task 10 rows, then task E-switch's MV-N173 to MV-N179, then the final-review fix's MV-N180; the rest appended by Plan 2 Task 11)
| id | file | text | status | |---|---|---|---| | MV-N168 | internal/provision/provision.go | configuration changed during the move | approved | | MV-N169 | internal/provision/provision.go | the landing ran past its deadline | approved | | MV-N170 | internal/provision/provision.go | this box cannot land a move while host claims are on | approved | | MV-N171 | internal/provision/provision.go | the landing row is not at the flat path | approved | | MV-N172 | internal/provision/provision.go | the installed database does not match the build | approved | | MV-N173 | internal/console/apiinstancemove.go | this app has no host-claim admission yet. try again shortly | approved | | MV-N174 | internal/console/apiinstancemove.go | could not read this app's admission. try again | approved | | MV-N175 | internal/console/apiinstancemove.go | another operation holds this app. try again when it finishes | approved | | MV-N176 | internal/console/apiinstancemove.go | could not register the move. try again | approved | | MV-N177 | internal/console/apiinstancemove.go | the host roster does not allow this move. the source and target must be active boxes in <from> and <to> | approved | | MV-N178 | internal/console/apiinstancemove.go | the instance or its boxes changed under this request. read its state and try again | approved | | MV-N179 | internal/console/apiinstancemove.go | the move could not be recorded and is held until an operator checks the record | approved | | MV-N180 | internal/provision/provision.go | the files directory already holds files this move did not write | approved | | MV-N181 | internal/stopevidence/reconstruct_linux.go | <count> process(es) in the agent's cgroup cannot be identified | approved |
Plan 3
(Task 2's rows, then the rest appended by Plan 3 Task 9)
| id | file | text | status | |---|---|---|---| | MV-N182 | internal/provision/provision.go | the probe ran out of attempts | approved | | MV-N183 | internal/provision/provision.go | the commit came after the deadline | approved | | MV-N184 | internal/moves/startcheck.go | this box holds another generation of the instance | approved | | MV-N185 | internal/moves/startcheck.go | the instance's files belong to another generation | approved | | MV-N186 | internal/moves/startcheck.go | the instance's database belongs to another generation | approved | | MV-N187 | internal/provision/provision.go | the cut recorded no probe attempts for this landing | approved |
Plan 4
(each task's rows in the task that writes them, ruling J23; the rest appended by Plan 4 Task 11)
| id | file | text | status | |---|---|---|---| | MV-N300 | internal/provision/provision.go | the target box was declared lost | approved | | MV-N301 | internal/provision/provision.go | the source no longer matches what it recorded when it stopped | approved | | MV-N302 | internal/provision/provision.go | the recovery probe ran out of attempts | approved | | MV-N303 | internal/provision/provision.go | the source did not answer within 15 minutes of moving back | approved | | MV-N304 | internal/provision/provision.go | the move has committed. it cannot roll back | approved | | MV-N305 | internal/provision/provision.go | the move is not stuck. there is nothing to retry | approved | | MV-N306 | internal/provision/provision.go | the target holds nothing the move still needs. there is nothing to declare lost | approved | | MV-N307 | internal/provision/provision.go | the files directory holds files the return did not write | approved | | MV-N308 | internal/provision/provision.go | the returned database does not match the build | approved | | MV-N309 | internal/provision/provision.go | the source holds files of an installation the return cannot account for | approved | | MV-N310 | internal/provision/provision.go | the target's stopped database is missing | approved | | MV-N311 | internal/provision/provision.go | the target changed after it stopped | approved | | MV-N312 | internal/provision/provision.go | the target's files cannot be archived | approved |
Plan 5
(each task's rows in the task that writes them; the rest appended by Plan 5 Task 8. Task 0's stuck page continues Plan 4's MV-N3xx block, ruling K11)
| id | file | text | status | |---|---|---|---| | MV-N313 | internal/host/movesrollback.go | carlos move stuck: <host> | approved | | MV-N314 | internal/host/movesrollback.go | The move <id> of <host> stopped for an operator on <box>, its <role>: <reason>. Run carlos move resolve --host <host> --retry once the cause is fixed. | approved |
Task 7's pages, the console's move pass (internal/console/movesrecovery.go, §13). MV-N319 to MV-N324 are Plan 6 Task 2's.
| id | file | text | status | |---|---|---|---| | MV-N325 | internal/console/movesrecovery.go | carlos move waiting for <to> to release: <host> | approved | | MV-N326 | internal/console/movesrecovery.go | The move <id> of <host> is rolling back, and <to> has not released it by its deadline. <host> stays offline until a box in <to> confirms it has stopped, or that box is declared lost. | approved | | MV-N327 | internal/console/movesrecovery.go | carlos move preparing for over an hour: <host> | approved | | MV-N328 | internal/console/movesrecovery.go | The move <id> of <host> to <to> has been preparing for over an hour. It aborts itself six hours after the request if <to> is still not ready. | approved | | MV-N329 | internal/console/movesrecovery.go | carlos move without a record: <host> | approved | | MV-N330 | internal/console/movesrecovery.go | The host claim of <host> is held by the move <id>, and the instance record carries no move. Nothing releases it on its own. An operator must check the record. | approved | | MV-N331 | internal/console/movesrecovery.go | carlos move close overdue: <host> | approved | | MV-N332 | internal/console/movesrecovery.go | The move <id> of <host> was ready to close for more than 10 minutes and no box closed it. The console has closed it. | approved | | MV-N333 | internal/console/movesrecovery.go | carlos move abort not closed: <host> | approved | | MV-N334 | internal/console/movesrecovery.go | The abort of the move <id> of <host> has nothing left to park in <to>, and <box> has not closed it. <box> closes it once it has removed the move's snapshots from its own bucket. Check <box>. | approved |
Task 9a's rows (the production park wiring). MV-N335 is the stuck reason a rollback records when the target's files cannot be archived at its release; it reaches carlos move status through Move.StuckReason and the stuck page (MV-N314).
| id | file | text | status | |---|---|---|---| | MV-N335 | internal/provision/provision.go | the target's files cannot be archived now that it has stopped | approved |
The anomaly page (Task 9a's quiet refusals, internal/host/movesrefusals.go): a box pages once when the moves evidence for a host's at-rest cleanup contradicts itself. A warning; the reason stays in the agent's log.
| id | file | text | status | |---|---|---|---| | MV-N336 | internal/host/movesrefusals.go | carlos move cleanup refused: <host> | approved | | MV-N337 | internal/host/movesrefusals.go | The cleanup of <host> on <box> stopped: its move history does not add up, or something wrote where no move could. Nothing of <host> is cleaned up in this region until an operator checks the agent's log on <box>. | approved |
Floor gate fix (#267 review)
(root's floor gate for the system units, carlos system floor-allows-prev; its stderr reaches the unit's journal)
| id | file | text | status | |---|---|---|---| | MV-N315 | internal/host/movesfloorgate.go | moves protocol floor: carlos.prev may not run: <error> | approved | | MV-N316 | internal/host/movesfloorgate.go | the verdict on carlos.prev does not name the floor on this box | approved | | MV-N317 | internal/host/movesfloorgate.go | <path> is not a regular file with one link, at most 256 bytes, owned by an expected account | approved | | MV-N318 | internal/host/movesfloorgate.go | floor-allows-prev takes two absolute paths: the floor file and the verdict file | approved |
Plan 6 Task 7's audit: MV-N317 used to be said in two parts, the path from movesfloorgate_linux.go in front of a sentinel error in movesfloorgate.go. It is now one format in movesfloorgate.go (floorGateUnsafe), which still wraps the sentinel for errors.Is and prints the same text, so the gate can pass it once it is approved.
Plan 6
Completed by Plan 6 Task 7.
- Task 2 took MV-N319 to MV-N324 and used MV-N319. It made the abort door speak M-C13 on a console without the host claim, so M-C13 is approved verbatim. Task 2b made the delete door speak M-C19 through moveDeleteRefusal in internal/console/apiinstancemove.go, so its File cell moved there (preflight L6) and it is approved verbatim.
- Task 1a took MV-N350 to MV-N371 for
carlos move(move.go, and its usage line in main.go, MV-N363). It speaks M-C1 to M-C12 and M-C22 verbatim, so those rows are approved. Task 1b took MV-N372 to MV-N379; it speaks M-C18 verbatim from move.go and MV-N11 from the resolve door of a console without the host claim, so both are approved. - Task 3 took MV-N380 (the member line for stuck, preflight L9) and MV-N381 (the name a member line gives the home region, which a box knows only as the record's empty region; fix round 1, ruling 4). Its fix round 1 (ruling 6, preflight L12) moved M-C1 and M-C5 to M-C9 into shared templates in internal/moves/operatorcopy.go, which both move.go and the member stream (internal/host/movesevents.go) call, so their File cells, and MV-N380's, name that file. Their text is unchanged.
- Task 4 adds no sentence: the detail answer's timings reuse MV-N319 when their read fails.
- Task 5 took MV-N385 to MV-N390, the box's pages (internal/host/movespaging.go, preflight L8), and speaks M-C20 verbatim from movespaging.go when the cut's stop evidence finds a stray process, so M-C20 is approved. Its fix round 1 (ruling 1) keeps M-C20 to operators, as the stray page's body and the agent's log line, never the app's members, so MV-N388, the body it replaced, is withdrawn.
- MV-N382 to MV-N384 and MV-N392 to MV-N399 were never used (claim Plan 12 took MV-N391). Plan 5 owns MV-N325 to MV-N349.
- Task 7's audit: MV-N363 is the
moveline of the usage. It is now one literal in main.go (moveUsageLine), still printed wrapped afterabort |, so the gate can pass it once it is approved.
| id | file | text | status | |---|---|---|---| | MV-N319 | internal/console/apiinstancemove.go | could not read the move's reports. try again | approved | | MV-N350 | move.go | Moves are not enabled on this console. | approved | | MV-N351 | move.go | carlos move needs --app: name the app the instance belongs to. | approved | | MV-N352 | move.go | Run carlos move as yourself, not as root. It uses your own credentials. | approved | | MV-N353 | move.go | carlos move works through a console. Unset CARLOSDEPLOYMENTBUCKET and CARLOSDEPLOYMENTDIR and run carlos auth login. | approved | | MV-N354 | move.go | This move's outcome is not recorded. | approved | | MV-N355 | move.go | The console is limiting requests. Checking every 10 seconds. | approved | | MV-N356 | move.go | carlos move needs --host <host>. | approved | | MV-N357 | move.go | carlos move needs --to <region>. | approved | | MV-N358 | move.go | carlos move resolve needs --retry or --declare-lost <box>. | approved | | MV-N359 | move.go | No move is in flight for <host>. | approved | | MV-N360 | move.go | Move <id> of <host> is <state>. | approved | | MV-N361 | move.go | The move was aborted. <host> stays in <from>. | approved | | MV-N362 | move.go | <host> is relocating after its box was terminated. This move will not finish. | approved | | MV-N363 | main.go | move an instance to another region: status | cut | abort | resolve --retry (operators; off unless the console enables it) | approved | | MV-N364 | move.go (M-C10's <box> when the claim names none) | the target box | approved | | MV-N365 | move.go | Could not reach the console. Check the connection and try again. | approved | | MV-N366 | move.go | The console's answer could not be read. | approved | | MV-N367 | move.go | Not found. Check --account and --app, and that you are a member of the instance's account. | approved | | MV-N368 | move.go | The console answered <status> and gave no reason. | approved | | MV-N369 | move.go | carlos move could not sign in to a console. Run carlos auth login, or check --console, CARLOSCONSOLE and CARLOSSERVICE_TOKEN. | approved | | MV-N370 | move.go | This console has moved to <url>. Run carlos auth login --console <url>, then run the command again. | approved | | MV-N371 | move.go | Stopped waiting. | approved | | MV-N372 | move.go | This records that <box> is gone with its disks. <host> keeps nothing there that it still needs. Type the host name to confirm. | approved | | MV-N373 | move.go | That is not <host>. Nothing was declared. | approved | | MV-N374 | move.go | declare-lost needs your AWS credentials to confirm the box is stopped or terminated. | approved | | MV-N375 | move.go | AWS does not show <box> stopped or terminated, so it cannot be declared lost. | approved | | MV-N376 | move.go | The move's flip time could not be read, so its target cannot be declared lost from here. | approved | | MV-N377 | move.go | The console asks to confirm a loss carlos move does not know. Use carlos ops host resolve. | approved | | MV-N378 | move.go | carlos move resolve takes --retry or --declare-lost, not both. | approved | | MV-N379 | move.go | The move changed since it was read. Nothing was declared. Run the command again. | approved | | MV-N380 | internal/moves/operatorcopy.go | <host> could not start in <to> or in <from>. It is offline. An operator has been paged. | approved | | MV-N381 | internal/moves/operatorcopy.go | the home region | approved | | MV-N385 | internal/host/movespaging.go | carlos move rolled back: <host> | approved | | MV-N386 | internal/host/movespaging.go | The move <id> of <host> rolled back, and <host> is serving on <box> again. | approved | | MV-N387 | internal/host/movespaging.go | carlos move stray process: <host> | approved | | MV-N389 | internal/host/movespaging.go | carlos move start refused: <host> | approved | | MV-N390 | internal/host/movespaging.go | <box> refused to start <host> for the move <id>: what it holds of <host> is not the generation the host claim names. Nothing was started. | approved |
Review of 2026-10-09
Paul approved the wording of all 205 rows then pending (M-C1 to M-C22 and MV-N1 to MV-N183), with no edits. 160 are marked approved above: their code already speaks the approved text.
The other 45 were pending only because the copy gate requires an approved row's text to appear in its file. Their wording is approved and final. 24 are now approved: their code was brought back to the approved text (18 rows: MV-N12, MV-N13, MV-N14, MV-N15, MV-N40 to MV-N45, MV-N52, MV-N59, MV-N77, MV-N80, MV-N86, MV-N92, MV-N105, MV-N106), or the row's File cell was corrected to the file that says it (6 rows, now internal/host/movesphase1.go: M-C14, M-C15, M-C16, MV-N72, MV-N79, MV-N81).
21 stay pending. Each flips to approved, verbatim, in the commit that makes its file speak it.
Not built yet (18), all approved since: Plan 6 built every one and flipped it verbatim (Tasks 1a, 1b, 2, 2b and 5):
- M-C1 to M-C12, M-C18 and M-C22 (14 rows,
move.go): thecarlos moveCLI is Plan 6, and nomove.goexists. - M-C13 and M-C19 (
internal/console/apiinstancemove.go,internal/console/apiinstances.go): the abort refusal after the cut started and the delete door's refusal during a move. Plan 5 Task 8 lists both as Plan 6 copy; no code says either. - M-C20 (
internal/host/movespaging.go): the paging sentence is Plan 5, and nomovespaging.goexists. - MV-N11 (
internal/console/apiinstancemove.go): the declare-lost door is Plan 4 Task 10.resolveanswers "nothing to resolve yet" to anything but a retry.
Built, but the gate could not read the row (3). Paul adopted three notation rules for them on 2026-10-10 (preflight L14), and Plan 6 Task 7 put the rules in the gate and flipped all three to approved, verbatim:
- MV-N54: the row ends in a list template,
<sqid> ("<name>"), .... The code builds that list at run time (apicli.go) and joins it into the sentence. Rule: a final<element>, ...is one runtime list placeholder. The gate checks the head with the list as the code's own fill-in, and checks the element where the code builds it (movesListElementFilesnamesapicli.go). Rule:%qprints quotes, so the gate reads it as a quoted fill-in and the row's"<segment>"matches. - MV-N129: the same list rule,
<NAME (kind)>, ...;t3Differsininternal/host/movesreport.gojoins the names it found. Its element is one placeholder, so only the head and the join are checked. - MV-N146: the note moved from the Text cell to the File cell (
internal/moves/readiness.go (the <which> of MV-N68)), and the text is the fill-inthe disk's per-file sync, whichreadiness.gosays as one literal.
The 16 rows added after that review (MV-N184 to MV-N186, MV-N300 to MV-N312) were approved by Paul on 2026-10-09 with no edits.
The 58 rows that were still pending on 2026-10-11 (MV-N187, MV-N313 to MV-N337, MV-N319, MV-N350 to MV-N381 and MV-N385 to MV-N390, without the withdrawn MV-N388) were approved by Paul on 2026-10-11 with no edits.
Claim Plan 12 rev 3
| id | file | text | status | |---|---|---|---| | MV-N391 | internal/provision/provision.go | target fenced | approved |
MV-N391 was approved by Paul on 2026-10-12 with no edits, with claim Plan 12's two box-log strings.
Moves cut guard
Paul's ruling of 2026-10-11 (N1.3 in the Plan 7 preflight): the source refuses to start a cut unless the target's latest report states T13, that its box can land the move and roll it back, and unless the source can recover the move itself. MV-N440 is T13's reason, which carlos move status shows through M-C4 while the move is preparing. MV-N441 and MV-N442 are the refusals the cut records on the move (MoveBackToReadyRefused, like MV-N167). MV-N443 is the line carlos move status now prints for a ready move with a recorded refusal, MV-N167 included. MV-N444 to MV-N449 are not used.
| id | file | text | status | |---|---|---|---| | MV-N440 | internal/host/movesreport.go | this box cannot land the move or roll it back yet | approved | | MV-N441 | internal/provision/provision.go | the target box cannot land the move or roll it back yet | approved | | MV-N442 | internal/provision/provision.go | the source box cannot recover the move if the cut fails | approved | | MV-N443 | move.go | The cut did not start: <reason>. | approved |
Moves after claim Plan 12
This branch's rows take MV-N450 to MV-N499 (Ruling F13: each a pending row in the commit of the task that writes it). Task 5's two are the console move pass's page for a dirty fenced target (ruling Q8): a target box terminated after it released with writes the source does not have, whose fence the recovery grant refuses, so the pass leaves it for an operator's declaration (J4) and pages once. <to> and <from> are the move's regions as a sentence names them (moves.RegionText). MV-N452 and MV-N453 are not used: loss-timer pages are the claim's (platform-1e, 2026-10-12).
| id | file | text | status | |---|---|---|---| | MV-N450 | internal/console/movesrecovery.go | carlos move target terminated before returning its writes: <host> | approved | | MV-N451 | internal/console/movesrecovery.go | <box> in <to> was terminated after it wrote data it had not returned to <from>. the move <id> of <host> cannot finish on its own. run carlos move resolve --host <host> --declare-lost <box> to move <host> back to <from>. | approved |
Task 7's row is T7's reason under the claim (Paul's T7 ruling of 2026-10-12). With host claims on, T7 stages the source's release on the target and checks it, and leaves the publication to the landing. When this box cannot write its version store, the release is not staged and there is no root stage pass to relay it to, so carlos move status shows MV-N455 while the move prepares.
| id | file | text | status | |---|---|---|---| | MV-N455 | internal/host/movesreport.go | the release is not staged on this box yet | approved |
Task 6's two rows are the prepared files manifest's refusal (C8, ruling Q3). local writes the prepared manifest create-only under the location's files/g-<op>/ and a replay reads its own bytes back; a manifest already at that key that is not this install's is not the install's to replace, so the landing rolls back (MV-N454, the sentence carlos move status shows, beside MV-N180 for an occupied files directory) and a dirty return goes stuck (MV-N456, beside MV-N307). MV-N457 to MV-N459 are reserved and unused.
| id | file | text | status | |---|---|---|---| | MV-N454 | internal/provision/provision.go | the destination already holds a files manifest this move did not write | approved | | MV-N456 | internal/provision/provision.go | the source already holds a files manifest the return did not write | approved |
Region HA completion barrier
Paul's copy review of 2026-10-12 (region HA stage B, final review minor 9). The move door now refuses with its own sentence when the instance's last relocation has completed but its box has not yet discharged it (hostclaim.ErrRelocationFinishing, which wraps ErrPendingChore). MV-N30 is unchanged and still covers every other pending chore. The row was first numbered MV-N450; moves reserved MV-N450 to MV-N499 for its post-Plan 12 rows, so it is MV-N500, the first number past that range (same text, same approval).
| id | file | text | status | |---|---|---|---| | MV-N500 | internal/console/apiinstancemove.go | the instance's last relocation is still finishing. try again in a few minutes | approved |
The five rows added after Plan 12 (MV-N450, MV-N451, MV-N454, MV-N455 and MV-N456) were approved by Paul on 2026-10-13 with no edits.
Files manifest past the path cap
Paul's ruling of 2026-10-15 on amadan #236 (Astra's P2 on a8cebad). The files readers refuse, terminally, a manifest with an entry path longer than 4095 bytes (hostfiles.ErrTooDeep), and a manifest written before that cap can still hold one. A recovery after the cut that selected it could not restore the source, so the source's round refuses the move before any staging write, stop or cut, and writes MV-N501 into S1, where carlos move status shows it. MV-N501 is the first number past MV-N500.
| id | file | text | status | |---|---|---|---| | MV-N501 | internal/host/movesphase1.go | <host> has a files manifest with a path longer than 4095 bytes, so it cannot move. an operator must replace the manifest | approved |
Fix round 1 (Astra's second P2 on #236). The same cap applies to the files directory itself. The archive format carries a path of up to 4096 bytes (MV-N81), but the target stages every file into a prepared files manifest after the cut, and that refuses a path longer than 4095 bytes, so the landing would fail. The source's archive now refuses a path of exactly 4096 bytes at S1 with MV-N502, before the cut. A longer path still gets MV-N81. MV-N501 names a manifest, not a file, so it does not fit here.
| id | file | text | status | |---|---|---|---| | MV-N502 | internal/host/movesphase1.go | <host> has a file whose path is longer than 4095 bytes, so it cannot move. shorten the path or remove the file | approved |
Moves into home (part 2)
Plan docs/superpowers/plans/2026-10-15-moves-home-region-part2.md (amadan #236). Its rows take MV-N510 to MV-N529, each a pending row in the commit of the task that writes it. Task 1's row is T3's new kind. A home box sets no CARLOS_REGION, so a move into home fails T3 only when the target does set one: that box is not the home box. region-present is a fill-in inside MV-N129's list (<NAME (kind)>, ...), so it has its own row, as MV-N146's fill-in does.
| id | file | text | status | |---|---|---|---| | MV-N512 | internal/moves/envreport.go (a T3 kind inside MV-N129's list) | region-present | approved |
Task 2b's row (fix round 3) is T3's refusal of a version 1 env report. A v1 report carries no instance layer names, so it cannot say whether a box variable such as HOSTNAME came from the instance's files or the box: a target on this release refuses it before comparing anything, fail closed, until the source runs this release too.
| id | file | text | status | |---|---|---|---| | MV-N513 | internal/host/movesreport.go | the source reported its launch environment in an older format | approved |
Task 5's rows are the Phase 1 request door's two new refusals of a home target. MV-N510 is Ruling R1: only a host-claim console admits a move into home, so the plain path refuses it before any read or write. MV-N511 is a box that sets CARLOS_REGION to the home's name. The console reads that box as home but the box does not, so it cannot take the move; the door names it before the staging check, which would otherwise answer MV-N20 for a bucket the box may well publish.
Task 5 withdraws MV-N21 ("moves to the home region are not supported yet. the home publishes no staging lane"), withdrawal approved by Paul 2026-10-15. The Phase 1 door no longer speaks it: MV-N510 replaces it on the plain path, and a host-claim console admits the move. The legacy door (switch off) keeps main's em-dash sentence, which the gate exempts as main's own (movesLegacyFuncs) until Plan 6 Task 8 retires that door. MV-N2, MV-N5, MV-N20, MV-N23, MV-N30 and MV-N177 keep their approved text. Only their fills change: <to> (and MV-N20's <list>) now name the home by its configured name instead of the empty code. MV-N16 already did.
| id | file | text | status | |---|---|---|---| | MV-N510 | internal/console/apiinstancemove.go | moves into the home region need host claims. this console runs without them | approved | | MV-N511 | internal/console/apiinstancemove.go | <box> sets CARLOS_REGION, so it cannot take a move into the home region | approved |
Offline operator move
Plan docs/superpowers/plans/2026-10-20-offline-operator-move.md. The rows take MV-O1 to MV-O43, each pending until Paul reviews them, and merge waits for that. MV-O1 to MV-O10 are carlos move offline output (move.go, Task 7); MV-O20 to MV-O31 are the console doors' refusals (internal/console/apiinstancemove_offline.go, Task 6); MV-O32 and MV-O33 are the two paragraphs of the docs page (Task 7). MV-O34 to MV-O43 are the refusals of the record's offline transitions (internal/provision/offline_move.go), which a console door maps onto its own rows or sends as they are; they are not free text. MV-O11 to MV-O19 are not used. The sentences the offline verbs share with the move already reviewed keep their ids and approved text, and only their fills change: M-C1 (Moving <host> from <from> to <to>.), M-C10 with <from> filled in (Waiting for <box> in <from> to confirm it has stopped <host>. <host> is offline.), M-C6, M-C7, and M-C9. <from> is the home's configured name, or the word home when none is configured. Door sentences are lower case, as the move door's are.
| id | file | text | status | |---|---|---|---| | MV-O1 | move.go | <host> is offline from now until the move opens or is aborted. | approved | | MV-O2 | move.go | Every box in <from> has stopped <host>. Run carlos move offline seal --host <host> to copy it. | approved | | MV-O3 | move.go | Copying <host> to <to>. | approved | | MV-O4 | move.go | Copied and checked. Run carlos move offline flip --host <host> to start it in <to>. | approved | | MV-O5 | move.go | <to> holds everything <host> needs. Nothing is served yet. Run carlos move offline open --host <host> to serve it from <to>. | approved | | MV-O6 | move.go | <to> could not take <host>: <reason>. Run carlos move offline abort --host <host> to move it back. | approved | | MV-O7 | move.go | <host> is served from <to>. Run carlos move offline finish --host <host> once <from> has cleaned up. | approved | | MV-O8 | move.go | Moving <host> back to <from>. | approved | | MV-O9 | move.go | The move could not finish a step: <reason>. Run carlos move offline status --host <host> for each item. | approved | | MV-O10 | move.go | Waiting for <box> to clean up <host>. | approved | | MV-O20 | internal/console/apiinstancemoveoffline.go | offline moves are not available while host claims or Phase 1 moves are on | approved | | MV-O21 | internal/console/apiinstancemoveoffline.go | <box> cannot take part in an offline move. it runs an older release or has host claims on | approved | | MV-O22 | internal/console/apiinstancemoveoffline.go | <region> has <n> boxes. an offline move needs exactly one box in the region it moves to | approved | | MV-O23 | internal/console/apiinstancemoveoffline.go | <box> does not hold this deployment's secrets key | approved | | MV-O24 | internal/console/apiinstancemoveoffline.go | <host> has no offline move in flight | approved | | MV-O25 | internal/console/apiinstancemoveoffline.go | this move is <state>. run carlos move offline status --host <host> to see what it waits for | approved | | MV-O26 | internal/console/apiinstancemoveoffline.go | <host> keeps local data on more than one box: <boxes>. remove the stale copy first | approved | | MV-O27 | internal/console/apiinstancemoveoffline.go | <host> is already served from <to>. move it back with a new move | approved | | MV-O28 | internal/console/apiinstancemoveoffline.go | <box> keeps a sidecar file for <host>. instances with sidecars cannot move yet | approved | | MV-O29 | internal/console/apiinstancemoveoffline.go | <host> has a host claim. offline moves are only for hosts without one | approved | | MV-O30 | internal/console/apiinstancemoveoffline.go | <other> is moving offline. move one instance at a time | approved | | MV-O31 | internal/console/apiinstancemoveoffline.go | <box> still runs a process for <host>. wait for it to stop, then flip | approved |
The docs page (docs/site/cli.md, under ### carlos move) gets two paragraphs and one block of commands. The block is commands, not prose, and is not a row. The docsite gate reads it.
| id | file | text | status | |---|---|---|---| | MV-O32 | docs/site/cli.md | carlos move offline moves one instance with planned downtime, one step at a time. It is for operators, works with host claims and Phase 1 moves off, and never acts on its own: each verb does one step and says which verb comes next. | approved | | MV-O33 | docs/site/cli.md | The instance is offline from start until open. Until open, abort puts it back where it was with nothing lost. After open, moving it back is a new move. | approved |
The offline transitions' refusals (fix round 1, review M4). A refusal that names no value is a whole sentence; the ones that name a value are one format. The no-move, same-region, malformed-start, replica and in-flight refusals reuse the existing rows (MV-N45, MV-N16, MV-N14, MV-N44, MV-N43).
| id | file | text | status | |---|---|---|---| | MV-O34 | internal/provision/offlinemove.go | this is not an offline move | approved | | MV-O35 | internal/provision/offlinemove.go | the record carries a different offline move | approved | | MV-O36 | internal/provision/offlinemove.go | this offline move is <state>. this step follows <state> | approved | | MV-O37 | internal/provision/offlinemove.go | a seal names the box that seals and the boxes that stopped the instance. the box that seals is one of them | approved | | MV-O38 | internal/provision/offlinemove.go | this offline move is sealed by <box> | approved | | MV-O39 | internal/provision/offlinemove.go | the record's region is <region>, which this step does not expect. the instance changed under the move | approved | | MV-O40 | internal/provision/offlinemove.go | this offline move is <state>. it cannot be aborted once it is open | approved | | MV-O41 | internal/provision/offlinemove.go | this offline move is <state>. it finishes from finishing or reverting | approved | | MV-O42 | internal/provision/offlinemove.go | this is an offline move. use carlos move offline | approved | | MV-O43 | internal/provision/offlinemove.go | the instance is in <region>, not <from> | approved |
Two more refusals of the console doors (Task 6), for cases MV-O20 to MV-O31 do not cover: the start door's class rail for a steered or unit-backed instance, and its rule that the region the instance moves to must not already list the host.
| id | file | text | status | |---|---|---|---| | MV-O50 | internal/console/apiinstancemoveoffline.go | <host> is steered or runs under a unit. offline moves cannot carry either yet | approved | | MV-O51 | internal/console/apiinstancemoveoffline.go | <box> already serves <host>. an offline move needs the region it moves to to be empty of it | approved |
Seven more lines of carlos move offline (move.go, Task 7), for places MV-O1 to MV-O10 do not reach: what abort says once every box has put the instance back (the plan's runbook has the operator run finish next, and no row said so), the lines status prints for each box and for each manifest item (<status> is ok, waiting, failed or unchecked; <detail> and <phase> are box diagnostics, not copy; an item with no detail uses the shorter line), the usage line, and the line status prints for a path a source box could not park and left where it was (MV-O58; <reason> is the box's own reason, not copy). A box whose report is not current says so: flip and open refuse on a stale report, so status has to show it. MV-O56 was the usage line before stage; MV-O181 replaced it, and Luna approved its withdrawal on 2026-10-21.
| id | file | text | status | |---|---|---|---| | MV-O52 | move.go | Every box has put <host> back in <from>. Run carlos move offline finish --host <host> to end the move. | approved | | MV-O53 | move.go | <box> is <phase> as the <role>. | approved | | MV-O54 | move.go | <box> is <phase> as the <role>. Its report is not current. | approved | | MV-O55 | move.go | Item <id> is <status>: <detail>. | approved | | MV-O57 | move.go | Item <id> is <status>. | approved | | MV-O58 | move.go | <box> left <path> in place: <reason>. | approved | | MV-O65 | move.go | <box> could not clean up <host>: <reason>. Run carlos move offline status --host <host> for each item. | approved |
The source box's reasons for a path it left in place (internal/host/movesoffline.go, retire), which carlos move offline status shows as MV-O58's <reason>. They are human text the box writes into its parked report. The two that end in the box's own OS error (<error>, a diagnostic, not copy) are recorded for their fixed beginning.
| id | file | text | status | |---|---|---|---| | MV-O59 | internal/host/movesoffline.go | root-owned: listed for the census, not renamed | approved | | MV-O60 | internal/host/movesoffline.go | the mount view of the instance directory: it stays, empty | approved | | MV-O61 | internal/host/movesoffline.go | the instance directory stays, empty | approved | | MV-O62 | internal/host/movesoffline.go | not renamed yet: the stop could not be confirmed this pass | approved | | MV-O63 | internal/host/movesoffline.go | cannot be renamed: <error> | approved | | MV-O64 | internal/host/movesoffline.go | not renamed yet: <error> | approved |
The sealer's lost-stop page (internal/host/movesoffline_seal.go, Task 4 fix round 1, review I2 and M2). After the seal the stop is kept, never taken again; when it no longer holds, the sealer pages an operator once per move. The body splits by state: before open, abort puts the instance back with nothing lost; from open on, abort is refused (MV-O40) and the move is fixed forward by hand. <state> is the record's offline state (offline-sealed, offline-open and so on). <cause> is one of the two causes below, and the failed item's reason (MV-O71) carries the same cause. <lease> is one of the four lease states MV-O72 to MV-O75; <owner> is a lease owner (moved:<id> or a box's lease id) and <time> an RFC 3339 time.
| id | file | text | status | |---|---|---|---| | MV-O66 | internal/host/movesofflineseal.go | offline move of <host> lost its stop | approved | | MV-O67 | internal/host/movesofflineseal.go | The offline move <id> of <host> is <state>, and its stop no longer holds: <cause>. The move cannot go on. Read carlos move offline status --host <host> and abort the move. | approved | | MV-O68 | internal/host/movesofflineseal.go | The offline move <id> of <host> is <state>, and its stop no longer holds: <cause>. It cannot be aborted once it is open. Read carlos move offline status --host <host> and follow the offline move runbook. | approved | | MV-O69 | internal/host/movesofflineseal.go | the source's lease is <lease>, not this move's tombstone | approved | | MV-O70 | internal/host/movesofflineseal.go | <box> reports TXID <n>, past the sealed position <final> | approved | | MV-O71 | internal/host/movesofflineseal.go | lost stop: <cause> | approved | | MV-O72 | internal/host/movesofflineseal.go | absent | approved | | MV-O73 | internal/host/movesofflineseal.go | lifted (<owner>) | approved | | MV-O74 | internal/host/movesofflineseal.go | expired (<owner> at <time>) | approved | | MV-O75 | internal/host/movesofflineseal.go | held by <owner> until <time> | approved |
The sealer's item reasons, which carlos move offline status shows as MV-O55's <detail>. The copy gate reads every Check reason, so each has a row. MV-O76 is the files refusal for an entry the app keeps beside its files directory (review I3): the move cannot carry it, and moving it into the files directory or removing it would lose the app's data, so the row offers neither and points to the runbook; <paths> are the entries' paths on the box. MV-O77 to MV-O84 are the other refusals. Two refusals carry an error's own text and are box diagnostics, not rows (movesDiagnosticWrites): the archive's own refusal (its sentinels are MV-N77 to MV-N81 and MV-N86) and a secrets key that does not decode. MV-O85 to MV-O98 are the reasons of the items that passed; <rung> is the replication rung, quoted.
| id | file | text | status | |---|---|---|---| | MV-O76 | internal/host/movesofflineseal.go | <host> keeps <paths> beside its files directory, and this move can carry only the files directory. see the offline move runbook | approved | | MV-O77 | internal/host/movesofflineseal.go | <box> keeps a sidecar file for <host> | approved | | MV-O78 | internal/host/movesofflineseal.go | this box's local position <n> is past the replica's <replica> and there is no database at <path> to checkpoint | approved | | MV-O79 | internal/host/movesofflineseal.go | the replica at <bucket>/<prefix> holds no database | approved | | MV-O80 | internal/host/movesofflineseal.go | <box>'s local position <n> is past the replica's final position <final> | approved | | MV-O81 | internal/host/movesofflineseal.go | this box has no exec backend to compose the launch environment | approved | | MV-O82 | internal/host/movesofflineseal.go | this box holds no CARLOSSECRETSKEY to key the env report | approved | | MV-O83 | internal/host/movesofflineseal.go | no build is live for <host> on this box | approved | | MV-O84 | internal/host/movesofflineseal.go | this box holds no CARLOSSECRETSKEY to seal the archive under | approved | | MV-O85 | internal/host/movesofflineseal.go | no source box keeps a sidecar file | approved | | MV-O86 | internal/host/movesofflineseal.go | every source box is at or below TXID <final> | approved | | MV-O87 | internal/host/movesofflineseal.go | <owner> holds <location>/lock.json | approved | | MV-O88 | internal/host/movesofflineseal.go | the request record is in the build state the replica prefix carries | approved | | MV-O89 | internal/host/movesofflineseal.go | this box keeps no request records; the build state travels as the replica prefix holds it | approved | | MV-O90 | internal/host/movesofflineseal.go | <n> objects staged at <bucket>/<prefix>, newest TXID <final> | approved | | MV-O91 | internal/host/movesofflineseal.go | <n> files, <bytes> bytes, sealed at <prefix><object> | approved | | MV-O92 | internal/host/movesofflineseal.go | <n> active timers at <prefix><object> | approved | | MV-O93 | internal/host/movesofflineseal.go | absent on the source, so none is carried | approved | | MV-O94 | internal/host/movesofflineseal.go | carried in the archive | approved | | MV-O95 | internal/host/movesofflineseal.go | launch environment digest <digest> | approved | | MV-O96 | internal/host/movesofflineseal.go | live build <version> | approved | | MV-O97 | internal/host/movesofflineseal.go | replication "<rung>" | approved | | MV-O98 | internal/host/movesoffline_seal.go | next due <unix time> | approved |
The landing's item reasons (internal/host/movesoffline_land.go, Task 5 fix round 1), which carlos move offline status shows as MV-O55's <detail> for the target's items. The copy gate reads every Check reason, so each has a row. A reason never carries an error's text: the error behind a reason that waits or fails is the box's log line. <names> is the list of variables a failed env pin finds different, each as <name> (<kind>) from moves.CompareEnv, never a value. MV-O110 to MV-O177. MV-O177 is the release item's failure when the target box keeps a live link for the host that names another build than the sealed one (final review, return moves).
| id | file | text | status | |---|---|---|---| | MV-O110 | internal/host/movesofflineland.go | a stale tombstone in the target's bucket could not be lifted | approved | | MV-O111 | internal/host/movesofflineland.go | a writer holds the target's lock | approved | | MV-O112 | internal/host/movesofflineland.go | after extraction the files are not the seal's | approved | | MV-O113 | internal/host/movesofflineland.go | next due could not be set | approved | | MV-O114 | internal/host/movesofflineland.go | next due reads back differently from the seal's | approved | | MV-O115 | internal/host/movesofflineland.go | no registry on this box | approved | | MV-O116 | internal/host/movesofflineland.go | no row yet: reconcile mints it held | approved | | MV-O117 | internal/host/movesofflineland.go | the archive could not be extracted | approved | | MV-O118 | internal/host/movesofflineland.go | the archive could not be read | approved | | MV-O119 | internal/host/movesofflineland.go | the archive does not verify | approved | | MV-O120 | internal/host/movesofflineland.go | the archive is not the one the seal names | approved | | MV-O121 | internal/host/movesofflineland.go | the channel could not be resolved | approved | | MV-O122 | internal/host/movesofflineland.go | the channel points at another build than the sealed one | approved | | MV-O123 | internal/host/movesofflineland.go | the config ack could not be read | approved | | MV-O124 | internal/host/movesofflineland.go | the config ack does not name this move and the rendered file | approved | | MV-O125 | internal/host/movesofflineland.go | the config could not be rendered | approved | | MV-O126 | internal/host/movesofflineland.go | the config file could not be read | approved | | MV-O127 | internal/host/movesofflineland.go | the config file does not hold the rendered bytes | approved | | MV-O128 | internal/host/movesofflineland.go | the config was rendered at another path | approved | | MV-O129 | internal/host/movesofflineland.go | the env file could not be read | approved | | MV-O130 | internal/host/movesofflineland.go | the env file is missing | approved | | MV-O131 | internal/host/movesofflineland.go | the env file is not the source's | approved | | MV-O132 | internal/host/movesofflineland.go | the env file the target held could not be parked | approved | | MV-O133 | internal/host/movesofflineland.go | the files directory the target held could not be parked | approved | | MV-O134 | internal/host/movesofflineland.go | the instance key is not the source's | approved | | MV-O135 | internal/host/movesofflineland.go | the launch environment could not be composed | approved | | MV-O136 | internal/host/movesofflineland.go | the launch environment differs from the source's | approved | | MV-O137 | internal/host/movesofflineland.go | the launch environment differs from the source's in <names> | approved | | MV-O138 | internal/host/movesofflineland.go | the record and the seal disagree on this item | approved | | MV-O139 | internal/host/movesofflineland.go | the record carries no seal evidence | approved | | MV-O140 | internal/host/movesofflineland.go | the replication rung could not be set | approved | | MV-O141 | internal/host/movesofflineland.go | the replication rung reads back differently from the seal's | approved | | MV-O142 | internal/host/movesofflineland.go | the row is held by another operation | approved | | MV-O143 | internal/host/movesofflineland.go | the row is not held landing by this move yet | approved | | MV-O144 | internal/host/movesofflineland.go | the row's paths are nested, and an offline move lands flat rows only | approved | | MV-O145 | internal/host/movesofflineland.go | the seal could not be read | approved | | MV-O146 | internal/host/movesofflineland.go | the seal does not decode | approved | | MV-O147 | internal/host/movesofflineland.go | the seal is missing | approved | | MV-O148 | internal/host/movesofflineland.go | the seal is not a valid seal of this move | approved | | MV-O149 | internal/host/movesofflineland.go | the seal is not the one the record names | approved | | MV-O150 | internal/host/movesofflineland.go | the seal staged nothing | approved | | MV-O151 | internal/host/movesofflineland.go | the staged replica could not be listed | approved | | MV-O152 | internal/host/movesofflineland.go | the staged replica could not be verified this pass | approved | | MV-O153 | internal/host/movesofflineland.go | the staged replica does not end at the sealed position | approved | | MV-O154 | internal/host/movesofflineland.go | the staged replica does not restore clean | approved | | MV-O155 | internal/host/movesofflineland.go | the staged replica is not the one the seal staged | approved | | MV-O156 | internal/host/movesofflineland.go | the target's lock could not be read | approved | | MV-O157 | internal/host/movesofflineland.go | the target's lock is still held after the lift | approved | | MV-O158 | internal/host/movesofflineland.go | the target's lock is this move's own tombstone | approved | | MV-O159 | internal/host/movesofflineland.go | the timers could not be read | approved | | MV-O160 | internal/host/movesofflineland.go | the timers could not be replaced | approved | | MV-O161 | internal/host/movesofflineland.go | the timers read back differently from the seal's | approved | | MV-O162 | internal/host/movesofflineland.go | the timers snapshot could not be read | approved | | MV-O163 | internal/host/movesofflineland.go | the timers snapshot is not the one the seal names | approved | | MV-O164 | internal/host/movesofflineland.go | this box cannot resolve channels | approved | | MV-O165 | internal/host/movesofflineland.go | this box has no exec backend to compose the launch environment | approved | | MV-O166 | internal/host/movesofflineland.go | this box has no held render | approved | | MV-O167 | internal/host/movesofflineland.go | this box holds no CARLOSSECRETSKEY to key the env report | approved | | MV-O168 | internal/host/movesofflineland.go | this box holds no CARLOSSECRETSKEY to open the archive with | approved | | MV-O169 | internal/host/movesofflineland.go | waits for the config item: the instance key is read from both files | approved | | MV-O170 | internal/host/movesofflineland.go | waits for the files and config items | approved | | MV-O171 | internal/host/movesofflineland.go | waits for the files item | approved | | MV-O172 | internal/host/movesofflineland.go | a path the target held for the host could not be parked | approved | | MV-O173 | internal/host/movesofflineland.go | a path the target held for the host is still there | approved | | MV-O174 | internal/host/movesofflineland.go | not checked: the row is not held for this move | approved | | MV-O175 | internal/host/movesofflineland.go | not checked: the seal could not be used | approved | | MV-O176 | internal/host/movesofflineland.go | the row could not be read back | approved | | MV-O177 | internal/host/movesofflineland.go | the target box still holds an older build of this instance. see the offline move runbook | approved |
A door's answer to a box report it cannot believe for its age (harness Run 9, finding 7). flip answered MV-O31 for a source report older than two minutes, and an operator who reads "still runs a process" goes looking for a process that is not there. seal, flip and open now answer MV-O178 for any report that is there but stale, the sealer's included; a report that is missing keeps M-C10. A box taking part in a move rewrites its report about every 15 seconds, so the step is retried rather than looked into. <box> is the box whose report it is. Pending until Paul reviews it.
| id | file | text | status | |---|---|---|---| | MV-O178 | internal/console/apiinstancemove_offline.go | the report from <box> is not current. it reports again within a minute. try again then | approved |
Offline move: stage (pre-copy)
carlos move offline stage copies the instance's replica to the region it moves to while it keeps serving, so the seal has less to copy with the instance offline (branch moves-offline-precopy). Paul handed the review of these rows to Luna. Luna approved MV-O179 to MV-O189 on 2026-10-21, with two rewrites applied as given (MV-O182 and MV-O183).
MV-O179 and MV-O180 are console door refusals (internal/console/apiinstancemove_offline.go), lower case like the other door sentences. MV-O179 answers a stage or start that names another region than the one the instance is staged for (<to> is that region's name). MV-O180 is the stage door's own box rule: every box in both regions must publish offline-stage-1.
MV-O181 is the usage line with stage added. It replaces MV-O56, whose withdrawal Luna approved with it. MV-O182 to MV-O188 are carlos move offline output (move.go): what stage says when it posts and when its round has run, what status says while the move is staging, and what abort says for a move that was staged and never started (nothing is moved back, only the staged copy is removed). In MV-O183, <staged> and <total> are object counts, <staged_mb> and <total_mb> are megabytes with one decimal, and <time> is the round's end as 2006-01-02 15:04 UTC. In MV-O185, <reason> is the stager box's own diagnostic, not copy. MV-O189 is the CLI page's paragraph for stage.
| id | file | text | status | |---|---|---|---| | MV-O179 | internal/console/apiinstancemoveoffline.go | <host> is staged for <to>. abort the move to move it to another region | approved | | MV-O180 | internal/console/apiinstancemoveoffline.go | <box> cannot stage an offline move. it runs an older release | approved | | MV-O181 | move.go | carlos move offline needs stage, start, seal, flip, open, finish, abort or status. | approved | | MV-O182 | move.go | Staging <host> in <to>. <host> keeps serving while the data is copied. | approved | | MV-O183 | move.go | Staged <staged> of <total> objects for <host> in <to> (<stagedmb> of <totalmb> MB) at <time>. | approved | | MV-O184 | move.go | <host> keeps serving. Run carlos move offline start --host <host> --to <to> when you are ready to take it offline. | approved | | MV-O185 | move.go | The last staging round failed: <reason>. Run carlos move offline stage --host <host> --to <to> to try again. | approved | | MV-O186 | move.go | Waiting for the first staging round of <host>. | approved | | MV-O187 | move.go | Removing the staged copy of <host> from <to>. | approved | | MV-O188 | move.go | The staged copy of <host> is removed. Run carlos move offline finish --host <host> to end the move. | approved | | MV-O189 | docs/site/cli.md | stage is optional and comes before start. It copies the instance's data to the region it moves to while the instance keeps serving, and each time it runs it copies only what is new. The more it has copied, the less is left to copy while the instance is offline. Until start, abort removes the staged copy and the instance is never offline. | approved |
Three more console door refusals for a staged move (internal/console/apiinstancemove_offline.go, Astra round 2), approved by Luna 2026-10-21 (MV-O192 as Luna rewrote it). MV-O190 is what start and seal answer when the box that staged the instance (the stager, pinned on the record at the stage) no longer publishes an edges file in the source region: its absence is not proof that it stopped copying, so the step waits for it or the operator aborts. <box> is the stager, <from> the source region's name. MV-O191 is the stage door's refusal of a target whose data bucket is a source box's own, where a staged copy would replace the serving replica. MV-O192 is what stage, start and seal answer once the target publishes another data bucket or region than the one the stage fixed on the record: the staged copy is in the stored bucket and is never followed elsewhere.
| id | file | text | status | |---|---|---|---| | MV-O190 | internal/console/apiinstancemoveoffline.go | <box>, which staged <host>, no longer publishes an edges file in <from>. bring it back or abort the move | approved | | MV-O191 | internal/console/apiinstancemoveoffline.go | <to> publishes the same data bucket as <from>. a staged copy there would replace the serving one | approved | | MV-O192 | internal/console/apiinstancemove_offline.go | <to> now publishes a different data bucket or region from what was recorded when <host> was staged. abort the move and stage it again | approved |