ADR-0040: Scenarios against a generated application, and size budgets¶
Status: Accepted, amended by ADR-0044 (an add-on's chunks have a budget of their own) Date: 2026-10-16 Deciders: Alex Nodeland
Context¶
Phase 7a (stackr ADR-0038) made the template mount portalr. Everything that drives the UIs in a browser still ran against portalr's own test application (tests/app, ADR-0013), which has its own world: acme, globex and eight people, a scripted agent, an in-memory grantr and no Supabase of its own. Nothing showed that the application the template generates, with its own Supabase wiring, its own gateway and its own agents, lets a person sign in and work, or shows them by email. Phase 7b of the plan also promised the Acceptance job's portalr steps, and ADR-0015 left the bundles' size budgets to phase 7.
Two facts bound the design. The browser needs a Supabase Auth it can reach at <url>/auth/v1: local Supabase has it on this machine, but GoTrue alone (the PostgreSQL adapter) serves at its root, and a single host's edge is a TLS name the runner can't resolve. And #172, #254 and lattice ADR-0002: Chromium fails every request in flight with ERR_NETWORK_CHANGED when any network interface comes or goes, as a container's does.
Decision¶
- A few scenarios, not all of them, run against the application stackr's smoke test starts.
bdd/generated/in app and admin holds the features that hold for any generated application: in app, signing in to the owner's tenant, starting a thread in a workspace and reading the agent's answer (the gateway's mocked reply,stackr smoke ok), a wrong password refused, signing out; in admin, opening on the tenant, and Access's Members naming the tenant's people by email. The other features stay on the test application: they rely on its seeded world (a scripted agent that asks questions and proposes edits, eight roles, tools that are up and down), which a generated application does not have, and rebuilding it there would test the seeds. - The same step definitions, one more feature directory.
PORTALR_FEATURESin each Playwright configuration names the features,bdd/features/*.featureunlessscripts/generatedsets it, so a step written for the test application serves both and a change to one is run against the other. - The smoke test seeds the world and runs them last. Once its own checks have passed,
smoke_scenariosmakes alice (acme's owner) and bob (a member) in local Supabase, creates acme with the application's own admin command, and makes bob an editor of its workspacenotes(which is how a workspace becomes one grantr offers), then runs app's and admin's scenarios fromscripts/generated, which start and stop nothing. The application's.envnames local Supabase as the portal's project, with its publishable key, and gives the application the secret key, for naming people and inviting; the file is written mode 0600, the key is in no argument, and the browser is given only the publishable one. - Local Supabase only, and only where asked. The leg of the Smoke job that runs the application on local Supabase sets
SMOKE_SCENARIOS=true, installs Bun's packages and Chromium before the stack starts, and uploads Playwright's traces on failure. A run without it, on another machine or leg, says so and why, and does not pretend. The single host's leg already checks the sign-in call through the edge, in curl, and that Chromium would need the edge's names. - Serialised. The Chromium phase is the last thing the smoke test does with the application:
docker compose run, which the probes use, makes a container and a network interface, so nothing of the sort runs while it does, and Chromium's install and the packages come before the stack's containers do. The Acceptance job already runs app's and admin's scenarios against the test application, one project after another (lattice ADR-0002); that is the plan's "Acceptance job's portalr steps", and it is unchanged. - Size budgets, as ceilings.
size-budget.jsonholds, for each UI, three gzip sizes in bytes:initial(the entry, the chunks index.html preloads, the stylesheet),total(every script and stylesheet) andlargest(the largest file, which is Streamdown's chunk, in both).scripts/check_size.pyreads what the builds wrote and fails over any of them;moon run portalr:sizeis one ofportalr:check's dependencies. The ceilings are about ten percent above the sizes at the time, so growth is noticed in the change that causes it: whoever raises a budget says why in that change. Sizes at the time: app 179 / 425 / 144 kB, admin 202 / 604 / 144 kB (initial / total / largest). Ceilings: app 200 / 470 / 160 kB, admin 225 / 670 / 160 kB.
Options considered¶
| Choice | Option | For | Against |
|---|---|---|---|
| Where | The smoke test's application (chosen) | A real stack, gateway, Supabase and agents; no second stack to start | Chromium waits for the smoke test's other checks |
| A job of its own | Isolated | Starts the whole stack again for a few scenarios | |
| The template's own CI | The application's tests run there | It has no stack, no gateway, no Supabase | |
| Which | A tagged few, in their own directory (chosen) | Each runs and says what a generated application can do | Two feature directories to keep in step |
| Every feature, seeding the test application's world | One set | The seeds, not the application, would be tested | |
| Adapter | Local Supabase (chosen) | /auth/v1 and CORS, as supabase-js expects |
The PostgreSQL and single-host legs have no browser scenario |
| GoTrue behind a path proxy | Every leg | A test-only proxy | |
| Budgets | Ceilings in a file, gzip (chosen) | Simple, checked on every build, nothing to host | A number to raise by hand |
| A bundle-size service | Trends | Another account (no external infra) |
Consequences¶
- Easier: a change to the template, the router or the UIs that breaks sign-in, the agent's answer or the people's names in a generated application fails the Smoke job; a UI that grows past its budget fails
portalr:check. - Harder: the Smoke job on local Supabase installs Chromium and runs two more projects' scenarios, a few minutes more.
- Not covered: the scenarios on the PostgreSQL and single-host legs; the invitation flow, since local Supabase in CI runs without its mail server.
- Phase 7b of RFC-0001 is done.