spec: edge asset cache rev 7 (corrections against the code)
Summary
Corrections found while planning: per-app records confirmed time from reconcile itself (record view optional), installed plus serving build for the paths union, manifests from the bucket, the full list of ReconcileAliases silent failures, carlos instances set-assets, citation fixes.
1 file changed,
+74
−31
docs/superpowers/specs/2026-10-24-edge-asset-cache-design.md+74 −31
CI passed — run details
CI log
=== gate ===
.amadan/test-gate.sh
=== launchctx (0 s)
ok amadan.net/carlos/platform.git/launchctx 0.012s
? amadan.net/carlos/platform.git/launchctx/controlname [no test files]
? amadan.net/carlos/platform.git/launchctx/wireid [no test files]
=== vet (18 s)
=== gofmt (0 s)
gofmt -l .: clean
=== rest (174 s)
? github.com/carlosframework/platform/cmd/changelog [no test files]
ok github.com/carlosframework/platform/cmd/console 3.816s
ok github.com/carlosframework/platform/cmd/moveprobe 11.864s
ok github.com/carlosframework/platform/cmd/zddprobe 0.799s
ok github.com/carlosframework/platform/internal/activator 57.717s
ok github.com/carlosframework/platform/internal/agentsock 0.027s
ok github.com/carlosframework/platform/internal/alerting 0.004s
ok github.com/carlosframework/platform/internal/analytics 0.023s
ok github.com/carlosframework/platform/internal/appconfig 3.124s
ok github.com/carlosframework/platform/internal/apperr 0.094s
ok github.com/carlosframework/platform/internal/applog 0.221s
ok github.com/carlosframework/platform/internal/awsinv 0.006s
ok github.com/carlosframework/platform/internal/boxchoice 0.008s
ok github.com/carlosframework/platform/internal/boxid 1.398s
? 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.018s
ok github.com/carlosframework/platform/internal/caps 0.024s
ok github.com/carlosframework/platform/internal/certwarm 0.004s
ok github.com/carlosframework/platform/internal/cgroup 0.831s
ok github.com/carlosframework/platform/internal/changelog 0.547s
ok github.com/carlosframework/platform/internal/claimactivation 1.263s
ok github.com/carlosframework/platform/internal/clamd 0.059s
? github.com/carlosframework/platform/internal/cliflag [no test files]
? github.com/carlosframework/platform/internal/controlname [no test files]
? github.com/carlosframework/platform/internal/controlsock [no test files]
ok github.com/carlosframework/platform/internal/dbdoor 8.176s
ok github.com/carlosframework/platform/internal/dbdoor/dbdoortest 0.017s
ok github.com/carlosframework/platform/internal/dbquery 0.008s
ok github.com/carlosframework/platform/internal/dbreads 2.098s
? github.com/carlosframework/platform/internal/dbreads/dbreadstest [no test files]
ok github.com/carlosframework/platform/internal/dbverify 0.007s
ok github.com/carlosframework/platform/internal/dnsrec 0.007s
ok github.com/carlosframework/platform/internal/docsite 0.006s
ok github.com/carlosframework/platform/internal/domains 3.135s
ok github.com/carlosframework/platform/internal/doorkey 0.016s
ok github.com/carlosframework/platform/internal/economics 0.004s
ok github.com/carlosframework/platform/internal/edge 45.792s
? github.com/carlosframework/platform/internal/edgeproof [no test files]
ok github.com/carlosframework/platform/internal/edgesfile 0.008s
ok github.com/carlosframework/platform/internal/fleetapi 0.134s
ok github.com/carlosframework/platform/internal/gate 1.656s
ok github.com/carlosframework/platform/internal/genselect 0.015s
ok github.com/carlosframework/platform/internal/geo 0.016s
ok github.com/carlosframework/platform/internal/geoip 0.030s
ok github.com/carlosframework/platform/internal/historyreadback 0.157s
ok github.com/carlosframework/platform/internal/host 144.697s
ok github.com/carlosframework/platform/internal/hostactors 6.053s
ok github.com/carlosframework/platform/internal/hostactors/awsmembership 0.016s
ok github.com/carlosframework/platform/internal/hostbox 3.001s
ok github.com/carlosframework/platform/internal/hostclaim 0.845s
ok github.com/carlosframework/platform/internal/hostenv 0.016s
ok github.com/carlosframework/platform/internal/hostevidence 0.017s
ok github.com/carlosframework/platform/internal/hostexec 5.387s
ok github.com/carlosframework/platform/internal/hostfence 0.054s
ok github.com/carlosframework/platform/internal/hostfiles 10.355s
ok github.com/carlosframework/platform/internal/hostfloor 0.024s
ok github.com/carlosframework/platform/internal/hostguard 1.066s
ok github.com/carlosframework/platform/internal/hostops 2.020s
? github.com/carlosframework/platform/internal/hostops/fencerelease [no test files]
ok github.com/carlosframework/platform/internal/hostops/internal/fencerelease 0.016s
ok github.com/carlosframework/platform/internal/hostquarantine 0.005s
ok github.com/carlosframework/platform/internal/hostrelocate 0.624s
ok github.com/carlosframework/platform/internal/hostresolve 0.198s
ok github.com/carlosframework/platform/internal/hostwrite 0.016s
ok github.com/carlosframework/platform/internal/infraview 0.018s
ok github.com/carlosframework/platform/internal/instancectl 5.239s
ok github.com/carlosframework/platform/internal/instanceexec 2.769s
ok github.com/carlosframework/platform/internal/instancefs 0.005s
ok github.com/carlosframework/platform/internal/instancekey 0.017s
ok github.com/carlosframework/platform/internal/instanceupdates 0.015s
ok github.com/carlosframework/platform/internal/layout 1.773s
ok github.com/carlosframework/platform/internal/lbhealth 0.017s
ok github.com/carlosframework/platform/internal/lease 10.188s
ok github.com/carlosframework/platform/internal/lease/leasetest 0.599s
ok github.com/carlosframework/platform/internal/ledger 0.034s
ok github.com/carlosframework/platform/internal/mail 0.261s
ok github.com/carlosframework/platform/internal/maintenance 0.119s
ok github.com/carlosframework/platform/internal/maintenanceexec 5.599s
ok github.com/carlosframework/platform/internal/maintenanceguard 0.081s
ok github.com/carlosframework/platform/internal/metrics 0.011s
ok github.com/carlosframework/platform/internal/moves 22.756s
ok github.com/carlosframework/platform/internal/moves/archive 1.044s
ok github.com/carlosframework/platform/internal/objstore 0.298s
ok github.com/carlosframework/platform/internal/ops 0.234s
ok github.com/carlosframework/platform/internal/park 0.298s
ok github.com/carlosframework/platform/internal/plans 0.011s
ok github.com/carlosframework/platform/internal/privatefile 0.008s
ok github.com/carlosframework/platform/internal/provision 23.927s
ok github.com/carlosframework/platform/internal/publicrelease 0.008s
? github.com/carlosframework/platform/internal/regionname [no test files]
ok github.com/carlosframework/platform/internal/registry 5.667s
ok github.com/carlosframework/platform/internal/relay 7.240s
ok github.com/carlosframework/platform/internal/release 25.919s
ok github.com/carlosframework/platform/internal/rollout 3.855s
ok github.com/carlosframework/platform/internal/rolloutwords 0.006s
ok github.com/carlosframework/platform/internal/rootaws 0.009s
ok github.com/carlosframework/platform/internal/rootparticipation 0.066s
ok github.com/carlosframework/platform/internal/rootstate 0.028s
ok github.com/carlosframework/platform/internal/runs 0.566s
ok github.com/carlosframework/platform/internal/s3compat 0.009s
ok github.com/carlosframework/platform/internal/safefile 0.012s
ok github.com/carlosframework/platform/internal/schedule 2.677s
ok github.com/carlosframework/platform/internal/seeds 0.011s
ok github.com/carlosframework/platform/internal/sim/engines 2.603s
ok github.com/carlosframework/platform/internal/sim/hostlife 10.893s
ok github.com/carlosframework/platform/internal/sim/litestreamfacts 3.907s
ok github.com/carlosframework/platform/internal/status 0.083s
ok github.com/carlosframework/platform/internal/steering 0.012s
ok github.com/carlosframework/platform/internal/stopevidence 0.345s
ok github.com/carlosframework/platform/internal/svcrole 0.002s
ok github.com/carlosframework/platform/internal/telemetry 0.004s
ok github.com/carlosframework/platform/internal/termfence 0.004s
ok github.com/carlosframework/platform/internal/tunnelclient 0.007s
ok github.com/carlosframework/platform/internal/unixdial 0.003s
ok github.com/carlosframework/platform/internal/updatewords 0.005s
ok github.com/carlosframework/platform/internal/usage 0.009s
ok github.com/carlosframework/platform/internal/version 0.002s
ok github.com/carlosframework/platform/internal/vet 1.707s
=== internal_console@0 (81 s)
ok github.com/carlosframework/platform/internal/console 79.472s
=== internal_console@1 (43 s)
ok github.com/carlosframework/platform/internal/console 41.571s
=== internal_console@2 (41 s)
ok github.com/carlosframework/platform/internal/console 39.669s
=== internal_console@3 (42 s)
ok github.com/carlosframework/platform/internal/console 40.200s
=== internal_console@4 (43 s)
ok github.com/carlosframework/platform/internal/console 41.164s
=== internal_console@5 (41 s)
ok github.com/carlosframework/platform/internal/console 39.890s
=== internal_console@6 (38 s)
ok github.com/carlosframework/platform/internal/console 36.302s
=== internal_console@7 (47 s)
ok github.com/carlosframework/platform/internal/console 45.230s
=== root@0 (110 s)
ok github.com/carlosframework/platform 108.291s
=== root@1 (83 s)
ok github.com/carlosframework/platform 81.878s
=== root@2 (93 s)
ok github.com/carlosframework/platform 91.330s
=== cross ===
--- shard 0 of 5 ---
cross-compiling darwin/arm64
--- shard 1 of 5 ---
cross-compiling darwin/amd64
--- shard 2 of 5 ---
cross-compiling linux/arm64
linux/arm64 carlos binary: 50528418 bytes (ceiling 52000000)
--- shard 3 of 5 ---
cross-compiling windows/amd64
--- shard 4 of 5 ---
cross-compiling windows/arm64
=== minio-contract ===
minio contract: arm64, minio RELEASE.2025-09-07T16-13-09Z, mc RELEASE.2025-08-13T08-35-41Z
minio: cached, digest verified
mc: cached, digest verified
minio healthy on 127.0.0.1:20999
Added `local` successfully.
Bucket created successfully `local/contract-bucket`.
Bucket created successfully `local/denied-bucket`.
Created policy `contract-only` successfully.
Added user `scopeduser` successfully.
Attached Policies: [contract-only]
To User: scopeduser
buckets and scoped user ready
=== RUN TestDeliveredContractMinio
=== RUN TestDeliveredContractMinio/put
=== RUN TestDeliveredContractMinio/get_round-trips_the_body
=== RUN TestDeliveredContractMinio/list_shows_the_key
=== RUN TestDeliveredContractMinio/head_and_list_agree_on_the_version
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_its_own_object
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/not_by_PutObjectTagging
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/and_not_by_x-amz-tagging_on_upload
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CreateMultipartUpload
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CopyObject_with_a_REPLACE_tagging_directive
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/but_multipart_itself_is_allowed
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_copy_itself_is_allowed
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/a_POST-policy_forged_tag_with_a_matching_version_is_still_not_a_verdict
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_an_ordinary_tagged-with-something-else_upload_is_allowed
=== RUN TestDeliveredContractMinio/delete_removes_it
=== RUN TestDeliveredContractMinio/cross-bucket_denial
--- PASS: TestDeliveredContractMinio (0.02s)
--- PASS: TestDeliveredContractMinio/put (0.00s)
--- PASS: TestDeliveredContractMinio/get_round-trips_the_body (0.00s)
--- PASS: TestDeliveredContractMinio/list_shows_the_key (0.00s)
--- PASS: TestDeliveredContractMinio/head_and_list_agree_on_the_version (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_its_own_object (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/not_by_PutObjectTagging (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/and_not_by_x-amz-tagging_on_upload (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route (0.01s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CreateMultipartUpload (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CopyObject_with_a_REPLACE_tagging_directive (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/but_multipart_itself_is_allowed (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_copy_itself_is_allowed (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/a_POST-policy_forged_tag_with_a_matching_version_is_still_not_a_verdict (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_an_ordinary_tagged-with-something-else_upload_is_allowed (0.00s)
--- PASS: TestDeliveredContractMinio/delete_removes_it (0.00s)
--- PASS: TestDeliveredContractMinio/cross-bucket_denial (0.00s)
PASS
ok github.com/carlosframework/platform/internal/objstore 0.024s
=== RUN TestT4StorageProbeMinio
2026/10/24 18:32:39 moves: T4: STORE: list: operation error S3: ListObjectsV2, https response error StatusCode: 403, RequestID: 18E18AD186FF3253, HostID: 7c420053e0fbcd7f41653fdd92ffa2802c903d05303b1aa15adc2e8bb65d183f, api error SignatureDoesNotMatch: The request signature we calculated does not match the signature you provided. Check your key and signing method.
--- PASS: TestT4StorageProbeMinio (0.01s)
PASS
ok github.com/carlosframework/platform/internal/host 0.022s
=== RUN TestS3StoreMinioContract
=== RUN TestS3StoreMinioContract/conditional_get_and_etag_listing
=== RUN TestS3StoreMinioContract/bump_through_every_mutation_path
--- PASS: TestS3StoreMinioContract (0.03s)
--- PASS: TestS3StoreMinioContract/conditional_get_and_etag_listing (0.01s)
--- PASS: TestS3StoreMinioContract/bump_through_every_mutation_path (0.02s)
PASS
ok github.com/carlosframework/platform/internal/release 0.030s
Bucket created successfully `local/dbf-deploy`.
Bucket created successfully `local/dbf-data-ie`.
Bucket created successfully `local/dbf-data-uk`.
local/dbf-deploy versioning is enabled
Created policy `dbf-console` successfully.
Created policy `dbf-ukbox` successfully.
Added user `dbfconsole` successfully.
Added user `dbfukbox` successfully.
Attached Policies: [dbf-console]
To User: dbfconsole
Attached Policies: [dbf-ukbox]
To User: dbfukbox
=== RUN TestDBForwardMinio
2026/10/24 18:32:41 INFO replica sync system=store db=source.db replica=file txid.replica=0000000000000000 txid.db=0000000000000001
2026/10/24 18:32:41 INFO ltx file uploaded system=store db=source.db replica=file level=0 minTXID=0000000000000001 maxTXID=0000000000000001 size=720
2026/10/24 18:32:41 INFO replica sync system=store db=source.db replica=file txid.replica=0000000000000001 txid.db=0000000000000001
2026/10/24 18:32:41 INFO replica sync system=store db=source.db replica=file txid.replica=0000000000000001 txid.db=0000000000000002
2026/10/24 18:32:41 INFO ltx file uploaded system=store db=source.db replica=file level=0 minTXID=0000000000000002 maxTXID=0000000000000002 size=370
2026/10/24 18:32:41 INFO replica sync system=store db=source.db replica=file txid.replica=0000000000000002 txid.db=0000000000000002
2026/10/24 18:32:41 INFO replica sync system=store db=source.db replica=file txid.replica=0000000000000002 txid.db=0000000000000003
2026/10/24 18:32:41 INFO ltx file uploaded system=store db=source.db replica=file level=0 minTXID=0000000000000003 maxTXID=0000000000000003 size=454
2026/10/24 18:32:41 INFO replica sync system=store db=source.db replica=file txid.replica=0000000000000003 txid.db=0000000000000003
2026/10/24 18:32:41 INFO replica sync system=store db=source.db replica=file txid.replica=0000000000000000 txid.db=0000000000000001
2026/10/24 18:32:41 INFO ltx file uploaded system=store db=source.db replica=file level=0 minTXID=0000000000000001 maxTXID=0000000000000001 size=720
2026/10/24 18:32:41 INFO replica sync system=store db=source.db replica=file txid.replica=0000000000000001 txid.db=0000000000000001
2026/10/24 18:32:41 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/24 18:32:41 console: could not notify member@example.com of a CLI token issue: keymail: lookup member@example.com: keymail: GET /api/federation/lookup?addr=member%40example.com&from=console%40127.0.0.1%3A33055: could not determine target mailbox
agent.go:135: dbreads: dbr_iruc3qos3j4hjpbfvxjhb42kze: planned 3 files (1544 bytes) and restored to 3 in 10ms
agent.go:135: dbreads: dbr_iruc3qos3j4hjpbfvxjhb42kze: output 5 bytes in 1 confirmed chunks in 1ms
2026/10/24 18:32:42 console: db read dbr_iruc3qos3j4hjpbfvxjhb42kze: relayed 1 chunks (102 bytes) in 1.004s, ended done
agent.go:135: dbreads: dbr_gsbn5vixx3k3mzprsulsfknks4: planned 1 files (720 bytes) and restored to 1 in 8ms
agent.go:135: dbreads: dbr_gsbn5vixx3k3mzprsulsfknks4: output 5 bytes in 1 confirmed chunks in 1ms
2026/10/24 18:32:43 console: db read dbr_gsbn5vixx3k3mzprsulsfknks4: relayed 1 chunks (102 bytes) in 1.003s, ended done
agent.go:135: dbreads: dbr_5mmlttc54dhz2wjtxdhofjksfi: planned 3 files (1544 bytes) and held through 3 in 9ms
agent.go:135: dbreads: dbr_5mmlttc54dhz2wjtxdhofjksfi: output 1544 bytes in 1 confirmed chunks in 1ms
2026/10/24 18:32:44 console: db read dbr_5mmlttc54dhz2wjtxdhofjksfi: relayed 1 chunks (1641 bytes) in 1.004s, ended done
=== RUN TestDBForwardMinio/by_disconnect
=== NAME TestDBForwardMinio
agent.go:135: dbreads: dbr_raryplh27ew6kwba765omv7u4e: planned 3 files (1544 bytes) and restored to 3 in 9ms
2026/10/24 18:32:44 console: db read dbr_raryplh27ew6kwba765omv7u4e: relayed 0 chunks (0 bytes) in 13ms, ended client gone
agent.go:135: dbreads: dbr_raryplh27ew6kwba765omv7u4e: output 5 bytes in 1 confirmed chunks in 12ms
=== RUN TestDBForwardMinio/by_timeout
=== NAME TestDBForwardMinio
agent.go:135: dbreads: dbr_x4zbwdeu2dkzk3xwaor2h3fgkm: planned 3 files (1544 bytes) and restored to 3 in 9ms
2026/10/24 18:33:04 console: db read dbr_x4zbwdeu2dkzk3xwaor2h3fgkm: relayed 0 chunks (0 bytes) in 19.218s, ended no-result
agent.go:135: dbreads: dbr_x4zbwdeu2dkzk3xwaor2h3fgkm: output 5 bytes in 1 confirmed chunks in 19.188s
--- PASS: TestDBForwardMinio (22.55s)
--- PASS: TestDBForwardMinio/by_disconnect (0.13s)
--- PASS: TestDBForwardMinio/by_timeout (19.33s)
PASS
ok github.com/carlosframework/platform/internal/console 22.569s
minio contract: PASS
diff --git a/docs/superpowers/specs/2026-10-24-edge-asset-cache-design.md b/docs/superpowers/specs/2026-10-24-edge-asset-cache-design.md| index ce6d8618f..2952a8983 100644 |
| --- a/docs/superpowers/specs/2026-10-24-edge-asset-cache-design.md |
| +++ b/docs/superpowers/specs/2026-10-24-edge-asset-cache-design.md |
| @@ -1,6 +1,6 @@ |
| # Edge asset cache: versioned build assets served from every edge |
| -Branch `edge-asset-cache`. Status: rev 6, 2026-10-24, builder check folded in; rulings R1 to R3 (§13). Approved by Paul 2026-10-24; Astra accepted rev 6 in round 4. |
| +Branch `edge-asset-cache`. Status: rev 7, 2026-10-24, builder check folded in; rulings R1 to R3 (§13). Approved by Paul 2026-10-24; Astra accepted rev 6 in round 4; rev 7 corrections under review (round 5). |
| Related: media roadmap (`2026-09-07-media-platform-roadmap-design.md`), |
| which this does not amend; #368 (the shared cache stores 206 responses). |
| @@ -164,9 +164,11 @@ prefix. (My call.) |
| The instance record gains `Assets *bool` (`omitempty`; absent means |
| "follow the release", `false` means "catch nothing for this instance"). |
| Set through the existing PATCH door (`apiinstances.go`, the PATCH body |
| -beside `gate` and `steering`) and `carlos instances update <host> |
| ---assets=off|on`. It reaches the route row through `RouteFor` and |
| -`convergeOwned` like `Steering` and `Gate`. Use: an instance whose traffic |
| +beside `gate` and `steering`) and a new verb, `carlos instances |
| +set-assets <host> on|off`, beside `set-upstreams` and `set-channel` |
| +(`carlos instances update` is already "install the newest build now", |
| +`instances_updates.go:201`). It reaches the route row through `RouteFor` |
| +and `convergeOwned` like `Steering`. Use: an instance whose traffic |
| must always reach the app, or a quick off switch while debugging. |
| (R1.) |
| @@ -186,7 +188,7 @@ The home box publishes no prefixes for a hostname when any of these hold: |
| hostname can serve different releases, gates and opt-outs, and one |
| hostname-wide declaration cannot describe them. The hostname must also |
| have a single owner by the rule that decides `edgeHost.Account`/`App` |
| - (`edgesfile.go:73-80`) **(astra B2)**; |
| + (`edgesfile.go:77-85`, fold at `:93-116`) **(astra B2)**; |
| - the route is pooled (`Route.Pool != ""`): pooled routes are a tenancy |
| boundary only their pool serves, the same reason steering refuses them |
| (`apiinstances.go:2112-2116`). A pooled route still uses the local disk |
| @@ -214,16 +216,29 @@ that catches gains: |
| generation-managed route `Route.Version` names only what is installed, |
| not what answers (`internal/edge/edge.go:842-845`; the served build comes |
| from the generation, `internal/edge/genproxy.go:298-302`), so the home |
| - publishes the **union** of the installed build's declared paths and those |
| - of every generation currently live on the route **(astra B5)**. The |
| + publishes the **union** of the installed build's declared paths and the |
| + current serving generation's (`ServingGen(host).Build`, |
| + `internal/activator/genservice.go:68`) **(astra B5)**. The activator |
| + keeps one current generation per host (`actEntry.gen`, |
| + `internal/activator/activator.go:469`); a generation being replaced only |
| + finishes requests already sent to it, so no third build can answer a new |
| + request. The |
| installed build is in the union so that a sleeping instance, which has no |
| live generation (`ServingGen` is false while asleep, |
| `internal/edge/gensource.go:24-28`), keeps catching: the installed build |
| is the one a wake would start, and its assets are what a page served |
| before the sleep asks for **(astra R1-5)**. During an update both builds' |
| assets are caught, and the old build's prefix drops out when its |
| - generation retires. Reading those manifests happens on the home box, |
| - which already holds them; no other box reads them. |
| + generation retires. The home reads each build's manifest from the |
| + bucket with `release.GetManifest` (`internal/release/release.go:710`) |
| + once per version and keeps it in memory, since a manifest is immutable. |
| + It does not use the staged copy under `/opt/carlos/versions` |
| + (`release.ReadStagedManifest`, `versions.go:261`), which an older binary |
| + may have re-marshalled without the `assets` key. A manifest that cannot |
| + be read publishes no paths for that build until it can. No other box |
| + reads manifests. The edges-file writer gets the serving build through a |
| + narrow function the agent hands it, since it has no activator handle |
| + today. |
| - `gen` is the instance's asset generation (§6), copied from the instance |
| record through the route row. |
| - Aliases (custom domains, and the exact names instance catch-alls |
| @@ -254,16 +269,26 @@ are failing still writes its file from its local registry rows |
| obsolete generation as fresh **(astra R1-3)**. |
| So `at` is the host's **authority time**: the last moment the home box |
| -knew the records behind that host were current and applied. The record |
| -view (`internal/provision/recordview.go`) gains a per-scope confirmed |
| -time: the time of the last change-index read (or backstop walk) that |
| -succeeded **and** left the app's scope `apps/<acct>/<app>` applied rather |
| -than pending, **and** after which the registry rows for that scope were |
| -converged. A refresh that returns nil while a scope stays pending |
| -(`recordview.go:164-175`: a failed scope read is logged and left pending, |
| -and the snapshot is published incomplete) does not advance that scope's |
| -time. Publishing never advances it. The publisher copies the scope's |
| -confirmed time into each host's `at`. |
| +knew the records behind that host were current and applied. The box keeps |
| +a per-app **records confirmed time**: the start time of the last |
| +reconcile pass (`provision.Reconcile`, run by |
| +`reconcileSerializedWith`, `internal/host/agent.go:1604`) that read the |
| +app's records completely and applied them to the registry without error. |
| +Reconcile today returns one error for the whole pass, so it must also |
| +report which app scopes it read completely and applied. Where those reads |
| +come from depends on how the box is configured: |
| + |
| +- with the change-index record view armed (`CARLOS_CHANGE_INDEX=read`, |
| + `internal/host/changeindex.go:57-78`), a scope counts as read |
| + completely only when it is applied and not pending in the snapshot the |
| + pass used. A refresh that returns nil while a scope stays pending |
| + (`internal/provision/recordview.go:164-175`) does not count; |
| +- without it (`armedRecordView` nil, the bucket read directly), a scope |
| + counts only when its listing and every record body under it were read |
| + without error. |
| + |
| +The pass's **start** time is the one recorded, because a record written |
| +during the pass may not have been seen. Publishing never advances it. |
| **The invariant.** A host's `at` may only be a time at which *every* |
| input the catch decision for that host reads was known complete and |
| @@ -273,19 +298,28 @@ path rows), so the spec names the inputs instead of the cases |
| **(astra R2-1, R3-1, R3-2)**. The catch decision (§3.3, §4.1) reads: |
| 1. **The app's records** (the instance record, its route row, gate, |
| - import gate, opt-out, generation): bounded by the record view's |
| - per-scope confirmed time above. |
| + import gate, opt-out, generation): bounded by the app's records |
| + confirmed time above. |
| 2. **Domain claims**, which decide whether a hostname has path rows, |
| which aliases exist, and which aliases instance catch-alls derive |
| (`internal/domains/aliases.go`, `ReconcileAliases`): bounded by the |
| box's **domain confirmed time**, the completion time of the last |
| - `ReconcileAliases` pass that was *complete*. Today the pass returns an |
| - error only when the claim listing fails; a failed label count for a |
| - catch-all (`aliases.go:161`), a failed alias upsert (`:310`) or a |
| - failed path delete (`:590`) is logged and the pass still returns nil. |
| - So `ReconcileAliases` must also report completeness, and any of those |
| - failures makes the pass incomplete. An incomplete pass does not |
| - advance the domain confirmed time. |
| + `ReconcileAliases` pass that was *complete* (again its start time). |
| + Today the pass returns an error only when the claim listing fails. |
| + Everything else is logged and the pass returns nil: the label count for |
| + a catch-all (`aliases.go:162-171`), instance alias upsert (`:204`), |
| + alias upsert (`:310`), catch-all upsert (`:531`), alias delete |
| + (`:541`), wildcard upsert and delete (`:561`, `:568`), path upsert |
| + (`:584`) and path delete (`:590`). Two inputs fail silently too: claim |
| + bodies the claim cache could not read are dropped |
| + (`internal/domains/claimcache.go:137-145`), and a failed |
| + `edgesfile.DeploymentRoutes` read comes back nil and is treated as |
| + "allow". So `ReconcileAliases` must also report completeness, and |
| + **any** of those makes the pass incomplete. An incomplete pass does not |
| + advance the domain confirmed time. Existing callers keep today's error |
| + behaviour; `TestInstanceCatchallKeepsNamesWhenTheListingFails` |
| + (`instancecatchall_test.go:507`) still sees nil and now also sees |
| + "incomplete". |
| 3. **Release manifests**: immutable once read, so they add no bound. |
| Every published host, primary or alias, gets `at` = the older of (1) for |
| @@ -538,8 +572,8 @@ sha256, identical on every edge. |
| the asset path writes the chosen representation itself. |
| - Added on every caught response, hit or not: `Carlos-Cache: hit`, `miss` |
| or `bypass` (the last for anything forwarded uncached). A hit carries |
| - no `Carlos-Version`: an entry can outlive many deploys, so a version on |
| - it would be a false "verified" for the deploy watch (`edge.go:630-660` |
| + no `X-Carlos-Version`: an entry can outlive many deploys, so a version on |
| + it would be a false "verified" for the deploy watch (`edge.go:813-847` |
| explains the header's freshness contract). |
| - `Age` on hits: the lifetime minus the time left to the entry's expiry. |
| @@ -655,7 +689,7 @@ any touched package; a test runs the full fill/hit/purge path against |
| AU) to watch disk, then the rest one at a time. |
| - **CLI.** `carlos ship --assets`, `carlos deploy --assets` and |
| `--no-assets`, the `assets` project default, `carlos assets purge`, |
| - `carlos assets status`, `carlos instances update --assets` get |
| + `carlos assets status`, `carlos instances set-assets` get |
| `docs/site/cli.md` |
| headings (docsite gate). Every user-facing string goes through copy |
| review before it is written. |
| @@ -811,3 +845,12 @@ On flagship, the same from a small box with the disk figures recorded. |
| waiter deadlines; R3-4 HTML is never stored; R3-5 arm the home first. |
| - round 4, 2026-10-24: Astra **accept** at 287a59c4, no new findings |
| (`CODEX-FINDINGS-ASSET-CACHE-R4.md`). |
| +- rev 7, 2026-10-24: corrections found while writing the plan, against |
| + the code: the records confirmed time comes from reconcile's own |
| + per-app completeness, with or without the record view (which is armed |
| + only under `CARLOS_CHANGE_INDEX=read`); the paths union is installed |
| + build plus `ServingGen`'s build (the activator keeps one current |
| + generation); manifests are read from the bucket, not the staged copy; |
| + every `ReconcileAliases` log-and-continue point and silent input is |
| + listed; the opt-out verb is `carlos instances set-assets`; two citation |
| + fixes. |