Record review round two and the Opus review, their dispositions, and two designs
The Astra round 2 findings and the Opus production review are committed unedited. The dispositions file covers both. Two designs for work not yet built: evidence-backed ownership attestation for existing and adopted store buckets, and partial-month billing by whole paid days against a prorated allowance. The handoff drops the partial-month waiver question: it is required engineering.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
7 files changed,
+375
−8
docs/superpowers/plans/2026-10-01-bucket-ownership-attestation.md+42 −0docs/superpowers/plans/2026-10-01-partial-month-billing.md+30 −0docs/superpowers/specs/CODEX-DISPOSITIONS-carloku-paid-launch-overnight.md+20 −0docs/superpowers/specs/CODEX-FINDINGS-carloku-paid-launch-overnight-round2.md+102 −0docs/superpowers/specs/CODEX-REVIEW-BRIEF-carloku-paid-launch-overnight-round3.md+35 −0docs/superpowers/specs/OPUS-REVIEW-carloku-paid-launch-overnight.md+137 −0implementation/carloku-paid-launch/AGENT_HANDOFF.md+9 −8
CI passed — run details
diff --git a/docs/superpowers/plans/2026-10-01-bucket-ownership-attestation.md b/docs/superpowers/plans/2026-10-01-bucket-ownership-attestation.md| new file mode 100644 |
| index 000000000..087096d91 |
| --- /dev/null |
| +++ b/docs/superpowers/plans/2026-10-01-bucket-ownership-attestation.md |
| @@ -0,0 +1,42 @@ |
| +# Attesting the ownership history of existing and adopted store buckets |
| + |
| +Date: 2026-10-01. Status: proposal with the evidence it would rest on; not built. It adds a billing authority path, so it follows the review of everything already on the branch. |
| + |
| +## The problem |
| + |
| +Object-store transfer is attributed by `usage/buckets/<bucket>.json`. A ledger resolves an owner only when it can vouch that it names everyone the platform ever gave access to the bucket. Today that is true only for a bucket whose ledger was created by the grant that minted it. Two kinds of bucket cannot get there on their own: |
| + |
| +- the stores provisioned before ledgers existed (four on the flagship); |
| +- any bucket an operator adopted, which existed and may have been reachable before the platform knew it. |
| + |
| +Their charged transfer holds the month. That is correct and it also means store transfer cannot be billed for those tenants until something proves who has had access. |
| + |
| +## What can be proved, and from what |
| + |
| +No evidence the platform holds can prove who had access to a bucket in the past in general. Three sources prove narrower things, and together they are enough going forward: |
| + |
| +1. **Store records** (`apps/<acct>/<app>/store.json`): every current grant, with its bucket, grant time and actor. Deleted at teardown, so they say nothing about a store that no longer exists. |
| +2. **IAM, read now**: every store principal is a user under the store path whose one inline policy names exactly one bucket ARN. Listing the users under that path and reading each policy gives the complete set of platform-issued principals that can reach a bucket at this instant. This is the only source that can show the absence of a grantee. |
| +3. **CloudTrail management events** (CreateUser, PutUserPolicy, CreateAccessKey, DeleteUser, CreateBucket, DeleteBucket): ninety days of history without a trail, longer with one. It can show that no other principal was given the bucket inside that window. |
| + |
| +So the honest claim available is: "from instant T, the ledger names everyone", where T is when the IAM inventory was read and matched the records. With CloudTrail, T can be pushed back to the start of the window it covers. Before T nothing is proved, and nothing should be billed. |
| + |
| +## The mechanism |
| + |
| +An operator verb, `carlos store ownership attest --bucket <name>`, backed by a console door, that: |
| + |
| +1. reads every store record naming the bucket (the recorded grantees); |
| +2. reads IAM: every user under the store path whose policy names the bucket ARN (the actual grantees); |
| +3. refuses unless the two sets are equal, naming the difference; |
| +4. opens an interval for each grantee from its grant block's time, as recorded history, and sets `complete_since` to the attestation instant, with an attestation block in the ledger: who ran it, when, the principal ARNs and policy digests it saw, and the record keys it matched; |
| +5. is append-only like every other ledger write: an attestation can be added once to a ledger that has none, and never moved earlier. |
| + |
| +`OwnerAt` already answers only for hours at or after `complete_since` and only when every interval touching the hour names one owner. A bucket shared by two apps therefore still holds after attestation, which is right: per-bucket billing cannot split their transfer. Sharing must end (one app moved to its own bucket) before either can be billed. |
| + |
| +Changes needed: a completeness claim that can be added once by this door (today `appendOnly` forbids adding it after creation), the attestation block in the ledger and in the month evidence projection, an IAM inventory read in the provisioner (`internal/awsinv` already holds an IAM client), the door, the verb and its documentation page. |
| + |
| +## What stays a blocker, precisely |
| + |
| +- Transfer from these buckets before their attestation instant can never be attributed. If paid plans launch after the attestations, nothing is lost: no month before launch is billed. If a store is attested mid-month, that month's charged transfer from it holds the month for every account, so attest before the first billable month begins. |
| +- An adopted bucket that non-platform principals can reach (another role in the account, a bucket policy) is outside what IAM user inventory shows. The door must also read the bucket policy and refuse a bucket with any grant beyond the store principals. |
| +- A bucket shared by two apps cannot be billed to either. |
diff --git a/docs/superpowers/plans/2026-10-01-partial-month-billing.md b/docs/superpowers/plans/2026-10-01-partial-month-billing.md| new file mode 100644 |
| index 000000000..76c7272ab |
| --- /dev/null |
| +++ b/docs/superpowers/plans/2026-10-01-partial-month-billing.md |
| @@ -0,0 +1,30 @@ |
| +# Billing a month the paid plan covers only partly |
| + |
| +Date: 2026-10-01. Status: design; not built. Required work, not a waiver: the complete metered model must bill a first month that starts mid-month, a month a subscription ends in, and a free-to-paid change, correctly. |
| + |
| +## What happens today |
| + |
| +`plans.AllowanceForMonth` refuses (`ErrAssignmentHistory`) any month not covered from its first instant to its last by active, paid assignments. The refusal is quiet: no review is filed, the invoice pass retries the month forever, and the month is never billed. Every subscriber's first month is in this state. |
| + |
| +It refuses for a real reason. A statement holds one total per meter for the month. With usage as a single monthly number, there is no way to say how much of it fell inside the paid part, and charging overage against usage from the free part would bill people for what Free allowed. |
| + |
| +## The design |
| + |
| +Bill only usage that provably fell inside the paid interval, against the allowance prorated to that interval. |
| + |
| +1. **Statements carry daily meters.** `usage.Statement` gains `Days map[date]map[meter]float64`, additive on the wire. Every source already arrives per day or finer (metrics and edge by hour, storage and census by day, the durability ledger by hour, the AWS report by hour), so the sweep accumulates per day as well as per month. The monthly totals stay, and must equal the sum of the days, which a test pins. Evidence is unchanged: the same inputs are read. |
| +2. **Allowance history yields intervals, not a refusal.** `AllowanceForMonth` returns the paid intervals inside the month with the assignment in force for each, and the allowance summed over them, each weighted by its share of the month as it already is for mid-month quantity changes. It still refuses what it must: an unordered history, a mixed terms version, a house and non-house mix, a status it cannot read. |
| +3. **Usage is counted by whole days inside a paid interval.** A UTC day counts only when all 24 hours lie inside one paid interval. The day a plan starts and the day it ends are not counted. That rounds toward the customer by at most two days a month and needs no finer attribution than the statement holds. |
| +4. **Overage** is the counted usage minus the prorated allowance, per meter, floored at zero, then the existing line and €1 floor rules. Storage is already a per-day accrual (bytes over days in month), so counting days is exact for it. |
| +5. **Completeness is still whole-month.** The month's sources must be complete as today. A partial month is not a reason to bill on partial evidence. |
| +6. **A month with no paid day** (the plan started on the last day, or ended on the first) is recorded as waived with no provider call, which ends the endless retry. |
| + |
| +What this does not do: bill Free usage. Free is bounded by enforcement, which is separate work. |
| + |
| +## Changes |
| + |
| +`internal/usage/statement.go` (the `Days` field and its invariant), `internal/console/economicspass.go` (accumulate per day in the sweep and both folds), `internal/plans/month.go` (intervals instead of refusal for an uncovered start or end), `internal/console/billingcalculation.go` (count whole paid days when the month is partial; record which days were counted in the frozen calculation so the invoice can be reproduced), and the tests that pin today's refusals, which change from "holds" to "bills the paid days". |
| + |
| +## Order |
| + |
| +After the review findings on the branch are closed, because it changes the frozen calculation that the invoice engine and its recovery tests rest on, and it wants its own independent review. |
diff --git a/docs/superpowers/specs/CODEX-DISPOSITIONS-carloku-paid-launch-overnight.md b/docs/superpowers/specs/CODEX-DISPOSITIONS-carloku-paid-launch-overnight.md| index 1e91bd0c5..ba0a1c966 100644 |
| --- a/docs/superpowers/specs/CODEX-DISPOSITIONS-carloku-paid-launch-overnight.md |
| +++ b/docs/superpowers/specs/CODEX-DISPOSITIONS-carloku-paid-launch-overnight.md |
| @@ -24,3 +24,23 @@ Consequences worth knowing: |
| - The Data Export must use versioned delivery ("create new export file"), not overwrite. The collector plan says so now. |
| - Stores provisioned before ownership ledgers, and every adopted bucket, hold their month's store transfer until an operator attests their history. No such door exists yet. That is a launch blocker for billing store transfer on the four existing flagship stores. |
| - Replication writes bill only `PutObject`, `UploadPart` and `CreateMultipartUpload` made for continuous replication. That undercounts, by design. |
| + |
| +# Dispositions: round 2 (Astra) and the Opus production review |
| + |
| +Reviewed commit `8e7499ae`. Astra round 2 (`CODEX-FINDINGS-carloku-paid-launch-overnight-round2.md`, unedited): reject, seven P1 and one P2. Opus production and authority review (`OPUS-REVIEW-carloku-paid-launch-overnight.md`, unedited): accept with fixes, two P1, three P2, three P3. Every finding was checked against the code. All were accepted. Where the two reviews found the same thing it is listed once. |
| + |
| +| Finding | Disposition | Where | Reproduction | |
| +| --- | --- | --- | --- | |
| +| Astra 1, Opus N2: the house marker can change between checkout's read and the intent | Accepted. The intent transaction re-reads the marker and refuses when it disagrees with the coupon. The house door also takes the account's billing lock, and audits only a real change. | `billingstore.go` `beginBillingCheckout`, `billingoperator.go` | `TestCheckoutIntentRefusesAStaleHouseSnapshot` (fails with the check removed), `TestBillingOperatorHouseNoOpLeavesNoAuditTrail` | |
| +| Astra 2: two console processes can lower a quantity | Accepted. One persisted operation per account (`billing_quantity_operations`): a newer target cannot start until the outstanding one is acknowledged, whoever finishes it sends the same idempotency key, and a process paused past two minutes does not send. | `billingquantity.go` | `TestQuantityWorkerCannotLowerAQuantityAcrossProcesses`, `TestQuantityWorkerDoesNotSendAfterALongPause`, `TestQuantityWorkerFinishesAnOutstandingOperation` | |
| +| Astra 3, Opus N1: an expired period's addition raises the renewed subscription | Accepted. Only additions recorded in the subscription's current period can raise it. Additions from a period that has renewed are closed as not charged and a review is filed. A member still present after renewal is seated in the new period, from now. | `billingquantity.go` | `TestQuantityWorkerNeverChargesAnExpiredPeriodsAddition`, `TestQuantityWorkerSeatsAMemberWhoOutlivedTheirPeriod` | |
| +| Astra 4, Opus N6: `minted` does not prove this grant created the bucket | Accepted. Completeness now needs a positive record on the grant block (`GrantBlock.Minted`, written with the block, false for adopting grants and older blocks), no delivered credentials, and a probe showing the bucket does not exist yet. A provisioner that cannot answer, or a failed probe, claims nothing. | `objstore/walk.go`, `objstore.go`, `provisioner.go`, `aws.go`, `memprovisioner.go` | `TestGrantClaimsNothingForABucketThatAlreadyExists`, `TestGrantClaimsNothingWhenTheProbeFails`, `TestReRunOfAnAdoptingGrantIsStillAdopted`, `TestPartialBackfillOfASharedBucketResolvesNoOwner` | |
| +| Astra 5: teardown closes ownership while credentials can still reach a recreated bucket | Accepted. The interval is closed only after `DeleteUser` succeeds. Until then anyone else granted the name shares its hours, which resolve to nobody. | `objstore/walk.go` | `TestTeardownKeepsOwnershipOpenUntilCredentialsAreRevoked` | |
| +| Astra 6, Opus N7: charged transfer on a non-usage line disappears | Accepted. Any non-usage S3 line on a transfer usage type with a cost that is not zero holds (`transfer_adjustment`): fees, refunds and credits alike. | `awsbilling/classify.go` | classifier table | |
| +| Astra 7 and 9 residual, Opus N4: the catch-up watches only the platform statement and stops at six months | Accepted. The catch-up walks back to where metering began (bounded at 36 months), treats a month as open if any account's statement is, and logs on every pass for a month held more than six. | `console/economicspass.go` | `TestStatementCatchUpRetriesAnAccountLeftOpen` (fails with the per-account check removed) | |
| +| Astra 8: evidence discards the completeness timestamp | Accepted. The month projection carries the boundary, clipped to the month. | `usage/evidence_month.go` | `TestBucketOwnershipEvidenceKeepsTheCompletenessBoundary` | |
| +| Opus N3: the fold skips a box's days before its ledger registered | Accepted. Every box another source expected that day must be expected by the durability source for at least the same span. | `console/durabilityfold.go` | `TestDurabilityFoldHoldsBeforeABoxRegisteredItsLedger` (fails with the check removed) | |
| +| Opus N5: additions against an ended subscription hold forever | Accepted. Additions bound to a subscription that is over, or is no longer the account's, are closed with no provider call. | `billingquantity.go` | `TestQuantityWorkerClosesAdditionsOfAnEndedSubscription` | |
| +| Opus N8, minor | Accepted: the idempotency key carries the behaviour and the period; a mismatch review is filed once while open; the no-op house change is not audited; the unavailable line is shown for Personal only; the stale comment is corrected. Not changed: after a crash between reconcile and mark the outcome is recorded as `included` with reference `quantity_already_billed`, and `charged` is recorded when the prorated invoice is created, not when it is paid. Both are stated here rather than hidden. | | | |
| + |
| +Operational consequence both reviews drew, and it stands: do not set `CONSOLE_STORE_EGRESS_REPORT_PREFIX` in production until existing store buckets have an attested ownership history, and do not set `CONSOLE_BILLING_PRORATION` until Paul has decided the policy and renewal reduction exists. |
diff --git a/docs/superpowers/specs/CODEX-FINDINGS-carloku-paid-launch-overnight-round2.md b/docs/superpowers/specs/CODEX-FINDINGS-carloku-paid-launch-overnight-round2.md| new file mode 100644 |
| index 000000000..8b5001d79 |
| --- /dev/null |
| +++ b/docs/superpowers/specs/CODEX-FINDINGS-carloku-paid-launch-overnight-round2.md |
| @@ -0,0 +1,102 @@ |
| +## 1. Disposition table |
| + |
| +Reviewed `aed42815..8e7499ae`; the clean working tree matches `8e7499ae`. This was a static, local review. No files were modified, tests run, network commands issued, or Stripe/AWS CLIs invoked. Statements about tests without their fixes are based on source inspection. |
| + |
| +| # | Round 1 finding | Disposition and fix | Regression coverage; would it pass without the fix? | |
| +|---|---|---|---| |
| +| 1 | Mixed overwrite delivery accepted | **closed** for the original overwrite scenario. [delivery.go:257](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/awsbilling/delivery.go:257) refuses overwrite layouts; [delivery.go:294](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/awsbilling/delivery.go:294) confines files to the selected delivery directory. | [TestReadDeliveryRefusesTheOverwriteLayout:239](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/awsbilling/delivery_test.go:239) and the directory-escape case at [delivery_test.go:222](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/awsbilling/delivery_test.go:222) would fail without the respective guards. Neither simulates the original two-file overwrite sequence. The claimed AWS immutability guarantee is not independently established by these local fixtures. | |
| +| 2 | Partial backfill attributes another grantee’s transfer | **partly closed**. [ownership.go:321](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/objstore/ownership.go:321) requires completeness, but [walk.go:187](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/objstore/walk.go:187) can assert it without proving creation or absence of credentials. See new finding 4. | [TestGrantBackfillsLedgerForHealthyStore:321](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/objstore/ownership_test.go:321) and [TestStoreEgressFoldHoldsOnAnUnprovenLedger:378](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/console/meterfolds_test.go:378) would fail without the completeness protection. They do not reproduce two existing grantees with only one backfilled. | |
| +| 3 | Pre-adoption transfer attributed to adopter | **partly closed**. Explicit adoption normally produces an incomplete ledger through [walk.go:187](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/objstore/walk.go:187), which `OwnerAt` refuses. Adoption provenance can disappear on retry; see finding 4. | [TestAdoptedBucketResolvesNoOwner:388](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/objstore/ownership_test.go:388) would fail without the completeness gate. It uses a distinct adoption name, does not seed earlier transfer, and does not exercise a crashed grant followed by retry. | |
| +| 4 | Embedded-error HTTP 200 counted as success | **closed**. [durability.go:250](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/usage/durability.go:250) excludes both affected operations. | The `CompleteMultipartUpload` and `CopyObject` cases at [billable_test.go:26](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/usage/billable_test.go:26) would fail without the exclusion. These reproduce the persisted raw-200 ambiguity, not the HTTP error-body/retry sequence. | |
| +| 5 | Forced checkpoint billed to tenant | **closed**. [durability.go:245](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/usage/durability.go:245) permits only `replicate`. | The checkpoint case at [billable_test.go:25](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/usage/billable_test.go:25) would fail without the fix. It tests the resulting row; there is no move-worker integration reproduction. | |
| +| 6 | Never-registered durability box omitted | **closed** for the reported scenario. [durabilityfold.go:52](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/console/durabilityfold.go:52) checks independently known metrics/edge boxes. | [TestDurabilityFoldHoldsForAnActiveBoxWithNoLedger:412](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/console/meterfolds_test.go:412) reproduces an attested neighbour plus an edge-known box without a ledger. It would fail without the fix. | |
| +| 7 | Positive-cost CloudFront transfer discarded | **closed** for the reported usage row. [classify.go:129](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/awsbilling/classify.go:129) holds positive-cost CloudFront/inbound usage. Other line types still escape; see finding 6. | [TestStoreEgressFoldHoldsOnAChargedCloudFrontLine:398](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/console/meterfolds_test.go:398) reproduces the charged row and would fail without the fix. | |
| +| 8 | Later month’s usage billed to earlier month | **closed** for the original usage scenario. [delivery.go:325](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/awsbilling/delivery.go:325) checks supplied period ends on every row; [delivery.go:334](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/awsbilling/delivery.go:334) checks usage against the requested month. | [TestReadDeliveryRefusesUsageOutsideTheMonth:258](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/awsbilling/delivery_test.go:258) reproduces the stretched period and would fail without the period-end fix. Its second, honest-period case already fails the older parser. Non-usage windows remain exempt. | |
| +| 9 | Held month permanently forgotten | **partly closed**. [economicspass.go:1257](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/console/economicspass.go:1257) adds catch-up, but only through six months and only while the platform statement remains open. A September hold repaired the following April is still forgotten. Finding 7 covers another failure within the six-month window. | [TestStoreEgressHeldMonthIsRevisitedAfterTheNextMonthEnds:435](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/console/meterfolds_test.go:435) reproduces November repair of September and would fail without catch-up. It tests neither expiry from the search window nor partial statement-write failure. | |
| +| 10 | Chooser offers an invalid plan | **closed**. [billingpage.go:266](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/console/billingpage.go:266) uses checkout’s quantity and [billing.html:60](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/console/templates/billing.html:60) disables unavailable choices. | [TestBillingPageDoesNotOfferAPlanCheckoutWouldRefuse:348](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/console/billingpage_test.go:348) covers two members, the disabled choice, and checkout refusal. It would fail without the fix. | |
| + |
| +## 2. New findings |
| + |
| +1. **P1 — A house change can race checkout’s coupon snapshot.** |
| + [billingoperator.go:279](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/console/billingoperator.go:279), [billingservice.go:209](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/console/billingservice.go:209), [billingstore.go:218](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/console/billingstore.go:218). |
| + |
| + Checkout reads `House=false` and prepares an intent without a coupon. Before `beginBillingCheckout` saves that intent, the operator sets `House=true`: there is still no subscription or intent, so the change commits. Checkout then saves its stale coupon choice because the transaction checks membership and subscription state, but not the house snapshot. The house account is charged normally. Reversing the flag change grants the house coupon to an account now marked non-house. Reconciliation snapshots the current flag into assignment history, preserving the disagreement. |
| + |
| + **Smallest acceptable fix:** Bind the house value to the checkout request and revalidate it inside the transaction that inserts the intent. Retry coupon selection if it changed. The operator transaction alone cannot protect the earlier checkout read. |
| + |
| + [TestBillingOperatorHouseMarker:147](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/console/billingoperator_test.go:147) tests already-existing intents and subscriptions, not this interleaving. |
| + |
| +2. **P1 — Concurrent console processes can lower a quantity after its additions were marked processed.** |
| + [billingquantity.go:210](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/console/billingquantity.go:210), [billingquantity.go:251](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/console/billingquantity.go:251), [billingquantity.go:128](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/console/billingquantity.go:128). |
| + |
| + Both database and provider initially carry quantity 10. Process A reads an addition targeting 11 and pauses before its POST. Another member joins. Process B reads target 12, updates the provider, reconciles 12, and marks both additions processed. A then sends its previously prepared update to 11 under a different idempotency key and reconciles 11. Its mark finds no remaining events and succeeds. |
| + |
| + Twelve members now have quantity 11; all additions are processed. The account is excluded from future quantity passes by [billingquantity.go:81](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/console/billingquantity.go:81). The lock is explicitly [process-local](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/console/storelock.go:27); different idempotency keys do not order the requests. |
| + |
| + **Smallest acceptable fix:** Persist and serialize each account’s outstanding quantity operation across processes, including its target, generation and acknowledgement. Resolve an outstanding request before allowing a newer target. |
| + |
| + [TestQuantityWorkerRecoversALostResponse:247](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/console/billingquantity_test.go:247) covers one worker and an unchanged target, not competing operations. |
| + |
| +3. **P1 — An expired period’s pending addition can be charged for the next year.** |
| + [billingquantity.go:103](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/console/billingquantity.go:103), [billingquantity.go:238](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/console/billingquantity.go:238). |
| + |
| + An annual subscription bills ten members. Just before renewal, an eleventh member joins and leaves before the worker runs. The subscription renews at ten. The next pass still selects the old addition: the query filters by subscription, but ignores the event’s recorded period and occurrence time. It raises the renewed subscription to eleven and prorates that seat over the new year, although the person left before renewal. |
| + |
| + This is separate from the acknowledged missing renewal reduction: the worker actively introduces a new-period charge from an old-period event. |
| + |
| + **Smallest acceptable fix:** Bind additions and provider updates to the subscription period they belong to. Hold expired or mismatched-period events for resolution instead of applying their peak to the current period; repeat the period check when marking them processed. |
| + |
| + [TestQuantityWorkerChargesTheTransientSeat:220](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/console/billingquantity_test.go:220) exercises only one period. |
| + |
| +4. **P1 — `minted` does not prove that this grant created the bucket or preceded all credentials.** |
| + [walk.go:187](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/objstore/walk.go:187), [ownership.go:258](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/objstore/ownership.go:258). |
| + |
| + A bucket already exists at an app’s deterministic `MintedBucket` name and another legacy grantee can access it. A first grant with no explicit adoption argument and no `Provisioned` block creates a complete ledger naming only the new app. [EnsureBucket:277](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/objstore/aws.go:277) accepts `BucketAlreadyOwnedByYou`, so the walk succeeds without discovering that its completeness claim was false. Transfer by the existing grantee is attributed to the new app. |
| + |
| + The same mistake occurs after explicit adoption of that deterministic name crashes after saving the grant block but before saving ownership: a retry without the adoption argument computes `minted=true`. Likewise, `Provisioned=nil` is possible after credentials were delivered but recording success failed. |
| + |
| + **Smallest acceptable fix:** Persist trustworthy creation/adoption provenance and require proof that no earlier credentials or grantees existed before asserting completeness. Unknown creation or recovery states must remain incomplete; name equality and a missing success block are insufficient. |
| + |
| +5. **P1 — Teardown closes ownership while credentials can still access a recreated bucket.** |
| + [walk.go:558](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/objstore/walk.go:558), [walk.go:566](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/objstore/walk.go:566). |
| + |
| + App A has a complete ledger. Teardown deletes its bucket and closes A’s interval, then crashes or fails at `DeleteUser`. A’s credentials and bucket-name policy remain. App B is subsequently granted that same name; the grant recreates the bucket and appends B to the existing complete ledger. In a later hour, A’s surviving credentials can access the recreated bucket, but `OwnerAt` resolves only B. B can therefore be billed for A’s transfer. |
| + |
| + **Smallest acceptable fix:** Keep the old grantee represented until credential revocation is confirmed, and prevent bucket-name reuse from outrunning that revocation. Closing after bucket deletion alone is insufficient. |
| + |
| + [TestTeardownClosesOwnershipOnlyAfterTheBucketIsGone:453](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/objstore/ownership_test.go:453) checks deletion ordering but not credential-deletion failure followed by name reuse. |
| + |
| +6. **P1 — Positive-cost transfer on a non-usage line still disappears without a hold.** |
| + [classify.go:115](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/awsbilling/classify.go:115), [delivery.go:334](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/awsbilling/delivery.go:334). |
| + |
| + A synthetic invoiced `AmazonS3` row with type `Fee`, usage type `EUW1-DataTransfer-Out-Bytes`, amount `1`, and cost `0.09` is accepted but classified `not_usage_line`. That reason does not hold billing. With otherwise valid evidence, the delivery can make the source complete while this positive transfer charge contributes nothing. |
| + |
| + Non-usage rows also bypass usage-window bounds, so the same row can carry an October window in a September delivery. The supplied billing-period end is checked, but the charged ambiguity still disappears. The repository does not establish whether AWS currently emits this exact combination; the reader accepts it without the required hold. |
| + |
| + **Smallest acceptable fix:** Before excluding an unsupported line type, detect positive-cost transfer and hold it unless an explicit supported rule explains the exclusion. Preserve legitimate credit/refund handling without counting ambiguous charges. |
| + |
| +7. **P1 — Closing the platform statement can permanently abandon another account’s open statement.** |
| + [economicspass.go:1282](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/console/economicspass.go:1282), [economicspass.go:1445](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/console/economicspass.go:1445). |
| + |
| + During November catch-up for September, the report becomes provable. Writing the platform statement succeeds, but writing a tenant statement fails transiently. Per-account write failures are logged and skipped, so the platform statement remains closed while the tenant statement remains open. Every subsequent catch-up pass checks only the platform statement and skips September. The tenant can never close through this worker, even after the write failure clears. |
| + |
| + **Smallest acceptable fix:** Determine catch-up completion from all relevant account statements, or persist a month-level completion record only after every required write succeeds. Retain unresolved months beyond the rolling six-month discovery window. |
| + |
| +8. **P2 — Ownership evidence discards the completeness timestamp that affects attribution.** |
| + [evidence_month.go:103](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/usage/evidence_month.go:103), [ownership.go:327](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/objstore/ownership.go:327). |
| + |
| + Pin September evidence for an interval beginning September 1 with `CompleteSince=September 1`. Replace the stored completeness timestamp with September 20, leaving intervals unchanged. `OwnerAt` now refuses September 3, but both month projections still encode only `complete:true`. Consequently, [evidence verification](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/usage/evidence.go:196) accepts changed attribution evidence. |
| + |
| + Ordinary ownership writes prohibit this mutation, which limits exposure, but the verifier is intended to detect changed stored inputs independently. |
| + |
| + **Smallest acceptable fix:** Include the effective completeness boundary in the month projection, preserving distinctions that change any hour’s attribution. |
| + |
| + [TestBucketOwnershipEvidenceIsPinnedByItsMonthProjection:47](/home/paulca/amadan.net/carlos/platform-paid-launch/internal/usage/billable_test.go:47) never supplies `complete_since`, so it does not test the new field. |
| + |
| +## 3. Handoff claims the code contradicts |
| + |
| +- [AGENT_HANDOFF.md:34](/home/paulca/amadan.net/carlos/platform-paid-launch/implementation/carloku-paid-launch/AGENT_HANDOFF.md:34) says the classifier “holds on any charged line it does not count.” Positive-cost transfer on unsupported line types bypasses that protection, as finding 6 demonstrates. |
| +- [AGENT_HANDOFF.md:35](/home/paulca/amadan.net/carlos/platform-paid-launch/implementation/carloku-paid-launch/AGENT_HANDOFF.md:35) says ownership resolves only when the ledger was created by the grant that minted the bucket, and that adopted/pre-existing stores hold. The `minted` predicate does not establish either guarantee; finding 4 gives accepted counterexamples. |
| +- [AGENT_HANDOFF.md:74](/home/paulca/amadan.net/carlos/platform-paid-launch/implementation/carloku-paid-launch/AGENT_HANDOFF.md:74) describes all ten round 1 findings as fixed. Ownership completeness and recovery of held months remain only partly closed. |
| + |
| +**Verdict: reject.** |
| \ No newline at end of file |
diff --git a/docs/superpowers/specs/CODEX-REVIEW-BRIEF-carloku-paid-launch-overnight-round3.md b/docs/superpowers/specs/CODEX-REVIEW-BRIEF-carloku-paid-launch-overnight-round3.md| new file mode 100644 |
| index 000000000..1e56d933f |
| --- /dev/null |
| +++ b/docs/superpowers/specs/CODEX-REVIEW-BRIEF-carloku-paid-launch-overnight-round3.md |
| @@ -0,0 +1,35 @@ |
| +# Review brief, round 3: Carloku paid launch, overnight changes of 2026-10-01 |
| + |
| +Read the round 1 brief (`CODEX-REVIEW-BRIEF-carloku-paid-launch-overnight.md`) for the fixed rulings and the output rules, then `CODEX-DISPOSITIONS-carloku-paid-launch-overnight.md`, which now covers round 1, round 2 (`CODEX-FINDINGS-...-round2.md`) and the Opus review (`OPUS-REVIEW-carloku-paid-launch-overnight.md`). |
| + |
| +Review the branch between `aed42815` and the commit named in the prompt. The changes since round 2 are `git diff 8e7499ae <commit>`. |
| + |
| +Two more rulings are now fixed: |
| + |
| +6. `CONSOLE_BILLING_PRORATION` is unset and stays unset until Paul decides. The worker must make no provider call without it. Whether additions are charged at once or on the next invoice is not yours to decide; whether the mechanism is safe under either is. |
| +7. An existing or adopted bucket whose ownership history cannot be proved must hold. Holding is acceptable; attributing is not. |
| + |
| +## First: a disposition table |
| + |
| +For every round 2 finding (Astra 1 to 8) and every Opus finding (N1 to N8): closed, partly closed or not closed, with the file and line of the fix and of the test that reproduces the scenario, and whether that test would pass without the fix. |
| + |
| +## Then: new findings only |
| + |
| +What changed since round 2, and where to push: |
| + |
| +- `internal/console/billingquantity.go`: rewritten. One persisted operation per account (`billing_quantity_operations`), current-period additions only, stale-period and ended-subscription additions closed without a provider call, the send window, the replay limit, `resolveQuantityOperation`. Find an interleaving of two processes, a crash at any line, a renewal, a cancellation, a membership change or a policy change that double-charges, lowers a quantity, raises the wrong subscription or period, marks an addition the billed quantity does not cover, loses an addition, or leaves an operation outstanding forever. Check `beginQuantityOperation`'s conflict clause, the acknowledgement, and what `markQuantityAdditionsProcessed` re-checks. |
| +- `internal/console/billingstore.go` `beginBillingCheckout` (house marker against coupon) and `internal/console/billingoperator.go` (lock, changed flag). |
| +- `internal/objstore/walk.go`, `objstore.go`, `provisioner.go`, `aws.go`, `memprovisioner.go`, `ownership.go`: `GrantBlock.Minted`, `BucketProber`, the completeness condition, the teardown order. Find a walk that claims completeness for a bucket someone else could reach, including through a crash and re-run at each step, a re-declared app, and an adopter of a minted name. |
| +- `internal/awsbilling/classify.go`: `transfer_adjustment`. |
| +- `internal/usage/evidence_month.go`: the projected completeness boundary. |
| +- `internal/console/durabilityfold.go`: the span comparison against metrics and edge windows. |
| +- `internal/console/economicspass.go`: the catch-up walk (36 months, stops at the first month with no platform statement, per-account check). |
| + |
| +Three design documents under `docs/superpowers/plans/` dated 2026-10-01 describe work not yet built (bucket ownership attestation, partial-month billing, durability attestation path). Read them for anything that would make the code already on the branch wrong; do not review them as code. |
| + |
| +## What to return |
| + |
| +1. The disposition table. |
| +2. New findings, most severe first, in the round 1 format. |
| +3. Handoff claims the code contradicts (`implementation/carloku-paid-launch/AGENT_HANDOFF.md`). |
| +4. A one-line verdict: accept, accept with fixes, or reject. |
diff --git a/docs/superpowers/specs/OPUS-REVIEW-carloku-paid-launch-overnight.md b/docs/superpowers/specs/OPUS-REVIEW-carloku-paid-launch-overnight.md| new file mode 100644 |
| index 000000000..c8065491e |
| --- /dev/null |
| +++ b/docs/superpowers/specs/OPUS-REVIEW-carloku-paid-launch-overnight.md |
| @@ -0,0 +1,137 @@ |
| +# Opus production and authority review, round 2: Carloku paid launch, `aed42815..8e7499ae` |
| + |
| +I did this review read-only. I read the code statically, modified nothing, and ran no tests and no network commands. The claim that a reproduction "fails without the fix" is the dispositions document's own; I did not re-verify it by reverting anything. |
| + |
| +## 1. Round 1 dispositions |
| + |
| +| # | Finding | Status | Fix | Reproducing test | Would the test pass without the fix? | |
| +|---|---|---|---|---|---| |
| +| 1 | Partially overwritten delivery accepted | **Closed** | `internal/awsbilling/delivery.go:255-263` refuses a manifest that is not in a stamped directory (`ErrMutableDelivery`). `delivery.go:294` confines data files to that directory. | `delivery_test.go:239` `TestReadDeliveryRefusesTheOverwriteLayout`, and `:199` for "outside the delivery directory" | No. The overwrite fixture was accepted before. | |
| +| 2 | Partial backfill bills a bucket-mate | **Closed**, with one residual edge (N6) | `internal/objstore/ownership.go:257-263` (`CompleteSince` only at creation), `:320-329` (`OwnerAt` answers nothing without it), `appendOnly` `:167-171`, `walk.go:187` (`minted`) | `ownership_test.go:321` `TestGrantBackfillsLedgerForHealthyStore`, `:411` `TestCompletenessCannotBeClaimedLater`, `meterfolds_test.go:378` | No. The backfilled ledger resolved an owner before. | |
| +| 3 | Adoption assigns pre-adoption transfer | **Closed** | Same mechanism: `walk.go:187` sets `adoptBucket != ""` to not minted | `ownership_test.go:388` `TestAdoptedBucketResolvesNoOwner` | No | |
| +| 4 | HTTP 200 with an embedded error counted as a write | **Closed** | `internal/usage/durability.go:250-253` | `billable_test.go:13` | No | |
| +| 5 | Move checkpoints billed to the tenant | **Closed** | `durability.go:245-248` (only `replicate` counts) | `billable_test.go:13` | No | |
| +| 6 | A box that never registered durability is left out of completeness | **Partly closed** | `internal/console/durabilityfold.go:48-61` | `meterfolds_test.go:412` | No. But the check keys on whether the box has a ledger directory at all, not on whether its durability window covers the day. See N3. | |
| +| 7 | Positive-cost CloudFront transfer discarded | **Closed** | `internal/awsbilling/classify.go:122-136`, `IsHold` `:49-52` | `classify_test.go:63`, `meterfolds_test.go:398` | No | |
| +| 8 | A later month's usage billed to an earlier month | **Closed** | `delivery.go:325-337` (period end must equal the month end; the usage window must lie inside the month read) | `delivery_test.go:258` | No | |
| +| 9 | A transfer hold becomes permanent after the following month ends | **Partly closed** | `internal/console/economicspass.go:1257-1268` | `meterfolds_test.go:435` | No. But the catch-up stops at six months and uses the platform account's statement as its sentinel. See N4. | |
| +| 10 | Chooser offers a plan checkout refuses | **Closed** | `internal/console/billingpage.go:262-275`, `templates/billing.html:60,64` | `billingpage_test.go:348` | No | |
| + |
| +## 2. New findings, most severe first |
| + |
| +### N1. P1: The quantity worker raises a subscription for additions from a period that has already renewed |
| + |
| +**Where:** `internal/console/billingquantity.go:103-105`, used at `:218` and `:225-252`. |
| + |
| +`pendingQuantityAdditions` filters by subscription only. It never compares an event's `period_start` or `period_end` with the subscription's current period. |
| + |
| +**Scenario:** |
| +1. Tonight the policy is unset, and the handoff says it must stay unset until Paul decides. A Community account billed for 12 adds member A, so the desired quantity is 13. The addition stays pending. |
| +2. A leaves in the same period. Per the spec, A "comes off at renewal". |
| +3. The subscription renews at 12. |
| +4. Months later Paul sets `CONSOLE_BILLING_PRORATION=immediate`. The peak is still 13, so the worker raises the subscription to 13 with `always_invoice`. |
| + |
| +The customer is charged a prorated seat in a period A never belonged to. Because nothing lowers the quantity at renewal yet, they are also charged for it at every renewal after that. |
| + |
| +Every account that adds a member before the policy is set carries this. Setting the policy is the planned rollout path, so this is the expected outcome, not an edge case. |
| + |
| +**Smallest acceptable fix:** only raise for additions whose `period_start` equals the subscription's current `period_start`. For additions from an earlier period, make no provider call: file a review, or close them with a non-charging outcome. |
| + |
| +### N2. P1: The house marker can change between checkout's read of it and the intent's write |
| + |
| +**Where:** |
| +- `billingoperator.go:81-134` does not take the per-account lock `b.locks.lock(acct.Sqid,"billing")`. |
| +- `billingservice.go:210-221` reads `state.House` and chooses the coupon several Stripe round trips before the intent row exists. |
| +- `billingstore.go:189-237` (`beginBillingCheckout`) never re-checks `house` against `want.Coupon`. |
| + |
| +**Scenario:** An owner starts checkout and the page reads `house=false`. While the handler is still calling Stripe (price, product, tax registrations, portal), an operator sets `house=true`. That write is allowed, because there is no intent and no subscription yet. The intent and the Stripe Checkout are then created with no coupon. |
| + |
| +After purchase, reconcile snapshots `house=1` into the assignment (`billingstore.go:456-457`). The customer pays the full annual price, while the history says house and overages are waived. |
| + |
| +The reverse flip is also possible: a coupon ends up on a subscription that the history calls a paying account. |
| + |
| +**Smallest acceptable fix:** inside `beginBillingCheckout`'s transaction, refuse (with a conflict) when `billing_accounts.house != (want.Coupon != "")`. Also take the billing lock in the house door. |
| + |
| +### N3. P2: The durability fold still skips a box's days before its durability registration (finding 6 residual) |
| + |
| +**Where:** `durabilityfold.go:48-61` together with `:62-65`. |
| + |
| +`ledgered` means "a ledger directory exists", with no reference to the day being checked. For a box that has a directory, the metrics and edge check is skipped entirely. Its own durability window (`usage/lifecycle.go:343`, the per-source first-seen time) then says "not expected" for any day before durability registered, so nothing holds. |
| + |
| +**Scenario:** Box B's ledger fails to open on days 1 to 10 while metrics and edge register it. It opens on day 11 and registers durability from day 11. On day 5, B has a directory, so the metrics and edge check is skipped. Its durability window is not expected on day 5, so the fold loop skips it too. Day 5 reads complete on box A's attestation alone. |
| + |
| +The effect is an undercharge, and it is masked today because attestation is false in production. |
| + |
| +**Smallest acceptable fix:** for each box and source in `active` whose window is expected on day `d`, require that box's durability window also to be expected on `d`, whether or not a directory exists. |
| + |
| +### N4. P2: The month catch-up has a hard six-month horizon, and there is no way out of the hold today |
| + |
| +**Where:** `economicspass.go:1257-1268`, `:1275`, and `storeegressfold.go` (`HoldClose` is true for every result that is not complete, including no delivery at all). |
| + |
| +**Scenario:** The dispositions say the four existing flagship stores hold store transfer until an operator attests their history, and no such door exists. So once `StoreEgressReportPrefix` is set, every month with charged transfer from those stores stays open, for every meter of every account. |
| + |
| +Each tick re-sweeps up to five extra whole months. A month more than six months back is never looked at again and stays open permanently: finding 9 again, one horizon later. |
| + |
| +The sentinel is also the platform account's statement (`:1281-1290`). If the closing pass fails to write one account's statement but writes the platform's, that account's month is never retried once it is more than one month back. |
| + |
| +**Smallest acceptable fix:** |
| +- Revisit every month whose platform statement is still open, not only six. |
| +- Check each account's statement, not the platform's. |
| +- Log loudly when a month is still held past the horizon. |
| +- Do not configure the report prefix in production until the attestation door exists. |
| + |
| +### N5. P2: Additions against a subscription that has ended stay pending forever and hold the account's freeze |
| + |
| +**Where:** `billingquantity.go:215-224` and `:231-232`, with `billingquantityaudit.go:35`. |
| + |
| +The worker only looks at `state.Subscription` and returns `done` when that subscription is not active. Meanwhile `quantityObligationTx` holds on any unprocessed addition for the account, whatever subscription it belongs to. |
| + |
| +**Scenario:** A team account adds a member (pending), then cancels, then later subscribes again. The old subscription's addition is never processed, so the new subscription's monthly freeze is held permanently. The same happens to a canceled subscription whose peak is above its quantity. |
| + |
| +**Smallest acceptable fix:** mark additions bound to a subscription that is over, or that is not the account's current subscription, as processed. Use outcome `included` with a reference such as `subscription_ended:<id>`, and make no provider call. |
| + |
| +### N6. P3: `minted` is computed from the current call, not from what the bucket's history proves |
| + |
| +**Where:** `walk.go:187`. |
| + |
| +Account names cannot contain a hyphen, so two apps cannot mint the same bucket name. The remaining edge case: before ledgers existed, an operator adopted `prefix-acctA-appA` for app B. Later A is declared and granted. There is no ledger, and `minted` is true, so a complete ledger is created while B still holds a working key. A is then billed for B's transfer. |
| + |
| +The same thing happens on a re-run of a crashed adopt-grant whose adopted name equals the minted name, because `adoptBucket` comes from the re-run's arguments, not from `rec.Grant`. |
| + |
| +**Smallest acceptable fix:** only claim completeness when the bucket did not exist before this grant (a HeadBucket check before step 3). Derive "adopted" from `rec.Grant.Source` rather than from the argument. |
| + |
| +### N7. P3: Refunds and credits on S3 transfer are ignored |
| + |
| +**Where:** `classify.go:113-115`. |
| + |
| +A non-usage S3 line (`Refund`, `Credit`, `Fee`) with a transfer usage type returns `ReasonNotUsage`, which is not a hold. If AWS refunds transfer charges in the month, the tenant is still billed the bytes AWS gave back. That runs against rulings 2 and 4. |
| + |
| +**Smallest acceptable fix:** in `IsHold`, hold on any non-usage S3 line with non-zero cost whose usage type classifies as transfer. |
| + |
| +### N8. P3: Minor issues in the quantity worker and the operator doors |
| + |
| +- **Idempotency key.** The key (`billingquantity.go:251`) leaves out the proration behaviour. Changing the policy within 24 hours of a failed attempt reuses the key with different parameters, and Stripe refuses it until the key expires. |
| +- **Outcome labels.** After a crash between reconcile and mark, a charged raise is recorded as `included`/`quantity_already_billed` (`:196-199`). Separately, `always_invoice` records `charged` even when the prorated invoice's payment fails. |
| +- **Review re-filing.** `billing_quantity_mismatch` is filed again on every pass (`:262`), and `fileBillingReviewTx` resets `resolved_at`. An operator's resolution reopens within a pass while the mismatch persists, so the door gives no real path out of it. |
| +- **Audit on no-op house set.** Setting the house marker to its current value writes no `billing_house_changes` row, but `handleAPIBillingHouseSet` still writes a `billing-house-set` general audit entry (`billingoperator.go:114-116`, `:286`). |
| +- **Unavailable-plan copy.** The unavailable-row text (`billing.html:64`) is Personal-specific but is shown on any unavailable row. |
| +- **Stale comment.** The doc comment at `economicspass.go:1216` ("Closing has no catch-up") is now wrong. |
| + |
| +**Checked and found sound:** |
| +- The doors gate a CLI bearer token, then operator status, with a 404 for everyone else. Signed-out callers get the same 401 as every other `/api` route. The operator bit is re-checked inside the transaction, and both writes append their audit row in the same transaction. |
| +- With the policy unset the worker makes no provider call (`:157-159`, and the test at `billingquantity_test.go:188`). |
| +- Before raising, the worker checks the subscription's identity, mode, customer, price, and single item. |
| +- The mark re-checks, under the membership lock, that the billed quantity covers the peak. |
| +- The billing page's gate order, owner-only forms, and absence of any Stripe identifier or postal code are all correct. |
| +- Nothing sets attestation true. |
| + |
| +## 3. Handoff claims the code contradicts |
| + |
| +- `AGENT_HANDOFF.md` item 3 says "the console fold holds any day on which a box known to metrics or edge has no ledger." It only holds when the box has **no ledger directory at all**. The days before a box's durability registration are not held (N3). |
| +- Item 4 says the classifier "holds on any charged line it does not count." Charged non-usage S3 transfer lines (refund, credit, fee) are neither counted nor held (N7). |
| +- "Next actions" item 4 says to build the quantity worker and the two operator doors. Items 7 and 8 of the same file say both are built. |
| +- Item 8 implies the only gap before team plans is lowering the quantity at renewal. Turning the policy on as described would also raise subscriptions for additions from periods that have already renewed (N1). |
| + |
| +## 4. Verdict |
| + |
| +**Accept with fixes.** N1 and N2 must be fixed before `CONSOLE_BILLING_PRORATION` is set or the house door is used in production. N3 to N5 must be fixed before the report prefix is configured or attestation is turned on. |
diff --git a/implementation/carloku-paid-launch/AGENT_HANDOFF.md b/implementation/carloku-paid-launch/AGENT_HANDOFF.md| index ae4e4b384..8a64add27 100644 |
| --- a/implementation/carloku-paid-launch/AGENT_HANDOFF.md |
| +++ b/implementation/carloku-paid-launch/AGENT_HANDOFF.md |
| @@ -30,12 +30,12 @@ When a review is approved, apply each string byte for byte: the site by replacin |
| 1. Plans and calculations, Stripe boundary, entitlements and membership, quantity history, invoice engine: as described in the September handoff (kept in git history at `aed42815`). Unchanged in this run. |
| 2. Console GET `/a/{sqid}/billing` (`billingpage.go`, `templates/billing.html`): current plan, owner-only chooser posting to the existing checkout door, `?country=` price preview computed from the plans terms, error and pending banners, portal button once a customer exists, read-only for members, byte-identical 404 for non-members, closed state when billing is off. `plans.Countries()` exposes the embedded country list. |
| -3. Durability ledger: the epoch read race is fixed (epoch and created-at are immutable after recover) with a `-race` regression. An unmetered replication fallback on a box whose ledger opened now files an escape (`DurabilityLedger.Escaped`): the box-day is incomplete and the owner is excluded for the run. A box whose ledger failed to open has no publisher and files nothing; the console fold holds any day on which a box known to metrics or edge has no ledger. Attestation is still false everywhere in production. |
| -4. `internal/awsbilling`: Cost and Usage Report reader (versioned deliveries only), newest-delivery selection, digests, charged S3 transfer classifier that holds on any charged line it does not count. |
| -5. `internal/objstore/ownership.go`: append-only bucket ownership ledger, opened at grant, closed at teardown. It resolves an owner only when created by the grant that minted the bucket; adopted and pre-existing stores hold. |
| +3. Durability ledger: the epoch read race is fixed (epoch and created-at are immutable after recover) with a `-race` regression. An unmetered replication fallback on a box whose ledger opened now files an escape (`DurabilityLedger.Escaped`): the box-day is incomplete and the owner is excluded for the run. A box whose ledger failed to open has no publisher and files nothing; the console fold holds any day on which a box that metrics or edge expected is not expected by the durability source for at least the same span (no ledger at all, or one registered later). Attestation is still false everywhere in production. |
| +4. `internal/awsbilling`: Cost and Usage Report reader (versioned deliveries only), newest-delivery selection, digests, charged S3 transfer classifier. It holds on a charged usage line it does not count and on any non-usage line (fee, refund, credit) with a cost on a transfer usage type. |
| +5. `internal/objstore/ownership.go`: append-only bucket ownership ledger, opened at grant, closed at teardown. It resolves an owner only for a ledger that claims completeness, which needs a grant block recorded as minting, no delivered credentials and a probe showing the bucket did not exist yet. Adopted buckets, pre-existing stores and anything the probe cannot answer for hold. Teardown closes the interval only after the credentials are revoked. |
| 6. Statement folds: `console/durabilityfold.go` (`durability_writes_1k`, policy in `usage.BillableReplicationWrites`) and `console/storeegressfold.go` (`store_egress_gb`). `usage.MeterSource` now names a source for both meters. Month evidence pins ownership ledgers by their month projection and knows the durability box directory. |
| 7. Operator API doors (`billingoperator.go`): list and resolve billing review holds, list and set the house marker. No CLI verb yet. |
| -8. Team quantity worker (`billingquantity.go`) behind `CONSOLE_BILLING_PRORATION` (`immediate` or `next_invoice`). Unset, which is how it must stay until Paul decides, it makes no provider call. It does not yet lower the quantity at renewal. |
| +8. Team quantity worker (`billingquantity.go`) behind `CONSOLE_BILLING_PRORATION` (`immediate` or `next_invoice`). Unset, which is how it must stay until Paul decides, it makes no provider call. One persisted operation per account keeps two console processes from sending conflicting targets. Only additions of the subscription's current period can raise it; older ones are closed uncharged with a review; additions of an ended subscription are closed. It does not yet lower the quantity at renewal. |
| 9. Stripe provisioning plan `stripe-provisioning.md` and print-only `stripe-provision.sh` (verified never to invoke the CLI). |
| 10. Plans: `docs/superpowers/plans/2026-10-01-store-transfer-cur-collector.md` and `2026-10-01-durability-attestation-path.md`. |
| @@ -49,9 +49,10 @@ The monthly freeze requires all five billed meters complete. Two cannot be, in p |
| ## Still missing from the full requirements audit |
| - Quantity at renewal: the worker raises a subscription for added members but nothing lowers it at renewal for members who left, so a renewal bills the old quantity. Must be built before team plans are sold. The proration policy itself is Paul's decision and the switch is unset. |
| -- Partial first month and free-to-paid months hold permanently (`plans.ErrAssignmentHistory` is quiet and files no review). Months spanning two tax codes hold with a review; per-line tax codes are not implemented. |
| +- Partial months: a first month that starts mid-month, a month a subscription ends in and a free-to-paid change all hold permanently and quietly today (`plans.ErrAssignmentHistory`). This is required engineering, not a waiver: the design is `docs/superpowers/plans/2026-10-01-partial-month-billing.md` (daily meters on the statement, whole paid days counted against a prorated allowance). Not built. |
| +- Months spanning two tax codes hold with a review; per-line tax codes are not implemented. |
| - The operator doors have no `carlos` verb and no documentation page, and have not had their own independent review. |
| -- An operator door to attest a bucket's ownership history (existing and adopted stores): missing, and it holds store-transfer billing for the four existing flagship stores. |
| +- Ownership history for existing and adopted store buckets: their ledgers cannot vouch for who had access, so their charged transfer holds. The evidence-backed way out is designed in `docs/superpowers/plans/2026-10-01-bucket-ownership-attestation.md` (match store records against an IAM inventory, claim completeness from that instant, never earlier). Not built. Transfer before a bucket's attestation can never be attributed, so attest before the first billable month. |
| - Free tier: no Free entitlement, no app cap, no storage refusal, no per-plan hibernation idle, no flush-only replication class, nothing at runtime reads entitlements. |
| - Dormancy archive and warnings, recognised-revenue pool accounting at 15 percent, the books site: not started. |
| - Statement rebuild door for a closed month: missing. |
| @@ -71,7 +72,7 @@ Merchant `acct_1UD7LXI4Nrr6AEsO`, Team Tito Limited, Ireland, EUR; Irish standar |
| ## Next actions, in order |
| 1. Apply approved copy from both reviews when `result.json` arrives. |
| -2. Independent review: round 1 by Astra rejected with ten findings, all accepted and fixed (`docs/superpowers/specs/CODEX-DISPOSITIONS-carloku-paid-launch-overnight.md`). Round 2 and the Opus production review must pass on the exact tip before anything lands. |
| +2. Independent review: Astra rejected round 1 (ten findings) and round 2 (eight); the Opus production review accepted with fixes (eight). All were accepted and fixed with reproductions (`docs/superpowers/specs/CODEX-DISPOSITIONS-carloku-paid-launch-overnight.md`). A further round of both must pass on the exact tip before anything lands; do not read "fixed" as "accepted". |
| 3. Scoped approvals: Cost and Usage Report export; Stripe catalog, portal, webhook and the `rk_live` key; then a test-mode rehearsal and a real purchase. |
| -4. Build the quantity and proration worker behind an explicit policy switch, the two operator doors, then Free-tier enforcement. |
| +4. Build partial-month billing and the bucket ownership attestation (both designed in `docs/superpowers/plans/`), quantity reduction at renewal, a `carlos` verb and documentation for the operator doors, then Free-tier enforcement. |
| 5. Choose and build the attestation path. Until then replication writes hold every month. |