Scaffold Next.js frontend in web/ (Track C) - #17
Open
kirillakovalenko wants to merge 2 commits into
Open
Conversation
Next.js 16 (App Router) + React 19 + Tailwind 4 + TypeScript, configured for static export to Firebase Hosting per Implementation-Plan Track C. Runs, builds, lints, and deploys; renders a placeholder page. Stops at a running shell on purpose. The device select screen, symptom form, and result dashboard all read device_catalog.json, whose schema is frozen in Phase 0 task 9 — building them now would invent the contract that Tracks A, B, and D queue behind. Settles an open question in PRD §5: it lists the market data service as "Cloud Function / Next.js route handler", but Next.js does not support Route Handlers under output: "export". Track B must ship as a standalone Cloud Function. Hosting headers move to the repo-root firebase.json, since next.config headers are unsupported under static export too. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`npm run start` failed outright under output: "export" — next start requires a Node server that a static export does not have. Next.js errors and points at serving the folder instead. Replace it with `npm run preview` (serve out), with serve pinned as a devDependency rather than fetched via npx, so previewing a build works offline. This matters while the team is developing locally without deploy access. Add engines.node >=20.9 and .nvmrc so a team on mixed machines without Docker stays on one Node version. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
jpatrickdevine
approved these changes
Jul 30, 2026
Contributor
|
@kirillakovalenko I approved but then I realized it's merging into the chore branch? Was that intentional or did you want to merge the feature branch into main? |
kirillakovalenko
changed the base branch from
chore/archive-legacy-prototype
to
main
July 31, 2026 02:09
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Scaffolds the Track C frontend in
web/— Next.js 16 (App Router), React 19,Tailwind 4, TypeScript — configured for static export to Firebase Hosting. It runs,
builds, lints, and is deployable. It renders a placeholder page, deliberately.
Targets
maindirectly. This was originally stacked on #16 so it could build on a treewith the prototype already archived; now that #16 has merged, that is simply the state
of
main, and this PR contains only the Next.js work — 2 commits, 20 files.Motivation
Implementation-Plan Track C, first task: "Scaffold with static export to Firebase
Hosting." Track C is meant to run against a mock catalog and a stubbed proxy so it
never blocks on Track A or B — but it needs to exist first.
Changes
web/— Next.js 16 App Router app withsrc/layout and@/*import alias.web/next.config.ts—output: "export",trailingSlash: true(emitsout/route/index.html, so any static host serves it with no rewrite rules), andimages.unoptimized(the defaultnext/imageloader needs a server).firebase.jsonat repo root — hosting fromweb/out, immutable caching for/_next/static/**,no-cachefor HTML, plusX-Content-Type-Options,Referrer-Policy, andX-Frame-Options..firebaserc.example—.firebasercitself is gitignored until infra isprovisioned (Phase 0 task 6), so a placeholder project id cannot be deployed by
accident.
web/.env.example—NEXT_PUBLIC_*only, with a note that in a static export everyone of those is compiled into browser JavaScript and is therefore public.
startscript withpreview.next startfails outrightunder
output: "export"— it needs a Node server a static export doesn't have, andNext.js errors telling you to serve the folder instead.
npm run previewdoes thatvia
serve, pinned as a devDependency rather than fetched throughnpxso it worksoffline. This matters while the team develops locally without deploy access.
engines.node >=20.9and.nvmrc— keeps a team on mixed machines on one Nodeversion without reaching for Docker.
.gitignore— Node/Next/Firebase sections.CLAUDE.md— Commands section now lists the real frontend commands; repo-state andEnvironment sections updated; the route-handler constraint recorded under Target
architecture.
web/README.md— how to run and deploy it, and what is deliberately missing.AGENTS.md, which isNext 16's own agent guidance and is what pointed me at the bundled docs.
This settles an open question in PRD §5. The PRD lists the market data service as
"Cloud Function / or Next.js route handler". Those are mutually exclusive: under
output: "export"Next.js supports no Route Handlers, Server Actions, Middleware, ornext.configheaders/redirects/rewrites. Since Track C specifies static export,Track B must ship as a standalone Cloud Function. That is why hosting headers are in
firebase.jsonrather thannext.config.ts. Nothing in the PRD's design needs thefeatures we gave up — no user state is persisted anyway (invariant 1).
What is deliberately missing. The device select screen, symptom form, and result
dashboard all read
device_catalog.json, whose schema is frozen in Phase 0 task 9 —which has not happened. Building them now would invent the contract that Tracks A, B,
and D queue behind, which CLAUDE.md explicitly warns against. So this stops at a running
shell. Also absent: a real favicon (still the Next.js default) and any visual design.
Validation
npm run build— compiles, typechecks, and writes a 40-file static export toweb/out/without/index.htmltitled "Revive or Recycle". Google fonts areself-hosted as
.woff2in the bundle, so there is no runtime third-party request.npm run lint— ESLint clean, exit 0.npm run dev— dev server ready in 612ms;curl localhost:3000returns HTTP 200and serves the expected title. Server stopped afterward, port 3000 confirmed clear.
npm run preview— serves the builtout/folder, HTTP 200 with the expectedtitle. This replaced
npm run start, which I confirmed fails with"next start" does not work with "output: export" configuration.git status --short— confirmsnode_modules/,web/out/, and.next/are allcorrectly ignored; only 19 source and config files are staged.
Not validated:
firebase deploy. The Firebase CLI is not installed locally and no GCPproject exists yet (Phase 0 task 6), so hosting config is written but unexercised.
Risk
Low. Additive — this creates a new directory and touches no existing code. There is
nothing deployed to break.
firebase.jsonis unexercised. Thepublic: "web/out"path and header rules arewritten from the docs, not verified against a real deploy. First deploy should be to a
preview channel.
trailingSlash: truefixes the URL shape now (/describe/, not/describe).Changing it later after links are public means redirects.
ships an
AGENTS.mdwarning that its APIs differ from older knowledge — I read theversion's own bundled docs in
node_modules/next/dist/docs/rather than relying onrecall, and the static-export constraints above come from there.
.firebasercgitignore is a deliberate departure from the Firebase convention ofcommitting it. Once the project id exists the team may prefer to commit it instead;
that is a one-line change.
Rollout and Recovery
Merges after #16. Nothing ships to users — there is no Firebase project yet, so this
changes no running system. CI (
pr-description-check.yml) does not touchweb/.To recover, revert the merge commit;
web/and the two root config files disappear andnothing else is affected, since no existing file depends on them.
Follow-ups this unblocks, in order: provision the GCP project and fill
.firebaserc(task 6), freeze
device_catalog.json(task 9), then the rest of Track C against themock fixture.