carlos / platform Public

spec: instance sizes and egress allows, rev 4 (Astra R3)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev 788ca4451e0155216a876858bb649ee79497d191 parent 4af48ba
2 files changed, +118 −42
  • docs/superpowers/specs/2026-10-21-instance-sizes-and-egress-design.md +87 −42
  • docs/superpowers/specs/CODEX-FINDINGS-instance-sizes-egress-R3.md +31 −0

CI passed — 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	343.140s
?   	github.com/carlosframework/platform/cmd/changelog	[no test files]
ok  	github.com/carlosframework/platform/cmd/console	3.041s
ok  	github.com/carlosframework/platform/cmd/moveprobe	93.373s
ok  	github.com/carlosframework/platform/cmd/zddprobe	0.724s
ok  	github.com/carlosframework/platform/internal/activator	250.653s
ok  	github.com/carlosframework/platform/internal/agentsock	0.027s
ok  	github.com/carlosframework/platform/internal/alerting	0.006s
ok  	github.com/carlosframework/platform/internal/analytics	0.022s
ok  	github.com/carlosframework/platform/internal/appconfig	2.549s
ok  	github.com/carlosframework/platform/internal/apperr	0.096s
ok  	github.com/carlosframework/platform/internal/applog	0.186s
ok  	github.com/carlosframework/platform/internal/awsinv	0.004s
ok  	github.com/carlosframework/platform/internal/boxchoice	0.005s
ok  	github.com/carlosframework/platform/internal/boxid	0.643s
?   	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.011s
ok  	github.com/carlosframework/platform/internal/caps	0.012s
ok  	github.com/carlosframework/platform/internal/certwarm	0.008s
ok  	github.com/carlosframework/platform/internal/cgroup	0.745s
ok  	github.com/carlosframework/platform/internal/changelog	0.360s
ok  	github.com/carlosframework/platform/internal/claimactivation	0.909s
ok  	github.com/carlosframework/platform/internal/clamd	0.060s
?   	github.com/carlosframework/platform/internal/cliflag	[no test files]
ok  	github.com/carlosframework/platform/internal/console	439.553s
?   	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	6.056s
ok  	github.com/carlosframework/platform/internal/dbdoor/dbdoortest	0.010s
ok  	github.com/carlosframework/platform/internal/dbverify	0.005s
ok  	github.com/carlosframework/platform/internal/dnsrec	0.003s
ok  	github.com/carlosframework/platform/internal/docsite	0.010s
ok  	github.com/carlosframework/platform/internal/domains	2.531s
ok  	github.com/carlosframework/platform/internal/doorkey	0.010s
ok  	github.com/carlosframework/platform/internal/economics	0.006s
ok  	github.com/carlosframework/platform/internal/edge	45.012s
?   	github.com/carlosframework/platform/internal/edgeproof	[no test files]
ok  	github.com/carlosframework/platform/internal/edgesfile	0.016s
ok  	github.com/carlosframework/platform/internal/fleetapi	0.138s
ok  	github.com/carlosframework/platform/internal/gate	1.658s
ok  	github.com/carlosframework/platform/internal/genselect	0.023s
ok  	github.com/carlosframework/platform/internal/geo	0.012s
ok  	github.com/carlosframework/platform/internal/geoip	0.034s
ok  	github.com/carlosframework/platform/internal/historyreadback	0.171s
ok  	github.com/carlosframework/platform/internal/host	192.271s
ok  	github.com/carlosframework/platform/internal/hostactors	5.526s
ok  	github.com/carlosframework/platform/internal/hostactors/awsmembership	0.005s
ok  	github.com/carlosframework/platform/internal/hostbox	2.774s
ok  	github.com/carlosframework/platform/internal/hostclaim	0.623s
ok  	github.com/carlosframework/platform/internal/hostenv	0.005s
ok  	github.com/carlosframework/platform/internal/hostevidence	0.005s
ok  	github.com/carlosframework/platform/internal/hostexec	4.945s
ok  	github.com/carlosframework/platform/internal/hostfence	0.055s
ok  	github.com/carlosframework/platform/internal/hostfiles	10.401s
ok  	github.com/carlosframework/platform/internal/hostfloor	0.017s
ok  	github.com/carlosframework/platform/internal/hostguard	1.058s
ok  	github.com/carlosframework/platform/internal/hostops	1.732s
?   	github.com/carlosframework/platform/internal/hostops/fencerelease	[no test files]
ok  	github.com/carlosframework/platform/internal/hostops/internal/fencerelease	0.002s
ok  	github.com/carlosframework/platform/internal/hostquarantine	0.002s
ok  	github.com/carlosframework/platform/internal/hostrelocate	0.545s
ok  	github.com/carlosframework/platform/internal/hostresolve	0.138s
ok  	github.com/carlosframework/platform/internal/hostwrite	0.002s
ok  	github.com/carlosframework/platform/internal/infraview	0.004s
ok  	github.com/carlosframework/platform/internal/instancectl	5.219s
ok  	github.com/carlosframework/platform/internal/instanceexec	2.737s
ok  	github.com/carlosframework/platform/internal/instancefs	0.009s
ok  	github.com/carlosframework/platform/internal/instancekey	0.003s
ok  	github.com/carlosframework/platform/internal/instanceupdates	0.008s
ok  	github.com/carlosframework/platform/internal/layout	1.580s
ok  	github.com/carlosframework/platform/internal/lbhealth	0.006s
ok  	github.com/carlosframework/platform/internal/lease	9.907s
ok  	github.com/carlosframework/platform/internal/lease/leasetest	0.422s
ok  	github.com/carlosframework/platform/internal/ledger	0.013s
ok  	github.com/carlosframework/platform/internal/mail	0.188s
ok  	github.com/carlosframework/platform/internal/maintenance	0.082s
ok  	github.com/carlosframework/platform/internal/maintenanceexec	5.558s
ok  	github.com/carlosframework/platform/internal/maintenanceguard	0.073s
ok  	github.com/carlosframework/platform/internal/metrics	0.006s
ok  	github.com/carlosframework/platform/internal/moves	21.045s
ok  	github.com/carlosframework/platform/internal/moves/archive	0.812s
ok  	github.com/carlosframework/platform/internal/objstore	0.208s
ok  	github.com/carlosframework/platform/internal/ops	0.191s
ok  	github.com/carlosframework/platform/internal/park	0.208s
ok  	github.com/carlosframework/platform/internal/privatefile	0.004s
ok  	github.com/carlosframework/platform/internal/provision	18.396s
ok  	github.com/carlosframework/platform/internal/publicrelease	0.010s
?   	github.com/carlosframework/platform/internal/regionname	[no test files]
ok  	github.com/carlosframework/platform/internal/registry	5.430s
ok  	github.com/carlosframework/platform/internal/relay	7.236s
ok  	github.com/carlosframework/platform/internal/release	23.726s
ok  	github.com/carlosframework/platform/internal/rollout	3.676s
ok  	github.com/carlosframework/platform/internal/rolloutwords	0.004s
ok  	github.com/carlosframework/platform/internal/rootaws	0.012s
ok  	github.com/carlosframework/platform/internal/rootparticipation	0.058s
ok  	github.com/carlosframework/platform/internal/rootstate	0.027s
ok  	github.com/carlosframework/platform/internal/runs	0.578s
ok  	github.com/carlosframework/platform/internal/s3compat	0.005s
ok  	github.com/carlosframework/platform/internal/safefile	0.004s
ok  	github.com/carlosframework/platform/internal/schedule	2.582s
ok  	github.com/carlosframework/platform/internal/seeds	0.012s
ok  	github.com/carlosframework/platform/internal/sim/hostlife	9.292s
ok  	github.com/carlosframework/platform/internal/sim/litestreamfacts	3.931s
ok  	github.com/carlosframework/platform/internal/status	0.065s
ok  	github.com/carlosframework/platform/internal/steering	0.007s
ok  	github.com/carlosframework/platform/internal/stopevidence	0.128s
ok  	github.com/carlosframework/platform/internal/svcrole	0.003s
ok  	github.com/carlosframework/platform/internal/telemetry	0.004s
ok  	github.com/carlosframework/platform/internal/termfence	0.005s
ok  	github.com/carlosframework/platform/internal/tunnelclient	0.004s
ok  	github.com/carlosframework/platform/internal/unixdial	0.002s
ok  	github.com/carlosframework/platform/internal/updatewords	0.006s
ok  	github.com/carlosframework/platform/internal/usage	0.005s
ok  	github.com/carlosframework/platform/internal/version	0.002s
ok  	github.com/carlosframework/platform/internal/vet	1.573s
=== 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: 49873058 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: downloading https://github.com/minio/minio/releases/download/RELEASE.2025-09-07T16-13-09Z/minio.linux-arm64.RELEASE.2025-09-07T16-13-09Z
  minio: downloaded, digest verified
  mc: downloading https://github.com/minio/mc/releases/download/RELEASE.2025-08-13T08-35-41Z/mc.linux-arm64.RELEASE.2025-08-13T08-35-41Z
  mc: downloaded, 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.025s
=== RUN   TestT4StorageProbeMinio
2026/10/21 13:01:28 moves: T4: STORE: list: operation error S3: ListObjectsV2, https response error StatusCode: 403, RequestID: 18E08D013848DF38, 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.023s
minio contract: PASS
diff --git a/docs/superpowers/specs/2026-10-21-instance-sizes-and-egress-design.md b/docs/superpowers/specs/2026-10-21-instance-sizes-and-egress-design.md
index bb37d8e2a..528be53ac 100644
--- a/docs/superpowers/specs/2026-10-21-instance-sizes-and-egress-design.md
+++ b/docs/superpowers/specs/2026-10-21-instance-sizes-and-egress-design.md
@@ -1,9 +1,9 @@
# Instance sizes and egress allows
Amadan #328 (an isolated instance reaching a private service its operator
-runs) and #329 (operator-set CPU and memory per instance). Revision 3,
-2026-10-21. Revisions 2 and 3 answer the builder check and Astra rounds
-1 and 2 (`CODEX-FINDINGS-instance-sizes-egress-R<n>.md`); the
+runs) and #329 (operator-set CPU and memory per instance). Revision 4,
+2026-10-21. Revisions 2 to 4 answer the builder check and Astra rounds
+1 to 3 (`CODEX-FINDINGS-instance-sizes-egress-R<n>.md`); the
dispositions are §9.
Both are the same mechanism: **an operator-set policy for an app and its
@@ -175,20 +175,48 @@ IPv6 (`internal/edgesfile/private.go:15`) (Astra R1-5).
old: a dead or replaced box's last file keeps its addresses excluded
until an operator removes the file. That is the conservative direction;
the refusal names the box so the operator knows which file to retire.
-- **Console:** refuses a grant naming any address in any box's `addrs`.
- It also refuses unless the inventory is complete: the edges snapshot's
- `BoxesComplete` (`internal/edgesfile/snapshot.go:198`, every box with a
- status snapshot has a readable edges file and the reverse), and every
- file carries `addrs` (an older box does not). The refusal names the box
- the inventory cannot vouch for.
+- **Inventory completeness** is its own check, independent of liveness
+ and of the status listing's 24-hour horizon (Astra R3-2;
+ `BoxesComplete` is a liveness join and does not give it): list
+ `control/edges/` completely, read and parse every file, and every file
+ carries `addrs`. Any listing error, unreadable file, or file without
+ `addrs` (an older box) makes the inventory incomplete. A retired box's
+ retained file is simply one more readable file; it blocks nothing but
+ its own addresses.
+- **Console:** refuses a grant naming any address in the inventory, and
+ refuses any grant while the inventory is incomplete, naming the file
+ that made it so.
- **Root on each box:** before every write (§4.3), drops any entry that
- is one of its own addresses (`isLocal`) or appears in any edges file's
- `addrs`, and reports the drop (§4.4). If it cannot read the edges
- prefix completely, it **fails closed**: it keeps the previous
- exclusions and writes no newly-allowed address until it can (Astra
- R2-5). An address already held stays only if it was already checked
- against a complete read. This catches a box that joins, or an address
- that moves, after the grant.
+ is one of its own addresses (`isLocal`) or appears in the inventory,
+ and reports the drop (§4.4). **If the inventory is incomplete, root
+ clears the app's effective allows** (writes the reset) and reports
+ `inventory incomplete` until a complete read returns, then writes them
+ again (Astra R3-1, R1-5). A past complete read is not evidence about
+ now. Its own-address check needs no inventory and always applies. The
+ cost is stated: a store outage closes granted addresses for its
+ duration, within one 30-second loop each way.
+- **Transport** (Astra R3-3): a box with direct store access reads the
+ policy object and `control/edges/` itself. A bucketless fleet box reads
+ through the console's store door (`openFleetDoorStore`,
+ `internal/host/agent.go:4366`), whose read ACL
+ (`internal/edge/storedoor.go:431`) grants neither today, and whose
+ client reads a refusal as an absent object (`fleetapi.go:127`), which
+ would silently mean "no policy". So:
+ - the door's `aclRead` grants `control/app-policy/<sqid>/<app>.json`
+ for the box's own account scope only, read-only (`aclWrite` never
+ answers true for it);
+ - the door serves the inventory as one console-computed object,
+ `control/fleet-addrs.json` (`{"complete": bool, "addrs": [...],
+ "generation": ...}`), rewritten by the console whenever an edges
+ file changes, rather than granting fleet boxes a listing of
+ `control/edges/`. Root fails closed on `complete: false` or on any
+ read error;
+ - a fleet-door box tells "refused" from "absent" for these two keys: a
+ refusal is reported as `policy unreadable`, and root treats it as
+ incomplete (allows cleared, sizes at the build's tier), never as
+ "no policy".
+ Tests prove delivery through the door and that both keys stay
+ unwritable from a box.
- **Residual, stated:** the inventory is published by each box's agent.
Under claims a compromised agent could leave its own box's addresses
out, so that other boxes' tenants could reach it if an operator also
@@ -212,8 +240,9 @@ may run (`validateStagedExe`):
(`instanceRegistryHostApp`), so the agent already decides which build
an isolated instance runs. The agent converges the policy into a
registry column on each of the app's route rows; root reads that
- column and validates it. This adds no trust the agent does not already
- hold. **One extra rail:** a box whose layout is not wholly nested (any
+ column and validates it, and reads the inventory verdict (§4.2) the
+ agent converges beside it. This adds no trust the agent does not
+ already hold. **One extra rail:** a box whose layout is not wholly nested (any
flat exec child, which runs as the agent's user and may be able to
write `registry.db`) gets no allow written at all, and says so.
@@ -351,23 +380,24 @@ are: they protect the overlap allowance (Astra R1-1).
not, or the reply lost), the host is marked unsettled: no swap starts
until a fresh read-back settles it, and the captured tier is then taken
from that read-back, never from the request.
-- **A lowering restore waits for the incumbent to be gone** (Astra
- R2 new 1). Today a swap restores the parent (`dropMember`) before it
- replaces a legacy co-spawned sidecar (`replaceSidecar`, `swap.go:678`),
- and the old sidecar shares the slice. When the successor's size is
- smaller than the incumbent's, the restore to the smaller figure happens
- only after the retiring generation and the old sidecar (and their
- descendants) are proven stopped: units inactive and cgroups empty. Until
- then the parent keeps the overlap figure, and a failed restore retries
- with the same proof. An equal-size restore keeps today's order.
-- **Shrinking memory never touches a running process** (Astra R1-3: a
- usage sample cannot make lowering `memory.max` safe, because the
- process can allocate between the sample and the write). The new size
- takes effect when a process starts under it: the next generation (a
- deploy's swap starts the successor at the effective size, and the
- overlap figure is old plus new), or the next cold start (a wake from
- hibernation). Until then it reports `pending`. An operator who needs
- it now restarts the instance.
+- **Memory never shrinks while anything runs in the slice** (Astra
+ R1-3, R2 new 1, R3 new 1; a gate, not a tolerance: three rounds of
+ proving each co-tenant of the slice stopped kept finding another). A
+ usage sample cannot make lowering `memory.max` safe, and a swap's
+ restore runs while the old sidecar and the retiring generation's `carlos
+ run` commands may still hold memory (`swap.go:678`,
+ `activator/cmd.go:336,377`). So:
+ - a swap's successor starts at **the larger of** the effective size
+ and the incumbent's size, so the restore never lowers the parent;
+ - a smaller memory size takes effect only at a **cold start**, when
+ the slice is empty (a wake from hibernation, or a restart the
+ operator asks for); CPU and IO shrinks still apply live, as they kill
+ nothing;
+ - until then the instance reports `pending` with the size it will take
+ (§5.6). An operator who needs it now restarts the instance.
+
+ No swap ever lowers memory, so today's restore order, the sidecar
+ replacement and command admission are untouched.
- **Read-back** reports the serving generation unit's `MemoryMax` when
there is one, else the slice's, and `swap` while the parent is raised.
@@ -432,7 +462,9 @@ default and the overrides.
Carloku's plan caps. Self-service can come with plans and caps.
- The four sizes and their numbers (§5.1).
- Shrinking below the build's tier is allowed with a warning (§5.2).
-- Memory shrinks wait for the next generation or start (§5.4).
+- Memory shrinks wait for a cold start; a swap never lowers memory (§5.4).
+- An incomplete address inventory clears every allow until it is
+ complete again (§4.2).
- The quarter-of-a-box refusal (§5.5).
- Public addresses are refused as pointless; every fleet box address is
refused as dangerous, and an incomplete inventory refuses the grant
@@ -474,6 +506,15 @@ does exist, at `internal/activator/`.
| new 2 | Freshness and totals depend on region HA | Accepted: `addrs` and `mem_total_mib` are gated fields, no freshness relied on (§4.2, §5.5) |
| new 3 | A reused host name inherits an override | Accepted: overrides bound to the record's `CreatedAt` (§3) |
+**Astra round 3** (REJECT; everything else resolved):
+
+| # | Finding | Disposition |
+|---|---|---|
+| R1-3 / new 1 | `carlos run` commands share the slice through a lowering restore | Accepted as a gate: no swap lowers memory; shrinks land at a cold start only (§5.4) |
+| R1-5 | Root keeps old allows through an incomplete read | Accepted: root clears allows while the inventory is incomplete (§4.2) |
+| new 2 | `BoxesComplete` is a liveness join, not inventory completeness | Accepted: its own completeness check (§4.2) |
+| new 3 | Fleet-door boxes cannot read the policy or the inventory | Accepted: account-scoped door read, a console-computed inventory object, refusal is not absence (§4.2) |
+
## 10. Left to the builder
- Column names, the call shapes, and the status snapshot fields.
@@ -496,15 +537,19 @@ does exist, at `internal/activator/`.
- A grant naming another box's address is refused; one naming an address
a box acquires later is dropped by root and reported.
- Resize during a swap waits; growth reaches the serving generation's
- own cap; a memory shrink leaves the running process's limits alone and
- applies to the next generation.
+ own cap; a memory shrink leaves every running process's limits alone,
+ a swap's successor starts at the larger size, and the shrink lands at
+ the next cold start.
- `small` and `xlarge` through the complete claim launch.
- A live growth updates the captured tier; a partly applied change
blocks the next swap until a read-back settles it.
-- A shrinking swap keeps the overlap figure until the old generation and
- old sidecar are proven stopped.
-- Root with an unreadable edges prefix writes no newly-allowed address;
- an old edges file still excludes its box's addresses.
+- Root with an incomplete inventory (listing error, unreadable file, a
+ file without `addrs`, `complete: false` through the door) clears the
+ allows and restores them on the next complete read; an old edges file
+ still excludes its box's addresses.
+- A bucketless fleet box receives the policy and the inventory through
+ the store door, cannot write either, and treats a refusal as
+ incomplete, not absent.
- A host name deleted and recreated does not inherit its override.
- Another app's instance on the same box cannot reach the allowed
address; the probe cannot either.
diff --git a/docs/superpowers/specs/CODEX-FINDINGS-instance-sizes-egress-R3.md b/docs/superpowers/specs/CODEX-FINDINGS-instance-sizes-egress-R3.md
new file mode 100644
index 000000000..8e61cad10
--- /dev/null
+++ b/docs/superpowers/specs/CODEX-FINDINGS-instance-sizes-egress-R3.md
@@ -0,0 +1,31 @@
+R1-1: **RESOLVED** — Populated-slice skips remain; resizing waits for swap completion.
+
+R1-2: **RESOLVED** — Captured `Gen.Tier` follows read-back, and partial changes block swaps until settled.
+
+R1-3: **PARTLY (P1)** — The old-sidecar case is addressed; commands remain exposed to a lowering restore. See new finding 1.
+
+R1-4: **RESOLVED** — Startup reconciliation and periodic read-back retries cover failed revocations.
+
+R1-5: **PARTLY (P1)** — Expiry no longer removes exclusions, but [§4.2:184](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/instance-egress-sizes-328-329/docs/superpowers/specs/2026-10-21-instance-sizes-and-egress-design.md:184) explicitly retains previously checked allows during incomplete reads. If another box subsequently acquires an allowed address and its edges file cannot be read, that address stays open indefinitely. A past complete read cannot establish current safety. **Smallest fix:** clear effective app allows when inventory completeness cannot be established, retain known exclusions, and report the refusal until completeness returns.
+
+R1-6: **RESOLVED** — All three claim validators and complete-launch tests are explicitly included.
+
+R1-7: **RESOLVED** — Separate policy storage prevents older manifest writers from erasing it.
+
+R1-8: **RESOLVED** — The declined finding matches the explicitly retained size trust model.
+
+R1-9: **RESOLVED** — `HighOf` remains authoritative.
+
+R2-1: **RESOLVED** — Lowering restores now require proof that the old sidecar and its descendants stopped.
+
+R2-2: **RESOLVED** — Address changes and memory totals publish independently of region HA.
+
+R2-3: **RESOLVED** — Overrides are bound to the instance record’s `CreatedAt`.
+
+1. **P1 — The shrinking-swap proof still omits command units.** Commands share the host slice and have no separate memory cap ([carlos-instance-cmd@.service:30](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/instance-egress-sizes-328-329/internal/host/units/carlos-instance-cmd@.service:30)). Retiring-generation commands are stopped asynchronously, without joining their termination before parent restoration ([cmd.go:377](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/instance-egress-sizes-328-329/internal/activator/cmd.go:377)). Furthermore, command admission waits only for a spawning successor, so a new command can start after the switch while retirement still holds the overlap allowance ([cmd.go:336](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/instance-egress-sizes-328-329/internal/activator/cmd.go:336)). Its start preserves that raised limit ([instancectl/cmd.go:78](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/instance-egress-sizes-328-329/internal/instancectl/cmd.go:78)). During an `xlarge` → `small` swap, such a command can hold more than 256 MiB when the parent shrinks, allowing the resize to trigger OOM killing. [Kernel documentation](https://docs.kernel.org/admin-guide/cgroup-v2.html). **Smallest fix:** require retiring commands and descendants to be proven stopped, and hold new command starts until the smaller parent limit is successfully restored; preserve both requirements across restore retries.
+
+2. **P2 — `BoxesComplete` does not provide the inventory guarantee claimed in §4.2.** Unreadable edges files set `Snapshot.Complete=false` separately ([snapshot.go:233](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/instance-egress-sizes-328-329/internal/edgesfile/snapshot.go:233)); missing counterparts are checked only for **fresh** status snapshots ([snapshot.go:173](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/instance-egress-sizes-328-329/internal/edgesfile/snapshot.go:173)). Thus `BoxesComplete` can remain true with an unreadable edges file and stale status. Conversely, status objects older than 24 hours disappear from the listing ([fleet.go:268](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/instance-egress-sizes-328-329/internal/status/fleet.go:268)), making a deliberately retained retired-box edges file fail the reverse join and block unrelated grants. **Smallest fix:** specify an inventory completeness check independent of liveness and the status listing horizon, checking every edges read and preserving retired-box exclusions.
+
+3. **P2 — Fleet boxes using the store API cannot read the new policy.** Bucketless fleet boxes obtain their deployment store through `openFleetDoorStore` ([agent.go:4366](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/instance-egress-sizes-328-329/internal/host/agent.go:4366)). Its read ACL excludes `control/app-policy/<sqid>/<app>.json` ([storedoor.go:431](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/instance-egress-sizes-328-329/internal/edge/storedoor.go:431)), and the client treats the resulting 404 as an absent object ([fleetapi.go:127](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/instance-egress-sizes-328-329/internal/fleetapi/fleetapi.go:127)). These instances therefore silently retain build-default sizes and receive no allow. The required edges-prefix listing is also refused. **Smallest fix:** specify account-scoped policy reads and an authorized inventory transport through the fleet API, with tests proving policy delivery while policy writes remain forbidden.
+
+REJECT
\ No newline at end of file