carlos / platform Public

The handoff records the storage branch's first review, its fixes and the ruling it needs

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev 218c2a357b2df1578c45b5fefa6de4a73a28b443 parent cd4d40b
2 files changed, +5 −4
  • .claude/HANDOFF.md +3 −3
  • implementation/carloku-paid-launch/OUTSTANDING.md +2 −1

CI passed — run details

diff --git a/.claude/HANDOFF.md b/.claude/HANDOFF.md
index 4726a1810..deb0137f8 100644
--- a/.claude/HANDOFF.md
+++ b/.claude/HANDOFF.md
@@ -7,7 +7,7 @@
Get Carloku's paid plans live with Stripe Tax and the complete metered model; have the pricing page and signup flow reviewable in the morning.
**STATUS:**
-Written 10:45 Dublin on 2026-10-01. Three things, kept apart.
+Written 10:57 Dublin on 2026-10-01. Three things, kept apart.
1. Draft copy to review, not approved: pricing http://100.119.180.57:38009 (name form http://docs-t1.tail5fd3ba.ts.net:38009), console billing page http://100.119.180.57:45033 (name form http://docs-t1.tail5fd3ba.ts.net:45033).
2. A demo to click through: http://100.119.180.57:41881, real console handlers with Keymail and Stripe simulated. It is not Stripe proof.
@@ -34,11 +34,11 @@ Side branches, all on the forge, none run through CI, nothing on the launch bran
- `feat/carloku-late-request-fencing-20261001` (`157f0de8`, full local gate passes): a Stripe request cannot outlive its connection, and a void for a wrong-total invoice (remediation only). Both reviewers rejected it at `1772e840`. Four findings are fixed since. One finding limits the design: the provider's idle-connection limit bounds when a request can begin to be written, not when a whole request is received. As designed the branch does not close the two defects. Open items: `DISPOSITIONS-late-request-fencing.md` on that branch.
- `feat/carloku-metered-replicator-20261001` (`e8a38df0`, full local gate passes): path B, first pieces. Both reviewers rejected it at `ebab1a37`. Both P1s are fixed since and unread by a reviewer: ownership is decided on the file and not the path's spelling (a tenant's link is refused, shadow state left the tenant's directory), and ownership is read under the registry's change witness, with every request refused once the registry changes. Open: the rest of `DISPOSITIONS-metered-replicator.md` on that branch.
- `feat/carloku-bucket-hour-evidence-20261001` (`b50f29bc`; code `58c1870c`, full local gate passes): the operator evidence door for a store's partial first hour. Astra rejected it, Opus accepted with fixes. Both confirmed it releases only that hour for the owner the ledger names. Both found the evidence is free text one operator records, which in practice is a waiver. That needs Paul's ruling (approval J in `OUTSTANDING.md`). Nothing fixed yet. `DISPOSITIONS-bucket-hour-evidence.md` on that branch.
-- `feat/carloku-storage-sole-holder-20261001` (`d5464eb1`, off the launch branch at `c68ecddd`, full local gate passes): the fix for `OUTSTANDING.md` 19i. The storage pass sizes a store only when it is the sole holder of its bucket. First review by both reviewers started at 10:42 (run directory `logs/astra-storage-review1.md`, `logs/opus-storage-review1.md`).
+- `feat/carloku-storage-sole-holder-20261001` (`55c11cf3`, off the launch branch at `c68ecddd`, full local gate passes): the fix for `OUTSTANDING.md` 19i. The storage pass sizes a store only when it is the sole holder of its bucket. First review at `d5464eb1`: Astra rejected, Opus accepted with fixes. The fixes are in `55c11cf3`; a second reading started at 10:55 (run directory `logs/astra-storage-review2.md`, `logs/opus-storage-review2.md`). It needs a ruling: whose bytes a departed store's leftover objects are (approval K).
- `feat/carloku-scoped-attestation-20261001` (`058ea506`): path A, parked. It cannot prove an exclusive writer. Replication-write completeness is a launch blocker, not waived.
**NEEDED:**
-- Paul: approve or edit both copy reviews; confirm the HALF code word; decide the proration policy; approve or change the whole-paid-day rule for partial months; review copy for the store refusals (`OUTSTANDING.md` 19g); rule on what counts as evidence for a held bucket-hour (approval J). No ruling is asked for on a store's first clock hour or a box's first minutes: they hold by default, and what releases one is an operator evidence door, not a waiver.
+- Paul: approve or edit both copy reviews; confirm the HALF code word; decide the proration policy; approve or change the whole-paid-day rule for partial months; review copy for the store refusals (`OUTSTANDING.md` 19g); rule on what counts as evidence for a held bucket-hour (approval J); rule on storage for a bucket a store has left (approval K). No ruling is asked for on a store's first clock hour or a box's first minutes: they hold by default, and what releases one is an operator evidence door, not a waiver.
- Scoped approvals, prepared as plans only: Stripe test mode, to run `TestSandboxQuantityFence`; the `rk_live` restricted key and Stripe catalog; the Cost and Usage Report export; one scratch-object write to check S3's conditional delete. Exact scopes are in the approvals table of `OUTSTANDING.md`.
**RECOMMENDED:**
diff --git a/implementation/carloku-paid-launch/OUTSTANDING.md b/implementation/carloku-paid-launch/OUTSTANDING.md
index e208e5727..c5822144b 100644
--- a/implementation/carloku-paid-launch/OUTSTANDING.md
+++ b/implementation/carloku-paid-launch/OUTSTANDING.md
@@ -65,7 +65,7 @@ Each is concrete. None is waived.
19. **No review of the whole branch has passed.** Astra rejected rounds one to sixteen; Opus accepted each with fixes. Round seventeen read only the diff since round sixteen: Opus accepted, Astra accepted with fixes (one P2, fixed since and unconfirmed). That is not a pass of the branch: the last whole-branch reading was round sixteen's, and it was rejected. Every round has closed the shapes the round before reported and found neighbouring ones, in the store walks since round seven. Some concern records no walk of this build writes; others need only this build's ordinary walks (a failed rotation, a retained teardown, a scanning door racing a teardown), and two were faults in the previous round's own fixes. Round eleven found one P1 and two P2 in the store walks (a grant could move an older record to another bucket name; a waiting rotation ignored a grant that ran; the user and its policy were written before the grant was counted). Its fixes are on the launch branch (`1395f7bf`). Round twelve read them: every round-eleven finding is closed for its reported shape, Astra rejected again (one P1, two P2) and Opus accepted with fixes (one P2, three P3). The new findings were about records no walk of this build writes (a teardown note for another bucket, a record naming two buckets) being acted on by teardown, rotation and the scanning doors; a rotation started after a grant that failed at its last write; and a take-back that returned access to a bucket another account holds, through the member's own door. Round thirteen read those fixes: closed for their reported shapes, Astra rejected again (two P1, four P2) and Opus accepted with fixes (one P2, five P3). The new findings were paths that read such a record and acted without the check (the holder inventory, the storage pass, the declare door), a rotation's own failed last write, and a retained record blocking another account's take-back. The check now sits in the record's reader and writer. Round fourteen read those: closed for their reported shapes; Astra rejected (two P1, two P2), Opus accepted with fixes (two P2, two P3). Round fifteen read those: all closed; Astra rejected (one P1, one P2), Opus accepted with fixes (one P2, two P3), all about readers of a bucket that a retained teardown left to another account (a sweep in flight, the scan status, the storage pass). Round sixteen read those: all closed, and one of them had made a single store under teardown hold storage for every account. Astra rejected (one P1, two P2), Opus accepted with fixes (one P1, one P2, two P3). The storage hold is now per account; two findings are left open as 19f and 19i. Those fixes are on the launch branch and unconfirmed until the next round reads them. Each round's fixes are on the launch branch, gated locally, and unconfirmed until the next round reads them.
19g. **Refusals whose doors say the wrong thing, and records only a person can repair.** A record that names two buckets (`ErrTwoBuckets`) or whose teardown note is about another bucket (`ErrUnfinishedTeardown`) is refused by the record's one reader and one writer, so nothing acts on it and nothing writes it; the settings page shows such a store as unreadable. No walk of this build writes either; whether an earlier build left such records in production has not been checked. There is no operator verb to repair one. The doors that meet these refusals answer with sentences written for other failures: the grant and provision doors say to run the grant again, the destroy door and the purge say to try again, the scanning doors say to try again or to re-run the grant. The same goes for a rotation refused because a grant must be run again first (`ErrGrantUnfinished`) and for a member's take-back refused because another account holds the bucket (`ErrBucketHeld`). Correct copy for each needs approval (A).
-19i. **A live bucket shared by two stores is sized in full for each of them.** The daily storage pass lists the whole bucket for every store record that names it, so two accounts on one operator-adopted bucket would each carry all of its bytes. It predates this branch. Open here. A fix is on `feat/carloku-storage-sole-holder-20261001` (`d5464eb1`, full local gate passes, first review started 10:42 Dublin): the pass sizes a store only when it is the sole holder of its bucket and holds it, for its own account, otherwise. With it, a store on a shared bucket is held every day until a person decides how that bucket is billed; there is no door for that.
+19i. **A live bucket shared by two stores is sized in full for each of them.** The daily storage pass lists the whole bucket for every store record that names it, so two accounts on one operator-adopted bucket would each carry all of its bytes. It predates this branch. Open here. A fix is on `feat/carloku-storage-sole-holder-20261001` (`55c11cf3`, full local gate passes): the pass sizes a store only when it is the sole holder of its bucket and holds it, for its own account, otherwise. Its first review (at `d5464eb1`): Astra rejected, Opus accepted with fixes; the fixes are in `55c11cf3` and a second reading started at 10:55 Dublin. With it, a store on a shared bucket is held every day until a person acts, and a departed store's leftover objects hold the remaining store until Paul rules whose bytes they are (approval K).
19h. **A rotation whose delivery fails is repaired by a grant, which swaps the app's key with no overlap.** A failed delivery may still land, so the record stays marked, the next rotation is refused, and the grant run again replaces whichever key the app holds. Hosts hold a dead key until they adopt the new config. Before this branch such a rotation was resumed by running it again, which kept the overlap when the delivery had not landed and broke the app when it had. A repair that waits out the signing window and then reads which key was delivered would keep the overlap where the window is enforced (observed on AWS only, 19d); it is not built.
19f. **A store that leaves a bucket with an unsupported name does not hand on its scanner debt.** There is no ledger for such a name. The last holder would delete the bucket and leave a scanning plan behind. Today's scanner (ClamAV) has no plan to leave, so this is latent. Also latent for the same reason: the scan grant door checks that no teardown has begun and then enables scanning as two steps, so a retained teardown landing between them would leave scanning enabled on the other account's bucket with nothing recording that it is owed. And the scan queue's drain checks permission once per batch, so a drain admitted before a retained teardown could return the other account's object metadata; nothing calls the drain or enqueues today, and it must be fixed before the queue is wired.
19e. **A purge can end with a store that was granted during it.** A declare and a grant admitted while the app was live can land between the purge's last check and the removal of its marker. The purge now reports that as an error and never deletes the record, but it does not prevent it: the doors check liveness when they admit a request, not when they write. And after that error the estate is named only by its record and a log line. The app's console row is still trashed, so the settings page and the store-destroy door answer 404 and a second purge answers "not trashed", until the console restarts and restores the row from the record. The flash the member sees says "Nothing was deleted", which is false in this case; correct copy needs approval (A).
@@ -91,6 +91,7 @@ Nothing below was assumed overnight. None of it has been done. No row asks anyon
| G | AWS, read-write, scoped | Create the Cost and Usage Report data export in the collector plan. Separately: write, conditionally delete and remove one scratch object in the deployment bucket, to check that S3 honours `If-Match` on a delete (item 19c) | The store transfer meter's source; the conditional delete the store walks rely on |
| H | Box change | Path B on a dev box: replace the external replicator with the platform's own, including which user holds the replica credentials. Not ready to ask for: the code is partial and unwired (item 1) | The replication meter |
| J | What counts as evidence for a held bucket-hour | Whether an operator's entry must carry a retained artifact (which trail, which query, which interval) and whether a second person must approve it before it stands. The door as built takes free text from one operator | Whether the evidence door can ever release a store's first hour. Until ruled, it releases nothing |
+| K | Storage on a bucket a store has left | When a store leaves a bucket to another store, its objects may stay in it. Whether the remaining store's storage is billed with them, without them (which needs the bucket split by prefix), or held until the departed store's teardown is finished. As built on the storage branch: held | The storage meter for any account on an operator-adopted shared bucket |
| I | Merge | None requested. No review round has passed | |
## Tested on this branch, not proven