Skip to content

Route Contract E2E Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Add a deterministic Chromium-only pull-request gate for exact dynamic route HTTP contracts.

Architecture: Build the production frontend against one loopback adapter that emulates the required Supabase and backend calls. Run a dedicated single-worker Playwright project with synthetic sessions, exact status assertions, upstream call-count proof, blocked service workers, and bounded CI lifecycle.

Tech Stack: Next.js 15, Playwright 1.62, Node HTTP server, GitHub Actions.


Task 1: Isolated Chromium project and adapter contract

Files: - Create: frontend/playwright.route-contract.config.ts - Create: frontend/scripts/prepare-route-contract-standalone.mjs - Create: frontend/tests/route-contract-e2e/stub-services.mjs - Create: frontend/tests/unit/test-harness/route-contract-harness.test.ts - Modify: frontend/package.json

  • Step 1: Write the failing project discovery check

Run before creating the config:

cd frontend
CI=1 npx playwright test --config=playwright.route-contract.config.ts --list

Expected: FAIL because the dedicated config does not exist.

  • Step 2: Create the isolated config

Define exactly one project named chromium-route-contract, matching only tests/route-contract-e2e/**/*.spec.ts, with workers: 1, fullyParallel: false, retries: 0, serviceWorkers: "block", and base URL http://127.0.0.1:3006. Configure two Playwright-owned web servers:

webServer: [
  {
    command: "node tests/route-contract-e2e/stub-services.mjs",
    url: "http://127.0.0.1:4311/__route-contract/ready",
    reuseExistingServer: false,
    timeout: 30_000,
    stdout: "pipe",
    stderr: "pipe",
  },
  {
    command: "npm run start:e2e:route-contract",
    url: "http://127.0.0.1:3006",
    reuseExistingServer: false,
    timeout: 60_000,
    stdout: "pipe",
    stderr: "pipe",
    env: {
      PORT: "3006",
      HOSTNAME: "127.0.0.1",
    },
  },
]

Use a real public route for the Next readiness probe if the named sentinel does not exist; readiness must only detect liveness and must not be an asserted test case.

  • Step 3: Implement the minimal loopback adapter

Bind to 127.0.0.1:4311, reject unexpected hosts/methods, and implement:

GET  /__route-contract/ready     -> 200
POST /__route-contract/reset     -> 204 and clear request log
GET  /__route-contract/requests  -> 200 JSON sanitized request log
GET  /auth/v1/user               -> user, invalid-session 401, or outage 503
GET  /rest/v1/chats              -> owner row or empty result
GET  required collection/document/schema/extraction backend paths

Scenario IDs must return deterministic 200/404/422/429/503 responses. Log only method and parsed pathname/query; never log cookies or Authorization headers. Exit non-zero on bind errors and close cleanly on SIGTERM/SIGINT.

  • Step 4: Add the explicit npm lifecycle

Add:

"prepare:e2e:route-contract": "node scripts/prepare-route-contract-standalone.mjs",
"pretest:e2e:route-contract": "npm run prepare:e2e:route-contract",
"start:e2e:route-contract": "node .next/standalone/frontend/server.js",
"lint:route-contract-harness": "eslint playwright.route-contract.config.ts scripts/prepare-route-contract-standalone.mjs tests/route-contract-e2e tests/unit/test-harness/route-contract-harness.test.ts",
"test:e2e:route-contract": "playwright test --config=playwright.route-contract.config.ts --max-failures=1"

The preparation script copies public/ and .next/static/ into .next/standalone/frontend/ after the production build. Include the targeted harness lint command in npm run validate, with only narrow inline exemptions for intentional child-process diagnostics.

  • Step 5: Verify project discovery and adapter readiness

Run:

cd frontend
CI=1 npx playwright test --config=playwright.route-contract.config.ts --list
node tests/route-contract-e2e/stub-services.mjs

Expected: the list contains only chromium-route-contract; the adapter starts on loopback and its readiness endpoint returns 200. Stop the adapter and verify no process remains.

  • Step 6: Commit the infrastructure
git add frontend/playwright.route-contract.config.ts frontend/tests/route-contract-e2e/stub-services.mjs frontend/package.json
git commit -m "test(routes): add isolated chromium harness" -m "Refs #411"

Task 2: Exact route and upstream status matrix

Files: - Create: frontend/tests/route-contract-e2e/route-status.spec.ts - Modify: frontend/tests/route-contract-e2e/stub-services.mjs

  • Step 1: Write the exact failing matrix

Create serial tests for the matrix in the design. Use helpers with explicit signatures:

async function resetAdapter(request: APIRequestContext): Promise<void>;
async function adapterRequests(request: APIRequestContext): Promise<AdapterRequest[]>;
async function setSyntheticSession(context: BrowserContext, mode: "valid" | "invalid" | "outage"): Promise<void>;
async function expectWireStatus(request: APIRequestContext, method: string, path: string, status: number): Promise<APIResponse>;

For page cases, call page.goto and compare response.status() with strict equality. Cover authenticated known chat, collection, document, schema, and extraction pages with route-specific response content and exact adapter method/path/query/count proof. Use a JavaScript-disabled Chromium context for this server-rendering matrix so hydration cannot duplicate reads. For redirects and methods, call request.fetch with maxRedirects: 0. Assert exact Location, Allow, JSON/content type, empty HEAD bodies, final browser URL, adapter method/path, and call count. Assert zero domain reads for anonymous, invalid-session, auth-outage, unknown-route, and unsupported-method cases.

  • Step 2: Run against the production build to verify failure

Run:

cd frontend
NEXT_PUBLIC_SUPABASE_URL=http://127.0.0.1:4311 \
NEXT_PUBLIC_SUPABASE_ANON_KEY=route-contract-anon \
NEXT_PUBLIC_API_BASE_URL=http://127.0.0.1:4311 \
API_BASE_URL=http://127.0.0.1:4311 \
BACKEND_API_KEY=route-contract-backend-key npm run build
CI=1 NEXT_PUBLIC_SUPABASE_URL=http://127.0.0.1:4311 \
NEXT_PUBLIC_SUPABASE_ANON_KEY=route-contract-anon \
NEXT_PUBLIC_API_BASE_URL=http://127.0.0.1:4311 \
API_BASE_URL=http://127.0.0.1:4311 \
BACKEND_API_KEY=route-contract-backend-key npm run test:e2e:route-contract

Expected: initial failures identify the missing adapter endpoints or mismatched fixtures; a generic Next 404 must not satisfy a dynamic missing test because the adapter-call assertion also fails.

  • Step 3: Complete only the adapter fixtures required by failures

Add exact route patterns and payloads based on the existing production contract fixtures in chat, collections, documents, schemas, and extractions. Preserve the extraction upstream mapping 404 -> 404, 422 -> 422, 429 -> 429, and 503 -> 503; do not generalize this behavior to document or schema routes.

  • Step 4: Re-run until the full matrix passes

Run the command from Step 2. Expected: all route-contract tests PASS with one Chromium project, one worker, zero retries, and no skipped tests.

  • Step 5: Commit the matrix
git add frontend/tests/route-contract-e2e/route-status.spec.ts frontend/tests/route-contract-e2e/stub-services.mjs
git commit -m "test(routes): assert exact production statuses" -m "Refs #411"

Task 3: Pull-request CI gate and diagnostics

Files: - Modify: .github/workflows/ci.yml

  • Step 1: Add a failing workflow assertion to the review checklist

Before editing, verify that no current PR job runs the command:

rg -n "test:e2e:route-contract|Frontend Route Contract" .github/workflows/ci.yml

Expected: no matches.

  • Step 2: Add the bounded dedicated job

Add frontend-route-contract for pull requests and manual dispatch. Use the pinned mcr.microsoft.com/playwright:v1.62.1-jammy image, Node 24, npm ci, a single production build with all service URLs set to http://127.0.0.1:4311, then npm run test:e2e:route-contract with the same runtime environment. Set timeout-minutes: 12. Do not use real credentials or depend on the broad authenticated Playwright setup.

  • Step 3: Upload actionable failure evidence

On failure, upload frontend/playwright-report/, frontend/test-results/, and the sanitized adapter/Next logs emitted by the dedicated config. Retain them for 14 days. Ensure artifact upload itself still runs when the test step fails.

  • Step 4: Validate workflow and frontend gates

Run:

cd frontend
npm run validate
npm run typecheck
npm test -- --runInBand --coverage=false
CI=1 npx playwright test --config=playwright.route-contract.config.ts --list
cd ..
git diff --check

Expected: validation, typecheck, 2438-or-more unit tests, project discovery, and diff check PASS. Confirm the route project has no setup dependency, no skipped tests, no external URL, no service worker, one worker, and zero retries.

  • Step 5: Commit CI and design documents
git add .github/workflows/ci.yml
git add -f docs/superpowers/specs/2026-08-06-route-contract-e2e-design.md docs/superpowers/plans/2026-08-06-route-contract-e2e.md
git commit -m "test(ci): gate route contracts on pull requests" -m "Refs #411"

Task 4: Review, PR, and merge readiness

Files: - Review all files changed from origin/main.

  • Step 1: Self-review scope and safety

Run git diff --check origin/main...HEAD, inspect every changed file, verify no generated build/report/log artifact is tracked, and verify the worktree is clean.

  • Step 2: Obtain two independent reviews

First obtain spec/security approval for every #411 acceptance item. Only after that passes, obtain code-quality approval for process lifecycle, isolation, status false positives, and CI behavior. Fix every finding with a new commit and re-run the affected review.

  • Step 3: Push and open the PR

Push test/411-route-contract-e2e and open a PR to main titled test(routes): gate route-contract E2E on pull requests, with Closes #411, the exact matrix, local verification, and safety notes. Do not include tool or co-author attribution.

  • Step 4: Verify live CI

Wait for all required checks, including Frontend Route Contract (Chromium). If a check fails, inspect the actual job log, fix the root cause in a new commit, repeat both reviews for the delta, and wait for the replacement run.