The handoff records the storage branch's three reviews and the replicator's further fixes
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2 files changed,
+7
−7
.claude/HANDOFF.md+5 −5implementation/carloku-paid-launch/OUTSTANDING.md+2 −2
CI passed — run details
diff --git a/.claude/HANDOFF.md b/.claude/HANDOFF.md| index deb0137f8..e05292bb6 100644 |
| --- a/.claude/HANDOFF.md |
| +++ b/.claude/HANDOFF.md |
| @@ -7,13 +7,13 @@ |
| 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:57 Dublin on 2026-10-01. Three things, kept apart. |
| +Written 11:27 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. |
| 3. A tested branch that is not production-ready. `implementation/carloku-paid-launch/OUTSTANDING.md` says what is reviewable, tested and proven, lists every launch blocker, and lists each approval to ask for with its exact scope. |
| -All three review servers answered 200 on the IP form at 10:30 Dublin. |
| +All three review servers answered 200 on the IP form at 11:00 Dublin. |
| Preview readiness with billing off is not launch readiness, and a review that accepts a diff is not approval of the branch. Full metering is not ready: neither metered source is proven (replication writes, store transfer), Stripe has no catalog or runtime key and the quantity fence is unverified, and quantity reduction at renewal and the Free tier are not built. |
| @@ -32,9 +32,9 @@ Two open defects, both with their paths disabled, neither offered as a risk to a |
| Side branches, all on the forge, none run through CI, nothing on the launch branch calls them. Each has now had one review by both reviewers, and none passed: |
| - `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-metered-replicator-20261001` (`f4fe6f2b`, full local gate passes): path B, first pieces. Both reviewers rejected it at `ebab1a37`. Fixed since and unread by a reviewer: both P1s (ownership decided on the file, not the path's spelling; ownership read under the registry's change witness, with every request refused once the registry changes) and five P2s (the store kept private, unclaimed databases named, unreadable directories refused, platform identities the ledger accepts, the fleet's recovery window). Open: the rest of `DISPOSITIONS-metered-replicator.md` on that branch, and everything `OUTSTANDING.md` item 1 lists as absent. |
| - `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` (`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-storage-sole-holder-20261001` (`cd66eabf`, 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. Read three times: Astra rejected each time, Opus accepted with fixes each time, the findings narrowing. A fourth reading, of the last diff, started at 11:26 (run directory `logs/astra-storage-review4.md`, `logs/opus-storage-review4.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:** |
| @@ -42,7 +42,7 @@ Side branches, all on the forge, none run through CI, nothing on the launch bran |
| - 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:** |
| -- Replicator branch: the rest of its dispositions (discovery errors read as coverage, unknown databases dropped before the refusal, the store's ordinary registration path, the recovery window), then a second review. |
| +- Replicator branch: a second review of its fixes, then the rest of its dispositions. |
| - Decide what bounds a complete late request at Stripe, or redesign so nothing depends on it. The fencing branch's remaining items are the void's fixed key, the sandbox probe and three transport tests. |
| - Evidence door: after the ruling, withdrawal by entry, audit correctness, a canonical pin, and the refusal of a withdrawal that would invalidate a closed month. |
| - Have the whole launch branch read again at its tip, not a diff, before anything is called accepted. |
diff --git a/implementation/carloku-paid-launch/OUTSTANDING.md b/implementation/carloku-paid-launch/OUTSTANDING.md| index c5822144b..a7df8a303 100644 |
| --- a/implementation/carloku-paid-launch/OUTSTANDING.md |
| +++ b/implementation/carloku-paid-launch/OUTSTANDING.md |
| @@ -32,7 +32,7 @@ Each is concrete. None is waived. |
| ### Meters (the complete metered model cannot invoice until both are proven) |
| -1. **Replication writes (`durability_writes_1k`) cannot be proven on any box.** The external replicator writes outside the ledger. Path A (scoped attestation) was built, reviewed in round six, and cannot prove an exclusive writer; it is off this branch and parked on `feat/carloku-scoped-attestation-20261001`. Path B (the platform's own replicator as the only writer) is **partial, unwired**, on `feat/carloku-metered-replicator-20261001` (`e8a38df0`, on the forge; the full local gate passes on it; first review: both reviewers rejected it, on an owner resolved once and never re-checked and on paths compared as text, among others. Both of those are fixed since: ownership is decided on the file, 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. Those fixes are unread by a reviewer, and the rest is open, see `DISPOSITIONS-metered-replicator.md` on that branch): a litestream store whose every replica client is metered with no unmetered fallback, an owner for each database the box replicates, and a fleet store that refuses anything it cannot meter, each with tests. **Absent**: the agent wiring that binds those meters to the box's ledger, a restore path that does not shell out to the external binary, the unit change, moving the replica credentials so nothing else on the box holds them (the unit runs as the same user as the agent today), and any attestation. Until all of that exists and is approved for a box, this meter is incomplete every month and nothing is invoiced. |
| +1. **Replication writes (`durability_writes_1k`) cannot be proven on any box.** The external replicator writes outside the ledger. Path A (scoped attestation) was built, reviewed in round six, and cannot prove an exclusive writer; it is off this branch and parked on `feat/carloku-scoped-attestation-20261001`. Path B (the platform's own replicator as the only writer) is **partial, unwired**, on `feat/carloku-metered-replicator-20261001` (`f4fe6f2b`, on the forge; the full local gate passes on it; first review: both reviewers rejected it, on an owner resolved once and never re-checked and on paths compared as text, among others. Both of those are fixed since: ownership is decided on the file, 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. Five of the P2 findings are fixed or partly fixed too (the store kept private, unclaimed databases named, unreadable directories refused, platform identities the ledger accepts, the fleet's recovery window). None of the fixes has been read by a reviewer, and the rest is open, see `DISPOSITIONS-metered-replicator.md` on that branch): a litestream store whose every replica client is metered with no unmetered fallback, an owner for each database the box replicates, and a fleet store that refuses anything it cannot meter, each with tests. **Absent**: the agent wiring that binds those meters to the box's ledger, a restore path that does not shell out to the external binary, the unit change, moving the replica credentials so nothing else on the box holds them (the unit runs as the same user as the agent today), and any attestation. Until all of that exists and is approved for a box, this meter is incomplete every month and nothing is invoiced. |
| 2. **Store transfer (`store_egress_gb`) has no source.** No Cost and Usage Report export exists on the account. It needs the scoped infrastructure approval, a first real delivery, an IAM principal inventory in the AWS provisioner (not built), and the bucket ownership attestation for stores that already exist (designed, not built). |
| 3. **A store's partial first hour** with a charged line holds the month, and nothing releases it. It holds by default and nobody is asked to waive it. What can release it is evidence: an operator door that records, for one bucket-hour, who is shown to have owned it and on what evidence, which the fold then honours. **Partial, unwired into this branch**, on `feat/carloku-bucket-hour-evidence-20261001` (`b50f29bc`; code at `58c1870c`): the record, the fold change and three operator API doors, with tests and mutation checks. It accepts only the hour a complete ledger began vouching in and only the owner that ledger names. The full local gate passes on it. First review: Astra rejected it, Opus accepted with fixes. Both confirmed that through the fold it releases nothing but that hour for that owner. Both also found that the evidence is free text that the same operator records and that stands at once, which in practice is a waiver: what counts as evidence, and whether a second person must approve it, needs Paul's ruling (approval J) before more is built. Nothing fixed yet (`DISPOSITIONS-bucket-hour-evidence.md` on that branch). No CI run, no CLI verb, and its operator-facing sentences are unreviewed. |
| 4. **A box's first minutes** before its ledger opened hold that day for everyone. Same as item 3: it holds by default, and the release is an operator evidence door, not a waiver. **Absent**. |
| @@ -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` (`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). |
| +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` (`cd66eabf`, 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. It has been read three times (at `d5464eb1`, `55c11cf3`, `e6015870`): Astra rejected each time, Opus accepted with fixes each time, the findings narrowing. The third reading's fixes are in `cd66eabf` and a fourth reading, of that diff, started at 11:26 Dublin. Its own open items are in 19i on that branch. 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). |