Deployed Apr 3, 2026. The observe-only brain runs on every invoice.marked_uncollectible and invoice.paid event, reads from Stripe, evaluates all policy decisions, and logs what it would do. No writes, no side effects.

ItemStatusDetail
Code: observe-only brain✅ DoneMR !9658 — internal/dunning package, 44 test cases passing
Wiring: handler integration✅ DoneRuns after audit trail, nil guard for no-op when unwired
Logdash spec: dunning-brain✅ DoneMR !23 — 27 patterns in specs/dunning-brain.yaml
Grafana dashboards✅ DoneAggregate (6 rows, 13 panels) + account drilldown
Deploy to production✅ DoneDeployed Apr 3, 2026 16:00 UTC
Validate parity✅ Done20/20 amounts match, 10/10 BQ coverage, 3/3 filters agree, 0 errors

Validation Results

Validation ran Apr 3–5. Cross-referenced brain ES logs against billing-webhooks behavior and BigQuery invoice data.

20/20

Bad debt amounts match billing-webhooks exactly

10/10

BQ invoices found in brain logs

3/3

Filtered invoices agree (all account_resolution filter)

0

Errors over entire validation period

Fill the three convergence gaps in applystripe so it can be the sole bad debt execution path. See the applystripe convergence page for the full gap analysis and commit breakdown.

Deploy the convergence MR and activate the brain. Writes begin immediately — no config flip. Then disable billing-webhooks dunning path.

subs-api
Step 1 · Deploy
1-3 days
Step 2 · Validate
billing-webhooks
Step 3 · Disable billing-webhooks
1 week
Step 4 · Monitor

Dual-Write Safety

Between Step 1 (brain activated) and Step 3 (billing-webhooks disabled), both systems process the same events. applystripe is fully idempotent — the second writer is a no-op for all actions except email.

ActionIdempotent?During Dual-Write
Flag bad debt in DB✅ YesSets boolean flag — same result on re-apply
Apply account ban✅ YesBan already applied = IAPI no-op
Remove DNU from zones✅ YesAlready removed = IAPI no-op
Cancel subscription✅ YesAlready canceled = Stripe no-op
Update Stripe metadata✅ YesSet to same value — no change
Send bad debt email⚠️ NoCustomer may get duplicate email during overlap. Keep window short.

After 1+ weeks of stable production operation with billing-webhooks dunning disabled.

Cleanup ItemWhereJira
Delete dunning handlersbilling-webhooksFINPE-1402
Delete DISABLED_EVENTS configbilling-webhooks deploymentFINPE-1402
Remove observe-only logging codeinternal/dunning (optional)
Update documentationbilling-knowledge pages
RiskSeverityMitigation
Brain applystripe call fails silentlyMediumErrors are logged non-fatally (dunning brain: applystripe failed). The webhook still succeeds, so Stripe won't retry. Monitor ES for this pattern after deploy. If persistent, revert the convergence MR.
Duplicate email during dual-writeLowCustomer gets two bad debt emails. Keep dual-write window to 1-3 days. Not fixable without a dedup mechanism — acceptable tradeoff.
hasEnterpriseSubs omissionLowBrain may flag accounts that billing-webhooks would skip. All remaining enterprise customers are contract-segment (filtered by shouldSkipDunning). Validated in Phase 1.
TOCTOU: invoice paid between check and banLowPre-existing from billing-webhooks. Same race window. Brain doesn't change the risk profile.
applystripe Gather adds latencyLowapplystripe.Gather makes ~15 sequential external calls inside the webhook handler. If it times out, the error is logged and the webhook succeeds. Stripe will redeliver and retry.

Rollback Strategy

PhaseRollbackEffect
Phase 1Revert MR !9658 → deployBrain stops running. Nothing to undo — it never wrote anything.
Phase 2Revert convergence MR → deployBrain returns to observe-only. applystripe gap fixes remain (harmless).
Phase 3Remove DISABLED_EVENTS in billing-webhooks → restartbilling-webhooks resumes dunning. Both systems active until brain reverted.
TicketSummaryStatusBlocked By
FINPE-1393Ship observe-only dunning brain✅ Closed
FINPE-1394Validate parity (brain vs billing-webhooks)✅ Closed
FINPE-1400Implement write ops — applystripe convergence + invoice-repo🔴 Open
FINPE-1401Cut over dunning from billing-webhooks to brain⏳ OpenFINPE-1400
FINPE-1402Delete billing-webhooks dunning code⏳ OpenFINPE-1401