spec: plans, caps and packs, Paul's ruling on lapsed paid features
Grace, then disabled, then deleted 90 days later through the Trash. Sponsored accounts never take this path. Lists the author's own calls for Paul to check.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 file changed,
+58
−17
docs/superpowers/specs/2026-10-20-plans-caps-packs-design.md+58 −17
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 328.159s
? github.com/carlosframework/platform/cmd/changelog [no test files]
ok github.com/carlosframework/platform/cmd/console 2.840s
ok github.com/carlosframework/platform/cmd/moveprobe 93.241s
ok github.com/carlosframework/platform/cmd/zddprobe 0.713s
ok github.com/carlosframework/platform/internal/activator 251.242s
ok github.com/carlosframework/platform/internal/agentsock 0.015s
ok github.com/carlosframework/platform/internal/alerting 0.004s
ok github.com/carlosframework/platform/internal/analytics 0.020s
ok github.com/carlosframework/platform/internal/appconfig 2.519s
ok github.com/carlosframework/platform/internal/apperr 0.071s
ok github.com/carlosframework/platform/internal/applog 0.141s
ok github.com/carlosframework/platform/internal/awsinv 0.004s
ok github.com/carlosframework/platform/internal/boxchoice 0.004s
ok github.com/carlosframework/platform/internal/boxid 0.371s
? 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.008s
ok github.com/carlosframework/platform/internal/caps 0.013s
ok github.com/carlosframework/platform/internal/certwarm 0.002s
ok github.com/carlosframework/platform/internal/cgroup 0.703s
ok github.com/carlosframework/platform/internal/changelog 0.336s
ok github.com/carlosframework/platform/internal/claimactivation 0.818s
ok github.com/carlosframework/platform/internal/clamd 0.055s
? github.com/carlosframework/platform/internal/cliflag [no test files]
ok github.com/carlosframework/platform/internal/console 418.750s
? 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.226s
ok github.com/carlosframework/platform/internal/dbdoor/dbdoortest 0.010s
ok github.com/carlosframework/platform/internal/dbverify 0.008s
ok github.com/carlosframework/platform/internal/dnsrec 0.004s
ok github.com/carlosframework/platform/internal/docsite 0.005s
ok github.com/carlosframework/platform/internal/domains 2.372s
ok github.com/carlosframework/platform/internal/doorkey 0.006s
ok github.com/carlosframework/platform/internal/economics 0.006s
ok github.com/carlosframework/platform/internal/edge 44.953s
? github.com/carlosframework/platform/internal/edgeproof [no test files]
ok github.com/carlosframework/platform/internal/edgesfile 0.011s
ok github.com/carlosframework/platform/internal/fleetapi 0.135s
ok github.com/carlosframework/platform/internal/gate 1.655s
ok github.com/carlosframework/platform/internal/genselect 0.017s
ok github.com/carlosframework/platform/internal/geo 0.016s
ok github.com/carlosframework/platform/internal/geoip 0.039s
ok github.com/carlosframework/platform/internal/historyreadback 0.156s
ok github.com/carlosframework/platform/internal/host 190.341s
ok github.com/carlosframework/platform/internal/hostactors 6.574s
ok github.com/carlosframework/platform/internal/hostactors/awsmembership 0.005s
ok github.com/carlosframework/platform/internal/hostbox 2.752s
ok github.com/carlosframework/platform/internal/hostclaim 0.576s
ok github.com/carlosframework/platform/internal/hostenv 0.005s
ok github.com/carlosframework/platform/internal/hostevidence 0.006s
ok github.com/carlosframework/platform/internal/hostexec 5.080s
ok github.com/carlosframework/platform/internal/hostfence 0.035s
ok github.com/carlosframework/platform/internal/hostfiles 10.571s
ok github.com/carlosframework/platform/internal/hostfloor 0.015s
ok github.com/carlosframework/platform/internal/hostguard 1.037s
ok github.com/carlosframework/platform/internal/hostops 1.654s
? github.com/carlosframework/platform/internal/hostops/fencerelease [no test files]
ok github.com/carlosframework/platform/internal/hostops/internal/fencerelease 0.003s
ok github.com/carlosframework/platform/internal/hostquarantine 0.002s
ok github.com/carlosframework/platform/internal/hostrelocate 0.477s
ok github.com/carlosframework/platform/internal/hostresolve 0.135s
ok github.com/carlosframework/platform/internal/hostwrite 0.002s
ok github.com/carlosframework/platform/internal/infraview 0.002s
ok github.com/carlosframework/platform/internal/instancectl 5.220s
ok github.com/carlosframework/platform/internal/instanceexec 2.760s
ok github.com/carlosframework/platform/internal/instancefs 0.005s
ok github.com/carlosframework/platform/internal/instancekey 0.002s
ok github.com/carlosframework/platform/internal/instanceupdates 0.016s
ok github.com/carlosframework/platform/internal/layout 1.564s
ok github.com/carlosframework/platform/internal/lbhealth 0.004s
ok github.com/carlosframework/platform/internal/lease 9.925s
ok github.com/carlosframework/platform/internal/lease/leasetest 0.364s
ok github.com/carlosframework/platform/internal/ledger 0.016s
ok github.com/carlosframework/platform/internal/mail 0.193s
ok github.com/carlosframework/platform/internal/maintenance 0.081s
ok github.com/carlosframework/platform/internal/maintenanceexec 5.533s
ok github.com/carlosframework/platform/internal/maintenanceguard 0.083s
ok github.com/carlosframework/platform/internal/metrics 0.006s
ok github.com/carlosframework/platform/internal/moves 21.189s
ok github.com/carlosframework/platform/internal/moves/archive 0.840s
ok github.com/carlosframework/platform/internal/objstore 0.179s
ok github.com/carlosframework/platform/internal/ops 0.177s
ok github.com/carlosframework/platform/internal/park 0.213s
ok github.com/carlosframework/platform/internal/privatefile 0.004s
ok github.com/carlosframework/platform/internal/provision 39.027s
ok github.com/carlosframework/platform/internal/publicrelease 0.011s
? github.com/carlosframework/platform/internal/regionname [no test files]
ok github.com/carlosframework/platform/internal/registry 5.558s
ok github.com/carlosframework/platform/internal/relay 7.240s
ok github.com/carlosframework/platform/internal/release 24.559s
ok github.com/carlosframework/platform/internal/rollout 3.894s
ok github.com/carlosframework/platform/internal/rolloutwords 0.005s
ok github.com/carlosframework/platform/internal/rootaws 0.007s
ok github.com/carlosframework/platform/internal/rootparticipation 0.059s
ok github.com/carlosframework/platform/internal/rootstate 0.027s
ok github.com/carlosframework/platform/internal/runs 0.579s
ok github.com/carlosframework/platform/internal/s3compat 0.006s
ok github.com/carlosframework/platform/internal/safefile 0.004s
ok github.com/carlosframework/platform/internal/schedule 2.537s
ok github.com/carlosframework/platform/internal/seeds 0.012s
ok github.com/carlosframework/platform/internal/sim/hostlife 10.094s
ok github.com/carlosframework/platform/internal/sim/litestreamfacts 3.845s
ok github.com/carlosframework/platform/internal/status 0.060s
ok github.com/carlosframework/platform/internal/steering 0.006s
ok github.com/carlosframework/platform/internal/stopevidence 0.245s
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.007s
ok github.com/carlosframework/platform/internal/tunnelclient 0.005s
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.005s
ok github.com/carlosframework/platform/internal/version 0.002s
ok github.com/carlosframework/platform/internal/vet 1.575s
=== 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: 49807522 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.030s
=== RUN TestT4StorageProbeMinio
2026/10/20 18:07:20 moves: T4: STORE: list: operation error S3: ListObjectsV2, https response error StatusCode: 403, RequestID: 18E04F1D988B9D7F, 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.029s
minio contract: PASS
diff --git a/docs/superpowers/specs/2026-10-20-plans-caps-packs-design.md b/docs/superpowers/specs/2026-10-20-plans-caps-packs-design.md| index 78bbbbd27..d96c54559 100644 |
| --- a/docs/superpowers/specs/2026-10-20-plans-caps-packs-design.md |
| +++ b/docs/superpowers/specs/2026-10-20-plans-caps-packs-design.md |
| @@ -46,7 +46,9 @@ Two deliveries, in this order: |
| - A free account that hits a cap can buy Personal (and packs) in the |
| console and is lifted immediately on payment. |
| - Every cap warns at 80%, allows a grace period, then refuses only the |
| - capped action. Apps keep serving. Data is never deleted by a cap. |
| + capped action. Apps keep serving. Data is never deleted by a cap, |
| + except what a lapsed or reduced paid account no longer pays for, and |
| + only after grace, a 90-day disabled period and the Trash (9.2). |
| - Existing finite Carloku accounts stay sponsored: they act as paid, at |
| no charge (settled policy, #321). |
| - Plans are a general platform feature. A deployment with no plan catalog |
| @@ -65,7 +67,8 @@ Two deliveries, in this order: |
| ### Anti-goals (would make this a failure even if it works) |
| - A surprise bill. Nothing is charged that the customer did not choose. |
| -- Losing a customer's data because of a cap or a lapsed payment. |
| +- Losing a customer's data without warning, or while they are still |
| + paying for it. |
| - A free tier that can be farmed without limit, which makes every cap |
| meaningless. |
| - Carloku-specific code paths in the platform. carloku.com is a |
| @@ -87,6 +90,9 @@ By Paul in this conversation (2026-10-20), not to be reopened by review: |
| - **Every cap has a grace period with fair notice** before anything is cut |
| off. |
| - **Bandwidth** follows the same cap pattern. |
| +- **After a lapse, paid-only features get a grace period, are then |
| + disabled, and are eventually deleted.** Agreed details: 7 days grace, |
| + 90 days disabled with reminders, then the Trash's 30 days (9.2). |
| - Abuse guards: account farming, things that never sleep, and counting |
| everything that runs are in v1. Phishing defences are in v1 if free and |
| paid apps share a domain. Outbound abuse starts with an audit. Hidden |
| @@ -190,9 +196,10 @@ lowered limit would take something away from something running. |
| **Over the cap:** creation is refused. Nothing that already runs is |
| touched. An account can be over the cap only by losing a plan or a pack |
| (lapse, downgrade, reduction at renewal). Then: grace (section 6), and |
| -after grace the **newest** running things beyond the cap stop serving (a |
| -suspended state with a clear page) until the account is back under the |
| -cap or buys more. Nothing is deleted. |
| +after grace the **newest** running things beyond the cap are disabled |
| +(stop serving, show a clear page), and, if still over, deleted after the |
| +retention period (9.2). This is the one case where a cap leads to |
| +deletion. |
| ### 5.2 Idle window, and things that never sleep |
| @@ -485,12 +492,32 @@ change plans or quantities; the console does. |
| A failed renewal follows Stripe's retry schedule. While Stripe retries, the |
| account keeps its paid limits. When the subscription ends, the account |
| drops to free limits and section 6 applies to every cap it is now over, |
| -with its normal grace. Nothing is deleted. |
| - |
| -Paid-only features already in use when an account drops to free |
| -(attached custom domains, fleets, schedules under 15 minutes, sidecars |
| -beyond the free cap) (codex 1.7): **open, for Paul** (see the end of |
| -this document). |
| +with its normal grace. |
| + |
| +**What the account no longer pays for is disabled, then deleted (Paul, |
| +2026-10-20, codex 1.7).** This covers everything a lapse, downgrade or |
| +pack reduction leaves beyond free (or the remaining) limits: running |
| +things beyond the cap and their data, sidecars beyond the cap, attached |
| +custom domains, and fleets. The steps: |
| + |
| +1. **Grace, 7 days** (section 6, including its notice rule). Everything |
| + keeps working. |
| +2. **Disabled.** Stops serving; a custom domain stops routing; a fleet |
| + takes no new work. Configuration and data are kept. Paying again (or |
| + buying the pack back) re-enables it at once. |
| +3. **Deleted, 90 days after being disabled**, with reminders 30, 7 and 1 |
| + days before. Deletion goes through the existing Trash |
| + (`release.TrashCooloff`, 30 days, `internal/release/trash.go:33`), so |
| + it can still be restored for a month. Data is gone for good about four |
| + months after the lapse at the earliest. |
| + |
| +Which running things are kept is the account's choice where it makes |
| +one before grace ends (pick which three stay); otherwise the newest go |
| +first. Schedules faster than the free minimum are not deleted: after |
| +grace they run at the free minimum interval. |
| + |
| +**Sponsored accounts are never on this path.** They keep the settled |
| +policy: paused at exhaustion, data kept at Carloku's expense. |
| ### 9.3 Bands |
| @@ -626,15 +653,29 @@ codex 1.1 to 1.9; the three citation errors, in order, are codex 1.10 to |
| | 1.4 what counts as notice | section 6 | |
| | 1.5 storage unit | 5.4: per running thing | |
| | 1.6 boxes without quotas | 5.4: warn-only there | |
| -| 1.7 paid-only features after downgrade | open, for Paul | |
| +| 1.7 paid-only features after downgrade | 9.2, Paul's ruling: grace, disable, delete after 90 days via the Trash | |
| | 1.8 one free account per person after lapse | 8.1: creation only | |
| | 1.9 sponsored "current usage" measurement | 8.7 | |
| | 1.10 `CreateRecord` is not the instance choke point | 5.1 | |
| | 1.11 email caps are per sender | 5.6 | |
| | 1.12 house flag publishes no limits without a subscription | 9.4 | |
| -## Open, for Paul |
| - |
| -1. **Paid-only features after a downgrade** (codex 1.7). When a paid |
| - account lapses to free, what happens to custom domains, fleets and |
| - short schedules it already uses? |
| +## Calls made by the author, for Paul to check |
| + |
| +Not discussed with Paul; each is a proposal in the document: |
| + |
| +- All numbers in table 4.2 other than those marked (Paul), and pack |
| + prices left blank. |
| +- Free gets 256 MB / 0.5 CPU (5.3). |
| +- No affordability bands in v1 (9.3). |
| +- One apps domain for free and paid, with paid-only custom domains, |
| + abuse reports and takedown instead of a separate free domain (8.4). |
| +- Storage cap per running thing, not an account pool (5.4). |
| +- Notice rule: after-grace waits for received notice plus 3 days (6). |
| +- Awake-hours enforcement: throttle, close WebSockets, 15-minute |
| + continuous-awake ceiling (5.2). |
| +- Sponsored limits from measured usage, busiest of the last three months |
| + for recurring figures (8.7). |
| +- One free account per person checked at creation only (8.1). |
| +- Limits fail open on a box that has never read an account's record (7). |
| +- Outbound port 25 blocked for tenants on plan-governed deployments (8.5). |