Add lifecycle_status = 'published' to the public-catalog queries (plans and add-ons listings) and reject checkout before any Stripe call unless the product behind the price clears the shared member gate (published + active + public). The currently-enrolled ladder rung stays renderable even if its product is later drafted or retired, fetched directly so members keep seeing what they are on. Introduce a single evaluateMemberGate definition shared by the catalog paths and the operator readiness panel so the surfaces cannot disagree about what is publishable for members.
3.3 KiB
webhook-partition-maintenance Specification
Purpose
Keep the monthly RANGE partitions of core.webhook_events — the inbound event store shared by the Stripe and Discourse webhook receivers — provisioned ahead of need, so webhook ingestion never fails for lack of a partition. The baseline migration creates only the current and next two monthly partitions; this capability is the "application boot or a scheduler" creator that migration's contract promises.
Requirements
Requirement: Webhook event partitions are provisioned ahead of need
The system SHALL provide an idempotent ensure operation that creates monthly RANGE partitions of core.webhook_events (named core.webhook_events_YYYY_MM, month-start to next-month-start bounds in UTC) covering the current month plus at least the next three months. Running the operation when partitions already exist SHALL be a no-op (CREATE TABLE IF NOT EXISTS). The operation SHALL execute under a database role that holds CREATE on schema core; if the application's runtime role lacks that privilege, a migration SHALL grant it.
Scenario: Missing future partitions are created
- WHEN the ensure operation runs in a month whose next three monthly partitions do not all exist
- THEN the missing partitions are created with correct month boundaries
- AND webhook inserts timestamped in any of those months succeed
Scenario: Ensure is idempotent
- WHEN the ensure operation runs twice in succession
- THEN the second run completes without error and creates nothing
Requirement: Boot pass ensures partitions without Temporal
The application boot sequence SHALL run the ensure operation before serving traffic, independent of whether Temporal is configured. A boot-pass failure SHALL be logged at warning level or higher and SHALL NOT abort startup.
Scenario: Temporal-less deployment stays covered across restarts
- WHEN a deployment with no Temporal configuration restarts in a month for which no partition exists beyond the migration's initial three
- THEN the boot pass creates the current and upcoming partitions and webhook ingestion continues to work
Scenario: Boot pass failure does not block startup
- WHEN the ensure operation fails at boot (for example, a transient database error)
- THEN the application logs the failure and continues starting up
Requirement: Recurring Temporal schedule keeps partitions extended
When Temporal is configured, the application SHALL register (create-or-update, describe-then-update) a recurring schedule that runs the ensure operation, with the interval configurable via a startup knob defaulting to 24 hours, and SHALL trigger one run immediately on registration. Schedule-spawned runs SHALL be resolvable through the schedule handle (schedule-spawned workflow IDs carry a timestamp suffix).
Scenario: Long-running deployment never exhausts partitions
- WHEN a deployment runs without restart for longer than the partition lookahead window
- THEN the scheduled ensure runs keep partitions created ahead of the current month
- AND no webhook insert ever fails for lack of a partition
Scenario: Schedule registration is idempotent across restarts
- WHEN the application boots repeatedly with Temporal configured
- THEN the schedule is created once and subsequently updated in place, not duplicated