Set up SvelteKit project with Tailwind and design tokens #1

Open
opened 2026-05-05 10:56:38 +02:00 by marcel · 9 comments
Owner

Task 1 — Plan reference: docs/superpowers/plans/2026-05-05-erbstuecke-wannsee.md

User story: As a developer, I need a working SvelteKit project scaffold with the correct dependencies and design system configured so that all subsequent tasks have a consistent foundation.

Acceptance criteria

  • npx sv create erbstuecke-wannsee scaffolds a SvelteKit 2 + Svelte 5 + TypeScript project
  • @sveltejs/adapter-node is configured in svelte.config.js
  • Tailwind CSS 3 is configured with the project design tokens as named colors (canvas, surface, line, primary, primary-dark, accent, ink, ink-muted, status-free, status-taken, admin-bg)
  • Lora (serif) and Inter (sans) fonts loaded via @fontsource packages (no CDN dependency)
  • src/app.d.ts declares App.Locals with familyCode and admin fields
  • src/lib/types.ts exports KATEGORIEN (8 fixed categories) and shared row types
  • npm run dev starts without TypeScript errors

Files to create

  • svelte.config.js, tailwind.config.js, postcss.config.js, vite.config.ts
  • src/app.html, src/app.css, src/app.d.ts
  • src/lib/types.ts

Design tokens

Tailwind name Hex
canvas #EAE5DC
surface #FFFFFF
line #E0D8CC
primary #5B7A66
primary-dark #4A6855
accent #C4874A
ink #1C2820
ink-muted #6B6050
status-free #4A7C5C
status-taken #9B6060
admin-bg #2A3B30

Dependencies to install

npm install better-sqlite3 sharp bcryptjs @fontsource/lora @fontsource/inter @sveltejs/adapter-node
npm install --save-dev @types/better-sqlite3 @types/bcryptjs

Size: S | Spec: system-design §1 (Stack)

## Task 1 — Plan reference: `docs/superpowers/plans/2026-05-05-erbstuecke-wannsee.md` **User story:** As a developer, I need a working SvelteKit project scaffold with the correct dependencies and design system configured so that all subsequent tasks have a consistent foundation. ### Acceptance criteria - [ ] `npx sv create erbstuecke-wannsee` scaffolds a SvelteKit 2 + Svelte 5 + TypeScript project - [ ] `@sveltejs/adapter-node` is configured in `svelte.config.js` - [ ] Tailwind CSS 3 is configured with the project design tokens as named colors (`canvas`, `surface`, `line`, `primary`, `primary-dark`, `accent`, `ink`, `ink-muted`, `status-free`, `status-taken`, `admin-bg`) - [ ] Lora (serif) and Inter (sans) fonts loaded via `@fontsource` packages (no CDN dependency) - [ ] `src/app.d.ts` declares `App.Locals` with `familyCode` and `admin` fields - [ ] `src/lib/types.ts` exports `KATEGORIEN` (8 fixed categories) and shared row types - [ ] `npm run dev` starts without TypeScript errors ### Files to create - `svelte.config.js`, `tailwind.config.js`, `postcss.config.js`, `vite.config.ts` - `src/app.html`, `src/app.css`, `src/app.d.ts` - `src/lib/types.ts` ### Design tokens | Tailwind name | Hex | |---|---| | `canvas` | `#EAE5DC` | | `surface` | `#FFFFFF` | | `line` | `#E0D8CC` | | `primary` | `#5B7A66` | | `primary-dark` | `#4A6855` | | `accent` | `#C4874A` | | `ink` | `#1C2820` | | `ink-muted` | `#6B6050` | | `status-free` | `#4A7C5C` | | `status-taken` | `#9B6060` | | `admin-bg` | `#2A3B30` | ### Dependencies to install ``` npm install better-sqlite3 sharp bcryptjs @fontsource/lora @fontsource/inter @sveltejs/adapter-node npm install --save-dev @types/better-sqlite3 @types/bcryptjs ``` **Size:** S | **Spec:** system-design §1 (Stack)
marcel added this to the v1.0 — MVP milestone 2026-05-05 10:56:38 +02:00
Author
Owner

👤 Markus Keller — Application Architect

Observations

  • The acceptance criteria are a clean, direct translation of the system design spec §1 — the stack table, adapter choice, and env variable names are all consistent.
  • App.Locals shape in the plan (familyCode: { id, code, displayName } | null) matches the hooks design exactly. Good.
  • KATEGORIEN is defined as a const tuple with as const — this correctly enforces the 8-category closed set at the type level. No runtime branching needed.
  • The plan puts all shared types in src/lib/types.ts and all DB logic in src/lib/db.ts. This matches the single-responsibility-per-module rule.
  • The tailwind.config.js token structure nests primary and ink as objects with DEFAULT — this means Tailwind classes like text-primary, text-primary-dark, text-ink, text-ink-muted all resolve correctly. Correct approach.
  • The app.css uses @layer base with @apply font-sans text-ink bg-canvas on body — this is the right place to set document-level defaults. Consistent with not hardcoding hex values in components.

Recommendations

  • The App.Locals interface uses displayName (camelCase) but the DB schema stores display_name (snake_case). Align on one convention at the boundary: map to camelCase in hooks.server.ts when populating locals, never expose snake_case into Svelte templates.
  • The app.html does not include a <meta name="robots" content="noindex"> tag. For a private family app, add this to prevent accidental indexing if TLS is ever misconfigured and the URL leaks.
  • The issue acceptance criteria do not mention npm run build completing without errors — only npm run dev. Add a build-time check as a final criterion, because adapter-node type mismatches surface only at build time.
  • The plan's Step 2 installs @sveltejs/adapter-node as a runtime dependency (npm install). It should be a dev dependency (npm install --save-dev) since it is only used during build; the build output does not import it at runtime.

Open Decisions (omit if none)

  • canvas color discrepancy — The issue's design token table lists canvas as #EAE5DC, but the ui_expert.md brand system lists canvas as #F2EDE4. One of these is wrong. The plan and implementation must use the same hex value. Decide which is authoritative before Step 4 is executed.
## 👤 Markus Keller — Application Architect ### Observations - The acceptance criteria are a clean, direct translation of the system design spec §1 — the stack table, adapter choice, and env variable names are all consistent. - `App.Locals` shape in the plan (`familyCode: { id, code, displayName } | null`) matches the hooks design exactly. Good. - `KATEGORIEN` is defined as a `const` tuple with `as const` — this correctly enforces the 8-category closed set at the type level. No runtime branching needed. - The plan puts all shared types in `src/lib/types.ts` and all DB logic in `src/lib/db.ts`. This matches the single-responsibility-per-module rule. - The `tailwind.config.js` token structure nests `primary` and `ink` as objects with `DEFAULT` — this means Tailwind classes like `text-primary`, `text-primary-dark`, `text-ink`, `text-ink-muted` all resolve correctly. Correct approach. - The `app.css` uses `@layer base` with `@apply font-sans text-ink bg-canvas` on `body` — this is the right place to set document-level defaults. Consistent with not hardcoding hex values in components. ### Recommendations - The `App.Locals` interface uses `displayName` (camelCase) but the DB schema stores `display_name` (snake_case). Align on one convention at the boundary: map to camelCase in `hooks.server.ts` when populating `locals`, never expose snake_case into Svelte templates. - The `app.html` does not include a `<meta name="robots" content="noindex">` tag. For a private family app, add this to prevent accidental indexing if TLS is ever misconfigured and the URL leaks. - The issue acceptance criteria do not mention `npm run build` completing without errors — only `npm run dev`. Add a build-time check as a final criterion, because `adapter-node` type mismatches surface only at build time. - The plan's Step 2 installs `@sveltejs/adapter-node` as a runtime dependency (`npm install`). It should be a dev dependency (`npm install --save-dev`) since it is only used during build; the build output does not import it at runtime. ### Open Decisions _(omit if none)_ - **`canvas` color discrepancy** — The issue's design token table lists `canvas` as `#EAE5DC`, but the `ui_expert.md` brand system lists `canvas` as `#F2EDE4`. One of these is wrong. The plan and implementation must use the same hex value. Decide which is authoritative before Step 4 is executed.
Author
Owner

👤 Felix Brandt — Fullstack Developer

Observations

  • The plan follows TDD correctly for Task 1: the scaffold step produces working code before any feature logic is written. This is the right baseline.
  • src/lib/types.ts exports KATEGORIEN as a const tuple — as const ensures typeof KATEGORIEN[number] is a proper union type. ArtikelRow, ArtikelFotoRow, and ReservierungRow are plain object types with no inheritance: correct for a flat SQLite result shape.
  • The app.d.ts declares familyCode and admin as nullable (| null) — this forces all downstream load functions and form actions to guard against null explicitly. Good defensive typing.
  • The Tailwind config's fontFamily override replaces the default serif and sans stacks — this means font-serif and font-sans classes will use Lora and Inter throughout, with Georgia and system-ui as fallbacks. Correct.
  • The app.css imports specific weight files from @fontsource (/400.css, /600.css, etc.) — this is correct; importing the bare package would load all weights and bloat the bundle.

Recommendations

  • The @fontsource/inter import list includes weight 800. Check whether Inter 800 (ExtraBold) is actually specified anywhere in the design system. If it is only used in one place, prefer font-extrabold with the 700 weight loaded — unnecessary font weights add ~30–40 KB each to the CSS bundle.
  • The plan's Step 9 (types.ts) does not include a CodeRow type. The hooks.server.ts (Task 3 in the full plan) will need { id: number; code: string; display_name: string } from the DB query result. Define it in types.ts now to avoid a future as any cast in the hooks layer.
  • The postcss.config.js uses object shorthand ({ tailwindcss: {}, autoprefixer: {} }) — verify the installed Tailwind version is 3.x. Tailwind 4 changed the PostCSS integration to a single @tailwindcss/postcss plugin. The issue specifies Tailwind 3, so this config is correct, but pin tailwindcss to ^3 in package.json to prevent accidental upgrades.
  • The acceptance criterion "npm run dev starts without TypeScript errors" is verified by running the dev server — but TypeScript errors in SvelteKit are surfaced by svelte-check, not the Vite dev server. Add npm run check (which runs svelte-check) as an explicit verification step.

Open Decisions (omit if none)

  • @sveltejs/adapter-node as runtime vs dev dependency — The plan installs it as a runtime dep. It is only needed at build time. If it ends up in the production node_modules (because it was not --save-dev), the Docker image runner stage (npm ci --omit=dev) will correctly exclude it only if it is listed under devDependencies. Clarify intent before the scaffold step to avoid a bloated production image.
## 👤 Felix Brandt — Fullstack Developer ### Observations - The plan follows TDD correctly for Task 1: the scaffold step produces working code before any feature logic is written. This is the right baseline. - `src/lib/types.ts` exports `KATEGORIEN` as a `const` tuple — `as const` ensures `typeof KATEGORIEN[number]` is a proper union type. `ArtikelRow`, `ArtikelFotoRow`, and `ReservierungRow` are plain object types with no inheritance: correct for a flat SQLite result shape. - The `app.d.ts` declares `familyCode` and `admin` as nullable (`| null`) — this forces all downstream load functions and form actions to guard against `null` explicitly. Good defensive typing. - The Tailwind config's `fontFamily` override replaces the default `serif` and `sans` stacks — this means `font-serif` and `font-sans` classes will use Lora and Inter throughout, with Georgia and system-ui as fallbacks. Correct. - The `app.css` imports specific weight files from `@fontsource` (`/400.css`, `/600.css`, etc.) — this is correct; importing the bare package would load all weights and bloat the bundle. ### Recommendations - The `@fontsource/inter` import list includes weight `800`. Check whether Inter 800 (ExtraBold) is actually specified anywhere in the design system. If it is only used in one place, prefer `font-extrabold` with the `700` weight loaded — unnecessary font weights add ~30–40 KB each to the CSS bundle. - The plan's Step 9 (`types.ts`) does not include a `CodeRow` type. The `hooks.server.ts` (Task 3 in the full plan) will need `{ id: number; code: string; display_name: string }` from the DB query result. Define it in `types.ts` now to avoid a future `as any` cast in the hooks layer. - The `postcss.config.js` uses object shorthand (`{ tailwindcss: {}, autoprefixer: {} }`) — verify the installed Tailwind version is 3.x. Tailwind 4 changed the PostCSS integration to a single `@tailwindcss/postcss` plugin. The issue specifies Tailwind 3, so this config is correct, but pin `tailwindcss` to `^3` in `package.json` to prevent accidental upgrades. - The acceptance criterion "npm run dev starts without TypeScript errors" is verified by running the dev server — but TypeScript errors in SvelteKit are surfaced by `svelte-check`, not the Vite dev server. Add `npm run check` (which runs `svelte-check`) as an explicit verification step. ### Open Decisions _(omit if none)_ - **`@sveltejs/adapter-node` as runtime vs dev dependency** — The plan installs it as a runtime dep. It is only needed at build time. If it ends up in the production `node_modules` (because it was not `--save-dev`), the Docker image runner stage (`npm ci --omit=dev`) will correctly exclude it only if it is listed under `devDependencies`. Clarify intent before the scaffold step to avoid a bloated production image.
Author
Owner

👤 Nora "NullX" Steiner — Application Security Engineer

Observations

  • This is a scaffold task — no auth, no cookies, no file handling yet. The direct security attack surface at this stage is low.
  • The app.d.ts shape is security-relevant: declaring familyCode and admin as | null means the type system will force explicit null-checks at every auth boundary downstream. This is the correct foundation.
  • The App.Locals interface names the field familyCode (not familyCodeId) — the full DB row is stored in locals. This means downstream code can read locals.familyCode.id without a second DB lookup. Correct and consistent with the system design.
  • No SESSION_SECRET reference appears in this task. That is appropriate — it will be needed in Task 3 (hooks) and Task 5 (admin auth), not the scaffold.

Recommendations

  • Add src/app.html does not set a Content-Security-Policy meta tag. For this project, Caddy will deliver the CSP header — but document this expectation explicitly in a comment in app.html or in the Caddyfile task (Task 8). Without documentation, a future developer may wonder why there is no CSP in the HTML.
  • The tailwind.config.js content glob includes ./src/**/*.{html,js,svelte,ts} — this is correct and does not accidentally include test fixture files or spec HTML from docs/. Confirm docs/ is outside the src/ tree (it is, per the file map), so no design system HTML tokens will leak into the Tailwind purge output.
  • Ensure .env is listed in .gitignore before the first commit. The plan's Step 11 runs git add -A && git commit. If .gitignore does not exclude .env, a developer who creates .env locally before committing will accidentally commit it. Add a .gitignore step to the scaffold task.
  • The app.html uses data-sveltekit-preload-data="hover" — this is safe for a private app, but note that preloading on hover will fire load functions before the user clicks. For the gate screen (which validates the ?code= param), this is fine. No security concern at this stage.

Open Decisions (omit if none)

  • .gitignore contents not specified in this task — The npx sv create scaffold will generate a default .gitignore. Confirm it includes .env, node_modules/, and .svelte-kit/. If using sv create with the official template, this is likely covered — but it should be an explicit acceptance criterion, not an assumption.
## 👤 Nora "NullX" Steiner — Application Security Engineer ### Observations - This is a scaffold task — no auth, no cookies, no file handling yet. The direct security attack surface at this stage is low. - The `app.d.ts` shape is security-relevant: declaring `familyCode` and `admin` as `| null` means the type system will force explicit null-checks at every auth boundary downstream. This is the correct foundation. - The `App.Locals` interface names the field `familyCode` (not `familyCodeId`) — the full DB row is stored in locals. This means downstream code can read `locals.familyCode.id` without a second DB lookup. Correct and consistent with the system design. - No `SESSION_SECRET` reference appears in this task. That is appropriate — it will be needed in Task 3 (hooks) and Task 5 (admin auth), not the scaffold. ### Recommendations - Add `src/app.html` does not set a `Content-Security-Policy` meta tag. For this project, Caddy will deliver the CSP header — but document this expectation explicitly in a comment in `app.html` or in the Caddyfile task (Task 8). Without documentation, a future developer may wonder why there is no CSP in the HTML. - The `tailwind.config.js` `content` glob includes `./src/**/*.{html,js,svelte,ts}` — this is correct and does not accidentally include test fixture files or spec HTML from `docs/`. Confirm `docs/` is outside the `src/` tree (it is, per the file map), so no design system HTML tokens will leak into the Tailwind purge output. - Ensure `.env` is listed in `.gitignore` before the first commit. The plan's Step 11 runs `git add -A && git commit`. If `.gitignore` does not exclude `.env`, a developer who creates `.env` locally before committing will accidentally commit it. Add a `.gitignore` step to the scaffold task. - The `app.html` uses `data-sveltekit-preload-data="hover"` — this is safe for a private app, but note that preloading on hover will fire load functions before the user clicks. For the gate screen (which validates the `?code=` param), this is fine. No security concern at this stage. ### Open Decisions _(omit if none)_ - **`.gitignore` contents not specified in this task** — The `npx sv create` scaffold will generate a default `.gitignore`. Confirm it includes `.env`, `node_modules/`, and `.svelte-kit/`. If using `sv create` with the official template, this is likely covered — but it should be an explicit acceptance criterion, not an assumption.
Author
Owner

👤 Sara Holt — QA Engineer & Test Strategist

Observations

  • The acceptance criteria are specific and binary — each criterion is independently verifiable. This is the correct format for a scaffold task.
  • The plan selects Vitest during npx sv create — this means vitest.config.ts (or the Vite config integration) will be generated by the scaffold, not hand-written. This is correct for the scaffold task; Felix's TDD tests in Task 2 build on this foundation.
  • The issue does not include any test files in the "Files to create" list — correct, because the scaffold task produces infrastructure, not tested behavior. Task 2 (Database Layer) is where the first failing tests appear.
  • The KATEGORIEN array is a fixed-at-compile-time list. A unit test that asserts KATEGORIEN.length === 8 and that each member is a non-empty string is inexpensive and catches future accidental edits. This is worth adding to the Task 2 test suite.

Recommendations

  • The acceptance criterion "npm run dev starts without TypeScript errors" should be split into two explicit steps: (1) npm run dev loads the server, (2) npx svelte-check --tsconfig ./tsconfig.json exits 0. The dev server swallows some type errors silently; svelte-check catches them all.
  • Add a vitest.config.ts verification step: run npx vitest run immediately after scaffold to confirm the baseline test suite (any tests the sv create template generates) passes before any code is added. This establishes a known-green baseline.
  • The App.Locals interface in app.d.ts should have an integration test in Task 3 (hooks) that asserts locals.familyCode is null when no cookie is present and has the correct shape when a valid cookie is present. Flag this now so it is not forgotten when implementing the hooks layer.
  • The KATEGORIEN const is declared in types.ts but has no corresponding test file listed in this task's scope. Confirm that src/lib/types.test.ts is planned for Task 2 or that type-level coverage is considered sufficient for a const export.

Open Decisions (omit if none)

  • Vitest configuration locationsv create may generate Vitest config inline in vite.config.ts rather than a standalone vitest.config.ts. The plan's file map lists vite.config.ts but not vitest.config.ts. Decide which approach is used so downstream tasks reference the correct config file when adding test scripts.
## 👤 Sara Holt — QA Engineer & Test Strategist ### Observations - The acceptance criteria are specific and binary — each criterion is independently verifiable. This is the correct format for a scaffold task. - The plan selects Vitest during `npx sv create` — this means `vitest.config.ts` (or the Vite config integration) will be generated by the scaffold, not hand-written. This is correct for the scaffold task; Felix's TDD tests in Task 2 build on this foundation. - The issue does not include any test files in the "Files to create" list — correct, because the scaffold task produces infrastructure, not tested behavior. Task 2 (Database Layer) is where the first failing tests appear. - The `KATEGORIEN` array is a fixed-at-compile-time list. A unit test that asserts `KATEGORIEN.length === 8` and that each member is a non-empty string is inexpensive and catches future accidental edits. This is worth adding to the Task 2 test suite. ### Recommendations - The acceptance criterion "npm run dev starts without TypeScript errors" should be split into two explicit steps: (1) `npm run dev` loads the server, (2) `npx svelte-check --tsconfig ./tsconfig.json` exits 0. The dev server swallows some type errors silently; `svelte-check` catches them all. - Add a `vitest.config.ts` verification step: run `npx vitest run` immediately after scaffold to confirm the baseline test suite (any tests the `sv create` template generates) passes before any code is added. This establishes a known-green baseline. - The `App.Locals` interface in `app.d.ts` should have an integration test in Task 3 (hooks) that asserts `locals.familyCode` is `null` when no cookie is present and has the correct shape when a valid cookie is present. Flag this now so it is not forgotten when implementing the hooks layer. - The `KATEGORIEN` const is declared in `types.ts` but has no corresponding test file listed in this task's scope. Confirm that `src/lib/types.test.ts` is planned for Task 2 or that type-level coverage is considered sufficient for a `const` export. ### Open Decisions _(omit if none)_ - **Vitest configuration location** — `sv create` may generate Vitest config inline in `vite.config.ts` rather than a standalone `vitest.config.ts`. The plan's file map lists `vite.config.ts` but not `vitest.config.ts`. Decide which approach is used so downstream tasks reference the correct config file when adding test scripts.
Author
Owner

👤 Leonie Voss — UI/UX Design Lead

Observations

  • The design token table in the issue matches the Tailwind config in the plan exactly — canvas, surface, line, primary, primary-dark, accent, ink, ink-muted, status-free, status-taken, admin-bg are all present. No hex values are hardcoded in component files at this stage.
  • Lora and Inter are loaded via @fontsource (no CDN dependency) — correct per the spec. The fontFamily config overrides serif and sans stacks with correct fallbacks (Georgia, system-ui).
  • The app.css sets body { @apply font-sans text-ink bg-canvas; } — this means every page inherits Inter, the dark ink text color, and the warm canvas background by default. Components that need Lora use font-serif. This is the right layering.
  • The app.html sets lang="de" — correct for a German-only app. Screen readers use this to select the correct pronunciation and hyphenation rules.

Recommendations

  • The tailwind.config.js does not configure fontFamily weights explicitly — Tailwind's font utilities do not control which weights are loaded, only the font-family stack. The @fontsource imports in app.css are the actual weight gate. Verify that the weights imported (400, 600, 700 for Lora; 400, 500, 600, 700, 800 for Inter) cover every usage in the design spec before later tasks start using font-bold (700) or font-semibold (600) on Inter elements.
  • The canvas token in the issue is #EAE5DC, but the ui_expert.md brand system lists #F2EDE4. This will cause visible color inconsistency between the spec HTML mockups and the implemented app. Resolve before any UI task begins — the design system HTML files are the visual ground truth.
  • The app.html viewport meta uses initial-scale=1 without user-scalable=no. Do not add user-scalable=no — WCAG 1.4.4 (Resize Text) requires that users can zoom. The current initial-scale=1 without zoom lock is correct.
  • The app.css imports @fontsource/inter/800.css (ExtraBold). Verify a concrete usage in the design spec that requires weight 800. If only weights up to 700 are used, remove the 800 import to save ~35 KB of CSS.

Open Decisions (omit if none)

  • canvas color authoritative value#EAE5DC (issue + plan) vs #F2EDE4 (ui_expert.md brand table + design system HTML). Both are warm off-whites but visually distinguishable side-by-side. This must be resolved before any CSS is written. (Raised by: Leonie Voss, also flagged by Markus Keller)
## 👤 Leonie Voss — UI/UX Design Lead ### Observations - The design token table in the issue matches the Tailwind config in the plan exactly — `canvas`, `surface`, `line`, `primary`, `primary-dark`, `accent`, `ink`, `ink-muted`, `status-free`, `status-taken`, `admin-bg` are all present. No hex values are hardcoded in component files at this stage. - Lora and Inter are loaded via `@fontsource` (no CDN dependency) — correct per the spec. The `fontFamily` config overrides `serif` and `sans` stacks with correct fallbacks (Georgia, system-ui). - The `app.css` sets `body { @apply font-sans text-ink bg-canvas; }` — this means every page inherits Inter, the dark ink text color, and the warm canvas background by default. Components that need Lora use `font-serif`. This is the right layering. - The `app.html` sets `lang="de"` — correct for a German-only app. Screen readers use this to select the correct pronunciation and hyphenation rules. ### Recommendations - The `tailwind.config.js` does not configure `fontFamily` weights explicitly — Tailwind's font utilities do not control which weights are loaded, only the font-family stack. The `@fontsource` imports in `app.css` are the actual weight gate. Verify that the weights imported (`400, 600, 700` for Lora; `400, 500, 600, 700, 800` for Inter) cover every usage in the design spec before later tasks start using `font-bold` (700) or `font-semibold` (600) on Inter elements. - The `canvas` token in the issue is `#EAE5DC`, but the `ui_expert.md` brand system lists `#F2EDE4`. This will cause visible color inconsistency between the spec HTML mockups and the implemented app. Resolve before any UI task begins — the design system HTML files are the visual ground truth. - The `app.html` viewport meta uses `initial-scale=1` without `user-scalable=no`. Do not add `user-scalable=no` — WCAG 1.4.4 (Resize Text) requires that users can zoom. The current `initial-scale=1` without zoom lock is correct. - The `app.css` imports `@fontsource/inter/800.css` (ExtraBold). Verify a concrete usage in the design spec that requires weight 800. If only weights up to 700 are used, remove the 800 import to save ~35 KB of CSS. ### Open Decisions _(omit if none)_ - **`canvas` color authoritative value** — `#EAE5DC` (issue + plan) vs `#F2EDE4` (ui_expert.md brand table + design system HTML). Both are warm off-whites but visually distinguishable side-by-side. This must be resolved before any CSS is written. _(Raised by: Leonie Voss, also flagged by Markus Keller)_
Author
Owner

👤 Tobias Wendt (@tobiwendt) — DevOps & Platform Engineer

Observations

  • This task correctly defers Dockerfile and docker-compose.yml to a later task. The scaffold produces a local dev environment only. No infrastructure concerns at this stage.
  • @sveltejs/adapter-node is listed as a runtime dependency in the plan (npm install @sveltejs/adapter-node). The adapter is used only during npm run build — it should be --save-dev. The multi-stage Dockerfile runner stage runs npm ci --omit=dev, so a miscategorized adapter will bloat the production image with a build-only package.
  • The plan installs better-sqlite3 as a runtime dependency — correct. The native module is needed at runtime by the running Node process.
  • sharp is correctly a runtime dependency — it does image processing at request time inside the running container, not only at build time.

Recommendations

  • Move @sveltejs/adapter-node to devDependencies. In the plan's Step 2, change: npm install @sveltejs/adapter-nodenpm install --save-dev @sveltejs/adapter-node. This keeps the production image lean and matches how every SvelteKit project handles adapters.
  • Add a .env.example file to the scaffold task output. Even though no environment variables are consumed yet, establishing the file at Step 1 sets the convention and ensures it is committed before anyone creates a .env. Minimal initial content:
    # .env.example — copy to .env and fill in real values (never commit .env)
    DATABASE_PATH=./local.db
    UPLOAD_DIR=./uploads
    SESSION_SECRET=
    ADMIN_MARCEL_PASSWORD_HASH=
    ADMIN_RENATE_PASSWORD_HASH=
    ADMIN_BERIT_PASSWORD_HASH=
    
  • The plan's commit step (git add -A) will stage everything in the scaffolded directory. Verify npx sv create generates a .gitignore that excludes .env, .svelte-kit/, and node_modules/ before running the commit step.
  • Pin the Node version in the Dockerfile (Task 8) to node:22-alpine — consistent with the LTS version used during development. Note it here so the scaffold's package.json engines field can be set to "node": ">=22" from the start.

Open Decisions (omit if none)

  • @sveltejs/adapter-node dependency category — Runtime (dependencies) vs build-time (devDependencies). Installing as runtime is technically harmless for local dev but produces a larger Docker image. Recommend devDependencies; confirm before Step 2 executes. (Raised by: Tobias Wendt, also flagged by Felix Brandt)
## 👤 Tobias Wendt (@tobiwendt) — DevOps & Platform Engineer ### Observations - This task correctly defers Dockerfile and docker-compose.yml to a later task. The scaffold produces a local dev environment only. No infrastructure concerns at this stage. - `@sveltejs/adapter-node` is listed as a runtime dependency in the plan (`npm install @sveltejs/adapter-node`). The adapter is used only during `npm run build` — it should be `--save-dev`. The multi-stage Dockerfile runner stage runs `npm ci --omit=dev`, so a miscategorized adapter will bloat the production image with a build-only package. - The plan installs `better-sqlite3` as a runtime dependency — correct. The native module is needed at runtime by the running Node process. - `sharp` is correctly a runtime dependency — it does image processing at request time inside the running container, not only at build time. ### Recommendations - Move `@sveltejs/adapter-node` to `devDependencies`. In the plan's Step 2, change: `npm install @sveltejs/adapter-node` → `npm install --save-dev @sveltejs/adapter-node`. This keeps the production image lean and matches how every SvelteKit project handles adapters. - Add a `.env.example` file to the scaffold task output. Even though no environment variables are consumed yet, establishing the file at Step 1 sets the convention and ensures it is committed before anyone creates a `.env`. Minimal initial content: ``` # .env.example — copy to .env and fill in real values (never commit .env) DATABASE_PATH=./local.db UPLOAD_DIR=./uploads SESSION_SECRET= ADMIN_MARCEL_PASSWORD_HASH= ADMIN_RENATE_PASSWORD_HASH= ADMIN_BERIT_PASSWORD_HASH= ``` - The plan's commit step (`git add -A`) will stage everything in the scaffolded directory. Verify `npx sv create` generates a `.gitignore` that excludes `.env`, `.svelte-kit/`, and `node_modules/` before running the commit step. - Pin the Node version in the Dockerfile (Task 8) to `node:22-alpine` — consistent with the LTS version used during development. Note it here so the scaffold's `package.json` `engines` field can be set to `"node": ">=22"` from the start. ### Open Decisions _(omit if none)_ - **`@sveltejs/adapter-node` dependency category** — Runtime (`dependencies`) vs build-time (`devDependencies`). Installing as runtime is technically harmless for local dev but produces a larger Docker image. Recommend `devDependencies`; confirm before Step 2 executes. _(Raised by: Tobias Wendt, also flagged by Felix Brandt)_
Author
Owner

👤 Elicit — Requirements Engineer

Observations

  • The user story ("As a developer, I need a working SvelteKit project scaffold...") is correctly scoped and clearly names the beneficiary (the developer, not the end user). This is an infrastructure story, not a user-facing feature — correct framing.
  • All 7 acceptance criteria are binary and independently verifiable. The story passes the INVEST Testable criterion.
  • The issue references the plan file (docs/superpowers/plans/2026-05-05-erbstuecke-wannsee.md) and the spec section (system-design §1) — good traceability.
  • The "Files to create" list is complete and matches the plan's file map for Task 1. No ambiguity about scope.
  • Size is labeled "S" — consistent with a scaffold task that produces configuration files and no business logic.

Recommendations

  • The acceptance criterion npm run dev starts without TypeScript errors is partially testable but imprecise. TypeScript errors in .svelte files require svelte-check to surface, not just the Vite dev server. Rewrite as: "npm run dev starts and npm run check exits 0 with no errors." This is fully testable and unambiguous.
  • The criterion src/lib/types.ts exports KATEGORIEN (8 fixed categories) and shared row types does not specify which row types are required. The plan lists ArtikelRow, ArtikelFotoRow, ReservierungRow — add CodeRow to the list (it is needed by hooks in Task 3 but absent from the issue's acceptance criteria).
  • The issue has no labels. For backlog health, add at minimum: type label (feature or chore) and area label (infrastructure). The milestone (v1.0 — MVP) is correctly set.
  • The acceptance criterion for the adapter does not specify that adapter-node is active in svelte.config.js — it just says "is configured." Add a testable condition: "npm run build produces a build/ directory containing index.js" — this proves the adapter is wired correctly, not just imported.

Open Decisions (omit if none)

  • Missing CodeRow type in acceptance criteria — The issue lists shared row types as an acceptance criterion but does not enumerate them. If CodeRow is omitted from types.ts in Task 1, the hooks implementation in Task 3 will introduce an as any cast or inline the type. Decide whether CodeRow belongs in this task's scope or is deferred to Task 3. (Raised by: Elicit)
## 👤 Elicit — Requirements Engineer ### Observations - The user story ("As a developer, I need a working SvelteKit project scaffold...") is correctly scoped and clearly names the beneficiary (the developer, not the end user). This is an infrastructure story, not a user-facing feature — correct framing. - All 7 acceptance criteria are binary and independently verifiable. The story passes the INVEST Testable criterion. - The issue references the plan file (`docs/superpowers/plans/2026-05-05-erbstuecke-wannsee.md`) and the spec section (`system-design §1`) — good traceability. - The "Files to create" list is complete and matches the plan's file map for Task 1. No ambiguity about scope. - Size is labeled "S" — consistent with a scaffold task that produces configuration files and no business logic. ### Recommendations - The acceptance criterion `npm run dev starts without TypeScript errors` is partially testable but imprecise. TypeScript errors in `.svelte` files require `svelte-check` to surface, not just the Vite dev server. Rewrite as: _"`npm run dev` starts and `npm run check` exits 0 with no errors."_ This is fully testable and unambiguous. - The criterion `src/lib/types.ts exports KATEGORIEN (8 fixed categories) and shared row types` does not specify which row types are required. The plan lists `ArtikelRow`, `ArtikelFotoRow`, `ReservierungRow` — add `CodeRow` to the list (it is needed by hooks in Task 3 but absent from the issue's acceptance criteria). - The issue has no labels. For backlog health, add at minimum: type label (`feature` or `chore`) and area label (`infrastructure`). The milestone (`v1.0 — MVP`) is correctly set. - The acceptance criterion for the adapter does not specify that `adapter-node` is active in `svelte.config.js` — it just says "is configured." Add a testable condition: _"`npm run build` produces a `build/` directory containing `index.js`"_ — this proves the adapter is wired correctly, not just imported. ### Open Decisions _(omit if none)_ - **Missing `CodeRow` type in acceptance criteria** — The issue lists shared row types as an acceptance criterion but does not enumerate them. If `CodeRow` is omitted from `types.ts` in Task 1, the hooks implementation in Task 3 will introduce an `as any` cast or inline the type. Decide whether `CodeRow` belongs in this task's scope or is deferred to Task 3. _(Raised by: Elicit)_
Author
Owner

🗳️ Decision Queue — Action Required

5 decisions need your input before implementation starts.

Design Token

  • canvas color authoritative value — The issue and implementation plan use #EAE5DC, while ui_expert.md and the design system persona use #F2EDE4. Both are warm off-whites but visually distinguishable. One source must be declared authoritative; all others updated to match before any CSS is written. (Raised by: Leonie Voss, Markus Keller)

Dependency Classification

  • @sveltejs/adapter-node should be devDependency — The plan installs it as a runtime dependency. It is only consumed during npm run build, not at runtime. The Docker multi-stage runner stage uses npm ci --omit=dev, so this only matters if the runner stage is used — but correct classification is cheap now and prevents a bloated production image. Move to --save-dev. (Raised by: Tobias Wendt, Felix Brandt, Markus Keller)

Acceptance Criteria Gaps

  • npm run check vs npm run dev as the TypeScript verification step — The current criterion says "npm run dev starts without TypeScript errors." The dev server does not surface all TypeScript errors in .svelte files. Replace with npm run check (runs svelte-check) as the explicit verification command. (Raised by: Sara Holt, Felix Brandt, Elicit)

  • CodeRow type scope — Task 1 or Task 3?CodeRow ({ id, code, display_name }) is needed by hooks.server.ts (Task 3). If it is not defined in types.ts during this task, the hooks implementation will either inline the type or use as any. Decide: add CodeRow to this task's acceptance criteria, or explicitly defer it to Task 3 with a note. (Raised by: Felix Brandt, Elicit)

Infrastructure Convention

  • .env.example and .gitignore as explicit acceptance criteria — The scaffold commits with git add -A. If .gitignore does not exclude .env (or if .env.example is not created first), a developer who creates .env locally before the first commit risks committing secrets. The sv create template likely handles this, but it should be a named acceptance criterion, not an assumption. (Raised by: Tobias Wendt, Nora Steiner)
## 🗳️ Decision Queue — Action Required _5 decisions need your input before implementation starts._ ### Design Token - **`canvas` color authoritative value** — The issue and implementation plan use `#EAE5DC`, while `ui_expert.md` and the design system persona use `#F2EDE4`. Both are warm off-whites but visually distinguishable. One source must be declared authoritative; all others updated to match before any CSS is written. _(Raised by: Leonie Voss, Markus Keller)_ ### Dependency Classification - **`@sveltejs/adapter-node` should be `devDependency`** — The plan installs it as a runtime dependency. It is only consumed during `npm run build`, not at runtime. The Docker multi-stage runner stage uses `npm ci --omit=dev`, so this only matters if the runner stage is used — but correct classification is cheap now and prevents a bloated production image. Move to `--save-dev`. _(Raised by: Tobias Wendt, Felix Brandt, Markus Keller)_ ### Acceptance Criteria Gaps - **`npm run check` vs `npm run dev` as the TypeScript verification step** — The current criterion says "npm run dev starts without TypeScript errors." The dev server does not surface all TypeScript errors in `.svelte` files. Replace with `npm run check` (runs `svelte-check`) as the explicit verification command. _(Raised by: Sara Holt, Felix Brandt, Elicit)_ - **`CodeRow` type scope — Task 1 or Task 3?** — `CodeRow` (`{ id, code, display_name }`) is needed by `hooks.server.ts` (Task 3). If it is not defined in `types.ts` during this task, the hooks implementation will either inline the type or use `as any`. Decide: add `CodeRow` to this task's acceptance criteria, or explicitly defer it to Task 3 with a note. _(Raised by: Felix Brandt, Elicit)_ ### Infrastructure Convention - **`.env.example` and `.gitignore` as explicit acceptance criteria** — The scaffold commits with `git add -A`. If `.gitignore` does not exclude `.env` (or if `.env.example` is not created first), a developer who creates `.env` locally before the first commit risks committing secrets. The `sv create` template likely handles this, but it should be a named acceptance criterion, not an assumption. _(Raised by: Tobias Wendt, Nora Steiner)_
Author
Owner

Design Token: We will use the token from the spec
Dependency Classification: move to --save-dev
CodeRow will be done here

Design Token: We will use the token from the spec Dependency Classification: move to --save-dev CodeRow will be done here
Sign in to join this conversation.