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. “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. The same preflight pattern applies anywhere else you write a paywall assertion against a real deployment.Running it
Related
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.
