carlos / platform Public

Record the collector and attestation plans, refresh the audit and handoff

The store-transfer collector plan carries the infrastructure needed and the
scoped-approval request; the attestation plan inventories the S3 writers
on a box and the two honest ways to make replication completeness true.
The requirements audit and the agent handoff describe what is on the
branch, what holds billing and what is still missing. The review brief is
what the independent second reader is given.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev 70bb511408e48299c876923cf4d5a96d15b10dbb parent de2ee4a
5 files changed, +179 −84
  • docs/superpowers/plans/2026-10-01-durability-attestation-path.md +37 −0
  • docs/superpowers/plans/2026-10-01-store-transfer-cur-collector.md +44 −0
  • docs/superpowers/specs/CODEX-REVIEW-BRIEF-carloku-paid-launch-overnight.md +31 −0
  • implementation/carloku-paid-launch/AGENT_HANDOFF.md +51 −70
  • implementation/carloku-paid-launch/requirements-audit.json +16 −14

CI failed — run details

diff --git a/docs/superpowers/plans/2026-10-01-durability-attestation-path.md b/docs/superpowers/plans/2026-10-01-durability-attestation-path.md
new file mode 100644
index 000000000..70255e2d3
--- /dev/null
+++ b/docs/superpowers/plans/2026-10-01-durability-attestation-path.md
@@ -0,0 +1,37 @@
+# Replication writes: how a box could honestly attest
+
+Date: 2026-10-01. Status: the meter folds and holds; no production box attests; nothing here flips that.
+
+## Where the meter stands
+
+`durability_writes_1k` is folded from each box's published replication ledger (`usage/durability/<box>/days/<date>.json`) by `internal/console/durabilityfold.go`. A day counts toward the source only when every expected box published a day that is closed, has one ledger generation, no gap, no conflict, no recording failure, no unknown outcome, and coverage attested for every account. The billable count is `usage.BillableReplicationWrites`: answered 2xx write operations (PutObject, UploadPart, CreateMultipartUpload, CompleteMultipartUpload, CopyObject) made for the tenant's own replication or checkpoint. Leases, restores, moves, the verify drill, platform buckets, reads, lists, deletes, failed and unanswered attempts are never billed.
+
+The only production open is `usage.OpenDurabilityLedger(dir, box, now, usage.DurabilityLedgerOptions{})` in `internal/host/durability.go`: not attested. So the source reads zero days on every production box and the monthly freeze is refused. That is correct today, because the statement below is not yet true.
+
+## Who writes to the replica bucket on a box
+
+| Writer | Client | Metered |
+| --- | --- | --- |
+| Activator lease acquire, renew, release (exec-backed routes) | `internal/lease` S3 client with the request-meter middleware | Yes, when a recorder binds |
+| Activator replication and restore (exec-backed routes), vendored Litestream | `third_party/litestream/s3` with client options passed through | Yes, when a recorder binds |
+| Move mirror and checkpoint | `lease.NewS3MirrorWithRequestMeter`, `ReplicateWithRequestMeters` | Yes, when a recorder binds |
+| Any of the above when the bind fails | unmetered fallback client | No. Filed as an escape (`DurabilityLedger.Escaped`): the box-day is incomplete and the owner is excluded for the run |
+| Root `carlos-litestream.service` for unit-backed routes and the box's own databases | the external `/usr/local/bin/litestream` binary | No. The ledger cannot see it |
+| Deployment bucket reads and writes (`internal/release` store), object-store provisioning (`internal/objstore`), inventory (`internal/awsinv`) | their own S3 clients | Not replication; out of scope of this meter |
+
+The external Litestream binary is the one writer that makes the attestation false. It replicates every unit-backed tenant database named in `/etc/litestream.yml`.
+
+## Two ways to make the attestation true
+
+**A. Scope the attestation (smaller, recommended first).** The ledger already supports attested spans with an exclusion set (`DurabilityAttestation{Attested, Unmetered}`). The agent knows which accounts own unit-backed routes (the registry). On open, and whenever the route set changes, it calls `SetAttestation` naming every account that owns a unit-backed route on the box. Accounts with only exec-backed routes are then attested; the others are excluded. Two things are needed before this is honest: the console fold must read completeness per account (today `SourceDays` is one number for every account, so an exclusion set holds everyone), and the agent must prove the set is complete at every instant, which means reading it under the same registry witness the ownership proof uses. Tight durability is a Business feature, and Business tenants on unit-backed routes would stay unbilled for writes until B.
+
+**B. Meter the root replicator (complete).** Replace the external binary in `carlos-litestream.service` with `carlos ops replicate`, running the vendored Litestream already in this tree with the request-meter middleware and its own ledger directory, attributing each database to its route's owner from the registry and the box's own databases to the platform purpose. Both ledgers publish under the box; the fold already unions every ledger directory it finds. After that every S3 replication client on the box is instrumented and `AllClientsInstrumented` can be set from a startup check that the external unit is absent. This changes a root unit on every box and needs a rollout plan and a restore drill before and after.
+
+## What would flip the flag, and the test that must exist first
+
+The flag may be set only by code that checks its own premise at startup: no `carlos-litestream.service` running the external binary (B), or the exclusion set computed from the registry under a witness (A). A host test must show that a box with a unit-backed route and the external replicator present publishes a day that is not complete for that route's owner. No test may set the flag to make a billing test pass.
+
+## Open decisions
+
+- Whether LIST requests, which S3 prices at the write tier, should count. They do not today.
+- Whether a checkpoint forced by a platform-initiated move is the tenant's write. It counts today when recorded under the checkpoint purpose; the move's own copies do not.
diff --git a/docs/superpowers/plans/2026-10-01-store-transfer-cur-collector.md b/docs/superpowers/plans/2026-10-01-store-transfer-cur-collector.md
new file mode 100644
index 000000000..50accd7ce
--- /dev/null
+++ b/docs/superpowers/plans/2026-10-01-store-transfer-cur-collector.md
@@ -0,0 +1,44 @@
+# Object-store transfer from the AWS Cost and Usage Report
+
+Date: 2026-10-01. Status: collector built and tested locally; no infrastructure exists; nothing applied. Supersedes the access-log design in `2026-09-10-store-egress-and-durability-meters.md` and the `feat/carloku-store-logs-20260917` branch of platform-infrastructure.
+
+## The ruling this implements
+
+Object-storage transfer counts only transfer that AWS charges Team Tito for. S3 server access logs state delivered bytes, not charged bytes: they cannot tell a free-tier byte, a CloudFront origin byte or an in-region byte from a charged one, and AWS says a record may be missing. They are not billing authority. Missing evidence holds billing for the month. It never bills zero silently and never guesses.
+
+## What is built (branch feat/carloku-paid-launch-20260916)
+
+- `internal/awsbilling`: reads a Cost and Usage Report delivery (CUR 2.0 Data Export or legacy CUR, gzip or plain CSV, header driven), picks the newest delivery for a billing period, refuses a delivery that disagrees with itself or changes while it is read, and pins the manifest and every file by digest. `ChargedS3Transfer` counts a line only when it is AmazonS3, a usage line, a transfer-out usage type, priced above zero, names a bucket and covers one whole UTC hour. Zero-cost lines (free tier, CloudFront origin, inbound) are named and never counted. A charged line it cannot classify is a hold.
+- `internal/objstore/ownership.go`: `usage/buckets/<bucket>.json`, an append-only, effective-dated ownership ledger written with compare-and-swap. `Grant` opens an interval before any credential is delivered. `Teardown` closes it after the bucket is deleted. `OwnerAt` answers for an hour only when every interval touching it names one account and app.
+- `internal/console/storeegressfold.go`: for a month, folds each account's charged, owned bytes into `store_egress_gb`. The source `store_egress` reads complete (the month's full day count) only when a delivery exists, AWS has invoiced it (every S3 line carries `bill/InvoiceId`), no charged line was unclassifiable, and every charged bucket-hour has exactly one owner or belongs to a declared platform bucket. Otherwise the attributable bytes fold as a lower bound for the modelling views, the source stays at zero days and the monthly freeze is refused.
+- A configured report that is not yet provable holds the month open. A closed statement is never rewritten, so closing on the fourth of the month, before AWS invoices, would make the month unbillable for good.
+- Evidence: the report is read through the month's evidence store like every other source. Ownership ledgers are pinned by their projection onto the month (`usage/evidence_month.go`), so a teardown in October does not invalidate September, and an interval closed inside September does.
+- Hold reasons, as logged: `store_egress_report_unconfigured`, `store_egress_no_delivery`, `store_egress_delivery_refused`, `store_egress_not_invoiced`, `store_egress_unclassified_charge`, `store_egress_unowned_bucket`, `store_egress_ownership_unreadable`.
+
+Tests: `go test ./internal/awsbilling ./internal/objstore`, and in `internal/console` the `StoreEgressFold*` tests (charged and owned bills, unowned holds and recovers after repair, uninvoiced waits, unclassified holds, unconfigured behaves as before, evidence survives a later teardown and breaks on a restated report).
+
+## Infrastructure needed (not applied; needs scoped approval)
+
+Account 271376211898, region eu-west-1. The console reads the report from a prefix of the deployment bucket so that report objects pass through statement evidence and the console needs no new read grant.
+
+1. One Data Export (AWS Billing and Cost Management, Data Exports): type CUR 2.0, time granularity hourly, "include resource IDs" on, format gzip CSV, file versioning "overwrite existing", delivered to bucket `carlos-flagship-271376211898` under prefix `billing/cur/` with export name `carloku-transfer`. Column selection: the default set is enough; the reader needs `identity_line_item_id`, `identity_time_interval`, `bill_invoice_id`, `bill_billing_period_start_date`, `bill_billing_period_end_date`, `line_item_line_item_type`, `line_item_usage_start_date`, `line_item_usage_end_date`, `line_item_usage_type`, `line_item_resource_id`, `line_item_usage_amount`, `line_item_unblended_rate`, `line_item_unblended_cost`, `product_servicecode`.
+2. One bucket policy statement on the deployment bucket allowing `bcm-data-exports.amazonaws.com` and `billingreports.amazonaws.com` to `s3:PutObject` and `s3:GetBucketPolicy` under `billing/cur/*`, conditioned on `aws:SourceAccount` 271376211898 and the export's `aws:SourceArn`.
+3. Host IAM: confirm the box role cannot read `billing/cur/*` (tenant boxes have no business reading the account's bill); add an explicit Deny if the current grant is broader than its own prefixes.
+4. Console environment: `CONSOLE_STORE_EGRESS_REPORT_PREFIX=billing/cur/carloku-transfer/` and, if the platform owns buckets beyond the deployment bucket, `CONSOLE_STORE_EGRESS_PLATFORM_BUCKETS`.
+
+Alternative considered: a dedicated private report bucket (`carloku-billing-cur-271376211898-eu-west-1`) read through a second store. It keeps billing lines out of the control-plane bucket at the cost of a second evidence path and a new console read grant. Not built; say so if this is preferred and the fold gains a second store seam.
+
+Cost: Data Exports are free; storage is a few megabytes a month; the console reads one manifest and its files once an hour while a month is open. Duration: permanent. Traffic effect: none on tenant traffic.
+
+Verification after apply: within 24 hours `billing/cur/carloku-transfer/metadata/BILLING_PERIOD=<month>/` holds a manifest; the console log stops saying `store_egress_no_delivery` and says `store_egress_not_invoiced` until AWS issues the month's invoice, then nothing. Rollback: delete the export and unset the prefix; the meter returns to holding, which is today's state.
+
+## Before a first invoice can include store transfer
+
+- Scoped approval for the four changes above, then a first real delivery read end to end.
+- Backfill: the four stores already provisioned on the flagship have no ownership ledger. A re-grant opens one from the grant block's own time. Until then their charged transfer holds the month, by design. An operator verb for the backfill does not exist yet.
+- Confirm against the first real report that usage types, resource ids and the invoice id behave as the fixtures assume, in particular how the 100 GB free allowance is written across buckets. Anything unexpected arrives as `store_egress_unclassified_charge`, never as a bill.
+- The per-month rebuild door for a statement closed before this existed is still missing (requirements audit step 1).
+
+## Scoped approval request
+
+Account 271376211898, eu-west-1, deployment bucket `carlos-flagship-271376211898`. Create one CUR 2.0 Data Export named `carloku-transfer` delivering hourly, resource-level, gzip CSV reports under `billing/cur/`; add one bucket policy statement letting the AWS billing service principals write under that prefix; confirm or add a Deny keeping box roles out of it; set two console environment variables. Purpose: bill object-store transfer only from what AWS charged. No DNS, no compute, no tenant traffic change. Cost negligible. Permanent. Cleanup: delete the export, remove the policy statement, unset the variables.
diff --git a/docs/superpowers/specs/CODEX-REVIEW-BRIEF-carloku-paid-launch-overnight.md b/docs/superpowers/specs/CODEX-REVIEW-BRIEF-carloku-paid-launch-overnight.md
new file mode 100644
index 000000000..e9696ee12
--- /dev/null
+++ b/docs/superpowers/specs/CODEX-REVIEW-BRIEF-carloku-paid-launch-overnight.md
@@ -0,0 +1,31 @@
+# Review brief: Carloku paid launch, overnight changes of 2026-10-01
+
+You are an independent second reader. Review the changes on branch `feat/carloku-paid-launch-20260916` between `aed42815` (base) and the commit named in the prompt. Read the code, not the summaries. Verify every claim you make against a file and line.
+
+## Rulings that are fixed (do not re-argue them; report only mechanisms that fail to honour them)
+
+1. The complete metered model is required at launch. No annual-only launch, no blanket waiver of overages.
+2. Object-storage transfer counts only transfer AWS charges Team Tito for. Access logs and IP heuristics are not billing authority. Missing evidence holds billing; it never bills zero silently and never guesses.
+3. Billing stays off. No production, AWS or live Stripe mutation is part of this change. Attestation of replication completeness must not be set true anywhere in production code.
+4. Every ambiguity in a meter resolves in the customer's favour.
+5. Console security gate order: session, CSRF, owner (byte-identical 404 for a non-member), fresh-auth last.
+
+## What changed, and what to attack
+
+- `internal/console/billingpage.go`, `templates/billing.html`, `billingpage_test.go`: the GET billing page. Can a non-member learn an account exists? Can a member who is not an owner reach a form that posts to checkout or the portal? Is any Stripe identifier, postal code or secret rendered? Does `?country=`, `?error=` or `?pending=` allow injection or a 500? Does the price shown equal what `startBillingCheckout` would charge for the same members and country?
+- `internal/usage/durabilityledger.go` (epoch immutability; `Escaped`, `applyEscapes`, `applyEscapesLocked`, `setAttestationLocked`) and `internal/host/durability.go`: data races, lock order (`drainMu`, `mu`, the escape latch, the failure latch), a path where an unmetered client runs and a published day still reads complete, an escape lost across Close or a crash, an attestation that can be re-granted after an owner-unknown escape.
+- `internal/awsbilling`: a report line that is charged and counted twice, counted when AWS did not charge, or not held when it is charged and unclassifiable; a restated or partially overwritten delivery read as one; a manifest steering the reader outside its prefix; decimal handling; an hour or period boundary error; the finality rule (every S3 line carries an invoice id).
+- `internal/objstore/ownership.go` and its two call sites in `walk.go`: a byte that can leave a bucket before the ledger names an owner or after it stops; a write that can remove, move or reopen an interval; a CAS race; what happens to stores provisioned before this change; adopted buckets with two owners.
+- `internal/console/storeegressfold.go` and `durabilityfold.go`, and the edits to `economicspass.go` (`sweepUsage`, `statementsForMonth`): a path that marks a month complete without an invoiced delivery, with an unowned charged bucket-hour, or with an unattested box-day; a path that bills a platform bucket to a tenant or a tenant bucket to nobody without holding; the close-hold (`HoldClose`) wedging a month forever or letting it close unbillable; interaction with `Statement.BillingSnapshot` and the invoice engine's freeze.
+- `internal/usage/evidence.go`, `evidence_month.go`: the bucket-ownership month projection and the durability discovery rule. Can evidence verification pass after the inputs a month was billed on changed? Can it fail forever for a benign later change?
+- `usage.BillableReplicationWrites`: is any counted attempt one the customer should not pay for under ruling 4?
+- `internal/console/billingflow_demo_test.go` and `billingpage_preview_test.go`: test-only harnesses. Confirm they cannot run in a normal test pass, touch nothing external, and weaken no production check.
+
+## What to return
+
+1. Findings, most severe first. For each: severity (P1 can mischarge, leak, corrupt or wedge; P2 wrong but contained; P3 hygiene), the file and line, a concrete scenario (inputs or state, then the wrong outcome), and the smallest fix you would accept.
+2. Claims in `implementation/carloku-paid-launch/AGENT_HANDOFF.md` that the code contradicts.
+3. Tests that assert less than their names say.
+4. A one-line verdict: accept, accept with fixes, or reject.
+
+Do not propose redesigns outside the changed code. Do not restate the rulings. If something cannot be determined from the repository, say so rather than guessing.
diff --git a/implementation/carloku-paid-launch/AGENT_HANDOFF.md b/implementation/carloku-paid-launch/AGENT_HANDOFF.md
index 1512984b7..d5ed3adbc 100644
--- a/implementation/carloku-paid-launch/AGENT_HANDOFF.md
+++ b/implementation/carloku-paid-launch/AGENT_HANDOFF.md
@@ -1,93 +1,74 @@
-# Agent pickup instructions — Carloku paid launch
+# Agent pickup instructions: Carloku paid launch
-CURRENT GATE: local build, vet and `make test` GREEN after fixing the intermittent durability snapshot failure and sustained-drain fallback. Amadan CI passed on final durability commit `c0d60af8`. This remains a checkpoint of unfinished work.
+CURRENT GATE (2026-10-01): work in progress, pushed, not merged, not deployed. Billing is OFF. Live Stripe is read-only and was not touched in this run. No AWS or production change was made. Nothing here is launch-ready until a real purchase, webhook and metering are verified live.
-This file is context and instructions for the next coding agent. The branch is a work-in-progress checkpoint, not a release candidate. Do not merge, deploy, enable billing, or infer that the overnight launch goal was achieved.
+Read `AGENTS.md`, the spec `docs/superpowers/specs/2026-09-07-carloku-pricing-plans-design.md`, `requirements-audit.json` (the 18 spec steps, none complete) and this file. `session.md` is the September investigation log; prefer this file where they disagree. `.claude/HANDOFF.md` and the Amadan branch handoff carry the short status.
-## Objective and current boundary
+## What Paul asked for
-Paul asked to get carloku.com paid pricing live by morning and asked whether Stripe Tax can handle tax. Yes for calculation and collection. The VAT/OSS registrations are active, and Paul approved Stripe's PaaS classification and the plan-specific codes below. He requires the complete metered model at launch; annual subscriptions with temporarily waived overages are not an acceptable launch scope. Object-storage overages count only transfer that AWS charges Team Tito for, so delivered-byte access logs alone are not billing authority. He then explicitly requested this branch handoff so another agent can pick up the work.
+Paid plans live with Stripe Tax and the complete metered model. No annual-only launch and no blanket waiver of overages. Object-storage overages count only transfer AWS charges Team Tito for; delivered-byte access logs are not billing authority; missing evidence holds billing. On 2026-09-30 he asked for an unattended overnight run with the pricing page and signup flow ready to review in the morning.
-Billing remains OFF. Read-only live Stripe requests verified the account on 2026-09-21; no charges, catalog mutations, production infrastructure changes, or deployments were made. The platform changes are being checkpointed on `feat/carloku-paid-launch-20260916` at `https://amadan.net/carlos/platform`. Nothing is being merged to main.
+## Where everything is
-Read `AGENTS.md` and `docs/superpowers/specs/2026-09-07-carloku-pricing-plans-design.md` before proceeding. `requirements-audit.json` inventories all 18 spec steps and deliberately marks the overall implementation incomplete. `session.md` is the chronological investigation/test log; prefer this handoff for current state where older log entries disagree.
+- Platform worktree: `/home/paulca/amadan.net/carlos/platform-paid-launch`, branch `feat/carloku-paid-launch-20260916`, origin `https://amadan.net/carlos/platform`. Pass `carlos/platform` and `-hub https://amadan.net` to the amadan CLI and `AMADAN_HOME=~/.amadan-agent`.
+- Site worktree: `/home/paulca/github.com/carlosframework/carloku-paid-pricing`, branch `feat/paid-pricing-20260916` (GitHub `carlosframework/carloku`). Pages live under `src/` (Eleventy layout). The pricing page is rendered from `copy-review/pricing/strings.json` by `copy-review/pricing/build.mjs`.
+- Infrastructure worktree: `/home/paulca/github.com/carlosframework/platform-infrastructure-store-logs` (`feat/carloku-store-logs-20260917`). It holds nothing new: the access-log design it was for is superseded. The old uncommitted `store-logs.tf` was lost with `/tmp`.
+- Run directory (brief, logs, screenshots, server pids): `/home/paulca/.local/share/carloku-paid-launch-20260930/`.
+- The old `/tmp/carloku-pricing-overnight-20260916` tree is gone. The copy approved on 2026-09-22 lived only there. No transcript or repository holds it. Do not claim it.
-## Resume locations
+## Review surfaces (running on docs-t1; restart recipes below)
-- Platform worktree: `/tmp/carloku-pricing-overnight-20260916/platform`.
-- Site worktree: `/tmp/carloku-pricing-overnight-20260916/site`, branch `feat/paid-pricing-20260916`, base `a71ce06`, no edits applied.
-- Infrastructure worktree: `/tmp/carloku-pricing-overnight-20260916/infrastructure`, branch `feat/carloku-store-logs-20260917`, base `1cb7bc9`; local-only `flagship/region-ie.tf` modification and new `flagship/store-logs.tf`. These are NOT included in this platform branch.
-- Scratch reports, task briefs, logs and review artifacts: `/tmp/carloku-pricing-overnight-20260916/`.
-- Original user checkout `/home/paulca/amadan.net/carlos/platform` has unrelated dirty `go.mod`, `.claude/`, `.gocache-tmp/`, and `pitch-ten-slides.txt`; do not overwrite or clean it. Its handoff concerns another task.
-- This repository's `origin` is Amadan itself. There is no `amadan` remote. Use `-hub https://amadan.net` with the Amadan CLI because the operator is logged into several hubs. Older GitHub/mirror passages in AGENTS are stale relative to the actual remote; preserve branch review and CI, never direct-push main.
+- Pricing page copy review: `http://docs-t1.tail5fd3ba.ts.net:38009`, fallback `http://100.119.180.57:38009`. 87 strings, hand-built preview.
+- Console billing page copy review: `http://docs-t1.tail5fd3ba.ts.net:36481`, fallback `http://100.119.180.57:36481`. 39 strings; the preview is ten page states rendered through the real handlers.
+- Interactive signup and billing demo: `http://100.119.180.57:41881`. Real console handlers and store in test mode. Keymail sign-in and Stripe checkout and portal are labelled simulations on the same host. It is not Stripe proof.
+- The tailnet name did not resolve from the box itself or from the supervising session; only the IP form was verified by request. Both review results land in `result.json` beside each `strings.json`.
+- Restart a review: `~/.claude/skills/copy-review/serve.sh <strings.json> 1440` with the sandbox off. Restart the demo: `CARLOS_BILLING_DEMO_ADDR=0.0.0.0:41881 CARLOS_BILLING_DEMO_ORIGIN=http://100.119.180.57:41881 CARLOS_BILLING_DEMO_STOP=<file> go test ./internal/console -run TestBillingFlowDemo -count=1 -timeout 15h`; create the stop file to end it. Re-render billing page states: `CARLOS_BILLING_PREVIEW_DIR=<dir> go test ./internal/console -run TestBillingPageRenderPreview`, then `copy-review/billing-page/bind.py <dir>`.
-## Existing local implementation
+When a review is approved, apply each string byte for byte: the site by replacing `strings.json` text and re-running `build.mjs`; the console by editing `templates/billing.html` at the matching `<!-- id -->` comment and the constants in `billingpage_test.go`. Count occurrences of a string before replacing it (shared strings need their own review).
-1. Plans and calculations: immutable integer-unit EUR plans, effective assignment history, customer-favour rounding and allowance allocation. Rates in the authoritative spec: Personal EUR24/year; Community EUR5/member/year; Business EUR50/member/year; team minimum10; monthly pooled inclusions. Country factor boundary assumptions still need review. Full-month paid history is required: partial first month, free-to-paid transitions, cancellation gaps and some house changes currently HOLD rather than guess.
-2. Stripe boundary: restricted-key client, pinned API version, mode/identity validation, sanitized failures, checkout intents with complete commercial snapshots, tax/location checks, safe portal configuration, signed webhook reconciliation and missed-webhook polling. The console now accepts one tax code per paid plan, verifies each Stripe Product against its plan's code and freezes the applicable code into overage calculations before any provider write.
-3. Entitlements and membership: live-only publication outbox and authority/revision checks; Personal member limit; team quantity event history. Runtime Free-tier enforcement and the quantity/proration worker are not implemented.
-4. Quantity history: append-only events with exact time/generation/owner/terms, including transient add/remove. SQL guards reject REPLACE, preprocessed inserts, invalid processing timestamps and missing outcome/reference. Processing audit cannot be rewritten. Clock rollback retains events and holds history. Reconciliation refreshes summaries; pending additions/history gaps block retirement and first monthly freeze. Bound authoritative checkout expiry and proven never-created checkout abandonment atomically audit their own events. Positive proration collection timing is still an explicit product/implementation decision; no worker silently collects or waives it.
-5. Invoice engine: frozen month, tax code, durable ownership lease and per-operation request journal; idempotent recovery before/after provider requests; bounded replay window; exact draft/lines/finalize/pay verification; auto-advance disabled; terminal payment failure and review holds. A month spanning plans with different tax codes holds before any provider write until line allocation is defined. Unfinished calculations frozen by an older binary without a tax code also hold, while terminal rows stay terminal. Every POST rechecks ownership, age, frozen identity and open reviews. It reloads after acquiring ownership to avoid acting on a stale pre-lease draft. SQLite writes reserve the writer before transaction reads where needed. These mocks do not prove a real verified all-meter purchase path.
-6. Metering: durable edge spool and evidence closure; ownership and lifecycle proofs; separate CPU interval attribution; cost model no longer prices ordinary edge HTTP requests as S3 writes and pools edge/store transfer once. Object access-log parser exists, including unknown classification, but the collector is absent.
-7. Replication ledger: real SDK per-attempt hooks, bounded recording, durable SQLite ledger, immutable owner/purpose binding, publication lineage, incomplete/late/crash evidence, Abandon cleanup and read-connection snapshots. Vendored Litestream passes client options through its real S3 clients; close-order fixes preserve final flush. The new vendor test fixture drains multipart request bodies to avoid TCP reset.
-8. NEWEST / least reviewed: host integration in `internal/host/durability.go`, `agent.go`, `movespass.go`, `internal/activator/backend_exec.go`, and `internal/lease/requestmeter_api.go`. Opens ledger before tenant clients; binds account/app/host/purpose with bounded waits; records acquire, replicate, restore, staged copy/delete, move mirror and checkpoint requests; independently publishes lifecycle evidence and closes after child shutdown. Host attestation stays FALSE. No automatic handoff-token replay. Unknown owners never default to ops. External Litestream processes and continuity across arbitrary old-binary restarts are still unproved. Do not enable the meter as complete merely because embedded hooks exist.
+## What exists on the branch
-## Verification and review
+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. Every unmetered replication fallback now files an escape (`DurabilityLedger.Escaped`): the box-day is incomplete and the owner is excluded for the run. Attestation is still false everywhere in production.
+4. `internal/awsbilling`: Cost and Usage Report reader, newest-delivery selection, digests, charged S3 transfer classifier.
+5. `internal/objstore/ownership.go`: append-only bucket ownership ledger, opened at grant, closed at teardown.
+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. Stripe provisioning plan `stripe-provisioning.md` and print-only `stripe-provision.sh` (verified never to invoke the CLI).
+8. Plans: `docs/superpowers/plans/2026-10-01-store-transfer-cur-collector.md` and `2026-10-01-durability-attestation-path.md`.
-- The immutable `verification-0324` snapshot contains the completed quantity/invoice fixes, BEFORE the newest host integration. Source manifest SHA256: `1b25b52b64efa1621511fa8b8c079de13d0ec57f208bf67d6b708a02f890c569`.
-- That snapshot passed `go build ./...`, `make test` (nested launchctx, vet, formatting, all packages plus vendored Litestream S3), required JS pricing parity, and ALL invoice tests under `-race`. Log: `full-gate-snapshot0324-realtemp.txt`; invoice race36.415s.
-- Invoice two-store concurrency and deterministic stale-pre-lease regression passed `-race -count=20` (40.639s). Log: `invoice-lock-contention-root2.txt`.
-- Quantity/checkout/membership audit tests passed `-race` (49.015s), before the final invoice lock changes. Log: `quantity-complete-root-race.txt`.
-- New host ownership, locked-ledger timeout, shutdown/publication and actual fake-HTTP SDK List/Copy/Delete-to-ledger tests passed `-race` (1.551s). Log: `host-durability-sdk-root-race.txt`.
-- New activator meter selection plus existing staging-restore tests passed `-race` (1.032s). Log: `activator-metering-root-race.txt`.
-- The prior intermittent failure was real: a snapshot could read SQLite before a background failure drain, then read the latch after that drain cleared it. The fix retries when a committed drain crosses the read and checks the final latch state under the writer mutex, which spans commit and subtraction. A forced-interleaving regression, an arrival-during-read regression, a sustained-drain regression, and the original late-failure test passed repeated `-race` runs. The complete local gate (`go build ./...`, `go vet ./...`, `make test` with JS parity) passed on the final code on 2026-09-18. Amadan CI passed on the previous handoff commit `099586ea`, first fix commit `eea00ee`, first reviewed amendment `665bcb7`, and final durability commit `c0d60af8`.
-- Opus reviews found and verified three follow-up corrections: arriving failures do not cause retries; after eight crossed drains a ninth snapshot holds a dedicated drain lock so publication gets a stable view; the drainer waits without holding the ordinary recording lock. The ninth-read test proves `Start`/`Finish` still progress and a new failure is counted. Final Opus review found no deadlock or data loss in this diff. Low residual risk: `Close(ctx)` and `Seal` can wait on a fallback read without a context bound, and a long fallback delays drain persistence; this does not block ordinary recording. A reviewer also noted a pre-existing unsynchronized read of `l.state.epoch` in the snapshot; it has not been addressed here.
-- CURRENT tree passed all five cross-builds (Darwin arm64/amd64, Linux arm64, Windows amd64/arm64). Log: `handoff-cross.txt`.
-- Final whole-branch Fable review and production/authority Opus review remain outstanding. All three Claude agents stopped at the shared usage limit, which reports reset at05:50 Europe/Dublin on2026-09-17 (04:50UTC). No agents remain running. That reset time has now passed; availability has not been rechecked for this handoff. Relevant task briefs/reports are in scratch, especially `durability-host-integration-brief.txt`, `opus-durability-integration-design.txt`, `quantity-history-hardening-brief.txt` and the invoice reports. The older design's arbitrary five-minute late-callback slack is NOT accepted proof.
+## Why no overage can be invoiced yet, precisely
-Use BOTH short real temp directories; the symlink `/tmp/j` and a long GOTMPDIR each broke test isolation/socket tests:
+The monthly freeze requires all five billed meters complete. Two cannot be, in production, today:
-```sh
-export TMPDIR=/tmp/k GOTMPDIR=/tmp/k
-export GOCACHE=/home/paulca/.cache/carloku-pricing-overnight-20260916/gocache
-export GOFLAGS=-mod=mod GOPROXY=off CARLOS_REQUIRE_JS_PARITY=1
-go build ./...
-make test
-```
+- `store_egress_gb`: no Cost and Usage Report export exists, so the source is unconfigured. It needs the scoped infrastructure approval in the collector plan, a first real delivery, and ownership ledgers for the stores that already exist.
+- `durability_writes_1k`: no box attests, because the external `carlos-litestream` binary replicates unit-backed routes outside the ledger. The attestation plan gives the two ways out. Do not set the flag to pass a test.
-`/tmp/k` is a task-owned real directory, mode0700. Do not replace it with a symlink. /tmp had approximately2.9GB free at the last check; cache is on the main disk. Never edit source while a test of that source is running; immutable checkpoint tests may run independently.
+## Still missing from the full requirements audit
-## Stripe live account state (read-only, 2026-09-21)
+- Quantity and proration worker: nothing processes subscription-bound member additions, `billingstripe.UpdateQuantity` has no console caller, removals at renewal are not applied, and an account that adds a member holds its own monthly freeze forever. The collection policy is Paul's decision; the pricing review proposes "charged straight away, prorated".
+- 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.
+- House marker door and billing review resolution door: store functions only, no HTTP or CLI caller.
+- 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.
+- Move and checkpoint metering tests in `internal/host/movespass_test.go`: missing.
+- An operator verb to backfill bucket ownership for existing stores: missing.
-- 1Password item `Carloku/Stripe Production Keys/Live Key` authenticated successfully against expected merchant `acct_1UD7LXI4Nrr6AEsO`: Ireland, EUR, charges and payouts enabled.
-- The key is a full `sk_live` provisioning key. Production console configuration deliberately requires an `rk_live` restricted key, which does not exist in the inspected item. Never put the full key into console runtime configuration.
-- The live account has zero active products, recurring prices, portal configurations and webhook endpoints.
-- Stripe legal entity is `Team Tito Limited`, business profile `Carloku`. Paul confirmed Team Tito is VAT and OSS registered. On 2026-09-21 the live account gained and read back two active registrations: Irish `standard` with `place_of_supply_scheme=standard` (`taxreg_1UI9NEI4Nrr6AEsOPpkwK80n`) and Irish `oss_union` (`taxreg_1UI9NQI4Nrr6AEsOrBwcY1rE`). Stable idempotency keys are recorded in `stripe-live-readiness.json`.
-- The account default remains generic tax code `txcd_10000000`; do not use it for Carloku. Paul approved Platform as a Service on 2026-09-21. Configure Personal and Community as `txcd_10102001` (PaaS, personal use) and Business as `txcd_10102000` (PaaS, business use). Stripe says the personal-use category fits products available to serious hobbyists; the business category is for products restricted to commercial enterprise use.
-- `stripe-live-readiness.json` records the exact required 12-price EUR catalog and the remaining mutation gates. The two Tax Registration objects are the only live Stripe mutations so far; no product, price, webhook, portal, customer, subscription or charge was created or changed.
-- Raw API responses were kept only in task-owned scratch with mode 0600. The temporary 1Password session token and raw response files were removed after the registration work.
+## Verification in this run
-## Pending user/external inputs — do not repeatedly ask
+Recorded in the run directory's `logs/`. Targeted: BillingPage tests; usage Durability under `-race`; host and activator under `-race` outside the sandbox; awsbilling; objstore; the new fold and evidence tests, with a mutation check that the hold conditions are pinned. The full gate result and the Amadan CI result for the tip are in `.claude/HANDOFF.md`, which is rewritten at each checkpoint; do not trust a gate claim that is not there with a commit.
-- Pricing copy review: http://docs-t1.tail5fd3ba.ts.net:37461
-- Billing copy review: http://docs-t1.tail5fd3ba.ts.net:34185
-- Postal/location copy review: http://docs-t1.tail5fd3ba.ts.net:46235
-- Review data lives in `writing/{pricing-launch,billing-launch,billing-location}` in scratch. All three servers last responded HTTP200; no result.json was present. No pricing page or billing UI copy has been applied. Apply submitted source strings verbatim; use the writing/copy-review skills for new user-facing copy.
-- Object-store transfer contract is settled: count only transfer that AWS charges Team Tito for. AWS access logs show delivered bytes but cannot reliably identify every free-transfer case, so they are not sufficient billing authority by themselves. The collector needs billing-grade AWS evidence, durable customer attribution and a hold when that evidence is incomplete.
-- Launch scope is settled: the complete metered model is required. Do not launch annual subscriptions with overages waived or defer any meter as a post-launch follow-up.
+Environment that works on docs-t1: inside the sandbox `GOCACHE=$TMPDIR/gocache GOFLAGS='-mod=mod -buildvcs=false' GOPROXY=off`; socket-binding packages (host, activator, edge) and the full gate outside the sandbox with `TMPDIR=/tmp/k GOTMPDIR=/tmp/k` (a real, short, 0700 directory) and the default build cache. Without `-buildvcs=false` eight `TestControlPlane*` tests fail in a linked worktree with "error obtaining VCS status"; that is the worktree, not the code, and it reproduces on `aed42815`.
-## Infrastructure and artifact cautions
+## Stripe and secrets
-The separate infrastructure worktree prepares seven resources for private/versioned/SSE-S3 bucket `carloku-store-logs-271376211898-eu-west-1` and narrowly scoped delivery/host permissions. No source bucket logging was enabled, and no Terraform apply occurred. A refreshed targeted plan has exactly7 creates and0 updates/deletes. The full root plan also contains unrelated dangerous drift (DNS deletion, port25 removal, host user_data replacements/restarts, other-region IAM); NEVER apply it wholesale. Scoped production approval, retention/cost/activation design and ownership history are still required. Current bucket ownership/name/listing does not prove historical ownership.
+Merchant `acct_1UD7LXI4Nrr6AEsO`, Team Tito Limited, Ireland, EUR; Irish standard VAT and Union OSS registrations active; zero products, prices, portal configurations and webhook endpoints as of 2026-09-30. The Stripe CLI is logged in under project `carloku` for read-only live inspection; that credential and the 1Password `sk_live` are provisioning credentials, never the console runtime key, which must be `rk_live_`. Never print or read a secret into output. Raw Terraform plans from September contained an SES SMTP credential: never post or attach plan bodies.
-A local generated full Terraform plan contained an existing unrelated SES SMTP credential and had broad file permissions. Scratch is now0700 and21 plan/JSON artifacts are0600. Do not print, commit, attach, or post those plan bodies. Access by other local users was not established and no credential rotation occurred. Paul was informed. Only unchanged upstream test-key fixtures matched the credential-pattern scan in the platform source.
+## Next actions, in order
-A scratch SQLite experiment was also found replacing local root main.go. It was preserved outside the repo and restored byte-for-byte from the verified checkpoint/HEAD; no CLI change was intended or deployed. The succeeding full gate used the restored entry point. Other changed Go files were compared to the checkpoint and corresponded to known work.
-
-## Next agent actions
-
-1. Read Amadan CI for the final durability-fix commit and inspect the complete branch diff as WIP. The local gate is green, but that does not make the launch ready.
-2. Review host wiring: `bindingFailed` is set on unmetered fallbacks but currently only the permanently false attestation prevents billing. Finish per-route move/checkpoint tests and client inventory, source completeness folding, scoped external-client exclusion, crash/late-callback/restart proof, bounded ledger retention and real-disk latency measurement. Keep unattested usage unbillable until proved.
-3. Implement P5 collector with durable deduplication, immutable historical bucket ownership and AWS billing-grade classification so only transfer AWS charges Team Tito for reaches overage billing. Prepare exact scoped logging activation before seeking production approval.
-4. Complete partial-month billing, quantity/proration collection, operator review/house doors, Free enrollment/caps/idle/flush-only, dormancy warnings/archive and revenue-pool accounting required by the spec. Use the requirements audit rather than treating these as optional follow-ups.
-5. After copy approvals, provision and verify the Stripe catalog with the approved per-plan PaaS codes, run the real sandbox purchase/webhook/portal flow, obtain independent reviews, pass Amadan CI and release gates, then land/deploy through the required reviewed path and verify live behaviour. No main merge or deployment is part of this handoff.
+1. Apply approved copy from both reviews when `result.json` arrives.
+2. Get the independent whole-branch review (Astra via codex, brief committed beside the spec) and resolve findings.
+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.
+5. Choose and build the attestation path. Until then replication writes hold every month.
diff --git a/implementation/carloku-paid-launch/requirements-audit.json b/implementation/carloku-paid-launch/requirements-audit.json
index fc6deeb35..59b9c7c64 100644
--- a/implementation/carloku-paid-launch/requirements-audit.json
+++ b/implementation/carloku-paid-launch/requirements-audit.json
@@ -1,7 +1,7 @@
{
"spec": "docs/superpowers/specs/2026-09-07-carloku-pricing-plans-design.md",
"audit_kind": "incomplete-work inventory; no completion claim",
- "all_changes_local": true,
+ "all_changes_local": false,
"production_billing_verified": false,
"requirements": [
{
@@ -21,22 +21,22 @@
{
"step": 3,
"requirement": "durable collection and completeness",
- "observed_status": "edge proof implemented; P5/P6 integration incomplete",
- "inspection_anchor": "internal/usage/edgespool.go; internal/usage/evidence.go",
+ "observed_status": "edge proof implemented; durability and store-transfer sources now fold into statements with their own completeness (durabilityfold.go, storeegressfold.go) and month evidence; neither can read complete in production yet",
+ "inspection_anchor": "internal/usage/edgespool.go; internal/usage/evidence.go; internal/usage/evidence_month.go",
"complete": false
},
{
"step": 4,
"requirement": "object-store transfer",
- "observed_status": "commercial contract settled: bill only transfer AWS charges Team Tito for; parser exists, but billing-grade classification, collector, historical ownership and logging activation remain incomplete; delivered-byte access logs alone are insufficient",
- "inspection_anchor": "internal/usage/accesslog.go",
+ "observed_status": "collector implemented locally on the AWS Cost and Usage Report: charged-line classifier, newest-delivery reader with digests, append-only bucket ownership ledger opened at grant and closed at teardown, monthly fold that holds on no delivery, uninvoiced month, unclassified charge or unowned bucket, and holds the close until provable. Not complete: no Data Export or bucket policy exists (scoped approval pending, plan in docs/superpowers/plans/2026-10-01-store-transfer-cur-collector.md), existing stores have no ownership ledger until re-granted, and no real delivery has been read",
+ "inspection_anchor": "internal/awsbilling; internal/objstore/ownership.go; internal/console/storeegressfold.go",
"complete": false
},
{
"step": 5,
"requirement": "replication writes",
- "observed_status": "ledger and hooks implemented; host integration interrupted; root continuing; external clients and continuity proof incomplete",
- "inspection_anchor": "internal/usage/durabilityledger.go; internal/lease/requestmeter.go",
+ "observed_status": "ledger, hooks and host integration implemented; epoch read race fixed with a -race regression; every unmetered fallback now files an escape that makes the box-day incomplete; console fold and billable-write policy implemented and tested; production attestation remains false because the root litestream unit is uninstrumented (plan in docs/superpowers/plans/2026-10-01-durability-attestation-path.md); move and checkpoint route tests still missing",
+ "inspection_anchor": "internal/usage/durabilityledger.go; internal/host/durability.go; internal/console/durabilityfold.go",
"complete": false
},
{
@@ -63,8 +63,8 @@
{
"step": 9,
"requirement": "Stripe catalog",
- "observed_status": "live account authenticated; Irish standard VAT and Union OSS registrations active; PaaS classification approved with personal/community txcd_10102001 and business txcd_10102000; console per-plan mapping implemented locally; no products or recurring prices; exact 12-price plan recorded in stripe-live-readiness.json; provisioning blocked on approved display names and remaining release gates",
- "inspection_anchor": "external Stripe account",
+ "observed_status": "live account authenticated; registrations active; PaaS codes approved and mapped per plan; no products or prices; exact provisioning plan and print-only script written (stripe-provisioning.md, stripe-provision.sh); provisioning blocked on copy approval and scoped approval",
+ "inspection_anchor": "external Stripe account; implementation/carloku-paid-launch/stripe-provisioning.md",
"complete": false
},
{
@@ -77,8 +77,8 @@
{
"step": 11,
"requirement": "checkout and portal",
- "observed_status": "local handlers; public copy and explicit proration collection policy pending; no real purchase test",
- "inspection_anchor": "internal/console/billinghandlers.go; internal/console/server.go",
+ "observed_status": "checkout and portal POST doors plus the GET billing page implemented with tests; tailnet demo drives sign-in, step-up, checkout, simulated payment, webhook, active plan and portal against simulated Keymail and Stripe; copy is draft under review; proration policy undecided; no real purchase test",
+ "inspection_anchor": "internal/console/billinghandlers.go; internal/console/billingpage.go; internal/console/billingflow_demo_test.go",
"complete": false
},
{
@@ -126,8 +126,8 @@
{
"step": 18,
"requirement": "restricted Stripe key and webhook config",
- "observed_status": "live sk_live provisioning key found in 1Password and account identity verified; runtime requires a separate rk_live key; webhook endpoint, signing secret and deployed configuration remain absent",
- "inspection_anchor": "cmd/console/billing.go; cmd/console/main.go; internal/console/billingservice.go",
+ "observed_status": "live sk_live provisioning key exists; runtime needs a separate rk_live key whose minimal permissions are listed in stripe-provisioning.md; webhook endpoint, signing secret and deployed configuration remain absent",
+ "inspection_anchor": "cmd/console/billing.go; implementation/carloku-paid-launch/stripe-provisioning.md",
"complete": false
}
],
@@ -147,5 +147,7 @@
"deployment version verification",
"actual Stripe purchase/entitlement/portal proof",
"approved public copy and deployed tax configuration"
- ]
+ ],
+ "audited_at": "2026-10-01",
+ "branch_state": "pushed to origin feat/carloku-paid-launch-20260916; not merged; billing off; nothing deployed"
}