Context
modules/billing/middlewares/billing.requirePlan.js gates by subscription?.plan only. A subscription with plan: 'growth' but status: 'canceled' (or past_due/unpaid) still passes requirePlan('growth','pro').
Pre-existing (middleware had zero route usages until trawl_node #1183 wired it on apiKeys/webhooks/org-invites for plan-entitlement enforcement). Surfaced during Trawl Phase 2 (infra plan 2026-05-19-pricing-perfect-pre-stripe-live.md). Left out of scope deliberately (locked design = plan-membership 403; status filtering would expand the contract).
Ask
Decide intended semantics: should an active-plan check require status ∈ {active, trialing}? If yes, add status filtering in requirePlan (generic, propagates to all downstream via /update-stack).
Related divergence to reconcile
trawl_node #1183 already changed requirePlan.js response shape to surface {errorCode:'PLAN_REQUIRED', requiredPlans, currentPlan} at top level (the documented contract wasn't achievable with the old responses.error nesting). That is currently a downstream patch in trawl_node — should be upstreamed here (or tracked in trawl_node DOWNSTREAM_PATCHES.md) so /update-stack --theirs doesn't wipe it. Backward-compat verified (6 existing requirePlan unit tests pass).
Context
modules/billing/middlewares/billing.requirePlan.jsgates bysubscription?.planonly. A subscription withplan: 'growth'butstatus: 'canceled'(or past_due/unpaid) still passesrequirePlan('growth','pro').Pre-existing (middleware had zero route usages until trawl_node #1183 wired it on apiKeys/webhooks/org-invites for plan-entitlement enforcement). Surfaced during Trawl Phase 2 (infra plan
2026-05-19-pricing-perfect-pre-stripe-live.md). Left out of scope deliberately (locked design = plan-membership 403; status filtering would expand the contract).Ask
Decide intended semantics: should an active-plan check require
status ∈ {active, trialing}? If yes, add status filtering inrequirePlan(generic, propagates to all downstream via /update-stack).Related divergence to reconcile
trawl_node #1183 already changed
requirePlan.jsresponse shape to surface{errorCode:'PLAN_REQUIRED', requiredPlans, currentPlan}at top level (the documented contract wasn't achievable with the oldresponses.errornesting). That is currently a downstream patch in trawl_node — should be upstreamed here (or tracked in trawl_node DOWNSTREAM_PATCHES.md) so /update-stack --theirs doesn't wipe it. Backward-compat verified (6 existing requirePlan unit tests pass).