Skip to main content

Why this changed

Staging historically ran with the subscription gate bypassed. That was convenient — no test needed an entitlement to reach a paid path — but it made every paywall assertion on that deployment vacuous: a denial test could pass for the wrong reason, and an enforcement test could only ever pass. Paywall behaviour had to stay a Tier A concern, and the live paywall was the one launch-critical surface no automated test could observe. Staging now runs the gate in enforce mode. Two consequences follow, and both matter when you write a test:

Paid paths need a real entitlement

An identity with no subscription is genuinely denied on staging. Tests that exercise a paid feature must provision Pro first.

Denial is now observable

A free identity really is refused, so the paywall can be asserted against the deployed backend instead of only in the in-memory tier.

The three states

The subscription suite covers the three states a real account can be in, plus the two transitions between them — each on a brand-new, really-logged-in Clerk identity. Transitions are asserted in both directions: free → active Pro opens every gate, and active Pro → lapsed closes every gate.

Three surfaces, checked together

An entitlement is only correct if it agrees on all three surfaces the app actually depends on. Every state case checks the same identity against all three:
1

Convex

A Pro-gated action runs its entitlement check before any work. A denied caller gets a paywall error; an admitted caller gets past the gate.
2

The deployed Hono API

A Pro-gated route’s middleware order is body limit → entitlement → validator, so the status code is the verdict: 401 means identity failed, 402 means entitlement denied, 400 means the request passed both gates and was stopped by the schema.
3

The entitlement projection

The internal endpoint the API actually reads, probed directly. This separates “Convex says free” from “the API asked the wrong deployment” — a distinction that is invisible if you only look at the API’s answer.

The zero-cost probe

Both gate probes send a deliberately invalid body — an empty selection on the Convex side, a malformed field on the API side.
Because each route checks entitlement before it validates input, the failure mode is the verdict: paywall vs validation error on Convex, 402 vs 400 on the API. A correctly-closed gate costs nothing, and a wrongly-open gate costs a validation error rather than a paid AI turn.
“Past the gate” deliberately does not mean “succeeded”. It means the entitlement check passed and a later, cheaper check stopped the call — which is the only evidence a zero-cost probe can give, and exactly the evidence needed. Exactly one case in the suite spends real money: a single end-to-end paid parse that proves an active-Pro identity can complete a real paid operation. Everything else is a status code.

The preflight, and why it skips loudly

Every state assertion is meaningless unless the deployment is actually enforcing. The suite therefore reads the effective gate first and, if it finds the gate bypassed, skips the state matrix with a loud warning rather than letting the denial assertions invert into silent passes.
This is the single most likely false-green in the whole platform. A denial test on a bypassed deployment does not fail — it passes for the wrong reason. Assert the gate state before asserting anything about the paywall, and treat a skip here as a staging-configuration regression, not a pass.
The same preflight pattern applies anywhere else you write a paywall assertion against a real deployment.

Running it

The suite is part of the full Tier C run and the fast lane. Its smoke-tagged cases (the gate preflight and the per-state subscription reads) also run in the smoke lane, so a gate flip on staging is caught in under 30 seconds rather than 30 minutes.

Tier C — Staging Integration

The identity model, production guard, and cost controls these tests run under.

What's in Pro

The user-facing definition of what the gate protects.