| 1 | # Review brief: `carlos run` and seeded instances |
| 2 | |
| 3 | You are an independent second reader of two design specs for the CARLOS |
| 4 | platform (this repository). Read them cold and adversarially. |
| 5 | |
| 6 | **Specs:** read both, at the commit named in the prompt: |
| 7 | |
| 8 | - `docs/superpowers/specs/2026-10-08-carlos-run-design.md` (#259) |
| 9 | - `docs/superpowers/specs/2026-10-08-seeded-instances-design.md` (#260, |
| 10 | #261) |
| 11 | |
| 12 | ## Fixed rulings: do not re-argue them |
| 13 | |
| 14 | §2 of each spec lists the rulings the product owner made on 2026-10-08. |
| 15 | Do not propose alternatives to them. That includes: |
| 16 | |
| 17 | - member-wide access; |
| 18 | - service credentials with the `instance` role on the seed doors; |
| 19 | - output as bucket chunks; |
| 20 | - uploads streaming through the console; |
| 21 | - the operator-account exception for the swap. |
| 22 | |
| 23 | Report only: |
| 24 | |
| 25 | - a mechanism that **fails to honour** a ruling; |
| 26 | - a mechanism that breaks something else in the platform; |
| 27 | - a mechanism that cannot be built as described against the code on |
| 28 | `main`. |
| 29 | |
| 30 | ## What to check |
| 31 | |
| 32 | Check against the code, not the specs' description of it. Cite |
| 33 | `file:line` for every claim about existing code. These are the places that |
| 34 | matter. |
| 35 | |
| 36 | **Instances and units:** |
| 37 | |
| 38 | - `internal/host/units/carlos-instance-seed@.service` and its two |
| 39 | siblings; |
| 40 | - `internal/instancectl/` (names.go, server.go `/seed`, client.go); |
| 41 | - `internal/instanceexec/exec.go`. |
| 42 | |
| 43 | **The activator:** |
| 44 | |
| 45 | - `internal/activator/backend_exec.go`: `Restore`, the staging |
| 46 | fallback, `seedUnit`, `seedVars`, `instanceEnv`, `nestedGate`; |
| 47 | - `prepare.go` (`writeLaunch`); |
| 48 | - `layout.go` (`Classify`); |
| 49 | - `claimwake.go`; |
| 50 | - hibernation and the `Quiesced` and idle logic in `activator.go`. |
| 51 | |
| 52 | **The box:** |
| 53 | |
| 54 | - `internal/host/restart.go`: the serial and ETag pattern the run pickup |
| 55 | copies; |
| 56 | - `logship.go`; |
| 57 | - `agent.go`: the serial poll. |
| 58 | |
| 59 | **The console:** |
| 60 | |
| 61 | - `internal/console/apidb.go`: `dbGate`, `resolveReplica`; |
| 62 | - `internal/dbdoor/restore.go`; |
| 63 | - `apiinstances.go`: `handleAPIInstanceCreate`; |
| 64 | - `authcli.go`: `cliRoute`; |
| 65 | - `apicli.go`: `apiAccountApp`, the ship size limits; |
| 66 | - `server.go`: audit helpers; |
| 67 | - `apistatuslayout.go`. |
| 68 | |
| 69 | **Provisioning, claims and storage:** |
| 70 | |
| 71 | - `internal/provision/provision.go`: `Record`, `CreateRecord`; |
| 72 | - `reconcile.go`: around lines 1140-1300, the "leave it alone" rule; |
| 73 | - the host claim: `internal/hostclaim`, `internal/hostbox`, |
| 74 | `internal/hostops/record.go`; |
| 75 | - the moves protocol floor (`moves_protocol`); |
| 76 | - `infra/modules/region-host/main.tf`: what a box and the console may |
| 77 | read and write; |
| 78 | - `docs/superpowers/specs/2026-10-02-move-any-app-design.md`: the claim |
| 79 | and restore contract a new restore fallback must fit. |
| 80 | |
| 81 | ## Questions worth your time |
| 82 | |
| 83 | **`carlos run`** |
| 84 | |
| 85 | 1. Can a run reach anything beyond the instance's own reach? |
| 86 | - Environment, files, the cmd unit's sandbox against the seed unit's. |
| 87 | - The exe link the control service binds. |
| 88 | - The output sockets. |
| 89 | - `.carlos-run/` inside the instance tree. |
| 90 | 2. **Data loss.** Can the command's writes be lost, or can it write to a |
| 91 | database nobody replicates? Look at: |
| 92 | - hibernation during a run; |
| 93 | - a move; |
| 94 | - a host-claim handoff; |
| 95 | - a restore that replaces the file under it; |
| 96 | - the run hold as specified. |
| 97 | 3. **Pickup.** Does "the box that owns the row" pick it up exactly once? |
| 98 | Consider: |
| 99 | - the claim on and off; |
| 100 | - replica placements; |
| 101 | - two boxes during a move; |
| 102 | - a box restart mid-run; |
| 103 | - the index ETag pattern against restart.go's seed-don't-replay rule. |
| 104 | 4. **Output.** Ordering, the cap, cancel, timeout, ack and the sweep. Can |
| 105 | a run be left open forever, or deleted while the CLI still follows it? |
| 106 | |
| 107 | **Seeded instances** |
| 108 | |
| 109 | 5. **The new restore fallback.** Does it fit both the plain and the claim |
| 110 | restore paths, as the move-any-app spec's generation and marker |
| 111 | contract requires? Can a seeded instance ever come up empty, come up |
| 112 | with the source's lineage, or replicate into the source's prefix? |
| 113 | 6. **The swap.** Is a registry transaction plus a claim CAS, in that |
| 114 | order, actually achievable in reconcile? Can it take a route that |
| 115 | isn't the app's own? Does abort-after-swap restore the addr row |
| 116 | exactly, and is "refused once it has served a non-verify request" |
| 117 | observable by the platform at all? |
| 118 | 7. **Old boxes.** Does the protocol-floor gate stop an old box minting a |
| 119 | seeded record as an empty instance? Is there a window where it |
| 120 | doesn't? |
| 121 | 8. **`--from` on the console.** Disk, memory and time per restore; the |
| 122 | claim read; residency; scratch cleanup on every exit path. |
| 123 | 9. **Missing pieces.** Anything either spec relies on that doesn't exist, |
| 124 | or exists in a different shape. |
| 125 | |
| 126 | ## Output |
| 127 | |
| 128 | A numbered list of findings. Each one gives: |
| 129 | |
| 130 | - **severity:** P1 breaks a ruling, loses data or crosses a tenant; P2 is |
| 131 | wrong behaviour; P3 is clarity or a gap; |
| 132 | - the spec and section; |
| 133 | - the code evidence, with `file:line`; |
| 134 | - a concrete fix. |
| 135 | |
| 136 | Then a one-line verdict for each spec: accept, or revise. Don't rewrite |
| 137 | the specs. No praise. |
| 138 | |
| 139 | On a later round you'll be given your earlier findings files. Start with a |
| 140 | disposition table (finding: fixed, declined with an acceptable reason, or |
| 141 | not fixed), then give only new findings. |
| 142 | |