docs: cohort-1 step 4 done: orphan row, corrected flow, cert step
The pool change leaves leo's already-minted wakecheck row as an orphan (the removal phase skips out-of-scope rows); Paul approved a one-off carlos remove on leo. instances create is signed-in only, so enable in store mode and create through the dev console with --channel edge. A host homed only on a non-issuer box needs one TLS hello to the issuer before --resolve writes work. claimtrial serves on leo only: three 200s at hits 3, 4 and 5 (two probe rows from the first wake).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 file changed,
+38
−7
docs/superpowers/plans/2026-10-16-claim-activation-dev-cohort1.md+38 −7
CI cancelled — superseded by a newer push — run details
CI log
=== gate === cd launchctx && go vet ./... && go test ./... -count=1 ok amadan.net/carlos/platform.git/launchctx 0.011s ? amadan.net/carlos/platform.git/launchctx/controlname [no test files] ? amadan.net/carlos/platform.git/launchctx/wireid [no test files] go vet ./... gofmt -l .: clean go test ./... -count=1 -timeout 30m ok github.com/carlosframework/platform 323.845s ? github.com/carlosframework/platform/cmd/changelog [no test files] ok github.com/carlosframework/platform/cmd/console 2.859s ok github.com/carlosframework/platform/cmd/moveprobe 93.302s ok github.com/carlosframework/platform/cmd/zddprobe 0.723s ok github.com/carlosframework/platform/internal/activator 251.098s ok github.com/carlosframework/platform/internal/agentsock 0.032s ok github.com/carlosframework/platform/internal/alerting 0.003s ok github.com/carlosframework/platform/internal/analytics 0.021s ok github.com/carlosframework/platform/internal/appconfig 2.530s ok github.com/carlosframework/platform/internal/apperr 0.091s ok github.com/carlosframework/platform/internal/applog 0.173s ok github.com/carlosframework/platform/internal/awsinv 0.003s ok github.com/carlosframework/platform/internal/boxchoice 0.008s ok github.com/carlosframework/platform/internal/boxid 0.588s ? github.com/carlosframework/platform/internal/boxid/boxidtest [no test files] ? github.com/carlosframework/platform/internal/boxid/internal/signtest [no test files] ok github.com/carlosframework/platform/internal/boxvolumes 0.009s ok github.com/carlosframework/platform/internal/caps 0.021s ok github.com/carlosframework/platform/internal/certwarm 0.006s ok github.com/carlosframework/platform/internal/cgroup 0.722s ok github.com/carlosframework/platform/internal/changelog 0.339s ok github.com/carlosframework/platform/internal/claimactivation 0.032s ok github.com/carlosframework/platform/internal/clamd 0.058s ? github.com/carlosframework/platform/internal/cliflag [no test files] amadan: job cancelled — a newer push superseded it
diff --git a/docs/superpowers/plans/2026-10-16-claim-activation-dev-cohort1.md b/docs/superpowers/plans/2026-10-16-claim-activation-dev-cohort1.md| index 26f57e16b..131f10185 100644 |
| --- a/docs/superpowers/plans/2026-10-16-claim-activation-dev-cohort1.md |
| +++ b/docs/superpowers/plans/2026-10-16-claim-activation-dev-cohort1.md |
| @@ -406,22 +406,53 @@ Why a pool: dev's two home boxes both mint every unregioned, pool-less record. A |
| systemctl restart carlos-edge.service && systemctl is-active carlos-edge.service' |
| ``` |
| -3. After the next provision pass (up to 5 minutes), run P4. Expected: wakecheck is gone from leo's registry. Sophie still serves it through DNS. |
| -4. Create the disposable app `claimtrial` in account `bab`, from wakecheck's own build (version `wc4`), so each request writes one SQLite row and replies with the host and the row count (`hits=`). Ship and promote it the same way wakecheck was, in store mode, then: |
| +3. Run P4 after the next provision pass (up to 5 minutes). **Corrected 2026-10-18:** the pool change does NOT take wakecheck off leo. It stops leo minting `""`-pool records, but the reconciler's removal phase skips every row outside the box's scope (`internal/provision/reconcile.go`: `if !rt.Provisioned || !cfg.inScope(rt.Region, rt.Pool, ...) { continue }`, and `poolServed([cohort1], "")` is false). So a row leo minted before the change stays as an orphan: never converged, never retired. Clear it with a one-off removal on leo only, as the carlos user (Paul's go needed; given 2026-10-18), then re-run P4: |
| ```bash |
| - export AWS_PROFILE=kass CARLOS_DEPLOYMENT_BUCKET=carlos-dev-640012953448 |
| - carlos instances enable --app claimtrial --account bab --domain dev.carlosframework.com --pool cohort1 |
| - carlos instances create --app claimtrial --account bab --host claimtrial.dev.carlosframework.com |
| + leo 'runuser -u carlos -- carlos remove --registry /data/registry.db wakecheck.dev.carlosframework.com; systemctl is-active carlos-edge.service' |
| ``` |
| -5. After the next provision pass, the route is on leo only. Write three rows, outside the sandbox: |
| + This removes leo's registry row only. The shared record and sophie's route are untouched, and leo's stale local wakecheck database stays on disk. Expected: P4 shows no routes on leo; sophie still serves wakecheck through DNS. |
| +4. Create the disposable app `claimtrial` in account `bab`, from wakecheck's own build (version `wc4`), so each request writes one SQLite row and replies with the host and the row count (`hits=`). **Corrected 2026-10-18:** `instances create` refuses store mode (it is a console-authorized write, `requireAPISession` in `instances_cli.go`), and `instances enable --pool` is store-mode only. So enable in store mode, then create signed in with the store env unset. This machine's default console is flagship, so every signed-in command names the dev console. Store-mode enable defaults the channel to `stable`, so pass `--channel edge` to match the app's only channel. `--account bab` (the sqid) resolves without a local registry. |
| ```bash |
| + # wakecheck's shipped wc4 artifact; check it against releases/wc4/manifest.json |
| + aws --profile kass s3 cp s3://carlos-dev-640012953448/apps/bab/wakecheck/releases/wc4/linux-arm64 "$TMPDIR/cohort1/linux-arm64" |
| + sha256sum "$TMPDIR/cohort1/linux-arm64" # ee276c083a04b6de11fc81b3a219f587394efd9600b46d21185398e44a4abcbb |
| + |
| + DEV=https://platform.dev.carlosframework.com |
| + carlos apps create --app claimtrial --account bab --console $DEV |
| + carlos ship --app claimtrial --account bab --console $DEV --version wc4 \ |
| + --label "dev claim trial tenant, wakecheck wc4 build" --no-prompt "$TMPDIR/cohort1/linux-arm64" |
| + carlos promote --app claimtrial --account bab --console $DEV --no-prompt wc4 edge |
| + |
| + AWS_PROFILE=kass CARLOS_DEPLOYMENT_BUCKET=carlos-dev-640012953448 \ |
| + carlos instances enable --app claimtrial --account bab --domain dev.carlosframework.com --pool cohort1 --channel edge |
| + |
| + # signed in: CARLOS_DEPLOYMENT_BUCKET must be unset here |
| + carlos instances create --app claimtrial --account bab --host claimtrial.dev.carlosframework.com --console $DEV --no-prompt |
| + ``` |
| + |
| +5. After the next provision pass, the route is on leo only (read-only check on both boxes). **Corrected 2026-10-18:** before writing, get the host a certificate. Only the box holding the housekeeper lease (`control/housekeeper-lease/lock.json`, sophie on 2026-10-18) orders certificates, and only when a TLS hello for the name reaches it (#277). Every other edge serves only what it mirrors from `control/certs/`, so a `--resolve` to leo alone answers `tlsv1 alert internal error` (leo logs `acme: not available here`). Send one handshake-only hello to the issuer, wait for `control/certs/claimtrial.dev.carlosframework.com` and leo's 30-second mirror, then write three rows, outside the sandbox: |
| + |
| + ```bash |
| + openssl s_client -connect 99.80.78.4:443 -servername claimtrial.dev.carlosframework.com </dev/null |
| + aws --profile kass s3 ls s3://carlos-dev-640012953448/control/certs/claimtrial.dev.carlosframework.com |
| for i in 1 2 3; do curl -s --resolve claimtrial.dev.carlosframework.com:443:18.200.150.210 https://claimtrial.dev.carlosframework.com/; echo; done |
| ``` |
| - Expected: three 200 answers with `host=ip-172-31-19-180` and `hits=1`, `2`, `3`. Sophie's registry has no `claimtrial` route. |
| + Expected: three 200 answers with `host=ip-172-31-19-180` and `hits=` rising by one each time. The first values are not 1, 2 and 3: the app's first wake writes rows of its own (probe requests). Sophie's registry has no `claimtrial` route. |
| + |
| +**2026-10-18, done.** platform-d8 was told (H2) and the pool set as in 4.2 (backup `host.env.bak-pool-20261018`; the edge restarted at 09:48:57Z and logged `instances loop armed (5min + jitter), region "" pools [cohort1]`). |
| +- P4, 09:51Z to 10:00Z, polled every 30 s: wakecheck stayed on leo, `provisioned=1, pool='', region='', channel=edge, hibernate=1`, no instance unit. Leo's edges file said `serves_shared_pool: false` from 09:49:59Z. That is the orphan row described in 4.3. |
| +- Paul approved clearing it. The controller ran `carlos remove --registry /data/registry.db wakecheck.dev.carlosframework.com` on leo only, as the carlos user, which output `removed`; the edge stayed active. Sophie still served wakecheck (`build=wc4 host=ip-172-31-14-16`, hits 1037 then 1038). |
| +- P4 re-run 10:02Z: leo has 0 routes, no instance units, one carlos-user process, edge active. |
| +- claimtrial, built with the commands in 4.4 (CLI v0.28.0): the wc4 artifact's sha256 `ee276c08…4abcbb` (9437346 bytes) matched wakecheck's manifest and claimtrial's own `releases/wc4/manifest.json` (built by `console-api 3b69c2bd`, program `wakecheck`). `apps create` gave the default single `edge` channel; `ship` gave `shipped claimtrial wc4`; `promote` gave `promoted claimtrial wc4 -> edge` (10:03:13Z). `instances.json` reads `{"schema":1,"app":"claimtrial","domain":"dev.carlosframework.com","pool":"cohort1","channel":"edge"}`. `instances create` gave `created instance claimtrial.dev.carlosframework.com (channel edge, Ireland · Dublin)`, and the record carries `pool: cohort1` and no region. |
| +- Leo minted the route at 10:03:33Z: `('claimtrial.dev.carlosframework.com', 'claimtrial', provisioned=1, pool='cohort1', region='', channel='edge', hibernate=1)`. The first stage of wc4 logged `chmod /opt/carlos/versions/wc4: read-only file system` and asked for a root pass, which ran by itself: `repointed -> wc4 (edge; hibernating — runs it on next wake)`. |
| +- Sophie, 10:03:48Z: 0 claimtrial rows, no `CARLOS_POOLS` in host.env. |
| +- The first three `--resolve` curls (10:03:56Z) answered HTTP 000 with `tlsv1 alert internal error`. Leo logged `acme: not available here`, and `control/certs/` had nothing for claimtrial. Sophie held the housekeeper lease (`owner ip-172-31-14-16`). |
| +- The controller sent one handshake-only `openssl s_client` to sophie with SNI claimtrial. Sophie got a Let's Encrypt YE2 certificate and published it to `control/certs/claimtrial.dev.carlosframework.com` at 10:05:53Z. |
| +- After leo's mirror picked it up, the three `--resolve` curls to leo each answered 200 with `host=ip-172-31-19-180`, at `hits=3`, `4` and `5`. Two rows were already written when the app first woke, most likely by wake or build probe requests to `/` or `/healthz`. The count rose by one on every call and leo served all three: PASS. |
| `claimtrial` is the only host the files seed and acceptance touch. It holds nothing anyone needs. |