Skip to content

docs(billing): the docs still described a rail that was deleted yesterday - #514

Merged
github-actions[bot] merged 1 commit into
mainfrom
docs/stripe-rail-staleness
Sep 7, 2026
Merged

docs(billing): the docs still described a rail that was deleted yesterday#514
github-actions[bot] merged 1 commit into
mainfrom
docs/stripe-rail-staleness

Conversation

@catomean

@catomean catomean commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator

#508 removed the Stripe rail. Four living docs kept describing it — and one is named in CLAUDE.md as an SSOT, so the wrong answer was the documented answer.

The Stripe residue

Doc Claimed Reality
development/cloud-local-workflows.md Stripe billing | STRIPE_* in the env-gated feature table Now ORANGECAT_PAY_URL_{PERSONAL,PRO,TEAM}, names checked against lib/oc-pay.ts
sustainability-gates.md Gate 3 argues from a quantity: 1 hardcode in src/app/api/stripe/checkout/route.ts Route deleted
oc-rail-monetization-scope.md "the Stripe webhook already calls" updateUserBilling; /pricing has an isStripeReady() seam to reuse The write survives, the seam does not
development/user-flow-audit.md /api/stripe/portal returns 503 Returns 404

Gate 3 is the interesting one: it did not go stale, it hardened. It used to name a fixable line. The BTC rail that replaced Stripe is a static per-plan URL with no quantity concept, so there is no seat number anywhere in the purchase path to correct — per-seat Team is now a build, not a config change.

CH01–CH04 and the CH04 runbook in the flow audit tested four deleted endpoints; retired with a pointer to what replaced them. scripts/grant-plan.ts claimed it performs "the SAME write the Stripe webhook does" — now names the entitlement route.

Two findings that were not about Stripe

docs/self-improvement-plan.md would have shipped an incomplete change. It enumerates the insertPromptHistory call sites as three. There are four — api/control/tab-inject is missing — and all three line numbers had drifted (270→397, 452→514, 80→138). Anyone implementing runId linking from that list leaves one dispatch path unlinked. Line numbers dropped in favour of "grep the symbol"; likewise src/auth.ts:234 in the OC scope doc, which now points at unrelated code (the scope string is at 268).

src/config/demo.ts still denied /api/checkout under its "money" heading, long after #508 deleted the route — a deny rule for a route on no disk, making the billing section of the sandbox policy look staffed.

demo-sandbox.ts already had the reverse check for DEMO_SAFE_FAMILIES ("a family listed as safe that no longer exists is dead reassurance"). The denied lists had no equivalent, which is exactly why that entry survived. Added the mirror check — it caught /api/checkout on its first run — and mutation-proved it with a fake prefix. 262 assertions.

What I measured and deliberately did NOT gate

I went in intending to build a doc-staleness gate and concluded it would be a gate that lies:

  • 314 path references in docs + CLAUDE.md: clean once false positives were removed. CLAUDE.md's four flagged paths were all correct — two retirement records, a NNNN_ placeholder, a bitbaum/fleet path.
  • 77 pnpm run commands: zero genuinely wrong (the flagged three were desktop/-relative, or prose describing a rename).
  • 22 routes in CLAUDE.md's Views table: all exist (nested route groups).

Twice my scan reported mass failure and both times the scan was wrong, not the docs. The dominant "dead reference" pattern is prose naming a file precisely to record its removal — grep cannot tell that from rot, so such a gate fires on the correct docs and stays silent on the wrong ones.

Verification

pnpm run verify green — 13 steps, exit 0 (137/137 unit files, 150 home/ tests, 262 demo-sandbox assertions).

One local snag worth recording: a first run failed in tsc on .next/dev/types/routes.d.ts. Not a code error — the dev server wrote a shorter version over a longer one without truncating, leaving a d<string, ... fragment of the old tail. Removing the artifact cleared it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01UvjGNAS9CMfEGNW26tUR4P

…rday

#508 removed the Stripe rail. Four living docs kept describing it, and one of
them is named in CLAUDE.md as an SSOT — so the wrong answer was the documented
answer.

- `development/cloud-local-workflows.md` listed `Stripe billing | STRIPE_*` in
  the env-gated feature table. Replaced with the rail that exists:
  `ORANGECAT_PAY_URL_{PERSONAL,PRO,TEAM}`, names checked against lib/oc-pay.ts.
- `sustainability-gates.md` Gate 3 argued from a `quantity: 1` hardcode in
  `src/app/api/stripe/checkout/route.ts`. The gate did not go stale, it
  HARDENED: that route is gone and the BTC rail is a static per-plan URL with no
  quantity concept, so there is no seat number left in the purchase path to
  correct. Per-seat Team is now a build, not a config change.
- `oc-rail-monetization-scope.md` claimed the Stripe webhook "already calls"
  updateUserBilling and that /pricing has an isStripeReady() seam to reuse. The
  write survives (via /api/orangecat/entitlement, plus scripts/grant-plan.ts);
  the seam does not — the BTC CTA is the default case now, not one to add.
- `development/user-flow-audit.md` asserted `/api/stripe/portal` returns 503. It
  returns 404. CH01-CH04 and the CH04 runbook tested four deleted endpoints;
  retired with a pointer to what replaced them.

`scripts/grant-plan.ts` said it performs "the SAME write the Stripe webhook
does" — updated to name the entitlement route instead.

Two findings that were not about Stripe:

`docs/self-improvement-plan.md` enumerated three `insertPromptHistory` call
sites. There are four — `api/control/tab-inject` was missing — and all three
line numbers had drifted (270→397, 452→514, 80→138). Following that list would
have shipped the runId linking with one dispatch path still unlinked. Line
numbers dropped in favour of "grep the symbol"; same for `src/auth.ts:234` in
the OC scope doc, which now points at line 268's unrelated code.

`src/config/demo.ts` still denied `/api/checkout` under its "money" heading long
after #508 deleted that route — a deny rule for a route on no disk, which made
the billing section of the sandbox policy look staffed.

demo-sandbox.ts already had the reverse check for DEMO_SAFE_FAMILIES ("a family
listed as safe that no longer exists is dead reassurance"); the denied lists had
no equivalent, which is why that entry survived. Added the mirror check — it
caught `/api/checkout` on its first run — and mutation-proved it with a fake
prefix. 262 assertions.

Also measured and did NOT gate: doc path references (314) and `pnpm run`
commands (77) both came back clean once false positives were removed, and the
dominant pattern is prose naming a file precisely to record its REMOVAL, which
grep cannot tell from rot. A gate there would fire on the correct docs and stay
silent on the wrong ones. CLAUDE.md's own four flagged paths were all correct,
and all 22 routes in its Views table exist.

verify green (13 steps, exit 0).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UvjGNAS9CMfEGNW26tUR4P
@github-actions
github-actions Bot merged commit a48e7c5 into main Sep 7, 2026
3 checks passed
@github-actions
github-actions Bot deleted the docs/stripe-rail-staleness branch September 7, 2026 05:05
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