Investigated whether the four LLM-gated specs (chat rename, chat delete, project-assistant create+submit, critical path) could run keyless with the demo model they select. They cannot on this codebase: no keyless model exists here — ModelToggle's MODELS are all Anthropic/Google/OpenAI entries gated on a configured provider key (env or user-stored, backend/src/lib/userApiKeys.ts), ChatInput.handleSubmit hard-blocks a send for an unavailable model, and the backend model set (backend/src/lib/llm/models.ts ALL_MODELS) has no demo id either. The 'Demo (no key needed)' model the specs target lives only in the amal66 fork. So the blanket test.skip(!hasLlmKey, ...) guard stays as-is — the evidence does not support narrowing it. What this change does instead: - docs/e2e-ci.md: new 'Enable the LLM specs' section with the exact UI path and gh CLI command for the ANTHROPIC_API_KEY repository secret, the fork-PR secrets caveat (use a maintainer branch or workflow_dispatch to exercise the specs), expected per-run cost, and how to confirm in the Actions log that the specs ran (27 passed, no skipped line) rather than skipped (4 skipped / 23 passed). Also documents the known gap: until the specs' selectDemoModel helper is pointed at a Claude model (or the demo provider is upstreamed), the secret unskips the specs but they fail at model selection. - e2e/llm.ts: rewrite the doc comment with the precise gate rationale (no keyless model in this repo; title-generation 500s are best-effort and not the reason) and pointers to the setup docs. - .github/workflows/e2e.yml: comment-only updates aligning the skip narrative (23 keyless specs, not ~20/~23) and pointing at the new docs section. No behavioral change anywhere. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
7.3 KiB
End-to-end tests in CI
The Playwright suite (e2e/) runs on every pull request through
.github/workflows/e2e.yml. This document covers the one repository secret it
needs and the branch-protection step that turns a red run into a blocked
merge — the workflow reports pass/fail on its own, but only branch protection
makes that check required.
What the workflow does
On every pull_request targeting main (or upstream-main, the fork mirror),
and on manual workflow_dispatch, the e2e / playwright job:
- installs the root (Playwright),
backend/, andfrontend/dependencies; - boots MinIO (S3-compatible object storage — several specs upload documents);
- boots local Supabase (Auth + Postgres) via the Supabase CLI, loads
backend/schema.sql, then applies every dated migration inbackend/migrations/on top.schema.sqlis meant to be the latest shape but in practice lags the migrations (e.g. it is missingworkflow_open_source_submissions, whichGET /workflows/:idqueries — a 500 without the migrations). It also grantsservice_rolefull access to thepublictables afterward, becauseschema.sqlrevokes client grants assuming a hosted Supabase whereservice_roleis already privileged; - writes
backend/.envandfrontend/.env.localfrom the live Supabase values; - builds the web app (
next build) and serves it withnext start— a production build, notnext dev, so there is no on-demand compilation (which makes first-hit page loads slow enough to time out specs) and no dev hydration-error overlay (whose injected DOM pollutes text locators). Starts the backend API (:3001) and the web server (:3000) and waits for both healthy; - runs
npx playwright testand uploads the HTML report + traces as an artifact (playwright-report) on pass, fail, or timeout.
e2e/auth.setup.ts bootstraps the shared test user (e2e@mike.local) against
the local Supabase admin API, so no login secret is needed — the credentials
baked into that file are the single source of truth.
Typical run: ~7 minutes, 23 passed / 4 skipped / 0 failed with no secret.
Optional secret (fuller coverage)
| Secret | What it unlocks | Without it |
|---|---|---|
ANTHROPIC_API_KEY |
The 4 LLM-dependent specs (chat rename/delete/submit, critical-path "ask a question") send a message and assert a streamed answer. With the key set they run and are enforced. | Those 4 specs skip (see e2e/llm.ts) instead of hanging, so the run is still green on the other 23 specs. |
The suite is green without any secret — the LLM specs skip themselves via
test.skip(!process.env.ANTHROPIC_API_KEY, …), which keeps keyless runs (local,
and fork PRs with no secret access) green and fast. The gate is a blanket one on
purpose: the app ships no keyless model — every model in the picker
(frontend/src/app/components/assistant/ModelToggle.tsx) needs an
Anthropic/Google/OpenAI key before ChatInput will send at all — so without a
key the 4 specs cannot even submit a message. (The auto title-generation call is
not the reason for the gate; keyless it returns a 500 the specs already
tolerate.) Setup steps below.
Enable the LLM specs
1. Add the repository secret
UI path:
- Open the repository on GitHub → Settings.
- In the left sidebar: Secrets and variables → Actions.
- On the Secrets tab, click New repository secret.
- Name:
ANTHROPIC_API_KEY— exactly this name; both the workflow env ande2e/llm.tsread it. Secret: an Anthropic API key (sk-ant-…) from https://console.anthropic.com/settings/keys. - Click Add secret.
CLI equivalent (repo admin):
gh secret set ANTHROPIC_API_KEY --repo Open-Legal-Products/mike
# paste the key at the prompt (or pipe it: --body "$ANTHROPIC_API_KEY")
2. The fork-PR caveat
On pull_request events from forks, GitHub withholds repository secrets, so
fork PRs — most external contributions — still run keyless and skip the 4 specs.
That is by design and keeps those runs green. Runs that actually receive the
secret and exercise the specs are:
- PRs from branches pushed to this repository (maintainer branches), and
- manual runs: Actions → e2e → Run workflow (
workflow_dispatch) on any branch.
So after adding the secret, the quickest way to see the specs run is a
workflow_dispatch run from the Actions tab.
3. Expected cost per run
A handful of short completions: one streamed chat answer per LLM spec plus a few
small title generations (claude-haiku-4-5, 64-token cap). On the order of a
few cents per run — negligible next to the CI minutes.
4. Confirm the specs ran (not skipped)
Open the Run Playwright step in the Actions log:
- Keyless run: the summary ends with
4 skipped/23 passed, and each skipped spec carries the reasonrequires a model key — set the ANTHROPIC_API_KEY secret to run LLM-dependent specs. - With the secret: the summary shows
27 passedand noskippedline; searching the log forrequires a model keyfinds nothing.
The uploaded playwright-report artifact shows the same per-spec statuses.
Known gap: model selection
As of this branch the four specs drive the model picker to a keyless
"Demo (no key needed)" entry that exists in the amal66 fork but not in
this repository (no mike-demo model id, no demo provider). With the secret
set they therefore unskip and then fail at model selection. Before the secret
turns them green, one of two follow-ups must land: point the specs'
selectDemoModel helper at a Claude model when the key is present, or upstream
the fork's keyless demo provider. The secret setup above is correct and ready
either way.
Make it merge-blocking
The workflow failing is not enough on its own — GitHub will still allow the merge unless the check is required. Enable branch protection once you have seen the suite go green a few times (it is environment-sensitive by nature):
- Settings → Branches → Add branch protection rule (or edit the rule for
main). - Enable Require status checks to pass before merging.
- Enable Require branches to be up to date before merging.
- In the checks search box add
e2e / playwright(the job appears in the list after it has run at least once on a PR). - Recommended alongside it: the unit/build check
backendand thelicense/clacheck. - Save. From now on a red e2e run blocks the Merge button.
Equivalent via the GitHub CLI (repo admin token required):
gh api -X PUT repos/OWNER/REPO/branches/main/protection \
-H "Accept: application/vnd.github+json" \
-f 'required_status_checks[strict]=true' \
-f 'required_status_checks[contexts][]=e2e / playwright' \
-f 'enforce_admins=true' \
-f 'required_pull_request_reviews[required_approving_review_count]=1' \
-f 'restrictions='
Running the suite locally
Locally, playwright.config.ts starts the backend and web dev servers for you
(webServer is only disabled when CI=true), so a full local stack plus:
npm ci
npx playwright install --with-deps chromium
npm run test:e2e # or test:e2e:ui / test:e2e:headed
e2e/auth.setup.ts reads SUPABASE_URL / SUPABASE_SECRET_KEY from the
environment or backend/.env, so a running local Supabase + a populated
backend/.env is all the setup needs.