Skip to content

platform: accounts, plans, and the Stripe billing that charges for them - #57

Merged
mattrickslauer merged 3 commits into
mainfrom
account-billing
Aug 15, 2026
Merged

mattrickslauer merged 3 commits into
mainfrom
account-billing

Conversation

@mattrickslauer

Copy link
Copy Markdown
Owner

The account and billing layer, plus the console and landing surfaces that show it.

  • accounts.py, plans.py, billing.py — accounts, the plan catalogue, and Stripe with webhook signature verification
  • platform/schema/033_account_billing.sql — the migration behind them
  • platform/bin/stripe_setup.py — one-shot product/price setup, refuses an sk_live_ key
  • Settings reads the Stripe mode off the key prefix rather than a second flag, so test and live cannot disagree
  • Landing and console reworked to show plan state
  • test_accounts.py, test_billing.py

Stacked on #56 — that one should merge first. Once it does, this diff narrows to the billing work alone.

Not test-verified. Merged at the author's explicit direction without running the suite; main was green at 610 passed beforehand.

🤖 Generated with Claude Code

mattrickslauer and others added 3 commits August 14, 2026 14:35
§3 was "tailored, not blasted" and argued restraint from the schema. Migrations
023 and 026-032 landed and the first contact harvest ran, so it can now argue it
from evidence: contact_route went from 1 row to 2,351 across email, phone and
form, 439 counterparties became reachable, seven sites' robots.txt refused us and
the agents stopped, and zero of 1,622 harvested addresses were fabricated. A
refusal on screen beats a sentence about being responsible.

Telegram and WhatsApp are named, and the wording is load-bearing: "where Telegram
and WhatsApp land when a label needs them". That is a claim about the model and
it is true — `channel` already admits `social`, the consent boundary is one SQL
predicate in `sender._prepare`, and a DM channel is a column value plus an
adapter rather than a second compliance regime. What may not be said is in "What
must not be said": no messenger logo on screen, no implication that we message
anyone on either platform, and no naming of a statute. There are zero `social`
routes and no adapter, and a judge reads a logo as an integration.

The "distributed" claim is the honest one: the route predicate lives in SQL
"rather than in Python so that no caller can forget it", so every worker inherits
it regardless of which agent runs. There is no rogue agent that forgot the rule,
because the rule is not in the agent.

Two corrections found while writing it:

  - §3 has told the shoot, since this file was written, to film a `contact_route`
    row in `opted_out`. That state has never held a row — measured today, 2,350
    unverified and 1 verified. It was true of the design and never of the table.
    Now a gate in the pre-shoot checklist.
  - §4 also said "one open thread per counterparty is a partial unique index",
    which §3 now says. Cut from §4: twice is four seconds the runtime does not
    have, and the second mention sounds like a point that could not find a home.

Runtime 2:55 against the 3:00 cap: §3 grew 0:30 -> 0:44, fourteen seconds from
the existing headroom and four from that cut. Genre precision moved to §2, where
the vector index is already on screen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…tion

Two things, because they divide along the runtime cap.

**SUBMISSION.md** is new: the who / what / where / when / why-CockroachDB / how
rundown, with no 3:00 budget forcing it to choose. It is the narrative of record
for a judge and for a model reading the repository, and it sits under the
adversarial sponsor audit rather than over it.

Every hard figure in it was executed against the cluster on 2026-08-14 and
checked again before this commit: 14,170 counterparties, 66,103 live facts,
14,169 roles classified, 2,351 contact routes, 439 reachable, 55,569 agent runs,
$0.12 all time, 3,180 country facts.

The centrepiece SQL is now `agents.shortlist_as_of` in shape rather than a
plausible-looking query — `<=>` not `<->`, `party@party_shortlist`, all four
predicates in the index prefix. Writing it out surfaced a detail that belonged in
the submission and was not in it: past `gc.ttlseconds` the function *raises*
rather than retrying without the clause, because retrying would return the
current ranking presented as the historical one — a true answer to a question
nobody asked, offered as the justification for having emailed somebody. A system
that can say "I cannot tell you why, and here is exactly why I cannot" is more
accountable than one that always has an answer.

The "global label, thousands of conversations" framing is stated as the design
and immediately labelled with its N, because the cluster holds one thread, one
send, one region and US-only counterparties. The argument is not that we ran
thousands; it is that every guarantee making thousands safe is a constraint, and
a constraint holds identically at one and at a hundred thousand. A dedicated
section lists the seven things this submission does not claim.

**DEMO_SCRIPT.md §3** was a replacement and should have been an addition. The
lines cut in the last commit are back — blast radius, `opted_out` as a state no
discovery stage can overwrite, the sender refusing an inferred address, and
"sustainable outreach is a schema property here, not a policy document" — now
carrying the contactability evidence underneath rather than instead. Only the
genre line stayed moved, because §2 says it better with the vector index already
on screen.

That costs four seconds over the cap, so §1 gets a marked trim that returns five:
it was already 0:26 in a 0:20 slot, and it no longer needs to name the sources
now that §3 shows them. Cut runs 2:59.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds the account and billing layer: an accounts module, a plan catalogue,
a Stripe integration with webhook signature verification, and the 033
schema migration that backs them. Settings learns to read the Stripe mode
off the key prefix rather than a second flag, so test and live cannot
disagree. Console and landing surfaces are reworked to show plan state.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@mattrickslauer
mattrickslauer merged commit e6c6bd4 into main Aug 15, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant