Instruction file imported from microcropoc/vibetest (
.cursor/rules/angular-cli.mdc). Copyright stays with the author.
vibetest — Angular 22 SPA/PWA
Offline-first course PWA. Spec: docs/SPECIFICATION.md. Tasks: docs/tasks/.
Architecture & generation
- Standalone only — no NgModules
- CLI (
ng g): component, service, guard, pipe. Manual: domain models, validators, Web Workers, pure functions, adapters, constants - Domain folders (vertical slices; add subfolders only when several files share a role — do not pre-create layers):
courses/,player/,progress/,execution/,storage/,shared/ui/.storage/— infrastructure: Dexie schema, migrations, queries only here; domain types never import Dexie.execution/— Workers, message types, one wrapper service; components do not callpostMessagedirectly - Domain: pure functions / types; services: DI, orchestration, side-effects (storage, Workers, HTTP later);
providedIn: 'root'unless route-scopedproviderswhen state must die with the route. One component, one job; no “service soup” - Smart / dumb: pages orchestrate; reusable UI via
input()/output(); no HTTP/storage inshared/uiwithout reason - Lazy: pages via
loadComponent/loadChildren— never eager-import a page inapp.tsorapp.routes.ts; wire new pages withloadComponent, not parentimports. Heavy step UIs in player:@deferor dynamicimport(). Eager: shell, layout,shared/ui. No NgModules or barrelindex.tsre-exporting lazy pages/steps
Code style
- TypeScript strict.
anyforbidden (any[],Record<string, any>,as any,input<any>(),$any()), with or without eslint unknownat boundaries only:JSON.parse(course import), WorkerMessageEvent.data, IndexedDB rows (Dexie generics ≠ runtime), HTTP when added, untyped third-party. ImmediatelyparseX(unknown): X(throws on mismatch) /isX(v): v is Xnext to domain types (courses/,execution/, …); components getX, notunknown- No domain
as Tinstead of parse; ok:as const,as unknownat boundary;as unknown as Tonly insideparseX - Zoneless (no
zone.js): state —signal(), derived —computed()(noteffect()).effect()only to sync outward (Dexie, Worker, DOM); never write a signal the effect reads. Domain typesreadonly; update via new objects, not mutation signal()/computed(),inject();@if/@for/@switch; Observables:asyncpipe ortoSignal(); reactive forms for non-trivial input; SCSS, BEM-like, no inline template styles; avoid editingangular.json/tsconfig*without need
PWA
- SW on production
ng build, not defaultng serve; updates viaSwUpdate; SW caches lazy chunks — do not bundle everything into main “for offline”
Tests
ng test
- Required: domain logic, storage, guards, pipes, non-trivial component logic; behavior, not internals;
*.spec.tscolocated; domain tests withoutTestBed, components withTestBed; zoneless —await fixture.whenStable()notdetectChanges(); one-off runng test --watch=false; green before task done (when tests exist); config/types/simple markup — no test-first mandate
Tasks & git
Tasks in docs/tasks/ — status = folder (backlog → in-progress → done); no status in REPORT.md. backlog/vt-{n}-{slug}.md; in-progress/…/ → TASK.md + REPORT.md (branch:); done/ after merge; archive/{YYYY-MM}/ in batches on request; _template/; {n} = max vt-* in backlog, in-progress, done, and archive/ + 1; kebab-case slug.
vt-{n}: branch feature/vt-{n}-{slug} from main; backlog → in-progress/ + REPORT.md; read TASK.md + spec; update REPORT.md; one task per branch. WIP commits on feature branch need no confirmation (squashed on merge). Merge only on explicit request (“merge vt-{n}”, “merge to main”) — also authorizes doc move, squash, push:
- Move
in-progress/vt-{n}-{slug}/→done/+ finalREPORT.md git rebase main(conflicts on branch only)- Squash (
git reset --soft main && git commit); messagevt-{n}: {short summary}— show before commit git switch main && git merge --ff-only feature/…, push, delete branch
Merged code must not land in main while folder is still in-progress/. Review: REPORT.md filled per _template/ (all sections), green tests; diff main..feature/….
Never
- Direct commits or merge commits on
main - Duplicate logic across domains
- Secrets,
.env,node_modules/in git