proof-harness 0.1.0-next.1 → 0.1.0-next.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/COMPATIBILITY.md +1 -1
- package/README.md +6 -1
- package/dist/playwright.d.ts +15 -4
- package/dist/playwright.js +24 -1
- package/dist/playwright.js.map +1 -1
- package/package.json +1 -1
package/COMPATIBILITY.md
CHANGED
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ Public prereleases are published under the npm `next` tag. Pin an exact
|
|
|
17
17
|
prerelease version while the API is stabilizing:
|
|
18
18
|
|
|
19
19
|
```sh
|
|
20
|
-
pnpm add proof-harness@0.1.0-next.
|
|
20
|
+
pnpm add proof-harness@0.1.0-next.3
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
The package exposes environment-specific entry points:
|
|
@@ -44,6 +44,11 @@ Named seed actors (`"admin"` and `"member"`) read
|
|
|
44
44
|
`SEED_MEMBER_EMAIL`/`SEED_MEMBER_PASSWORD` from the consumer environment. The
|
|
45
45
|
package does not ship product seed credentials.
|
|
46
46
|
|
|
47
|
+
Disposable users that authenticate a Playwright page must call
|
|
48
|
+
`actAsUser.logout(page)` before their auth row is deleted.
|
|
49
|
+
`assert.tenantIsolation({ page })` performs that browser cleanup automatically;
|
|
50
|
+
the page is logged out when the helper returns.
|
|
51
|
+
|
|
47
52
|
## License
|
|
48
53
|
|
|
49
54
|
Copyright 2026 The Nexus Collective GmbH.
|
package/dist/playwright.d.ts
CHANGED
|
@@ -17,6 +17,14 @@ declare const actAsUser: {
|
|
|
17
17
|
* spec that creates fresh users per run.
|
|
18
18
|
*/
|
|
19
19
|
loginAs(page: Page, email: string, password: string): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Stop requests from the current document, clear the browser context's
|
|
22
|
+
* shared cookie jar, and allow already-accepted server work to drain. Call
|
|
23
|
+
* before deleting a disposable user that authenticated this page; otherwise
|
|
24
|
+
* router prefetches or streaming continuations can keep presenting that
|
|
25
|
+
* user's JWT after its auth row is gone.
|
|
26
|
+
*/
|
|
27
|
+
logout(page: Page): Promise<void>;
|
|
20
28
|
/**
|
|
21
29
|
* Invoke a server action end-to-end while authenticated as the given user.
|
|
22
30
|
*
|
|
@@ -124,10 +132,13 @@ interface TenantIsolationBaseOptions {
|
|
|
124
132
|
where: Record<string, unknown>;
|
|
125
133
|
};
|
|
126
134
|
/**
|
|
127
|
-
* Optional: pass a `page` to
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
135
|
+
* Optional: pass a `page` to verify userB's browser login roundtrip during
|
|
136
|
+
* the probe. The helper stops that document and clears its cookies before
|
|
137
|
+
* deleting the disposable users, so the page is intentionally logged out
|
|
138
|
+
* when this method returns.
|
|
139
|
+
*
|
|
140
|
+
* Product-specific UI assertions need their own fixture lifecycle; they
|
|
141
|
+
* cannot safely reuse credentials after this helper tears them down.
|
|
131
142
|
*/
|
|
132
143
|
page?: Page;
|
|
133
144
|
/**
|
package/dist/playwright.js
CHANGED
|
@@ -235,6 +235,23 @@ var actAsUser = {
|
|
|
235
235
|
async loginAs(page, email, password) {
|
|
236
236
|
await callLoginRoute(page, { email, password });
|
|
237
237
|
},
|
|
238
|
+
/**
|
|
239
|
+
* Stop requests from the current document, clear the browser context's
|
|
240
|
+
* shared cookie jar, and allow already-accepted server work to drain. Call
|
|
241
|
+
* before deleting a disposable user that authenticated this page; otherwise
|
|
242
|
+
* router prefetches or streaming continuations can keep presenting that
|
|
243
|
+
* user's JWT after its auth row is gone.
|
|
244
|
+
*/
|
|
245
|
+
async logout(page) {
|
|
246
|
+
const context = page.context();
|
|
247
|
+
if (!page.isClosed()) {
|
|
248
|
+
await page.goto("about:blank");
|
|
249
|
+
}
|
|
250
|
+
await context.clearCookies();
|
|
251
|
+
if (!page.isClosed()) {
|
|
252
|
+
await page.waitForTimeout(500);
|
|
253
|
+
}
|
|
254
|
+
},
|
|
238
255
|
/**
|
|
239
256
|
* Invoke a server action end-to-end while authenticated as the given user.
|
|
240
257
|
*
|
|
@@ -1595,7 +1612,13 @@ var assertMethods = {
|
|
|
1595
1612
|
await actAsUser.loginAs(page, userB.email, userB.password);
|
|
1596
1613
|
}
|
|
1597
1614
|
} finally {
|
|
1598
|
-
|
|
1615
|
+
try {
|
|
1616
|
+
if (page) {
|
|
1617
|
+
await actAsUser.logout(page);
|
|
1618
|
+
}
|
|
1619
|
+
} finally {
|
|
1620
|
+
await teardown(orgA, orgB, userA, userB);
|
|
1621
|
+
}
|
|
1599
1622
|
}
|
|
1600
1623
|
},
|
|
1601
1624
|
/**
|
package/dist/playwright.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/playwright/actAsUser.ts","../src/server/transient.ts","../src/server/auth-rate-limit.ts","../src/playwright/trace.ts","../src/server/code-provenance.ts","../src/shared/trace-types.ts","../src/server/service-client.ts","../src/server/seed.ts","../src/server/fixture.ts","../src/playwright/assert.ts"],"sourcesContent":["// Import External Packages\nimport type { Page } from \"@playwright/test\";\nimport { createClient, type SupabaseClient } from \"@supabase/supabase-js\";\n// Import Local Imports\nimport { createRetryingFetch, retryTransient } from \"../server/transient\";\nimport {\n isAuthRateLimited,\n retryAuthRateLimit,\n} from \"../server/auth-rate-limit\";\nimport type { ActionResult } from \"../shared/action-types\";\n// Import Core Dependencies\n// Import Shared Dependencies\n// Import Extension Dependencies\n\n// ---------------------------------------------------------------------------\n// actAsUser\n//\n// Two surfaces for \"become a user\" inside a proof spec:\n//\n// 1. `actAsUser.login(page, role)` / `actAsUser.loginAs(page, email, pw)`\n// — log a Playwright browser context into Supabase via\n// `POST /api/proof/login-as-seed-user`. Use when the proof drives the\n// UI: cookies are written into `page.request`'s shared jar and Next.js\n// sees the session after a `page.reload()`.\n//\n// 2. `actAsUser.supabaseClient({ email, password })`\n// — return a *fresh* Supabase client authenticated as the given user, no\n// cookies involved. Use when the proof needs to query / mutate the DB\n// with the user's real RLS privileges, entirely outside the browser\n// (e.g. \"prove user B's DELETE against user A's row is filtered by\n// RLS\"). This is the primitive behind `assert.tenantIsolation`'s\n// primary check.\n// ---------------------------------------------------------------------------\n\ninterface LoginOkResponse {\n ok: true;\n userId: string | null;\n email: string;\n}\n\ninterface LoginErrorResponse {\n error: string;\n suggestion: string;\n}\n\ninterface ActionInvokeErrorResponse {\n success: false;\n error: string;\n suggestion?: string;\n}\n\nfunction isActionResult<TData>(body: unknown): body is ActionResult<TData> {\n if (typeof body !== \"object\" || body === null) return false;\n const b = body as { success?: unknown; data?: unknown; error?: unknown };\n if (b.success === true) return \"data\" in b;\n if (b.success === false) return typeof b.error === \"string\";\n return false;\n}\n\nfunction getProofSecret(): string {\n const secret = process.env.API_SECRET_KEY;\n if (!secret) {\n throw new Error(\n \"[PROOF_FAIL] proof_secret_missing: API_SECRET_KEY is not set in the test runner env\\n\" +\n \" file: src/playwright/actAsUser.ts\\n\" +\n \" suggestion: Run setup-local-env.sh or ensure .env.local is loaded. Playwright tests need API_SECRET_KEY to sign proof requests.\",\n );\n }\n return secret;\n}\n\nasync function callLoginRoute(\n page: Page,\n payload: { role: \"admin\" | \"member\" } | { email: string; password: string },\n): Promise<void> {\n // Retried on 5xx only: the route talks to Supabase, so a gateway blip arrives\n // as a server error, while a 401 is the auth system answering and must be\n // reported as-is.\n const { response } = await retryAuthRateLimit(\n \"proof login route\",\n () =>\n retryTransient(\"proof login route\", async () => {\n const res = await page.request.post(\"/api/proof/login-as-seed-user\", {\n headers: {\n \"x-proof-secret\": getProofSecret(),\n \"Content-Type\": \"application/json\",\n },\n data: payload,\n });\n return {\n response: res,\n status: res.status(),\n error: res.ok()\n ? null\n : { message: res.statusText(), status: res.status() },\n };\n }),\n (result) => ({\n limited: result.status === 429,\n retryAfter:\n typeof result.response.headers === \"function\"\n ? result.response.headers()[\"retry-after\"]\n : undefined,\n }),\n );\n\n if (!response.ok()) {\n let body: Partial<LoginErrorResponse> = {};\n try {\n body = (await response.json()) as LoginErrorResponse;\n } catch {\n // Non-JSON error (network / HTML error page). Fall through.\n }\n const label = \"email\" in payload ? payload.email : `role=${payload.role}`;\n const category = isAuthRateLimited(\n { message: body.error, status: response.status() },\n response.status(),\n body.error,\n )\n ? \"auth_rate_limited\"\n : \"auth_signin\";\n throw new Error(\n `[PROOF_FAIL] ${category}: login failed for ${label} (status ${response.status()})\\n` +\n ` file: src/playwright/actAsUser.ts\\n` +\n ` suggestion: ${body.suggestion ?? body.error ?? \"Check that the dev server is running and seed users exist.\"}`,\n );\n }\n\n // Validate shape just enough to surface parse errors early.\n const json = (await response.json()) as LoginOkResponse;\n if (!json.ok) {\n throw new Error(\n \"[PROOF_FAIL] auth_signin: login route returned ok:false\\n\" +\n \" file: src/playwright/actAsUser.ts\\n\" +\n \" suggestion: Inspect the response body of /api/proof/login-as-seed-user.\",\n );\n }\n\n // If the page is already somewhere, reload so Next.js picks up the cookies.\n if (page.url() !== \"about:blank\") {\n await page.reload();\n }\n}\n\nexport const actAsUser = {\n /**\n * Log in as one of the seeded development users (`admin` or `member`).\n * The consumer application's proof login route resolves the role to its\n * configured server-only seed credentials.\n */\n async login(page: Page, role: \"admin\" | \"member\"): Promise<void> {\n await callLoginRoute(page, { role });\n },\n\n /**\n * Log in as an arbitrary user — typically one created by `seed.user()`\n * inside a `.proof.ts` spec. Used by `assert.tenantIsolation` and any\n * spec that creates fresh users per run.\n */\n async loginAs(page: Page, email: string, password: string): Promise<void> {\n await callLoginRoute(page, { email, password });\n },\n\n /**\n * Invoke a server action end-to-end while authenticated as the given user.\n *\n * Unlike `actAsUser.supabaseClient` — which goes directly to the DB and\n * therefore only proves RLS — this helper goes through the full Next.js\n * request lifecycle: route handler → middleware pipeline → `withAuth`,\n * `withTenantIsolation`, the action's own logic, and any Zod validation.\n * It is the primitive you reach for when proving action-layer invariants\n * (RBAC rejection, validation errors, business-logic branching) that RLS\n * alone cannot cover.\n *\n * The action must be listed in\n * the application's generated Proof action registry. Unregistered actions\n * return a 404 with a structured [PROOF_FAIL] message.\n *\n * Authentication:\n * - `as: \"admin\" | \"member\"` — uses the seeded dev users\n * - `as: { email, password }` — uses arbitrary credentials (typically a\n * user created by `seed.user()` earlier in the proof)\n *\n * A registered action returns an `ActionResult<TData>`. Harness failures\n * (missing registry entry, malformed request, action escaping the pipeline)\n * throw instead: absence or breakage of the thing under test must never\n * satisfy a refusal proof.\n *\n * @example\n * const result = await actAsUser.invokeAction<{ removed: boolean }>(\n * page,\n * {\n * as: \"member\",\n * module: \"workspace\",\n * action: \"removeMember\",\n * inputParams: { formData: { workspaceId, memberId } },\n * },\n * );\n * expect(result.success, \"[PROOF_FAIL] authorization: ...\").toBe(false);\n */\n async invokeAction<TData = unknown>(\n page: Page,\n opts: {\n as: \"admin\" | \"member\" | { email: string; password: string };\n module: string;\n action: string;\n inputParams: Record<string, unknown>;\n },\n ): Promise<ActionResult<TData>> {\n if (typeof opts.as === \"string\") {\n await actAsUser.login(page, opts.as);\n } else {\n await actAsUser.loginAs(page, opts.as.email, opts.as.password);\n }\n\n const response = await page.request.post(\"/api/proof/invoke-action\", {\n headers: {\n \"x-proof-secret\": getProofSecret(),\n \"Content-Type\": \"application/json\",\n },\n data: {\n module: opts.module,\n action: opts.action,\n inputParams: opts.inputParams,\n },\n });\n\n let body: unknown;\n try {\n body = await response.json();\n } catch {\n throw new Error(\n `[PROOF_FAIL] action_invoke: response from /api/proof/invoke-action is not valid JSON (status ${response.status()})\\n` +\n \" file: src/playwright/actAsUser.ts\\n\" +\n \" suggestion: The dev server likely returned an error page. Check server logs and confirm the route file at app/api/proof/invoke-action/route.ts is registered.\",\n );\n }\n\n if (!response.ok()) {\n const harnessError = body as Partial<ActionInvokeErrorResponse>;\n throw new Error(\n `${typeof harnessError.error === \"string\" ? harnessError.error : `[PROOF_FAIL] action_invoke: harness request failed (status ${response.status()})`}\\n` +\n \" file: src/playwright/actAsUser.ts\\n\" +\n ` suggestion: ${harnessError.suggestion ?? \"Inspect /api/proof/invoke-action and confirm the action is registered and pipeline-wrapped.\"}`,\n );\n }\n\n if (!isActionResult<TData>(body)) {\n throw new Error(\n `[PROOF_FAIL] action_invoke: response body is not a valid ActionResult shape (status ${response.status()})\\n` +\n ` file: src/playwright/actAsUser.ts\\n` +\n ` body: ${JSON.stringify(body)}\\n` +\n \" suggestion: The invoke route must always return { success, data } or { success: false, error }. Confirm PROOF_ACTION_REGISTRY contains the action and that it uses createAction().\",\n );\n }\n\n return body;\n },\n\n /**\n * Return a Supabase client authenticated as the given user via\n * `signInWithPassword` against a *fresh* client (no cookies, no session\n * persistence). Queries made through the returned client run with that\n * user's RLS privileges.\n *\n * Use for server-side proofs where the browser is irrelevant and you want\n * direct evidence of whether the DB allows or denies a specific operation\n * for a specific user. This is the primitive behind the primary check in\n * `assert.tenantIsolation` and is intentionally exposed for specs that\n * need finer-grained RLS probes.\n */\n async supabaseClient(opts: {\n email: string;\n password: string;\n }): Promise<SupabaseClient> {\n const url = process.env.NEXT_PUBLIC_SUPABASE_URL;\n const publishableKey = process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY;\n\n if (!url || !publishableKey) {\n throw new Error(\n \"[PROOF_FAIL] config_missing: expected NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY to be set, found one or both missing\\n\" +\n \" file: src/playwright/actAsUser.ts\\n\" +\n \" suggestion: Run setup-local-env.sh or load .env.local before running proof specs.\",\n );\n }\n\n const client = createClient(url, publishableKey, {\n auth: {\n persistSession: false,\n autoRefreshToken: false,\n detectSessionInUrl: false,\n },\n // Gateway 502s are retried at the transport, so every probe written\n // against this client — including ad-hoc ones in specs — is covered.\n global: { fetch: createRetryingFetch(`request as ${opts.email}`) },\n });\n\n const { error } = await retryAuthRateLimit(\n `signInWithPassword as ${opts.email}`,\n () =>\n client.auth.signInWithPassword({\n email: opts.email,\n password: opts.password,\n }),\n (result) => ({\n limited: isAuthRateLimited(result.error, result.error?.status),\n }),\n );\n if (error) {\n const category = isAuthRateLimited(error, error.status)\n ? \"auth_rate_limited\"\n : \"auth_signin\";\n throw new Error(\n `[PROOF_FAIL] ${category}: expected successful sign-in for ${opts.email}, found signInWithPassword error: ${error.message}\\n` +\n \" file: src/playwright/actAsUser.ts\\n\" +\n ` suggestion: ${\n category === \"auth_rate_limited\"\n ? \"The auth budget is exhausted, not the credentials. Wait for the window to reset or reduce parallel proof workers.\"\n : \"Confirm the user was created by seed.user() and that password/email match.\"\n }`,\n );\n }\n\n return client;\n },\n};\n","// Import External Packages\n// Import Local Imports\n// Import Core Dependencies\n// Import Shared Dependencies\n// Import Extension Dependencies\n\n// ---------------------------------------------------------------------------\n// Transient-failure retry\n//\n// Local Supabase sits behind Kong. During a proof run — every spec seeding its\n// own fixtures through the service role, plus the dev server's own queries — the\n// gateway intermittently answers a request with a 502 whose body reads \"An\n// invalid response was received from the upstream server\", then serves the next\n// request normally. Roughly one local `proof:check` in two hit this, always on a\n// different spec.\n//\n// That noise is not evidence about the code under test, and it can hurt in two\n// ways:\n//\n// 1. A spurious red build. Worse than it sounds: a suite that fails half the\n// time for reasons nobody controls teaches people to re-run until green,\n// which is exactly the reflex that lets a real failure through.\n// 2. A false pass. Several probes read \"the actor's write returned an error and\n// no row landed\" as proof of denial. A write that never reached PostgREST\n// looks identical, so an unretried gateway error can certify an invariant\n// that was never exercised. Callers use `isTransient` to refuse that\n// reading.\n//\n// The retry lives at the transport (`createRetryingFetch`, installed on every\n// Supabase client the SDK hands out) rather than at each call site, because the\n// call sites that matter most are the ad-hoc queries inside `.proof.ts` specs —\n// there is no version of \"remember to wrap it\" that survives the next spec\n// somebody writes.\n//\n// It is bounded, announced on stdout, and deliberately narrow: only gateway\n// statuses (502/503/504) and transport-level failures qualify. A 4xx is the\n// database answering, and even a 500 is usually PostgREST or a route handler\n// answering; retrying either would turn a real failure into a slow real failure,\n// or worse, hide it.\n// ---------------------------------------------------------------------------\n\n/** The shape both `PostgrestError` and `AuthError` satisfy. */\nexport interface TransientErrorLike {\n message?: string | null;\n status?: number | null;\n code?: string | null;\n}\n\n/**\n * Messages that mean \"the request did not reach the database\", matched because\n * Supabase surfaces gateway and transport failures as error text without a\n * usable status on every client path.\n */\nconst TRANSIENT_MESSAGES = [\n /invalid response was received from the upstream server/i,\n /upstream connect error/i,\n /(service|server) (is )?temporarily unavailable/i,\n /bad gateway/i,\n /gateway time-?out/i,\n /fetch failed/i,\n /network( request)? failed/i,\n /socket hang up/i,\n /ECONNRESET|ECONNREFUSED|EPIPE|ETIMEDOUT|EAI_AGAIN|UND_ERR/,\n];\n\n/** Node wraps the real cause; the useful text is often one or two levels down. */\nfunction messageChain(error: unknown, depth = 0): string {\n if (!error || depth > 3) return \"\";\n if (typeof error === \"string\") return error;\n const record = error as { message?: unknown; cause?: unknown };\n const own = typeof record.message === \"string\" ? record.message : \"\";\n return `${own} ${messageChain(record.cause, depth + 1)}`;\n}\n\n/**\n * Statuses the gateway returns when it could not get an answer out of the\n * upstream service. A 500 is excluded on purpose: PostgREST and our own route\n * handlers use it to report real failures.\n */\nexport const GATEWAY_STATUSES = new Set([502, 503, 504]);\n\n/**\n * True when a failure is infrastructure, not an answer.\n *\n * `status` is the HTTP status when the caller has it separately from the error\n * (PostgREST responses carry it alongside `error`; auth errors carry it inside).\n */\nexport function isTransient(\n error: TransientErrorLike | null | undefined,\n status?: number | null,\n): boolean {\n if (!error) return false;\n const httpStatus = status ?? error.status ?? null;\n if (typeof httpStatus === \"number\") {\n if (GATEWAY_STATUSES.has(httpStatus)) return true;\n // Any other status is the stack answering — a denial, a validation error, an\n // internal error — and each is a fact the proof is entitled to act on.\n if (httpStatus >= 400) return false;\n }\n const text = messageChain(error);\n return TRANSIENT_MESSAGES.some((pattern) => pattern.test(text));\n}\n\nexport const TRANSIENT_MAX_ATTEMPTS = 4;\nconst BASE_DELAY_MS = 150;\n\nfunction backoffMs(attempt: number): number {\n return BASE_DELAY_MS * 2 ** (attempt - 1);\n}\n\nfunction announce(\n label: string,\n attempt: number,\n maxAttempts: number,\n reason: string,\n delayMs: number,\n): void {\n // Announced rather than silent: a suite that only goes green on its third\n // attempt every time is a real problem, and hidden retries hide it.\n console.log(\n `[proof:retry] ${label} failed transiently (attempt ${attempt}/${maxAttempts}): ` +\n `${reason} — retrying in ${delayMs}ms`,\n );\n}\n\nconst sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * A `fetch` that retries gateway failures, for installing on a Supabase client\n * via `createClient(url, key, { global: { fetch } })`.\n *\n * Covers every request that client makes — PostgREST queries, auth admin calls,\n * RPC — including the ad-hoc ones written inside proof specs, which is the whole\n * reason the retry lives here instead of at the call sites.\n *\n * Retrying a write is safe here only because gateway failures mean the request\n * did not reach PostgREST; a request carrying a stream body is never retried,\n * since it cannot be replayed and we cannot know how far it got.\n */\nexport function createRetryingFetch(\n label: string,\n baseFetch: typeof fetch = fetch,\n { maxAttempts = TRANSIENT_MAX_ATTEMPTS } = {},\n): typeof fetch {\n return async (input, init) => {\n const replayable =\n init?.body == null ||\n typeof init.body === \"string\" ||\n init.body instanceof URLSearchParams;\n\n for (let attempt = 1; ; attempt++) {\n const lastAttempt = attempt >= maxAttempts || !replayable;\n try {\n const response = await baseFetch(input, init);\n if (lastAttempt || !GATEWAY_STATUSES.has(response.status)) {\n return response;\n }\n const delayMs = backoffMs(attempt);\n announce(\n label,\n attempt,\n maxAttempts,\n `HTTP ${response.status}`,\n delayMs,\n );\n await sleep(delayMs);\n } catch (error) {\n if (lastAttempt || !isTransient(error as TransientErrorLike))\n throw error;\n const delayMs = backoffMs(attempt);\n announce(\n label,\n attempt,\n maxAttempts,\n messageChain(error).trim() || \"transport failure\",\n delayMs,\n );\n await sleep(delayMs);\n }\n }\n };\n}\n\n/**\n * Retry a call that reports failure in its result rather than by throwing.\n *\n * For anything going through a Supabase client, prefer `createRetryingFetch` —\n * it is already installed. This is for the surfaces that do not use it, such as\n * Playwright's `APIRequestContext`.\n *\n * `run` must build the request from scratch on each call: a PostgREST builder is\n * a one-shot thenable, so re-awaiting the same object does not re-issue it.\n *\n * The final result is returned as-is — including a still-failing error — because\n * the caller owns the diagnostics. Nothing here throws, and nothing here decides\n * what a failure means.\n */\nexport async function retryTransient<\n T extends { error?: TransientErrorLike | null; status?: number },\n>(\n label: string,\n // `PromiseLike`, not `Promise`: a PostgREST builder is a thenable, and typing\n // this as `Promise<T>` makes inference fall back to the constraint, which\n // erases `data` and `count` from the caller's result.\n run: () => PromiseLike<T>,\n { maxAttempts = TRANSIENT_MAX_ATTEMPTS } = {},\n): Promise<T> {\n let result = await run();\n\n for (let attempt = 1; attempt < maxAttempts; attempt++) {\n if (!isTransient(result.error, result.status)) return result;\n const delayMs = backoffMs(attempt);\n announce(\n label,\n attempt,\n maxAttempts,\n result.error?.message ?? \"unknown error\",\n delayMs,\n );\n await sleep(delayMs);\n result = await run();\n }\n\n return result;\n}\n","// ---------------------------------------------------------------------------\n// Bounded authentication rate-limit retry.\n//\n// HTTP 429 is neither a product verdict nor a transient gateway failure. Proof\n// login may retry it briefly, honoring Retry-After within a strict budget; once\n// exhausted it remains an explicit auth_rate_limited infrastructure diagnosis.\n// ---------------------------------------------------------------------------\n\nexport interface AuthRateLimitErrorLike {\n message?: string | null;\n status?: number | null;\n}\n\nexport const AUTH_RATE_LIMIT_MAX_ATTEMPTS = 3;\nexport const AUTH_RATE_LIMIT_MAX_DELAY_MS = 5_000;\nexport const AUTH_RATE_LIMIT_MAX_TOTAL_WAIT_MS = 10_000;\nconst AUTH_RATE_LIMIT_FALLBACK_DELAY_MS = 1_000;\n\nexport function isAuthRateLimited(\n error: AuthRateLimitErrorLike | null | undefined,\n status?: number | null,\n body?: string | null,\n): boolean {\n const httpStatus = status ?? error?.status ?? null;\n if (httpStatus === 429) return true;\n if (typeof httpStatus === \"number\" && httpStatus >= 400) return false;\n\n const text = `${error?.message ?? \"\"} ${body ?? \"\"}`;\n return (\n /\\[PROOF_FAIL\\]\\s*(auth_)?rate_limited:/i.test(text) ||\n /\\b(rate limit|too many (requests|attempts|sign-?ins?))\\b/i.test(text)\n );\n}\n\nexport function parseRetryAfterMs(\n value: string | number | null | undefined,\n now = Date.now(),\n): number | null {\n if (typeof value === \"number\" && Number.isFinite(value)) {\n return Math.max(0, value * 1_000);\n }\n if (typeof value !== \"string\" || !value.trim()) return null;\n\n const seconds = Number(value);\n if (Number.isFinite(seconds)) return Math.max(0, seconds * 1_000);\n\n const at = Date.parse(value);\n return Number.isFinite(at) ? Math.max(0, at - now) : null;\n}\n\nexport function clampAuthRateLimitDelay(\n retryAfterMs: number | null,\n remainingBudgetMs = AUTH_RATE_LIMIT_MAX_TOTAL_WAIT_MS,\n): number {\n const requested = retryAfterMs ?? AUTH_RATE_LIMIT_FALLBACK_DELAY_MS;\n return Math.max(\n 0,\n Math.min(requested, AUTH_RATE_LIMIT_MAX_DELAY_MS, remainingBudgetMs),\n );\n}\n\nexport async function retryAuthRateLimit<T>(\n label: string,\n run: () => PromiseLike<T>,\n classify: (result: T) => {\n limited: boolean;\n retryAfter?: string | number | null;\n },\n {\n maxAttempts = AUTH_RATE_LIMIT_MAX_ATTEMPTS,\n maxTotalWaitMs = AUTH_RATE_LIMIT_MAX_TOTAL_WAIT_MS,\n sleep = (ms: number) =>\n new Promise<void>((resolve) => setTimeout(resolve, ms)),\n } = {},\n): Promise<T> {\n let result = await run();\n let waitedMs = 0;\n\n for (let attempt = 1; attempt < maxAttempts; attempt += 1) {\n const signal = classify(result);\n if (!signal.limited) return result;\n\n const remaining = maxTotalWaitMs - waitedMs;\n if (remaining <= 0) return result;\n const delayMs = clampAuthRateLimitDelay(\n parseRetryAfterMs(signal.retryAfter),\n remaining,\n );\n if (delayMs <= 0) return result;\n\n console.log(\n `[proof:retry] ${label} auth rate limited (attempt ${attempt}/${maxAttempts}) — retrying in ${delayMs}ms`,\n );\n await sleep(delayMs);\n waitedMs += delayMs;\n result = await run();\n }\n\n return result;\n}\n","// Import External Packages\nimport * as crypto from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { test } from \"@playwright/test\";\n// Import Local Imports\nimport { codeProvenance } from \"../server/code-provenance\";\nimport {\n TRACE_ARTIFACT_SCHEMA_VERSION,\n normalizeAssertionStatus,\n type ProofOptions,\n type StepOptions,\n type StepResult,\n type TraceArtifact,\n type TraceAssertion,\n type TraceStep,\n} from \"../shared/trace-types\";\n// Import Core Dependencies\n// Import Shared Dependencies\n// Import Extension Dependencies\n\n// ---------------------------------------------------------------------------\n// trace.proof / t.step\n//\n// A proof is a single run that answers \"did we observe the intended\n// behavior?\" — one happy_path, one tenant_isolation check, one authorization\n// probe, etc. Each step inside the proof records an intent, a kind (from the\n// closed vocabulary), and what the agent actually observed. The resulting\n// JSON artifact is written to `.proof/traces/<proofId>.json` and — in v1 —\n// consumed by the mission-manifest validator, which groups proofs by their\n// optional `missionId` and confirms every `trace_must_prove` requirement has\n// a matching passing step across the group.\n//\n// Write path in v0.5 is hardcoded to `<cwd>/.proof/traces/`. Playwright runs\n// from the repo root so this resolves to `<repo>/.proof/traces/`.\n// ---------------------------------------------------------------------------\n\nconst TRACE_DIR_RELATIVE = [\".proof\", \"traces\"] as const;\n\n/**\n * AsyncLocalStorage used to pipe `recordAssertion(...)` calls from inside\n * helper code (e.g. assert.tenantIsolation) to the step that wraps them,\n * without requiring every helper to take a recorder argument.\n */\nconst currentStepAssertions = new AsyncLocalStorage<TraceAssertion[]>();\n\n/**\n * AsyncLocalStorage naming the `assert.*` helper currently executing. Set only\n * by `withAssertionProvenance`, which is deliberately NOT re-exported from the\n * package entry point (`./index`): the public `recordAssertion` strips any\n * caller-supplied `emittedBy`, so the only way an assertion carries helper\n * provenance is by actually being recorded while a helper runs. That is what\n * lets the mission validator treat `emittedBy` as evidence of origin rather\n * than as a string a spec happened to write.\n */\nconst currentHelperProvenance = new AsyncLocalStorage<string>();\n\n/**\n * Run `fn` with helper provenance attached: every assertion recorded inside it\n * is stamped `emittedBy: helper`. Internal to the SDK — imported directly by\n * `./assert`, never exported from `./index`. Note that provenance spans the\n * whole helper call, including any user-supplied callbacks the helper invokes.\n */\nexport async function withAssertionProvenance<T>(\n helper: string,\n fn: () => Promise<T>,\n): Promise<T> {\n return currentHelperProvenance.run(helper, fn);\n}\n\n/**\n * Run `fn` with helper provenance suspended. Helpers wrap every user-supplied\n * callback (`fixture.create`, `setup`, ...) in this, because those callbacks\n * are executor-authored code executing inside the helper's context — without\n * the suspension, a hostile fixture could call `recordAssertion` and receive\n * the helper's trusted stamp. Internal to the SDK, like its counterpart.\n */\nexport async function withoutAssertionProvenance<T>(\n fn: () => Promise<T>,\n): Promise<T> {\n return currentHelperProvenance.exit(fn);\n}\n\n/**\n * Called by `assert.*` helpers while running inside a step. If no step is\n * active (e.g. someone invokes a helper outside `trace.proof`), this is a\n * no-op so the helper still runs without crashing.\n *\n * Spec code may call this directly to record bespoke assertions; those are\n * recorded WITHOUT `emittedBy` (any supplied value is stripped), which marks\n * them as spec-authored claims rather than SDK-verified probes.\n */\nexport function recordAssertion(assertion: TraceAssertion): void {\n const bucket = currentStepAssertions.getStore();\n if (!bucket) return;\n // Provenance comes from the execution context, never from the caller: a\n // spec claiming `emittedBy: \"assert.tenantIsolation\"` on a hand-rolled\n // assertion must not be able to impersonate the helper.\n const { emittedBy: _claimed, ...rest } = assertion;\n const emittedBy = currentHelperProvenance.getStore();\n // Materialize `status` at write time rather than leaving consumers to derive\n // it: the artifact should say what it means without a decoder ring.\n bucket.push({\n ...rest,\n ...(emittedBy ? { emittedBy } : {}),\n status: normalizeAssertionStatus(assertion.status, assertion.passed),\n });\n}\n\nclass TraceRecorderImpl {\n private readonly steps: TraceStep[] = [];\n\n constructor(private readonly proofId: string) {}\n\n getSteps(): readonly TraceStep[] {\n return this.steps;\n }\n\n async step<T extends StepResult | void>(\n opts: StepOptions,\n fn: () => Promise<T>,\n ): Promise<T> {\n const startedAt = performance.now();\n const assertions: TraceAssertion[] = [];\n\n try {\n const result = await currentStepAssertions.run(\n assertions,\n async () => (await fn()) as T,\n );\n const durationMs = performance.now() - startedAt;\n\n const observation =\n result && typeof result === \"object\" && \"observation\" in result\n ? String((result as StepResult).observation ?? \"\")\n : \"\";\n\n this.steps.push({\n intent: opts.intent,\n kind: opts.kind,\n target: opts.target,\n actor: opts.actor,\n workspaceId: opts.workspaceId,\n observation,\n passed: true,\n durationMs: Math.round(durationMs),\n ...(assertions.length > 0 ? { assertions: [...assertions] } : {}),\n });\n\n return result;\n } catch (error) {\n const durationMs = performance.now() - startedAt;\n\n const message = error instanceof Error ? error.message : String(error);\n\n this.steps.push({\n intent: opts.intent,\n kind: opts.kind,\n target: opts.target,\n actor: opts.actor,\n workspaceId: opts.workspaceId,\n observation: \"\",\n passed: false,\n durationMs: Math.round(durationMs),\n error: message,\n ...(assertions.length > 0 ? { assertions: [...assertions] } : {}),\n });\n\n throw error;\n }\n }\n}\n\nconst specHashCache = new Map<string, string>();\n\n/**\n * Identify the spec file that is currently executing, plus a short content\n * hash, so a trace can be tied back to the exact code that produced it.\n *\n * Returns an empty object outside a Playwright run (or if the file can't be\n * read): provenance is evidence, and absent evidence must never be the reason\n * a proof fails to record.\n */\nfunction specProvenance(): { specFile?: string; specHash?: string } {\n let absolutePath: string;\n try {\n absolutePath = test.info().file;\n } catch {\n return {};\n }\n if (!absolutePath) return {};\n\n const specFile = path.relative(process.cwd(), absolutePath);\n const cached = specHashCache.get(absolutePath);\n if (cached) return { specFile, specHash: cached };\n\n try {\n const contents = fs.readFileSync(absolutePath);\n const specHash = crypto\n .createHash(\"sha256\")\n .update(contents)\n .digest(\"hex\")\n .slice(0, 12);\n specHashCache.set(absolutePath, specHash);\n return { specFile, specHash };\n } catch {\n return { specFile };\n }\n}\n\nfunction writeArtifact(proofId: string, artifact: TraceArtifact): void {\n const traceDir = path.join(process.cwd(), ...TRACE_DIR_RELATIVE);\n fs.mkdirSync(traceDir, { recursive: true });\n const target = path.join(traceDir, `${proofId}.json`);\n fs.writeFileSync(target, JSON.stringify(artifact, null, 2), \"utf8\");\n}\n\nfunction normalizeOptions(arg: string | ProofOptions): ProofOptions {\n return typeof arg === \"string\" ? { proofId: arg } : arg;\n}\n\nfunction mutationProvenance(): TraceArtifact[\"mutation\"] | undefined {\n const id = process.env.PROOF_MUTATION_ID?.trim();\n return id ? { id, planted: true } : undefined;\n}\n\nexport const trace = {\n /**\n * Start a proof. The callback receives a recorder whose `.step()` method\n * wraps individual observations. When the callback resolves (or throws),\n * a JSON artifact is written to `.proof/traces/<proofId>.json`.\n *\n * The proof rethrows any thrown error so the Playwright test still fails —\n * the trace is written first so you can inspect the failure.\n *\n * @example\n * // Common v0.5 case: no mission attached\n * await trace.proof(\"auth-login-admin\", async (t) => { ... });\n *\n * @example\n * // v1 case: proof belongs to an external mission\n * await trace.proof(\n * { proofId: \"auth-login-admin\", missionId: \"M-042\" },\n * async (t) => { ... },\n * );\n */\n async proof(\n arg: string | ProofOptions,\n fn: (t: TraceRecorderImpl) => Promise<void>,\n ): Promise<void> {\n const { proofId, missionId } = normalizeOptions(arg);\n const startedAt = performance.now();\n const timestamp = new Date().toISOString();\n const recorder = new TraceRecorderImpl(proofId);\n\n let proofError: unknown;\n try {\n await fn(recorder);\n } catch (error) {\n proofError = error;\n }\n\n const durationMs = Math.round(performance.now() - startedAt);\n const steps = recorder.getSteps();\n const passed = !proofError && steps.every((s) => s.passed);\n const mutation = mutationProvenance();\n\n const artifact: TraceArtifact = {\n schemaVersion: TRACE_ARTIFACT_SCHEMA_VERSION,\n proofId,\n ...(missionId ? { missionId } : {}),\n ...specProvenance(),\n ...codeProvenance(),\n ...(mutation ? { mutation } : {}),\n timestamp,\n durationMs,\n passed,\n steps: [...steps],\n };\n\n writeArtifact(proofId, artifact);\n\n if (proofError) throw proofError;\n },\n};\n","// Import External Packages\nimport { spawnSync } from \"node:child_process\";\n// Import Local Imports\n// Import Core Dependencies\n// Import Shared Dependencies\n// Import Extension Dependencies\n\n// ---------------------------------------------------------------------------\n// Which code did this proof observe?\n//\n// A trace says \"the outsider could not read the row\". On its own that is a claim\n// about nothing in particular: without the commit, a reader cannot tell whether\n// it describes the PR under review, the branch point, or a working tree that has\n// since been edited. `specHash` pins the spec that made the claim; this pins the\n// application it was made against.\n//\n// Two facts, both optional:\n//\n// commit — the SHA that was checked out while the proof ran.\n// dirty — whether the working tree had uncommitted changes at that moment. A\n// green proof from a dirty tree describes no reviewable commit, so a\n// consumer that gates on evidence needs to be able to refuse it. It\n// is not the same information as `commit`, and it is the field people\n// forget to ask for.\n//\n// Absent rather than fabricated when unknown: shallow clones, exported tarballs\n// and non-git checkouts all have to keep producing usable artifacts, so a missing\n// SHA weakens the evidence instead of failing the run.\n// ---------------------------------------------------------------------------\n\nexport interface CodeProvenance {\n commit?: string;\n dirty?: boolean;\n}\n\n/** Environment variables `proof-harness verify` sets for the spec run. */\nexport const PROOF_COMMIT_ENV = \"PROOF_COMMIT\";\nexport const PROOF_DIRTY_ENV = \"PROOF_DIRTY\";\n\ntype GitReader = (args: string[]) => string | undefined;\n\nconst readGit: GitReader = (args) => {\n try {\n const result = spawnSync(\"git\", args, { encoding: \"utf8\" });\n if (result.status !== 0) return undefined;\n return result.stdout;\n } catch {\n return undefined;\n }\n};\n\n/**\n * Resolve provenance, preferring what the runner already worked out.\n *\n * The runner path matters for more than speed. `proof_verify.mjs` reads git once\n * for the whole run, so every trace in one run agrees; letting each spec ask git\n * for itself would let `dirty` flip mid-run as files are written, and two traces\n * from the same run could then disagree about the code they observed.\n *\n * The git fallback exists because specs are also run directly —\n * `pnpm exec playwright test e2e/proofs/foo.proof.ts` — and a trace written that\n * way should not silently lose its provenance.\n */\nexport function resolveCodeProvenance(\n // Deliberately not NodeJS.ProcessEnv: the app augments that type with required\n // keys, and this reads two optional variables that have nothing to do with the\n // application's own configuration.\n env: Record<string, string | undefined> = process.env,\n git: GitReader = readGit,\n): CodeProvenance {\n const fromRunner = env[PROOF_COMMIT_ENV]?.trim();\n if (fromRunner) {\n const dirty = env[PROOF_DIRTY_ENV]?.trim();\n return {\n commit: fromRunner,\n // Only \"true\"/\"false\" carry meaning; anything else means the runner could\n // not tell, which is different from \"clean\".\n ...(dirty === \"true\" || dirty === \"false\"\n ? { dirty: dirty === \"true\" }\n : {}),\n };\n }\n\n const commit = git([\"rev-parse\", \"HEAD\"])?.trim();\n if (!commit) return {};\n\n const status = git([\"status\", \"--porcelain\"]);\n return {\n commit,\n ...(status === undefined ? {} : { dirty: status.trim().length > 0 }),\n };\n}\n\nlet cached: CodeProvenance | undefined;\n\n/**\n * Cached `resolveCodeProvenance` for the writer path.\n *\n * Playwright workers are separate processes, so this is one git call per worker\n * at worst — and none at all when the runner supplied the values.\n */\nexport function codeProvenance(): CodeProvenance {\n cached ??= resolveCodeProvenance();\n return cached;\n}\n","// Import External Packages\n// Import Local Imports\nimport type { ProofKind } from \"./vocabulary\";\n// Import Core Dependencies\n// Import Shared Dependencies\n// Import Extension Dependencies\n\n// ---------------------------------------------------------------------------\n// Trace artifact types\n//\n// These are the durable shape written to `.proof/traces/<proofId>.json`.\n//\n// Two separate identifiers on purpose:\n// - `proofId` — per-file id; what _this_ proof verifies (e.g. \"auth-login-admin\",\n// \"workspace-invite-tenant-isolation\"). The filename is keyed on it,\n// so proofs never collide.\n// - `missionId` — optional external ticket reference (e.g. \"M-042\"). A single mission\n// typically has multiple proofs (happy_path + tenant_isolation +\n// authorization), each with its own file. The v1 mission-manifest\n// validator groups by `missionId` and checks that every\n// `trace_must_prove` entry has at least one matching passing step\n// across the group.\n// ---------------------------------------------------------------------------\n\n/**\n * The evidential role an assertion plays.\n *\n * - `primary` — the claim the proof exists to make. For an authorization or\n * tenant-isolation proof that is a refusal (\"the outsider could\n * NOT read the row\"); for a happy_path proof it is a success\n * (\"the user reached their workspace\"). This is what a mission's\n * `trace_must_prove` entry matches by default.\n * - `control` — a companion assertion proving the primary probe was capable of\n * failing at all: \"the operation that SHOULD be permitted still\n * succeeded\", or \"the row the primary probe looked for genuinely\n * exists and is readable by someone.\"\n *\n * The distinction is load-bearing, not cosmetic. A refusal probe alone cannot\n * tell \"RLS correctly hid this row\" apart from \"there was no row / the grant\n * denies everyone / the table name was a typo\" — every one of those yields zero\n * rows and would record a passing assertion. The control is what makes the zero\n * mean something.\n *\n * Because the validator matches `trace_must_prove` on `{ kind, target,\n * passed: true }`, controls MUST be labelled: otherwise a control (which by\n * design passes) could satisfy a requirement all on its own and the primary\n * probe would never be checked at all.\n *\n * Naming note: an earlier draft called these `negative` / `positive_control`.\n * That reads backwards for `happy_path`, whose primary claim is a success, so\n * the roles are named by evidential function instead. Both older names are\n * accepted as deprecated aliases wherever a role is parsed.\n */\nexport type AssertionRole = \"primary\" | \"control\";\n\n/**\n * Concrete operation exercised by an assertion. Mutation inventory keys claims\n * by kind + target + operation so a SELECT mutation cannot silently stand in\n * for an untested UPDATE/DELETE or action invocation.\n */\nexport type ProofOperation =\n | \"select\"\n | \"insert\"\n | \"update\"\n | \"delete\"\n | \"invoke\"\n | \"request\";\n\n/** Deprecated role spellings accepted on input and normalized to AssertionRole. */\nexport const ASSERTION_ROLE_ALIASES: Readonly<Record<string, AssertionRole>> = {\n negative: \"primary\",\n positive_control: \"control\",\n};\n\n/**\n * Normalize a role from an artifact or manifest. `undefined` means `primary`:\n * traces written before schemaVersion 2 carry no role, and their assertions\n * were all primary claims.\n */\nexport function normalizeAssertionRole(role: unknown): AssertionRole | null {\n if (role === undefined || role === null) return \"primary\";\n if (role === \"primary\" || role === \"control\") return role;\n if (typeof role === \"string\" && role in ASSERTION_ROLE_ALIASES) {\n return ASSERTION_ROLE_ALIASES[role];\n }\n return null;\n}\n\n/**\n * What an assertion concluded.\n *\n * `passed` / `failed` are the two verdicts a boolean can carry. The other two\n * exist because \"we did not find out\" is a real outcome that a boolean has to\n * round to one side or the other, and rounding it to `false` is indistinguishable\n * from a genuine failure while rounding it to `true` is how a suite quietly stops\n * proving anything:\n *\n * - `incomplete` — the probe ran but could not reach a verdict (a precondition\n * was missing, a dependency was unavailable).\n * - `skipped` — the probe was deliberately not run, e.g. a tenant-isolation\n * direction with no fixture rows on that side.\n *\n * Neither counts as evidence: both carry `passed: false`, so neither can satisfy\n * a mission requirement. The status is what lets a consumer tell them apart from\n * a real failure, and stops a skip from disappearing into \"one fewer assertion\".\n */\nexport type AssertionStatus = \"passed\" | \"failed\" | \"incomplete\" | \"skipped\";\n\n/** Derive the status of an assertion that predates the field. */\nexport function normalizeAssertionStatus(\n status: unknown,\n passed: boolean,\n): AssertionStatus {\n if (\n status === \"passed\" ||\n status === \"failed\" ||\n status === \"incomplete\" ||\n status === \"skipped\"\n ) {\n return status;\n }\n return passed ? \"passed\" : \"failed\";\n}\n\n/**\n * A single structured assertion recorded during a step. Produced by the\n * `assert.*` helpers via `recordAssertion(...)`. Hand-written `expect(...)`\n * calls inside a step do NOT appear here — only helper-driven assertions do,\n * because the v1 mission-manifest validator matches on\n * `{ kind, target, passed: true, role }` and needs a closed, machine-readable\n * surface.\n */\nexport interface TraceAssertion {\n /** Category from the closed `PROOF_KINDS` vocabulary. */\n kind: ProofKind;\n /** The thing asserted against: table, route, action, etc. */\n target: string;\n /** Concrete operation the probe exercised. */\n operation?: ProofOperation;\n /**\n * Whether this specific assertion held. Remains the authoritative field for\n * the validator and for consumers: only `true` is evidence.\n */\n passed: boolean;\n /**\n * Why `passed` has the value it has. Omitted is derived from `passed`, so\n * traces written before this field stay readable.\n */\n status?: AssertionStatus;\n /**\n * Evidential role. Omitted is treated as `\"primary\"` by the validator for\n * backward compatibility with traces written before schemaVersion 2.\n */\n role?: AssertionRole;\n /** Optional human-readable note for failure triage. */\n detail?: string;\n /**\n * Which SDK helper produced this assertion, e.g. `\"assert.tenantIsolation\"`.\n *\n * Stamped by the SDK itself through an internal channel that the public\n * `recordAssertion(...)` entry point cannot reach: a value supplied by spec\n * code is stripped before the assertion is recorded. Absent means the\n * assertion was recorded directly by a spec via `recordAssertion(...)`.\n *\n * The distinction is evidential. A helper-emitted assertion went through the\n * helper's vacuity controls, ground-truth re-reads, and role bookkeeping; a\n * spec-recorded assertion asserts whatever the spec chose to write into it.\n * The mission validator therefore requires helper provenance for\n * `tenant_isolation` requirements (a hand-rolled isolation probe has none of\n * `assert.tenantIsolation`'s protections), and a mission can demand it for\n * any requirement via `trace_must_prove[].evidence: \"helper\"`.\n *\n * For `tenant_isolation` the stamp is also identity-checked, not merely\n * present-checked — see TENANT_ISOLATION_TABLE_HELPERS. `assert.httpResponse`\n * records whatever `kind` its caller passes, so \"some helper emitted this\"\n * is not the same claim as \"the isolation probe ran\".\n */\n emittedBy?: string;\n}\n\n/**\n * The only helpers whose stamp counts as tenant-isolation evidence, split by\n * target (tables are stricter than action refs). The single runtime source is\n * [`portable-vocabulary.mjs`](./portable-vocabulary.mjs), which the\n * dependency-free Node scripts (`proof_verify.mjs`, `proof_coverage.mjs`)\n * import directly; see there for the identity rationale.\n */\nexport {\n TENANT_ISOLATION_ACTION_HELPERS,\n TENANT_ISOLATION_TABLE_HELPERS,\n} from \"./portable-vocabulary.mjs\";\n\n/**\n * A single step inside a proof.\n * Produced by `t.step({ intent, kind, target }, async () => { ... })`.\n */\nexport interface TraceStep {\n /** Human-readable description of what the step attempted. */\n intent: string;\n /** Category of the step, from closed vocabulary. */\n kind: ProofKind;\n /** The thing the step targets: action name, table, route, etc. */\n target: string;\n /** What the step observed (returned by the step callback). */\n observation: string;\n /** Whether the step's assertions held. */\n passed: boolean;\n /** Optional: who the step acted as (email, role, seed identifier). */\n actor?: string;\n /** Optional: which workspace the step operated in. */\n workspaceId?: string;\n /** Wall-clock duration of the step callback. */\n durationMs: number;\n /** On failure, the error message captured. */\n error?: string;\n /**\n * Structured assertions recorded by `assert.*` helpers during this step.\n * Empty or omitted for steps that only use `expect(...)` directly.\n * The v1 mission-manifest validator matches `trace_must_prove` entries\n * against this array.\n */\n assertions?: TraceAssertion[];\n}\n\n/**\n * Current per-proof trace artifact schema version.\n *\n * 2 — adds `role` and `status` to assertions and the `schemaVersion` /\n * `specFile` / `specHash` / `commit` / `dirty` provenance fields below.\n * Consumers written against v1 keep working: every v1 field is still present\n * with the same meaning, and both new assertion fields are derivable when\n * absent.\n *\n * `commit`, `dirty`, and `mutation` arrived after v2 shipped and did not bump\n * it: they are optional additions, and the contract's rule is that additive\n * fields do not move a version. A consumer that needs them must treat absence\n * as \"unknown\" anyway, since normal runs have no mutation and a non-git\n * checkout cannot supply code provenance.\n */\nexport const TRACE_ARTIFACT_SCHEMA_VERSION = 2;\n\n/**\n * Provenance attached only when the mutation harness deliberately plants a\n * defect. A red mutation trace means the check detected the named defect; it\n * must never be mistaken for a real application regression.\n */\nexport interface TraceMutation {\n /** Stable mutation identifier from the repository-owned mutation catalog. */\n id: string;\n /** Always true; makes the intentional nature explicit to copied consumers. */\n planted: true;\n}\n\n/**\n * A trace artifact — one proof's worth of evidence.\n * Written atomically to `.proof/traces/<proofId>.json` when the proof\n * completes (pass or fail).\n */\nexport interface TraceArtifact {\n /**\n * Pinned to TRACE_ARTIFACT_SCHEMA_VERSION. Absent on traces written by\n * SDK versions before provenance was added.\n */\n schemaVersion?: number;\n /**\n * Repo-relative path of the spec file that produced this trace, e.g.\n * `e2e/proofs/workspace-invite.proof.ts`. Lets a reviewer go from a claim\n * straight to the code that made it.\n */\n specFile?: string;\n /**\n * First 12 hex chars of the SHA-256 of the spec file's contents. Two runs\n * claiming the same proof with different hashes were produced by different\n * code — which is the whole question when a previously-red proof turns\n * green.\n */\n specHash?: string;\n /**\n * Full SHA of the commit that was checked out while the proof ran. `specHash`\n * pins the spec that made the claim; this pins the application it was made\n * against, which is what lets an external ledger say \"red at X, green at Y\".\n *\n * Absent outside a git checkout. On a CI `pull_request` run this is the\n * ephemeral merge commit the checkout created — accurate about what was\n * tested, but not a commit you can return to later.\n */\n commit?: string;\n /**\n * Whether the working tree had uncommitted changes when the proof ran. A pass\n * from a dirty tree is evidence about code that exists nowhere but one laptop,\n * so a consumer gating on traces should be able to refuse it. Absent when it\n * could not be determined, which is not the same as `false`.\n */\n dirty?: boolean;\n /**\n * Present only for intentional red runs produced by `proof:mutate`.\n * Stored inside the trace so provenance survives artifact renames/copies.\n */\n mutation?: TraceMutation;\n /**\n * Per-file identifier for this proof. Used as the filename stem and as the\n * join key when the v1 mission-manifest validator looks for a matching\n * passing step for a `trace_must_prove` entry.\n */\n proofId: string;\n /**\n * Optional reference to the external mission (ticket) this proof belongs\n * to. Multiple proofs share one `missionId`. Absent in v0.5 local runs\n * that are not tied to a mission.\n */\n missionId?: string;\n /** ISO 8601 timestamp of proof start. */\n timestamp: string;\n /** Total wall-clock duration across all steps. */\n durationMs: number;\n /** True only if every step passed. */\n passed: boolean;\n /** Ordered list of steps as they were executed. */\n steps: TraceStep[];\n}\n\n/**\n * Options accepted by `trace.proof(...)`. A bare string is treated as\n * `{ proofId: string }` — the common v0.5 case where no mission is attached.\n */\nexport interface ProofOptions {\n proofId: string;\n missionId?: string;\n}\n\n/**\n * The handle passed to a proof callback. Provides the step recorder.\n */\nexport interface TraceRecorder {\n step<T extends StepResult | void>(\n opts: StepOptions,\n fn: () => Promise<T>,\n ): Promise<T>;\n}\n\nexport interface StepOptions {\n intent: string;\n kind: ProofKind;\n target: string;\n actor?: string;\n workspaceId?: string;\n}\n\n/**\n * Optional payload returned by a step callback. If the callback returns\n * `void` the step is still recorded with `observation: \"\"`.\n */\nexport interface StepResult {\n observation?: string;\n}\n","// Import External Packages\nimport { createClient } from \"@supabase/supabase-js\";\n// Import Local Imports\nimport { createRetryingFetch } from \"./transient\";\n// Import Core Dependencies\n// Import Shared Dependencies\n// Import Extension Dependencies\n\n// ---------------------------------------------------------------------------\n// Thin re-export of the service-role Supabase client, scoped to Proof.\n//\n// Proof helpers use the service-role client for two things:\n// 1. Seed fixture setup (`seed.workspace`, `seed.user`)\n// 2. Teardown of test data after assertions complete\n//\n// Consumers inside `.proof.ts` specs receive the result of this factory\n// through the `setup({ sb })` callback of `assert.tenantIsolation`. They\n// should NEVER construct a service-role client themselves — keeping the\n// construction centralized makes it easy to add logging, usage limits, or\n// a future mock in one place.\n//\n// This package does not depend on an application's generated database types or\n// service-client wrapper. The factory reads the standard environment variables\n// directly and gives proof-specific diagnostics.\n// ---------------------------------------------------------------------------\n\nfunction requireProofEnv(name: string): string {\n const value = process.env[name];\n if (!value) {\n throw new Error(\n `[PROOF_FAIL] missing_env: expected ${name}, found empty\\n` +\n \" file: src/server/service-client.ts\\n\" +\n \" suggestion: Ensure CI exports the Supabase env vars before running pnpm proof:verify.\",\n );\n }\n return value;\n}\n\n/**\n * Factory for a service-role Supabase client used inside Proof helpers and\n * `.proof.ts` spec files.\n *\n * Returns a service-role Supabase client so spec code can safely type the `sb`\n * parameter as the library's `SupabaseClient`.\n */\nexport function createProofServiceClient() {\n return createClient(\n requireProofEnv(\"NEXT_PUBLIC_SUPABASE_URL\"),\n requireProofEnv(\"SUPABASE_SECRET_KEY\"),\n {\n auth: {\n persistSession: false,\n autoRefreshToken: false,\n detectSessionInUrl: false,\n },\n // Local Supabase sits behind Kong, which intermittently 502s under the\n // load of a proof run. Installing the retry here covers every query made\n // through this client, including ad-hoc ones inside proof specs.\n global: { fetch: createRetryingFetch(\"service-role request\") },\n },\n );\n}\n","// Import External Packages\n// Import Local Imports\nimport { createProofServiceClient } from \"./service-client\";\nimport { isTransient, TRANSIENT_MAX_ATTEMPTS } from \"./transient\";\n// Import Core Dependencies\n// Import Shared Dependencies\n// Import Extension Dependencies\n\n// ---------------------------------------------------------------------------\n// seed.workspace / seed.user\n//\n// Fixture helpers used by `.proof.ts` specs and by `assert.tenantIsolation`\n// to set up isolated test data. They bypass the business-logic pipeline on\n// purpose: creating fixtures should not exercise the very actions we are\n// trying to verify. Instead we insert directly via the service-role client.\n//\n// Both helpers are idempotent on upsert conflicts is NOT implemented; callers\n// are responsible for passing unique names/emails (`assert.tenantIsolation`\n// uses a short random suffix).\n// ---------------------------------------------------------------------------\n\nexport interface SeedWorkspace {\n id: string;\n name: string;\n ownerId: string | null;\n}\n\nexport interface SeedUser {\n id: string;\n email: string;\n password: string;\n workspaceId: string;\n role: \"owner\" | \"admin\" | \"member\" | \"viewer\";\n /**\n * `workspace_members.id` for this user's membership row in `workspaceId`.\n * Needed by proofs that invoke actions keyed on the membership (e.g.\n * `removeMember` takes `memberId = workspace_members.id`, not user_id).\n */\n membershipId: string;\n}\n\nfunction failProof(\n category: string,\n expected: string,\n found: string,\n suggestion = `Check that Supabase is running (pnpm exec supabase status) and that SUPABASE_SECRET_KEY is set in .env.local.`,\n): never {\n throw new Error(\n `[PROOF_FAIL] ${category}: expected ${expected}, found ${found}\\n` +\n ` file: src/server/seed.ts\\n` +\n ` suggestion: ${suggestion}`,\n );\n}\n\n/**\n * Fixture setup that failed because the gateway never answered is reported as\n * infrastructure. Otherwise the operator reads \"expected a workspace row to be\n * created\" and goes looking for a broken migration.\n */\nfunction failSeed(\n category: string,\n expected: string,\n found: string,\n error: { message?: string | null; status?: number | null } | null | undefined,\n status?: number,\n): never {\n if (isTransient(error, status)) {\n failProof(\n `${category}_transient`,\n expected,\n `${found} — still failing after ${TRANSIENT_MAX_ATTEMPTS} attempts`,\n `This is a gateway/transport failure, not a schema problem: the request never reached Postgres. ` +\n `Check that the local Supabase stack is healthy (pnpm exec supabase status) and is not restarting mid-run.`,\n );\n }\n failProof(category, expected, found);\n}\n\n/**\n * Wait until `public.users` has a row for the given `userId`.\n *\n * `supabase.auth.admin.createUser` creates a row in `auth.users`, which fires\n * the `handle_new_user` trigger that inserts into `public.users`. Postgres\n * runs the trigger inside the same transaction as the INSERT, so the row\n * exists the moment the admin API's HTTP call returns — in theory.\n *\n * In practice the admin API occasionally returns before the post-commit\n * visibility window has settled, and the very next `workspace_members`\n * INSERT fails its FK to `public.users(id)`. This poll closes that window\n * deterministically: short, bounded, ~500ms worst case. A real failure\n * still surfaces with a structured [PROOF_FAIL] so the race is not masked.\n *\n * Note: `workspace_members.user_id` FKs to `public.users(id)`, NOT\n * `auth.users`. The value is the same UUID because `handle_new_user`\n * copies it, but the FK target matters if future migrations ever split\n * them.\n */\nasync function waitForPublicUser(\n sb: ReturnType<typeof createProofServiceClient>,\n userId: string,\n email: string,\n): Promise<void> {\n const maxAttempts = 10;\n const delayMs = 50;\n for (let i = 0; i < maxAttempts; i++) {\n const { data } = await sb\n .from(\"users\")\n .select(\"id\")\n .eq(\"id\", userId)\n .maybeSingle();\n if (data) return;\n await new Promise((r) => setTimeout(r, delayMs));\n }\n failProof(\n \"seed_user_trigger\",\n `public.users row for ${email} (id ${userId}) within ${maxAttempts * delayMs}ms`,\n \"row still missing — the handle_new_user trigger did not materialize the public.users mirror row\",\n );\n}\n\nexport const seed = {\n /**\n * Create a workspace with no owner. Use `seed.user({ workspace, role: \"owner\" })`\n * afterwards to attach an owner. Returns the inserted row.\n *\n * Bypasses RLS via the service-role client, so it works even when no user\n * is authenticated.\n */\n async workspace(name: string): Promise<SeedWorkspace> {\n const sb = createProofServiceClient();\n\n const { data, error, status } = await sb\n .from(\"workspaces\")\n .insert({\n name,\n type: \"team\",\n })\n .select(\"id, name, owner_id\")\n .single();\n\n if (error || !data) {\n failSeed(\n \"seed_workspace\",\n \"a workspace row to be created\",\n `insert error: ${error?.message ?? \"no row returned\"}`,\n error,\n status,\n );\n }\n\n return {\n id: data.id,\n name: data.name,\n ownerId: data.owner_id,\n };\n },\n\n /**\n * Create an auth user with the given credentials and attach them to the\n * provided workspace with the given role. Relies on the `handle_new_user`\n * trigger to mirror the row into `public.users` automatically.\n */\n async user(opts: {\n email: string;\n password: string;\n workspace: SeedWorkspace;\n role: \"owner\" | \"admin\" | \"member\" | \"viewer\";\n }): Promise<SeedUser> {\n const sb = createProofServiceClient();\n\n const { data: userData, error: createErr } = await sb.auth.admin.createUser(\n {\n email: opts.email,\n password: opts.password,\n email_confirm: true,\n },\n );\n\n if (createErr || !userData?.user) {\n failSeed(\n \"seed_user_auth\",\n `auth user to be created for ${opts.email}`,\n `admin.createUser error: ${createErr?.message ?? \"no user returned\"}`,\n createErr,\n );\n }\n\n const userId = userData.user.id;\n\n // `workspace_members.user_id` references `public.users(id)`, not\n // `auth.users`. The `handle_new_user` trigger mirrors the row on\n // `auth.users` INSERT; poll briefly to rule out any post-commit\n // visibility race before the FK insert below.\n await waitForPublicUser(sb, userId, opts.email);\n\n const {\n data: memberRow,\n error: memberErr,\n status: memberStatus,\n } = await sb\n .from(\"workspace_members\")\n .insert({\n workspace_id: opts.workspace.id,\n user_id: userId,\n role: opts.role,\n })\n .select(\"id\")\n .single();\n\n if (memberErr || !memberRow) {\n failSeed(\n \"seed_user_membership\",\n `workspace_members row for user ${opts.email} in workspace ${opts.workspace.id}`,\n `insert error: ${memberErr?.message ?? \"no row returned\"}`,\n memberErr,\n memberStatus,\n );\n }\n\n return {\n id: userId,\n email: opts.email,\n password: opts.password,\n workspaceId: opts.workspace.id,\n role: opts.role,\n membershipId: memberRow.id,\n };\n },\n\n /**\n * Delete a workspace and cascade-clean its members. Used by helpers for\n * teardown. Safe to call on a workspace that no longer exists.\n */\n async deleteWorkspace(workspaceId: string): Promise<void> {\n const sb = createProofServiceClient();\n await sb.from(\"workspaces\").delete().eq(\"id\", workspaceId);\n },\n\n /**\n * Delete an auth user (cascades to public.users and workspace_members via\n * the schema's ON DELETE CASCADE). Safe to call if the user no longer\n * exists.\n */\n async deleteUser(userId: string): Promise<void> {\n const sb = createProofServiceClient();\n await sb.auth.admin.deleteUser(userId);\n },\n};\n","// Import External Packages\nimport type { SupabaseClient } from \"@supabase/supabase-js\";\n// Import Local Imports\nimport type { SeedUser, SeedWorkspace } from \"./seed\";\n// Import Core Dependencies\n// Import Shared Dependencies\n// Import Extension Dependencies\n\n/**\n * Seed handles available to a tenant-isolation fixture factory.\n *\n * A factory normally inserts one row for each side so the helper can prove both\n * isolation directions. It may use fewer handles when the table is user-scoped\n * or when an existing seeded workspace/user row is itself the fixture.\n */\nexport interface ProofFixtureContext {\n orgA: SeedWorkspace;\n orgB: SeedWorkspace;\n userA: SeedUser;\n userB: SeedUser;\n sb: SupabaseClient;\n}\n\n/**\n * Explicit, table-owned setup contract for a Proof fixture.\n *\n * Keep factories under `e2e/fixtures/<table>.ts`. The table name is repeated at\n * runtime so a copied factory cannot silently seed one table while a proof\n * probes another.\n */\nexport interface ProofFixtureFactory<TTable extends string = string> {\n readonly table: TTable;\n create(context: ProofFixtureContext): Promise<void>;\n}\n\nexport interface DefineProofFixtureOptions<TTable extends string> {\n table: TTable;\n create(context: ProofFixtureContext): Promise<void>;\n}\n\n/** Define a completed, explicit fixture factory. */\nexport function defineProofFixture<const TTable extends string>(\n options: DefineProofFixtureOptions<TTable>,\n): ProofFixtureFactory<TTable> {\n return Object.freeze({\n table: options.table,\n create: options.create,\n });\n}\n\n/**\n * Error used when a proof exists before its table's valid fixture shape is\n * known. `assert.tenantIsolation` recognizes it and records an incomplete\n * assertion rather than allowing the missing setup to look green.\n */\nexport class ProofFixturePendingError extends Error {\n readonly code = \"fixture_factory_required\";\n\n constructor(\n readonly table: string,\n readonly reason: string,\n ) {\n super(\n `Fixture factory for \"${table}\" is incomplete: ${reason}. Complete e2e/fixtures/${table}.ts with values valid for the final schema.`,\n );\n this.name = \"ProofFixturePendingError\";\n }\n}\n\n/**\n * Declare the intentional pre-schema state of a fixture.\n *\n * The builder replaces this with `defineProofFixture(...)` after migrations\n * settle. Until then the proof remains explicitly incomplete.\n */\nexport function pendingProofFixture<const TTable extends string>(\n table: TTable,\n reason = \"the table schema and product constraints are not final\",\n): ProofFixtureFactory<TTable> {\n return defineProofFixture({\n table,\n async create() {\n throw new ProofFixturePendingError(table, reason);\n },\n });\n}\n\nexport function isProofFixturePendingError(\n error: unknown,\n): error is ProofFixturePendingError {\n return error instanceof ProofFixturePendingError;\n}\n","// Import External Packages\nimport type { SupabaseClient } from \"@supabase/supabase-js\";\nimport type { APIResponse, Page } from \"@playwright/test\";\n// Import Local Imports\nimport { actAsUser } from \"./actAsUser\";\nimport {\n recordAssertion,\n withAssertionProvenance,\n withoutAssertionProvenance,\n} from \"./trace\";\nimport { seed, type SeedUser, type SeedWorkspace } from \"../server/seed\";\nimport {\n isProofFixturePendingError,\n type ProofFixtureContext,\n type ProofFixtureFactory,\n} from \"../server/fixture\";\nimport { createProofServiceClient } from \"../server/service-client\";\nimport { isTransient, TRANSIENT_MAX_ATTEMPTS } from \"../server/transient\";\nimport { isAuthRateLimited } from \"../server/auth-rate-limit\";\nimport type { AssertionRole } from \"../shared/trace-types\";\nimport type { ProofKind } from \"../shared/vocabulary\";\n// Import Core Dependencies\n// Import Shared Dependencies\n// Import Extension Dependencies\n\n// ---------------------------------------------------------------------------\n// assert.tenantIsolation\n//\n// The core SDK thesis: prove that RLS actually keeps tenant data separated.\n//\n// The helper:\n// 1. Creates two fresh workspaces (orgA, orgB) and one user per workspace\n// 2. Runs the caller's `setup()` in orgA so there is data to try to leak\n// 3. Counts orgA's rows with the SERVICE ROLE to establish ground truth. Zero\n// rows means the proof would measure nothing, and it fails as vacuous.\n// 4. Runs a POSITIVE CONTROL: userA (a member of orgA) must be able to read\n// those rows through RLS. Without this, \"userB saw 0 rows\" is\n// indistinguishable from a blanket-deny policy, a missing grant, or a\n// typo'd table name — all of which would otherwise record a green\n// tenant-isolation assertion.\n// 5. Runs the negative probe: signs in as userB against a direct Supabase\n// client (no browser) and queries `<table>` filtered to orgA.id. If rows\n// come back, RLS is broken — fail with a structured [PROOF_FAIL].\n// 6. Repeats 3–5 in reverse (userA against orgB). The reverse direction is\n// SKIPPED WITHOUT RECORDING when the caller only populated orgA, because\n// an empty-org probe is exactly the vacuous pass step 3 exists to reject.\n// 7. Optionally: if a `page` is provided, log userB into the browser and\n// navigate somewhere the data would be visible — asserting empty/403\n// UI state as a secondary check.\n// 8. Tears down the fixtures (cascades handle most of it).\n//\n// Ergonomics are the whole point: the caller writes ~3 lines. Any more and\n// agents skip the helper, which defeats the SDK. Note that the control in step\n// 4 is derived automatically from the seeded fixtures — callers get it for\n// free and cannot forget it.\n// ---------------------------------------------------------------------------\n\ntype RandomSuffix = string;\n\nfunction randomSuffix(): RandomSuffix {\n return Math.random().toString(36).slice(2, 10);\n}\n\nfunction proofFail(\n category: string,\n expected: string,\n found: string,\n extras: { file?: string; suggestion: string },\n): Error {\n return new Error(\n `[PROOF_FAIL] ${category}: expected ${expected}, found ${found}\\n` +\n ` file: ${extras.file ?? \"src/playwright/assert.ts\"}\\n` +\n ` suggestion: ${extras.suggestion}`,\n );\n}\n\n/**\n * A probe whose request never reached the database has observed nothing, and\n * \"nothing\" must not be read as a denial.\n *\n * Every write probe below decides its verdict from ground truth — the row did\n * not land, so the policy held — which is exactly what a gateway failure also\n * looks like. The retrying fetch installed on these clients makes that rare;\n * this makes the leftover case loud instead of green. The `*_setup` category\n * follows the SDK convention for \"the harness could not establish the conditions\n * to measure\".\n */\nfunction failIfInfrastructure(\n error: { message?: string | null; status?: number | null } | null | undefined,\n status: number | undefined,\n setupCategory: string,\n what: string,\n): void {\n if (isAuthRateLimited(error, status)) {\n throw proofFail(\n setupCategory.replace(/_setup$/, \"_rate_limited\"),\n `${what} to receive a database verdict`,\n `HTTP 429/auth budget exhaustion: ${error?.message ?? \"rate limited\"}`,\n {\n suggestion:\n \"This is infrastructure, not an RLS denial. Wait for the auth window to reset or reduce parallel proof workers.\",\n },\n );\n }\n if (!isTransient(error, status)) return;\n throw proofFail(\n setupCategory,\n `${what} to reach the database, so that the result means something`,\n `gateway/transport failure after ${TRANSIENT_MAX_ATTEMPTS} attempts: ${error?.message ?? \"unknown error\"}`,\n {\n suggestion:\n `The request never got an answer from Postgres, so neither \"denied\" nor \"allowed\" was observed — ` +\n `reading it as a denial would certify an invariant that nothing exercised. Check that the local Supabase ` +\n `stack is healthy (pnpm exec supabase status) and re-run.`,\n },\n );\n}\n\n// Note: the \"sign-in as userX and return a fresh SupabaseClient\" primitive\n// lives in `actAsUser.supabaseClient()` so individual proof specs can use it\n// directly for ad-hoc RLS probes. This helper is the primary mechanism for\n// tenant isolation checks: it bypasses Playwright entirely.\n\ninterface TenantIsolationBaseOptions {\n /**\n * Table to query as userB. RLS on the table is what is being tested.\n * Must be a table governed by a tenant-scoped RLS policy — either\n * workspace-scoped (the canonical pattern uses `get_user_workspace_ids`) or\n * user-scoped (`user_id = auth.uid()`); see `scope`.\n */\n table: string;\n /**\n * What \"belongs to one tenant\" means for this table:\n *\n * - `\"workspace\"` (default) — rows carry a workspace id, and the two tenants\n * are the seeded orgA / orgB.\n * - `\"user\"` — rows carry a user id, and the two tenants are userA / userB.\n * This is the shape for personal data (notifications, billing records):\n * the invariant is \"another *user* must not read these rows\", which is the\n * same claim as workspace isolation with a different scope column.\n *\n * Tables classified `user_scoped` in `.proof/schema.json` need `\"user\"`.\n */\n scope?: \"workspace\" | \"user\";\n /**\n * Column carrying the scope value. Defaults to `workspace_id` for workspace\n * scope and `user_id` for user scope.\n *\n * Override when the scope lives on the primary key instead of a foreign key —\n * `workspaces` is scoped by its own `id`, and `users` by `id`.\n */\n scopeColumn?: string;\n /**\n * Planner-owned semantic subset that the fixture must materialize.\n *\n * The equality filter is applied together with tenant scope to service-role\n * ground truth, the owner's positive control, and the outsider probe. Put\n * product meaning here (for example `{ status: \"published\" }`), not in the\n * executor-owned fixture factory: rows outside this subset do not count as\n * evidence for the claim.\n */\n criterion?: {\n description: string;\n where: Record<string, unknown>;\n };\n /**\n * Optional: pass a `page` to additionally log userB into a browser and\n * assert the UI does not show orgA data. In v0.5 this step only signs in\n * and reloads — callers can add their own page.locator checks afterwards\n * by chaining additional `trace.step` calls.\n */\n page?: Page;\n /**\n * Optional: a tag included in seeded workspace/user names so concurrent\n * runs don't collide on error paths where teardown is skipped.\n */\n tag?: string;\n}\n\nfunction validateTenantIsolationCriterion(\n table: string,\n scopeColumn: string,\n criterion: TenantIsolationBaseOptions[\"criterion\"],\n): void {\n if (!criterion) return;\n\n const invalidReason =\n criterion.description.trim().length === 0\n ? \"criterion.description is empty\"\n : Object.keys(criterion.where).length === 0\n ? \"criterion.where has no equality filters\"\n : Object.prototype.hasOwnProperty.call(criterion.where, scopeColumn)\n ? `criterion.where contains the tenant scope column \"${scopeColumn}\"`\n : null;\n if (!invalidReason) return;\n\n recordAssertion({\n kind: \"tenant_isolation\",\n target: table,\n operation: \"select\",\n passed: false,\n status: \"incomplete\",\n role: \"primary\",\n detail: `planner-owned fixture criterion is invalid: ${invalidReason}`,\n });\n throw proofFail(\n \"fixture_criterion_invalid\",\n \"a non-empty semantic equality filter that does not redefine tenant scope\",\n invalidReason,\n {\n suggestion:\n `Keep \"${scopeColumn}\" out of criterion.where; assert.tenantIsolation applies the current side's scope automatically. ` +\n \"Use criterion.where only for semantic state such as status, type, or visibility.\",\n },\n );\n}\n\n/**\n * Tenant fixture source.\n *\n * Inline `setup` remains available for small, settled schemas. A proof written\n * before its schema exists should instead import an explicit factory from\n * `e2e/fixtures/<table>.ts`; that file can export `pendingProofFixture(...)`\n * until the builder knows the final constraints. Supplying both is rejected by\n * the type system so there is one source of fixture truth.\n */\nexport type TenantIsolationOptions = TenantIsolationBaseOptions &\n (\n | {\n /**\n * Inline setup for an already-known schema. It must create at least one\n * row for side A; side B is optional but required for a bidirectional\n * claim.\n */\n setup: (context: ProofFixtureContext) => Promise<void>;\n fixture?: never;\n }\n | {\n /** Explicit table-owned factory from `e2e/fixtures/<table>.ts`. */\n fixture: ProofFixtureFactory;\n setup?: never;\n }\n );\n\nasync function teardown(\n orgA: SeedWorkspace,\n orgB: SeedWorkspace,\n userA: SeedUser,\n userB: SeedUser,\n): Promise<void> {\n await Promise.allSettled([\n seed.deleteWorkspace(orgA.id),\n seed.deleteWorkspace(orgB.id),\n ]);\n await Promise.allSettled([\n seed.deleteUser(userA.id),\n seed.deleteUser(userB.id),\n ]);\n}\n\n// ---------------------------------------------------------------------------\n// assert.authorization\n//\n// Covers the two dominant \"X is NOT allowed to do Y\" shapes:\n//\n// 1. RLS-layer denial — the DB itself rejects (or silently filters) the\n// operation. Used when the invariant is \"the RLS policy on table T\n// should block op O for user U against row R.\"\n//\n// 2. Action-layer denial — the server action's `withRBAC` or its own\n// role-check rejects the call. Used when the invariant is \"action A\n// should return `{ success: false, error: ... }` when invoked by user\n// U.\" Goes through `actAsUser.invokeAction` → POST /api/proof/invoke-action.\n//\n// Callers can run either probe or both. RLS probes record `authorization`;\n// action probes default to that kind but may name another declared invariant.\n// The helper throws on the first probe that *succeeds when it was expected to\n// fail* — that's the PROOF_FAIL signal.\n//\n// Three things a naive version of this helper would grade wrongly, each of which\n// has bitten a real proof in this repo:\n//\n// - What PostgREST returns is not the verdict. `.select()` adds a RETURNING\n// clause that must satisfy the SELECT policy, so a permitted write can look\n// rejected. Write verdicts come from re-reading state with the service role.\n// - A probe needs something to act on. Against zero matching rows every op\n// looks perfectly enforced (`authorization_vacuous`).\n// - A write probe needs an actor who can SEE what it writes to. Postgres\n// filters the rows an UPDATE/DELETE reaches through its WHERE clause by the\n// SELECT policy, so an actor who cannot read the row matches nothing\n// regardless of write permissions, and the service-role re-read then\n// truthfully reports \"unchanged\". `requireWriteTargetVisible` records that\n// visibility as a control and fails as `authorization_blind` when it is\n// missing, rather than certifying an invariant nothing exercised.\n// ---------------------------------------------------------------------------\n\ntype AuthorizationOp = \"select\" | \"insert\" | \"update\" | \"delete\";\n\ninterface RlsProbe {\n /** Table the probe operates against. */\n table: string;\n /** Operation the probe attempts. Each op has distinct \"denied\" semantics. */\n op: AuthorizationOp;\n /**\n * For `select` / `update` / `delete`: `.eq()` filter applied to the query.\n * Required for update/delete (otherwise the probe has no target row) and\n * typical for select (otherwise it's an unqualified scan).\n */\n filter?: Record<string, unknown>;\n /**\n * For `insert` / `update`: the row payload. Ignored for select/delete.\n */\n payload?: Record<string, unknown>;\n /** Trace target override. Defaults to `table`. */\n target?: string;\n}\n\ninterface ActionProbe {\n /** Module part of the registry key: `\"<module>:<name>\"`. */\n module: string;\n /** Action name part of the registry key. Must be in PROOF_ACTION_REGISTRY. */\n name: string;\n /** Forwarded as-is to the action; validated by its own Zod schema. */\n inputParams: Record<string, unknown>;\n /** Optional regex the rejection message must match for a stricter proof. */\n expectedErrorMatch?: RegExp;\n /**\n * Invariant this action probe establishes. Defaults to `authorization`.\n * Use the action's declared withProof invariant (for example\n * `tenant_isolation` or `idempotency`) so coverage matches the evidence.\n */\n kind?: ProofKind;\n /** Trace target override. Defaults to `\"<module>:<name>\"`. */\n target?: string;\n}\n\ninterface AuthorizationOptions {\n /**\n * Who is attempting the operation. Must be the role that should be\n * *denied*. Never pass a role the invariant says should succeed —\n * authorization proofs assert rejection, not permission.\n */\n actor: \"admin\" | \"member\" | { email: string; password: string };\n /** RLS-layer probe. At least one of `rls` or `action` is required. */\n rls?: RlsProbe;\n /**\n * Action-layer probe. Requires `page` because the action is invoked via\n * the dev server's POST /api/proof/invoke-action route.\n */\n action?: ActionProbe;\n /** Required when `action` is supplied. */\n page?: Page;\n}\n\ninterface ActionSucceedsOptions {\n /** Who must be able to invoke the action successfully. */\n actor: \"admin\" | \"member\" | { email: string; password: string };\n /** Registered action and the exact input used by the control. */\n action: Omit<ActionProbe, \"expectedErrorMatch\">;\n /** Required because actions are invoked through the proof Route Handler. */\n page: Page;\n /** Assertion category. Defaults to `happy_path`. */\n kind?: ProofKind;\n /**\n * Positive action checks are controls by default: they prove a related\n * refusal was capable of failing, but cannot satisfy a mission on their own.\n */\n role?: AssertionRole;\n}\n\nfunction describeActor(actor: AuthorizationOptions[\"actor\"]): {\n email: string;\n password: string;\n label: string;\n} {\n if (typeof actor === \"string\") {\n // Server-only SEED_* env names — never NEXT_PUBLIC_ (credentials must not\n // be inlined into the client bundle). This code runs in Node (Playwright).\n const emailEnv =\n actor === \"admin\"\n ? process.env.SEED_ADMIN_EMAIL\n : process.env.SEED_MEMBER_EMAIL;\n const passwordEnv =\n actor === \"admin\"\n ? process.env.SEED_ADMIN_PASSWORD\n : process.env.SEED_MEMBER_PASSWORD;\n if (!emailEnv || !passwordEnv) {\n const prefix = actor === \"admin\" ? \"SEED_ADMIN\" : \"SEED_MEMBER\";\n throw new Error(\n `[PROOF_FAIL] seed_credentials_missing: ${prefix}_EMAIL and ${prefix}_PASSWORD must be set when using the \"${actor}\" seed actor`,\n );\n }\n return {\n email: emailEnv,\n password: passwordEnv,\n label: `seed:${actor}`,\n };\n }\n return { email: actor.email, password: actor.password, label: actor.email };\n}\n\n/**\n * Apply an equality filter map to a query builder.\n *\n * The cast is contained here on purpose: probes address tables and columns by\n * dynamic name, which the generated Database types cannot express, and letting\n * that inference run through every call site blows the instantiation depth\n * limit.\n */\ntype EqChain = { eq(column: string, value: unknown): unknown };\n\nfunction applyEq<T>(query: T, filter: Record<string, unknown>): T {\n let out = query as unknown as EqChain;\n for (const [k, v] of Object.entries(filter)) {\n out = out.eq(k, v) as EqChain;\n }\n return out as unknown as T;\n}\n\n/**\n * Count rows matching an exact filter using the service role.\n *\n * This is the ground truth for every write probe. What PostgREST returned to\n * the caller cannot be trusted to answer \"did the write land?\" — see the long\n * comment in the insert branch of runRlsProbe.\n */\nasync function countMatchingAsService(\n sb: SupabaseClient,\n table: string,\n filter: Record<string, unknown>,\n): Promise<number> {\n const { count, error } = await applyEq(\n sb.from(table).select(\"*\", { count: \"exact\", head: true }),\n filter,\n );\n if (error) {\n throw proofFail(\n \"authorization_setup\",\n `to verify the state of \"${table}\" with the service role`,\n `Supabase error: ${error.message}`,\n {\n suggestion: `The probe cannot confirm whether the write was actually blocked without reading the table as service_role. Check the table name and that SUPABASE_SERVICE_ROLE_KEY is set.`,\n },\n );\n }\n return count ?? 0;\n}\n\n/**\n * How many of the probe's target rows the ACTOR can actually read.\n *\n * This is the control that makes a write refusal mean something, and it is not\n * the same question as `countMatchingAsService`. PostgreSQL applies the SELECT\n * policy to the rows an UPDATE or DELETE reaches through its WHERE clause, so an\n * actor who cannot see a row cannot write to it *whatever* the UPDATE/DELETE\n * policy and grants say: the statement matches zero rows and reports success.\n * A before/after comparison then shows nothing changed and the probe records a\n * pass — on a table the actor may in fact be free to rewrite.\n *\n * Concretely: a member probing `audit_logs` (super-admin read only) passes even\n * with UPDATE granted and a `USING (true)` update policy in place. The\n * service-role ground truth cannot catch it, because the service role can see\n * the rows and correctly reports them unchanged.\n *\n * An error is treated as zero visible rows rather than a setup failure: a\n * permission-denied answer IS the observation that the actor cannot read there.\n */\nasync function countVisibleToActor(\n client: SupabaseClient,\n table: string,\n filter: Record<string, unknown>,\n): Promise<number> {\n const { count, error, status } = await applyEq(\n client.from(table).select(\"*\", { count: \"exact\", head: true }),\n filter,\n );\n failIfInfrastructure(\n error,\n status,\n \"authorization_setup\",\n `the actor's visibility check on ${table}`,\n );\n if (error) return 0;\n return count ?? 0;\n}\n\n/**\n * Refuse to grade a write probe the actor could never have won.\n *\n * Recorded as a `control` assertion either way, so the trace shows the probe was\n * capable of failing instead of leaving a reader to assume it.\n */\nasync function requireWriteTargetVisible(args: {\n client: SupabaseClient;\n probe: RlsProbe;\n target: string;\n filter: Record<string, unknown>;\n existing: number;\n actorLabel: string;\n}): Promise<void> {\n const { client, probe, target, filter, existing, actorLabel } = args;\n const visible = await countVisibleToActor(client, probe.table, filter);\n\n if (visible > 0) {\n recordAssertion({\n kind: \"authorization\",\n target,\n operation: \"select\",\n passed: true,\n role: \"control\",\n detail: `${actorLabel} can read ${visible} of the ${existing} row(s) matching ${JSON.stringify(filter)} in ${probe.table}, so a refused ${probe.op.toUpperCase()} is distinguishable from one that never reached a row`,\n });\n return;\n }\n\n recordAssertion({\n kind: \"authorization\",\n target,\n operation: \"select\",\n passed: false,\n status: \"incomplete\",\n role: \"control\",\n detail: `${actorLabel} cannot read any of the ${existing} row(s) matching ${JSON.stringify(filter)} in ${probe.table}, so its ${probe.op.toUpperCase()} matches zero rows whether or not the operation is permitted`,\n });\n throw proofFail(\n \"authorization_blind\",\n `${actorLabel} to be able to read at least one of the ${existing} target row(s) in \"${probe.table}\", so that a refused ${probe.op.toUpperCase()} means the write was rejected`,\n `0 visible row(s) — this probe cannot fail, because PostgreSQL filters the rows an ${probe.op.toUpperCase()} reaches through its WHERE clause by the SELECT policy`,\n {\n suggestion:\n `Point the probe at a row the actor can read, or use an actor who can — for an admin-only table that is the admin, and \"not even the actor who can read every row may change one\" is usually the stronger claim anyway. ` +\n `If the invariant you actually want is \"the actor cannot SEE these rows\", use \\`op: \"select\"\\` or assert.tenantIsolation, which grade visibility directly.`,\n },\n );\n}\n\nasync function runRlsProbe(\n probe: RlsProbe,\n actorCreds: { email: string; password: string; label: string },\n): Promise<void> {\n const target = probe.target ?? probe.table;\n const client = await actAsUser.supabaseClient({\n email: actorCreds.email,\n password: actorCreds.password,\n });\n // Loosely typed on purpose: probes address tables by dynamic name, which the\n // generated Database types (rightly) reject for literal-union table keys.\n const sb: SupabaseClient = createProofServiceClient();\n\n if (probe.op === \"select\") {\n // Ground truth first: \"the actor read 0 rows\" is only evidence if there was\n // something to read. Against an empty table it is indistinguishable from a\n // working policy, which is the same vacuity trap the write probes guard.\n const existing = await countMatchingAsService(\n sb,\n probe.table,\n probe.filter ?? {},\n );\n if (existing === 0) {\n recordAssertion({\n kind: \"authorization\",\n target,\n operation: probe.op,\n passed: false,\n status: \"incomplete\",\n role: \"primary\",\n detail: `no rows in ${probe.table} matching ${JSON.stringify(probe.filter ?? {})}, so \"${actorCreds.label} read 0 rows\" proves nothing`,\n });\n throw proofFail(\n \"authorization_vacuous\",\n `at least one row in \"${probe.table}\" matching ${JSON.stringify(probe.filter ?? {})} for the select probe to be denied access to`,\n \"0 row(s) — there was nothing for the actor to fail to read\",\n {\n suggestion: `Seed the fixture row(s) with the service-role client before probing. Reading zero rows from an empty table is not evidence that RLS filtered anything.`,\n },\n );\n }\n\n let query = client.from(probe.table).select(\"*\");\n for (const [k, v] of Object.entries(probe.filter ?? {})) {\n query = query.eq(k, v);\n }\n const { data, error, status } = await query;\n failIfInfrastructure(\n error,\n status,\n \"authorization_setup\",\n `the actor's SELECT on ${probe.table}`,\n );\n const rows = data ?? [];\n\n if (!error && rows.length > 0) {\n recordAssertion({\n kind: \"authorization\",\n target,\n operation: probe.op,\n passed: false,\n role: \"primary\",\n detail: `actor ${actorCreds.label} SELECTed ${rows.length} row(s) from ${probe.table}; expected RLS to filter to 0`,\n });\n throw proofFail(\n \"authorization\",\n `0 rows from ${probe.table} when selected by ${actorCreds.label} with filter ${JSON.stringify(probe.filter ?? {})}`,\n `${rows.length} row(s)`,\n {\n suggestion: `Check SELECT RLS policy on ${probe.table}. The actor should not have read access to these rows.`,\n },\n );\n }\n\n recordAssertion({\n kind: \"authorization\",\n target,\n operation: probe.op,\n passed: true,\n role: \"primary\",\n detail: `${actorCreds.label} SELECT on ${probe.table} returned 0 of the ${existing} row(s) that exist (RLS honored)${error ? `; the query itself errored: ${error.message}` : \"\"}`,\n });\n return;\n }\n\n if (probe.op === \"insert\") {\n if (!probe.payload) {\n throw new Error(\n \"[PROOF_FAIL] bad_probe: rls.op='insert' requires `payload`\\n\" +\n \" file: src/playwright/assert.ts\",\n );\n }\n\n // Deliberately NO .select() here.\n //\n // `.select()` makes PostgREST add a RETURNING clause, and RETURNING must\n // additionally satisfy the table's SELECT policy. On a workspace-scoped\n // table the freshly-inserted row usually CANNOT satisfy it — a policy like\n // `workspace_id IN (SELECT get_user_workspace_ids(auth.uid()))` calls a\n // STABLE function that cannot see the row being written — so the whole\n // statement fails with 42501 even when the INSERT itself was permitted.\n //\n // Trusting that error means reporting a wide-open table as \"denied\", which\n // is exactly how a self-join hole survived its own regression proof. An\n // attacker simply omits the RETURNING and the row lands.\n const { error, status } = await client\n .from(probe.table)\n .insert(probe.payload);\n failIfInfrastructure(\n error,\n status,\n \"authorization_setup\",\n `the actor's INSERT on ${probe.table}`,\n );\n\n // Ground truth: did a row actually land? Only the service role can answer,\n // because the actor may be unable to read what it just wrote.\n const committed = await countMatchingAsService(\n sb,\n probe.table,\n probe.payload,\n );\n\n if (committed > 0) {\n // Remove the row so teardown and any later probes see a clean table.\n await applyEq(sb.from(probe.table).delete(), probe.payload);\n\n recordAssertion({\n kind: \"authorization\",\n target,\n operation: probe.op,\n passed: false,\n role: \"primary\",\n detail: `actor ${actorCreds.label} INSERT on ${probe.table} COMMITTED ${committed} row(s)${error ? ` even though the API returned \"${error.message}\"` : \"\"}; expected RLS/policy denial`,\n });\n throw proofFail(\n \"authorization\",\n `INSERT on ${probe.table} to be denied for ${actorCreds.label}`,\n error\n ? `the API returned an error (\"${error.message}\") but a service-role read found ${committed} row(s) committed — the write went through`\n : `insert succeeded (${committed} row(s) committed)`,\n {\n suggestion: `Check INSERT RLS policy and grants on ${probe.table}.`,\n },\n );\n }\n\n recordAssertion({\n kind: \"authorization\",\n target,\n operation: probe.op,\n passed: true,\n role: \"primary\",\n detail: `${actorCreds.label} INSERT on ${probe.table} denied${error ? ` (${error.message})` : \"\"}; service-role read confirmed 0 row(s) committed`,\n });\n return;\n }\n\n // update / delete.\n //\n // As with insert, the number of rows PostgREST hands back is not evidence:\n // RETURNING is filtered by the SELECT policy, so a committed write can report\n // zero affected rows. The verdict therefore comes from comparing the table's\n // state before and after using the service role.\n if (!probe.filter) {\n throw new Error(\n `[PROOF_FAIL] bad_probe: rls.op='${probe.op}' requires \\`filter\\`\\n` +\n \" file: src/playwright/assert.ts\",\n );\n }\n\n if (probe.op === \"delete\") {\n const before = await countMatchingAsService(sb, probe.table, probe.filter);\n\n // Both guards run BEFORE the delete is attempted: a probe that cannot be\n // graded should not also mutate the fixture it was pointed at.\n //\n // A probe against rows that do not exist proves nothing: 0 → 0 looks\n // identical to a perfectly enforced policy.\n if (before === 0) {\n recordAssertion({\n kind: \"authorization\",\n target,\n operation: probe.op,\n passed: false,\n role: \"primary\",\n detail: `no rows in ${probe.table} matched ${JSON.stringify(probe.filter)} before the DELETE probe, so the probe could not have failed`,\n });\n throw proofFail(\n \"authorization_vacuous\",\n `at least one row in \"${probe.table}\" matching ${JSON.stringify(probe.filter)} so the DELETE has a target`,\n \"0 matching rows before the probe ran\",\n {\n suggestion: `Seed the row you expect the actor to be unable to delete before calling assert.authorization.`,\n },\n );\n }\n\n await requireWriteTargetVisible({\n client,\n probe,\n target,\n filter: probe.filter,\n existing: before,\n actorLabel: actorCreds.label,\n });\n\n const { error, status } = await applyEq(\n client.from(probe.table).delete(),\n probe.filter,\n );\n failIfInfrastructure(\n error,\n status,\n \"authorization_setup\",\n `the actor's DELETE on ${probe.table}`,\n );\n const after = await countMatchingAsService(sb, probe.table, probe.filter);\n\n if (after < before) {\n recordAssertion({\n kind: \"authorization\",\n target,\n operation: probe.op,\n passed: false,\n role: \"primary\",\n detail: `actor ${actorCreds.label} DELETE on ${probe.table} removed ${before - after} row(s) (${before} → ${after})${error ? ` even though the API returned \"${error.message}\"` : \"\"}; expected none`,\n });\n throw proofFail(\n \"authorization\",\n `0 rows removed from ${probe.table} by ${actorCreds.label}`,\n `${before - after} row(s) removed (${before} → ${after})`,\n {\n suggestion: `Check the DELETE RLS policy on ${probe.table}.`,\n },\n );\n }\n\n recordAssertion({\n kind: \"authorization\",\n target,\n operation: probe.op,\n passed: true,\n role: \"primary\",\n detail: `${actorCreds.label} DELETE on ${probe.table} removed 0 of ${before} matching row(s)${error ? ` (${error.message})` : \"\"}; service-role recount confirmed ${after} (RLS honored)`,\n });\n return;\n }\n\n // update\n if (!probe.payload || Object.keys(probe.payload).length === 0) {\n throw new Error(\n \"[PROOF_FAIL] bad_probe: rls.op='update' requires a non-empty `payload`\\n\" +\n \" file: src/playwright/assert.ts\\n\" +\n \" suggestion: The payload is both the attempted change and the way the probe verifies whether it was applied.\",\n );\n }\n\n const columns = Object.keys(probe.payload);\n\n const readTargetRows = async () => {\n const { data, error } = await applyEq(\n sb.from(probe.table).select(columns.join(\",\")),\n probe.filter as Record<string, unknown>,\n );\n if (error) {\n throw proofFail(\n \"authorization_setup\",\n `to read ${columns.join(\", \")} from \"${probe.table}\" with the service role`,\n `Supabase error: ${error.message}`,\n {\n suggestion: `The probe compares these columns before and after the update to decide whether the write landed.`,\n },\n );\n }\n return (data ?? []) as unknown as Array<Record<string, unknown>>;\n };\n\n const rowMatchesPayload = (row: Record<string, unknown>) =>\n columns.every(\n (c) => JSON.stringify(row[c]) === JSON.stringify(probe.payload?.[c]),\n );\n\n const before = await readTargetRows();\n\n if (before.length === 0) {\n recordAssertion({\n kind: \"authorization\",\n target,\n operation: probe.op,\n passed: false,\n role: \"primary\",\n detail: `no rows in ${probe.table} matched ${JSON.stringify(probe.filter)} before the UPDATE probe, so the probe could not have failed`,\n });\n throw proofFail(\n \"authorization_vacuous\",\n `at least one row in \"${probe.table}\" matching ${JSON.stringify(probe.filter)} so the UPDATE has a target`,\n \"0 matching rows before the probe ran\",\n {\n suggestion: `Seed the row you expect the actor to be unable to modify before calling assert.authorization.`,\n },\n );\n }\n\n await requireWriteTargetVisible({\n client,\n probe,\n target,\n filter: probe.filter,\n existing: before.length,\n actorLabel: actorCreds.label,\n });\n\n const { error, status } = await applyEq(\n client.from(probe.table).update(probe.payload),\n probe.filter,\n );\n failIfInfrastructure(\n error,\n status,\n \"authorization_setup\",\n `the actor's UPDATE on ${probe.table}`,\n );\n const after = await readTargetRows();\n\n const beforeApplied = before.filter(rowMatchesPayload).length;\n const afterApplied = after.filter(rowMatchesPayload).length;\n\n if (afterApplied > beforeApplied) {\n recordAssertion({\n kind: \"authorization\",\n target,\n operation: probe.op,\n passed: false,\n role: \"primary\",\n detail: `actor ${actorCreds.label} UPDATE on ${probe.table} applied ${columns.join(\", \")} to ${afterApplied - beforeApplied} row(s)${error ? ` even though the API returned \"${error.message}\"` : \"\"}; expected the write to be rejected`,\n });\n throw proofFail(\n \"authorization\",\n `${columns.join(\", \")} on ${probe.table} to be unchanged for ${actorCreds.label}`,\n `${afterApplied - beforeApplied} row(s) now hold the attempted value(s) ${JSON.stringify(probe.payload)}`,\n {\n suggestion: `Check the UPDATE RLS policy and any column guard on ${probe.table}.`,\n },\n );\n }\n\n recordAssertion({\n kind: \"authorization\",\n target,\n operation: probe.op,\n passed: true,\n role: \"primary\",\n detail: `${actorCreds.label} UPDATE on ${probe.table} left ${columns.join(\", \")} unchanged across ${before.length} targeted row(s)${error ? ` (${error.message})` : \"\"}; verified by service-role re-read (RLS honored)`,\n });\n}\n\n/**\n * Outcome of a single-direction isolation probe.\n *\n * - `proved` — the isolated org held at least one row, its own member\n * could read those rows, and the outsider could not.\n * - `skipped_no_data` — the isolated org held no rows, so there was nothing\n * to leak. NOTHING is recorded: a probe with no data\n * cannot prove isolation, and recording a pass here is\n * precisely the false-confidence bug this guards.\n */\ntype IsolationProbeOutcome = \"proved\" | \"skipped_no_data\";\n\n/**\n * Count the rows belonging to one tenant using the service role, bypassing RLS.\n * This is the ground truth the probe is measured against.\n */\nasync function countRowsAsService(\n sb: SupabaseClient,\n table: string,\n filter: Record<string, unknown>,\n): Promise<number> {\n const { count, error } = await applyEq(\n sb.from(table).select(\"*\", { count: \"exact\", head: true }),\n filter,\n );\n\n if (error) {\n throw proofFail(\n \"tenant_isolation_setup\",\n `to count rows in \"${table}\" with the service role to establish ground truth`,\n `Supabase error: ${error.message}`,\n {\n suggestion: `Confirm \"${table}\" exists and supports the planner-owned filter ${JSON.stringify(filter)}. Tenant isolation cannot be measured without first knowing how many matching rows are actually there.`,\n },\n );\n }\n return count ?? 0;\n}\n\nasync function probeTenantIsolation(args: {\n table: string;\n viewer: SeedUser;\n viewerLabel: string;\n /** Owner of the isolated rows; drives the positive control. */\n owner: SeedUser;\n ownerLabel: string;\n /** Column + value identifying the tenant whose rows must stay hidden. */\n scopeColumn: string;\n scopeValue: string;\n scopeLabel: string;\n criterion?: TenantIsolationBaseOptions[\"criterion\"];\n /** The canonical RLS pattern for this scope, quoted in failure suggestions. */\n scopePattern: string;\n /** Service-role client, used only to establish ground truth. */\n sb: SupabaseClient;\n}): Promise<IsolationProbeOutcome> {\n const {\n table,\n viewer,\n viewerLabel,\n owner,\n ownerLabel,\n scopeColumn,\n scopeValue,\n scopeLabel,\n criterion,\n scopePattern,\n sb,\n } = args;\n const filter = {\n [scopeColumn]: scopeValue,\n ...(criterion?.where ?? {}),\n };\n const criterionDetail = criterion\n ? ` matching planner criterion \"${criterion.description}\" (${JSON.stringify(criterion.where)})`\n : \"\";\n\n // ---- Ground truth: is there anything here to leak? ----------------------\n // Without this, \"the outsider saw 0 rows\" is unfalsifiable — an empty table\n // produces exactly the same observation as perfectly-enforced RLS.\n const actualRowCount = await countRowsAsService(sb, table, filter);\n if (actualRowCount === 0) {\n return \"skipped_no_data\";\n }\n\n // ---- Positive control: can the org's OWN member read those rows? --------\n // This is what separates \"RLS hid the rows from an outsider\" from \"nobody\n // can read this table at all\" (blanket-deny policy, missing grant, typo'd\n // table name). Both make the negative probe below return zero rows; only the\n // first one is tenant isolation.\n const ownerClient = await actAsUser.supabaseClient({\n email: owner.email,\n password: owner.password,\n });\n const {\n data: ownRows,\n error: ownError,\n status: ownStatus,\n } = await applyEq(ownerClient.from(table).select(\"*\"), filter);\n failIfInfrastructure(\n ownError,\n ownStatus,\n \"tenant_isolation_setup\",\n `the owner's control SELECT on ${table}`,\n );\n\n if (ownError || (ownRows?.length ?? 0) === 0) {\n recordAssertion({\n kind: \"tenant_isolation\",\n target: table,\n operation: \"select\",\n passed: false,\n role: \"control\",\n detail: `positive control FAILED: ${ownerLabel} (owner of ${scopeLabel}) read ${ownRows?.length ?? 0} of ${actualRowCount} row(s) in ${table}${criterionDetail}${ownError ? `; error: ${ownError.message}` : \"\"}`,\n });\n throw proofFail(\n \"tenant_isolation_control\",\n `${ownerLabel} to read the ${actualRowCount} row(s) that exist in \"${table}\" for ${scopeLabel}${criterionDetail}`,\n ownError\n ? `Supabase error: ${ownError.message}`\n : `0 row(s) — their own data is invisible to them`,\n {\n suggestion:\n `The negative probe would have \"passed\" here for the wrong reason: if the owner cannot read their own rows, ` +\n `then an outsider seeing 0 rows proves nothing. Check the SELECT policy/grant on \"${table}\" — the canonical pattern for ` +\n `this scope is ${scopePattern}. If this table is intentionally service-role-only, it is not a ` +\n `tenant-isolation target: use assert.authorization instead.`,\n },\n );\n }\n\n recordAssertion({\n kind: \"tenant_isolation\",\n target: table,\n operation: \"select\",\n passed: true,\n role: \"control\",\n detail: `${ownerLabel} read ${ownRows?.length ?? 0} of ${actualRowCount} row(s) in ${table} for ${scopeLabel}${criterionDetail} — the probe can observe these rows, so a leak would be detected`,\n });\n\n // ---- Negative probe: the outsider must see none of them -----------------\n const client = await actAsUser.supabaseClient({\n email: viewer.email,\n password: viewer.password,\n });\n\n const {\n data: leakedRows,\n error: queryError,\n status: queryStatus,\n } = await applyEq(client.from(table).select(\"*\"), filter);\n failIfInfrastructure(\n queryError,\n queryStatus,\n \"tenant_isolation_setup\",\n `the outsider's SELECT on ${table}`,\n );\n\n if (queryError) {\n recordAssertion({\n kind: \"tenant_isolation\",\n target: table,\n operation: \"select\",\n passed: false,\n role: \"primary\",\n detail: `${viewerLabel} select on ${table} scoped to ${scopeLabel}${criterionDetail} failed: ${queryError.message}`,\n });\n throw proofFail(\n \"tenant_isolation\",\n `query against ${table} as ${viewerLabel} to succeed (possibly returning 0 rows)`,\n `Supabase error: ${queryError.message}`,\n {\n suggestion: `Verify \"${table}\" has a ${scopeColumn} column and a SELECT grant for authenticated users. Tenant isolation cannot be proven if the query itself is denied at the grant level.`,\n },\n );\n }\n\n if (leakedRows && leakedRows.length > 0) {\n const sampleIds = leakedRows\n .slice(0, 3)\n .map((r: { id?: string | number }) => r.id ?? \"?\")\n .join(\", \");\n\n recordAssertion({\n kind: \"tenant_isolation\",\n target: table,\n operation: \"select\",\n passed: false,\n role: \"primary\",\n detail: `${viewerLabel} saw ${leakedRows.length} row(s) in ${table} belonging to ${scopeLabel}${criterionDetail} (sample ids: ${sampleIds})`,\n });\n throw proofFail(\n \"tenant_isolation\",\n `0 rows in \"${table}\" for ${viewerLabel} filtered to ${JSON.stringify(filter)}`,\n `${leakedRows.length} row(s) (sample ids: ${sampleIds})`,\n {\n suggestion: `Inspect the RLS policy on \"${table}\". The canonical pattern for this scope is ${scopePattern}.`,\n },\n );\n }\n\n recordAssertion({\n kind: \"tenant_isolation\",\n target: table,\n operation: \"select\",\n passed: true,\n role: \"primary\",\n detail: `${viewerLabel} saw 0 of the ${actualRowCount} row(s) that exist in ${table} for ${scopeLabel}${criterionDetail}, which ${ownerLabel} can read (RLS honored)`,\n });\n\n return \"proved\";\n}\n\nasync function runActionProbe(\n probe: ActionProbe,\n actorCreds: { email: string; password: string; label: string },\n page: Page,\n): Promise<void> {\n const target = probe.target ?? `${probe.module}:${probe.name}`;\n const kind = probe.kind ?? \"authorization\";\n const result = await actAsUser.invokeAction(page, {\n as:\n actorCreds.label === \"seed:admin\"\n ? \"admin\"\n : actorCreds.label === \"seed:member\"\n ? \"member\"\n : { email: actorCreds.email, password: actorCreds.password },\n module: probe.module,\n action: probe.name,\n inputParams: probe.inputParams,\n });\n\n if (result.success) {\n recordAssertion({\n kind,\n target,\n operation: \"invoke\",\n passed: false,\n role: \"primary\",\n detail: `actor ${actorCreds.label} successfully invoked ${target}; expected action-layer rejection`,\n });\n throw proofFail(\n kind,\n `action \"${target}\" to return { success: false } for ${actorCreds.label}`,\n \"action returned { success: true }\",\n {\n suggestion: `Check withRBAC / role-check logic inside ${probe.module}/${probe.name}. The action should reject the actor.`,\n },\n );\n }\n\n if (\n probe.expectedErrorMatch &&\n !probe.expectedErrorMatch.test(result.error)\n ) {\n recordAssertion({\n kind,\n target,\n operation: \"invoke\",\n passed: false,\n role: \"primary\",\n detail: `action \"${target}\" rejected ${actorCreds.label} but with unexpected message: ${result.error}`,\n });\n throw proofFail(\n kind,\n `rejection message matching ${probe.expectedErrorMatch}`,\n `error: \"${result.error}\"`,\n {\n suggestion: `The action rejected the caller (good) but not for the expected reason. Confirm the proof is asserting on the right invariant.`,\n },\n );\n }\n\n recordAssertion({\n kind,\n target,\n operation: \"invoke\",\n passed: true,\n role: \"primary\",\n detail: `action \"${target}\" rejected ${actorCreds.label} with: ${result.error}`,\n });\n}\n\nasync function runSuccessfulActionProbe<TData>(\n opts: ActionSucceedsOptions,\n): Promise<TData> {\n const actorCreds = describeActor(opts.actor);\n const target =\n opts.action.target ?? `${opts.action.module}:${opts.action.name}`;\n const kind = opts.kind ?? opts.action.kind ?? \"happy_path\";\n const role = opts.role ?? \"control\";\n const result = await actAsUser.invokeAction<TData>(opts.page, {\n as:\n actorCreds.label === \"seed:admin\"\n ? \"admin\"\n : actorCreds.label === \"seed:member\"\n ? \"member\"\n : { email: actorCreds.email, password: actorCreds.password },\n module: opts.action.module,\n action: opts.action.name,\n inputParams: opts.action.inputParams,\n });\n\n if (!result.success) {\n recordAssertion({\n kind,\n target,\n operation: \"invoke\",\n passed: false,\n role,\n detail: `action \"${target}\" rejected ${actorCreds.label}; expected success: ${result.error}`,\n });\n throw proofFail(\n kind,\n `action \"${target}\" to return { success: true } for ${actorCreds.label}`,\n `action returned { success: false, error: \"${result.error}\" }`,\n {\n suggestion: `A refusal proof needs a working positive path. Check the action registry, validation input, and the permissions for ${actorCreds.label}.`,\n },\n );\n }\n\n recordAssertion({\n kind,\n target,\n operation: \"invoke\",\n passed: true,\n role,\n detail: `action \"${target}\" succeeded for ${actorCreds.label}`,\n });\n return result.data;\n}\n\nconst assertMethods = {\n /**\n * Prove that workspace-scoped data in `table` is invisible to members of a\n * different workspace. See file header for full semantics.\n *\n * Records two assertions per proved direction: a `control` showing the org's\n * own member CAN read the rows, and the `primary` showing the outsider\n * cannot. Only the `primary` can satisfy a mission's `trace_must_prove`\n * entry.\n *\n * Fails rather than passing vacuously when `setup()` leaves orgA empty.\n */\n async tenantIsolation(opts: TenantIsolationOptions): Promise<void> {\n const { table, page } = opts;\n const tag = opts.tag ?? randomSuffix();\n const scope = opts.scope ?? \"workspace\";\n const scopeColumn =\n opts.scopeColumn ?? (scope === \"user\" ? \"user_id\" : \"workspace_id\");\n validateTenantIsolationCriterion(table, scopeColumn, opts.criterion);\n\n const orgA = await seed.workspace(`Proof OrgA ${tag}`);\n const orgB = await seed.workspace(`Proof OrgB ${tag}`);\n\n const userA = await seed.user({\n email: `proof-a-${tag}@proof.test`,\n password: `ProofPass!${tag}1`,\n workspace: orgA,\n role: \"owner\",\n });\n const userB = await seed.user({\n email: `proof-b-${tag}@proof.test`,\n password: `ProofPass!${tag}1`,\n workspace: orgB,\n role: \"owner\",\n });\n\n const sb = createProofServiceClient();\n\n // Which column carries the tenant, and what each side's value is. Personal\n // data is scoped by user, workspace data by workspace; `workspaces` and\n // `users` carry their scope on the primary key, hence the override.\n const scopePattern =\n scope === \"user\"\n ? \"user_id = auth.uid()\"\n : \"workspace_id IN (SELECT get_user_workspace_ids(auth.uid()))\";\n const sideA = scope === \"user\" ? userA.id : orgA.id;\n const sideB = scope === \"user\" ? userB.id : orgB.id;\n const labelA = scope === \"user\" ? \"user A's rows\" : \"orgA\";\n const labelB = scope === \"user\" ? \"user B's rows\" : \"orgB\";\n\n try {\n const fixtureContext: ProofFixtureContext = {\n orgA,\n orgB,\n userA,\n userB,\n sb,\n };\n\n if (opts.fixture && opts.fixture.table !== table) {\n recordAssertion({\n kind: \"tenant_isolation\",\n target: table,\n operation: \"select\",\n passed: false,\n status: \"incomplete\",\n role: \"primary\",\n detail: `fixture for \"${opts.fixture.table}\" cannot seed proof target \"${table}\"`,\n });\n throw proofFail(\n \"fixture_factory_mismatch\",\n `the imported fixture factory to declare table \"${table}\"`,\n `factory declares \"${opts.fixture.table}\"`,\n {\n file: `e2e/fixtures/${table}.ts`,\n suggestion:\n `Import the factory owned by \"${table}\", or correct its table declaration. ` +\n `A copied factory must not silently seed one table while the proof probes another.`,\n },\n );\n }\n\n try {\n // Fixture factories and setup callbacks are executor-authored code\n // running inside this helper. Provenance is suspended around them so a\n // hostile callback calling recordAssertion cannot inherit this\n // helper's trusted stamp.\n if (opts.fixture) {\n await withoutAssertionProvenance(() =>\n opts.fixture!.create(fixtureContext),\n );\n } else {\n await withoutAssertionProvenance(() => opts.setup!(fixtureContext));\n }\n } catch (error) {\n if (!isProofFixturePendingError(error)) throw error;\n\n recordAssertion({\n kind: \"tenant_isolation\",\n target: table,\n operation: \"select\",\n passed: false,\n status: \"incomplete\",\n role: \"primary\",\n detail: `fixture factory incomplete: ${error.reason}`,\n });\n throw proofFail(\n error.code,\n `a completed fixture factory for \"${table}\"`,\n error.reason,\n {\n file: `e2e/fixtures/${table}.ts`,\n suggestion:\n `Replace pendingProofFixture(...) with defineProofFixture(...), using values valid for the final schema. ` +\n `Resolve required foreign keys from seeded/existing rows and honor NOT NULL, CHECK, domain, and enum constraints. ` +\n `Do not weaken a product constraint to make the proof easier to seed.`,\n },\n );\n }\n\n // ---- Primary direction: orgA holds the data, userB must not see it ---\n // `setup()` is contracted to populate orgA, so an empty orgA here means\n // the proof is measuring nothing. Fail loudly rather than record the\n // vacuous pass — a green \"tenant isolation holds\" on a table with no\n // rows is worse than no proof at all, because it gets believed.\n const forward = await probeTenantIsolation({\n table,\n viewer: userB,\n viewerLabel: \"user B\",\n owner: userA,\n ownerLabel: \"user A\",\n scopeColumn,\n scopeValue: sideA,\n scopeLabel: labelA,\n criterion: opts.criterion,\n scopePattern,\n sb,\n });\n\n if (forward === \"skipped_no_data\") {\n recordAssertion({\n kind: \"tenant_isolation\",\n target: table,\n operation: \"select\",\n passed: false,\n status: \"incomplete\",\n role: \"primary\",\n detail:\n `fixture setup created 0 rows in \"${table}\" where ${scopeColumn} = ${sideA}` +\n (opts.criterion\n ? ` matching planner criterion \"${opts.criterion.description}\" (${JSON.stringify(opts.criterion.where)})`\n : \"\"),\n });\n throw proofFail(\n \"tenant_isolation_vacuous\",\n `setup() to create at least one \"${table}\" row for ${labelA}${opts.criterion ? ` matching planner criterion \"${opts.criterion.description}\"` : \"\"} so there is something for user B to fail to see`,\n `0 rows in \"${table}\" matching ${JSON.stringify({\n [scopeColumn]: sideA,\n ...(opts.criterion?.where ?? {}),\n })} after setup() returned`,\n {\n suggestion:\n `An empty table cannot demonstrate isolation: user B seeing 0 rows would be indistinguishable from working RLS. ` +\n `Insert the fixture row(s) in e2e/fixtures/${table}.ts (or the inline setup) using the provided service-role client (\\`sb\\`), and make sure they carry ` +\n `${scopeColumn} = ${sideA}${opts.criterion ? ` and satisfy planner criterion \"${opts.criterion.description}\" (${JSON.stringify(opts.criterion.where)})` : \"\"}.`,\n },\n );\n }\n\n // ---- Reverse direction: only meaningful if the caller populated orgB -\n // Legitimately skipped for the common single-org setup. Skipping records\n // NOTHING, which is the point: the old behaviour recorded a passing\n // assertion for this probe even against an empty orgB, and that pass was\n // enough on its own to satisfy a mission's trace requirement.\n const reverse = await probeTenantIsolation({\n table,\n viewer: userA,\n viewerLabel: \"user A\",\n owner: userB,\n ownerLabel: \"user B\",\n scopeColumn,\n scopeValue: sideB,\n scopeLabel: labelB,\n criterion: opts.criterion,\n scopePattern,\n sb,\n });\n\n if (reverse === \"skipped_no_data\") {\n // Recorded, not just logged: a direction that was never measured is a\n // real state, and a consumer reading the trace must be able to see it\n // rather than inferring it from one fewer passing assertion.\n recordAssertion({\n kind: \"tenant_isolation\",\n target: table,\n operation: \"select\",\n passed: false,\n status: \"skipped\",\n role: \"primary\",\n detail:\n `reverse direction (A→B) not measured: setup() left ${labelB}${opts.criterion ? ` without rows matching planner criterion \"${opts.criterion.description}\"` : \" empty\"}, so there is nothing for user A to fail to see. ` +\n `Populate both sides in setup() to prove isolation in both directions.`,\n });\n console.log(\n `[proof] tenantIsolation(${table}): reverse probe (A→B) skipped — setup() left ${labelB}${opts.criterion ? ` without rows matching criterion \"${opts.criterion.description}\"` : \" empty\"}, ` +\n `so there is nothing for user A to fail to see. The A→B direction is unproven; ` +\n `populate it in setup() if you need it proven in both directions.`,\n );\n }\n\n // ---- Optional check: secondary UI assertion -----------------------\n if (page) {\n await actAsUser.loginAs(page, userB.email, userB.password);\n // Intentionally no navigation — callers add context-specific\n // `page.goto` + assertions in their own trace steps. The login\n // itself asserts the auth cookie roundtrip.\n }\n } finally {\n await teardown(orgA, orgB, userA, userB);\n }\n },\n\n /**\n * Prove that an operation is rejected when performed by an unauthorized\n * actor. Can probe either the RLS layer (direct Supabase client), the\n * action layer (via POST /api/proof/invoke-action), or both.\n *\n * RLS probes record `kind: \"authorization\"`. Action probes default to the\n * same kind but may set `action.kind` to the withProof invariant they\n * establish. The helper throws on the first probe that SUCCEEDS when it was\n * expected to fail — that's the PROOF_FAIL signal.\n *\n * Caller is responsible for seeding any fixture rows needed; this helper\n * creates no users or workspaces on its own.\n *\n * @example\n * // Action-layer: member cannot remove admin\n * await assert.authorization({\n * actor: { email: member.email, password: member.password },\n * action: {\n * module: \"workspace\",\n * name: \"removeMember\",\n * inputParams: { formData: { workspaceId, memberId } },\n * expectedErrorMatch: /permission|unauthorized/i,\n * },\n * page,\n * });\n *\n * @example\n * // RLS-layer: member cannot DELETE admin's workspace_members row\n * await assert.authorization({\n * actor: { email: member.email, password: member.password },\n * rls: {\n * table: \"workspace_members\",\n * op: \"delete\",\n * filter: { user_id: admin.id, workspace_id: orgA.id },\n * },\n * });\n */\n async authorization(opts: AuthorizationOptions): Promise<void> {\n if (!opts.rls && !opts.action) {\n throw new Error(\n \"[PROOF_FAIL] bad_options: assert.authorization requires at least one of `rls` or `action`\\n\" +\n \" file: src/playwright/assert.ts\\n\" +\n \" suggestion: Pass an `rls` probe for DB-layer denial or an `action` probe for action-layer denial (or both).\",\n );\n }\n if (opts.action && !opts.page) {\n throw new Error(\n \"[PROOF_FAIL] bad_options: assert.authorization's `action` probe requires a Playwright `page`\\n\" +\n \" file: src/playwright/assert.ts\\n\" +\n \" suggestion: Destructure `page` from the test callback and pass it in.\",\n );\n }\n\n const actorCreds = describeActor(opts.actor);\n\n if (opts.rls) {\n await runRlsProbe(opts.rls, actorCreds);\n }\n if (opts.action && opts.page) {\n await runActionProbe(opts.action, actorCreds, opts.page);\n }\n },\n\n /**\n * Prove that a registered action succeeds for an actor who should be allowed.\n *\n * This is the positive-control companion to action-level refusal probes.\n * Assertions default to `role: \"control\"`, so a working happy path cannot\n * satisfy the mission's primary requirement by itself. Override `role` only\n * when success is the primary invariant being proved.\n *\n * Returns the action's data so the caller can make domain-specific checks.\n */\n async actionSucceeds<TData = unknown>(\n opts: ActionSucceedsOptions,\n ): Promise<TData> {\n return runSuccessfulActionProbe<TData>(opts);\n },\n\n /**\n * Assert on an HTTP response from a page route. Streaming-RSC aware: when\n * only body markers (`mustContain` / `mustNotContain`) are provided, the\n * helper does NOT check the status code — crucial for routes guarded by\n * a server component's `notFound()` call that Next.js can only signal via\n * an embedded NOT_FOUND marker because headers were already committed as\n * 200 by `loading.tsx`. See\n * [proof-authoring.mdc](mdc:.cursor/rules/proof-authoring.mdc) for the\n * streaming-RSC gotcha in full.\n *\n * Defaults:\n * - method: \"GET\"\n * - maxRedirects: 0 (intentional — silent redirect-following has caused\n * real false passes on auth-gated pages)\n *\n * Records one TraceAssertion with caller-provided `kind` and `target`.\n * Returns the raw Playwright APIResponse so callers can run additional\n * custom checks (headers, etc.) after.\n *\n * @example\n * // Streaming-RSC page: assert body markers, not status code\n * await assert.httpResponse({\n * page,\n * path: \"/secret-admin\",\n * as: \"member\",\n * kind: \"authorization\",\n * target: \"/secret-admin\",\n * expect: {\n * mustContain: [/Sorry, we could not find this page/i],\n * mustNotContain: [/Here you can see what.?s happening on your platform/i],\n * },\n * });\n *\n * @example\n * // Flat API route: assert status\n * await assert.httpResponse({\n * page,\n * path: \"/api/public-ping\",\n * kind: \"happy_path\",\n * expect: { status: 200 },\n * });\n */\n async httpResponse(opts: {\n page: Page;\n path: string;\n kind: ProofKind;\n target?: string;\n /**\n * Evidential role of the recorded assertion. Defaults to `\"primary\"`.\n * Pass `\"control\"` when this request exists only to show the probe could\n * have failed — e.g. \"the admin CAN reach the page a member was refused\" —\n * so it cannot satisfy a mission's `trace_must_prove` entry by itself.\n */\n role?: AssertionRole;\n as?: \"admin\" | \"member\" | { email: string; password: string };\n method?: \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\";\n expect: {\n status?: number;\n mustContain?: RegExp[];\n mustNotContain?: RegExp[];\n };\n }): Promise<APIResponse> {\n const target = opts.target ?? opts.path;\n const method = opts.method ?? \"GET\";\n const role: AssertionRole = opts.role ?? \"primary\";\n\n if (opts.as !== undefined) {\n if (typeof opts.as === \"string\") {\n await actAsUser.login(opts.page, opts.as);\n } else {\n await actAsUser.loginAs(opts.page, opts.as.email, opts.as.password);\n }\n }\n\n const requestCtx = opts.page.request;\n const requestOpts = { maxRedirects: 0 } as const;\n\n let response: APIResponse;\n switch (method) {\n case \"GET\":\n response = await requestCtx.get(opts.path, requestOpts);\n break;\n case \"POST\":\n response = await requestCtx.post(opts.path, requestOpts);\n break;\n case \"PUT\":\n response = await requestCtx.put(opts.path, requestOpts);\n break;\n case \"PATCH\":\n response = await requestCtx.patch(opts.path, requestOpts);\n break;\n case \"DELETE\":\n response = await requestCtx.delete(opts.path, requestOpts);\n break;\n }\n\n const status = response.status();\n let body: string | null = null;\n\n const failures: string[] = [];\n\n if (opts.expect.status !== undefined && status !== opts.expect.status) {\n failures.push(\n `status mismatch: expected ${opts.expect.status}, got ${status}`,\n );\n }\n\n if (\n (opts.expect.mustContain && opts.expect.mustContain.length > 0) ||\n (opts.expect.mustNotContain && opts.expect.mustNotContain.length > 0)\n ) {\n body = await response.text();\n\n for (const re of opts.expect.mustContain ?? []) {\n if (!re.test(body)) {\n failures.push(`mustContain not matched: ${re}`);\n }\n }\n for (const re of opts.expect.mustNotContain ?? []) {\n if (re.test(body)) {\n failures.push(\n `mustNotContain matched (should have been absent): ${re}`,\n );\n }\n }\n }\n\n if (failures.length > 0) {\n recordAssertion({\n kind: opts.kind,\n target,\n operation: \"request\",\n passed: false,\n role,\n detail: `${method} ${opts.path} -> ${status}; failures: ${failures.join(\"; \")}`,\n });\n throw new Error(\n `[PROOF_FAIL] ${opts.kind}: response from ${method} ${opts.path} did not match expectations\\n` +\n ` status: ${status}\\n` +\n ` failures:\\n${failures.map((f) => ` - ${f}`).join(\"\\n\")}\\n` +\n ` file: src/playwright/assert.ts\\n` +\n ` suggestion: ${opts.expect.mustContain || opts.expect.mustNotContain ? \"For streaming RSC routes, assert on body markers rather than status. See .cursor/rules/proof-authoring.mdc.\" : \"Check the route handler or page component and confirm the expected response shape.\"}`,\n );\n }\n\n recordAssertion({\n kind: opts.kind,\n target,\n operation: \"request\",\n passed: true,\n role,\n detail: `${method} ${opts.path} -> ${status}${opts.expect.mustContain || opts.expect.mustNotContain ? \" (body markers matched)\" : \"\"}`,\n });\n\n return response;\n },\n};\n\n/**\n * Wrap a helper so every assertion it records carries `emittedBy: name`.\n *\n * The stamp travels through `withAssertionProvenance`'s AsyncLocalStorage, so\n * it reaches the shared probe internals (`runRlsProbe`, `runActionProbe`, ...)\n * without threading a name through thirty call sites — and it cannot be set\n * from spec code, because the public `recordAssertion` strips caller-supplied\n * `emittedBy` and `withAssertionProvenance` is not exported from the package\n * entry point.\n */\nfunction withHelperProvenance<TArgs extends unknown[], TResult>(\n name: string,\n fn: (...args: TArgs) => Promise<TResult>,\n): (...args: TArgs) => Promise<TResult> {\n return (...args: TArgs) => withAssertionProvenance(name, () => fn(...args));\n}\n\nexport const assert = {\n tenantIsolation: withHelperProvenance(\n \"assert.tenantIsolation\",\n assertMethods.tenantIsolation,\n ),\n authorization: withHelperProvenance(\n \"assert.authorization\",\n assertMethods.authorization,\n ),\n // Cast preserves the <TData> generic that the wrapper's closed-over\n // signature would otherwise collapse to `unknown`.\n actionSucceeds: withHelperProvenance(\n \"assert.actionSucceeds\",\n assertMethods.actionSucceeds,\n ) as typeof assertMethods.actionSucceeds,\n httpResponse: withHelperProvenance(\n \"assert.httpResponse\",\n assertMethods.httpResponse,\n ),\n};\n"],"mappings":"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","names":["sleep","cached","createClient","createClient","error","status","before","after"]}
|
|
1
|
+
{"version":3,"sources":["../src/playwright/actAsUser.ts","../src/server/transient.ts","../src/server/auth-rate-limit.ts","../src/playwright/trace.ts","../src/server/code-provenance.ts","../src/shared/trace-types.ts","../src/server/service-client.ts","../src/server/seed.ts","../src/server/fixture.ts","../src/playwright/assert.ts"],"sourcesContent":["// Import External Packages\nimport type { Page } from \"@playwright/test\";\nimport { createClient, type SupabaseClient } from \"@supabase/supabase-js\";\n// Import Local Imports\nimport { createRetryingFetch, retryTransient } from \"../server/transient\";\nimport {\n isAuthRateLimited,\n retryAuthRateLimit,\n} from \"../server/auth-rate-limit\";\nimport type { ActionResult } from \"../shared/action-types\";\n// Import Core Dependencies\n// Import Shared Dependencies\n// Import Extension Dependencies\n\n// ---------------------------------------------------------------------------\n// actAsUser\n//\n// Two surfaces for \"become a user\" inside a proof spec:\n//\n// 1. `actAsUser.login(page, role)` / `actAsUser.loginAs(page, email, pw)`\n// — log a Playwright browser context into Supabase via\n// `POST /api/proof/login-as-seed-user`. Use when the proof drives the\n// UI: cookies are written into `page.request`'s shared jar and Next.js\n// sees the session after a `page.reload()`.\n//\n// 2. `actAsUser.supabaseClient({ email, password })`\n// — return a *fresh* Supabase client authenticated as the given user, no\n// cookies involved. Use when the proof needs to query / mutate the DB\n// with the user's real RLS privileges, entirely outside the browser\n// (e.g. \"prove user B's DELETE against user A's row is filtered by\n// RLS\"). This is the primitive behind `assert.tenantIsolation`'s\n// primary check.\n//\n// A browser logged in as a disposable proof user must call\n// `actAsUser.logout(page)` before deleting that user. It stops trailing page\n// requests first, clears the shared browser/request cookie jar, then allows\n// already-accepted server work to drain so no validly-signed JWT can outlive\n// the auth row named by its `sub` claim.\n// ---------------------------------------------------------------------------\n\ninterface LoginOkResponse {\n ok: true;\n userId: string | null;\n email: string;\n}\n\ninterface LoginErrorResponse {\n error: string;\n suggestion: string;\n}\n\ninterface ActionInvokeErrorResponse {\n success: false;\n error: string;\n suggestion?: string;\n}\n\nfunction isActionResult<TData>(body: unknown): body is ActionResult<TData> {\n if (typeof body !== \"object\" || body === null) return false;\n const b = body as { success?: unknown; data?: unknown; error?: unknown };\n if (b.success === true) return \"data\" in b;\n if (b.success === false) return typeof b.error === \"string\";\n return false;\n}\n\nfunction getProofSecret(): string {\n const secret = process.env.API_SECRET_KEY;\n if (!secret) {\n throw new Error(\n \"[PROOF_FAIL] proof_secret_missing: API_SECRET_KEY is not set in the test runner env\\n\" +\n \" file: src/playwright/actAsUser.ts\\n\" +\n \" suggestion: Run setup-local-env.sh or ensure .env.local is loaded. Playwright tests need API_SECRET_KEY to sign proof requests.\",\n );\n }\n return secret;\n}\n\nasync function callLoginRoute(\n page: Page,\n payload: { role: \"admin\" | \"member\" } | { email: string; password: string },\n): Promise<void> {\n // Retried on 5xx only: the route talks to Supabase, so a gateway blip arrives\n // as a server error, while a 401 is the auth system answering and must be\n // reported as-is.\n const { response } = await retryAuthRateLimit(\n \"proof login route\",\n () =>\n retryTransient(\"proof login route\", async () => {\n const res = await page.request.post(\"/api/proof/login-as-seed-user\", {\n headers: {\n \"x-proof-secret\": getProofSecret(),\n \"Content-Type\": \"application/json\",\n },\n data: payload,\n });\n return {\n response: res,\n status: res.status(),\n error: res.ok()\n ? null\n : { message: res.statusText(), status: res.status() },\n };\n }),\n (result) => ({\n limited: result.status === 429,\n retryAfter:\n typeof result.response.headers === \"function\"\n ? result.response.headers()[\"retry-after\"]\n : undefined,\n }),\n );\n\n if (!response.ok()) {\n let body: Partial<LoginErrorResponse> = {};\n try {\n body = (await response.json()) as LoginErrorResponse;\n } catch {\n // Non-JSON error (network / HTML error page). Fall through.\n }\n const label = \"email\" in payload ? payload.email : `role=${payload.role}`;\n const category = isAuthRateLimited(\n { message: body.error, status: response.status() },\n response.status(),\n body.error,\n )\n ? \"auth_rate_limited\"\n : \"auth_signin\";\n throw new Error(\n `[PROOF_FAIL] ${category}: login failed for ${label} (status ${response.status()})\\n` +\n ` file: src/playwright/actAsUser.ts\\n` +\n ` suggestion: ${body.suggestion ?? body.error ?? \"Check that the dev server is running and seed users exist.\"}`,\n );\n }\n\n // Validate shape just enough to surface parse errors early.\n const json = (await response.json()) as LoginOkResponse;\n if (!json.ok) {\n throw new Error(\n \"[PROOF_FAIL] auth_signin: login route returned ok:false\\n\" +\n \" file: src/playwright/actAsUser.ts\\n\" +\n \" suggestion: Inspect the response body of /api/proof/login-as-seed-user.\",\n );\n }\n\n // If the page is already somewhere, reload so Next.js picks up the cookies.\n if (page.url() !== \"about:blank\") {\n await page.reload();\n }\n}\n\nexport const actAsUser = {\n /**\n * Log in as one of the seeded development users (`admin` or `member`).\n * The consumer application's proof login route resolves the role to its\n * configured server-only seed credentials.\n */\n async login(page: Page, role: \"admin\" | \"member\"): Promise<void> {\n await callLoginRoute(page, { role });\n },\n\n /**\n * Log in as an arbitrary user — typically one created by `seed.user()`\n * inside a `.proof.ts` spec. Used by `assert.tenantIsolation` and any\n * spec that creates fresh users per run.\n */\n async loginAs(page: Page, email: string, password: string): Promise<void> {\n await callLoginRoute(page, { email, password });\n },\n\n /**\n * Stop requests from the current document, clear the browser context's\n * shared cookie jar, and allow already-accepted server work to drain. Call\n * before deleting a disposable user that authenticated this page; otherwise\n * router prefetches or streaming continuations can keep presenting that\n * user's JWT after its auth row is gone.\n */\n async logout(page: Page): Promise<void> {\n const context = page.context();\n if (!page.isClosed()) {\n await page.goto(\"about:blank\");\n }\n await context.clearCookies();\n if (!page.isClosed()) {\n // Navigation cancels browser work, but a request already accepted by\n // the application server can finish after that cancellation. Keep the\n // disposable auth row alive for one quiet-window before callers delete\n // it, matching Playwright's network-idle settling interval.\n await page.waitForTimeout(500);\n }\n },\n\n /**\n * Invoke a server action end-to-end while authenticated as the given user.\n *\n * Unlike `actAsUser.supabaseClient` — which goes directly to the DB and\n * therefore only proves RLS — this helper goes through the full Next.js\n * request lifecycle: route handler → middleware pipeline → `withAuth`,\n * `withTenantIsolation`, the action's own logic, and any Zod validation.\n * It is the primitive you reach for when proving action-layer invariants\n * (RBAC rejection, validation errors, business-logic branching) that RLS\n * alone cannot cover.\n *\n * The action must be listed in\n * the application's generated Proof action registry. Unregistered actions\n * return a 404 with a structured [PROOF_FAIL] message.\n *\n * Authentication:\n * - `as: \"admin\" | \"member\"` — uses the seeded dev users\n * - `as: { email, password }` — uses arbitrary credentials (typically a\n * user created by `seed.user()` earlier in the proof)\n *\n * A registered action returns an `ActionResult<TData>`. Harness failures\n * (missing registry entry, malformed request, action escaping the pipeline)\n * throw instead: absence or breakage of the thing under test must never\n * satisfy a refusal proof.\n *\n * @example\n * const result = await actAsUser.invokeAction<{ removed: boolean }>(\n * page,\n * {\n * as: \"member\",\n * module: \"workspace\",\n * action: \"removeMember\",\n * inputParams: { formData: { workspaceId, memberId } },\n * },\n * );\n * expect(result.success, \"[PROOF_FAIL] authorization: ...\").toBe(false);\n */\n async invokeAction<TData = unknown>(\n page: Page,\n opts: {\n as: \"admin\" | \"member\" | { email: string; password: string };\n module: string;\n action: string;\n inputParams: Record<string, unknown>;\n },\n ): Promise<ActionResult<TData>> {\n if (typeof opts.as === \"string\") {\n await actAsUser.login(page, opts.as);\n } else {\n await actAsUser.loginAs(page, opts.as.email, opts.as.password);\n }\n\n const response = await page.request.post(\"/api/proof/invoke-action\", {\n headers: {\n \"x-proof-secret\": getProofSecret(),\n \"Content-Type\": \"application/json\",\n },\n data: {\n module: opts.module,\n action: opts.action,\n inputParams: opts.inputParams,\n },\n });\n\n let body: unknown;\n try {\n body = await response.json();\n } catch {\n throw new Error(\n `[PROOF_FAIL] action_invoke: response from /api/proof/invoke-action is not valid JSON (status ${response.status()})\\n` +\n \" file: src/playwright/actAsUser.ts\\n\" +\n \" suggestion: The dev server likely returned an error page. Check server logs and confirm the route file at app/api/proof/invoke-action/route.ts is registered.\",\n );\n }\n\n if (!response.ok()) {\n const harnessError = body as Partial<ActionInvokeErrorResponse>;\n throw new Error(\n `${typeof harnessError.error === \"string\" ? harnessError.error : `[PROOF_FAIL] action_invoke: harness request failed (status ${response.status()})`}\\n` +\n \" file: src/playwright/actAsUser.ts\\n\" +\n ` suggestion: ${harnessError.suggestion ?? \"Inspect /api/proof/invoke-action and confirm the action is registered and pipeline-wrapped.\"}`,\n );\n }\n\n if (!isActionResult<TData>(body)) {\n throw new Error(\n `[PROOF_FAIL] action_invoke: response body is not a valid ActionResult shape (status ${response.status()})\\n` +\n ` file: src/playwright/actAsUser.ts\\n` +\n ` body: ${JSON.stringify(body)}\\n` +\n \" suggestion: The invoke route must always return { success, data } or { success: false, error }. Confirm PROOF_ACTION_REGISTRY contains the action and that it uses createAction().\",\n );\n }\n\n return body;\n },\n\n /**\n * Return a Supabase client authenticated as the given user via\n * `signInWithPassword` against a *fresh* client (no cookies, no session\n * persistence). Queries made through the returned client run with that\n * user's RLS privileges.\n *\n * Use for server-side proofs where the browser is irrelevant and you want\n * direct evidence of whether the DB allows or denies a specific operation\n * for a specific user. This is the primitive behind the primary check in\n * `assert.tenantIsolation` and is intentionally exposed for specs that\n * need finer-grained RLS probes.\n */\n async supabaseClient(opts: {\n email: string;\n password: string;\n }): Promise<SupabaseClient> {\n const url = process.env.NEXT_PUBLIC_SUPABASE_URL;\n const publishableKey = process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY;\n\n if (!url || !publishableKey) {\n throw new Error(\n \"[PROOF_FAIL] config_missing: expected NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY to be set, found one or both missing\\n\" +\n \" file: src/playwright/actAsUser.ts\\n\" +\n \" suggestion: Run setup-local-env.sh or load .env.local before running proof specs.\",\n );\n }\n\n const client = createClient(url, publishableKey, {\n auth: {\n persistSession: false,\n autoRefreshToken: false,\n detectSessionInUrl: false,\n },\n // Gateway 502s are retried at the transport, so every probe written\n // against this client — including ad-hoc ones in specs — is covered.\n global: { fetch: createRetryingFetch(`request as ${opts.email}`) },\n });\n\n const { error } = await retryAuthRateLimit(\n `signInWithPassword as ${opts.email}`,\n () =>\n client.auth.signInWithPassword({\n email: opts.email,\n password: opts.password,\n }),\n (result) => ({\n limited: isAuthRateLimited(result.error, result.error?.status),\n }),\n );\n if (error) {\n const category = isAuthRateLimited(error, error.status)\n ? \"auth_rate_limited\"\n : \"auth_signin\";\n throw new Error(\n `[PROOF_FAIL] ${category}: expected successful sign-in for ${opts.email}, found signInWithPassword error: ${error.message}\\n` +\n \" file: src/playwright/actAsUser.ts\\n\" +\n ` suggestion: ${\n category === \"auth_rate_limited\"\n ? \"The auth budget is exhausted, not the credentials. Wait for the window to reset or reduce parallel proof workers.\"\n : \"Confirm the user was created by seed.user() and that password/email match.\"\n }`,\n );\n }\n\n return client;\n },\n};\n","// Import External Packages\n// Import Local Imports\n// Import Core Dependencies\n// Import Shared Dependencies\n// Import Extension Dependencies\n\n// ---------------------------------------------------------------------------\n// Transient-failure retry\n//\n// Local Supabase sits behind Kong. During a proof run — every spec seeding its\n// own fixtures through the service role, plus the dev server's own queries — the\n// gateway intermittently answers a request with a 502 whose body reads \"An\n// invalid response was received from the upstream server\", then serves the next\n// request normally. Roughly one local `proof:check` in two hit this, always on a\n// different spec.\n//\n// That noise is not evidence about the code under test, and it can hurt in two\n// ways:\n//\n// 1. A spurious red build. Worse than it sounds: a suite that fails half the\n// time for reasons nobody controls teaches people to re-run until green,\n// which is exactly the reflex that lets a real failure through.\n// 2. A false pass. Several probes read \"the actor's write returned an error and\n// no row landed\" as proof of denial. A write that never reached PostgREST\n// looks identical, so an unretried gateway error can certify an invariant\n// that was never exercised. Callers use `isTransient` to refuse that\n// reading.\n//\n// The retry lives at the transport (`createRetryingFetch`, installed on every\n// Supabase client the SDK hands out) rather than at each call site, because the\n// call sites that matter most are the ad-hoc queries inside `.proof.ts` specs —\n// there is no version of \"remember to wrap it\" that survives the next spec\n// somebody writes.\n//\n// It is bounded, announced on stdout, and deliberately narrow: only gateway\n// statuses (502/503/504) and transport-level failures qualify. A 4xx is the\n// database answering, and even a 500 is usually PostgREST or a route handler\n// answering; retrying either would turn a real failure into a slow real failure,\n// or worse, hide it.\n// ---------------------------------------------------------------------------\n\n/** The shape both `PostgrestError` and `AuthError` satisfy. */\nexport interface TransientErrorLike {\n message?: string | null;\n status?: number | null;\n code?: string | null;\n}\n\n/**\n * Messages that mean \"the request did not reach the database\", matched because\n * Supabase surfaces gateway and transport failures as error text without a\n * usable status on every client path.\n */\nconst TRANSIENT_MESSAGES = [\n /invalid response was received from the upstream server/i,\n /upstream connect error/i,\n /(service|server) (is )?temporarily unavailable/i,\n /bad gateway/i,\n /gateway time-?out/i,\n /fetch failed/i,\n /network( request)? failed/i,\n /socket hang up/i,\n /ECONNRESET|ECONNREFUSED|EPIPE|ETIMEDOUT|EAI_AGAIN|UND_ERR/,\n];\n\n/** Node wraps the real cause; the useful text is often one or two levels down. */\nfunction messageChain(error: unknown, depth = 0): string {\n if (!error || depth > 3) return \"\";\n if (typeof error === \"string\") return error;\n const record = error as { message?: unknown; cause?: unknown };\n const own = typeof record.message === \"string\" ? record.message : \"\";\n return `${own} ${messageChain(record.cause, depth + 1)}`;\n}\n\n/**\n * Statuses the gateway returns when it could not get an answer out of the\n * upstream service. A 500 is excluded on purpose: PostgREST and our own route\n * handlers use it to report real failures.\n */\nexport const GATEWAY_STATUSES = new Set([502, 503, 504]);\n\n/**\n * True when a failure is infrastructure, not an answer.\n *\n * `status` is the HTTP status when the caller has it separately from the error\n * (PostgREST responses carry it alongside `error`; auth errors carry it inside).\n */\nexport function isTransient(\n error: TransientErrorLike | null | undefined,\n status?: number | null,\n): boolean {\n if (!error) return false;\n const httpStatus = status ?? error.status ?? null;\n if (typeof httpStatus === \"number\") {\n if (GATEWAY_STATUSES.has(httpStatus)) return true;\n // Any other status is the stack answering — a denial, a validation error, an\n // internal error — and each is a fact the proof is entitled to act on.\n if (httpStatus >= 400) return false;\n }\n const text = messageChain(error);\n return TRANSIENT_MESSAGES.some((pattern) => pattern.test(text));\n}\n\nexport const TRANSIENT_MAX_ATTEMPTS = 4;\nconst BASE_DELAY_MS = 150;\n\nfunction backoffMs(attempt: number): number {\n return BASE_DELAY_MS * 2 ** (attempt - 1);\n}\n\nfunction announce(\n label: string,\n attempt: number,\n maxAttempts: number,\n reason: string,\n delayMs: number,\n): void {\n // Announced rather than silent: a suite that only goes green on its third\n // attempt every time is a real problem, and hidden retries hide it.\n console.log(\n `[proof:retry] ${label} failed transiently (attempt ${attempt}/${maxAttempts}): ` +\n `${reason} — retrying in ${delayMs}ms`,\n );\n}\n\nconst sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * A `fetch` that retries gateway failures, for installing on a Supabase client\n * via `createClient(url, key, { global: { fetch } })`.\n *\n * Covers every request that client makes — PostgREST queries, auth admin calls,\n * RPC — including the ad-hoc ones written inside proof specs, which is the whole\n * reason the retry lives here instead of at the call sites.\n *\n * Retrying a write is safe here only because gateway failures mean the request\n * did not reach PostgREST; a request carrying a stream body is never retried,\n * since it cannot be replayed and we cannot know how far it got.\n */\nexport function createRetryingFetch(\n label: string,\n baseFetch: typeof fetch = fetch,\n { maxAttempts = TRANSIENT_MAX_ATTEMPTS } = {},\n): typeof fetch {\n return async (input, init) => {\n const replayable =\n init?.body == null ||\n typeof init.body === \"string\" ||\n init.body instanceof URLSearchParams;\n\n for (let attempt = 1; ; attempt++) {\n const lastAttempt = attempt >= maxAttempts || !replayable;\n try {\n const response = await baseFetch(input, init);\n if (lastAttempt || !GATEWAY_STATUSES.has(response.status)) {\n return response;\n }\n const delayMs = backoffMs(attempt);\n announce(\n label,\n attempt,\n maxAttempts,\n `HTTP ${response.status}`,\n delayMs,\n );\n await sleep(delayMs);\n } catch (error) {\n if (lastAttempt || !isTransient(error as TransientErrorLike))\n throw error;\n const delayMs = backoffMs(attempt);\n announce(\n label,\n attempt,\n maxAttempts,\n messageChain(error).trim() || \"transport failure\",\n delayMs,\n );\n await sleep(delayMs);\n }\n }\n };\n}\n\n/**\n * Retry a call that reports failure in its result rather than by throwing.\n *\n * For anything going through a Supabase client, prefer `createRetryingFetch` —\n * it is already installed. This is for the surfaces that do not use it, such as\n * Playwright's `APIRequestContext`.\n *\n * `run` must build the request from scratch on each call: a PostgREST builder is\n * a one-shot thenable, so re-awaiting the same object does not re-issue it.\n *\n * The final result is returned as-is — including a still-failing error — because\n * the caller owns the diagnostics. Nothing here throws, and nothing here decides\n * what a failure means.\n */\nexport async function retryTransient<\n T extends { error?: TransientErrorLike | null; status?: number },\n>(\n label: string,\n // `PromiseLike`, not `Promise`: a PostgREST builder is a thenable, and typing\n // this as `Promise<T>` makes inference fall back to the constraint, which\n // erases `data` and `count` from the caller's result.\n run: () => PromiseLike<T>,\n { maxAttempts = TRANSIENT_MAX_ATTEMPTS } = {},\n): Promise<T> {\n let result = await run();\n\n for (let attempt = 1; attempt < maxAttempts; attempt++) {\n if (!isTransient(result.error, result.status)) return result;\n const delayMs = backoffMs(attempt);\n announce(\n label,\n attempt,\n maxAttempts,\n result.error?.message ?? \"unknown error\",\n delayMs,\n );\n await sleep(delayMs);\n result = await run();\n }\n\n return result;\n}\n","// ---------------------------------------------------------------------------\n// Bounded authentication rate-limit retry.\n//\n// HTTP 429 is neither a product verdict nor a transient gateway failure. Proof\n// login may retry it briefly, honoring Retry-After within a strict budget; once\n// exhausted it remains an explicit auth_rate_limited infrastructure diagnosis.\n// ---------------------------------------------------------------------------\n\nexport interface AuthRateLimitErrorLike {\n message?: string | null;\n status?: number | null;\n}\n\nexport const AUTH_RATE_LIMIT_MAX_ATTEMPTS = 3;\nexport const AUTH_RATE_LIMIT_MAX_DELAY_MS = 5_000;\nexport const AUTH_RATE_LIMIT_MAX_TOTAL_WAIT_MS = 10_000;\nconst AUTH_RATE_LIMIT_FALLBACK_DELAY_MS = 1_000;\n\nexport function isAuthRateLimited(\n error: AuthRateLimitErrorLike | null | undefined,\n status?: number | null,\n body?: string | null,\n): boolean {\n const httpStatus = status ?? error?.status ?? null;\n if (httpStatus === 429) return true;\n if (typeof httpStatus === \"number\" && httpStatus >= 400) return false;\n\n const text = `${error?.message ?? \"\"} ${body ?? \"\"}`;\n return (\n /\\[PROOF_FAIL\\]\\s*(auth_)?rate_limited:/i.test(text) ||\n /\\b(rate limit|too many (requests|attempts|sign-?ins?))\\b/i.test(text)\n );\n}\n\nexport function parseRetryAfterMs(\n value: string | number | null | undefined,\n now = Date.now(),\n): number | null {\n if (typeof value === \"number\" && Number.isFinite(value)) {\n return Math.max(0, value * 1_000);\n }\n if (typeof value !== \"string\" || !value.trim()) return null;\n\n const seconds = Number(value);\n if (Number.isFinite(seconds)) return Math.max(0, seconds * 1_000);\n\n const at = Date.parse(value);\n return Number.isFinite(at) ? Math.max(0, at - now) : null;\n}\n\nexport function clampAuthRateLimitDelay(\n retryAfterMs: number | null,\n remainingBudgetMs = AUTH_RATE_LIMIT_MAX_TOTAL_WAIT_MS,\n): number {\n const requested = retryAfterMs ?? AUTH_RATE_LIMIT_FALLBACK_DELAY_MS;\n return Math.max(\n 0,\n Math.min(requested, AUTH_RATE_LIMIT_MAX_DELAY_MS, remainingBudgetMs),\n );\n}\n\nexport async function retryAuthRateLimit<T>(\n label: string,\n run: () => PromiseLike<T>,\n classify: (result: T) => {\n limited: boolean;\n retryAfter?: string | number | null;\n },\n {\n maxAttempts = AUTH_RATE_LIMIT_MAX_ATTEMPTS,\n maxTotalWaitMs = AUTH_RATE_LIMIT_MAX_TOTAL_WAIT_MS,\n sleep = (ms: number) =>\n new Promise<void>((resolve) => setTimeout(resolve, ms)),\n } = {},\n): Promise<T> {\n let result = await run();\n let waitedMs = 0;\n\n for (let attempt = 1; attempt < maxAttempts; attempt += 1) {\n const signal = classify(result);\n if (!signal.limited) return result;\n\n const remaining = maxTotalWaitMs - waitedMs;\n if (remaining <= 0) return result;\n const delayMs = clampAuthRateLimitDelay(\n parseRetryAfterMs(signal.retryAfter),\n remaining,\n );\n if (delayMs <= 0) return result;\n\n console.log(\n `[proof:retry] ${label} auth rate limited (attempt ${attempt}/${maxAttempts}) — retrying in ${delayMs}ms`,\n );\n await sleep(delayMs);\n waitedMs += delayMs;\n result = await run();\n }\n\n return result;\n}\n","// Import External Packages\nimport * as crypto from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { test } from \"@playwright/test\";\n// Import Local Imports\nimport { codeProvenance } from \"../server/code-provenance\";\nimport {\n TRACE_ARTIFACT_SCHEMA_VERSION,\n normalizeAssertionStatus,\n type ProofOptions,\n type StepOptions,\n type StepResult,\n type TraceArtifact,\n type TraceAssertion,\n type TraceStep,\n} from \"../shared/trace-types\";\n// Import Core Dependencies\n// Import Shared Dependencies\n// Import Extension Dependencies\n\n// ---------------------------------------------------------------------------\n// trace.proof / t.step\n//\n// A proof is a single run that answers \"did we observe the intended\n// behavior?\" — one happy_path, one tenant_isolation check, one authorization\n// probe, etc. Each step inside the proof records an intent, a kind (from the\n// closed vocabulary), and what the agent actually observed. The resulting\n// JSON artifact is written to `.proof/traces/<proofId>.json` and — in v1 —\n// consumed by the mission-manifest validator, which groups proofs by their\n// optional `missionId` and confirms every `trace_must_prove` requirement has\n// a matching passing step across the group.\n//\n// Write path in v0.5 is hardcoded to `<cwd>/.proof/traces/`. Playwright runs\n// from the repo root so this resolves to `<repo>/.proof/traces/`.\n// ---------------------------------------------------------------------------\n\nconst TRACE_DIR_RELATIVE = [\".proof\", \"traces\"] as const;\n\n/**\n * AsyncLocalStorage used to pipe `recordAssertion(...)` calls from inside\n * helper code (e.g. assert.tenantIsolation) to the step that wraps them,\n * without requiring every helper to take a recorder argument.\n */\nconst currentStepAssertions = new AsyncLocalStorage<TraceAssertion[]>();\n\n/**\n * AsyncLocalStorage naming the `assert.*` helper currently executing. Set only\n * by `withAssertionProvenance`, which is deliberately NOT re-exported from the\n * package entry point (`./index`): the public `recordAssertion` strips any\n * caller-supplied `emittedBy`, so the only way an assertion carries helper\n * provenance is by actually being recorded while a helper runs. That is what\n * lets the mission validator treat `emittedBy` as evidence of origin rather\n * than as a string a spec happened to write.\n */\nconst currentHelperProvenance = new AsyncLocalStorage<string>();\n\n/**\n * Run `fn` with helper provenance attached: every assertion recorded inside it\n * is stamped `emittedBy: helper`. Internal to the SDK — imported directly by\n * `./assert`, never exported from `./index`. Note that provenance spans the\n * whole helper call, including any user-supplied callbacks the helper invokes.\n */\nexport async function withAssertionProvenance<T>(\n helper: string,\n fn: () => Promise<T>,\n): Promise<T> {\n return currentHelperProvenance.run(helper, fn);\n}\n\n/**\n * Run `fn` with helper provenance suspended. Helpers wrap every user-supplied\n * callback (`fixture.create`, `setup`, ...) in this, because those callbacks\n * are executor-authored code executing inside the helper's context — without\n * the suspension, a hostile fixture could call `recordAssertion` and receive\n * the helper's trusted stamp. Internal to the SDK, like its counterpart.\n */\nexport async function withoutAssertionProvenance<T>(\n fn: () => Promise<T>,\n): Promise<T> {\n return currentHelperProvenance.exit(fn);\n}\n\n/**\n * Called by `assert.*` helpers while running inside a step. If no step is\n * active (e.g. someone invokes a helper outside `trace.proof`), this is a\n * no-op so the helper still runs without crashing.\n *\n * Spec code may call this directly to record bespoke assertions; those are\n * recorded WITHOUT `emittedBy` (any supplied value is stripped), which marks\n * them as spec-authored claims rather than SDK-verified probes.\n */\nexport function recordAssertion(assertion: TraceAssertion): void {\n const bucket = currentStepAssertions.getStore();\n if (!bucket) return;\n // Provenance comes from the execution context, never from the caller: a\n // spec claiming `emittedBy: \"assert.tenantIsolation\"` on a hand-rolled\n // assertion must not be able to impersonate the helper.\n const { emittedBy: _claimed, ...rest } = assertion;\n const emittedBy = currentHelperProvenance.getStore();\n // Materialize `status` at write time rather than leaving consumers to derive\n // it: the artifact should say what it means without a decoder ring.\n bucket.push({\n ...rest,\n ...(emittedBy ? { emittedBy } : {}),\n status: normalizeAssertionStatus(assertion.status, assertion.passed),\n });\n}\n\nclass TraceRecorderImpl {\n private readonly steps: TraceStep[] = [];\n\n constructor(private readonly proofId: string) {}\n\n getSteps(): readonly TraceStep[] {\n return this.steps;\n }\n\n async step<T extends StepResult | void>(\n opts: StepOptions,\n fn: () => Promise<T>,\n ): Promise<T> {\n const startedAt = performance.now();\n const assertions: TraceAssertion[] = [];\n\n try {\n const result = await currentStepAssertions.run(\n assertions,\n async () => (await fn()) as T,\n );\n const durationMs = performance.now() - startedAt;\n\n const observation =\n result && typeof result === \"object\" && \"observation\" in result\n ? String((result as StepResult).observation ?? \"\")\n : \"\";\n\n this.steps.push({\n intent: opts.intent,\n kind: opts.kind,\n target: opts.target,\n actor: opts.actor,\n workspaceId: opts.workspaceId,\n observation,\n passed: true,\n durationMs: Math.round(durationMs),\n ...(assertions.length > 0 ? { assertions: [...assertions] } : {}),\n });\n\n return result;\n } catch (error) {\n const durationMs = performance.now() - startedAt;\n\n const message = error instanceof Error ? error.message : String(error);\n\n this.steps.push({\n intent: opts.intent,\n kind: opts.kind,\n target: opts.target,\n actor: opts.actor,\n workspaceId: opts.workspaceId,\n observation: \"\",\n passed: false,\n durationMs: Math.round(durationMs),\n error: message,\n ...(assertions.length > 0 ? { assertions: [...assertions] } : {}),\n });\n\n throw error;\n }\n }\n}\n\nconst specHashCache = new Map<string, string>();\n\n/**\n * Identify the spec file that is currently executing, plus a short content\n * hash, so a trace can be tied back to the exact code that produced it.\n *\n * Returns an empty object outside a Playwright run (or if the file can't be\n * read): provenance is evidence, and absent evidence must never be the reason\n * a proof fails to record.\n */\nfunction specProvenance(): { specFile?: string; specHash?: string } {\n let absolutePath: string;\n try {\n absolutePath = test.info().file;\n } catch {\n return {};\n }\n if (!absolutePath) return {};\n\n const specFile = path.relative(process.cwd(), absolutePath);\n const cached = specHashCache.get(absolutePath);\n if (cached) return { specFile, specHash: cached };\n\n try {\n const contents = fs.readFileSync(absolutePath);\n const specHash = crypto\n .createHash(\"sha256\")\n .update(contents)\n .digest(\"hex\")\n .slice(0, 12);\n specHashCache.set(absolutePath, specHash);\n return { specFile, specHash };\n } catch {\n return { specFile };\n }\n}\n\nfunction writeArtifact(proofId: string, artifact: TraceArtifact): void {\n const traceDir = path.join(process.cwd(), ...TRACE_DIR_RELATIVE);\n fs.mkdirSync(traceDir, { recursive: true });\n const target = path.join(traceDir, `${proofId}.json`);\n fs.writeFileSync(target, JSON.stringify(artifact, null, 2), \"utf8\");\n}\n\nfunction normalizeOptions(arg: string | ProofOptions): ProofOptions {\n return typeof arg === \"string\" ? { proofId: arg } : arg;\n}\n\nfunction mutationProvenance(): TraceArtifact[\"mutation\"] | undefined {\n const id = process.env.PROOF_MUTATION_ID?.trim();\n return id ? { id, planted: true } : undefined;\n}\n\nexport const trace = {\n /**\n * Start a proof. The callback receives a recorder whose `.step()` method\n * wraps individual observations. When the callback resolves (or throws),\n * a JSON artifact is written to `.proof/traces/<proofId>.json`.\n *\n * The proof rethrows any thrown error so the Playwright test still fails —\n * the trace is written first so you can inspect the failure.\n *\n * @example\n * // Common v0.5 case: no mission attached\n * await trace.proof(\"auth-login-admin\", async (t) => { ... });\n *\n * @example\n * // v1 case: proof belongs to an external mission\n * await trace.proof(\n * { proofId: \"auth-login-admin\", missionId: \"M-042\" },\n * async (t) => { ... },\n * );\n */\n async proof(\n arg: string | ProofOptions,\n fn: (t: TraceRecorderImpl) => Promise<void>,\n ): Promise<void> {\n const { proofId, missionId } = normalizeOptions(arg);\n const startedAt = performance.now();\n const timestamp = new Date().toISOString();\n const recorder = new TraceRecorderImpl(proofId);\n\n let proofError: unknown;\n try {\n await fn(recorder);\n } catch (error) {\n proofError = error;\n }\n\n const durationMs = Math.round(performance.now() - startedAt);\n const steps = recorder.getSteps();\n const passed = !proofError && steps.every((s) => s.passed);\n const mutation = mutationProvenance();\n\n const artifact: TraceArtifact = {\n schemaVersion: TRACE_ARTIFACT_SCHEMA_VERSION,\n proofId,\n ...(missionId ? { missionId } : {}),\n ...specProvenance(),\n ...codeProvenance(),\n ...(mutation ? { mutation } : {}),\n timestamp,\n durationMs,\n passed,\n steps: [...steps],\n };\n\n writeArtifact(proofId, artifact);\n\n if (proofError) throw proofError;\n },\n};\n","// Import External Packages\nimport { spawnSync } from \"node:child_process\";\n// Import Local Imports\n// Import Core Dependencies\n// Import Shared Dependencies\n// Import Extension Dependencies\n\n// ---------------------------------------------------------------------------\n// Which code did this proof observe?\n//\n// A trace says \"the outsider could not read the row\". On its own that is a claim\n// about nothing in particular: without the commit, a reader cannot tell whether\n// it describes the PR under review, the branch point, or a working tree that has\n// since been edited. `specHash` pins the spec that made the claim; this pins the\n// application it was made against.\n//\n// Two facts, both optional:\n//\n// commit — the SHA that was checked out while the proof ran.\n// dirty — whether the working tree had uncommitted changes at that moment. A\n// green proof from a dirty tree describes no reviewable commit, so a\n// consumer that gates on evidence needs to be able to refuse it. It\n// is not the same information as `commit`, and it is the field people\n// forget to ask for.\n//\n// Absent rather than fabricated when unknown: shallow clones, exported tarballs\n// and non-git checkouts all have to keep producing usable artifacts, so a missing\n// SHA weakens the evidence instead of failing the run.\n// ---------------------------------------------------------------------------\n\nexport interface CodeProvenance {\n commit?: string;\n dirty?: boolean;\n}\n\n/** Environment variables `proof-harness verify` sets for the spec run. */\nexport const PROOF_COMMIT_ENV = \"PROOF_COMMIT\";\nexport const PROOF_DIRTY_ENV = \"PROOF_DIRTY\";\n\ntype GitReader = (args: string[]) => string | undefined;\n\nconst readGit: GitReader = (args) => {\n try {\n const result = spawnSync(\"git\", args, { encoding: \"utf8\" });\n if (result.status !== 0) return undefined;\n return result.stdout;\n } catch {\n return undefined;\n }\n};\n\n/**\n * Resolve provenance, preferring what the runner already worked out.\n *\n * The runner path matters for more than speed. `proof_verify.mjs` reads git once\n * for the whole run, so every trace in one run agrees; letting each spec ask git\n * for itself would let `dirty` flip mid-run as files are written, and two traces\n * from the same run could then disagree about the code they observed.\n *\n * The git fallback exists because specs are also run directly —\n * `pnpm exec playwright test e2e/proofs/foo.proof.ts` — and a trace written that\n * way should not silently lose its provenance.\n */\nexport function resolveCodeProvenance(\n // Deliberately not NodeJS.ProcessEnv: the app augments that type with required\n // keys, and this reads two optional variables that have nothing to do with the\n // application's own configuration.\n env: Record<string, string | undefined> = process.env,\n git: GitReader = readGit,\n): CodeProvenance {\n const fromRunner = env[PROOF_COMMIT_ENV]?.trim();\n if (fromRunner) {\n const dirty = env[PROOF_DIRTY_ENV]?.trim();\n return {\n commit: fromRunner,\n // Only \"true\"/\"false\" carry meaning; anything else means the runner could\n // not tell, which is different from \"clean\".\n ...(dirty === \"true\" || dirty === \"false\"\n ? { dirty: dirty === \"true\" }\n : {}),\n };\n }\n\n const commit = git([\"rev-parse\", \"HEAD\"])?.trim();\n if (!commit) return {};\n\n const status = git([\"status\", \"--porcelain\"]);\n return {\n commit,\n ...(status === undefined ? {} : { dirty: status.trim().length > 0 }),\n };\n}\n\nlet cached: CodeProvenance | undefined;\n\n/**\n * Cached `resolveCodeProvenance` for the writer path.\n *\n * Playwright workers are separate processes, so this is one git call per worker\n * at worst — and none at all when the runner supplied the values.\n */\nexport function codeProvenance(): CodeProvenance {\n cached ??= resolveCodeProvenance();\n return cached;\n}\n","// Import External Packages\n// Import Local Imports\nimport type { ProofKind } from \"./vocabulary\";\n// Import Core Dependencies\n// Import Shared Dependencies\n// Import Extension Dependencies\n\n// ---------------------------------------------------------------------------\n// Trace artifact types\n//\n// These are the durable shape written to `.proof/traces/<proofId>.json`.\n//\n// Two separate identifiers on purpose:\n// - `proofId` — per-file id; what _this_ proof verifies (e.g. \"auth-login-admin\",\n// \"workspace-invite-tenant-isolation\"). The filename is keyed on it,\n// so proofs never collide.\n// - `missionId` — optional external ticket reference (e.g. \"M-042\"). A single mission\n// typically has multiple proofs (happy_path + tenant_isolation +\n// authorization), each with its own file. The v1 mission-manifest\n// validator groups by `missionId` and checks that every\n// `trace_must_prove` entry has at least one matching passing step\n// across the group.\n// ---------------------------------------------------------------------------\n\n/**\n * The evidential role an assertion plays.\n *\n * - `primary` — the claim the proof exists to make. For an authorization or\n * tenant-isolation proof that is a refusal (\"the outsider could\n * NOT read the row\"); for a happy_path proof it is a success\n * (\"the user reached their workspace\"). This is what a mission's\n * `trace_must_prove` entry matches by default.\n * - `control` — a companion assertion proving the primary probe was capable of\n * failing at all: \"the operation that SHOULD be permitted still\n * succeeded\", or \"the row the primary probe looked for genuinely\n * exists and is readable by someone.\"\n *\n * The distinction is load-bearing, not cosmetic. A refusal probe alone cannot\n * tell \"RLS correctly hid this row\" apart from \"there was no row / the grant\n * denies everyone / the table name was a typo\" — every one of those yields zero\n * rows and would record a passing assertion. The control is what makes the zero\n * mean something.\n *\n * Because the validator matches `trace_must_prove` on `{ kind, target,\n * passed: true }`, controls MUST be labelled: otherwise a control (which by\n * design passes) could satisfy a requirement all on its own and the primary\n * probe would never be checked at all.\n *\n * Naming note: an earlier draft called these `negative` / `positive_control`.\n * That reads backwards for `happy_path`, whose primary claim is a success, so\n * the roles are named by evidential function instead. Both older names are\n * accepted as deprecated aliases wherever a role is parsed.\n */\nexport type AssertionRole = \"primary\" | \"control\";\n\n/**\n * Concrete operation exercised by an assertion. Mutation inventory keys claims\n * by kind + target + operation so a SELECT mutation cannot silently stand in\n * for an untested UPDATE/DELETE or action invocation.\n */\nexport type ProofOperation =\n | \"select\"\n | \"insert\"\n | \"update\"\n | \"delete\"\n | \"invoke\"\n | \"request\";\n\n/** Deprecated role spellings accepted on input and normalized to AssertionRole. */\nexport const ASSERTION_ROLE_ALIASES: Readonly<Record<string, AssertionRole>> = {\n negative: \"primary\",\n positive_control: \"control\",\n};\n\n/**\n * Normalize a role from an artifact or manifest. `undefined` means `primary`:\n * traces written before schemaVersion 2 carry no role, and their assertions\n * were all primary claims.\n */\nexport function normalizeAssertionRole(role: unknown): AssertionRole | null {\n if (role === undefined || role === null) return \"primary\";\n if (role === \"primary\" || role === \"control\") return role;\n if (typeof role === \"string\" && role in ASSERTION_ROLE_ALIASES) {\n return ASSERTION_ROLE_ALIASES[role];\n }\n return null;\n}\n\n/**\n * What an assertion concluded.\n *\n * `passed` / `failed` are the two verdicts a boolean can carry. The other two\n * exist because \"we did not find out\" is a real outcome that a boolean has to\n * round to one side or the other, and rounding it to `false` is indistinguishable\n * from a genuine failure while rounding it to `true` is how a suite quietly stops\n * proving anything:\n *\n * - `incomplete` — the probe ran but could not reach a verdict (a precondition\n * was missing, a dependency was unavailable).\n * - `skipped` — the probe was deliberately not run, e.g. a tenant-isolation\n * direction with no fixture rows on that side.\n *\n * Neither counts as evidence: both carry `passed: false`, so neither can satisfy\n * a mission requirement. The status is what lets a consumer tell them apart from\n * a real failure, and stops a skip from disappearing into \"one fewer assertion\".\n */\nexport type AssertionStatus = \"passed\" | \"failed\" | \"incomplete\" | \"skipped\";\n\n/** Derive the status of an assertion that predates the field. */\nexport function normalizeAssertionStatus(\n status: unknown,\n passed: boolean,\n): AssertionStatus {\n if (\n status === \"passed\" ||\n status === \"failed\" ||\n status === \"incomplete\" ||\n status === \"skipped\"\n ) {\n return status;\n }\n return passed ? \"passed\" : \"failed\";\n}\n\n/**\n * A single structured assertion recorded during a step. Produced by the\n * `assert.*` helpers via `recordAssertion(...)`. Hand-written `expect(...)`\n * calls inside a step do NOT appear here — only helper-driven assertions do,\n * because the v1 mission-manifest validator matches on\n * `{ kind, target, passed: true, role }` and needs a closed, machine-readable\n * surface.\n */\nexport interface TraceAssertion {\n /** Category from the closed `PROOF_KINDS` vocabulary. */\n kind: ProofKind;\n /** The thing asserted against: table, route, action, etc. */\n target: string;\n /** Concrete operation the probe exercised. */\n operation?: ProofOperation;\n /**\n * Whether this specific assertion held. Remains the authoritative field for\n * the validator and for consumers: only `true` is evidence.\n */\n passed: boolean;\n /**\n * Why `passed` has the value it has. Omitted is derived from `passed`, so\n * traces written before this field stay readable.\n */\n status?: AssertionStatus;\n /**\n * Evidential role. Omitted is treated as `\"primary\"` by the validator for\n * backward compatibility with traces written before schemaVersion 2.\n */\n role?: AssertionRole;\n /** Optional human-readable note for failure triage. */\n detail?: string;\n /**\n * Which SDK helper produced this assertion, e.g. `\"assert.tenantIsolation\"`.\n *\n * Stamped by the SDK itself through an internal channel that the public\n * `recordAssertion(...)` entry point cannot reach: a value supplied by spec\n * code is stripped before the assertion is recorded. Absent means the\n * assertion was recorded directly by a spec via `recordAssertion(...)`.\n *\n * The distinction is evidential. A helper-emitted assertion went through the\n * helper's vacuity controls, ground-truth re-reads, and role bookkeeping; a\n * spec-recorded assertion asserts whatever the spec chose to write into it.\n * The mission validator therefore requires helper provenance for\n * `tenant_isolation` requirements (a hand-rolled isolation probe has none of\n * `assert.tenantIsolation`'s protections), and a mission can demand it for\n * any requirement via `trace_must_prove[].evidence: \"helper\"`.\n *\n * For `tenant_isolation` the stamp is also identity-checked, not merely\n * present-checked — see TENANT_ISOLATION_TABLE_HELPERS. `assert.httpResponse`\n * records whatever `kind` its caller passes, so \"some helper emitted this\"\n * is not the same claim as \"the isolation probe ran\".\n */\n emittedBy?: string;\n}\n\n/**\n * The only helpers whose stamp counts as tenant-isolation evidence, split by\n * target (tables are stricter than action refs). The single runtime source is\n * [`portable-vocabulary.mjs`](./portable-vocabulary.mjs), which the\n * dependency-free Node scripts (`proof_verify.mjs`, `proof_coverage.mjs`)\n * import directly; see there for the identity rationale.\n */\nexport {\n TENANT_ISOLATION_ACTION_HELPERS,\n TENANT_ISOLATION_TABLE_HELPERS,\n} from \"./portable-vocabulary.mjs\";\n\n/**\n * A single step inside a proof.\n * Produced by `t.step({ intent, kind, target }, async () => { ... })`.\n */\nexport interface TraceStep {\n /** Human-readable description of what the step attempted. */\n intent: string;\n /** Category of the step, from closed vocabulary. */\n kind: ProofKind;\n /** The thing the step targets: action name, table, route, etc. */\n target: string;\n /** What the step observed (returned by the step callback). */\n observation: string;\n /** Whether the step's assertions held. */\n passed: boolean;\n /** Optional: who the step acted as (email, role, seed identifier). */\n actor?: string;\n /** Optional: which workspace the step operated in. */\n workspaceId?: string;\n /** Wall-clock duration of the step callback. */\n durationMs: number;\n /** On failure, the error message captured. */\n error?: string;\n /**\n * Structured assertions recorded by `assert.*` helpers during this step.\n * Empty or omitted for steps that only use `expect(...)` directly.\n * The v1 mission-manifest validator matches `trace_must_prove` entries\n * against this array.\n */\n assertions?: TraceAssertion[];\n}\n\n/**\n * Current per-proof trace artifact schema version.\n *\n * 2 — adds `role` and `status` to assertions and the `schemaVersion` /\n * `specFile` / `specHash` / `commit` / `dirty` provenance fields below.\n * Consumers written against v1 keep working: every v1 field is still present\n * with the same meaning, and both new assertion fields are derivable when\n * absent.\n *\n * `commit`, `dirty`, and `mutation` arrived after v2 shipped and did not bump\n * it: they are optional additions, and the contract's rule is that additive\n * fields do not move a version. A consumer that needs them must treat absence\n * as \"unknown\" anyway, since normal runs have no mutation and a non-git\n * checkout cannot supply code provenance.\n */\nexport const TRACE_ARTIFACT_SCHEMA_VERSION = 2;\n\n/**\n * Provenance attached only when the mutation harness deliberately plants a\n * defect. A red mutation trace means the check detected the named defect; it\n * must never be mistaken for a real application regression.\n */\nexport interface TraceMutation {\n /** Stable mutation identifier from the repository-owned mutation catalog. */\n id: string;\n /** Always true; makes the intentional nature explicit to copied consumers. */\n planted: true;\n}\n\n/**\n * A trace artifact — one proof's worth of evidence.\n * Written atomically to `.proof/traces/<proofId>.json` when the proof\n * completes (pass or fail).\n */\nexport interface TraceArtifact {\n /**\n * Pinned to TRACE_ARTIFACT_SCHEMA_VERSION. Absent on traces written by\n * SDK versions before provenance was added.\n */\n schemaVersion?: number;\n /**\n * Repo-relative path of the spec file that produced this trace, e.g.\n * `e2e/proofs/workspace-invite.proof.ts`. Lets a reviewer go from a claim\n * straight to the code that made it.\n */\n specFile?: string;\n /**\n * First 12 hex chars of the SHA-256 of the spec file's contents. Two runs\n * claiming the same proof with different hashes were produced by different\n * code — which is the whole question when a previously-red proof turns\n * green.\n */\n specHash?: string;\n /**\n * Full SHA of the commit that was checked out while the proof ran. `specHash`\n * pins the spec that made the claim; this pins the application it was made\n * against, which is what lets an external ledger say \"red at X, green at Y\".\n *\n * Absent outside a git checkout. On a CI `pull_request` run this is the\n * ephemeral merge commit the checkout created — accurate about what was\n * tested, but not a commit you can return to later.\n */\n commit?: string;\n /**\n * Whether the working tree had uncommitted changes when the proof ran. A pass\n * from a dirty tree is evidence about code that exists nowhere but one laptop,\n * so a consumer gating on traces should be able to refuse it. Absent when it\n * could not be determined, which is not the same as `false`.\n */\n dirty?: boolean;\n /**\n * Present only for intentional red runs produced by `proof:mutate`.\n * Stored inside the trace so provenance survives artifact renames/copies.\n */\n mutation?: TraceMutation;\n /**\n * Per-file identifier for this proof. Used as the filename stem and as the\n * join key when the v1 mission-manifest validator looks for a matching\n * passing step for a `trace_must_prove` entry.\n */\n proofId: string;\n /**\n * Optional reference to the external mission (ticket) this proof belongs\n * to. Multiple proofs share one `missionId`. Absent in v0.5 local runs\n * that are not tied to a mission.\n */\n missionId?: string;\n /** ISO 8601 timestamp of proof start. */\n timestamp: string;\n /** Total wall-clock duration across all steps. */\n durationMs: number;\n /** True only if every step passed. */\n passed: boolean;\n /** Ordered list of steps as they were executed. */\n steps: TraceStep[];\n}\n\n/**\n * Options accepted by `trace.proof(...)`. A bare string is treated as\n * `{ proofId: string }` — the common v0.5 case where no mission is attached.\n */\nexport interface ProofOptions {\n proofId: string;\n missionId?: string;\n}\n\n/**\n * The handle passed to a proof callback. Provides the step recorder.\n */\nexport interface TraceRecorder {\n step<T extends StepResult | void>(\n opts: StepOptions,\n fn: () => Promise<T>,\n ): Promise<T>;\n}\n\nexport interface StepOptions {\n intent: string;\n kind: ProofKind;\n target: string;\n actor?: string;\n workspaceId?: string;\n}\n\n/**\n * Optional payload returned by a step callback. If the callback returns\n * `void` the step is still recorded with `observation: \"\"`.\n */\nexport interface StepResult {\n observation?: string;\n}\n","// Import External Packages\nimport { createClient } from \"@supabase/supabase-js\";\n// Import Local Imports\nimport { createRetryingFetch } from \"./transient\";\n// Import Core Dependencies\n// Import Shared Dependencies\n// Import Extension Dependencies\n\n// ---------------------------------------------------------------------------\n// Thin re-export of the service-role Supabase client, scoped to Proof.\n//\n// Proof helpers use the service-role client for two things:\n// 1. Seed fixture setup (`seed.workspace`, `seed.user`)\n// 2. Teardown of test data after assertions complete\n//\n// Consumers inside `.proof.ts` specs receive the result of this factory\n// through the `setup({ sb })` callback of `assert.tenantIsolation`. They\n// should NEVER construct a service-role client themselves — keeping the\n// construction centralized makes it easy to add logging, usage limits, or\n// a future mock in one place.\n//\n// This package does not depend on an application's generated database types or\n// service-client wrapper. The factory reads the standard environment variables\n// directly and gives proof-specific diagnostics.\n// ---------------------------------------------------------------------------\n\nfunction requireProofEnv(name: string): string {\n const value = process.env[name];\n if (!value) {\n throw new Error(\n `[PROOF_FAIL] missing_env: expected ${name}, found empty\\n` +\n \" file: src/server/service-client.ts\\n\" +\n \" suggestion: Ensure CI exports the Supabase env vars before running pnpm proof:verify.\",\n );\n }\n return value;\n}\n\n/**\n * Factory for a service-role Supabase client used inside Proof helpers and\n * `.proof.ts` spec files.\n *\n * Returns a service-role Supabase client so spec code can safely type the `sb`\n * parameter as the library's `SupabaseClient`.\n */\nexport function createProofServiceClient() {\n return createClient(\n requireProofEnv(\"NEXT_PUBLIC_SUPABASE_URL\"),\n requireProofEnv(\"SUPABASE_SECRET_KEY\"),\n {\n auth: {\n persistSession: false,\n autoRefreshToken: false,\n detectSessionInUrl: false,\n },\n // Local Supabase sits behind Kong, which intermittently 502s under the\n // load of a proof run. Installing the retry here covers every query made\n // through this client, including ad-hoc ones inside proof specs.\n global: { fetch: createRetryingFetch(\"service-role request\") },\n },\n );\n}\n","// Import External Packages\n// Import Local Imports\nimport { createProofServiceClient } from \"./service-client\";\nimport { isTransient, TRANSIENT_MAX_ATTEMPTS } from \"./transient\";\n// Import Core Dependencies\n// Import Shared Dependencies\n// Import Extension Dependencies\n\n// ---------------------------------------------------------------------------\n// seed.workspace / seed.user\n//\n// Fixture helpers used by `.proof.ts` specs and by `assert.tenantIsolation`\n// to set up isolated test data. They bypass the business-logic pipeline on\n// purpose: creating fixtures should not exercise the very actions we are\n// trying to verify. Instead we insert directly via the service-role client.\n//\n// Both helpers are idempotent on upsert conflicts is NOT implemented; callers\n// are responsible for passing unique names/emails (`assert.tenantIsolation`\n// uses a short random suffix).\n// ---------------------------------------------------------------------------\n\nexport interface SeedWorkspace {\n id: string;\n name: string;\n ownerId: string | null;\n}\n\nexport interface SeedUser {\n id: string;\n email: string;\n password: string;\n workspaceId: string;\n role: \"owner\" | \"admin\" | \"member\" | \"viewer\";\n /**\n * `workspace_members.id` for this user's membership row in `workspaceId`.\n * Needed by proofs that invoke actions keyed on the membership (e.g.\n * `removeMember` takes `memberId = workspace_members.id`, not user_id).\n */\n membershipId: string;\n}\n\nfunction failProof(\n category: string,\n expected: string,\n found: string,\n suggestion = `Check that Supabase is running (pnpm exec supabase status) and that SUPABASE_SECRET_KEY is set in .env.local.`,\n): never {\n throw new Error(\n `[PROOF_FAIL] ${category}: expected ${expected}, found ${found}\\n` +\n ` file: src/server/seed.ts\\n` +\n ` suggestion: ${suggestion}`,\n );\n}\n\n/**\n * Fixture setup that failed because the gateway never answered is reported as\n * infrastructure. Otherwise the operator reads \"expected a workspace row to be\n * created\" and goes looking for a broken migration.\n */\nfunction failSeed(\n category: string,\n expected: string,\n found: string,\n error: { message?: string | null; status?: number | null } | null | undefined,\n status?: number,\n): never {\n if (isTransient(error, status)) {\n failProof(\n `${category}_transient`,\n expected,\n `${found} — still failing after ${TRANSIENT_MAX_ATTEMPTS} attempts`,\n `This is a gateway/transport failure, not a schema problem: the request never reached Postgres. ` +\n `Check that the local Supabase stack is healthy (pnpm exec supabase status) and is not restarting mid-run.`,\n );\n }\n failProof(category, expected, found);\n}\n\n/**\n * Wait until `public.users` has a row for the given `userId`.\n *\n * `supabase.auth.admin.createUser` creates a row in `auth.users`, which fires\n * the `handle_new_user` trigger that inserts into `public.users`. Postgres\n * runs the trigger inside the same transaction as the INSERT, so the row\n * exists the moment the admin API's HTTP call returns — in theory.\n *\n * In practice the admin API occasionally returns before the post-commit\n * visibility window has settled, and the very next `workspace_members`\n * INSERT fails its FK to `public.users(id)`. This poll closes that window\n * deterministically: short, bounded, ~500ms worst case. A real failure\n * still surfaces with a structured [PROOF_FAIL] so the race is not masked.\n *\n * Note: `workspace_members.user_id` FKs to `public.users(id)`, NOT\n * `auth.users`. The value is the same UUID because `handle_new_user`\n * copies it, but the FK target matters if future migrations ever split\n * them.\n */\nasync function waitForPublicUser(\n sb: ReturnType<typeof createProofServiceClient>,\n userId: string,\n email: string,\n): Promise<void> {\n const maxAttempts = 10;\n const delayMs = 50;\n for (let i = 0; i < maxAttempts; i++) {\n const { data } = await sb\n .from(\"users\")\n .select(\"id\")\n .eq(\"id\", userId)\n .maybeSingle();\n if (data) return;\n await new Promise((r) => setTimeout(r, delayMs));\n }\n failProof(\n \"seed_user_trigger\",\n `public.users row for ${email} (id ${userId}) within ${maxAttempts * delayMs}ms`,\n \"row still missing — the handle_new_user trigger did not materialize the public.users mirror row\",\n );\n}\n\nexport const seed = {\n /**\n * Create a workspace with no owner. Use `seed.user({ workspace, role: \"owner\" })`\n * afterwards to attach an owner. Returns the inserted row.\n *\n * Bypasses RLS via the service-role client, so it works even when no user\n * is authenticated.\n */\n async workspace(name: string): Promise<SeedWorkspace> {\n const sb = createProofServiceClient();\n\n const { data, error, status } = await sb\n .from(\"workspaces\")\n .insert({\n name,\n type: \"team\",\n })\n .select(\"id, name, owner_id\")\n .single();\n\n if (error || !data) {\n failSeed(\n \"seed_workspace\",\n \"a workspace row to be created\",\n `insert error: ${error?.message ?? \"no row returned\"}`,\n error,\n status,\n );\n }\n\n return {\n id: data.id,\n name: data.name,\n ownerId: data.owner_id,\n };\n },\n\n /**\n * Create an auth user with the given credentials and attach them to the\n * provided workspace with the given role. Relies on the `handle_new_user`\n * trigger to mirror the row into `public.users` automatically.\n */\n async user(opts: {\n email: string;\n password: string;\n workspace: SeedWorkspace;\n role: \"owner\" | \"admin\" | \"member\" | \"viewer\";\n }): Promise<SeedUser> {\n const sb = createProofServiceClient();\n\n const { data: userData, error: createErr } = await sb.auth.admin.createUser(\n {\n email: opts.email,\n password: opts.password,\n email_confirm: true,\n },\n );\n\n if (createErr || !userData?.user) {\n failSeed(\n \"seed_user_auth\",\n `auth user to be created for ${opts.email}`,\n `admin.createUser error: ${createErr?.message ?? \"no user returned\"}`,\n createErr,\n );\n }\n\n const userId = userData.user.id;\n\n // `workspace_members.user_id` references `public.users(id)`, not\n // `auth.users`. The `handle_new_user` trigger mirrors the row on\n // `auth.users` INSERT; poll briefly to rule out any post-commit\n // visibility race before the FK insert below.\n await waitForPublicUser(sb, userId, opts.email);\n\n const {\n data: memberRow,\n error: memberErr,\n status: memberStatus,\n } = await sb\n .from(\"workspace_members\")\n .insert({\n workspace_id: opts.workspace.id,\n user_id: userId,\n role: opts.role,\n })\n .select(\"id\")\n .single();\n\n if (memberErr || !memberRow) {\n failSeed(\n \"seed_user_membership\",\n `workspace_members row for user ${opts.email} in workspace ${opts.workspace.id}`,\n `insert error: ${memberErr?.message ?? \"no row returned\"}`,\n memberErr,\n memberStatus,\n );\n }\n\n return {\n id: userId,\n email: opts.email,\n password: opts.password,\n workspaceId: opts.workspace.id,\n role: opts.role,\n membershipId: memberRow.id,\n };\n },\n\n /**\n * Delete a workspace and cascade-clean its members. Used by helpers for\n * teardown. Safe to call on a workspace that no longer exists.\n */\n async deleteWorkspace(workspaceId: string): Promise<void> {\n const sb = createProofServiceClient();\n await sb.from(\"workspaces\").delete().eq(\"id\", workspaceId);\n },\n\n /**\n * Delete an auth user (cascades to public.users and workspace_members via\n * the schema's ON DELETE CASCADE). Safe to call if the user no longer\n * exists.\n */\n async deleteUser(userId: string): Promise<void> {\n const sb = createProofServiceClient();\n await sb.auth.admin.deleteUser(userId);\n },\n};\n","// Import External Packages\nimport type { SupabaseClient } from \"@supabase/supabase-js\";\n// Import Local Imports\nimport type { SeedUser, SeedWorkspace } from \"./seed\";\n// Import Core Dependencies\n// Import Shared Dependencies\n// Import Extension Dependencies\n\n/**\n * Seed handles available to a tenant-isolation fixture factory.\n *\n * A factory normally inserts one row for each side so the helper can prove both\n * isolation directions. It may use fewer handles when the table is user-scoped\n * or when an existing seeded workspace/user row is itself the fixture.\n */\nexport interface ProofFixtureContext {\n orgA: SeedWorkspace;\n orgB: SeedWorkspace;\n userA: SeedUser;\n userB: SeedUser;\n sb: SupabaseClient;\n}\n\n/**\n * Explicit, table-owned setup contract for a Proof fixture.\n *\n * Keep factories under `e2e/fixtures/<table>.ts`. The table name is repeated at\n * runtime so a copied factory cannot silently seed one table while a proof\n * probes another.\n */\nexport interface ProofFixtureFactory<TTable extends string = string> {\n readonly table: TTable;\n create(context: ProofFixtureContext): Promise<void>;\n}\n\nexport interface DefineProofFixtureOptions<TTable extends string> {\n table: TTable;\n create(context: ProofFixtureContext): Promise<void>;\n}\n\n/** Define a completed, explicit fixture factory. */\nexport function defineProofFixture<const TTable extends string>(\n options: DefineProofFixtureOptions<TTable>,\n): ProofFixtureFactory<TTable> {\n return Object.freeze({\n table: options.table,\n create: options.create,\n });\n}\n\n/**\n * Error used when a proof exists before its table's valid fixture shape is\n * known. `assert.tenantIsolation` recognizes it and records an incomplete\n * assertion rather than allowing the missing setup to look green.\n */\nexport class ProofFixturePendingError extends Error {\n readonly code = \"fixture_factory_required\";\n\n constructor(\n readonly table: string,\n readonly reason: string,\n ) {\n super(\n `Fixture factory for \"${table}\" is incomplete: ${reason}. Complete e2e/fixtures/${table}.ts with values valid for the final schema.`,\n );\n this.name = \"ProofFixturePendingError\";\n }\n}\n\n/**\n * Declare the intentional pre-schema state of a fixture.\n *\n * The builder replaces this with `defineProofFixture(...)` after migrations\n * settle. Until then the proof remains explicitly incomplete.\n */\nexport function pendingProofFixture<const TTable extends string>(\n table: TTable,\n reason = \"the table schema and product constraints are not final\",\n): ProofFixtureFactory<TTable> {\n return defineProofFixture({\n table,\n async create() {\n throw new ProofFixturePendingError(table, reason);\n },\n });\n}\n\nexport function isProofFixturePendingError(\n error: unknown,\n): error is ProofFixturePendingError {\n return error instanceof ProofFixturePendingError;\n}\n","// Import External Packages\nimport type { SupabaseClient } from \"@supabase/supabase-js\";\nimport type { APIResponse, Page } from \"@playwright/test\";\n// Import Local Imports\nimport { actAsUser } from \"./actAsUser\";\nimport {\n recordAssertion,\n withAssertionProvenance,\n withoutAssertionProvenance,\n} from \"./trace\";\nimport { seed, type SeedUser, type SeedWorkspace } from \"../server/seed\";\nimport {\n isProofFixturePendingError,\n type ProofFixtureContext,\n type ProofFixtureFactory,\n} from \"../server/fixture\";\nimport { createProofServiceClient } from \"../server/service-client\";\nimport { isTransient, TRANSIENT_MAX_ATTEMPTS } from \"../server/transient\";\nimport { isAuthRateLimited } from \"../server/auth-rate-limit\";\nimport type { AssertionRole } from \"../shared/trace-types\";\nimport type { ProofKind } from \"../shared/vocabulary\";\n// Import Core Dependencies\n// Import Shared Dependencies\n// Import Extension Dependencies\n\n// ---------------------------------------------------------------------------\n// assert.tenantIsolation\n//\n// The core SDK thesis: prove that RLS actually keeps tenant data separated.\n//\n// The helper:\n// 1. Creates two fresh workspaces (orgA, orgB) and one user per workspace\n// 2. Runs the caller's `setup()` in orgA so there is data to try to leak\n// 3. Counts orgA's rows with the SERVICE ROLE to establish ground truth. Zero\n// rows means the proof would measure nothing, and it fails as vacuous.\n// 4. Runs a POSITIVE CONTROL: userA (a member of orgA) must be able to read\n// those rows through RLS. Without this, \"userB saw 0 rows\" is\n// indistinguishable from a blanket-deny policy, a missing grant, or a\n// typo'd table name — all of which would otherwise record a green\n// tenant-isolation assertion.\n// 5. Runs the negative probe: signs in as userB against a direct Supabase\n// client (no browser) and queries `<table>` filtered to orgA.id. If rows\n// come back, RLS is broken — fail with a structured [PROOF_FAIL].\n// 6. Repeats 3–5 in reverse (userA against orgB). The reverse direction is\n// SKIPPED WITHOUT RECORDING when the caller only populated orgA, because\n// an empty-org probe is exactly the vacuous pass step 3 exists to reject.\n// 7. Optionally: if a `page` is provided, log userB into the browser and\n// navigate somewhere the data would be visible — asserting empty/403\n// UI state as a secondary check.\n// 8. Tears down the fixtures (cascades handle most of it).\n//\n// Ergonomics are the whole point: the caller writes ~3 lines. Any more and\n// agents skip the helper, which defeats the SDK. Note that the control in step\n// 4 is derived automatically from the seeded fixtures — callers get it for\n// free and cannot forget it.\n// ---------------------------------------------------------------------------\n\ntype RandomSuffix = string;\n\nfunction randomSuffix(): RandomSuffix {\n return Math.random().toString(36).slice(2, 10);\n}\n\nfunction proofFail(\n category: string,\n expected: string,\n found: string,\n extras: { file?: string; suggestion: string },\n): Error {\n return new Error(\n `[PROOF_FAIL] ${category}: expected ${expected}, found ${found}\\n` +\n ` file: ${extras.file ?? \"src/playwright/assert.ts\"}\\n` +\n ` suggestion: ${extras.suggestion}`,\n );\n}\n\n/**\n * A probe whose request never reached the database has observed nothing, and\n * \"nothing\" must not be read as a denial.\n *\n * Every write probe below decides its verdict from ground truth — the row did\n * not land, so the policy held — which is exactly what a gateway failure also\n * looks like. The retrying fetch installed on these clients makes that rare;\n * this makes the leftover case loud instead of green. The `*_setup` category\n * follows the SDK convention for \"the harness could not establish the conditions\n * to measure\".\n */\nfunction failIfInfrastructure(\n error: { message?: string | null; status?: number | null } | null | undefined,\n status: number | undefined,\n setupCategory: string,\n what: string,\n): void {\n if (isAuthRateLimited(error, status)) {\n throw proofFail(\n setupCategory.replace(/_setup$/, \"_rate_limited\"),\n `${what} to receive a database verdict`,\n `HTTP 429/auth budget exhaustion: ${error?.message ?? \"rate limited\"}`,\n {\n suggestion:\n \"This is infrastructure, not an RLS denial. Wait for the auth window to reset or reduce parallel proof workers.\",\n },\n );\n }\n if (!isTransient(error, status)) return;\n throw proofFail(\n setupCategory,\n `${what} to reach the database, so that the result means something`,\n `gateway/transport failure after ${TRANSIENT_MAX_ATTEMPTS} attempts: ${error?.message ?? \"unknown error\"}`,\n {\n suggestion:\n `The request never got an answer from Postgres, so neither \"denied\" nor \"allowed\" was observed — ` +\n `reading it as a denial would certify an invariant that nothing exercised. Check that the local Supabase ` +\n `stack is healthy (pnpm exec supabase status) and re-run.`,\n },\n );\n}\n\n// Note: the \"sign-in as userX and return a fresh SupabaseClient\" primitive\n// lives in `actAsUser.supabaseClient()` so individual proof specs can use it\n// directly for ad-hoc RLS probes. This helper is the primary mechanism for\n// tenant isolation checks: it bypasses Playwright entirely.\n\ninterface TenantIsolationBaseOptions {\n /**\n * Table to query as userB. RLS on the table is what is being tested.\n * Must be a table governed by a tenant-scoped RLS policy — either\n * workspace-scoped (the canonical pattern uses `get_user_workspace_ids`) or\n * user-scoped (`user_id = auth.uid()`); see `scope`.\n */\n table: string;\n /**\n * What \"belongs to one tenant\" means for this table:\n *\n * - `\"workspace\"` (default) — rows carry a workspace id, and the two tenants\n * are the seeded orgA / orgB.\n * - `\"user\"` — rows carry a user id, and the two tenants are userA / userB.\n * This is the shape for personal data (notifications, billing records):\n * the invariant is \"another *user* must not read these rows\", which is the\n * same claim as workspace isolation with a different scope column.\n *\n * Tables classified `user_scoped` in `.proof/schema.json` need `\"user\"`.\n */\n scope?: \"workspace\" | \"user\";\n /**\n * Column carrying the scope value. Defaults to `workspace_id` for workspace\n * scope and `user_id` for user scope.\n *\n * Override when the scope lives on the primary key instead of a foreign key —\n * `workspaces` is scoped by its own `id`, and `users` by `id`.\n */\n scopeColumn?: string;\n /**\n * Planner-owned semantic subset that the fixture must materialize.\n *\n * The equality filter is applied together with tenant scope to service-role\n * ground truth, the owner's positive control, and the outsider probe. Put\n * product meaning here (for example `{ status: \"published\" }`), not in the\n * executor-owned fixture factory: rows outside this subset do not count as\n * evidence for the claim.\n */\n criterion?: {\n description: string;\n where: Record<string, unknown>;\n };\n /**\n * Optional: pass a `page` to verify userB's browser login roundtrip during\n * the probe. The helper stops that document and clears its cookies before\n * deleting the disposable users, so the page is intentionally logged out\n * when this method returns.\n *\n * Product-specific UI assertions need their own fixture lifecycle; they\n * cannot safely reuse credentials after this helper tears them down.\n */\n page?: Page;\n /**\n * Optional: a tag included in seeded workspace/user names so concurrent\n * runs don't collide on error paths where teardown is skipped.\n */\n tag?: string;\n}\n\nfunction validateTenantIsolationCriterion(\n table: string,\n scopeColumn: string,\n criterion: TenantIsolationBaseOptions[\"criterion\"],\n): void {\n if (!criterion) return;\n\n const invalidReason =\n criterion.description.trim().length === 0\n ? \"criterion.description is empty\"\n : Object.keys(criterion.where).length === 0\n ? \"criterion.where has no equality filters\"\n : Object.prototype.hasOwnProperty.call(criterion.where, scopeColumn)\n ? `criterion.where contains the tenant scope column \"${scopeColumn}\"`\n : null;\n if (!invalidReason) return;\n\n recordAssertion({\n kind: \"tenant_isolation\",\n target: table,\n operation: \"select\",\n passed: false,\n status: \"incomplete\",\n role: \"primary\",\n detail: `planner-owned fixture criterion is invalid: ${invalidReason}`,\n });\n throw proofFail(\n \"fixture_criterion_invalid\",\n \"a non-empty semantic equality filter that does not redefine tenant scope\",\n invalidReason,\n {\n suggestion:\n `Keep \"${scopeColumn}\" out of criterion.where; assert.tenantIsolation applies the current side's scope automatically. ` +\n \"Use criterion.where only for semantic state such as status, type, or visibility.\",\n },\n );\n}\n\n/**\n * Tenant fixture source.\n *\n * Inline `setup` remains available for small, settled schemas. A proof written\n * before its schema exists should instead import an explicit factory from\n * `e2e/fixtures/<table>.ts`; that file can export `pendingProofFixture(...)`\n * until the builder knows the final constraints. Supplying both is rejected by\n * the type system so there is one source of fixture truth.\n */\nexport type TenantIsolationOptions = TenantIsolationBaseOptions &\n (\n | {\n /**\n * Inline setup for an already-known schema. It must create at least one\n * row for side A; side B is optional but required for a bidirectional\n * claim.\n */\n setup: (context: ProofFixtureContext) => Promise<void>;\n fixture?: never;\n }\n | {\n /** Explicit table-owned factory from `e2e/fixtures/<table>.ts`. */\n fixture: ProofFixtureFactory;\n setup?: never;\n }\n );\n\nasync function teardown(\n orgA: SeedWorkspace,\n orgB: SeedWorkspace,\n userA: SeedUser,\n userB: SeedUser,\n): Promise<void> {\n await Promise.allSettled([\n seed.deleteWorkspace(orgA.id),\n seed.deleteWorkspace(orgB.id),\n ]);\n await Promise.allSettled([\n seed.deleteUser(userA.id),\n seed.deleteUser(userB.id),\n ]);\n}\n\n// ---------------------------------------------------------------------------\n// assert.authorization\n//\n// Covers the two dominant \"X is NOT allowed to do Y\" shapes:\n//\n// 1. RLS-layer denial — the DB itself rejects (or silently filters) the\n// operation. Used when the invariant is \"the RLS policy on table T\n// should block op O for user U against row R.\"\n//\n// 2. Action-layer denial — the server action's `withRBAC` or its own\n// role-check rejects the call. Used when the invariant is \"action A\n// should return `{ success: false, error: ... }` when invoked by user\n// U.\" Goes through `actAsUser.invokeAction` → POST /api/proof/invoke-action.\n//\n// Callers can run either probe or both. RLS probes record `authorization`;\n// action probes default to that kind but may name another declared invariant.\n// The helper throws on the first probe that *succeeds when it was expected to\n// fail* — that's the PROOF_FAIL signal.\n//\n// Three things a naive version of this helper would grade wrongly, each of which\n// has bitten a real proof in this repo:\n//\n// - What PostgREST returns is not the verdict. `.select()` adds a RETURNING\n// clause that must satisfy the SELECT policy, so a permitted write can look\n// rejected. Write verdicts come from re-reading state with the service role.\n// - A probe needs something to act on. Against zero matching rows every op\n// looks perfectly enforced (`authorization_vacuous`).\n// - A write probe needs an actor who can SEE what it writes to. Postgres\n// filters the rows an UPDATE/DELETE reaches through its WHERE clause by the\n// SELECT policy, so an actor who cannot read the row matches nothing\n// regardless of write permissions, and the service-role re-read then\n// truthfully reports \"unchanged\". `requireWriteTargetVisible` records that\n// visibility as a control and fails as `authorization_blind` when it is\n// missing, rather than certifying an invariant nothing exercised.\n// ---------------------------------------------------------------------------\n\ntype AuthorizationOp = \"select\" | \"insert\" | \"update\" | \"delete\";\n\ninterface RlsProbe {\n /** Table the probe operates against. */\n table: string;\n /** Operation the probe attempts. Each op has distinct \"denied\" semantics. */\n op: AuthorizationOp;\n /**\n * For `select` / `update` / `delete`: `.eq()` filter applied to the query.\n * Required for update/delete (otherwise the probe has no target row) and\n * typical for select (otherwise it's an unqualified scan).\n */\n filter?: Record<string, unknown>;\n /**\n * For `insert` / `update`: the row payload. Ignored for select/delete.\n */\n payload?: Record<string, unknown>;\n /** Trace target override. Defaults to `table`. */\n target?: string;\n}\n\ninterface ActionProbe {\n /** Module part of the registry key: `\"<module>:<name>\"`. */\n module: string;\n /** Action name part of the registry key. Must be in PROOF_ACTION_REGISTRY. */\n name: string;\n /** Forwarded as-is to the action; validated by its own Zod schema. */\n inputParams: Record<string, unknown>;\n /** Optional regex the rejection message must match for a stricter proof. */\n expectedErrorMatch?: RegExp;\n /**\n * Invariant this action probe establishes. Defaults to `authorization`.\n * Use the action's declared withProof invariant (for example\n * `tenant_isolation` or `idempotency`) so coverage matches the evidence.\n */\n kind?: ProofKind;\n /** Trace target override. Defaults to `\"<module>:<name>\"`. */\n target?: string;\n}\n\ninterface AuthorizationOptions {\n /**\n * Who is attempting the operation. Must be the role that should be\n * *denied*. Never pass a role the invariant says should succeed —\n * authorization proofs assert rejection, not permission.\n */\n actor: \"admin\" | \"member\" | { email: string; password: string };\n /** RLS-layer probe. At least one of `rls` or `action` is required. */\n rls?: RlsProbe;\n /**\n * Action-layer probe. Requires `page` because the action is invoked via\n * the dev server's POST /api/proof/invoke-action route.\n */\n action?: ActionProbe;\n /** Required when `action` is supplied. */\n page?: Page;\n}\n\ninterface ActionSucceedsOptions {\n /** Who must be able to invoke the action successfully. */\n actor: \"admin\" | \"member\" | { email: string; password: string };\n /** Registered action and the exact input used by the control. */\n action: Omit<ActionProbe, \"expectedErrorMatch\">;\n /** Required because actions are invoked through the proof Route Handler. */\n page: Page;\n /** Assertion category. Defaults to `happy_path`. */\n kind?: ProofKind;\n /**\n * Positive action checks are controls by default: they prove a related\n * refusal was capable of failing, but cannot satisfy a mission on their own.\n */\n role?: AssertionRole;\n}\n\nfunction describeActor(actor: AuthorizationOptions[\"actor\"]): {\n email: string;\n password: string;\n label: string;\n} {\n if (typeof actor === \"string\") {\n // Server-only SEED_* env names — never NEXT_PUBLIC_ (credentials must not\n // be inlined into the client bundle). This code runs in Node (Playwright).\n const emailEnv =\n actor === \"admin\"\n ? process.env.SEED_ADMIN_EMAIL\n : process.env.SEED_MEMBER_EMAIL;\n const passwordEnv =\n actor === \"admin\"\n ? process.env.SEED_ADMIN_PASSWORD\n : process.env.SEED_MEMBER_PASSWORD;\n if (!emailEnv || !passwordEnv) {\n const prefix = actor === \"admin\" ? \"SEED_ADMIN\" : \"SEED_MEMBER\";\n throw new Error(\n `[PROOF_FAIL] seed_credentials_missing: ${prefix}_EMAIL and ${prefix}_PASSWORD must be set when using the \"${actor}\" seed actor`,\n );\n }\n return {\n email: emailEnv,\n password: passwordEnv,\n label: `seed:${actor}`,\n };\n }\n return { email: actor.email, password: actor.password, label: actor.email };\n}\n\n/**\n * Apply an equality filter map to a query builder.\n *\n * The cast is contained here on purpose: probes address tables and columns by\n * dynamic name, which the generated Database types cannot express, and letting\n * that inference run through every call site blows the instantiation depth\n * limit.\n */\ntype EqChain = { eq(column: string, value: unknown): unknown };\n\nfunction applyEq<T>(query: T, filter: Record<string, unknown>): T {\n let out = query as unknown as EqChain;\n for (const [k, v] of Object.entries(filter)) {\n out = out.eq(k, v) as EqChain;\n }\n return out as unknown as T;\n}\n\n/**\n * Count rows matching an exact filter using the service role.\n *\n * This is the ground truth for every write probe. What PostgREST returned to\n * the caller cannot be trusted to answer \"did the write land?\" — see the long\n * comment in the insert branch of runRlsProbe.\n */\nasync function countMatchingAsService(\n sb: SupabaseClient,\n table: string,\n filter: Record<string, unknown>,\n): Promise<number> {\n const { count, error } = await applyEq(\n sb.from(table).select(\"*\", { count: \"exact\", head: true }),\n filter,\n );\n if (error) {\n throw proofFail(\n \"authorization_setup\",\n `to verify the state of \"${table}\" with the service role`,\n `Supabase error: ${error.message}`,\n {\n suggestion: `The probe cannot confirm whether the write was actually blocked without reading the table as service_role. Check the table name and that SUPABASE_SERVICE_ROLE_KEY is set.`,\n },\n );\n }\n return count ?? 0;\n}\n\n/**\n * How many of the probe's target rows the ACTOR can actually read.\n *\n * This is the control that makes a write refusal mean something, and it is not\n * the same question as `countMatchingAsService`. PostgreSQL applies the SELECT\n * policy to the rows an UPDATE or DELETE reaches through its WHERE clause, so an\n * actor who cannot see a row cannot write to it *whatever* the UPDATE/DELETE\n * policy and grants say: the statement matches zero rows and reports success.\n * A before/after comparison then shows nothing changed and the probe records a\n * pass — on a table the actor may in fact be free to rewrite.\n *\n * Concretely: a member probing `audit_logs` (super-admin read only) passes even\n * with UPDATE granted and a `USING (true)` update policy in place. The\n * service-role ground truth cannot catch it, because the service role can see\n * the rows and correctly reports them unchanged.\n *\n * An error is treated as zero visible rows rather than a setup failure: a\n * permission-denied answer IS the observation that the actor cannot read there.\n */\nasync function countVisibleToActor(\n client: SupabaseClient,\n table: string,\n filter: Record<string, unknown>,\n): Promise<number> {\n const { count, error, status } = await applyEq(\n client.from(table).select(\"*\", { count: \"exact\", head: true }),\n filter,\n );\n failIfInfrastructure(\n error,\n status,\n \"authorization_setup\",\n `the actor's visibility check on ${table}`,\n );\n if (error) return 0;\n return count ?? 0;\n}\n\n/**\n * Refuse to grade a write probe the actor could never have won.\n *\n * Recorded as a `control` assertion either way, so the trace shows the probe was\n * capable of failing instead of leaving a reader to assume it.\n */\nasync function requireWriteTargetVisible(args: {\n client: SupabaseClient;\n probe: RlsProbe;\n target: string;\n filter: Record<string, unknown>;\n existing: number;\n actorLabel: string;\n}): Promise<void> {\n const { client, probe, target, filter, existing, actorLabel } = args;\n const visible = await countVisibleToActor(client, probe.table, filter);\n\n if (visible > 0) {\n recordAssertion({\n kind: \"authorization\",\n target,\n operation: \"select\",\n passed: true,\n role: \"control\",\n detail: `${actorLabel} can read ${visible} of the ${existing} row(s) matching ${JSON.stringify(filter)} in ${probe.table}, so a refused ${probe.op.toUpperCase()} is distinguishable from one that never reached a row`,\n });\n return;\n }\n\n recordAssertion({\n kind: \"authorization\",\n target,\n operation: \"select\",\n passed: false,\n status: \"incomplete\",\n role: \"control\",\n detail: `${actorLabel} cannot read any of the ${existing} row(s) matching ${JSON.stringify(filter)} in ${probe.table}, so its ${probe.op.toUpperCase()} matches zero rows whether or not the operation is permitted`,\n });\n throw proofFail(\n \"authorization_blind\",\n `${actorLabel} to be able to read at least one of the ${existing} target row(s) in \"${probe.table}\", so that a refused ${probe.op.toUpperCase()} means the write was rejected`,\n `0 visible row(s) — this probe cannot fail, because PostgreSQL filters the rows an ${probe.op.toUpperCase()} reaches through its WHERE clause by the SELECT policy`,\n {\n suggestion:\n `Point the probe at a row the actor can read, or use an actor who can — for an admin-only table that is the admin, and \"not even the actor who can read every row may change one\" is usually the stronger claim anyway. ` +\n `If the invariant you actually want is \"the actor cannot SEE these rows\", use \\`op: \"select\"\\` or assert.tenantIsolation, which grade visibility directly.`,\n },\n );\n}\n\nasync function runRlsProbe(\n probe: RlsProbe,\n actorCreds: { email: string; password: string; label: string },\n): Promise<void> {\n const target = probe.target ?? probe.table;\n const client = await actAsUser.supabaseClient({\n email: actorCreds.email,\n password: actorCreds.password,\n });\n // Loosely typed on purpose: probes address tables by dynamic name, which the\n // generated Database types (rightly) reject for literal-union table keys.\n const sb: SupabaseClient = createProofServiceClient();\n\n if (probe.op === \"select\") {\n // Ground truth first: \"the actor read 0 rows\" is only evidence if there was\n // something to read. Against an empty table it is indistinguishable from a\n // working policy, which is the same vacuity trap the write probes guard.\n const existing = await countMatchingAsService(\n sb,\n probe.table,\n probe.filter ?? {},\n );\n if (existing === 0) {\n recordAssertion({\n kind: \"authorization\",\n target,\n operation: probe.op,\n passed: false,\n status: \"incomplete\",\n role: \"primary\",\n detail: `no rows in ${probe.table} matching ${JSON.stringify(probe.filter ?? {})}, so \"${actorCreds.label} read 0 rows\" proves nothing`,\n });\n throw proofFail(\n \"authorization_vacuous\",\n `at least one row in \"${probe.table}\" matching ${JSON.stringify(probe.filter ?? {})} for the select probe to be denied access to`,\n \"0 row(s) — there was nothing for the actor to fail to read\",\n {\n suggestion: `Seed the fixture row(s) with the service-role client before probing. Reading zero rows from an empty table is not evidence that RLS filtered anything.`,\n },\n );\n }\n\n let query = client.from(probe.table).select(\"*\");\n for (const [k, v] of Object.entries(probe.filter ?? {})) {\n query = query.eq(k, v);\n }\n const { data, error, status } = await query;\n failIfInfrastructure(\n error,\n status,\n \"authorization_setup\",\n `the actor's SELECT on ${probe.table}`,\n );\n const rows = data ?? [];\n\n if (!error && rows.length > 0) {\n recordAssertion({\n kind: \"authorization\",\n target,\n operation: probe.op,\n passed: false,\n role: \"primary\",\n detail: `actor ${actorCreds.label} SELECTed ${rows.length} row(s) from ${probe.table}; expected RLS to filter to 0`,\n });\n throw proofFail(\n \"authorization\",\n `0 rows from ${probe.table} when selected by ${actorCreds.label} with filter ${JSON.stringify(probe.filter ?? {})}`,\n `${rows.length} row(s)`,\n {\n suggestion: `Check SELECT RLS policy on ${probe.table}. The actor should not have read access to these rows.`,\n },\n );\n }\n\n recordAssertion({\n kind: \"authorization\",\n target,\n operation: probe.op,\n passed: true,\n role: \"primary\",\n detail: `${actorCreds.label} SELECT on ${probe.table} returned 0 of the ${existing} row(s) that exist (RLS honored)${error ? `; the query itself errored: ${error.message}` : \"\"}`,\n });\n return;\n }\n\n if (probe.op === \"insert\") {\n if (!probe.payload) {\n throw new Error(\n \"[PROOF_FAIL] bad_probe: rls.op='insert' requires `payload`\\n\" +\n \" file: src/playwright/assert.ts\",\n );\n }\n\n // Deliberately NO .select() here.\n //\n // `.select()` makes PostgREST add a RETURNING clause, and RETURNING must\n // additionally satisfy the table's SELECT policy. On a workspace-scoped\n // table the freshly-inserted row usually CANNOT satisfy it — a policy like\n // `workspace_id IN (SELECT get_user_workspace_ids(auth.uid()))` calls a\n // STABLE function that cannot see the row being written — so the whole\n // statement fails with 42501 even when the INSERT itself was permitted.\n //\n // Trusting that error means reporting a wide-open table as \"denied\", which\n // is exactly how a self-join hole survived its own regression proof. An\n // attacker simply omits the RETURNING and the row lands.\n const { error, status } = await client\n .from(probe.table)\n .insert(probe.payload);\n failIfInfrastructure(\n error,\n status,\n \"authorization_setup\",\n `the actor's INSERT on ${probe.table}`,\n );\n\n // Ground truth: did a row actually land? Only the service role can answer,\n // because the actor may be unable to read what it just wrote.\n const committed = await countMatchingAsService(\n sb,\n probe.table,\n probe.payload,\n );\n\n if (committed > 0) {\n // Remove the row so teardown and any later probes see a clean table.\n await applyEq(sb.from(probe.table).delete(), probe.payload);\n\n recordAssertion({\n kind: \"authorization\",\n target,\n operation: probe.op,\n passed: false,\n role: \"primary\",\n detail: `actor ${actorCreds.label} INSERT on ${probe.table} COMMITTED ${committed} row(s)${error ? ` even though the API returned \"${error.message}\"` : \"\"}; expected RLS/policy denial`,\n });\n throw proofFail(\n \"authorization\",\n `INSERT on ${probe.table} to be denied for ${actorCreds.label}`,\n error\n ? `the API returned an error (\"${error.message}\") but a service-role read found ${committed} row(s) committed — the write went through`\n : `insert succeeded (${committed} row(s) committed)`,\n {\n suggestion: `Check INSERT RLS policy and grants on ${probe.table}.`,\n },\n );\n }\n\n recordAssertion({\n kind: \"authorization\",\n target,\n operation: probe.op,\n passed: true,\n role: \"primary\",\n detail: `${actorCreds.label} INSERT on ${probe.table} denied${error ? ` (${error.message})` : \"\"}; service-role read confirmed 0 row(s) committed`,\n });\n return;\n }\n\n // update / delete.\n //\n // As with insert, the number of rows PostgREST hands back is not evidence:\n // RETURNING is filtered by the SELECT policy, so a committed write can report\n // zero affected rows. The verdict therefore comes from comparing the table's\n // state before and after using the service role.\n if (!probe.filter) {\n throw new Error(\n `[PROOF_FAIL] bad_probe: rls.op='${probe.op}' requires \\`filter\\`\\n` +\n \" file: src/playwright/assert.ts\",\n );\n }\n\n if (probe.op === \"delete\") {\n const before = await countMatchingAsService(sb, probe.table, probe.filter);\n\n // Both guards run BEFORE the delete is attempted: a probe that cannot be\n // graded should not also mutate the fixture it was pointed at.\n //\n // A probe against rows that do not exist proves nothing: 0 → 0 looks\n // identical to a perfectly enforced policy.\n if (before === 0) {\n recordAssertion({\n kind: \"authorization\",\n target,\n operation: probe.op,\n passed: false,\n role: \"primary\",\n detail: `no rows in ${probe.table} matched ${JSON.stringify(probe.filter)} before the DELETE probe, so the probe could not have failed`,\n });\n throw proofFail(\n \"authorization_vacuous\",\n `at least one row in \"${probe.table}\" matching ${JSON.stringify(probe.filter)} so the DELETE has a target`,\n \"0 matching rows before the probe ran\",\n {\n suggestion: `Seed the row you expect the actor to be unable to delete before calling assert.authorization.`,\n },\n );\n }\n\n await requireWriteTargetVisible({\n client,\n probe,\n target,\n filter: probe.filter,\n existing: before,\n actorLabel: actorCreds.label,\n });\n\n const { error, status } = await applyEq(\n client.from(probe.table).delete(),\n probe.filter,\n );\n failIfInfrastructure(\n error,\n status,\n \"authorization_setup\",\n `the actor's DELETE on ${probe.table}`,\n );\n const after = await countMatchingAsService(sb, probe.table, probe.filter);\n\n if (after < before) {\n recordAssertion({\n kind: \"authorization\",\n target,\n operation: probe.op,\n passed: false,\n role: \"primary\",\n detail: `actor ${actorCreds.label} DELETE on ${probe.table} removed ${before - after} row(s) (${before} → ${after})${error ? ` even though the API returned \"${error.message}\"` : \"\"}; expected none`,\n });\n throw proofFail(\n \"authorization\",\n `0 rows removed from ${probe.table} by ${actorCreds.label}`,\n `${before - after} row(s) removed (${before} → ${after})`,\n {\n suggestion: `Check the DELETE RLS policy on ${probe.table}.`,\n },\n );\n }\n\n recordAssertion({\n kind: \"authorization\",\n target,\n operation: probe.op,\n passed: true,\n role: \"primary\",\n detail: `${actorCreds.label} DELETE on ${probe.table} removed 0 of ${before} matching row(s)${error ? ` (${error.message})` : \"\"}; service-role recount confirmed ${after} (RLS honored)`,\n });\n return;\n }\n\n // update\n if (!probe.payload || Object.keys(probe.payload).length === 0) {\n throw new Error(\n \"[PROOF_FAIL] bad_probe: rls.op='update' requires a non-empty `payload`\\n\" +\n \" file: src/playwright/assert.ts\\n\" +\n \" suggestion: The payload is both the attempted change and the way the probe verifies whether it was applied.\",\n );\n }\n\n const columns = Object.keys(probe.payload);\n\n const readTargetRows = async () => {\n const { data, error } = await applyEq(\n sb.from(probe.table).select(columns.join(\",\")),\n probe.filter as Record<string, unknown>,\n );\n if (error) {\n throw proofFail(\n \"authorization_setup\",\n `to read ${columns.join(\", \")} from \"${probe.table}\" with the service role`,\n `Supabase error: ${error.message}`,\n {\n suggestion: `The probe compares these columns before and after the update to decide whether the write landed.`,\n },\n );\n }\n return (data ?? []) as unknown as Array<Record<string, unknown>>;\n };\n\n const rowMatchesPayload = (row: Record<string, unknown>) =>\n columns.every(\n (c) => JSON.stringify(row[c]) === JSON.stringify(probe.payload?.[c]),\n );\n\n const before = await readTargetRows();\n\n if (before.length === 0) {\n recordAssertion({\n kind: \"authorization\",\n target,\n operation: probe.op,\n passed: false,\n role: \"primary\",\n detail: `no rows in ${probe.table} matched ${JSON.stringify(probe.filter)} before the UPDATE probe, so the probe could not have failed`,\n });\n throw proofFail(\n \"authorization_vacuous\",\n `at least one row in \"${probe.table}\" matching ${JSON.stringify(probe.filter)} so the UPDATE has a target`,\n \"0 matching rows before the probe ran\",\n {\n suggestion: `Seed the row you expect the actor to be unable to modify before calling assert.authorization.`,\n },\n );\n }\n\n await requireWriteTargetVisible({\n client,\n probe,\n target,\n filter: probe.filter,\n existing: before.length,\n actorLabel: actorCreds.label,\n });\n\n const { error, status } = await applyEq(\n client.from(probe.table).update(probe.payload),\n probe.filter,\n );\n failIfInfrastructure(\n error,\n status,\n \"authorization_setup\",\n `the actor's UPDATE on ${probe.table}`,\n );\n const after = await readTargetRows();\n\n const beforeApplied = before.filter(rowMatchesPayload).length;\n const afterApplied = after.filter(rowMatchesPayload).length;\n\n if (afterApplied > beforeApplied) {\n recordAssertion({\n kind: \"authorization\",\n target,\n operation: probe.op,\n passed: false,\n role: \"primary\",\n detail: `actor ${actorCreds.label} UPDATE on ${probe.table} applied ${columns.join(\", \")} to ${afterApplied - beforeApplied} row(s)${error ? ` even though the API returned \"${error.message}\"` : \"\"}; expected the write to be rejected`,\n });\n throw proofFail(\n \"authorization\",\n `${columns.join(\", \")} on ${probe.table} to be unchanged for ${actorCreds.label}`,\n `${afterApplied - beforeApplied} row(s) now hold the attempted value(s) ${JSON.stringify(probe.payload)}`,\n {\n suggestion: `Check the UPDATE RLS policy and any column guard on ${probe.table}.`,\n },\n );\n }\n\n recordAssertion({\n kind: \"authorization\",\n target,\n operation: probe.op,\n passed: true,\n role: \"primary\",\n detail: `${actorCreds.label} UPDATE on ${probe.table} left ${columns.join(\", \")} unchanged across ${before.length} targeted row(s)${error ? ` (${error.message})` : \"\"}; verified by service-role re-read (RLS honored)`,\n });\n}\n\n/**\n * Outcome of a single-direction isolation probe.\n *\n * - `proved` — the isolated org held at least one row, its own member\n * could read those rows, and the outsider could not.\n * - `skipped_no_data` — the isolated org held no rows, so there was nothing\n * to leak. NOTHING is recorded: a probe with no data\n * cannot prove isolation, and recording a pass here is\n * precisely the false-confidence bug this guards.\n */\ntype IsolationProbeOutcome = \"proved\" | \"skipped_no_data\";\n\n/**\n * Count the rows belonging to one tenant using the service role, bypassing RLS.\n * This is the ground truth the probe is measured against.\n */\nasync function countRowsAsService(\n sb: SupabaseClient,\n table: string,\n filter: Record<string, unknown>,\n): Promise<number> {\n const { count, error } = await applyEq(\n sb.from(table).select(\"*\", { count: \"exact\", head: true }),\n filter,\n );\n\n if (error) {\n throw proofFail(\n \"tenant_isolation_setup\",\n `to count rows in \"${table}\" with the service role to establish ground truth`,\n `Supabase error: ${error.message}`,\n {\n suggestion: `Confirm \"${table}\" exists and supports the planner-owned filter ${JSON.stringify(filter)}. Tenant isolation cannot be measured without first knowing how many matching rows are actually there.`,\n },\n );\n }\n return count ?? 0;\n}\n\nasync function probeTenantIsolation(args: {\n table: string;\n viewer: SeedUser;\n viewerLabel: string;\n /** Owner of the isolated rows; drives the positive control. */\n owner: SeedUser;\n ownerLabel: string;\n /** Column + value identifying the tenant whose rows must stay hidden. */\n scopeColumn: string;\n scopeValue: string;\n scopeLabel: string;\n criterion?: TenantIsolationBaseOptions[\"criterion\"];\n /** The canonical RLS pattern for this scope, quoted in failure suggestions. */\n scopePattern: string;\n /** Service-role client, used only to establish ground truth. */\n sb: SupabaseClient;\n}): Promise<IsolationProbeOutcome> {\n const {\n table,\n viewer,\n viewerLabel,\n owner,\n ownerLabel,\n scopeColumn,\n scopeValue,\n scopeLabel,\n criterion,\n scopePattern,\n sb,\n } = args;\n const filter = {\n [scopeColumn]: scopeValue,\n ...(criterion?.where ?? {}),\n };\n const criterionDetail = criterion\n ? ` matching planner criterion \"${criterion.description}\" (${JSON.stringify(criterion.where)})`\n : \"\";\n\n // ---- Ground truth: is there anything here to leak? ----------------------\n // Without this, \"the outsider saw 0 rows\" is unfalsifiable — an empty table\n // produces exactly the same observation as perfectly-enforced RLS.\n const actualRowCount = await countRowsAsService(sb, table, filter);\n if (actualRowCount === 0) {\n return \"skipped_no_data\";\n }\n\n // ---- Positive control: can the org's OWN member read those rows? --------\n // This is what separates \"RLS hid the rows from an outsider\" from \"nobody\n // can read this table at all\" (blanket-deny policy, missing grant, typo'd\n // table name). Both make the negative probe below return zero rows; only the\n // first one is tenant isolation.\n const ownerClient = await actAsUser.supabaseClient({\n email: owner.email,\n password: owner.password,\n });\n const {\n data: ownRows,\n error: ownError,\n status: ownStatus,\n } = await applyEq(ownerClient.from(table).select(\"*\"), filter);\n failIfInfrastructure(\n ownError,\n ownStatus,\n \"tenant_isolation_setup\",\n `the owner's control SELECT on ${table}`,\n );\n\n if (ownError || (ownRows?.length ?? 0) === 0) {\n recordAssertion({\n kind: \"tenant_isolation\",\n target: table,\n operation: \"select\",\n passed: false,\n role: \"control\",\n detail: `positive control FAILED: ${ownerLabel} (owner of ${scopeLabel}) read ${ownRows?.length ?? 0} of ${actualRowCount} row(s) in ${table}${criterionDetail}${ownError ? `; error: ${ownError.message}` : \"\"}`,\n });\n throw proofFail(\n \"tenant_isolation_control\",\n `${ownerLabel} to read the ${actualRowCount} row(s) that exist in \"${table}\" for ${scopeLabel}${criterionDetail}`,\n ownError\n ? `Supabase error: ${ownError.message}`\n : `0 row(s) — their own data is invisible to them`,\n {\n suggestion:\n `The negative probe would have \"passed\" here for the wrong reason: if the owner cannot read their own rows, ` +\n `then an outsider seeing 0 rows proves nothing. Check the SELECT policy/grant on \"${table}\" — the canonical pattern for ` +\n `this scope is ${scopePattern}. If this table is intentionally service-role-only, it is not a ` +\n `tenant-isolation target: use assert.authorization instead.`,\n },\n );\n }\n\n recordAssertion({\n kind: \"tenant_isolation\",\n target: table,\n operation: \"select\",\n passed: true,\n role: \"control\",\n detail: `${ownerLabel} read ${ownRows?.length ?? 0} of ${actualRowCount} row(s) in ${table} for ${scopeLabel}${criterionDetail} — the probe can observe these rows, so a leak would be detected`,\n });\n\n // ---- Negative probe: the outsider must see none of them -----------------\n const client = await actAsUser.supabaseClient({\n email: viewer.email,\n password: viewer.password,\n });\n\n const {\n data: leakedRows,\n error: queryError,\n status: queryStatus,\n } = await applyEq(client.from(table).select(\"*\"), filter);\n failIfInfrastructure(\n queryError,\n queryStatus,\n \"tenant_isolation_setup\",\n `the outsider's SELECT on ${table}`,\n );\n\n if (queryError) {\n recordAssertion({\n kind: \"tenant_isolation\",\n target: table,\n operation: \"select\",\n passed: false,\n role: \"primary\",\n detail: `${viewerLabel} select on ${table} scoped to ${scopeLabel}${criterionDetail} failed: ${queryError.message}`,\n });\n throw proofFail(\n \"tenant_isolation\",\n `query against ${table} as ${viewerLabel} to succeed (possibly returning 0 rows)`,\n `Supabase error: ${queryError.message}`,\n {\n suggestion: `Verify \"${table}\" has a ${scopeColumn} column and a SELECT grant for authenticated users. Tenant isolation cannot be proven if the query itself is denied at the grant level.`,\n },\n );\n }\n\n if (leakedRows && leakedRows.length > 0) {\n const sampleIds = leakedRows\n .slice(0, 3)\n .map((r: { id?: string | number }) => r.id ?? \"?\")\n .join(\", \");\n\n recordAssertion({\n kind: \"tenant_isolation\",\n target: table,\n operation: \"select\",\n passed: false,\n role: \"primary\",\n detail: `${viewerLabel} saw ${leakedRows.length} row(s) in ${table} belonging to ${scopeLabel}${criterionDetail} (sample ids: ${sampleIds})`,\n });\n throw proofFail(\n \"tenant_isolation\",\n `0 rows in \"${table}\" for ${viewerLabel} filtered to ${JSON.stringify(filter)}`,\n `${leakedRows.length} row(s) (sample ids: ${sampleIds})`,\n {\n suggestion: `Inspect the RLS policy on \"${table}\". The canonical pattern for this scope is ${scopePattern}.`,\n },\n );\n }\n\n recordAssertion({\n kind: \"tenant_isolation\",\n target: table,\n operation: \"select\",\n passed: true,\n role: \"primary\",\n detail: `${viewerLabel} saw 0 of the ${actualRowCount} row(s) that exist in ${table} for ${scopeLabel}${criterionDetail}, which ${ownerLabel} can read (RLS honored)`,\n });\n\n return \"proved\";\n}\n\nasync function runActionProbe(\n probe: ActionProbe,\n actorCreds: { email: string; password: string; label: string },\n page: Page,\n): Promise<void> {\n const target = probe.target ?? `${probe.module}:${probe.name}`;\n const kind = probe.kind ?? \"authorization\";\n const result = await actAsUser.invokeAction(page, {\n as:\n actorCreds.label === \"seed:admin\"\n ? \"admin\"\n : actorCreds.label === \"seed:member\"\n ? \"member\"\n : { email: actorCreds.email, password: actorCreds.password },\n module: probe.module,\n action: probe.name,\n inputParams: probe.inputParams,\n });\n\n if (result.success) {\n recordAssertion({\n kind,\n target,\n operation: \"invoke\",\n passed: false,\n role: \"primary\",\n detail: `actor ${actorCreds.label} successfully invoked ${target}; expected action-layer rejection`,\n });\n throw proofFail(\n kind,\n `action \"${target}\" to return { success: false } for ${actorCreds.label}`,\n \"action returned { success: true }\",\n {\n suggestion: `Check withRBAC / role-check logic inside ${probe.module}/${probe.name}. The action should reject the actor.`,\n },\n );\n }\n\n if (\n probe.expectedErrorMatch &&\n !probe.expectedErrorMatch.test(result.error)\n ) {\n recordAssertion({\n kind,\n target,\n operation: \"invoke\",\n passed: false,\n role: \"primary\",\n detail: `action \"${target}\" rejected ${actorCreds.label} but with unexpected message: ${result.error}`,\n });\n throw proofFail(\n kind,\n `rejection message matching ${probe.expectedErrorMatch}`,\n `error: \"${result.error}\"`,\n {\n suggestion: `The action rejected the caller (good) but not for the expected reason. Confirm the proof is asserting on the right invariant.`,\n },\n );\n }\n\n recordAssertion({\n kind,\n target,\n operation: \"invoke\",\n passed: true,\n role: \"primary\",\n detail: `action \"${target}\" rejected ${actorCreds.label} with: ${result.error}`,\n });\n}\n\nasync function runSuccessfulActionProbe<TData>(\n opts: ActionSucceedsOptions,\n): Promise<TData> {\n const actorCreds = describeActor(opts.actor);\n const target =\n opts.action.target ?? `${opts.action.module}:${opts.action.name}`;\n const kind = opts.kind ?? opts.action.kind ?? \"happy_path\";\n const role = opts.role ?? \"control\";\n const result = await actAsUser.invokeAction<TData>(opts.page, {\n as:\n actorCreds.label === \"seed:admin\"\n ? \"admin\"\n : actorCreds.label === \"seed:member\"\n ? \"member\"\n : { email: actorCreds.email, password: actorCreds.password },\n module: opts.action.module,\n action: opts.action.name,\n inputParams: opts.action.inputParams,\n });\n\n if (!result.success) {\n recordAssertion({\n kind,\n target,\n operation: \"invoke\",\n passed: false,\n role,\n detail: `action \"${target}\" rejected ${actorCreds.label}; expected success: ${result.error}`,\n });\n throw proofFail(\n kind,\n `action \"${target}\" to return { success: true } for ${actorCreds.label}`,\n `action returned { success: false, error: \"${result.error}\" }`,\n {\n suggestion: `A refusal proof needs a working positive path. Check the action registry, validation input, and the permissions for ${actorCreds.label}.`,\n },\n );\n }\n\n recordAssertion({\n kind,\n target,\n operation: \"invoke\",\n passed: true,\n role,\n detail: `action \"${target}\" succeeded for ${actorCreds.label}`,\n });\n return result.data;\n}\n\nconst assertMethods = {\n /**\n * Prove that workspace-scoped data in `table` is invisible to members of a\n * different workspace. See file header for full semantics.\n *\n * Records two assertions per proved direction: a `control` showing the org's\n * own member CAN read the rows, and the `primary` showing the outsider\n * cannot. Only the `primary` can satisfy a mission's `trace_must_prove`\n * entry.\n *\n * Fails rather than passing vacuously when `setup()` leaves orgA empty.\n */\n async tenantIsolation(opts: TenantIsolationOptions): Promise<void> {\n const { table, page } = opts;\n const tag = opts.tag ?? randomSuffix();\n const scope = opts.scope ?? \"workspace\";\n const scopeColumn =\n opts.scopeColumn ?? (scope === \"user\" ? \"user_id\" : \"workspace_id\");\n validateTenantIsolationCriterion(table, scopeColumn, opts.criterion);\n\n const orgA = await seed.workspace(`Proof OrgA ${tag}`);\n const orgB = await seed.workspace(`Proof OrgB ${tag}`);\n\n const userA = await seed.user({\n email: `proof-a-${tag}@proof.test`,\n password: `ProofPass!${tag}1`,\n workspace: orgA,\n role: \"owner\",\n });\n const userB = await seed.user({\n email: `proof-b-${tag}@proof.test`,\n password: `ProofPass!${tag}1`,\n workspace: orgB,\n role: \"owner\",\n });\n\n const sb = createProofServiceClient();\n\n // Which column carries the tenant, and what each side's value is. Personal\n // data is scoped by user, workspace data by workspace; `workspaces` and\n // `users` carry their scope on the primary key, hence the override.\n const scopePattern =\n scope === \"user\"\n ? \"user_id = auth.uid()\"\n : \"workspace_id IN (SELECT get_user_workspace_ids(auth.uid()))\";\n const sideA = scope === \"user\" ? userA.id : orgA.id;\n const sideB = scope === \"user\" ? userB.id : orgB.id;\n const labelA = scope === \"user\" ? \"user A's rows\" : \"orgA\";\n const labelB = scope === \"user\" ? \"user B's rows\" : \"orgB\";\n\n try {\n const fixtureContext: ProofFixtureContext = {\n orgA,\n orgB,\n userA,\n userB,\n sb,\n };\n\n if (opts.fixture && opts.fixture.table !== table) {\n recordAssertion({\n kind: \"tenant_isolation\",\n target: table,\n operation: \"select\",\n passed: false,\n status: \"incomplete\",\n role: \"primary\",\n detail: `fixture for \"${opts.fixture.table}\" cannot seed proof target \"${table}\"`,\n });\n throw proofFail(\n \"fixture_factory_mismatch\",\n `the imported fixture factory to declare table \"${table}\"`,\n `factory declares \"${opts.fixture.table}\"`,\n {\n file: `e2e/fixtures/${table}.ts`,\n suggestion:\n `Import the factory owned by \"${table}\", or correct its table declaration. ` +\n `A copied factory must not silently seed one table while the proof probes another.`,\n },\n );\n }\n\n try {\n // Fixture factories and setup callbacks are executor-authored code\n // running inside this helper. Provenance is suspended around them so a\n // hostile callback calling recordAssertion cannot inherit this\n // helper's trusted stamp.\n if (opts.fixture) {\n await withoutAssertionProvenance(() =>\n opts.fixture!.create(fixtureContext),\n );\n } else {\n await withoutAssertionProvenance(() => opts.setup!(fixtureContext));\n }\n } catch (error) {\n if (!isProofFixturePendingError(error)) throw error;\n\n recordAssertion({\n kind: \"tenant_isolation\",\n target: table,\n operation: \"select\",\n passed: false,\n status: \"incomplete\",\n role: \"primary\",\n detail: `fixture factory incomplete: ${error.reason}`,\n });\n throw proofFail(\n error.code,\n `a completed fixture factory for \"${table}\"`,\n error.reason,\n {\n file: `e2e/fixtures/${table}.ts`,\n suggestion:\n `Replace pendingProofFixture(...) with defineProofFixture(...), using values valid for the final schema. ` +\n `Resolve required foreign keys from seeded/existing rows and honor NOT NULL, CHECK, domain, and enum constraints. ` +\n `Do not weaken a product constraint to make the proof easier to seed.`,\n },\n );\n }\n\n // ---- Primary direction: orgA holds the data, userB must not see it ---\n // `setup()` is contracted to populate orgA, so an empty orgA here means\n // the proof is measuring nothing. Fail loudly rather than record the\n // vacuous pass — a green \"tenant isolation holds\" on a table with no\n // rows is worse than no proof at all, because it gets believed.\n const forward = await probeTenantIsolation({\n table,\n viewer: userB,\n viewerLabel: \"user B\",\n owner: userA,\n ownerLabel: \"user A\",\n scopeColumn,\n scopeValue: sideA,\n scopeLabel: labelA,\n criterion: opts.criterion,\n scopePattern,\n sb,\n });\n\n if (forward === \"skipped_no_data\") {\n recordAssertion({\n kind: \"tenant_isolation\",\n target: table,\n operation: \"select\",\n passed: false,\n status: \"incomplete\",\n role: \"primary\",\n detail:\n `fixture setup created 0 rows in \"${table}\" where ${scopeColumn} = ${sideA}` +\n (opts.criterion\n ? ` matching planner criterion \"${opts.criterion.description}\" (${JSON.stringify(opts.criterion.where)})`\n : \"\"),\n });\n throw proofFail(\n \"tenant_isolation_vacuous\",\n `setup() to create at least one \"${table}\" row for ${labelA}${opts.criterion ? ` matching planner criterion \"${opts.criterion.description}\"` : \"\"} so there is something for user B to fail to see`,\n `0 rows in \"${table}\" matching ${JSON.stringify({\n [scopeColumn]: sideA,\n ...(opts.criterion?.where ?? {}),\n })} after setup() returned`,\n {\n suggestion:\n `An empty table cannot demonstrate isolation: user B seeing 0 rows would be indistinguishable from working RLS. ` +\n `Insert the fixture row(s) in e2e/fixtures/${table}.ts (or the inline setup) using the provided service-role client (\\`sb\\`), and make sure they carry ` +\n `${scopeColumn} = ${sideA}${opts.criterion ? ` and satisfy planner criterion \"${opts.criterion.description}\" (${JSON.stringify(opts.criterion.where)})` : \"\"}.`,\n },\n );\n }\n\n // ---- Reverse direction: only meaningful if the caller populated orgB -\n // Legitimately skipped for the common single-org setup. Skipping records\n // NOTHING, which is the point: the old behaviour recorded a passing\n // assertion for this probe even against an empty orgB, and that pass was\n // enough on its own to satisfy a mission's trace requirement.\n const reverse = await probeTenantIsolation({\n table,\n viewer: userA,\n viewerLabel: \"user A\",\n owner: userB,\n ownerLabel: \"user B\",\n scopeColumn,\n scopeValue: sideB,\n scopeLabel: labelB,\n criterion: opts.criterion,\n scopePattern,\n sb,\n });\n\n if (reverse === \"skipped_no_data\") {\n // Recorded, not just logged: a direction that was never measured is a\n // real state, and a consumer reading the trace must be able to see it\n // rather than inferring it from one fewer passing assertion.\n recordAssertion({\n kind: \"tenant_isolation\",\n target: table,\n operation: \"select\",\n passed: false,\n status: \"skipped\",\n role: \"primary\",\n detail:\n `reverse direction (A→B) not measured: setup() left ${labelB}${opts.criterion ? ` without rows matching planner criterion \"${opts.criterion.description}\"` : \" empty\"}, so there is nothing for user A to fail to see. ` +\n `Populate both sides in setup() to prove isolation in both directions.`,\n });\n console.log(\n `[proof] tenantIsolation(${table}): reverse probe (A→B) skipped — setup() left ${labelB}${opts.criterion ? ` without rows matching criterion \"${opts.criterion.description}\"` : \" empty\"}, ` +\n `so there is nothing for user A to fail to see. The A→B direction is unproven; ` +\n `populate it in setup() if you need it proven in both directions.`,\n );\n }\n\n // ---- Optional check: secondary UI assertion -----------------------\n if (page) {\n await actAsUser.loginAs(page, userB.email, userB.password);\n // Intentionally no product-specific navigation. The login itself\n // asserts the auth cookie roundtrip; finally logs out this page before\n // teardown deletes the disposable auth users.\n }\n } finally {\n try {\n if (page) {\n await actAsUser.logout(page);\n }\n } finally {\n await teardown(orgA, orgB, userA, userB);\n }\n }\n },\n\n /**\n * Prove that an operation is rejected when performed by an unauthorized\n * actor. Can probe either the RLS layer (direct Supabase client), the\n * action layer (via POST /api/proof/invoke-action), or both.\n *\n * RLS probes record `kind: \"authorization\"`. Action probes default to the\n * same kind but may set `action.kind` to the withProof invariant they\n * establish. The helper throws on the first probe that SUCCEEDS when it was\n * expected to fail — that's the PROOF_FAIL signal.\n *\n * Caller is responsible for seeding any fixture rows needed; this helper\n * creates no users or workspaces on its own.\n *\n * @example\n * // Action-layer: member cannot remove admin\n * await assert.authorization({\n * actor: { email: member.email, password: member.password },\n * action: {\n * module: \"workspace\",\n * name: \"removeMember\",\n * inputParams: { formData: { workspaceId, memberId } },\n * expectedErrorMatch: /permission|unauthorized/i,\n * },\n * page,\n * });\n *\n * @example\n * // RLS-layer: member cannot DELETE admin's workspace_members row\n * await assert.authorization({\n * actor: { email: member.email, password: member.password },\n * rls: {\n * table: \"workspace_members\",\n * op: \"delete\",\n * filter: { user_id: admin.id, workspace_id: orgA.id },\n * },\n * });\n */\n async authorization(opts: AuthorizationOptions): Promise<void> {\n if (!opts.rls && !opts.action) {\n throw new Error(\n \"[PROOF_FAIL] bad_options: assert.authorization requires at least one of `rls` or `action`\\n\" +\n \" file: src/playwright/assert.ts\\n\" +\n \" suggestion: Pass an `rls` probe for DB-layer denial or an `action` probe for action-layer denial (or both).\",\n );\n }\n if (opts.action && !opts.page) {\n throw new Error(\n \"[PROOF_FAIL] bad_options: assert.authorization's `action` probe requires a Playwright `page`\\n\" +\n \" file: src/playwright/assert.ts\\n\" +\n \" suggestion: Destructure `page` from the test callback and pass it in.\",\n );\n }\n\n const actorCreds = describeActor(opts.actor);\n\n if (opts.rls) {\n await runRlsProbe(opts.rls, actorCreds);\n }\n if (opts.action && opts.page) {\n await runActionProbe(opts.action, actorCreds, opts.page);\n }\n },\n\n /**\n * Prove that a registered action succeeds for an actor who should be allowed.\n *\n * This is the positive-control companion to action-level refusal probes.\n * Assertions default to `role: \"control\"`, so a working happy path cannot\n * satisfy the mission's primary requirement by itself. Override `role` only\n * when success is the primary invariant being proved.\n *\n * Returns the action's data so the caller can make domain-specific checks.\n */\n async actionSucceeds<TData = unknown>(\n opts: ActionSucceedsOptions,\n ): Promise<TData> {\n return runSuccessfulActionProbe<TData>(opts);\n },\n\n /**\n * Assert on an HTTP response from a page route. Streaming-RSC aware: when\n * only body markers (`mustContain` / `mustNotContain`) are provided, the\n * helper does NOT check the status code — crucial for routes guarded by\n * a server component's `notFound()` call that Next.js can only signal via\n * an embedded NOT_FOUND marker because headers were already committed as\n * 200 by `loading.tsx`. See\n * [proof-authoring.mdc](mdc:.cursor/rules/proof-authoring.mdc) for the\n * streaming-RSC gotcha in full.\n *\n * Defaults:\n * - method: \"GET\"\n * - maxRedirects: 0 (intentional — silent redirect-following has caused\n * real false passes on auth-gated pages)\n *\n * Records one TraceAssertion with caller-provided `kind` and `target`.\n * Returns the raw Playwright APIResponse so callers can run additional\n * custom checks (headers, etc.) after.\n *\n * @example\n * // Streaming-RSC page: assert body markers, not status code\n * await assert.httpResponse({\n * page,\n * path: \"/secret-admin\",\n * as: \"member\",\n * kind: \"authorization\",\n * target: \"/secret-admin\",\n * expect: {\n * mustContain: [/Sorry, we could not find this page/i],\n * mustNotContain: [/Here you can see what.?s happening on your platform/i],\n * },\n * });\n *\n * @example\n * // Flat API route: assert status\n * await assert.httpResponse({\n * page,\n * path: \"/api/public-ping\",\n * kind: \"happy_path\",\n * expect: { status: 200 },\n * });\n */\n async httpResponse(opts: {\n page: Page;\n path: string;\n kind: ProofKind;\n target?: string;\n /**\n * Evidential role of the recorded assertion. Defaults to `\"primary\"`.\n * Pass `\"control\"` when this request exists only to show the probe could\n * have failed — e.g. \"the admin CAN reach the page a member was refused\" —\n * so it cannot satisfy a mission's `trace_must_prove` entry by itself.\n */\n role?: AssertionRole;\n as?: \"admin\" | \"member\" | { email: string; password: string };\n method?: \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\";\n expect: {\n status?: number;\n mustContain?: RegExp[];\n mustNotContain?: RegExp[];\n };\n }): Promise<APIResponse> {\n const target = opts.target ?? opts.path;\n const method = opts.method ?? \"GET\";\n const role: AssertionRole = opts.role ?? \"primary\";\n\n if (opts.as !== undefined) {\n if (typeof opts.as === \"string\") {\n await actAsUser.login(opts.page, opts.as);\n } else {\n await actAsUser.loginAs(opts.page, opts.as.email, opts.as.password);\n }\n }\n\n const requestCtx = opts.page.request;\n const requestOpts = { maxRedirects: 0 } as const;\n\n let response: APIResponse;\n switch (method) {\n case \"GET\":\n response = await requestCtx.get(opts.path, requestOpts);\n break;\n case \"POST\":\n response = await requestCtx.post(opts.path, requestOpts);\n break;\n case \"PUT\":\n response = await requestCtx.put(opts.path, requestOpts);\n break;\n case \"PATCH\":\n response = await requestCtx.patch(opts.path, requestOpts);\n break;\n case \"DELETE\":\n response = await requestCtx.delete(opts.path, requestOpts);\n break;\n }\n\n const status = response.status();\n let body: string | null = null;\n\n const failures: string[] = [];\n\n if (opts.expect.status !== undefined && status !== opts.expect.status) {\n failures.push(\n `status mismatch: expected ${opts.expect.status}, got ${status}`,\n );\n }\n\n if (\n (opts.expect.mustContain && opts.expect.mustContain.length > 0) ||\n (opts.expect.mustNotContain && opts.expect.mustNotContain.length > 0)\n ) {\n body = await response.text();\n\n for (const re of opts.expect.mustContain ?? []) {\n if (!re.test(body)) {\n failures.push(`mustContain not matched: ${re}`);\n }\n }\n for (const re of opts.expect.mustNotContain ?? []) {\n if (re.test(body)) {\n failures.push(\n `mustNotContain matched (should have been absent): ${re}`,\n );\n }\n }\n }\n\n if (failures.length > 0) {\n recordAssertion({\n kind: opts.kind,\n target,\n operation: \"request\",\n passed: false,\n role,\n detail: `${method} ${opts.path} -> ${status}; failures: ${failures.join(\"; \")}`,\n });\n throw new Error(\n `[PROOF_FAIL] ${opts.kind}: response from ${method} ${opts.path} did not match expectations\\n` +\n ` status: ${status}\\n` +\n ` failures:\\n${failures.map((f) => ` - ${f}`).join(\"\\n\")}\\n` +\n ` file: src/playwright/assert.ts\\n` +\n ` suggestion: ${opts.expect.mustContain || opts.expect.mustNotContain ? \"For streaming RSC routes, assert on body markers rather than status. See .cursor/rules/proof-authoring.mdc.\" : \"Check the route handler or page component and confirm the expected response shape.\"}`,\n );\n }\n\n recordAssertion({\n kind: opts.kind,\n target,\n operation: \"request\",\n passed: true,\n role,\n detail: `${method} ${opts.path} -> ${status}${opts.expect.mustContain || opts.expect.mustNotContain ? \" (body markers matched)\" : \"\"}`,\n });\n\n return response;\n },\n};\n\n/**\n * Wrap a helper so every assertion it records carries `emittedBy: name`.\n *\n * The stamp travels through `withAssertionProvenance`'s AsyncLocalStorage, so\n * it reaches the shared probe internals (`runRlsProbe`, `runActionProbe`, ...)\n * without threading a name through thirty call sites — and it cannot be set\n * from spec code, because the public `recordAssertion` strips caller-supplied\n * `emittedBy` and `withAssertionProvenance` is not exported from the package\n * entry point.\n */\nfunction withHelperProvenance<TArgs extends unknown[], TResult>(\n name: string,\n fn: (...args: TArgs) => Promise<TResult>,\n): (...args: TArgs) => Promise<TResult> {\n return (...args: TArgs) => withAssertionProvenance(name, () => fn(...args));\n}\n\nexport const assert = {\n tenantIsolation: withHelperProvenance(\n \"assert.tenantIsolation\",\n assertMethods.tenantIsolation,\n ),\n authorization: withHelperProvenance(\n \"assert.authorization\",\n assertMethods.authorization,\n ),\n // Cast preserves the <TData> generic that the wrapper's closed-over\n // signature would otherwise collapse to `unknown`.\n actionSucceeds: withHelperProvenance(\n \"assert.actionSucceeds\",\n assertMethods.actionSucceeds,\n ) as typeof assertMethods.actionSucceeds,\n httpResponse: withHelperProvenance(\n \"assert.httpResponse\",\n assertMethods.httpResponse,\n ),\n};\n"],"mappings":"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","names":["sleep","cached","createClient","createClient","error","status","before","after"]}
|
package/package.json
CHANGED