| status | done | |
|---|---|---|
| depends | ||
| specs |
|
|
| issues | ||
| pr | 9 |
Establish the monorepo skeleton. Empty Fastify and Vite apps that boot. Shared package stub. Toolchain config. CI hello-world. No business logic; no specs implemented beyond the repo-layout sections. Everything downstream depends on this.
Out of scope: any actual storage, API endpoints, or UI screens. Those land in dedicated plans.
- specs/architecture.md — repo layout, workspace tool choice (npm), TypeScript posture, ESM-only, asdf-managed Node version, conventions section
- .claude/CLAUDE.md source-control conventions (commit style, lockfile commits, generated-changes-first)
npm initat the root withworkspaces: ["apps/*", "packages/*"]. Set"type": "module".asdf set nodejs latest:22→ commits.tool-versions.asdf install.- Create
apps/api/,apps/web/,packages/shared/with their ownpackage.jsonper the appropriate skill (backend-fastifyfor api,frontend-shadcnfor web, plain TS for shared). - Root
tsconfig.base.jsonwithstrict: true, ESM target. Per-workspacetsconfig.jsonextends base. - ESLint flat-config at root, shared across workspaces. Prettier optional — defer unless contributors ask.
.gitignorecoversnode_modules/,dist/,*.local.*,.env*, the devprivate-storage/directory (see behaviors/private-storage.md).apps/api/src/index.tsboots Fastify on${PORT:-3001}, registers a/api/healthroute returning{status:'ok'}. No more than 30 lines.apps/web/src/main.tsxmounts React, renders "Hello, Code for Philly" at/. shadcn init can wait untilweb-shell.packages/shared/src/index.tsexports nothing useful yet — placeholder.- Root
package.jsonscripts:dev— concurrently runtsx watchfor api + vite for webbuild— build both workspacestype-check—tsc --noEmitacross workspaceslint—eslint .
.github/workflows/ci.yml— checkout, asdf install,npm ci,npm run type-check,npm run lint,npm run build. No test step untiltest-harness.- Generated changes commit first per CLAUDE.md: each
npm install <pkg>call gets its own commit with the command in the body. Manual file edits in separate commits.
-
git clone … && npm install && npm run devworks on a fresh machine with only asdf preinstalled -
curl localhost:3001/api/healthreturns{"status":"ok"} - The web dev server serves the placeholder page at
http://localhost:5173/ -
npm run type-checkexits 0 -
npm run buildproducesapps/api/dist/andapps/web/dist/ -
.github/workflows/ci.ymlpasses on a clean push -
package-lock.jsonis committed at root - No
.jsfiles inapps/api/src/orapps/web/src/(TypeScript only)
- ESM-only landmines. Some Fastify plugins still ship CJS-only. Hit them as they come; document the workaround.
- shadcn init disrupts the file layout. Deferred to
web-shell; this plan keeps the web app minimal. - asdf vs Volta vs nvm on contributor machines. CLAUDE.md mandates asdf; CI uses asdf-action. Linux/macOS focus; Windows is best-effort.
- Vite 8 + npm-workspace-hoisted React —
apps/web/vite.config.tsneedsresolve.dedupe: ['react', 'react-dom']or Vite's dep scanner fails to resolvereact/jsx-dev-runtimeeven though the package exposes it viaexports. Without dedupe,/src/main.tsxreturns 500 with "Failed to resolve import 'react/jsx-dev-runtime'." Worth knowing when adding more React-adjacent packages. npm install -w <ws> <pkg>followed bynpm install -w <ws> -D <pkg>can silently drop runtime deps — happened withreact/react-dommid-plan. The lockfile retained the entries but the workspace manifest didn't, sonpm ciwould have produced a broken tree. Fix is to re-run the install explicitly (fix(web): persist react / react-dom in workspace deps). Pattern: when chaining workspace installs, verify each step's manifest before continuing.exactOptionalPropertyTypesis not part ofstrict— it's an independent opt-in. Setting it tofalseexplicitly was a no-op that implied intent without explanation; removed during PR review.- Latest deps as of cutover commit (May 2026): Fastify 5.8, Vite 8.0, React 19.2, typescript-eslint 6.x, asdf-vm/actions@v4, actions/checkout@v6. Pin in
package.jsoncarets; rolling-major tags for GitHub Actions. - CLAUDE.md lives at
.claude/CLAUDE.mdafter the relocation in this PR — both<project>/CLAUDE.mdand<project>/.claude/CLAUDE.mdare valid project-scoped locations for Claude Code; the latter keeps all Claude-specific config under one directory.
- Deferred to
api-skeleton—.env.examplelands whenEnvSchemais introduced; the!.env.examplecarve-out in.gitignoreis harmless until then.