| name | crouton-run-and-operate |
| layer | stack |
| description | Day-to-day operation runbook for nuxt-crouton apps โ boot any app/POC locally with auth working, seed and inspect the local database (including the miniflare-vs-.data split that makes seeded data "disappear"), turn on the review overlay / eruda, observe deployed staging (tail, smoke, row counts), and capture screenshots. Use when asked to "run the app", "boot velo/fanfare/a poc locally", "why can't I log in locally", "seed the database", "the seed data doesn't show up", "look at the local db", "check what's on staging", "screenshot the app", or "where do the artifacts land". |
crouton-run-and-operate
Operate a running crouton app โ locally or on deployed staging โ from cold boot to evidence capture. ("crouton" = this monorepo's schema-driven CRUD framework: @fyit/crouton-* packages that generated Nuxt apps extend as layers.)
When to use / when NOT to use
| You want toโฆ | Use |
|---|
| Boot, log in, seed, inspect, observe, screenshot a running app | this skill |
| Recreate the environment (install/build traps, typecheck story, versions) | crouton-build-and-env |
| Deploy to staging / wire CI / first-time Cloudflare setup | deploy (apps), poc-deploy (pocs); prod only via deploy-production |
Generate/apply Drizzle migrations, schema.mjs gotcha, infra tables | db-migrations |
| Mirror one D1 env into another (refresh staging from prod) | db-clone |
| Run the Playwright fixture smoke (boot+auth+CRUD proof) | e2e-smoke |
| Deploy a preview for UI sign-off (the gate workflow) | ui-proposal |
| Symptom โ root-cause lookup | sibling crouton-diagnostics-index |
| Measure the harness itself (context budget, traces) | loop-station / sibling crouton-harness-observability |
1. Boot any app locally from cold, with auth working
Prerequisites (one line โ full story in crouton-build-and-env): pnpm install && pnpm build:packages. Apps consume @fyit/* packages from dist/; skipping build:packages = dev-server error on missing crouton-core/dist.
export BETTER_AUTH_SECRET=$(openssl rand -hex 32)
pnpm --filter velo dev
- There is no root
dev script (verified: root package.json scripts). Root CLAUDE.md's "Development Commands" listing pnpm dev only works inside an app dir โ a known doc contradiction; the app package.json is ground truth here.
- On Claude-web sessions the SessionStart hook (
.claude/hooks/session-start.sh) already did install + build:packages and exported a cached dev BETTER_AUTH_SECRET (from ~/.crouton-dev-auth-secret) โ check echo $BETTER_AUTH_SECRET before generating your own.
- Apps ship
.env.example (copy to .env). Trap: it hardcodes BETTER_AUTH_URL=http://localhost:3000 but each app runs on its own pinned port (table below) โ set it to the app's real port or cookies misbehave.
- E2e fixtures (
fixtures/*) commit a dummy .env on purpose (BETTER_AUTH_SECRET=dev-fixture-secret-do-not-use-in-production) โ they boot with zero env setup.
- No manual DB step:
hub: { db: 'sqlite' } in each app's nuxt.config.ts (root CLAUDE.md gotcha โ never hub: { database: true }) auto-applies all layers' migrations at dev boot; locally it's a sqlite file, at runtime it maps to Cloudflare D1/KV/R2.
Fixed dev ports โ pinned in each app's/POC's nuxt.config.ts devServer.port. Snapshot 2026-07-02 โ regenerate with the re-verify block before relying on numbers.
| App | Port | | POC | Port |
|---|
| apps/triage | 3005 | | pocs/alexdeforce | 3001 |
| apps/velo | 3006 | | pocs/sintlukas | 3003 |
| apps/fanfare | 3007 | | pocs/crouton-builder-demo | 3010 |
| e2e fixtures | 3000 | | pocs/crouton-builder and pocs/kvr | 3011 โ collision; run one with --port |
| | | pocs/loop-station | 3021 |
Pocs that set no port (blog, thinkgraph*, three-demo, booking-demo at the snapshot) default to 3000/auto-bump.
First login (auth = better-auth via @fyit/crouton-auth; facts from e2e/CLAUDE.md):
- Login/register is a RouteModal overlay, not a page โ
/auth/login redirects to / and opens the modal (useAuthModal). Register a fresh account in the modal on a fresh DB.
- Signup creates no team. Programmatically, create one via the better-auth org API:
POST /api/auth/organization/create then /set-active โ both require an Origin header (CSRF). See ensureTeam() in e2e/helpers.ts for the exact calls. Or seed staff accounts instead (ยง2).
- Generated collections render at
/admin/{teamSlug}/crouton/{collectionKey}.
- Session check:
GET /api/auth/get-session returns the authenticated user (what smoke-deployed.mjs uses as login proof).
Do NOT verify with nuxt preview: crouton collection pages currently 500 under production-preset SSR โ an internal data-fetch loses the auth cookie (e2e/CLAUDE.md, open problem). Operate against nuxt dev.
2. Seeding
Package demo data โ every app has db:seed scripts (verified in the apps' package.json):
pnpm --filter velo db:seed
pnpm --filter velo db:seed:staging
crouton-seed (bin: packages/crouton-cli/bin/crouton-seed.mjs) discovers SeedProviders from every extended @fyit/crouton-* package, emits idempotent upsert SQL, and executes it via npx wrangler d1 execute <db> --local|--remote. Flags (verified): --db (required) --remote --dir --team (default test1) --locale (default nl) --with-staff --dry-run. It also seeds the app's default layout: crouton.layout.json โ a layout_configs row with id default (#709; lib/seed-app.ts).
โ ๏ธ THE seed-visibility trap (code-derived, not reproduced end-to-end): a local crouton-seed writes via wrangler d1 execute --local into <app>/.wrangler/state/v3/d1/miniflare-D1DatabaseObject/*.sqlite (miniflare = wrangler's local Cloudflare simulator), but nuxt dev with hub: { db: 'sqlite' } reads <app>/.data/db/sqlite.db. seed-app.ts has no copy step into .data/ (verified: it only shells out to wrangler d1 execute โ re-check with the seed grep in the re-verify block). So locally-seeded data may never appear in the dev app. crouton db-pull handles the split explicitly โ lib/db-pull.ts copies the imported DB to .data/db/sqlite.db and marks _hub_migrations applied. If seeded data "doesn't show up", this split is why; workaround is copying the sqlite file into .data/db/sqlite.db by hand (stop dev first).
App-specific content seeds (seedData/ convention): velo keeps CSVs/markdown under apps/velo/seedData/{school-velotek,velosolidaire}/, consumed by HTTP endpoints POST /api/seed and POST /api/seed/velosolidaire (apps/velo/server/api/seed/*.post.ts โ additive, dedupes users by email, hashes passwords properly). This is the "per-collection seeding" the db-clone skill contrasts itself with.
Pull real remote data local:
cd apps/velo && npx crouton db-pull --env staging --dry-run
Flags (verified in bin/crouton-generate.js dbPullCmd): --env, --config, --keep-sql, --dry-run. Flow: export remote D1 โ wipe local wrangler D1 โ import โ copy to .data/db/sqlite.db.
3. Inspecting the local DB
4. Review overlay and eruda
- Review overlay โ
NUXT_PUBLIC_CROUTON_REVIEW=true at build time (read in packages/crouton-devtools/src/module.ts) makes @fyit/crouton-devtools install @fyit/crouton-feedback (epic #960) with the GitHub sink defaulted: a glasses launcher with Console (eruda), Annotate (pin a comment on an element โ resolves the source file โ lands as a ๐ฏ Preview feedback PR comment), and Changelog. Generated cf:staging scripts bake the flag in; zero prod footprint. The sign-off workflow around it belongs to the ui-proposal skill.
- Eruda mobile devtools layer โ opt-in per app:
extends: ['@fyit/crouton-devtools/eruda'], active in local dev or when NUXT_PUBLIC_CROUTON_ERUDA=true at build (verified in packages/crouton-devtools/eruda/nuxt.config.ts; default off, chunk never fetched in prod). Set the flag in cf:staging only, never cf:deploy.
5. Observing deployed staging
Deploy/migrate mechanics live in the deploy skill โ this is the watching side.
| Need | Command | Caveat |
|---|
| Live Worker logs | pnpm --filter triage logs / --filter fanfare logs (= npx wrangler tail triage|fanfare) | Only some apps have a logs script (grep for it โ re-verify block), and those tail the production worker. No staging tail script exists anywhere (verified at the snapshot); hand-run npx wrangler tail <app>-staging (unverified against a live worker โ needs CF creds). No log persistence: an error nobody was tailing is gone. |
| Prove a deployed preview works | node scripts/smoke-deployed.mjs --url https://<app>.pmcp.dev --email <e> --password <pw> [--app <n>] [--manifest <app>/deploy.config.json] | Login proof via /api/auth/get-session โ optional CRUD round-trip (from deploy.config.json smoke.crud) โ screenshot screenshots/<app>-smoke.png. CI runs it per deploy, report-only unless smoke.required: true. |
| Seed a loginable review account on a deploy | node scripts/seed-review-login.mjs --url <deployedUrl> --email <e> --password <pw> | Uses the app's own HTTP auth routes (real password hashing); best-effort. |
| What's in the staging DB | node scripts/db-counts.mjs --app <app> --env staging | ยง3. |
At last verification, no runtime analytics was wired in any deployed app: packages/crouton-analytics exists but no app/poc/fixture depends on it (unverified beyond a grep of package.jsons โ re-run that grep before relying on it; from the #1073 discovery sweep, adapters were in-flight, #947).
6. Screenshots
node scripts/app-shots.mjs <baseUrl> <path[:name]> [more paths...] [--out <dir>]
(Also pnpm app:shots.) Writes screenshots/<name>.png; exits 1 on any failure. It auto-resolves the pre-installed chromium under /opt/pw-browsers/ (globs the newest build; override with PLAYWRIGHT_CHROMIUM_PATH) โ a failing npx playwright install does NOT mean "no browser"; doctrine: crouton-harness-observability ยง5. All screenshots go in screenshots/ at repo root (HARD GATE, root CLAUDE.md).
7. Where artifacts land
| Artifact | Location | Committed? |
|---|
| Local dev DB (NuxtHub) | <app>/.data/db/sqlite.db (+ applied-migration copies in .data/db/migrations/sqlite/) | No (.data gitignored โ verified git check-ignore) |
| Wrangler-local D1 (miniflare) | <app>/.wrangler/state/v3/d1/miniflare-D1DatabaseObject/*.sqlite | No |
| Build output | <app>/.output/ | No |
| Screenshots | screenshots/<name>.png | No (*.png gitignored) |
| Drizzle migrations (source of truth) | <app>/server/db/migrations/sqlite/ | Yes |
| e2e outputs | e2e/.auth/, playwright-report/, test-results/ | No |
Provenance and maintenance
verified: 2026-07-02
grep -rn devServer apps/*/nuxt.config.ts pocs/*/nuxt.config.ts
grep -n '"dev"\|"logs"\|db:seed' apps/*/package.json
grep -n 'wrangler\|\.data' packages/crouton-cli/lib/seed-app.ts
grep -rln crouton-analytics apps pocs fixtures --include=package.json
head -20 scripts/app-shots.mjs scripts/smoke-deployed.mjs
grep -n CROUTON_REVIEW packages/crouton-devtools/src/module.ts