From 1c4cc9a412cdc791cca14f0b07101ce5d5e2d8a6 Mon Sep 17 00:00:00 2001 From: Gabriel Garcia Date: Tue, 29 Sep 2026 12:45:32 +0200 Subject: [PATCH] feat(example): add dev:env to run the example app on a pinned root env file npm run dev:env -- --env= loads ../.env. and tells Vite not to read example/.env (envDir: false), so the server and browser env both come from one file. The seed script already reads the same file, so an app run and a seeded employment always share a company. npm run dev and example/.env stay untouched as the human scratchpad. Adds a verify-local-app skill that builds the library, starts dev:env on a free port, seeds with the same env and drives the flow with Playwright. Co-Authored-By: Claude Opus 5.5 --- .claude/skills/verify-local-app/SKILL.md | 92 +++++++++++++++++++ .../verify-sandbox-deployed-app/SKILL.md | 2 +- CLAUDE.md | 1 + example/dev_server.js | 25 ++++- example/package.json | 1 + 5 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 .claude/skills/verify-local-app/SKILL.md diff --git a/.claude/skills/verify-local-app/SKILL.md b/.claude/skills/verify-local-app/SKILL.md new file mode 100644 index 000000000..931ba822e --- /dev/null +++ b/.claude/skills/verify-local-app/SKILL.md @@ -0,0 +1,92 @@ +--- +name: verify-local-app +description: Verify onboarding behavior of the current checkout's code by running the example app locally against a pinned root env file (.env.local / .env.sandbox / .env.staging / .env.partners), seeding a fresh employment with the same file, and driving the flow with Playwright — instead of the user doing it by hand. Use when the user asks to "verify X locally", "check my change against staging/partners/sandbox", or similar. For the deployed demo app (.env.review) use verify-sandbox-deployed-app instead; not for unit/e2e tests. +allowed-tools: Bash(npm run build:*), Bash(npm run dev:env:*), Bash(npm run seed:onboarding:*), Bash(node:*), Bash(curl:*), Bash(cat:*), Bash(grep:*), Read, Write +--- + +# Verify local app + +Runs `example/` on this machine against one of the root env files, so the run +is reproducible: the app, the seed script and the check all read the same +`.env.`. The user's own `npm run dev` and `example/.env` are their +scratchpad — never read, edit or rely on them here. + +| File | Gateway it's meant for | +| --------------- | ----------------------------- | +| `.env.local` | Tiger on `localhost:4000` | +| `.env.sandbox` | sandbox | +| `.env.staging` | staging | +| `.env.partners` | partners | +| `.env.review` | not this skill — deployed app | + +`VITE_REMOTE_GATEWAY` inside the file is what actually picks the gateway; the +file name only picks the credentials. + +## Step 1: Pick the env and confirm it exists + +Use the env the user named. If they didn't, ask which one rather than guessing +— different envs are different companies, and the result only means something +against the one they care about. + +Reading `.env*` files directly is denied by permission settings, so check keys +with a `node -e` one-liner that loads `/.env.` with `dotenv` +and prints only `true`/`false` per key — never the values. It must set +`VITE_REMOTE_GATEWAY`, `VITE_CLIENT_ID`, `VITE_CLIENT_SECRET` and +`VITE_REFRESH_TOKEN`. If the file is missing or incomplete, ask the user to +create it (same shape as `example/.env`), then stop and wait. + +Any feature flag the check depends on (e.g. `VITE_NEW_PREMIUM_BENEFITS`) must be +in that root file too: `dev:env` does not read `example/.env` at all, for the +browser or the server. + +## Step 2: Build the library + +`example/` depends on the repo root via `file:..`, so it serves whatever is in +`dist/`. Run `npm run build` at the repo root first, otherwise you're verifying +the last build, not the current code. + +## Step 3: Start the app on its own port + +From `example/`, start it in the background on a free port (not 3001 and not +the worktree's `PORT`, which the user's own dev server may be using): + +``` +PORT= npm run dev:env -- --env= +``` + +Wait until `http://localhost:` responds. The log line +`Loaded /.env.; example/.env is ignored.` confirms the right +file was picked up. + +## Step 4: Get an employment ID at the right step + +If the user gave you an existing employment ID for that env, use it. Otherwise +seed one with the **same** env name, so it belongs to the company the app's +server authenticates as (a mismatch returns 404 `Company not found`): + +``` +npm run seed:onboarding -- --country= --env= +``` + +Default `COUNTRY=DEU` unless the ask implies another. Seeding stops at +`contract_details`; drive later steps yourself in Step 5. + +## Step 5: Write a one-off Playwright script + +Write a small Node script per task and run it with `node` from `example/` +(Playwright is already installed there). It should: + +1. Launch headless Chromium (`playwright`'s `chromium.launch()`). +2. Open `http://localhost:/?demo=onboarding-basic&employmentId=`. +3. Click "Start Onboarding", then drive whatever steps reach the state to check + (selectors follow `example/e2e/helpers/onboarding.ts`). +4. Perform the actual check: DOM text/attributes for a specific claim, or + `page.screenshot({ path: ... })` for a visual one. +5. Close the browser. + +## Step 6: Report back and clean up + +State plainly what you checked, against which env, and pass/fail against the +user's actual claim — not just "the page loaded." Attach or describe any +screenshot. Then stop the dev server you started in Step 3; leave any server +you didn't start alone. diff --git a/.claude/skills/verify-sandbox-deployed-app/SKILL.md b/.claude/skills/verify-sandbox-deployed-app/SKILL.md index 2370c0115..9dab4d17d 100644 --- a/.claude/skills/verify-sandbox-deployed-app/SKILL.md +++ b/.claude/skills/verify-sandbox-deployed-app/SKILL.md @@ -1,6 +1,6 @@ --- name: verify-sandbox-deployed-app -description: Manually verify onboarding behavior on the deployed sandbox demo (https://remote-flows-eight.vercel.app) instead of the user doing it by hand — seeds a fresh employment via the sandbox gateway, logs past the Vercel password gate, drives the flow to the right step, and checks whatever the user asked about. Use when the user asks to "verify X on sandbox/the deployed app", "check the deployed demo", or similar — not for local dev (`example/`'s own dev server) or unit/e2e tests. +description: Manually verify onboarding behavior on the deployed sandbox demo (https://remote-flows-eight.vercel.app) instead of the user doing it by hand — seeds a fresh employment via the sandbox gateway, logs past the Vercel password gate, drives the flow to the right step, and checks whatever the user asked about. Use when the user asks to "verify X on sandbox/the deployed app", "check the deployed demo", or similar — not for local dev (use verify-local-app for that) or unit/e2e tests. allowed-tools: Bash(npm run seed:onboarding:*), Bash(node:*), Bash(cat:*), Bash(grep:*), Read, Write --- diff --git a/CLAUDE.md b/CLAUDE.md index 616a490fc..328036acd 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -28,6 +28,7 @@ npm run ci # full local CI: build + check-format + check-exports + l npm run seed:onboarding -- --country=XXX # create an onboarding via API up to (not including) contract_details; needs example dev server + example/.env npm run seed:onboarding -- --country=XXX --env=sandbox # same, but talks to the gateway directly using .env.sandbox at repo root (see scripts/seed-onboarding.ts) - no dev server needed npm run seed:onboarding -- --country=XXX --env=review # same, using .env.review - the deployed demo app's credentials; use this for employments you'll open on the deployed app +cd example && PORT= npm run dev:env -- --env= # run the example app on .env. from repo root only, ignoring example/.env - used by the verify-local-app skill ``` The `example/` app is a separate workspace (its own `package.json`, Vite + Express dev server on `:3001` by default, overridable via `PORT` — `scripts/create-worktree.ts` assigns each worktree its own free port so several can run their example apps at once). To work against local changes: `npm link` in repo root, then `npm link @remoteoss/remote-flows` inside `example/`, then run `npm run dev` in both. E2E lives in [example/e2e/](example/e2e/) and is run with `npm run test:e2e` from `example/` (Playwright). E2E is excluded from the root vitest run. diff --git a/example/dev_server.js b/example/dev_server.js index a686a97b4..313f29863 100644 --- a/example/dev_server.js +++ b/example/dev_server.js @@ -1,11 +1,33 @@ const axios = require('axios'); const dotenv = require('dotenv'); const express = require('express'); +const fs = require('fs'); const http = require('http'); +const path = require('path'); const { setupRoutes } = require('./api/routes.js'); const { createServer: createViteServer } = require('vite'); -dotenv.config(); +const envArg = process.argv.find( + (arg) => arg === '--env' || arg.startsWith('--env='), +); +const envName = envArg?.split('=')[1]; + +if (envArg && !envName) { + console.error('--env requires a value, e.g. --env=partners'); + process.exit(1); +} + +if (envName) { + const envFile = path.resolve(__dirname, '..', `.env.${envName}`); + if (!fs.existsSync(envFile)) { + console.error(`--env=${envName}: ${envFile} does not exist.`); + process.exit(1); + } + dotenv.config({ path: envFile }); + console.log(`Loaded ${envFile}; example/.env is ignored.`); +} else { + dotenv.config(); +} const startServer = async () => { const app = express(); @@ -16,6 +38,7 @@ const startServer = async () => { // for HMR's WebSocket instead of letting Vite open its own on the default // port — otherwise every worktree's example app collides on that port. const vite = await createViteServer({ + envDir: envName ? false : undefined, server: { middlewareMode: true, ws: { server: httpServer } }, }); diff --git a/example/package.json b/example/package.json index a51900a59..d532be2be 100644 --- a/example/package.json +++ b/example/package.json @@ -6,6 +6,7 @@ "scripts": { "audit": "npm audit --audit-level=high", "dev": "nodemon ./dev_server.js", + "dev:env": "node ./dev_server.js", "build": "tsc -b && vite build", "lint": "oxlint --type-aware .", "preview": "vite preview",