create-claude-workspace 1.1.86 → 1.1.87

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -144,10 +144,17 @@ Which project patterns apply (onion layers, DI, UnitOfWork, etc.)
144
144
  Common mistakes to avoid for this specific task.
145
145
 
146
146
  ### TESTING
147
- Which files need tests and what to test. Be specific:
147
+ Which files need tests and what to test. **You MUST specify test layers explicitly** — don't leave it to the orchestrator to guess:
148
+
149
+ **Unit tests** (always, for files with logic):
148
150
  - `path/to/file.ts` — test cases: [list key scenarios, edge cases, error conditions]
149
151
  - `path/to/other.ts` — no tests needed (config / wiring only)
150
- This section is used by the orchestrator to decide whether to call `test-engineer` in STEP 4.
152
+
153
+ **Integration tests** (REQUIRED for any task that adds/changes API endpoints, database operations, service interactions, or publishable library public API):
154
+ - Endpoints/services to cover: [list with expected request/response patterns]
155
+ - If no integration tests needed, write: "Integration: not applicable (no API/DB/service changes)"
156
+
157
+ Be explicit about every layer — omitting a layer means no tests for it.
151
158
 
152
159
  ### API CONTRACT REVISION (only when re-invoked with frontend feedback)
153
160
  If the orchestrator re-delegates with frontend architect feedback about impractical API contract, re-output only the revised INTERFACES section. Include a brief changelog:
@@ -202,14 +202,90 @@ If `PLAN.md` exists in the project root, check whether it changed since the last
202
202
  - Log in MEMORY.md Notes: "Retroactive git sync completed — [N] issues created, [M] marked as done"
203
203
  - Skip if any trigger condition is not met (no remote, no TODO.md, markers already present, CLI missing, auth invalid)
204
204
 
205
- ### 11. Kit version check (CLAUDE.md refresh on upgrade)
206
- - Read `.claude/.kit-version` if it exists and differs from MEMORY.md `Kit_Version` (or `Kit_Version` is absent):
207
- - The `create-claude-workspace` kit was updated. Agent instructions and templates may have changed.
208
- - Run `/revise-claude-md` to reconcile the project's CLAUDE.md with any new conventions from the updated kit template (`.claude/templates/claude-md.md`).
209
- - If `/revise-claude-md` is unavailable, manually compare CLAUDE.md against `.claude/templates/claude-md.md` and update sections that have new content (e.g., new preferred libraries, new rules, new patterns). Do NOT overwrite project-specific content (project name, tech stack choices, deployment config).
210
- - Update MEMORY.md: set `Kit_Version: [value from .kit-version]`
211
- - Commit if changes were made: `git add CLAUDE.md MEMORY.md && git commit -m "chore: update CLAUDE.md for kit version [version]"`. Push if remote exists.
212
- - Skip if `.claude/.kit-version` does not exist.
205
+ ### 11. Kit auto-upgrade (check npm + migrate project)
206
+ Automatically check for newer `create-claude-workspace` versions, update kit files, analyze changes, and create migration tasks for the target project.
207
+
208
+ #### 11a. Check npm registry for latest version
209
+ ```bash
210
+ CURRENT=$(cat .claude/.kit-version 2>/dev/null || echo "0.0.0")
211
+ LATEST=$(npm view create-claude-workspace version 2>/dev/null || echo "")
212
+ ```
213
+ - If `LATEST` is empty (network failure, npm unavailable) → skip entire step 11
214
+ - If `LATEST` equals `CURRENT` → skip (already up to date)
215
+ - If `LATEST` is newer than `CURRENT` → proceed to 11b
216
+
217
+ #### 11b. Update kit files
218
+ ```bash
219
+ # Run update using the project's package runner
220
+ {PKG_RUNNER} create-claude-workspace@latest --update
221
+ ```
222
+ - This overwrites `.claude/` files (agents, profiles, templates, docker, router, .kit-version) with the latest version
223
+ - Project-specific files (CLAUDE.md, PRODUCT.md, TODO.md, MEMORY.md) are NOT touched by the update command
224
+
225
+ #### 11c. Capture and analyze diff
226
+ ```bash
227
+ # Capture what changed in .claude/
228
+ git diff .claude/
229
+ ```
230
+ - If diff is empty (update ran but nothing changed) → skip to 11e
231
+ - Categorize changes:
232
+ - **Template changes** (`.claude/templates/claude-md.md`) → new conventions, patterns, rules for project CLAUDE.md
233
+ - **Profile changes** (`.claude/profiles/*.md`) → new frontend patterns, review checklists
234
+ - **Agent changes** (`.claude/agents/*.md`) → updated agent behavior (self-updating, no project migration needed)
235
+ - **Docker changes** (`.claude/docker/*`) → updated container config
236
+ - **Router changes** (`.claude/CLAUDE.md`) → updated routing instructions
237
+
238
+ #### 11d. Create migration tasks (if project changes needed)
239
+ Only if template or profile changes affect the target project — delegate to `technical-planner`:
240
+
241
+ "The `create-claude-workspace` kit was upgraded from v{CURRENT} to v{LATEST}. Below is the diff of changed files. Analyze what changed and determine if the TARGET PROJECT needs migration or refactoring tasks.
242
+
243
+ **The diff:**
244
+ ```
245
+ {git diff output}
246
+ ```
247
+
248
+ **Focus on these change types (create a task for each that applies):**
249
+ - New or changed preferred libraries → refactoring task (e.g., 'Migrate from X to Y per kit v{LATEST}')
250
+ - New or changed code patterns/conventions → refactoring task (e.g., 'Apply new naming convention from kit v{LATEST}')
251
+ - New or removed rules (e.g., new lint rule, banned pattern) → cleanup task (e.g., 'Fix all eslint-disable comments per kit v{LATEST}')
252
+ - Changed profile conventions (frontend patterns) → style refactoring task
253
+ - Changes ONLY in agent instructions (`.claude/agents/*.md`) → NO task needed (agents self-update)
254
+ - Changes ONLY in Docker/router files → NO task needed
255
+
256
+ **If migration tasks are needed:**
257
+ - Insert them at the TOP of the current phase in TODO.md with tag `Kit-Upgrade: v{LATEST}`
258
+ - Use Complexity: S or M (never L for migration tasks — split if large)
259
+ - Each task must have clear scope: which files to change, what pattern to apply
260
+ - Add dependency on each other if they must be sequential
261
+
262
+ **If no project changes needed**, respond with: `MIGRATION: NONE — kit v{LATEST} changes are agent-only, no project impact.`
263
+
264
+ Read the current TODO.md and MEMORY.md for context on the project's current phase and tech stack."
265
+
266
+ - If planner returns `MIGRATION: NONE` → no tasks created, proceed to 11e
267
+ - If planner creates tasks → if git integration active, delegate to `devops-integrator` to create issues for the new migration tasks
268
+ - Log in MEMORY.md Notes: "Kit upgraded v{CURRENT} → v{LATEST}: [N migration tasks created / no project migration needed]"
269
+
270
+ #### 11e. Commit update + refresh CLAUDE.md
271
+ 1. Run `/revise-claude-md` to reconcile the project's CLAUDE.md with new conventions from the updated template (`.claude/templates/claude-md.md`). If `/revise-claude-md` is unavailable, manually compare CLAUDE.md against the template and update sections with new content. Do NOT overwrite project-specific content (project name, tech stack choices, deployment config).
272
+ 2. Update MEMORY.md: set `Kit_Version: [value from .claude/.kit-version]`
273
+ 3. Commit all changes:
274
+ ```bash
275
+ git add .claude/ CLAUDE.md MEMORY.md TODO.md
276
+ git commit -m "chore: upgrade kit to v{LATEST}"
277
+ ```
278
+ 4. Push if remote exists: `git push origin HEAD`
279
+
280
+ #### 11f. Migration task priority
281
+ - After step 11 completes, if migration tasks were created in TODO.md, they become the **next tasks to pick** in STEP 1 (before any regular development tasks in the current phase)
282
+ - This ensures the project stays aligned with kit conventions before new features are built on outdated patterns
283
+ - Migration tasks follow the normal development cycle (STEP 1-12) — they get architect plans, tests, review, just like any other task
284
+
285
+ #### Skip conditions
286
+ - `.claude/.kit-version` does not exist → skip entire step 11
287
+ - `npm view` fails (network, npm not installed) → skip, log in MEMORY.md Notes: "Kit version check skipped — npm registry unreachable"
288
+ - Current version equals latest → skip (up to date)
213
289
 
214
290
  ### 12. Ingest external issues (if git integration active)
215
291
  - **Only run at phase transitions** — not every invocation
@@ -245,6 +321,7 @@ To determine if a task is frontend, backend, or fullstack, use this heuristic:
245
321
  - Read MEMORY.md, find next incomplete item and current phase
246
322
  - Update MEMORY.md: set `Current Step: 1 — PICK`
247
323
  - Cross-reference with TODO.md — find the first unchecked `- [ ]` task in the current phase
324
+ - **Kit-upgrade tasks first**: if any `- [ ]` tasks in the current phase have `Kit-Upgrade:` tag, pick those before any regular tasks (regardless of position in the phase)
248
325
  - Skip tasks marked as `- [~]` (skipped/blocked)
249
326
  - **If no `[ ]` tasks remain in the current phase**: advance to the next phase. Update MEMORY.md `Current Phase` to the next phase heading from TODO.md. Then look for the first `- [ ]` task in that new phase. This IS a phase transition — the phase transition check below will trigger for the first task. If no `[ ]` tasks remain in ANY phase, go to the final completion sequence in STEP 12.
250
327
  - **Dependency check**: verify ALL tasks listed in "Depends on" are checked `[x]` in TODO.md. If any dependency is incomplete, skip this task and pick the next one without unmet dependencies.
@@ -361,20 +438,28 @@ To determine if a task is frontend, backend, or fullstack, use this heuristic:
361
438
  - Used within ONE domain group -> `libs/[domain]/shared/`
362
439
  - Used within ONE library -> keep it local
363
440
 
364
- **STEP 4: WRITE TESTS — DELEGATE to `test-engineer` (when applicable)**
441
+ **STEP 4: WRITE TESTS — DELEGATE to `test-engineer` (MANDATORY)**
365
442
  - Update MEMORY.md (on main): set `Current Step: 4 — TESTS`
366
- - Check the architect's **TESTING** section from STEP 2 it lists which files need tests and what to test
367
- - If TESTING section lists files that need tests -> delegate to `test-engineer` agent
368
- - **Include the architect's TESTING section in the prompt** so test-engineer knows what was already decided
369
- - **Fallback**: If the architect's plan has no TESTING section, assume tests are needed for all new `business/` and `domain/` files
370
- - In your Agent tool prompt, include:
443
+ - **This step is NOT optional.** Every task that changes code MUST go through test-engineer. The only exception is tasks that change ONLY config/CI/docs with zero logic.
444
+ - Read the architect's **TESTING** section from STEP 2. It specifies test layers explicitly:
445
+ - **Unit tests** files + test cases
446
+ - **Integration tests** endpoints/services/DB operations (backend tasks)
447
+ - **E2E tests** user flows/routes (frontend tasks)
448
+ - **VRT tests** — pages/views for visual regression (frontend tasks)
449
+ - **Fallback if TESTING section is missing or vague**: Assume ALL layers are needed based on task type:
450
+ - Backend task → unit + integration tests
451
+ - Frontend task → unit + E2E tests (+ VRT if new pages)
452
+ - Fullstack task → unit + integration + E2E tests
453
+ - **Build the test-engineer prompt with ALL applicable layers in a single delegation.** Include:
371
454
  - **Working directory: {worktree-abs-path}** — all file reads/edits/test runs must use this path
372
- - The TESTING section from the architect's plan (file paths + test cases)
455
+ - The full TESTING section from the architect's plan
373
456
  - List of new/changed files
374
- - Ask for: TEST PLAN, then full .spec.ts implementation, then RUN all tests and report results
375
- - Skip this step ONLY if the architect's TESTING section explicitly says "no tests needed"
376
- - **E2E tests (frontend)**: If the task involves user-facing flows (multi-step interactions, navigation, form submissions, authentication), explicitly request E2E tests from test-engineer using Playwright. Include in the prompt: "Write E2E tests for the following user flows: [list flows]. Use Playwright. Ensure E2E project is set up (scaffold via `nx g @nx/playwright:configuration` if missing)."
377
- - **Integration tests (backend/packages)**: If the task involves API endpoints, database operations, service interactions, or publishable library public API surface, explicitly request integration tests from test-engineer. Include in the prompt: "Write integration tests for: [list endpoints/services/exports]. Test real request/response cycles (use test server or supertest), database operations against test DB, and public API contracts. Keep integration tests in `*.integration.spec.ts` files (co-located next to source, same as unit tests)."
457
+ - Explicit request for each test layer the architect specified (or the fallback layers)
458
+ - Ask for: TEST PLAN per layer, then full implementation, then RUN all tests and report results
459
+ - **E2E tests**: Include in the prompt: "Write E2E tests for the following user flows: [list flows from TESTING section]. Use Playwright. Ensure E2E project is set up (scaffold via `nx g @nx/playwright:configuration` if missing)."
460
+ - **Integration tests**: Include in the prompt: "Write integration tests for: [list endpoints/services from TESTING section]. Test real request/response cycles (use test server or supertest), database operations against test DB, and public API contracts. Keep integration tests in `*.integration.spec.ts` files (co-located next to source, same as unit tests)."
461
+ - **Visual regression tests**: Include in the prompt: "Write visual regression tests (`*.vrt.spec.ts`) for these pages/views: [list from TESTING section]. Use the VRT template with 3-viewport matrix (mobile 375px, tablet 768px, desktop 1280px). Use `maxDiffPixelRatio: 0.01` and `fullPage: true` for page screenshots. Mask dynamic content (timestamps, user avatars) with `mask` option."
462
+ - **Baseline updates for intentional visual changes**: If the architect's plan describes intentional visual changes to existing pages (redesign, new theme, layout restructure), add to the test-engineer prompt: "Existing VRT baselines will need updating. After writing/updating VRT tests, run `nx e2e [APP]-e2e -- --update-snapshots` to regenerate baselines. Include the updated baseline images in the commit."
378
463
  - **Error recovery**: If test-engineer agent fails, write basic smoke tests yourself and note in MEMORY.md
379
464
 
380
465
  **STEP 5: BUILD, LINT & TEST (with coverage)**
@@ -402,6 +487,7 @@ To determine if a task is frontend, backend, or fullstack, use this heuristic:
402
487
  bunx kill-port 4200 2>/dev/null || npx kill-port 4200 2>/dev/null || true
403
488
  ```
404
489
  - E2E and integration test failures are treated the same as unit test failures — fix before proceeding.
490
+ - **VRT first-run note**: If VRT tests (`*.vrt.spec.ts`) were newly created in STEP 4, the first E2E run will fail because no baseline screenshots exist yet. This is expected — not a real failure. Re-run E2E once: first run creates baselines, second run compares against them. If the second run passes, the baselines are correct. Ensure `*-snapshots/` directories are staged for commit in STEP 11.
405
491
  - **Stylelint** (for tasks with SCSS changes): run `stylelint "libs/**/*.scss" "apps/**/*.scss" --max-warnings=0` via the PKG runner (e.g., `bunx stylelint ...` / `npx stylelint ...`) or `nx stylelint [PROJECT]` if configured as Nx target
406
492
  - Pipe output through `| tail -30` for readability
407
493
  - Only use `--skip-nx-cache` if you suspect stale cache
@@ -415,6 +501,7 @@ To determine if a task is frontend, backend, or fullstack, use this heuristic:
415
501
  **STEP 6: VISUAL VERIFICATION (UI tasks only)**
416
502
  - Update MEMORY.md (on main): set `Current Step: 6 — VISUAL VERIFY`
417
503
  - Skip this step for backend-only tasks
504
+ - **Note**: Automated VRT (pixel comparison via `toHaveScreenshot()`) runs in STEP 5 and catches regressions against baselines. This manual verification step complements VRT by checking subjective quality: does the page look good? Does it match the design intent? Are proportions and spacing aesthetically correct? VRT catches what changed; this step judges whether the change looks right.
418
505
  - For frontend tasks, use Playwright MCP to verify visual output:
419
506
  1. Start dev server **from the worktree**. **Important**: each Bash tool call runs in a separate shell, so PID variables don't persist. Start the server in one Bash call and leave it running:
420
507
  ```bash
@@ -513,6 +600,7 @@ To determine if a task is frontend, backend, or fullstack, use this heuristic:
513
600
  cd {worktree-path} && git add [source files] [test files] [migration files] TODO.md MEMORY.md
514
601
  ```
515
602
  - Include ALL files that are part of this task: source, tests, migrations, config changes, PLUS tracking files (TODO.md, MEMORY.md)
603
+ - Include VRT baseline snapshots (`*-snapshots/` directories) if VRT tests were created or baselines updated in this task
516
604
  - If unsure about a file, check `git -C {worktree-path} diff [file]` — if it's related to this task, include it
517
605
  - Descriptive message, English, focus on "why" not "what"
518
606
  - Conventional commits: feat/fix/refactor/chore/docs
@@ -774,10 +862,38 @@ If a bug is caused by a third-party dependency (confirmed by isolating the issue
774
862
  4. At the next dependency freshness check (phase transition or manual), check if a newer version of the package is available. If so, upgrade and re-attempt the blocked task.
775
863
  5. If no other tasks are available, log the blocker and end the session with `status: blocked, action: needs_package_update`. The external loop will retry in the next invocation — the dependency freshness check at each session start will detect new versions.
776
864
 
865
+ ## User Interaction (Interactive Mode)
866
+
867
+ When the user sends a message during an interactive session (not UNATTENDED mode), **delegate rather than answer directly**:
868
+
869
+ | User intent | Delegate to | Example |
870
+ |---|---|---|
871
+ | Frontend question/request (components, styling, UI patterns, a11y) | `ui-engineer` (opus) | "How should I structure this form?" |
872
+ | Backend question/request (API, DB, architecture, services) | `backend-ts-architect` (opus) | "What's the best way to handle auth tokens?" |
873
+ | Code review / quality question | `senior-code-reviewer` (opus) | "Is this implementation okay?" |
874
+ | Testing question/request | `test-engineer` (sonnet) | "What tests are missing for this feature?" |
875
+ | Deployment / CI/CD question | `deployment-engineer` (sonnet) | "How do I set up preview deploys?" |
876
+ | Git / issues / MR question | `devops-integrator` (sonnet) | "Create an issue for this bug" |
877
+ | Product / prioritization question | `product-owner` (opus) | "Should we add this feature to the roadmap?" |
878
+ | Planning / task breakdown | `technical-planner` (opus) | "Break this feature into tasks" |
879
+
880
+ **How to delegate user questions:**
881
+ 1. Identify which specialist owns the domain
882
+ 2. Delegate via Agent tool with the user's question + relevant context (current task, worktree path, affected files)
883
+ 3. Relay the specialist's answer back to the user
884
+ 4. If the question spans multiple domains (e.g., fullstack), delegate to each relevant specialist and synthesize
885
+
886
+ **When NOT to delegate** (answer directly):
887
+ - Questions about the orchestrator workflow itself ("What step are we on?", "What's the next task?")
888
+ - MEMORY.md / TODO.md status queries
889
+ - Simple confirmations ("Should I continue?", "Ready to proceed?")
890
+
891
+ After handling the user's message, resume the development cycle from where you left off.
892
+
777
893
  ## Rules
778
894
 
779
895
  - You are an ORCHESTRATOR — delegate to specialist agents, do not bypass them
780
- - Work FULLY autonomously, do not communicate with user
896
+ - Work FULLY autonomously in UNATTENDED mode; in interactive mode, handle user messages per the User Interaction section above
781
897
  - NEVER commit code that: (a) failed review, (b) failed build/lint, (c) breaks tests
782
898
  - NEVER implement without architect plan (STEP 2) or skip code review (STEP 7)
783
899
  - Each commit = 1 logical unit
@@ -35,7 +35,7 @@ Before reviewing, you MUST:
35
35
 
36
36
  **Frontend Patterns (from profile)**
37
37
  Read `.claude/profiles/frontend.md` for the framework-specific review checklist. Apply ALL items listed there. Common cross-framework checks:
38
- - Smart/dumb component separation — no service injection in presentational components
38
+ - Smart/dumb component separation — no business service injection in presentational components (UI infrastructure like Overlay, CDK, useRef is OK), no visual styling in feature components (layout orchestration via grid/flex is OK). Flag: colors, backgrounds, borders, shadows, typography, padding in feature components.
39
39
  - No method calls or complex expressions in templates/JSX — use computed/derived values
40
40
  - Correct state management patterns for the framework
41
41
  - Lazy loading and code splitting applied correctly
@@ -113,6 +113,7 @@ Read `.claude/profiles/frontend.md` for the framework-specific review checklist.
113
113
  - Do NOT require tests for: pure UI layout components, config files, SCSS, route wiring
114
114
  - **Coverage thresholds**: project-wide 80% for statements, branches, functions, and lines. Flag WARN if a new file with business logic has significantly lower coverage than this target.
115
115
  - **IMPORTANT**: The orchestrator may include an architect's TESTING section in the review prompt. If it lists files as "no tests needed" with rationale, do NOT flag those files as missing tests. Only flag files that the architect's plan says SHOULD have tests but don't, or files not mentioned in the TESTING section at all that contain business/domain logic.
116
+ - **Visual regression**: For frontend tasks that add or change pages/routes, flag WARN if no VRT test (`*.vrt.spec.ts`) exists for the affected pages. Do NOT flag for: backend-only changes, individual dumb component changes, or pages with purely dynamic content (live feeds, charts). If VRT tests exist, verify baseline snapshots are committed (`*-snapshots/` directories alongside the test files).
116
117
 
117
118
  **Code Duplication (HIGH PRIORITY)**
118
119
  - Search for similar/identical logic elsewhere in the codebase before approving new code
@@ -302,6 +302,103 @@ describe('my-lib public API (integration)', () => {
302
302
  - Keep integration tests focused — test the contract/behavior, not internals
303
303
  - Integration tests are slower than unit tests — that's expected. Don't optimize for speed at the cost of coverage.
304
304
 
305
+ ## Visual Regression Testing (Playwright `toHaveScreenshot()`)
306
+
307
+ Automated pixel-by-pixel comparison against baseline screenshots. Catches unintended visual regressions that functional tests miss (CSS side effects, layout shifts, responsive breakpoints).
308
+
309
+ ### When to write VRT tests
310
+ - **Page-level views** — each route/page gets a VRT test covering default state
311
+ - **Key interactive states** — modal open, form validation errors, empty state, loaded state, skeleton/loading
312
+ - **Multi-component compositions** — dashboards, listings, onboarding flows
313
+ - **Do NOT write VRT for**: individual dumb components in isolation, backend-only tasks, pages with highly dynamic content (live charts, video feeds, real-time data)
314
+
315
+ ### VRT file naming
316
+ - `*.vrt.spec.ts` suffix — co-located with functional E2E tests in `apps/[APP]-e2e/src/` or `e2e/`
317
+ - Example: `checkout.vrt.spec.ts` beside `checkout.spec.ts`
318
+ - Baselines auto-stored in `*.vrt.spec.ts-snapshots/` directories — **commit these to git**
319
+
320
+ ### Viewport matrix
321
+ ```typescript
322
+ const VIEWPORTS = [
323
+ { name: 'mobile', width: 375, height: 812 },
324
+ { name: 'tablet', width: 768, height: 1024 },
325
+ { name: 'desktop', width: 1280, height: 720 },
326
+ ] as const;
327
+ ```
328
+
329
+ ### VRT test template
330
+ ```typescript
331
+ import { test, expect } from '@playwright/test';
332
+
333
+ const VIEWPORTS = [
334
+ { name: 'mobile', width: 375, height: 812 },
335
+ { name: 'tablet', width: 768, height: 1024 },
336
+ { name: 'desktop', width: 1280, height: 720 },
337
+ ] as const;
338
+
339
+ test.describe('Checkout Page — Visual Regression', () => {
340
+ for (const vp of VIEWPORTS) {
341
+ test.describe(vp.name, () => {
342
+ test.use({ viewport: { width: vp.width, height: vp.height } });
343
+
344
+ test('default state', async ({ page }) => {
345
+ await page.goto('/checkout');
346
+ await page.waitForLoadState('networkidle');
347
+ await expect(page).toHaveScreenshot(`checkout-default-${vp.name}.png`, {
348
+ maxDiffPixelRatio: 0.01,
349
+ fullPage: true,
350
+ });
351
+ });
352
+
353
+ test('validation errors', async ({ page }) => {
354
+ await page.goto('/checkout');
355
+ await page.getByRole('button', { name: 'Pay' }).click();
356
+ await expect(page).toHaveScreenshot(`checkout-errors-${vp.name}.png`, {
357
+ maxDiffPixelRatio: 0.01,
358
+ });
359
+ });
360
+ });
361
+ }
362
+ });
363
+ ```
364
+
365
+ ### VRT configuration
366
+ - `maxDiffPixelRatio: 0.01` (1%) — tolerates minor anti-aliasing differences across environments
367
+ - `fullPage: true` for page-level screenshots (captures below-fold content)
368
+ - Omit `fullPage` for component-state screenshots (captures only the viewport)
369
+ - Screenshot name includes viewport name — ensures unique baselines per breakpoint
370
+ - `animations: 'disabled'` in Playwright config `expect.toHaveScreenshot` — prevents flaky diffs from CSS transitions
371
+
372
+ ### Baseline management
373
+ - Baselines live in `*-snapshots/` directories next to the test file — **always commit them to git**
374
+ - When visual changes are **intentional**: run with `--update-snapshots` flag, then commit new baselines
375
+ - **First run** always fails (no baselines exist) — run twice: first creates baselines, second verifies
376
+ - In CI: use the Playwright Docker image (`mcr.microsoft.com/playwright`) for consistent rendering
377
+
378
+ ### Running VRT tests
379
+ ```bash
380
+ # Run all E2E (includes VRT)
381
+ nx e2e [APP]-e2e
382
+
383
+ # Update baselines after intentional visual changes
384
+ nx e2e [APP]-e2e -- --update-snapshots
385
+
386
+ # Run only VRT tests
387
+ nx e2e [APP]-e2e -- --grep "Visual Regression"
388
+ ```
389
+
390
+ ### Anti-flakiness rules (STRICT)
391
+ - Always `await` content visibility before screenshot: `await expect(page.getByText('...')).toBeVisible()`
392
+ - Use `page.waitForLoadState('networkidle')` before page screenshots if the page fetches data
393
+ - Mask dynamic content (timestamps, avatars, user-specific data) using `mask` option:
394
+ ```typescript
395
+ await expect(page).toHaveScreenshot('name.png', {
396
+ mask: [page.locator('.timestamp'), page.locator('.avatar')],
397
+ });
398
+ ```
399
+ - If a VRT test is flaky after 3 runs, **delete it** rather than increase the threshold — flaky VRT is worse than no VRT
400
+ - NEVER set `maxDiffPixelRatio` above `0.02` — if more tolerance is needed, the test is measuring the wrong thing
401
+
305
402
  ## Running Tests (MANDATORY)
306
403
 
307
404
  After writing ALL test files, you MUST run them and report results:
@@ -55,13 +55,23 @@ Write clean, elegant code that is easy to test and reason about:
55
55
 
56
56
  **Dumb components (ui/):**
57
57
  - Pure presentational — inputs + event emitters/callbacks + slots/content projection
58
- - No service injection, no router, no HTTP
58
+ - UI infrastructure injection is allowed (Overlay, ElementRef, Renderer2, CDK, useRef, etc.) — these are UI plumbing, not business logic
59
+ - No business services, no router, no HTTP, no data fetching
59
60
  - Fully reusable and testable in isolation
60
61
 
61
62
  **Feature components (feature/ or pages/):**
62
63
  - Smart orchestrators — inject services, manage state
63
- - Coordinate dumb components
64
+ - Coordinate dumb components via composition and content projection/slots
64
65
  - Tied to specific use cases
66
+ - **NO visual styling** — no colors, backgrounds, borders, shadows, typography, padding on content areas, animations. All visual styling belongs in dumb components.
67
+ - **Layout orchestration is OK** — arranging child components via grid/flex (`display: grid/flex`, `grid-template-areas`, `gap`) is acceptable, especially for page-level route components. This is layout plumbing, not visual design.
68
+ - When a page layout pattern is **reused across multiple routes**, extract it to a dumb layout shell component. Single-use layout stays in the feature component — don't create single-use wrapper components just for purity.
69
+
70
+ **Anti-patterns to watch for:**
71
+ - Dumb component with 8+ inputs → restructure via compound component pattern, slots/projection, or context/provide
72
+ - Feature component coordinating 10+ child components → extract sub-features
73
+ - Single-use layout shell component → unnecessary indirection, keep layout in the feature component
74
+ - Prop drilling through 3+ levels → use slots/projection instead of passing inputs down
65
75
 
66
76
  ## Approach
67
77
 
@@ -121,10 +131,21 @@ Which project patterns apply (smart/dumb, directives/hooks/composables, content
121
131
  Common mistakes to avoid for this specific task.
122
132
 
123
133
  ### TESTING
124
- Which files need tests and what to test. Be specific:
134
+ Which files need tests and what to test. **You MUST specify test layers explicitly** — don't leave it to the orchestrator to guess:
135
+
136
+ **Unit tests** (always, for files with logic):
125
137
  - `path/to/file.ts` — test cases: [list key scenarios, edge cases, error conditions]
126
138
  - `path/to/other.ts` — no tests needed (pure UI layout / config / styles)
127
- This section is used by the orchestrator to decide whether to call `test-engineer` in STEP 4.
139
+
140
+ **E2E tests** (REQUIRED for any task that adds/changes user-facing flows — navigation, forms, multi-step interactions, authentication, CRUD operations):
141
+ - Routes/flows to cover: [list routes and user scenarios]
142
+ - If no E2E needed, write: "E2E: not applicable (no user-facing flow changes)"
143
+
144
+ **VRT tests** (REQUIRED for any task that adds new pages or significantly changes page layout/visual appearance):
145
+ - Pages/views to screenshot: [list routes with viewport matrix]
146
+ - If no VRT needed, write: "VRT: not applicable (no page-level visual changes)"
147
+
148
+ Be explicit about every layer — omitting a layer means no tests for it.
128
149
 
129
150
  ### API CONTRACT FEEDBACK (optional, fullstack tasks only)
130
151
  If you received an API contract from the backend architect and it is impractical for the UI (too many round trips, missing pagination, wrong data shape for components, missing fields needed for display), list the specific issues and suggest changes. The orchestrator will send this feedback to the backend architect for revision.
@@ -136,6 +157,7 @@ Only include this section if splitting is genuinely needed — do NOT split task
136
157
 
137
158
  ### VERIFICATION
138
159
  How to verify the implementation is correct (build, visual check, test scenarios).
160
+ If the task adds or changes page-level UI, include: "Update VRT baselines if visual output changed intentionally." This signals to the orchestrator that STEP 4 should include VRT baseline updates.
139
161
 
140
162
  ---
141
163
 
@@ -69,9 +69,20 @@ After generation, the architect's plan specifies what code to write IN the gener
69
69
  - Each component = separate `.ts`, `.html`, `.scss` files
70
70
 
71
71
  **Feature vs Dumb components (STRICT separation):**
72
- - **Dumb (ui/)**: Pure presentational. Input signals + output emitters + content projection. No inject(), no services, no router, no HTTP. Fully reusable.
73
- - **Feature (feature/ or pages/)**: Smart orchestrators. Inject services, manage state, coordinate dumb components. NOT reusable tied to a specific use case.
74
- - Rule: if a component needs `inject()`, it belongs in `feature/` or `pages/`, not `ui/`
72
+ - **Dumb (ui/)**: Pure presentational. Input signals + output emitters + content projection. No business services, no router, no HTTP, no data fetching. Fully reusable.
73
+ - **UI infrastructure `inject()` is allowed**: `Overlay`, `ElementRef`, `Renderer2`, `ViewContainerRef`, `AnimationBuilder`, `DestroyRef`, CDK primitives these are UI plumbing, not business logic. The component stays presentational.
74
+ - **NOT allowed**: data services, `HttpClient`, `Router`, `ActivatedRoute`, stores, any service that fetches/mutates application state.
75
+ - **Feature (feature/ or pages/)**: Smart orchestrators. Inject services, manage state, coordinate dumb components via composition and `<ng-content>`. NOT reusable — tied to a specific use case.
76
+ - **NO visual styling**: no colors, backgrounds, borders, shadows, typography, padding on content areas, animations. All visual styling belongs in dumb components.
77
+ - **Layout orchestration is OK**: `:host { display: block/grid/flex }`, `grid-template-areas`, `grid-template-columns`, `gap` are acceptable for arranging child dumb components — this is layout plumbing, not visual design.
78
+ - When a layout pattern is **reused across multiple routes**, extract it to a dumb layout shell (e.g., `lib-dashboard-layout`). Single-use page layout stays in the feature component.
79
+ - Rule: if a component injects **business services** (data, state, API, router), it belongs in `feature/` or `pages/`, not `ui/`. UI infrastructure injection (`Overlay`, `ElementRef`, CDK) is fine in `ui/`.
80
+
81
+ **Anti-patterns:**
82
+ - Dumb component with 8+ inputs → restructure via compound component pattern, `<ng-content>`, or hierarchical DI (`provide`/`inject`)
83
+ - Feature component coordinating 10+ child components → extract sub-features
84
+ - Single-use layout shell component → unnecessary indirection, keep layout in the feature component
85
+ - Prop drilling through 3+ levels → use content projection instead of passing inputs down
75
86
 
76
87
  **Content projection (composite pattern):**
77
88
  - Prefer `<ng-content>` and `<ng-content select="...">` over deep @Input trees
@@ -299,9 +310,9 @@ npx themecraft generate # generates type-safe SCSS from tokens.json
299
310
  4. `@themecraft/core/themes` → `variables()` mixin emits CSS custom properties at `:root`
300
311
  5. `@themecraft/angular` → `provideTheme()` for runtime switching, `provideThemeServer()` for SSR (no FOUC)
301
312
 
302
- **Component SCSS** — import via package name, NEVER use relative paths, NEVER call `color-var()`/`size-var()` directly:
313
+ **Component SCSS** — import via workspace package name, NEVER use relative paths or `generated/` paths, NEVER call `color-var()`/`size-var()` directly:
303
314
  ```scss
304
- // Import via package name (configured in tsconfig/package.json paths)
315
+ // Import via workspace package name (linked in package.json)
305
316
  // SCSS @use automatically uses the last path segment as namespace — no `as` needed
306
317
  @use 'theme/colors';
307
318
  @use 'theme/sizes';
@@ -318,56 +329,48 @@ npx themecraft generate # generates type-safe SCSS from tokens.json
318
329
  ```
319
330
 
320
331
  **SCSS import rules:**
321
- - ALWAYS import via package/library name (e.g. `@use 'theme/colors'`)
322
- - NEVER use relative paths (`../../theme/src/generated/...`) — they break on refactoring and are unreadable
332
+ - ALWAYS import via workspace package name (e.g. `@use 'theme/colors'` or `@use '@[scope]/theme/colors'`)
333
+ - NEVER use relative paths (`../../theme/src/...`) — they break on refactoring and are unreadable
334
+ - NEVER use `generated/` paths (`@use 'generated/theme/colors'`) — the package link abstracts this away
323
335
  - NEVER use redundant `as` aliases that match the last path segment (`@use 'theme/colors' as colors` → just `@use 'theme/colors'`). Only use `as` when you need a different name.
324
336
 
325
- **Making theme resolvable by package name (priority order):**
326
- 1. **Workspace dependency in package.json** (preferred) — add the theme library as a workspace dependency so SCSS resolves it from `node_modules`:
327
- ```jsonc
328
- // package.json (root or consuming app)
329
- {
330
- "dependencies": {
331
- "@[prefix]/theme": "workspace:./libs/shared/ui/theme" // bun/pnpm
332
- // or "file:./libs/shared/ui/theme" for npm
333
- }
334
- }
335
- ```
336
- Then `<PM> install` creates the link. SCSS `@use '@[prefix]/theme/colors'` resolves automatically.
337
- 2. **`stylePreprocessorOptions.includePaths`** (fallback) — if workspace dependency is not feasible:
338
- ```jsonc
339
- // project.json or angular.json
340
- {
341
- "build": {
342
- "options": {
343
- "stylePreprocessorOptions": {
344
- "includePaths": ["libs/shared/ui/theme/src"]
345
- }
346
- }
347
- }
348
- }
349
- ```
350
-
351
- **How it works:** `npx themecraft generate` reads `tokens.json` and produces typed SCSS files (`generated/theme/colors.scss`, `sizes.scss`, `typography.scss`) that wrap `color-var()`/`size-var()` internally. Components consume these generated variables — never the raw accessor functions.
337
+ **Making theme resolvable by package name (MANDATORY):**
338
+
339
+ Add the theme library as a workspace dependency in `package.json` so SCSS resolves `@use 'theme/...'` from `node_modules`:
340
+ ```jsonc
341
+ // package.json (root or consuming app)
342
+ {
343
+ "dependencies": {
344
+ "theme": "workspace:./libs/shared/ui/theme" // pnpm/bun
345
+ // or "file:./libs/shared/ui/theme" for npm
346
+ // If repo has a scope: "@[scope]/theme": "workspace:./libs/shared/ui/theme"
347
+ }
348
+ }
349
+ ```
350
+ Then run `<PM> install` to create the link. SCSS `@use 'theme/colors'` (or `@use '@[scope]/theme/colors'` with scope) resolves automatically.
351
+
352
+ **NEVER use `stylePreprocessorOptions.includePaths`** — workspace dependency is the only supported approach. `includePaths` breaks IDE autocomplete, makes imports ambiguous, and doesn't survive refactoring.
353
+
354
+ **How it works:** `npx themecraft generate` runs inside the theme library (`libs/shared/ui/theme/`) and produces typed SCSS files (`src/colors.scss`, `sizes.scss`, `typography.scss`) that wrap `color-var()`/`size-var()` internally. Because the library is linked as a package, `@use 'theme/colors'` resolves to these generated files. Components consume the generated variables — never the raw accessor functions, never `generated/` paths.
352
355
 
353
356
  **Token source (pick one):**
354
357
  - **With Figma:** `npx themecraft figma-sync` pulls design variables into `tokens.json`, then `npx themecraft generate`
355
358
  - **Without Figma:** `npx themecraft init` creates `tokens.json` scaffold → manually define token names (color groups, size groups, typography levels) → `npx themecraft generate`
356
359
 
357
- Either way, the result is the same: typed SCSS files in `generated/theme/` ready to import.
360
+ Either way, the result is the same: typed SCSS files inside the theme library, importable via `@use 'theme/colors'`.
358
361
 
359
362
  **Theme definition** (`themes/light.scss`):
360
363
  ```scss
361
364
  @use '@themecraft/core/theming' as theming;
362
- @use '@themecraft/core/themes' as themes;
363
-
364
- themes.$themes: (
365
- light: theming.define-light-theme((
366
- color: theming.define-color-scheme(( surface: #ffffff, on-surface: #1a1a1a, primary: #0066cc )),
367
- size: theming.define-sizes(( card-padding: theming.size(12px, 16px, 24px) )),
368
- typography: ( body-primary: theming.define-typography-level(16px, 1.5, 400, 'Inter, sans-serif') ),
369
- breakpoint: ( medium: 768px, large: 1200px ),
370
- ))
365
+ @use '@themecraft/core/themes' as themes with (
366
+ $themes: (
367
+ light: theming.define-light-theme((
368
+ color: theming.define-color-scheme(( surface: #ffffff, on-surface: #1a1a1a, primary: #0066cc )),
369
+ size: theming.define-sizes(( card-padding: theming.size(12px, 16px, 24px) )),
370
+ typography: ( body-primary: theming.define-typography-level(16px, 1.5, 400, 'Inter, sans-serif') ),
371
+ breakpoint: ( medium: 768px, large: 1200px ),
372
+ ))
373
+ )
371
374
  );
372
375
 
373
376
  @include themes.variables();
@@ -460,6 +463,38 @@ describe('CardBadge', () => {
460
463
  });
461
464
  ```
462
465
 
466
+ ## Playwright VRT Configuration
467
+
468
+ Add to `playwright.config.ts` in the E2E project (`apps/[APP]-e2e/` or `e2e/`):
469
+ ```typescript
470
+ import { defineConfig } from '@playwright/test';
471
+
472
+ export default defineConfig({
473
+ testDir: './src',
474
+ testMatch: ['**/*.spec.ts', '**/*.vrt.spec.ts'],
475
+ fullyParallel: true,
476
+ retries: process.env.CI ? 2 : 0,
477
+ use: {
478
+ baseURL: 'http://localhost:4200',
479
+ trace: 'on-first-retry',
480
+ },
481
+ expect: {
482
+ toHaveScreenshot: {
483
+ maxDiffPixelRatio: 0.01,
484
+ animations: 'disabled',
485
+ },
486
+ },
487
+ webServer: {
488
+ command: 'nx serve [APP]',
489
+ url: 'http://localhost:4200',
490
+ reuseExistingServer: !process.env.CI,
491
+ timeout: 120_000,
492
+ },
493
+ });
494
+ ```
495
+
496
+ In CI, use the Playwright Docker image for consistent rendering: `mcr.microsoft.com/playwright:v[version]-jammy`.
497
+
463
498
  ## Angular Review Checklist
464
499
 
465
500
  When reviewing Angular code, check:
@@ -467,7 +502,7 @@ When reviewing Angular code, check:
467
502
  - Forms: signal forms API used (no legacy FormControl/FormGroup/FormBuilder)
468
503
  - Component file organization: each component in its own subdirectory (not flat in lib/)
469
504
  - ZERO method calls in templates — all derived state in computed()
470
- - Feature vs dumb separation — no inject() in ui/ components
505
+ - Feature vs dumb separation — no business service injection in ui/ components (UI infrastructure like Overlay, ElementRef, CDK is OK), no visual styling in feature/ components (layout orchestration via `:host { display: grid/flex }` + `gap` + `grid-template` is OK)
471
506
  - Content projection (composite pattern) over deep @Input trees
472
507
  - Behavior extracted to directives (not duplicated across components)
473
508
  - @for has track expression (by unique id, not index)
@@ -478,8 +513,9 @@ When reviewing Angular code, check:
478
513
  - Resource disposal (ImageBitmap.close(), stream.stop(), BroadcastChannel.close())
479
514
  - No memory leaks in effects or event listeners
480
515
  - Theme compliance: generated @themecraft SCSS variables — no hardcoded colors/sizes, no direct `color-var()`/`size-var()` calls
481
- - SCSS imports: via package name (no relative paths), no redundant `as` aliases
516
+ - SCSS imports: via workspace package name (no relative paths, no `generated/` paths), no redundant `as` aliases
482
517
  - Lazy loading via loadComponent/loadChildren, no eager page imports
483
518
  - @defer for heavy below-fold components
484
519
  - Naming: PascalCase components, lib- selectors, kebab-case files
485
- - i18n: ALL user-facing text has `i18n` attribute (templates) or `$localize` (TS) — no bare text
520
+ - i18n: ALL user-facing text has `i18n` attribute (templates) or `$localize` (TS) — no bare text
521
+ - VRT: new pages/routes have corresponding `*.vrt.spec.ts` with 3-viewport coverage (375px, 768px, 1280px), baseline snapshots committed
@@ -54,9 +54,20 @@ src/
54
54
  - One component per file, named after its default/named export
55
55
 
56
56
  **Container vs Presentational components (STRICT separation):**
57
- - **Presentational (ui/)**: Pure rendering. Props in, JSX out. No hooks that cause side effects, no data fetching, no direct service calls. Fully reusable and trivially testable.
57
+ - **Presentational (ui/)**: Pure rendering. Props in, JSX out. No data fetching, no application state hooks, no direct service calls. Fully reusable and trivially testable.
58
+ - **UI-infrastructure hooks are allowed**: `useRef` for DOM measurement, `useState` for local UI state (open/closed, animation phase), `useCallback` for stable callbacks. The component stays presentational.
59
+ - **NOT allowed**: data-fetching hooks, application state hooks, `useEffect` for side effects, direct API calls.
58
60
  - **Container (components/ or pages/)**: Smart orchestrators. Use hooks, fetch data, manage state, coordinate presentational components. Tied to a specific use case.
59
- - Rule: if a component calls `useEffect`, fetches data, or accesses context directly, it belongs in `components/` or `pages/`, not `ui/`
61
+ - **NO visual styling**: no colors, backgrounds, borders, shadows, typography, padding on content areas, animations. All visual styling belongs in presentational components.
62
+ - **Layout orchestration is OK**: arranging child components via grid/flex (`display: grid/flex`, `grid-template-areas`, `gap`) is acceptable, especially for page-level route components. This is layout plumbing, not visual design.
63
+ - When a layout pattern is **reused across multiple routes**, extract it to a presentational layout component. Single-use page layout stays in the container.
64
+ - Rule: if a component calls `useEffect` for data fetching, accesses application state context, or calls APIs, it belongs in `components/` or `pages/`, not `ui/`
65
+
66
+ **Anti-patterns:**
67
+ - Presentational component with 8+ props → restructure via compound component pattern, `children`, or context
68
+ - Container coordinating 10+ child components → extract sub-containers
69
+ - Single-use layout wrapper → unnecessary indirection, keep layout in the container
70
+ - Prop drilling through 3+ levels → use composition (`children`/render props) or context
60
71
 
61
72
  **Compound components (composition over prop drilling):**
62
73
  - Prefer `children` and render slots over deep prop trees
@@ -190,7 +201,7 @@ npx themecraft generate # generates type-safe SCSS from tokens.json
190
201
  4. `@themecraft/core/themes` → `variables()` mixin emits CSS custom properties at `:root`
191
202
  5. `ThemeManager` (from `@themecraft/core/runtime`) — runtime theme switching, storage persistence, FOUC prevention
192
203
 
193
- **Component SCSS** (`.module.scss`) — import via package name, NEVER use relative paths, NEVER call `color-var()`/`size-var()` directly:
204
+ **Component SCSS** (`.module.scss`) — import via workspace package name, NEVER use relative paths or `generated/` paths, NEVER call `color-var()`/`size-var()` directly:
194
205
  ```scss
195
206
  // Import via package name — SCSS @use uses last path segment as namespace automatically
196
207
  @use 'theme/colors';
@@ -208,31 +219,35 @@ npx themecraft generate # generates type-safe SCSS from tokens.json
208
219
  ```
209
220
 
210
221
  **SCSS import rules:**
211
- - ALWAYS import via package/library name (e.g. `@use 'theme/colors'`)
212
- - NEVER use relative paths (`../../theme/src/generated/...`) — they break on refactoring and are unreadable
222
+ - ALWAYS import via workspace package name (e.g. `@use 'theme/colors'` or `@use '@[scope]/theme/colors'`)
223
+ - NEVER use relative paths (`../../theme/src/...`) — they break on refactoring and are unreadable
224
+ - NEVER use `generated/` paths (`@use 'generated/theme/colors'`) — the package link abstracts this away
213
225
  - NEVER use redundant `as` aliases that match the last path segment (`@use 'theme/colors' as colors` → just `@use 'theme/colors'`). Only use `as` when you need a different name.
214
226
 
215
- **Making theme resolvable by package name (priority order):**
216
- 1. **Workspace dependency in package.json** (preferred) — add the theme library as a workspace dependency so SCSS resolves it from `node_modules`:
217
- ```jsonc
218
- // package.json (root or consuming app)
219
- {
220
- "dependencies": {
221
- "@[prefix]/theme": "workspace:./libs/shared/ui/theme" // bun/pnpm
222
- // or "file:./libs/shared/ui/theme" for npm
223
- }
224
- }
225
- ```
226
- Then `<PM> install` creates the link. SCSS `@use '@[prefix]/theme/colors'` resolves automatically.
227
- 2. **Bundler/framework SCSS config** (fallback) — e.g. `sassOptions.includeDirs` in `next.config.js`, or Vite `css.preprocessorOptions.scss.includePaths`.
228
-
229
- **How it works:** `npx themecraft generate` reads `tokens.json` and produces typed SCSS files (`generated/theme/colors.scss`, `sizes.scss`, `typography.scss`) that wrap `color-var()`/`size-var()` internally. Components consume these generated variables — never the raw accessor functions.
227
+ **Making theme resolvable by package name (MANDATORY):**
228
+
229
+ Add the theme library as a workspace dependency in `package.json` so SCSS resolves `@use 'theme/...'` from `node_modules`:
230
+ ```jsonc
231
+ // package.json (root or consuming app)
232
+ {
233
+ "dependencies": {
234
+ "theme": "workspace:./libs/shared/ui/theme" // pnpm/bun
235
+ // or "file:./libs/shared/ui/theme" for npm
236
+ // If repo has a scope: "@[scope]/theme": "workspace:./libs/shared/ui/theme"
237
+ }
238
+ }
239
+ ```
240
+ Then run `<PM> install` to create the link. SCSS `@use 'theme/colors'` (or `@use '@[scope]/theme/colors'` with scope) resolves automatically.
241
+
242
+ **NEVER use `sassOptions.includeDirs` or `css.preprocessorOptions.scss.includePaths`** — workspace dependency is the only supported approach.
243
+
244
+ **How it works:** `npx themecraft generate` runs inside the theme library and produces typed SCSS files (`src/colors.scss`, `sizes.scss`, `typography.scss`) that wrap `color-var()`/`size-var()` internally. Because the library is linked as a package, `@use 'theme/colors'` resolves to these generated files. Components consume the generated variables — never the raw accessor functions, never `generated/` paths.
230
245
 
231
246
  **Token source (pick one):**
232
247
  - **With Figma:** `npx themecraft figma-sync` pulls design variables into `tokens.json`, then `npx themecraft generate`
233
248
  - **Without Figma:** `npx themecraft init` creates `tokens.json` scaffold → manually define token names (color groups, size groups, typography levels) → `npx themecraft generate`
234
249
 
235
- Either way, the result is the same: typed SCSS files in `generated/theme/` ready to import.
250
+ Either way, the result is the same: typed SCSS files inside the theme library, importable via `@use 'theme/colors'`.
236
251
 
237
252
  **Runtime setup** (in layout or `_app.tsx`):
238
253
  ```typescript
@@ -321,12 +336,44 @@ describe('CardBadge', () => {
321
336
  - Test behavior, not implementation — don't assert on state variables or hook internals
322
337
  - Mock server state with MSW (Mock Service Worker) or TanStack Query's test utilities
323
338
 
339
+ ## Playwright VRT Configuration
340
+
341
+ Add to `playwright.config.ts` in the E2E project:
342
+ ```typescript
343
+ import { defineConfig } from '@playwright/test';
344
+
345
+ export default defineConfig({
346
+ testDir: './src',
347
+ testMatch: ['**/*.spec.ts', '**/*.vrt.spec.ts'],
348
+ fullyParallel: true,
349
+ retries: process.env.CI ? 2 : 0,
350
+ use: {
351
+ baseURL: 'http://localhost:3000',
352
+ trace: 'on-first-retry',
353
+ },
354
+ expect: {
355
+ toHaveScreenshot: {
356
+ maxDiffPixelRatio: 0.01,
357
+ animations: 'disabled',
358
+ },
359
+ },
360
+ webServer: {
361
+ command: 'nx serve [APP]',
362
+ url: 'http://localhost:3000',
363
+ reuseExistingServer: !process.env.CI,
364
+ timeout: 120_000,
365
+ },
366
+ });
367
+ ```
368
+
369
+ Adjust `baseURL`/`url` to match the dev server port (Next.js: 3000, Vite: 5173). In CI, use the Playwright Docker image for consistent rendering.
370
+
324
371
  ## React Review Checklist
325
372
 
326
373
  When reviewing React code, check:
327
374
  - Hooks rules: no conditional hooks, no hooks inside loops or nested functions
328
375
  - `useMemo`/`useCallback` usage: present where needed (expensive computations, stable references for children), absent where unnecessary (premature optimization)
329
- - Container vs presentational separation — no data fetching or side effects in ui/ components
376
+ - Container vs presentational separation — no data fetching or app state in ui/ components (UI hooks like useRef/useState for UI state are OK), no visual styling in container components (layout orchestration via grid/flex is OK)
330
377
  - Composition via children/compound components over deep prop drilling
331
378
  - Custom hooks extracted for shared behavior (not duplicated across components)
332
379
  - `key` prop on list items uses unique stable id, never array index
@@ -337,10 +384,11 @@ When reviewing React code, check:
337
384
  - Server/client separation: Flag WARN if a module mixes server and browser logic without clear boundaries
338
385
  - No memory leaks in effects, event listeners, or subscriptions
339
386
  - Theme compliance: generated @themecraft SCSS variables — no hardcoded colors/sizes, no direct `color-var()`/`size-var()` calls
340
- - SCSS imports: via package name (no relative paths), no redundant `as` aliases
387
+ - SCSS imports: via workspace package name (no relative paths, no `generated/` paths), no redundant `as` aliases
341
388
  - Code splitting: `React.lazy` + `Suspense` for heavy routes/components, no eager imports of page-level components
342
389
  - Dynamic imports for heavy third-party libraries
343
390
  - Naming: PascalCase components, camelCase hooks, kebab-case files
344
391
  - i18n: ALL user-facing text goes through `t()` — no bare strings in JSX or UI logic
345
392
  - No unstable nested component definitions (components defined inside render)
346
393
  - `React.memo` only on components with measured re-render cost — not as a default
394
+ - VRT: new pages/routes have corresponding `*.vrt.spec.ts` with 3-viewport coverage (375px, 768px, 1280px), baseline snapshots committed
@@ -48,8 +48,17 @@ src/
48
48
  **Feature vs Dumb components (STRICT separation):**
49
49
  - **Dumb (ui/)**: Pure presentational. `$props()` for inputs, callback props for events, slots/snippets for composition. No data fetching, no navigation, no global state access. Fully reusable.
50
50
  - **Feature (features/ or routes/)**: Smart orchestrators. Access load data, manage state, call APIs, coordinate dumb components. NOT reusable — tied to a specific use case.
51
+ - **NO visual styling**: no colors, backgrounds, borders, shadows, typography, padding on content areas, animations. All visual styling belongs in dumb components.
52
+ - **Layout orchestration is OK**: arranging child components via grid/flex (`display: grid/flex`, `grid-template-areas`, `gap`) is acceptable, especially for page-level route components.
53
+ - When a layout pattern is **reused across multiple routes**, extract it to a dumb layout component (e.g., `DashboardLayout.svelte`). Single-use page layout stays in the feature component.
51
54
  - Rule: if a component fetches data or accesses global state, it belongs in `features/` or `routes/`, not `ui/`
52
55
 
56
+ **Anti-patterns:**
57
+ - Dumb component with 8+ props → restructure via compound component pattern, `<slot>`s, or context
58
+ - Feature component coordinating 10+ child components → extract sub-features
59
+ - Single-use layout shell component → unnecessary indirection, keep layout in the feature component
60
+ - Prop drilling through 3+ levels → use slots or Svelte context
61
+
53
62
  **Composition patterns:**
54
63
  - Prefer slots (`<slot>`) and named slots (`<slot name="header">`) over deep prop drilling
55
64
  - Use snippet blocks (`{#snippet}`) for typed, reusable template fragments within a component
@@ -190,7 +199,7 @@ npx themecraft generate # generates type-safe SCSS from tokens.json
190
199
  4. `@themecraft/core/themes` → `variables()` mixin emits CSS custom properties at `:root`
191
200
  5. `ThemeManager` (from `@themecraft/core/runtime`) — runtime switching, storage, FOUC prevention
192
201
 
193
- **Component SCSS** — import via package name, NEVER use relative paths, NEVER call `color-var()`/`size-var()` directly:
202
+ **Component SCSS** — import via workspace package name, NEVER use relative paths or `generated/` paths, NEVER call `color-var()`/`size-var()` directly:
194
203
  ```svelte
195
204
  <style lang="scss">
196
205
  // Import via package name — SCSS @use uses last path segment as namespace automatically
@@ -210,31 +219,35 @@ npx themecraft generate # generates type-safe SCSS from tokens.json
210
219
  ```
211
220
 
212
221
  **SCSS import rules:**
213
- - ALWAYS import via package/library name (e.g. `@use 'theme/colors'`)
214
- - NEVER use relative paths (`../../theme/src/generated/...`) — they break on refactoring and are unreadable
222
+ - ALWAYS import via workspace package name (e.g. `@use 'theme/colors'` or `@use '@[scope]/theme/colors'`)
223
+ - NEVER use relative paths (`../../theme/src/...`) — they break on refactoring and are unreadable
224
+ - NEVER use `generated/` paths (`@use 'generated/theme/colors'`) — the package link abstracts this away
215
225
  - NEVER use redundant `as` aliases that match the last path segment (`@use 'theme/colors' as colors` → just `@use 'theme/colors'`). Only use `as` when you need a different name.
216
226
 
217
- **Making theme resolvable by package name (priority order):**
218
- 1. **Workspace dependency in package.json** (preferred) — add the theme library as a workspace dependency so SCSS resolves it from `node_modules`:
219
- ```jsonc
220
- // package.json (root or consuming app)
221
- {
222
- "dependencies": {
223
- "@[prefix]/theme": "workspace:./libs/shared/ui/theme" // bun/pnpm
224
- // or "file:./libs/shared/ui/theme" for npm
225
- }
226
- }
227
- ```
228
- Then `<PM> install` creates the link. SCSS `@use '@[prefix]/theme/colors'` resolves automatically.
229
- 2. **SvelteKit SCSS config** (fallback) — `css.preprocessorOptions.scss.includePaths` in `vite.config.ts`.
230
-
231
- **How it works:** `npx themecraft generate` reads `tokens.json` and produces typed SCSS files (`generated/theme/colors.scss`, `sizes.scss`, `typography.scss`) that wrap `color-var()`/`size-var()` internally. Components consume these generated variables — never the raw accessor functions.
227
+ **Making theme resolvable by package name (MANDATORY):**
228
+
229
+ Add the theme library as a workspace dependency in `package.json` so SCSS resolves `@use 'theme/...'` from `node_modules`:
230
+ ```jsonc
231
+ // package.json (root or consuming app)
232
+ {
233
+ "dependencies": {
234
+ "theme": "workspace:./libs/shared/ui/theme" // pnpm/bun
235
+ // or "file:./libs/shared/ui/theme" for npm
236
+ // If repo has a scope: "@[scope]/theme": "workspace:./libs/shared/ui/theme"
237
+ }
238
+ }
239
+ ```
240
+ Then run `<PM> install` to create the link. SCSS `@use 'theme/colors'` (or `@use '@[scope]/theme/colors'` with scope) resolves automatically.
241
+
242
+ **NEVER use `css.preprocessorOptions.scss.includePaths`** — workspace dependency is the only supported approach.
243
+
244
+ **How it works:** `npx themecraft generate` runs inside the theme library and produces typed SCSS files (`src/colors.scss`, `sizes.scss`, `typography.scss`) that wrap `color-var()`/`size-var()` internally. Because the library is linked as a package, `@use 'theme/colors'` resolves to these generated files. Components consume the generated variables — never the raw accessor functions, never `generated/` paths.
232
245
 
233
246
  **Token source (pick one):**
234
247
  - **With Figma:** `npx themecraft figma-sync` pulls design variables into `tokens.json`, then `npx themecraft generate`
235
248
  - **Without Figma:** `npx themecraft init` creates `tokens.json` scaffold → manually define token names (color groups, size groups, typography levels) → `npx themecraft generate`
236
249
 
237
- Either way, the result is the same: typed SCSS files in `generated/theme/` ready to import.
250
+ Either way, the result is the same: typed SCSS files inside the theme library, importable via `@use 'theme/colors'`.
238
251
 
239
252
  **Runtime setup** (in `+layout.svelte` or `hooks.server.ts`):
240
253
  ```typescript
@@ -324,6 +337,38 @@ describe('CardBadge', () => {
324
337
  });
325
338
  ```
326
339
 
340
+ ## Playwright VRT Configuration
341
+
342
+ Add to `playwright.config.ts` in the E2E project:
343
+ ```typescript
344
+ import { defineConfig } from '@playwright/test';
345
+
346
+ export default defineConfig({
347
+ testDir: './src',
348
+ testMatch: ['**/*.spec.ts', '**/*.vrt.spec.ts'],
349
+ fullyParallel: true,
350
+ retries: process.env.CI ? 2 : 0,
351
+ use: {
352
+ baseURL: 'http://localhost:5173',
353
+ trace: 'on-first-retry',
354
+ },
355
+ expect: {
356
+ toHaveScreenshot: {
357
+ maxDiffPixelRatio: 0.01,
358
+ animations: 'disabled',
359
+ },
360
+ },
361
+ webServer: {
362
+ command: 'nx serve [APP]',
363
+ url: 'http://localhost:5173',
364
+ reuseExistingServer: !process.env.CI,
365
+ timeout: 120_000,
366
+ },
367
+ });
368
+ ```
369
+
370
+ In CI, use the Playwright Docker image for consistent rendering: `mcr.microsoft.com/playwright:v[version]-jammy`.
371
+
327
372
  ## Svelte Review Checklist
328
373
 
329
374
  When reviewing Svelte code, check:
@@ -332,7 +377,7 @@ When reviewing Svelte code, check:
332
377
  - `$derived()` for all computed values — no recalculation in templates or reactive statements
333
378
  - `$state.raw()` for large data that doesn't need deep reactivity
334
379
  - `{#each}` has key expression `(item.id)` — NEVER keyed by index
335
- - Feature vs dumb separation — no data fetching or global state in `ui/` components
380
+ - Feature vs dumb separation — no data fetching or global state in `ui/` components, no visual styling in feature/ components (layout orchestration via grid/flex is OK)
336
381
  - Composition via slots/snippets over deep prop drilling
337
382
  - Reusable DOM behavior extracted to actions (`use:`) — not duplicated across components
338
383
  - SSR safety: no `window`/`document`/`navigator` in server-reachable code, use `browser` guard or `onMount`
@@ -340,10 +385,11 @@ When reviewing Svelte code, check:
340
385
  - `$lib/` imports — no deep relative paths (`../../../`)
341
386
  - `$lib/server/` for server-only modules — enforced by SvelteKit
342
387
  - Theme compliance: generated @themecraft SCSS variables — no hardcoded colors/sizes, no direct `color-var()`/`size-var()` calls
343
- - SCSS imports: via package name (no relative paths), no redundant `as` aliases
388
+ - SCSS imports: via workspace package name (no relative paths, no `generated/` paths), no redundant `as` aliases
344
389
  - Lazy loading: routes auto-split, dynamic `import()` for heavy non-route components
345
390
  - i18n: ALL user-facing text goes through translation functions — no bare strings in templates
346
391
  - Accessibility: all Svelte compiler a11y warnings resolved (not suppressed)
347
392
  - No `{@html}` without sanitization
348
393
  - Callback props for outputs (not `createEventDispatcher` — that's Svelte 4)
349
394
  - Resource cleanup in `onDestroy` or `$effect` return for manual subscriptions
395
+ - VRT: new pages/routes have corresponding `*.vrt.spec.ts` with 3-viewport coverage (375px, 768px, 1280px), baseline snapshots committed
@@ -58,9 +58,19 @@ src/
58
58
 
59
59
  **Feature vs Dumb components (STRICT separation):**
60
60
  - **Dumb (ui/components/)**: Pure presentational. `defineProps` + `defineEmits` + slots. No store access, no router, no HTTP, no side effects. Fully reusable.
61
+ - **Dumb UI-infrastructure usage is allowed**: `useTemplateRef`, `nextTick`, `useSlots` — these are UI plumbing, not business logic.
61
62
  - **Feature (features/[domain]/components/ or pages/)**: Smart orchestrators. Use composables, access stores, call APIs, coordinate dumb components. NOT reusable — tied to a specific use case.
63
+ - **NO visual styling**: no colors, backgrounds, borders, shadows, typography, padding on content areas, animations. All visual styling belongs in dumb components.
64
+ - **Layout orchestration is OK**: arranging child components via grid/flex (`display: grid/flex`, `grid-template-areas`, `gap`) is acceptable, especially for page-level route components.
65
+ - When a layout pattern is **reused across multiple routes**, extract it to a dumb layout component (e.g., `DashboardLayout.vue`). Single-use page layout stays in the feature component.
62
66
  - Rule: if a component imports a store or calls an API, it belongs in `features/` or `pages/`, not `ui/`
63
67
 
68
+ **Anti-patterns:**
69
+ - Dumb component with 8+ props → restructure via compound component pattern, `<slot>`s, or `provide`/`inject`
70
+ - Feature component coordinating 10+ child components → extract sub-features
71
+ - Single-use layout shell component → unnecessary indirection, keep layout in the feature component
72
+ - Prop drilling through 3+ levels → use slots or `provide`/`inject`
73
+
64
74
  **Slots (composite pattern):**
65
75
  - Prefer slots over deep prop drilling for component composition
66
76
  - Default slot for primary content: `<slot />`
@@ -217,7 +227,7 @@ npx themecraft generate # generates type-safe SCSS from tokens.json
217
227
  4. `@themecraft/core/themes` → `variables()` mixin emits CSS custom properties at `:root`
218
228
  5. `ThemeManager` (from `@themecraft/core/runtime`) — runtime switching, storage, FOUC prevention
219
229
 
220
- **Component SCSS** — import via package name, NEVER use relative paths, NEVER call `color-var()`/`size-var()` directly:
230
+ **Component SCSS** — import via workspace package name, NEVER use relative paths or `generated/` paths, NEVER call `color-var()`/`size-var()` directly:
221
231
  ```vue
222
232
  <style scoped lang="scss">
223
233
  // Import via package name — SCSS @use uses last path segment as namespace automatically
@@ -237,31 +247,35 @@ npx themecraft generate # generates type-safe SCSS from tokens.json
237
247
  ```
238
248
 
239
249
  **SCSS import rules:**
240
- - ALWAYS import via package/library name (e.g. `@use 'theme/colors'`)
241
- - NEVER use relative paths (`../../theme/src/generated/...`) — they break on refactoring and are unreadable
250
+ - ALWAYS import via workspace package name (e.g. `@use 'theme/colors'` or `@use '@[scope]/theme/colors'`)
251
+ - NEVER use relative paths (`../../theme/src/...`) — they break on refactoring and are unreadable
252
+ - NEVER use `generated/` paths (`@use 'generated/theme/colors'`) — the package link abstracts this away
242
253
  - NEVER use redundant `as` aliases that match the last path segment (`@use 'theme/colors' as colors` → just `@use 'theme/colors'`). Only use `as` when you need a different name.
243
254
 
244
- **Making theme resolvable by package name (priority order):**
245
- 1. **Workspace dependency in package.json** (preferred) — add the theme library as a workspace dependency so SCSS resolves it from `node_modules`:
246
- ```jsonc
247
- // package.json (root or consuming app)
248
- {
249
- "dependencies": {
250
- "@[prefix]/theme": "workspace:./libs/shared/ui/theme" // bun/pnpm
251
- // or "file:./libs/shared/ui/theme" for npm
252
- }
253
- }
254
- ```
255
- Then `<PM> install` creates the link. SCSS `@use '@[prefix]/theme/colors'` resolves automatically.
256
- 2. **Vite SCSS config** (fallback) — `css.preprocessorOptions.scss.includePaths` in `vite.config.ts`.
257
-
258
- **How it works:** `npx themecraft generate` reads `tokens.json` and produces typed SCSS files (`generated/theme/colors.scss`, `sizes.scss`, `typography.scss`) that wrap `color-var()`/`size-var()` internally. Components consume these generated variables — never the raw accessor functions.
255
+ **Making theme resolvable by package name (MANDATORY):**
256
+
257
+ Add the theme library as a workspace dependency in `package.json` so SCSS resolves `@use 'theme/...'` from `node_modules`:
258
+ ```jsonc
259
+ // package.json (root or consuming app)
260
+ {
261
+ "dependencies": {
262
+ "theme": "workspace:./libs/shared/ui/theme" // pnpm/bun
263
+ // or "file:./libs/shared/ui/theme" for npm
264
+ // If repo has a scope: "@[scope]/theme": "workspace:./libs/shared/ui/theme"
265
+ }
266
+ }
267
+ ```
268
+ Then run `<PM> install` to create the link. SCSS `@use 'theme/colors'` (or `@use '@[scope]/theme/colors'` with scope) resolves automatically.
269
+
270
+ **NEVER use `css.preprocessorOptions.scss.includePaths`** — workspace dependency is the only supported approach.
271
+
272
+ **How it works:** `npx themecraft generate` runs inside the theme library and produces typed SCSS files (`src/colors.scss`, `sizes.scss`, `typography.scss`) that wrap `color-var()`/`size-var()` internally. Because the library is linked as a package, `@use 'theme/colors'` resolves to these generated files. Components consume the generated variables — never the raw accessor functions, never `generated/` paths.
259
273
 
260
274
  **Token source (pick one):**
261
275
  - **With Figma:** `npx themecraft figma-sync` pulls design variables into `tokens.json`, then `npx themecraft generate`
262
276
  - **Without Figma:** `npx themecraft init` creates `tokens.json` scaffold → manually define token names (color groups, size groups, typography levels) → `npx themecraft generate`
263
277
 
264
- Either way, the result is the same: typed SCSS files in `generated/theme/` ready to import.
278
+ Either way, the result is the same: typed SCSS files inside the theme library, importable via `@use 'theme/colors'`.
265
279
 
266
280
  **Runtime setup** (in `App.vue` or plugin):
267
281
  ```typescript
@@ -348,13 +362,45 @@ describe('UserCard', () => {
348
362
 
349
363
  **Pinia stores:** Use `setActivePinia(createPinia())` in `beforeEach`, then test store actions/getters directly.
350
364
 
365
+ ## Playwright VRT Configuration
366
+
367
+ Add to `playwright.config.ts` in the E2E project:
368
+ ```typescript
369
+ import { defineConfig } from '@playwright/test';
370
+
371
+ export default defineConfig({
372
+ testDir: './src',
373
+ testMatch: ['**/*.spec.ts', '**/*.vrt.spec.ts'],
374
+ fullyParallel: true,
375
+ retries: process.env.CI ? 2 : 0,
376
+ use: {
377
+ baseURL: 'http://localhost:5173',
378
+ trace: 'on-first-retry',
379
+ },
380
+ expect: {
381
+ toHaveScreenshot: {
382
+ maxDiffPixelRatio: 0.01,
383
+ animations: 'disabled',
384
+ },
385
+ },
386
+ webServer: {
387
+ command: 'nx serve [APP]',
388
+ url: 'http://localhost:5173',
389
+ reuseExistingServer: !process.env.CI,
390
+ timeout: 120_000,
391
+ },
392
+ });
393
+ ```
394
+
395
+ Adjust `baseURL`/`url` to match the dev server port (Vite: 5173, Nuxt: 3000). In CI, use the Playwright Docker image for consistent rendering.
396
+
351
397
  ## Vue Review Checklist
352
398
 
353
399
  When reviewing Vue code, check:
354
400
  - Composition API only — no Options API (`data()`, `methods`, `computed` object syntax)
355
401
  - `<script setup>` in all SFCs — no `defineComponent()` with `setup()` function
356
402
  - ZERO method calls in templates — all derived state in `computed()`
357
- - Feature vs dumb separation — no store access or API calls in `ui/` components
403
+ - Feature vs dumb separation — no store access or API calls in `ui/` components (UI infrastructure like `useTemplateRef`/`nextTick` is OK), no visual styling in feature/ components (layout orchestration via grid/flex is OK)
358
404
  - Slots used for composition over deep prop drilling
359
405
  - Composables (`use*`) for shared stateful logic — not mixins, not utility classes
360
406
  - Custom directives for shared DOM behavior — not duplicated across components
@@ -368,10 +414,11 @@ When reviewing Vue code, check:
368
414
  - SSR/Client separation: Flag WARN if a composable mixes server and browser logic
369
415
  - `<style scoped>` on all components — no unscoped styles leaking globally
370
416
  - Theme compliance: generated @themecraft SCSS variables — no hardcoded colors/sizes, no direct `color-var()`/`size-var()` calls
371
- - SCSS imports: via package name (no relative paths), no redundant `as` aliases
417
+ - SCSS imports: via workspace package name (no relative paths, no `generated/` paths), no redundant `as` aliases
372
418
  - Lazy loading: `defineAsyncComponent` for heavy components, dynamic imports for routes
373
419
  - No `v-html` without sanitization (XSS risk)
374
420
  - Props defined with TypeScript types via `defineProps<{ ... }>()` — not runtime-only validation
375
421
  - Emits declared via `defineEmits<{ ... }>()` — not untyped string arrays
376
422
  - Naming: PascalCase components, `Base` prefix for atoms, `use` prefix for composables
377
423
  - i18n: ALL user-facing text goes through `$t()` / `t()` — no bare strings in templates or script
424
+ - VRT: new pages/routes have corresponding `*.vrt.spec.ts` with 3-viewport coverage (375px, 768px, 1280px), baseline snapshots committed
@@ -281,6 +281,16 @@ Framework-specific best practices, component architecture, state management, SSR
281
281
  - Coverage aggregated at workspace root under `coverage/` (add to `.gitignore`)
282
282
  - Run: `nx test [PROJECT] --coverage` or `nx affected --target=test -- --coverage`
283
283
 
284
+ ### Visual Regression Testing
285
+
286
+ Page-level VRT using Playwright `toHaveScreenshot()` at 3 viewports (375px, 768px, 1280px). Catches unintended visual regressions across pages.
287
+
288
+ - **Files**: `*.vrt.spec.ts` in E2E project (co-located with functional E2E tests)
289
+ - **Baselines**: `*-snapshots/` directories — committed to git
290
+ - **Threshold**: `maxDiffPixelRatio: 0.01` (1%), animations disabled
291
+ - **Update baselines**: `nx e2e [APP]-e2e -- --update-snapshots` after intentional visual changes
292
+ - **CI**: use Playwright Docker image (`mcr.microsoft.com/playwright`) for consistent rendering
293
+
284
294
  ### Code Quality & Linting
285
295
 
286
296
  Config templates are in `.claude/templates/` — copy to project root during Phase 0 scaffolding:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-claude-workspace",
3
- "version": "1.1.86",
3
+ "version": "1.1.87",
4
4
  "description": "Scaffold a project with Claude Code agents for autonomous AI-driven development",
5
5
  "type": "module",
6
6
  "bin": {