lib/billing.ts:142-151 exports SAMPLE_SUBSCRIPTION, a hardcoded fixture that regenerates a fresh "7 days left" trial on every page load. The checkout and portal calls just throw. /settings/billing renders the fixture and admits "Demo data" on the page itself.
The real organization document sets subscription_status: None (routers/links.py:182-183, whose own comment notes that nothing reads or enforces these fields) and nothing ever writes anything else. The trial banner is not reading that field at all — it reads the fixture, unconditionally, for every org.
Billing was deliberately descoped pending a pricing decision (BUSINESS_MODEL.md §9), so this is not a request to build Stripe. The immediate problem is narrower: the UI actively misrepresents account state.
Minimum acceptable interim fix: stop presenting a fabricated trial as if it were real.
`lib/billing.ts:142-151` exports `SAMPLE_SUBSCRIPTION`, a hardcoded fixture that regenerates a fresh "7 days left" trial on every page load. The checkout and portal calls just `throw`. `/settings/billing` renders the fixture and admits "Demo data" on the page itself.
The real organization document sets `subscription_status: None` (`routers/links.py:182-183`, whose own comment notes that nothing reads or enforces these fields) and nothing ever writes anything else. The trial banner is not reading that field at all — it reads the fixture, unconditionally, for every org.
Billing was deliberately descoped pending a pricing decision (`BUSINESS_MODEL.md` §9), so this is not a request to build Stripe. The immediate problem is narrower: the UI actively misrepresents account state.
Minimum acceptable interim fix: stop presenting a fabricated trial as if it were real.
Correcting this issue — I overstated it when filing, and the overstatement is the reason it looked urgent.
The fixture is real: lib/billing.ts:142 still defines SAMPLE_SUBSCRIPTION with a rolling 7-day trial, and getCurrentSubscription() returns it unconditionally. But it is not rendered anywhere except /settings/billing, and that page already says so on itself: "Demo data — this page isn't connected to a live payment processor." There is no trial banner in the app chrome, no nav badge, nothing on the dashboard. Grepping trial across app/ and components/ returns exactly two hits, both inside app/settings/billing/page.tsx.
So the accurate framing is "a demo page labelled as a demo page", not "the UI actively misrepresents account state to every org". That is a much weaker reason to act.
Leaving this open and folded into the descoped billing work rather than patching it tonight — building a half-real subscription state before the pricing decision exists would just be a second fixture. The TODO(billing) comments in lib/billing.ts already name the endpoints the real implementation needs (GET /org/subscription, /org/usage, /org/invoices).
Worth keeping in view: routers/links.py:182-183 writes subscription_status: None on the org document and nothing ever reads or enforces it, so the backend has a stub too.
Correcting this issue — I overstated it when filing, and the overstatement is the reason it looked urgent.
The fixture is real: `lib/billing.ts:142` still defines `SAMPLE_SUBSCRIPTION` with a rolling 7-day trial, and `getCurrentSubscription()` returns it unconditionally. But it is **not** rendered anywhere except `/settings/billing`, and that page already says so on itself: *"Demo data — this page isn't connected to a live payment processor."* There is no trial banner in the app chrome, no nav badge, nothing on the dashboard. Grepping `trial` across `app/` and `components/` returns exactly two hits, both inside `app/settings/billing/page.tsx`.
So the accurate framing is "a demo page labelled as a demo page", not "the UI actively misrepresents account state to every org". That is a much weaker reason to act.
Leaving this open and folded into the descoped billing work rather than patching it tonight — building a half-real subscription state before the pricing decision exists would just be a second fixture. The `TODO(billing)` comments in `lib/billing.ts` already name the endpoints the real implementation needs (`GET /org/subscription`, `/org/usage`, `/org/invoices`).
Worth keeping in view: `routers/links.py:182-183` writes `subscription_status: None` on the org document and nothing ever reads or enforces it, so the backend has a stub too.
The board ruled (Decision 4) that pricing comes off the public site entirely, replaced with "Pricing in development - contact us", rather than keeping the invented $0/$79/custom catalog behind a below-the-fold disclaimer. The CMO's softer alternative was explicitly overruled: the number is not merely unwired, it is invented, and BUSINESS_MODEL.md §3.1 - now ratified - says so in writing.
This issue (fabricated trial state shown to every org) is the same class of problem on the authenticated side. The public-surface half is tracked in #46; keep this one for the in-app billing UI.
**Referenced by board minutes #42 (2026-08-23).**
The board ruled (Decision 4) that **pricing comes off the public site entirely**, replaced with "Pricing in development - contact us", rather than keeping the invented `$0/$79/custom` catalog behind a below-the-fold disclaimer. The CMO's softer alternative was explicitly overruled: the number is not merely unwired, it is *invented*, and `BUSINESS_MODEL.md` §3.1 - now ratified - says so in writing.
This issue (fabricated trial state shown to every org) is the same class of problem on the authenticated side. The public-surface half is tracked in **#46**; keep this one for the in-app billing UI.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
lib/billing.ts:142-151exportsSAMPLE_SUBSCRIPTION, a hardcoded fixture that regenerates a fresh "7 days left" trial on every page load. The checkout and portal calls justthrow./settings/billingrenders the fixture and admits "Demo data" on the page itself.The real organization document sets
subscription_status: None(routers/links.py:182-183, whose own comment notes that nothing reads or enforces these fields) and nothing ever writes anything else. The trial banner is not reading that field at all — it reads the fixture, unconditionally, for every org.Billing was deliberately descoped pending a pricing decision (
BUSINESS_MODEL.md§9), so this is not a request to build Stripe. The immediate problem is narrower: the UI actively misrepresents account state.Minimum acceptable interim fix: stop presenting a fabricated trial as if it were real.
Correcting this issue — I overstated it when filing, and the overstatement is the reason it looked urgent.
The fixture is real:
lib/billing.ts:142still definesSAMPLE_SUBSCRIPTIONwith a rolling 7-day trial, andgetCurrentSubscription()returns it unconditionally. But it is not rendered anywhere except/settings/billing, and that page already says so on itself: "Demo data — this page isn't connected to a live payment processor." There is no trial banner in the app chrome, no nav badge, nothing on the dashboard. Greppingtrialacrossapp/andcomponents/returns exactly two hits, both insideapp/settings/billing/page.tsx.So the accurate framing is "a demo page labelled as a demo page", not "the UI actively misrepresents account state to every org". That is a much weaker reason to act.
Leaving this open and folded into the descoped billing work rather than patching it tonight — building a half-real subscription state before the pricing decision exists would just be a second fixture. The
TODO(billing)comments inlib/billing.tsalready name the endpoints the real implementation needs (GET /org/subscription,/org/usage,/org/invoices).Worth keeping in view:
routers/links.py:182-183writessubscription_status: Noneon the org document and nothing ever reads or enforces it, so the backend has a stub too.Referenced by board minutes #42 (2026-08-23).
The board ruled (Decision 4) that pricing comes off the public site entirely, replaced with "Pricing in development - contact us", rather than keeping the invented
$0/$79/customcatalog behind a below-the-fold disclaimer. The CMO's softer alternative was explicitly overruled: the number is not merely unwired, it is invented, andBUSINESS_MODEL.md§3.1 - now ratified - says so in writing.This issue (fabricated trial state shown to every org) is the same class of problem on the authenticated side. The public-surface half is tracked in #46; keep this one for the in-app billing UI.