Ready to test

What a project must satisfy before a person tests it by hand on a dev destination. The build applies this list on entry to the refine and test phases; a project can apply it to itself at any time.

How to read an item. Each begins with a six-character identifier — quote that, never a position in the list, because items are reordered, regrouped and retired and the identifier is the only thing that stays put. Each ends with where it comes from, so you can check the source rather than trust the summary: G is the client guidelines index (with the section), M a rule moved here from that index — the section named is where it used to sit and where it no longer appears, because this page now owns it — R the manifest reference, D the credential-less deploy page, X a behaviour the platform verified in its own code, Phil a direct instruction (dated), P a catalogue project by number, and C the conductor's field record (list, item, date) from which the item was vetted. The conductor's numbers are given so the two lists can be walked against each other; they are not identifiers here.

Owner. The platform writes this page. Regenerating the guidelines digest never touches it. A retired item stays on the page under Retired so a quoted identifier always resolves.

A. Manifest and configuration

  • gjtz6e wbsp.yaml declares variant: <uuid> immediately before name:, a dev destination with type: dev and a tenant:, and the reserved universal: {} block. (G §1; R; C test 1–3, 2026-09-12)
  • y226d9 Secrets are declared by reference only. No secret value appears in wbsp.yaml or any committed file, with the one allowed exception of the shared test-HAP values in a committed .env.<destination>.example. (G §5; C test 4)
  • s5ven7 A committed .env.dev.example exists, and a git-ignored .env.dev exists that runs without hand edits. .gitignore ignores .env* and un-ignores !.env.*.example; .env.dev is not tracked. (G §5; C test 5–6)
  • 5h372n The application's public URL and its OIDC redirect URI name the same host and port, and the callback path is one the shared test client accepts on localhost at any port: /api/auth/callback/hap, /api/auth/callback, /login/oauth2/code/hap or /signin-oidc. Moving the port is an edit to your own env file and nothing else — do not go looking for a registration to update; there isn't one. (M §2 Running under dev; G §5, §6; C test 7)

B. Ports

  • rcbznb The data services run on the platform-derived ports (54NNN for Postgres, 63NNN for Redis) — the deploy does this for you. (G §2; C test 8)
  • 9g5put Every host process you start yourself listens on a port derived from the project number (30NNN for a web app on 3000; a service on 8000 or 8080 folds deterministically into the IANA dynamic range [49152, 65535]), never a common default — not 3000, not 3001, not 4000, 5000, 8000 or 8080, because those are exactly the ports every other project on the machine is also holding. The derivation in the client guidelines is the only source for these numbers; do not invent a scheme of your own and do not settle for "a free port". The platform derives ports for the data services but cannot derive one for the application, because it does not start it — the tester does, from the editor — so this derivation is yours to apply by hand, in the launch configuration and in .env.dev alike. For project 356 that is next dev -p 30356 with the API on 80356. The port is pinned in the documented start command and in .env.dev, not left to the framework's default: a front end pinned to bare 3000 in the README, the compose file or .env.dev collides with every other project on the developer's machine. (M §2 Running under dev, Avoiding port clashes; C test 9)
  • gud32r After sourcing the deploy-written .env and your .env.dev, every host process still binds its derived port — not the PORT=8080 the deploy writes, which describes the container contract. A second process with its own health port gets its own derived port. (M §2 Running under dev; C test 32)
  • ky54x9 After sourcing the deploy-written .env, a framework dev server still runs in development mode. The NODE_ENV=production the deploy writes is for the container and must not reach next dev or its equivalent. (C test 33; P 61)
  • b779v4 A split front end and API become two origins under dev, and only under dev — in the cloud the platform serves both from one host, so a project with an internal API often has no CORS layer, no absolute API base and no explicit front-end base URL, none of which it needed until now. Three things then break at once, and the project handles all three itself. The browser cannot reach the API: give it a CORS allowance enabled only for dev, or put a dev proxy in front (Next.js rewrites, Vite server.proxy) so the browser sees one origin — never a permissive rule that survives into a cloud destination. Server-side fetches have no origin to be relative to: a default base of /api/… works in a browser and fails in a host process where there is no page URL, so every server render's API call fails, usually with a message about an invalid URL rather than about the base — read an absolute API base from the env file and let the relative form be the browser-only special case. Server-side redirects land on the wrong origin: a sign-in callback or sign-out that redirects to / returns to whichever origin generated it, and on the API that is a JSON 404 that looks like a broken auth flow — build every redirect from an explicit front-end base URL supplied per destination, never from a bare path. (M §2 Running under dev — host processes, derived ports, two origins; C test 10)

C. Data

  • 4mx8zv wbsp deploy --destination dev has been run: both data containers are up and the deploy-written .env exists. (R; C test 11)
  • f3ydht Schema is applied by versioned migrations checked into the repository. No hand-run SQL, no out-of-band DDL. (G §4.3; C test 12)
  • g8hwe4 The release: command is idempotent and brings an empty database to a usable first screen with no operator step — including any row a first user needs (default workspace, tenant, role). (G §4.4; C test 13)
  • b8sf33 Demo or seed data is optional, documented, idempotent, and says which account owns it. The documented migration and seed commands run from the deploy-written .env plus .env.dev alone; if the tooling needs a connection URL, the documented command assembles it rather than asking someone to hand-write one. (G §4; C test 14, 34)
  • bfcv5s The seed and the login bypass agree on who the tester is: after a clean seed and a first bypass sign-in, the tester lands on populated data holding the stated role — not an empty organisation, and not a staff-only view. (C test 35; P 61)
  • mtfjxv Automated browser suites that drive the running dev application clean up what they create, or run against a database other than the one the tester is about to use. The tester never meets stray organisations, users or records left by a test run. (C test 36)

D. Running the application

  • t4m8rz Do not ask the tester to start the application until you expect it to start and run correctly. Asking is a request for someone's attention, and an application that falls over on the first page spends it for nothing — so before asking, satisfy yourself on both halves. The environment is ready: the data services are up, the deploy-written environment file and .env.dev are both present, migrations and any seed have been run against the database the application will actually open, and every value a host process needs is in a file rather than in somebody's shell history. The work is finished: every user story and task in scope is complete, every phase of development that was meant to precede this one is closed, and nothing is left half-wired behind a flag or a stub that the first click will reach. A partially built application handed to a tester produces a defect report about the part you already knew was missing, which costs the tester a session and tells you nothing. If something genuinely cannot be finished before the test, say plainly which part is absent and what it will do when reached, before the tester starts — not afterwards, in answer to the report. (Phil 2026-09-21)
  • v7k3qd Inside a Manual Development item of the build pipeline the session never starts the application — the user does, from the VS Code launch configuration. Launching it that way is what puts it under the user's control in the editor, so they can stop and restart it from the interface for as long as they are working. A session that starts it instead takes those controls away, leaves a process the user cannot see to stop, and usually hides the very failure the hand-over was meant to surface. So when such an item begins, the session hands over and waits: it does not start the application and it does not poll it. Outside those items the session may start the application — to check a change, capture a screenshot, run a suite — on two conditions. It binds the ports derived from the project number and never a common default (9g5put). And it stops the application again before the next Manual Development item begins, so the user arrives at a clean machine and presses the launch configuration themselves rather than inheriting a process somebody else started. The configuration is written by the pipeline's Launch script for VSC item; this page does not ask the project to invent one. (Phil 2026-09-21, clarified 2026-09-26)
  • p5q4tg Under dev the server and the website run as host processes — never docker compose up for the application; v7k3qd says who starts them and when. The README, getting-started page and useful-commands page show the host-process form; if the project also supports compose, each command says which destination it belongs to. A README that says docker compose up api worker beat under a dev destination is describing a different destination. (M §2 Running under dev; C test 15)
  • 54jenk A run started from the VS Code launch configuration puts the application's logs and output on the console, where the user can read them in VS Code. That is the point of launching it from the editor, and a run that buries its output in a file the user has to go looking for defeats it. A run the session starts for itself — outside a Manual Development item, per v7k3qd — writes to a log file whose path the project names when it reports, so the session has something to read back. (C test 16; Phil 2026-09-21, clarified 2026-09-26)
  • dmfrec Every environment variable a host process needs comes from the project's own env files or a documented export. Nothing depends on a value only a container used to inject. (G §5; C test 17)
  • qnxzsy The documented start commands match the project's own toolchain (pnpm versus npm, uv versus pip) and the README describes what actually runs. (C test 18)
  • 5m8pgf A health endpoint answers, and the project says its path. (R health_check; C test 19)

E. Sign-in and roles

  • b6k9a7 The application is configured for the shared test client (issuer, client id, its own redirect URI on its derived port) so that the other local destinations work. The HAP round trip is not exercised during dev testing — see the bypass below — so a missing login route is noted, not a blocker. (G §5, §6; C test 20)
  • qjmj6n A user HAP has authenticated, for whom the application holds no row, is created and admitted with the ordinary default role on first sign-in. There is no "awaiting access" state that an operator must clear. (G §6; C test 21)
  • eqea7j The application's administrator role is mapped from HAP's administrator claims, and both claims are read. On the shared test client is_app_admin is false for everyone, so under dev the application treats is_tenant_admin as the designation, and logs which claim admitted a user, so the difference is visible rather than mysterious. is_app_admin answers "does this user administer this client", and on a client every project on the machine signs in through, that is not a statement about your application at all — so an admin mapping that works only when is_app_admin is true leaves you with no administrator locally while the same code is correct in the cloud, where the application has a registration of its own. An application whose authority is entirely per workspace or per organisation says so instead. (G §6 Read the administrator claims; M §6 On a local destination the claims mean less; C test 22, 37)
  • vb2wuv No first-user-wins bootstrap and no hand-typed administrator subject where HAP is present. The explicit-declaration fallback is only for an install with no identity provider. (G §6; C test 23)
  • wkdpe8 A user who is authenticated and then refused sees a page saying so and who to contact — never a redirect back toward sign-in. (G §6; C test 24)

F. The local login bypass

  • 9ckagf The project has a local-only login bypass: an environment-variable flag, off by default, honoured only on the local destinations (dev, compose, standalone) and impossible to activate on a cloud destination — the code refuses it there, not merely the configuration; gating additionally on the application's own address being a loopback host is a good way to guarantee that. It signs the tester in as a synthetic principal with a stated role. Manual testing under dev uses it; nobody signs in through HAP during dev testing. Carrying one is not a liberty the project takes — it is required, because three steps of the build pipeline (UI fine-tuning, end-to-end testing and walkthrough screenshot capture) run unattended and cannot complete without it: a real sign-in through the shared test client ends at a one-time code sent to an email address, which is exactly the step nobody is present to complete. It does not go through the identity provider — that is the whole point. The application stays configured against the shared test client, as every local destination is; the bypass establishes a local session directly for an identity the deployment has already declared, so it cannot grant a role that deployment did not declare. None of this replaces real sign-in for a developer, which the pre-wired local env files make work immediately. (M §6 A local sign-in bypass is expected — with hard limits; Phil 2026-09-05; C test 30)
  • p7hscb The bypass flag is in the committed .env.dev.example and switched on in .env.dev, so someone following the project's own getting-started page lands signed in. (C test 37)
  • bswm9j With the bypass on, every protected page answers 200 as that principal. The admin surface and any customer-facing surface each have a way in, or the project says which principal reaches which; where authority is per workspace, the bypass principal holds the widest role in a populated workspace. (C test 31, 37)
  • pjz4pr On a loopback address every sign-in cookie — session, state, PKCE verifier, CSRF, callback — carries the port as a leading segment of its name (p30045.authjs.session-token), and keeps the framework's default names on a deployed host, which has an address to itself and where prefixed (__Secure-…) names and domain scoping behave differently. Condition the renaming on the address being loopback, not on a "development" flag. Three rules guarantee the collision it prevents: local ports are derived from the project number, so several applications run on one host at once; Next.js with a standard auth library is the preferred stack, so they write the same cookie names by default; and the session signing secret is generated per install, so none of them can open another's cookie. A browser cookie is scoped to a host, not a port — localhost:30045 and localhost:30123 share one jar — so whichever application signed in last owns the name and the others are handed a cookie they cannot decrypt. The symptom does not resemble the cause: a decryption or JWT error with a stack trace, on an application the developer may not even have been using, read and reported as a broken install. Rename every cookie in the round trip, not just the session — the state, PKCE verifier and CSRF cookies are single-named too, so the callback collides even when the session does not. Prefix, never suffix: Auth.js splits an oversized session cookie into <name>.0, <name>.1, … and reassembles by collecting every cookie whose name begins with the base name, so an appended port is indistinguishable from a chunk index — 3004 would swallow 30045, and an application still on the plain name would collect a migrated neighbour's whole session as a fragment of its own. This is not a way to share one sign-on between applications; that is HAP's job at the identity layer. (M §6 Name your session cookies apart on a loopback address; C test 39)

G. Rendering

  • n95wud Every primary page answers 2xx with a rendered body: no blank page, no framework error boundary, no raw JSON where a page should be, no console error that breaks rendering. Primary navigation works signed out and signed in. (C test 25, 27)
  • s3eddp Front-end and back-end dependency versions agree with each other (a Next release with the React it requires, and so on), and the dev server log shows no error on a plain request. (C test 26)

H. Looking ahead to the demo

These are not needed for a dev test, but the projects that got them wrong did so because they were first considered at the sandbox gate. Consider them now.

  • 4zrr3v In sample mode (WBSP_SAMPLE_MODE=true, injected by the platform) the application attaches every newly provisioned user to the seeded demonstration data with the role the demonstration needs; a per-workspace model otherwise shows an empty picker over a full database. The dump is taken from a clean migrate-and-seed, never from the development database. (G §1, §6; C test 38; P 123)
  • ab5jvy On any HAP-backed destination an unauthenticated visitor is redirected straight into the authorization request — never to an in-app sign-in page with a button — and the sign-in start route, the OIDC callback, the health endpoint and static assets are exempt from that redirect. The in-app sign-in page is only the fallback for an install with no identity provider. (G §6 Silent SSO only happens if your application starts it; C test 40)

I. Correspondence

  • 6ppakn notes/inbox/ and notes/outbox/ exist and the mail skill is installed at a current revision. When asked for this checklist, the project replies one line per identifier. (G §1 /notes; C test 28)
  • h7vcd3 The project changes nothing outside its own repository and sends nothing to a core project. Anything it believes is a platform matter goes in its reply, for the operator to take up. (G §9; C test 29)

Retired

None yet.