The night's records: the enablement plan, the readiness checklist with evidence, the overage rate proposal, and the handoff at 04:15
ENABLEMENT.md is the scoped plan to enable payments: what must exist, the catalog of 36 prices, the two cutovers, the runtime key's permissions, the webhook and portal, the release order, the rollback, and the approvals each with its state (L, the approved book version, and M, the cutover note, are new). READINESS.md names every branch's tip, gate log, CI run and review verdict, the behaviour the tests verify, what is not verified and what would verify it. OVERAGE-RATES-PROPOSAL.md is the USD and GBP rate table and floors for approval, marked as the draft they are. The readiness record's remaining list is revised for the 36-price catalog and the book. The handoff's NEEDED list no longer asks for copy that was approved on 2026-10-01 and names what is actually open.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
5 files changed,
+387
−30
.claude/HANDOFF.md+23 −28implementation/carloku-paid-launch/ENABLEMENT.md+202 −0implementation/carloku-paid-launch/OVERAGE-RATES-PROPOSAL.md+67 −0implementation/carloku-paid-launch/READINESS.md+89 −0implementation/carloku-paid-launch/stripe-live-readiness.json+6 −2
CI passed — run details
diff --git a/.claude/HANDOFF.md b/.claude/HANDOFF.md| index 133410641..1c8f55d95 100644 |
| --- a/.claude/HANDOFF.md |
| +++ b/.claude/HANDOFF.md |
| @@ -7,42 +7,37 @@ |
| 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 23:10 Dublin on 2026-10-01, during the second overnight run (brief: "continue overnight until we're ready to enable payments"; ready means reviewed, tested, integrated code and a scoped enablement plan, not payments on). Billing is OFF; no live Stripe or AWS write; nothing deployed; no merge to main. |
| +Written 04:15 Dublin on 2026-10-02, during the second overnight run (brief: "continue overnight until we're ready to enable payments"; ready means reviewed, tested, integrated code and a scoped enablement plan, not payments switched on). Billing is OFF everywhere. No live Stripe write, no AWS change, no charge, no merge to main. Every payment anyone can see is a labelled simulation. |
| -Review servers, all answering 200: pricing http://100.119.180.57:38009, console billing page http://100.119.180.57:45033, demo http://100.119.180.57:41881 (old EUR-only redesigned binary; Keymail and Stripe simulated; to be rebuilt from the integrated source tonight). |
| +Review servers, all answering 200: pricing page `http://100.119.180.57:38009` (static, rebuilt from site `2fc6388`), signup and billing demo `http://100.119.180.57:41881` (rebuilt 04:0x from the currency branch's tip `8958392b`, price book configured, USD/EUR/GBP end to end; Keymail and Stripe simulated), console billing copy `http://100.119.180.57:45033` if still up. |
| -Decisions and approvals received 2026-10-01 evening: |
| -- Fixed annual base prices, confirmed for implementation (not production): Personal 28 USD / 24 EUR / 22 GBP; Community 6 / 5 / 4.50 per member; Business 60 / 50 / 45 per member; team minimum 10. Overage USD/GBP rates and floors remain draft; a full rate table is owed for morning approval. |
| -- Currency rulings: USD default (international and unknown), EUR for EU member states, GBP for the UK; visible override; the chosen currency persists across pricing, signup, annual and monthly bills; band from billing country only; allowances never change with currency. |
| -- Copy approved verbatim: the five currency strings (site `copy-review/currency/result.json`) and the regrouped pricing page (`copy-review/pricing/result.json`, latest user edits preserved). Both committed on the site branch `feat/paid-pricing-20260916` at 7bd8fcc. |
| +The durable records are on this branch under `implementation/carloku-paid-launch/`: `READINESS.md` (every branch's tip, gate log, CI and review state, with the verified behaviour), `ENABLEMENT.md` (the scoped plan to enable payments: catalog of 36, cutovers, key permissions, webhook, portal, release order, rollback, approvals), `OVERAGE-RATES-PROPOSAL.md` (the USD/GBP rate table for approval), `stripe-live-readiness.json` (remaining list revised for 36 prices and the book), `OUTSTANDING.md` (the metered model's open items and approvals A to K). |
| -Branches (all on the forge, none merged; the local gate is not CI): |
| -- `feat/carloku-multi-currency-20261001`: the price book and one stored currency per purchase, end to end (plans, Stripe client, console store, checkout, reconcile, version 2 calculation, invoice engine, config, provisioning plan and script). Review 1 at 9d104f1d (Opus accept with fixes, Astra reject) fixed in a88a39e6; review 2 at a88a39e6 (Opus accept with fixes, Astra reject: older-build barrier on mixed history, bound session not expired, paid session wedge, currency_options) fixed in a9304f16 (gate running at 23:10). 5047494b merges the layout branch's templates in; the billing page's currency controls are next, then a gate on the integrated tip and review 3. Design: `docs/superpowers/specs/2026-10-01-multi-currency-price-book-design.md`; dispositions `DISPOSITIONS-multi-currency.md`; discussion #198. |
| -- `feat/carloku-billing-layout-20261001` c1ae91a0: the layout session's billing page redesign, committed as handed over, merged into the currency branch. |
| -- `feat/carloku-storage-sole-holder-20261001` 3af02a65: review 8 both accept with fixes; fixes applied; review 9 running. Needs approval K. |
| -- `feat/carloku-metered-replicator-20261001` 4c9cdb3c: third build; review 3 Opus accept with fixes (one new P1: a single-file unit route follows links above it; a swap during litestream's first open), Astra reject. Reports on the branch; dispositions and fixes owed. |
| -- `fix/carloku-observe-routes-cleanup-20261001` 023e106c: both reviews accept; ready to merge into the launch branch. |
| -- `feat/carloku-late-request-fencing-20261001` 1b61de82, `feat/carloku-bucket-hour-evidence-20261001` 0aa1fbec: unchanged since the morning handoff (rejected / needs approval J). |
| - |
| -Launch branch tip 12e70d95 (this commit adds only this file). Last product-code commit cd8821c6; Amadan CI passed on it and on the handoff tips through a78d5982. |
| +Decisions received 2026-10-01 evening, applied: the annual bases (28/24/22, 6/5/4.50, 60/50/45 USD/EUR/GBP, team minimum ten) confirmed for implementation only; the currency rulings (USD default, EUR for EU member states, GBP for the UK, visible override, one stored currency for subscription and overages, bands by billing country only); the five currency strings and the regrouped pricing page copy, approved verbatim and applied. Still a draft: the book `2026-10-01-draft1` and the USD/GBP overage rates and floors. |
| -What the night still owes (the brief): finish and gate the currency branch with the page integrated; the site's currency selector wired to the generated price book; the demo rebuilt from the integrated source; the replicator and storage findings; the 36-price catalog, runtime credential and permission plan, webhook, portal, release and rollback plan; the readiness checklist with evidence; the morning report with the USD/GBP overage rate proposal and the scoped approvals. |
| +Branches (all on the forge, none merged; the local gate is not CI): |
| +- `feat/carloku-multi-currency-20261001` `8958392b`: the price book and one stored currency per purchase end to end, the billing page as one form, the cookies, the demo. Three review rounds; every finding of the third fixed with a distinguishing test (dispositions "Review three", eleven mutants killed). Gate on `8958392b` running at 04:15; CI pending; fourth review (Opus + Astra) running. CI passed on `de81dd70`. |
| +- `feat/carloku-metered-replicator-20261001` `01f2b072`: fourth build plus the chain-climb fix after CI failed on `b53f2a93`; gate and CI passed on `01f2b072`; fourth review running. |
| +- `feat/carloku-storage-sole-holder-20261001` `eaf6e63e`: round 9 both accept; CI passed; needs approval K and a cutover date. |
| +- `fix/carloku-observe-routes-cleanup-20261001` `023e106c`: both accept; CI passed; ready to merge into this branch. |
| +- `feat/carloku-billing-layout-20261001` `c1ae91a0`: merged into the currency branch; its two-form page was replaced by one form. |
| +- `feat/carloku-late-request-fencing-20261001` `1b61de82`, `feat/carloku-bucket-hour-evidence-20261001` `0aa1fbec`: unchanged (rejected / needs approval J). |
| +- Site `feat/paid-pricing-20260916` (GitHub) `2fc6388`: every amount from the platform's export; country band wiring; exact rates; honest link parameters; a draft book cannot be published. |
| **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); 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`. |
| +- Paul, commercial: approve the 36 amounts as an `approved` book version (approval L) together with the USD/GBP overage rates and floors in `OVERAGE-RATES-PROPOSAL.md`, or state other figures; the HALF code word; the proration policy (B); the partial-month rule (C). |
| +- Paul, copy: the console billing page's 40 strings; the store-refusal copy (`OUTSTANDING.md` 19g); a new sentence for the pricing page's `bands.p3` ("The euro price is what is charged" is wrong under fixed currencies); the approved pricing and currency strings themselves need nothing more. |
| +- Paul, rulings: evidence for a held bucket-hour (J); storage on a bucket a store has left (K); the cutover note (M: purchases before the book stay legacy EUR; binary first, variables second). |
| +- Scoped approvals, prepared as plans only: Stripe test mode (E) for the fence test and the rehearsal; Stripe live provisioning (F) after the approved book; the Cost and Usage Report export and the scratch-object write (G); Path B on a dev box (H). |
| +- Reviews to read when they land: the currency branch's fourth (`logs/opus-currency-review4.md`, `astra-currency-review4.md`), the replicator's fourth (`opus-replicator-review4.md`, `astra-replicator-review4.md`). |
| **RECOMMENDED:** |
| -- Rule on approvals J and K. K decides whether the storage branch can merge. |
| -- Storage branch: one more reading of `09520a62..4ed6b71e`, then of the whole change. |
| -- Replicator: a request gate in `internal/lease` that stops a request (not only fails its recording), and a version of the routes table's owner-bearing columns in the registry. Then the sibling-file link checks. Then a third review. |
| -- Decide what bounds a complete late request at Stripe, or redesign so nothing depends on it. |
| -- Have the whole launch branch read again at its tip, not a diff, before anything is called accepted. |
| -- Then, each on its own branch: quantity reduction at renewal, the evidence door for a box's first minutes, a short retry of the storage pass after 23:00 UTC, the AWS principal inventory and the bucket ownership attestation door, Free-tier enforcement, dormancy and pool accounting. |
| +- Read the fourth currency review; if both accept, merge the currency branch into this branch through the forge (`amadan branch merge`, CI green on the tip), then merge `fix/carloku-observe-routes-cleanup-20261001`. Rule on K to let the storage branch follow. |
| +- Publish the approved book version as soon as L is given; it is a new entry in `internal/plans/pricebook.json`, the export regenerated and copied to the site, and the 36 lookup keys renamed. |
| +- Test-mode rehearsal (E) before anything live: the list in both reviews and in `ENABLEMENT.md` section 6. |
| +- Then, each on its own branch: quantity reduction at renewal, the evidence door for a box's first minutes, the AWS principal inventory and the bucket ownership attestation door, Free-tier enforcement, dormancy and pool accounting. |
| **SESSION:** |
| -The bounded heartbeat (`carloku-paid-launch-continuity`, id `a1f7c9af`) ran from 10:00 Dublin and its three hours ended at about 12:37. It was not renewed: what is left is either a decision of Paul's or new implementation large enough that he should choose to start it. No review, gate or build of this run is still running. Not safe to close without a look: three review servers are serving on this box (pids and stop file under `/home/paulca/.local/share/carloku-paid-launch-20260930/logs`). |
| - |
| -The shared `/tmp` on this box was 99% full at 10:16 (about 478 MB free of 32 GB). Most of it is not this run's: `/tmp/titogo-admission-go-cache` (8.0 GB), `/tmp/admission-repair-cache` (6.5 GB, still growing), `/tmp/g` (3.2 GB) and two CI runner directories. This run did not touch them. It failed one gate run (tests that build a binary could not link). The gate wrapper (`~/.cache/carloku-gate/texec.sh`) now gives every package but the activator an on-disk scratch root, `/var/tmp/ck`; the activator keeps `/tmp/k` because its unix socket paths do not fit under a longer root. |
| +Running at 04:15: the gate on `8958392b` (`logs/gate-currency-8958392b.txt`), the two currency reviews, the two replicator reviews (detached `setsid` processes; their output files are named in `READINESS.md`). The demo (pid in `layout-review/demo-redesigned.log`'s process, stop marker `layout-review/demo-redesigned.stop`) and the pricing preview (`python3 -m http.server 38009`) keep serving. |
| -Scratch left on the box: `~/.cache/tmp/claude-1001/{mutcopy,fencing-wt,replicator-wt,evidence-wt}` (the last three are git worktrees), `/tmp/k`, `/var/tmp/ck`, `~/.cache/carloku-gate`. |
| +Scratch on the box: `~/.cache/tmp/claude-1001/{currency-wt,replicator-wt,fencing-wt,evidence-wt}` (git worktrees), the demo binaries, `/var/tmp/ck`, `~/.cache/carloku-gate`. The void gate logs are named `*VOID*`. |
diff --git a/implementation/carloku-paid-launch/ENABLEMENT.md b/implementation/carloku-paid-launch/ENABLEMENT.md| new file mode 100644 |
| index 000000000..f94e32558 |
| --- /dev/null |
| +++ b/implementation/carloku-paid-launch/ENABLEMENT.md |
| @@ -0,0 +1,202 @@ |
| +# Enabling payments: the scoped plan |
| + |
| +Written 2026-10-02, early morning, at the end of the second overnight run. |
| +This is a plan. Nothing in it has been done: billing is OFF on every console, |
| +the live Stripe account has no catalog, no portal configuration, no webhook |
| +endpoint and no runtime key, and no console has a price book configured. |
| +Every payment anyone can see today (the demo at |
| +`http://100.119.180.57:41881`) is a labelled simulation. |
| + |
| +The detailed procedure, with every API call, idempotency key and check, is |
| +`stripe-provisioning.md` in this directory. This file is the short form: what |
| +exists, what is asked for, in what order, and what undoes it. Where the two |
| +disagree, `stripe-provisioning.md` is wrong and this file says so. |
| + |
| +## 1. What a live console needs to exist |
| + |
| +| Thing | Count | Where it is defined | Exists today | |
| +| --- | --- | --- | --- | |
| +| Products | 3 (personal, community, business), tax codes `txcd_10102001`, `txcd_10102001`, `txcd_10102000` | plan step 1 | no | |
| +| Prices | 36: 3 plans x 4 bands x 3 currencies, yearly, licensed, tax exclusive, one currency each, no `currency_options` | plan step 2; `internal/plans/pricebook.json` | no | |
| +| Coupons | 2 (50% forever with the public code HALF; 100% house, no code) | plan step 3 | no | |
| +| Portal configuration | 1, locked | plan step 4 | no | |
| +| Webhook endpoint | 1, six events, API `2025-02-24.acacia` | plan step 5 | no | |
| +| Runtime key | 1 `rk_live_` restricted key, permissions in section 4 | plan step 6 | no | |
| +| Tax registrations | IE standard and OSS union | live, since 2026-09-21 | yes | |
| +| Console variables | nine, section 6 | plan step 7 | not set | |
| + |
| +## 2. The catalog of 36 prices |
| + |
| +Amounts are per member per year in the currency's minor unit, exactly |
| +`annual_unit_minor` in `internal/plans/pricebook.json`, book |
| +`2026-10-01-draft1`. The lookup key of each price is |
| +`carloku-<book>-<plan>-<band>-<currency>`; each carries |
| +`metadata[terms_version]=2026-09-07-v1`, `metadata[price_book]`, |
| +`metadata[currency]`, `metadata[plan]`, `metadata[band]`. The console |
| +compares the five metadata values, the price's own `currency` and |
| +`unit_amount`, `tax_behavior`, the recurring interval and usage type, and the |
| +absence of `currency_options`, against the book before it sells on a price. |
| + |
| +| Plan | Currency | A | B | C | D | |
| +| --- | --- | --- | --- | --- | --- | |
| +| personal | usd | 2800 | 2100 | 1400 | 980 | |
| +| personal | eur | 2400 | 1800 | 1200 | 840 | |
| +| personal | gbp | 2200 | 1650 | 1100 | 770 | |
| +| community | usd | 600 | 450 | 300 | 210 | |
| +| community | eur | 500 | 375 | 250 | 175 | |
| +| community | gbp | 450 | 338 | 225 | 158 | |
| +| business | usd | 6000 | 4500 | 3000 | 2100 | |
| +| business | eur | 5000 | 3750 | 2500 | 1750 | |
| +| business | gbp | 4500 | 3375 | 2250 | 1575 | |
| + |
| +The annual bases (28/24/22, 6/5/4.50, 60/50/45 USD/EUR/GBP, team minimum |
| +10) were confirmed by Paul on 2026-10-01 for implementation, not for |
| +production. The book is therefore `status: draft`. A console in live mode |
| +refuses to start on a draft book (`cmd/console/billing.go`), and |
| +`stripe-provision.sh` refuses `--mode live` on one. **Enabling payments needs |
| +a new book version marked `approved`**, published in `pricebook.json` with |
| +the same amounts (or the amounts Paul changes), after which the 36 lookup |
| +keys and `CONSOLE_BILLING_PRICE_BOOK` name that version. The draft is never |
| +edited in place. |
| + |
| +The overage rates and invoice floors in USD and GBP are proposed, not |
| +approved: `OVERAGE-RATES-PROPOSAL.md`. They are part of the same book version |
| +and are approved or changed with it. |
| + |
| +## 3. The two cutovers a console makes |
| + |
| +**Schema.** The console migrates at start: six columns, the |
| +`billing_book_assignments` table, and the trigger |
| +`billing_months_no_legacy_calc_over_book_history`, which refuses a version 1 |
| +(legacy, EUR) month calculation for an account that already has a book-priced |
| +assignment. An older console binary against the migrated database writes |
| +legacy rows as before and is refused only where it would misprice a |
| +book-priced account. This binary against an unmigrated database migrates it. |
| +Migration is additive and is not rolled back. |
| + |
| +**Price book.** `CONSOLE_BILLING_PRICE_BOOK` is required when billing is on. |
| +With no book configured (tests and the demo before today) the console sells |
| +in EUR from the terms' own figures. Once a book is configured, every new |
| +purchase records the book and a currency; purchases recorded before it stay |
| +legacy EUR for their life and their renewals. Changing the configured book |
| +later is a new version: existing purchases keep theirs. |
| + |
| +Roll note for the first deployment with a book: deploy the binary with |
| +billing OFF first (migration runs, nothing sells), confirm the console is up, |
| +then set the variables and switch the mode. The nine variables and their |
| +shape checks are plan step 7. |
| + |
| +## 4. The runtime key's permissions |
| + |
| +From plan step 6, derived from every `/v1/` path `internal/billingstripe` |
| +calls. Write includes read. |
| + |
| +| Permission | Level | |
| +| --- | --- | |
| +| Checkout Sessions | Write | |
| +| Customers | Write | |
| +| Subscriptions | Read | |
| +| Prices | Read | |
| +| Products | Read | |
| +| Tax registrations | Read | |
| +| Customer portal | Write | |
| +| Coupons | Read | |
| +| Invoices | Write | |
| +| Invoice items | Write | |
| +| Account | Read (if a restricted key needs it to read its own account) | |
| + |
| +Deliberately not granted: subscription items write (until the proration |
| +policy, approval B, and the sandbox-verified fence are set together), events, |
| +promotion codes, webhook endpoints, API keys, balance, charges, payouts, |
| +refunds, payment intents, customer deletion, Connect. The console refuses a |
| +key not prefixed `rk_live_`; the prefix is necessary and not sufficient, and |
| +the denied-read probes in the plan's verification tell a runtime key from the |
| +CLI's own. |
| + |
| +The key is created in the Dashboard by a person, shown once, and piped into |
| +the secret store. It is never printed, never in a command line, never |
| +committed. |
| + |
| +## 5. Webhook and portal |
| + |
| +Webhook: `https://console.carloku.com/billing/webhook`, exactly six events |
| +(`checkout.session.completed`, `customer.subscription.created`, `.updated`, |
| +`.deleted`, `invoice.paid`, `invoice.payment_failed`), pinned to API version |
| +`2025-02-24.acacia` (the handler reads `subscription` straight off the |
| +invoice; later versions move it). The signing secret goes to |
| +`CARLOS_STRIPE_WEBHOOK_SECRET`; rotation uses |
| +`CARLOS_STRIPE_PREVIOUS_WEBHOOK_SECRETS` (up to two) and two restarts. Until |
| +the first checkout exists no event flows, so the endpoint can be created |
| +before the console flips. |
| + |
| +Portal: one locked configuration (`bpc_`): payment method update and invoice |
| +history on, cancel at period end, no plan or quantity changes by the customer. |
| +Whether Stripe accepts `name` in `allowed_updates` at that API version is a |
| +rehearsal question; the console accepts any subset. |
| + |
| +## 6. Release order |
| + |
| +1. Deploy the console build with `CONSOLE_BILLING_MODE` off and no Stripe |
| + variables. Confirm `/billing/webhook` answers 404. |
| +2. Rehearse in Stripe test mode (approval E): the sandbox fence test, the |
| + full checkout-to-entitlement path on a test clock, the overage invoice |
| + path, signature verification and rotation, and every parameter the plan |
| + lists as unverified at `2025-02-24.acacia` (`adaptive_pricing[enabled]`, |
| + `currency` on a session, `discounts[0][coupon]`, `tax[validate_location]`, |
| + portal `name`). |
| +3. Publish the approved book version; rebuild; deploy again with billing |
| + off. |
| +4. Get the live scoped approval (F) and provision live, steps 1 to 6 of the |
| + plan, reading each result before the next block. |
| +5. `stripe-provision.sh --check --mode live`. Stop on any failure. |
| +6. Set the nine variables, validate the file, restart one console, switch |
| + `CONSOLE_BILLING_MODE=live`. Keep the way to set it `off` ready: a bad |
| + value crash-loops the unit (reset-failed over SSM, as on 2026-09-11). |
| +7. First live proof, with its own approval because it moves money: a house |
| + account checkout at 100% off (no charge), then one smallest Personal |
| + purchase by Paul, refunded from the Dashboard. Confirm the tax line. |
| +8. Only then announce prices or accept customers. |
| + |
| +## 7. Rollback |
| + |
| +- `CONSOLE_BILLING_MODE=off` and restart: stops every new checkout and |
| + portal session at once, no Stripe change. |
| +- Disable the webhook endpoint. Deactivate the promotion code and the portal |
| + configuration. Archive prices, then products. Nothing is deleted; prices |
| + cannot be. Existing subscriptions keep renewing on archived prices. |
| +- A subscription created in error is cancelled in the Dashboard, by a |
| + person, outside this plan. |
| +- The schema migration is not rolled back; an older binary runs against it. |
| + |
| +## 8. Approvals, each scoped |
| + |
| +The lettered approvals A to K are in `OUTSTANDING.md` with their exact |
| +scope. Those that gate enabling payments, plus what this run added: |
| + |
| +| # | Approval | State after the night | |
| +| --- | --- | --- | |
| +| A | Copy | Pricing page grouped review and the five currency strings: approved 2026-10-01 and applied. Still open: the HALF code word, console billing page copy (40 strings), store-refusal copy. One approved string is now wrong: `bands.p3`, "The euro price is what is charged", under fixed currencies; it needs a new sentence and its own review | |
| +| B | Proration policy | open | |
| +| C | Partial-month rule | open | |
| +| E | Stripe test mode | open; needed before the fence test and the rehearsal | |
| +| F | Stripe live provisioning | open; after A, the approved book and the rehearsal | |
| +| L | The price book | New. Approve the 36 amounts as an `approved` book version (the annual bases are confirmed for implementation only), together with the USD and GBP overage rates and floors in `OVERAGE-RATES-PROPOSAL.md` | |
| +| M | The cutover note | New. Agree that purchases recorded before the book stay legacy EUR for their life, and that the first deployment is binary first, variables second | |
| + |
| +Approvals G, H, J, K concern the meters and the storage pass; they gate the |
| +first *metered* invoice, not the first subscription, and are unchanged |
| +tonight except as `OUTSTANDING.md` records. |
| + |
| +## 9. What "ready to enable" means, and where it stands |
| + |
| +Ready means: reviewed, tested, integrated code, and this plan with its |
| +approvals granted. It does not mean payments switched on. |
| + |
| +- Code: the currency branch at its tip is gated locally and pushed; its third |
| + independent review (Opus and Astra) is the one to read before any merge. |
| + The readiness checklist (`READINESS.md`) names every commit, gate log, |
| + review verdict and CI run. |
| +- Not code: approvals A (remaining parts), B, E, F, L, M; the test-mode |
| + rehearsal; the live provisioning; the first live proof. |
| +- Not part of enabling subscriptions, still part of the complete metered |
| + model: items 1 to 4 and 6 to 9 of `OUTSTANDING.md`. |
diff --git a/implementation/carloku-paid-launch/OVERAGE-RATES-PROPOSAL.md b/implementation/carloku-paid-launch/OVERAGE-RATES-PROPOSAL.md| new file mode 100644 |
| index 000000000..08581acd0 |
| --- /dev/null |
| +++ b/implementation/carloku-paid-launch/OVERAGE-RATES-PROPOSAL.md |
| @@ -0,0 +1,67 @@ |
| +# Overage rates and invoice floors in USD and GBP: the proposal for approval |
| + |
| +Written 2026-10-02 for the morning. The EUR rates are the approved spec's |
| +(`2026-09-07-carloku-pricing-plans-design.md`, `internal/plans/plans.json`). |
| +The USD and GBP rates and the three floors are the platform's draft, in the |
| +embedded price book `2026-10-01-draft1`, behind the billing gate that is off. |
| +Nothing bills at them. They are what the pricing page preview shows for USD |
| +and GBP today, marked there as the book's draft. |
| + |
| +## The rule proposed |
| + |
| +One fixed multiplier per currency against the EUR rate, chosen so that every |
| +rate is an exact figure (no rounding anywhere), and the same multiplier for |
| +the floor: |
| + |
| +| Currency | Multiplier | Why that number | |
| +| --- | --- | --- | |
| +| EUR | 1.00 | the approved rates | |
| +| USD | 1.20 | near the market rate when proposed (about 1.13 EUR to USD on 2026-10-01), rounded up so the USD rate is never below the EUR one at any plausible rate this year | |
| +| GBP | 0.90 | near the market rate when proposed (about 0.85 EUR to GBP on 2026-10-01), rounded up for the same reason | |
| + |
| +The multipliers are commercial choices, not exchange rates: there is no |
| +exchange rate anywhere in the platform, and a rate is never recomputed. They |
| +match the direction of the annual prices Paul confirmed (28 / 24 / 22 is |
| +1.17 / 1.00 / 0.92 of EUR), rounded to one decimal. |
| + |
| +## The table |
| + |
| +Per unit, in the currency's major unit, as `internal/plans/pricebook.json` |
| +holds them in micro units: |
| + |
| +| Meter | Unit | EUR (approved) | USD (proposed) | GBP (proposed) | |
| +| --- | --- | --- | --- | --- | |
| +| Awake time | per hour | 0.010 | 0.012 | 0.009 | |
| +| Storage | per GB per month | 0.106 | 0.1272 | 0.0954 | |
| +| Transfer | per GB | 0.414 | 0.4968 | 0.3726 | |
| +| Replication writes | per 1,000 | 0.023 | 0.0276 | 0.0207 | |
| +| Invoice floor | per monthly bill | 1.00 | 1.20 | 0.90 | |
| + |
| +The pricing page shows rates to three decimals, as the approved EUR strings |
| +do (`€0.010 per hour`), so USD storage shows as `$0.127` and GBP storage as |
| +`£0.095`; the platform bills at the exact figure above. If three decimals |
| +are wanted to be the figure itself, the storage and transfer rates would |
| +become 0.127 / 0.497 / 0.028 USD and 0.095 / 0.373 / 0.021 GBP, which is a |
| +different proposal (not an exact multiplier) and would need the book changed |
| +before approval. |
| + |
| +## What approving does and does not change |
| + |
| +- Approving these, with the annual prices already confirmed, completes the |
| + amounts of the book. The book is then published as a new version marked |
| + `approved` (`2026-10-02-v1`, say); the draft is never edited. Until that |
| + version exists, live mode refuses to start. |
| +- Allowances do not change with currency. A band D GBP buyer gets band D's |
| + inclusions, the same as a band D EUR buyer. |
| +- The monthly bill for an account is in the one currency it bought in, at |
| + these rates, and nothing is converted. |
| +- A later change to the rates is a new book version; existing subscriptions |
| + stay on the book they were bought under for their term, as the pricing |
| + page promises. |
| + |
| +## What is asked |
| + |
| +1. Approve the four USD and four GBP rates and the three floors as above, |
| + or state different figures. |
| +2. Say whether the page should show three decimals of the exact figure (as |
| + now) or whether the figures themselves should be three-decimal amounts. |
diff --git a/implementation/carloku-paid-launch/READINESS.md b/implementation/carloku-paid-launch/READINESS.md| new file mode 100644 |
| index 000000000..fd5308031 |
| --- /dev/null |
| +++ b/implementation/carloku-paid-launch/READINESS.md |
| @@ -0,0 +1,89 @@ |
| +# Readiness checklist, with evidence |
| + |
| +Written 2026-10-02 at 04:10 Dublin, at the end of the second overnight run, |
| +and updated in place. "Gate" means the full local gate (`run-gate.sh`: |
| +build, vet, gofmt, `make test`, three cross builds) on the exact commit |
| +named, in a clean tree; it is not CI. "CI" means Amadan's run on the pushed |
| +tip. A review verdict is a reviewer's word on a stated commit, not a merge. |
| +Nothing below is deployed; billing is OFF; no live Stripe object exists |
| +beyond the two tax registrations of 2026-09-21; nothing was written to AWS. |
| + |
| +Log files are under `/home/paulca/.local/share/carloku-paid-launch-20260930/logs/`. |
| + |
| +## Branches |
| + |
| +| Branch | Tip | Gate | CI | Reviews | State | |
| +| --- | --- | --- | --- | --- | --- | |
| +| `feat/carloku-multi-currency-20261001` | `8958392b` (the third review's findings) | `de81dd70`: passed 23:39Z on a clean tree (`gate-currency-de81dd70.txt`, exit 0, three cross builds). `8958392b`: running at 04:10 (`gate-currency-8958392b.txt`); the four billing packages passed in full on the same tree before the commit. Void logs, not evidence: `gate-currency-a9304f16.txt` (tree moved), `gate-currency-f269365a-VOID-dirty-tree.txt` (started on an uncommitted tree, killed) | passed on `de81dd70`; `8958392b` pushed 04:00, run pending | 1 at `9d104f1d`: Opus accept with fixes, Astra reject. 2 at `a88a39e6`: Opus accept with fixes, Astra reject; fixed in `a9304f16`. 3 at `de81dd70`: Opus accept with fixes (P2-1 country default, P2-2 site rounding, five P3), Astra reject (nine findings: ownership before expiry, one-form, trigger, bound completion review, inclusions, rounding, cleared country, resume guard, verifier); every finding of either fixed in `8958392b` with a distinguishing test, eleven mutants killed, one equivalent (dispositions, "Review three"). 4 at `8958392b`: running (Opus + Astra, `currency-review4-brief.txt`) | integrated end to end: plans, Stripe client, console store/service/calculation/invoice, one billing form, cookies, demo; not merged | |
| +| `feat/carloku-billing-layout-20261001` | `c1ae91a0` | (templates only; gated inside the currency branch) | passed | none of its own | merged into the currency branch at `5047494b`; its two-form layout was replaced by one form in `8958392b` | |
| +| `feat/carloku-storage-sole-holder-20261001` | `eaf6e63e` | passed on `3af02a65`; `eaf6e63e` is wording and reports | passed | 9 rounds; round 9 both accept (omitted-holder class closed) | needs approval K and a recorded cutover date; not merged | |
| +| `feat/carloku-metered-replicator-20261001` | `01f2b072` (fourth build `b53f2a93` + the chain-climb fix) | `b53f2a93`: FAILED (two ops tests, the climb reached the temp root). `01f2b072`: passed 23:47Z (`gate-replicator-01f2b072.txt`, exit 0, three cross builds) | failed on `b53f2a93` (same two tests); **passed on `01f2b072`** | 3 rounds on the third build (Opus accept with fixes, Astra reject). 4 at `01f2b072`: running (`replicator-review4-brief.txt`) | not merged; open items in its dispositions, "Review three" | |
| +| `fix/carloku-observe-routes-cleanup-20261001` | `023e106c` | passed on `51587e00`; `023e106c` changes a test and two docs | passed | both accept at `51587e00` | ready to merge into the launch branch; not merged | |
| +| `feat/carloku-paid-launch-20260916` (launch) | `6a08f88d` (handoff) + this night's documents | last code gate on `cd8821c6` | passed on `6a08f88d` | 17 rounds, no whole-branch pass | the integration target | |
| +| site `feat/paid-pricing-20260916` (GitHub) | `2fc6388` | `node --test`: 18 pass (04:0x) | n/a | read-only by the fourth currency review (section K) | preview served at `http://100.119.180.57:38009`, rebuilt from `2fc6388` | |
| + |
| +## The flow, verified behaviour (2026-10-02) |
| + |
| +Verified by tests on `8958392b` (`internal/console`: `TestBillingFormPostsWhatItShows`, |
| +`TestBillingPageCurrencyControls`, `TestPricingLinkSurvivesSignIn`, |
| +`TestCurrencySuggestionCookie`, `TestCountrySuggestionCookie`, |
| +`TestBillingInclusionsFollowTheQuotedBand`, |
| +`TestCurrencyRefusalExpiresOnlyTheIntentsOwnSession`, |
| +`TestBoundSessionCompletedInAnotherCurrencyIsReviewedOnce`, |
| +`TestBarrierTriggerLetsThisBuildsLegacyMonthsThrough`, and the checkout, |
| +reconcile, calculation, invoice and recovery tests in |
| +`billingcurrency_test.go`) and by the site tests (`test/pricing-currency.test.mjs`, |
| +`test/pricing-pricebook.test.mjs`): |
| + |
| +- Pricing page: USD at band A without script; the currency control changes |
| + every priced string; the billing-country control changes the band of the |
| + plan prices and the per-member inclusions and not the overage rates; a |
| + band D country with GBP chosen stays in pounds at band D; overage rates |
| + are exact (`$0.1272`, `£0.0207`); the signup links always carry |
| + `?currency=` (only when chosen, else empty) and `?country=` (selected or |
| + empty); a page built from a draft book is marked and `npm run check` |
| + refuses it. |
| +- Console: a GET with `?currency=gbp&country=IN` sets two one-hour cookies, |
| + an empty value clears one; after the Keymail sign-in the billing page |
| + preselects GBP, selects India, says 35%, prices Personal at £7.70 and |
| + states band D inclusions (29.75 awake hours, 0.7 GB, 0.7 GB). One form: |
| + a country or currency posted that the page did not price is first shown |
| + as a quote; a currency shown as the country's default and not chosen |
| + buys in the country's own currency; a chosen one is bought in; an account |
| + that bought in a currency is offered it alone; an unsold currency or an |
| + unknown country sets nothing and is refused at checkout. |
| +- Demo at `http://100.119.180.57:41881`: rebuilt 04:0x from `8958392b` |
| + (binary `billing-redesigned.test`, started by the stop-marker procedure; |
| + the EUR-only binary is kept as `billing-redesigned-eur-only.test.bak`), |
| + serving with the price book configured: the whole path from the pricing |
| + link through sign-in, the one form, checkout, payment, webhook, active |
| + plan and portal, in USD, EUR or GBP. Keymail and Stripe there are |
| + labelled simulations; no card, no charge, no Stripe object. |
| + |
| +## Not verified, and what verifies it |
| + |
| +| Claim | Status | What would verify it | |
| +| --- | --- | --- | |
| +| Stripe accepts `currency` + `adaptive_pricing[enabled]=false` on a Checkout Session, and `currency` on a draft invoice and invoice item, at API `2025-02-24.acacia` | unverified; documented behaviour modelled by fakes | the test-mode rehearsal (approval E) | |
| +| A session created in GBP comes back `currency=gbp`, no `currency_conversion`, `adaptive_pricing.enabled=false`; the shape of `currency_options` on a single-currency price | unverified | same | |
| +| A restricted key with the permissions in `ENABLEMENT.md` section 4 can do every call the console makes | unverified | `stripe-provision.sh --check` in test mode, then live | |
| +| The quantity worker's fence | unverified | `TestSandboxQuantityFence` in test mode (approval E) | |
| +| Store transfer meter | no source | approval G, a first CUR delivery | |
| +| Replication writes meter | not provable on any box | approval H, Path B on a dev box; the replicator branch's fourth review | |
| + |
| +## Approvals needed to enable |
| + |
| +A (remaining parts: HALF code word, console billing copy, store-refusal copy, |
| +a new `bands.p3` sentence), B, C, E, F, L (the approved book version and the |
| +USD/GBP overage rates in `OVERAGE-RATES-PROPOSAL.md`), M (the cutover note). |
| +Each is scoped in `ENABLEMENT.md` section 8 and `OUTSTANDING.md`. |
| + |
| +## Known defects and conflicts, open |
| + |
| +- `bands.p3` on the pricing page says the euro price is what is charged; |
| + under fixed currencies that is wrong. Approved copy; not edited; needs its |
| + own review. |
| +- `OUTSTANDING.md` items 7 and 9 (late requests past Stripe's key memory) |
| + are open defects, held, not waived. |
| +- The replicator's open items (its dispositions, "Review three"): a swap |
| + during litestream's first open above all. |
diff --git a/implementation/carloku-paid-launch/stripe-live-readiness.json b/implementation/carloku-paid-launch/stripe-live-readiness.json| index 0b3f47e2b..d6a0df26a 100644 |
| --- a/implementation/carloku-paid-launch/stripe-live-readiness.json |
| +++ b/implementation/carloku-paid-launch/stripe-live-readiness.json |
| @@ -85,13 +85,14 @@ |
| }, |
| "remaining_before_live_mutation": [ |
| "Every item below is planned in implementation/carloku-paid-launch/stripe-provisioning.md; stripe-provision.sh prints the commands and executes nothing. No item has been started.", |
| + "Read the third independent review of feat/carloku-multi-currency-20261001 (Opus and Astra, 2026-10-02) and merge the currency branch through the forge; nothing live is created from a draft book.", |
| "Get scoped approval for the live Stripe work (approval text at the end of stripe-provisioning.md).", |
| "Land and deploy the approved plan-specific PaaS tax-code mapping before enabling billing (CONSOLE_STRIPE_TAX_CODES, plan step 7).", |
| "Approve customer-facing product names, coupon names, the promotion code word and billing copy (plan steps 1 and 3; the script takes them as arguments).", |
| "Create and verify a restricted live runtime key with only the API permissions the console needs (plan step 6, permission table and check 9 probes).", |
| "Choose the deployed webhook URL and create the endpoint and signing secret (plan step 5: console.carloku.com/billing/webhook, six events, API version 2025-02-24.acacia).", |
| "Create and verify the locked customer portal configuration (plan step 4).", |
| - "Provision the three products and twelve immutable prices, then record their IDs in CONSOLE_STRIPE_PRICES (plan steps 1 and 2).", |
| + "Publish an approved price book version (the 36 amounts and the USD/GBP overage rates and floors are confirmed for implementation only), then provision the three products and 36 immutable prices, one currency each, no currency_options, and record their IDs in CONSOLE_STRIPE_PRICES with CONSOLE_BILLING_PRICE_BOOK (plan steps 1 and 2).", |
| "Create the 50 percent public coupon and promotion code and the 100 percent house coupon, with no promotion code for the house coupon (plan step 3).", |
| "Run the read-only verification (stripe-provision.sh --check) and a test-mode rehearsal before CONSOLE_BILLING_MODE is switched to live." |
| ], |
| @@ -136,5 +137,8 @@ |
| "https://docs.stripe.com/payments/managed-payments/eligibility", |
| "https://support.stripe.com/questions/selecting-the-right-product-tax-code-for-managed-payments?locale=en-GB" |
| ] |
| - } |
| + }, |
| + "required_catalog_note": "Superseded 2026-10-02: the catalog is 36 prices in USD, EUR and GBP from the price book 2026-10-01-draft1 (ENABLEMENT.md section 2, stripe-provisioning.md step 2). The EUR column below is unchanged; the book is a draft and live mode refuses it until an approved version is published.", |
| + "updated_at": "2026-10-02T00:00:00Z", |
| + "updated_note": "remaining list revised for the multi-currency price book; account and observed_live_state are as checked on 2026-09-21 and were not re-read tonight (Stripe live is read-only and was not called)." |
| } |