per-type treatment specializes the brain's decision function

brain reads Stripe invoice status — accuracy is a prerequisite

applystripe/plan.go is the brain's existing foundation

Five independent dunning code paths — no locks, no dedup, no coordination between any of them
PathNameTriggerWhat it doesProblem
AWebhook recordingbilling-webhooks forwards Stripe eventsWrites to subs_dunning tableNobody reads subs_dunning — orphaned audit log
BDrift remediationAdmin / cron triggerGather-compute-apply from Stripe invoices → flag/unflag bad debt, ban/unbanNot wired to webhook events
CSubscription cancellationbilling-webhooks calls subs-apiCancels PayGo subscriptionsOnly PayGo; no coordination with Path B
DPay bad debtCustomer-facing endpointCreates consolidated invoice, processes paymentDoes NOT unflag account or reactivate subscriptions
EAdmin flag/unflagAdmin endpointDirectly mutates subs_account, subs_customer, Stripe metadataManual escape hatch, no automation
billing-webhooks owns dunning decisions today

flagAsBadDebt handler cancels, flags, bans, updates Stripe. subs-api is a passive executor.

subs_dunning is an orphaned audit log

Written by Path A, read by nobody. Drift remediation reads Stripe invoices directly.

What drift remediation (Path B) actually reads vs. what the design docs claim
SourceRead?Notes
Stripe invoicesYesUncollectible status + metadata (bad_debt_amount, bad_debt_paid)
subs_accountYesAccount type, flags (bit 15 = AcctFlagBadDebt)
subs_customerYesbad_debt timestamp
subs_product_probationYesWhether customer failed payment probation
subs_dunningNoNever read — orphaned audit log
subs_mandateNoNot referenced in drift remediation
subs_stuck_eventsNoNot referenced in drift remediation
Account type gates — binary supported/unsupported, not per-type rules
CategoryTypesTreatment
Supported (12)pay_go, startup, ad_hoc_contract, election, employee, galileo, ibm, intern, jdc, pangea, partners_basic, partners_entFull drift remediation: flag/unflag, ban/unban, Stripe metadata, sub item deletion
Unsupported (4)cloudflare_ent, msp, partners_pay_go, sfccEntire drift plan skipped — RestrictionUnsupportedAccountType
625
inconsistent debt flags
accounts where racing paths produced contradictory state
4,825
subs not reactivated
after customer paid bad debt — Path D doesn't unflag
300+
stuck dunning events
no retry/dead-letter mechanism in webhook path
No transaction wrapping in flagAsBadDebt

Cancel + flag + ban + update Stripe without wrapping. Partial failures leave inconsistent state.

Customer pays before flag processes

Payment succeeds but customer still gets flagged — handler doesn't check current invoice status.

Uncollectible then voided within seconds

Stripe voids immediately after marking uncollectible. Flag + ban + cancel fires before void arrives.

Single decision point eliminates racing

Five paths → one function. Read all state, compute correct, apply delta.

Extends drift remediation

applystripe already does gather-compute-apply. Brain Expansion wires it to webhook events.

Idempotent by design

Same event twice → same result. Reads current state, applies only the diff.

Skip → decide differently per type

Replace binary gates with graduated consequences. Requires business signoff.

Moving decisions to the brain is not a port of billing-webhooks code. The old code reads partial state and makes isolated decisions across scattered handlers. The brain reads everything and makes one coordinated decision.

Q2 '26
Phase 1: Bad debt flagging
Q2 '26
Phase 2: Bad debt lifting
Q3 '26
Phase 3: Account type rules
Q3 '26
Clean Pipe: delete old code
Brain Expansion must ship before Clean Pipe deletes billing-webhooks dunning code
Phase 1 — Flag + ban + cancel as one action

No dependencies. Tests must prove parity with billing-webhooks.

Phase 2 — Evaluate all invoices, decide whether to lift

Replaces the gap where Path D doesn't unflag. Depends on Phase 1.

Phase 3 — Per-type handling in brain evaluation

Replaces binary gates with graduated consequences. Depends on Phase 2 + segment discovery.

Known Jira issues — all trending upward
TicketImpactRoot cause
BILLACCT-2884,825 subscriptions not reactivated after paymentPath D does NOT unflag. Unflagging depends on drift running later.
BILLACCT-756Multiple payments for same bad debt invoiceNo dedup between Path D and Path B. No atomic check-and-update.
BILLACCT-1076Invoices uncollectible then voided within secondsFlag fires before void arrives. Customer flagged for voided invoice.
BILLSUB-229300+ stuck dunning eventsNo retry/dead-letter in webhook dunning path.
BILLACCT-1025Failed payment invoices not voidedUnvoided invoices inflate bad debt calculations in drift.
Coverage gap during transition

Both old and new code run during dual-write. Brain must handle every scenario before deletion.

Segment rules need business signoff

Per-type rules don't exist yet. Finance and product must define them.