AI agent index: llms.txt · Canonical requirements: docs/landingos-mvp.md
Mobile web/PWA for solo, budget travelers flying Poland → Milan-Bergamo (BGY). After landing at an unfamiliar airport, a traveler enters their flight number and a Milan destination and gets up to three clear transit variants (recommended / fastest / simplest), then optionally joins one temporary chat room shared by everyone on that flight to coordinate public transport or a shared taxi. LandingOS does not sell tickets, take payments, or verify boarding passes — purchase and navigation happen through external links.
The planner works with no one else in the room; the community layer is upside, not a dependency.
The LandingOS MVP AFK queue was implemented and closed through S10 on 2026-07-27. The production pilot still fails closed until live-provider commercial/licensing acceptance and independent privacy/compliance approval are recorded.
| Issue | Slice | Scope |
|---|---|---|
| #1 | PRD | Canonical requirements + delivery index (mirror of docs/landingos-mvp.md) |
| #2 | S0 | Provider contracts + deterministic fixtures + data-spike harness |
| #4 | S1 | Flight recognition + manual fallback |
| #5 | S2 | Destination selection + Milan boundary |
| #6 | S3 | Journey recommendations + transfer-catalog model |
| #7 | S4 | Operator Console (transfer-catalog admin) |
| #3 | S5 | Auth — email OTP + pseudonym + marketing consent |
| #8 | S6 | Flight Room core (rooms, selection, chat) |
| #9 | S7 | Safety — block, report, community-rules acceptance |
| #10 | S8 | Lifecycle & privacy (closure, retention, deletion) |
| #11 | S9 | Analytics funnel |
| #12 | S10 | PWA / mobile-readiness hardening (agent-browser E2E, manifest, native-API smoke) |
Each issue is self-contained with locked decisions and testable acceptance criteria. Read the
issue before starting its slice. All are completable unattended in fixture provider mode;
live provider credentials and privacy/compliance sign-off gate a production pilot only, never
code completion.
Monorepo using pnpm workspace:
- apps/user-application — TanStack Start SSR frontend (Polish UI)
- apps/data-service — Hono REST + WebSocket API, Durable Objects, scheduled handlers
- packages/data-ops — Shared DB layer (schemas, queries, auth)
Stack: TanStack Start, Hono, Better Auth, Drizzle ORM, Cloudflare Workers (Durable Objects for real-time chat), Neon Postgres.
Every package ships an AGENTS.md with structure, patterns, and workflows (CLAUDE.md symlinks to it). The root AGENTS.md carries the cross-cutting LandingOS constraints agents must follow.
pnpm install- Provision a Neon Postgres database.
- Create the per-environment env files (this repo has not run
init-projectyet — do so once, or fill the*.exampletemplates directly):packages/data-ops/.env.{dev,staging,production}—DATABASE_HOST/USERNAME/PASSWORD(see .env.example)apps/user-application/.env{,.staging,.production}—BETTER_AUTH_SECRET(openssl rand -base64 32),BETTER_AUTH_BASE_URL,AUTH_EMAIL_FROM, and the matchingVITE_API_TOKEN/DATA_SERVICE_API_TOKEN/API_TOKENtripleapps/data-service/.dev.vars(local) / Cloudflare dashboard (remote)
pnpm run setup && pnpm run db:generate:dev && pnpm run db:migrate:dev- Start dev in two terminals:
pnpm run dev:data-service(port 8788) andpnpm run dev:user-application(port 3000).
External providers (flight/places/transit) run in fixture mode by default and need no
credentials — see issue #2 (S0) for the contract and how live mode is enabled.
pnpm run dev:user-application # TanStack Start app (port 3000)
pnpm run dev:data-service # Hono API service (port 8788)From the repo root (proxies to packages/data-ops):
pnpm run db:generate:dev # Generate migration
pnpm run db:migrate:dev # Apply to database
pnpm run db:pull:dev # Pull schema from DB
pnpm run db:seed:dev # Seed sample data
pnpm run db:studio # Open Drizzle Studio (dev only)Replace dev with staging or production (except db:studio, which is dev-only).
Grant or revoke the initial transfer-catalog operator by normalized email with the non-interactive, database-only command:
pnpm run operator:role:dev grant operator@example.com
pnpm run operator:role:dev revoke operator@example.comReplace dev with staging or production. The command requires that environment's
DATABASE_HOST, DATABASE_USERNAME, and DATABASE_PASSWORD; no equivalent HTTP endpoint exists.
pnpm run test # run all tests
pnpm run test:watch # watch mode
pnpm run test:coverage # with coverage report
pnpm run types # type-check all packages (builds data-ops first)
pnpm run lint # Biome checks
pnpm run test:e2e # self-starting agent-browser suite at mobile + desktop viewports
pnpm run smoke:native-api # raw Bearer HTTP + raw WebSocket smoke testUses Vitest with workspace projects (Cloudflare Workers pool where
needed). Run tests + types before declaring a slice done — every issue requires
lint, types, and test to exit 0. The S10 E2E and native smoke commands are
non-interactive, use deterministic in-memory fixtures, and require no live-provider credentials.
pnpm run deploy:holding-page
pnpm run deploy:staging:user-application
pnpm run deploy:staging:data-service
pnpm run deploy:production:user-application
pnpm run deploy:production:data-serviceapps/holding-page is the temporary, static public site for landingos.app. It has no
application bindings, analytics, forms, or link to staging. Preview it locally with
pnpm run dev:holding-page (port 3001), and validate its bundle without publishing with
pnpm run deploy:dry-run:holding-page.
Before the first production user-application deployment, detach the landingos.app Custom
Domain from the landingos-holding Worker and deploy landingos-ua-production immediately.
Do not remove or alter the staging.landingos.app Custom Domain during that cutover.
Secrets sync: bash apps/{app}/sync-secrets.sh {env}. Fixture provider data must never render
in staging/production. See .claude/rules/cloudflare-deployment.md for hostname/SSL/redirect rules.
To deploy to a different CF account, copy .env.example to .env and fill in CLOUDFLARE_ACCOUNT_ID + CLOUDFLARE_API_TOKEN.
Planning skills (brainstormer) are pre-configured via extraKnownMarketplaces / enabledPlugins in .claude/settings.json and install on first open. Update with /plugin marketplace update brainstormer.