@eltonssouza/development-utility-kit 1.0.0

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.
Files changed (137) hide show
  1. package/.claude/agents/analyst.md +198 -0
  2. package/.claude/agents/backend-developer.md +126 -0
  3. package/.claude/agents/brain-keeper.md +229 -0
  4. package/.claude/agents/code-reviewer.md +181 -0
  5. package/.claude/agents/database-engineer.md +94 -0
  6. package/.claude/agents/devops-engineer.md +141 -0
  7. package/.claude/agents/frontend-developer.md +97 -0
  8. package/.claude/agents/gate-keeper.md +118 -0
  9. package/.claude/agents/migrator.md +291 -0
  10. package/.claude/agents/mobile-developer.md +80 -0
  11. package/.claude/agents/n8n-specialist.md +94 -0
  12. package/.claude/agents/product-owner.md +115 -0
  13. package/.claude/agents/qa-engineer.md +232 -0
  14. package/.claude/agents/release-engineer.md +204 -0
  15. package/.claude/agents/scaffold.md +87 -0
  16. package/.claude/agents/security-engineer.md +199 -0
  17. package/.claude/agents/sprint-runner.md +44 -0
  18. package/.claude/agents/stack-resolver.md +84 -0
  19. package/.claude/agents/tech-lead.md +182 -0
  20. package/.claude/agents/update-template.md +54 -0
  21. package/.claude/agents/ux-designer.md +118 -0
  22. package/.claude/settings.json +44 -0
  23. package/.claude/skills/README.md +332 -0
  24. package/.claude/skills/active-project/SKILL.md +129 -0
  25. package/.claude/skills/api-integration-test/SKILL.md +64 -0
  26. package/.claude/skills/auto-test-guard/SKILL.md +237 -0
  27. package/.claude/skills/auto-test-guard/resources/backend-tests.md +20 -0
  28. package/.claude/skills/auto-test-guard/resources/e2e-tests.md +24 -0
  29. package/.claude/skills/auto-test-guard/resources/execution-report.md +49 -0
  30. package/.claude/skills/auto-test-guard/resources/frontend-tests.md +18 -0
  31. package/.claude/skills/auto-test-guard/resources/initial-setup.md +108 -0
  32. package/.claude/skills/auto-test-guard/resources/run-suite.md +48 -0
  33. package/.claude/skills/auto-test-guard/resources/senior-gate.md +19 -0
  34. package/.claude/skills/brain-keeper/SKILL.md +60 -0
  35. package/.claude/skills/brain-keeper/obsidian/app.json +9 -0
  36. package/.claude/skills/brain-keeper/obsidian/appearance.json +4 -0
  37. package/.claude/skills/brain-keeper/obsidian/core-plugins.json +20 -0
  38. package/.claude/skills/brain-keeper/obsidian/daily-notes.json +5 -0
  39. package/.claude/skills/brain-keeper/obsidian/graph.json +32 -0
  40. package/.claude/skills/brain-keeper/obsidian/snippets/folder-colors.css +90 -0
  41. package/.claude/skills/brain-keeper/obsidian/templates.json +5 -0
  42. package/.claude/skills/brain-keeper/templates/README.md +51 -0
  43. package/.claude/skills/brain-keeper/templates/adr.md +40 -0
  44. package/.claude/skills/brain-keeper/templates/bug.md +35 -0
  45. package/.claude/skills/brain-keeper/templates/daily.md +38 -0
  46. package/.claude/skills/brain-keeper/templates/feature.md +62 -0
  47. package/.claude/skills/brain-keeper/templates/meeting.md +34 -0
  48. package/.claude/skills/brain-keeper/templates/tech-debt.md +21 -0
  49. package/.claude/skills/caveman/SKILL.md +187 -0
  50. package/.claude/skills/create-stack-pack/SKILL.md +281 -0
  51. package/.claude/skills/grill-me/SKILL.md +79 -0
  52. package/.claude/skills/honcho-memory/SKILL.md +207 -0
  53. package/.claude/skills/honcho-memory/docs/api-endpoints-verified.md +75 -0
  54. package/.claude/skills/honcho-memory/hooks/on-prompt-submit.js +221 -0
  55. package/.claude/skills/honcho-memory/hooks/on-stop.js +193 -0
  56. package/.claude/skills/honcho-memory/lib/honcho-client.js +363 -0
  57. package/.claude/skills/honcho-memory/lib/memory-injector.js +93 -0
  58. package/.claude/skills/honcho-memory/package.json +32 -0
  59. package/.claude/skills/honcho-memory/scripts/cli.js +370 -0
  60. package/.claude/skills/honcho-memory/scripts/setup.js +109 -0
  61. package/.claude/skills/honcho-memory/tests/t001-api-endpoints-verified.test.js +89 -0
  62. package/.claude/skills/honcho-memory/tests/t002-structure.test.js +97 -0
  63. package/.claude/skills/honcho-memory/tests/t003-honcho-client.test.js +162 -0
  64. package/.claude/skills/honcho-memory/tests/t004-soft-delete.test.js +259 -0
  65. package/.claude/skills/honcho-memory/tests/t005-memory-injector.test.js +175 -0
  66. package/.claude/skills/honcho-memory/tests/t006-on-prompt-submit.test.js +215 -0
  67. package/.claude/skills/honcho-memory/tests/t007-on-stop.test.js +165 -0
  68. package/.claude/skills/honcho-memory/tests/t008-cli.test.js +214 -0
  69. package/.claude/skills/honcho-memory/tests/t009-setup.test.js +232 -0
  70. package/.claude/skills/honcho-memory/tests/t010-skill-md.test.js +114 -0
  71. package/.claude/skills/honcho-memory/tests/t011-settings-hooks.test.js +105 -0
  72. package/.claude/skills/honcho-memory/tests/t012-docs-update.test.js +106 -0
  73. package/.claude/skills/honcho-memory/tests/t013-smoke-e2e.test.js +90 -0
  74. package/.claude/skills/pair-debug/SKILL.md +288 -0
  75. package/.claude/skills/prd-ready-check/SKILL.md +58 -0
  76. package/.claude/skills/project-manager/SKILL.md +167 -0
  77. package/.claude/skills/quality-standards/SKILL.md +201 -0
  78. package/.claude/skills/quick-feature/SKILL.md +264 -0
  79. package/.claude/skills/run-sprint/SKILL.md +342 -0
  80. package/.claude/skills/scaffold/SKILL.md +58 -0
  81. package/.claude/skills/stack-discovery/SKILL.md +159 -0
  82. package/.claude/skills/test-coverage-auditor/SKILL.md +59 -0
  83. package/.claude/skills/to-issues/SKILL.md +163 -0
  84. package/.claude/skills/to-prd/SKILL.md +130 -0
  85. package/.claude/skills/update-template/SKILL.md +254 -0
  86. package/.claude/stacks/CODEOWNERS +30 -0
  87. package/.claude/stacks/README.md +88 -0
  88. package/.claude/stacks/_template.md +116 -0
  89. package/.claude/stacks/java/spring-boot-3.md +376 -0
  90. package/.claude/stacks/java/spring-boot-4.md +438 -0
  91. package/.claude/stacks/typescript/angular-18.md +420 -0
  92. package/.claude/stacks/typescript/angular-19.md +397 -0
  93. package/.claude/stacks/typescript/angular-21.md +494 -0
  94. package/CLAUDE.md +453 -0
  95. package/README.md +391 -0
  96. package/bin/cli.js +773 -0
  97. package/bin/lib/backup.js +62 -0
  98. package/bin/lib/detect-stack.js +476 -0
  99. package/bin/lib/help.js +233 -0
  100. package/bin/lib/identity.js +108 -0
  101. package/bin/lib/local-dir.js +69 -0
  102. package/bin/lib/manifest.js +236 -0
  103. package/bin/lib/sync-all.js +394 -0
  104. package/bin/lib/version-check.js +398 -0
  105. package/dashboard/db.js +199 -0
  106. package/dashboard/package.json +22 -0
  107. package/dashboard/public/app.js +709 -0
  108. package/dashboard/public/content/docs/agents-reference.en.md +911 -0
  109. package/dashboard/public/content/docs/architecture-overview.en.md +260 -0
  110. package/dashboard/public/content/docs/autonomy-matrix.en.md +186 -0
  111. package/dashboard/public/content/docs/git-flow.en.md +525 -0
  112. package/dashboard/public/content/docs/honcho-memory.en.md +394 -0
  113. package/dashboard/public/content/docs/hooks-reference.en.md +420 -0
  114. package/dashboard/public/content/docs/pipeline.en.md +400 -0
  115. package/dashboard/public/content/docs/quality-gate.en.md +315 -0
  116. package/dashboard/public/content/docs/skills-reference.en.md +500 -0
  117. package/dashboard/public/content/docs/stack-rules.en.md +362 -0
  118. package/dashboard/public/content/docs/troubleshooting.en.md +637 -0
  119. package/dashboard/public/content/manifest.json +102 -0
  120. package/dashboard/public/content/manual/backend.en.md +1138 -0
  121. package/dashboard/public/content/manual/existing-project.en.md +831 -0
  122. package/dashboard/public/content/manual/frontend.en.md +1065 -0
  123. package/dashboard/public/content/manual/fullstack.en.md +1508 -0
  124. package/dashboard/public/content/manual/mobile.en.md +866 -0
  125. package/dashboard/public/index.html +108 -0
  126. package/dashboard/public/style.css +610 -0
  127. package/dashboard/public/vendor/marked.min.js +69 -0
  128. package/dashboard/rtk.js +143 -0
  129. package/dashboard/server-app.js +403 -0
  130. package/dashboard/server.js +104 -0
  131. package/dashboard/test/sprint1.test.js +406 -0
  132. package/dashboard/test/sprint2.test.js +571 -0
  133. package/dashboard/test/sprint3.test.js +560 -0
  134. package/package.json +33 -0
  135. package/scripts/hooks/subagent-telemetry.sh +14 -0
  136. package/scripts/hooks/telemetry-writer.js +250 -0
  137. package/scripts/latest-versions.json +56 -0
@@ -0,0 +1,494 @@
1
+ ---
2
+ stack: typescript/angular-21
3
+ versions_covered: "21.0.x — 21.x"
4
+ last_validated: 2026-05-27
5
+ validated_against: "sandbox — Angular 21.1.0 (current greenfield)"
6
+ status: active
7
+ pack_owner: "@elton"
8
+ security_review: 2026-05-27
9
+ next_review_due: 2027-05-27
10
+ ---
11
+
12
+ # Angular 21 (TypeScript) — greenfield default
13
+
14
+ Knowledge pack for greenfield Angular 21 projects. Default pack for any new frontend started on or after this pack's `last_validated` date. Covers zoneless apps with Signal Forms, Resource API, Server Components, and `@defer` blocks as first-class primitives. For maintained legacy projects on Angular 18 / 19, use the respective packs.
15
+
16
+ ## 1. When to use this pack
17
+
18
+ - Project declares `Primary stack: Angular 21.x` (or `Frontend stack: Angular 21.x`) in `## Project Identity`.
19
+ - `package.json` declares `"@angular/core": "^21.0.0"` (or higher within 21.x).
20
+ - **DEFAULT for NEW projects (greenfield)** — `scaffold` agent selects this pack unless project explicitly pins an older major.
21
+ - For Angular 18.x or 19.x projects in maintenance, use `angular-18.md` / `angular-19.md`. Do NOT use this pack to backport 21-only APIs (Signal Forms stable, Resource API stable, Server Components) onto those versions.
22
+
23
+ ## 2. Stack baseline (what this pack assumes)
24
+
25
+ | Component | Version range | Notes |
26
+ |---|---|---|
27
+ | Angular | 21.0.x — 21.x | Current LTS at pack creation (release ~nov/2025). Standalone is the only supported mode (NgModule deprecated). |
28
+ | Node.js | 22.x LTS | Required by Angular 21 toolchain. Node 20 EOL during 21.x lifetime. |
29
+ | TypeScript | 5.6+ | Strict mode mandatory (`"strict": true`, `"noUncheckedIndexedAccess": true`). |
30
+ | RxJS | 7.8.x | OPTIONAL — Resource API + Signals cover most cases. Keep for HTTP streams and event buses. |
31
+ | UI library | Angular Material 21 (preferred greenfield) OR ng-bootstrap 19.x | Material aligns with M3 tokens and signal-based APIs in 21. |
32
+ | Test framework | Jest 30.x + jest-preset-angular 14 | Karma is removed from `ng new` since 18; Jest is the canonical choice. |
33
+ | Component testing | @testing-library/angular 18 | Signal-aware; works with `resource()` mocks. |
34
+ | E2E | Playwright 1.52+ | `@playwright/test`. Replaces Protractor permanently. |
35
+ | A11y testing | jest-axe 9.x + @axe-core/playwright 4.12+ | Per ADR-007 senior+ gate. |
36
+ | Perf gate | @lhci/cli (Lighthouse CI) | Per ADR-007 thresholds. |
37
+ | Builder | `application` (esbuild) | Default since 18, mandatory in 21. `browser` (webpack) builder removed. |
38
+ | Change detection | **Zoneless** | Default for greenfield. `provideZonelessChangeDetection()` replaces `provideZoneChangeDetection()`. Zone.js NOT installed. |
39
+
40
+ ### Notable maturity vs Angular 19
41
+
42
+ - **Signal Forms STABLE** — preferred over Reactive Forms for greenfield work.
43
+ - **Resource API STABLE** — primary mechanism for declarative data fetching.
44
+ - **`effect()` STABLE** — fully supported outside experimental flag.
45
+ - **`output()` function is default** — `@Output` decorator deprecated; lint warns on use.
46
+ - **Server Components STABLE** — SSR / hybrid rendering first-class.
47
+ - **`linkedSignal()` mature** — derived state with explicit dependency tracking.
48
+ - **`@defer` enriched** — supports `on viewport`, `on interaction`, `on idle`, `on hover`, `on timer`, `prefetch` triggers.
49
+ - **Standalone-only** — NgModule path deprecated; new code rejects NgModule in code review.
50
+
51
+ ## 3. Project structure
52
+
53
+ ```
54
+ frontend/
55
+ ├── src/
56
+ │ ├── app/
57
+ │ │ ├── core/ ← auth, http interceptors, guards, singletons
58
+ │ │ │ ├── auth/
59
+ │ │ │ ├── interceptors/
60
+ │ │ │ └── guards/
61
+ │ │ ├── shared/ ← reusable components, pipes, directives
62
+ │ │ │ ├── components/
63
+ │ │ │ └── pipes/
64
+ │ │ ├── features/ ← lazy-loaded feature areas
65
+ │ │ │ ├── products/
66
+ │ │ │ │ ├── product-list.component.ts
67
+ │ │ │ │ ├── product-list.component.html
68
+ │ │ │ │ ├── product-list.component.scss
69
+ │ │ │ │ ├── product-form.component.ts
70
+ │ │ │ │ ├── product-form.component.html
71
+ │ │ │ │ ├── product-form.component.scss
72
+ │ │ │ │ └── product.service.ts
73
+ │ │ │ └── home/
74
+ │ │ ├── app.config.ts ← bootstrap providers (zoneless, router, http, etc.)
75
+ │ │ ├── app.routes.ts ← root routes (lazy via loadComponent)
76
+ │ │ └── app.component.{ts,html,scss}
77
+ │ ├── environments/
78
+ │ │ ├── environment.ts
79
+ │ │ └── environment.prod.ts
80
+ │ ├── styles/ ← global tokens, themes
81
+ │ │ └── _tokens.scss
82
+ │ ├── main.ts ← bootstrapApplication(AppComponent, appConfig)
83
+ │ └── index.html
84
+ ├── angular.json ← builder: "@angular/build:application" (esbuild)
85
+ ├── tsconfig.json ← strict + noUncheckedIndexedAccess
86
+ ├── jest.config.ts
87
+ ├── playwright.config.ts
88
+ ├── lighthouserc.json
89
+ └── package.json
90
+ ```
91
+
92
+ ## 4. Code patterns
93
+
94
+ ### 4.1 Component file layout (THREE FILES — HARD RULE, NON-NEGOTIABLE)
95
+
96
+ Every component / directive / pipe with a template MUST have three physical files:
97
+
98
+ - `name.component.ts` — logic only, uses `templateUrl` and `styleUrl` / `styleUrls`.
99
+ - `name.component.html` — template.
100
+ - `name.component.scss` — styles.
101
+
102
+ NEVER inline `template:` or `styles:` / `styleUrls: ['...inline...']` inside the `.ts`. No size threshold, no "small component" exception, no presentation-only exception. `code-reviewer` / `tech-lead` reject merges that violate this.
103
+
104
+ ### 4.2 Component with `resource()` (data fetching — greenfield default)
105
+
106
+ ```typescript
107
+ // product-list.component.ts
108
+ import { Component, ChangeDetectionStrategy, inject, resource } from '@angular/core';
109
+ import { CommonModule } from '@angular/common';
110
+ import { firstValueFrom } from 'rxjs';
111
+ import { ProductService } from './product.service';
112
+
113
+ @Component({
114
+ selector: 'app-product-list',
115
+ standalone: true,
116
+ imports: [CommonModule],
117
+ changeDetection: ChangeDetectionStrategy.OnPush,
118
+ templateUrl: './product-list.component.html',
119
+ styleUrl: './product-list.component.scss',
120
+ })
121
+ export class ProductListComponent {
122
+ private svc = inject(ProductService);
123
+
124
+ productsResource = resource({
125
+ loader: () => firstValueFrom(this.svc.list()),
126
+ });
127
+ }
128
+ ```
129
+
130
+ ```html
131
+ <!-- product-list.component.html -->
132
+ @if (productsResource.isLoading()) {
133
+ <p>Loading...</p>
134
+ } @else if (productsResource.error(); as err) {
135
+ <p class="error">Failed to load: {{ err.message }}</p>
136
+ } @else {
137
+ <ul>
138
+ @for (p of productsResource.value(); track p.id) {
139
+ <li>{{ p.name }} — {{ p.price | currency }}</li>
140
+ }
141
+ </ul>
142
+ }
143
+ ```
144
+
145
+ **Rule**: never manage `isLoading` / `error` signals by hand for HTTP calls — `resource()` owns that lifecycle.
146
+
147
+ ### 4.3 Signal Forms (greenfield default — preferred over Reactive Forms)
148
+
149
+ ```typescript
150
+ // product-form.component.ts
151
+ import { Component, ChangeDetectionStrategy, output } from '@angular/core';
152
+ import { signalForm, control } from '@angular/forms';
153
+ import { required, maxLength, min } from '@angular/forms/validators';
154
+
155
+ export interface Product {
156
+ name: string;
157
+ price: number;
158
+ stock: number;
159
+ }
160
+
161
+ @Component({
162
+ selector: 'app-product-form',
163
+ standalone: true,
164
+ changeDetection: ChangeDetectionStrategy.OnPush,
165
+ templateUrl: './product-form.component.html',
166
+ styleUrl: './product-form.component.scss',
167
+ })
168
+ export class ProductFormComponent {
169
+ saved = output<Product>();
170
+
171
+ form = signalForm({
172
+ name: control('', { validators: [required(), maxLength(120)] }),
173
+ price: control(0, { validators: [required(), min(0)] }),
174
+ stock: control(0, { validators: [required(), min(0)] }),
175
+ });
176
+
177
+ submit(): void {
178
+ if (this.form.valid()) {
179
+ this.saved.emit(this.form.value() as Product);
180
+ }
181
+ }
182
+ }
183
+ ```
184
+
185
+ **Rule**: `output()` function instead of `@Output()` decorator. Validators are functions returning `ValidatorFn` — typed end-to-end.
186
+
187
+ ### 4.4 `@defer` for lazy sections
188
+
189
+ ```html
190
+ @defer (on viewport) {
191
+ <app-product-list />
192
+ } @loading (minimum 200ms) {
193
+ <div class="skeleton">Loading list...</div>
194
+ } @placeholder {
195
+ <div>Scroll to see products</div>
196
+ } @error {
197
+ <div class="error">Failed to load the section.</div>
198
+ }
199
+ ```
200
+
201
+ **Rule**: use `@defer` for heavy, viewport-conditional sections. Route-level lazy is `loadComponent` (see 4.6). Combine both for max bundle savings.
202
+
203
+ ### 4.5 Server Component / SSR pattern
204
+
205
+ ```typescript
206
+ // product-detail.component.ts
207
+ import { Component, ChangeDetectionStrategy, input, resource } from '@angular/core';
208
+ import { firstValueFrom } from 'rxjs';
209
+ import { ProductService } from './product.service';
210
+ import { inject } from '@angular/core';
211
+
212
+ @Component({
213
+ selector: 'app-product-detail',
214
+ standalone: true,
215
+ changeDetection: ChangeDetectionStrategy.OnPush,
216
+ templateUrl: './product-detail.component.html',
217
+ styleUrl: './product-detail.component.scss',
218
+ })
219
+ export class ProductDetailComponent {
220
+ id = input.required<string>();
221
+ private svc = inject(ProductService);
222
+
223
+ productResource = resource({
224
+ request: () => ({ id: this.id() }),
225
+ loader: ({ request }) => firstValueFrom(this.svc.byId(request.id)),
226
+ });
227
+ }
228
+ ```
229
+
230
+ **Rule**: signal-based renders are SSR-friendly out of the box. For protected pages, validate the JWT on the server-side render boundary (interceptor running in server env) — never trust client-only auth in hybrid rendering.
231
+
232
+ ### 4.6 Lazy routes (`loadComponent`)
233
+
234
+ ```typescript
235
+ // app.routes.ts
236
+ import { Routes } from '@angular/router';
237
+
238
+ export const routes: Routes = [
239
+ {
240
+ path: '',
241
+ loadComponent: () =>
242
+ import('./features/home/home.component').then(m => m.HomeComponent),
243
+ },
244
+ {
245
+ path: 'products',
246
+ loadComponent: () =>
247
+ import('./features/products/product-list.component').then(m => m.ProductListComponent),
248
+ },
249
+ {
250
+ path: 'products/:id',
251
+ loadComponent: () =>
252
+ import('./features/products/product-detail.component').then(m => m.ProductDetailComponent),
253
+ },
254
+ ];
255
+ ```
256
+
257
+ ### 4.7 HttpClient + functional interceptor
258
+
259
+ ```typescript
260
+ // core/interceptors/auth.interceptor.ts
261
+ import { HttpInterceptorFn } from '@angular/common/http';
262
+ import { inject } from '@angular/core';
263
+ import { AuthService } from '../auth/auth.service';
264
+
265
+ export const authInterceptor: HttpInterceptorFn = (req, next) => {
266
+ const token = inject(AuthService).token();
267
+ if (!token) {
268
+ return next(req);
269
+ }
270
+ return next(req.clone({ setHeaders: { Authorization: `Bearer ${token}` } }));
271
+ };
272
+ ```
273
+
274
+ ```typescript
275
+ // app.config.ts
276
+ import { ApplicationConfig, provideZonelessChangeDetection } from '@angular/core';
277
+ import { provideRouter } from '@angular/router';
278
+ import { provideHttpClient, withInterceptors } from '@angular/common/http';
279
+ import { routes } from './app.routes';
280
+ import { authInterceptor } from './core/interceptors/auth.interceptor';
281
+
282
+ export const appConfig: ApplicationConfig = {
283
+ providers: [
284
+ provideZonelessChangeDetection(),
285
+ provideRouter(routes),
286
+ provideHttpClient(withInterceptors([authInterceptor])),
287
+ ],
288
+ };
289
+ ```
290
+
291
+ **Rule**: zoneless is the default. `provideZoneChangeDetection()` only allowed during legacy migration with explicit ADR.
292
+
293
+ ### 4.8 Signal-based state — immutability
294
+
295
+ ```typescript
296
+ import { signal } from '@angular/core';
297
+
298
+ const items = signal<string[]>([]);
299
+
300
+ // CORRECT — replace reference
301
+ items.update(arr => [...arr, 'new']);
302
+
303
+ // WRONG — mutates underlying array, breaks change detection guarantees
304
+ items().push('new');
305
+ ```
306
+
307
+ ## 5. Testing
308
+
309
+ ### Unit (Jest 30 + Testing Library 18)
310
+
311
+ ```typescript
312
+ // product-list.component.spec.ts
313
+ import { render, screen } from '@testing-library/angular';
314
+ import { of } from 'rxjs';
315
+ import { ProductListComponent } from './product-list.component';
316
+ import { ProductService } from './product.service';
317
+
318
+ describe('ProductListComponent', () => {
319
+ it('renders products from resource', async () => {
320
+ const mockSvc = { list: () => of([{ id: '1', name: 'widget', price: 10 }]) };
321
+ await render(ProductListComponent, {
322
+ providers: [{ provide: ProductService, useValue: mockSvc }],
323
+ });
324
+ expect(await screen.findByText(/widget/)).toBeInTheDocument();
325
+ });
326
+ });
327
+ ```
328
+
329
+ ### Signal Forms unit test
330
+
331
+ ```typescript
332
+ import { ProductFormComponent } from './product-form.component';
333
+
334
+ it('rejects invalid input', () => {
335
+ const c = new ProductFormComponent();
336
+ c.form.controls.name.setValue('');
337
+ c.form.controls.price.setValue(-5);
338
+ expect(c.form.valid()).toBe(false);
339
+ });
340
+ ```
341
+
342
+ ### Accessibility (jest-axe — component level)
343
+
344
+ ```typescript
345
+ import { render } from '@testing-library/angular';
346
+ import { axe, toHaveNoViolations } from 'jest-axe';
347
+ import { ProductFormComponent } from './product-form.component';
348
+
349
+ expect.extend(toHaveNoViolations);
350
+
351
+ it('has no a11y violations', async () => {
352
+ const { container } = await render(ProductFormComponent);
353
+ const results = await axe(container);
354
+ expect(results).toHaveNoViolations();
355
+ });
356
+ ```
357
+
358
+ ### E2E (Playwright + axe)
359
+
360
+ ```typescript
361
+ // e2e/products.spec.ts
362
+ import { test, expect } from '@playwright/test';
363
+ import AxeBuilder from '@axe-core/playwright';
364
+
365
+ test('product list has no critical a11y violations', async ({ page }) => {
366
+ await page.goto('/products');
367
+ await expect(page.getByText(/widget/i)).toBeVisible();
368
+ const accessibilityScanResults = await new AxeBuilder({ page })
369
+ .withTags(['wcag2a', 'wcag2aa'])
370
+ .analyze();
371
+ expect(accessibilityScanResults.violations.filter(v => v.impact === 'critical' || v.impact === 'serious')).toEqual([]);
372
+ });
373
+ ```
374
+
375
+ ### Lighthouse CI (perf gate per ADR-007)
376
+
377
+ `lighthouserc.json` thresholds: performance >= 0.80, LCP <= 2500ms, CLS <= 0.1, TBT <= 300ms — `gate-keeper` fails the build below these.
378
+
379
+ ## 6. Build & run commands
380
+
381
+ ```bash
382
+ npm install
383
+ npm start # esbuild dev server on http://localhost:4200
384
+ npm run build -- --configuration=production # production bundle (esbuild application builder)
385
+ npm test -- --watchAll=false --coverage # Jest with coverage
386
+ npx playwright test # E2E suite
387
+ npx playwright test --ui # interactive runner
388
+ npx lhci autorun # Lighthouse CI against built app
389
+ npx eslint . --max-warnings 0 # zero-warning policy
390
+ npm audit --audit-level=high # CVE scan (0 HIGH, 0 CRITICAL per ADR-007)
391
+ ```
392
+
393
+ ## 7. Security (per ADR-007 + ADR-027 — MANDATORY section)
394
+
395
+ ### 7.1 Authentication & Authorization
396
+
397
+ - **JWT** stored in memory (signal) or httpOnly cookie. NEVER `localStorage` for tokens — XSS risk.
398
+ - **Functional HttpInterceptor** attaches `Authorization: Bearer ...` (see 4.7).
399
+ - **`CanActivate` / `CanMatch` guards** for route-level auth + role checks. Use functional guards (`canActivateFn`).
400
+ - **Server Components**: validate JWT on the server-render boundary; do NOT rely on client-only checks for SSR'd pages.
401
+ - **Refresh tokens** rotated on every use; revoke on logout server-side.
402
+
403
+ ### 7.2 CORS
404
+
405
+ Frontend does not configure CORS — backend owns it. Required: backend declares explicit `Access-Control-Allow-Origin` per environment, never `*` in production. Frontend asserts this in E2E by failing fast if cross-origin preflight fails.
406
+
407
+ ### 7.3 Validation & XSS
408
+
409
+ - Angular template engine **auto-escapes** all interpolation by default — keep it that way.
410
+ - **NEVER** use `[innerHTML]` with untrusted input. If unavoidable, sanitize via `DomSanitizer.bypassSecurityTrustHtml(...)` ONLY after `DOMPurify` cleaning.
411
+ - **Signal Forms validators** are typed — invalid types fail at compile time, shrinking the exploit surface.
412
+ - **URL params** treated as untrusted: validate shape (UUID regex, enum match) before dispatching.
413
+ - **File upload**: enforce MIME + extension allowlist client-side AND server-side; never trust client MIME alone.
414
+
415
+ ### 7.4 Secrets management
416
+
417
+ - NEVER commit secrets in `environment.ts` / `environment.prod.ts`.
418
+ - Build-time env vars injected via CI (`process.env.X` replaced by build tooling); production secrets resolved via Vault / AWS Secrets Manager / Azure Key Vault and injected into the runtime container, not the bundle.
419
+ - Local dev: `.env.local` in `.gitignore` OR `~/.secrets/<project>.env`.
420
+ - API keys for third-party JS SDKs scoped (e.g. Stripe publishable key vs secret key) and origin-restricted in the vendor dashboard.
421
+
422
+ ### 7.5 Rate limiting
423
+
424
+ Backend responsibility (Bucket4j on Spring Boot or equivalent). Frontend MUST:
425
+ - Disable submit buttons during in-flight requests (`resource().isLoading()` driven).
426
+ - Surface `429 Too Many Requests` with a respectful retry UI (countdown + retry-after).
427
+ - Never bypass via parallel requests — orchestrate sequentially when the endpoint declares it.
428
+
429
+ ### 7.6 OWASP Top 10 mapping
430
+
431
+ | OWASP | Mitigation in this stack |
432
+ |---|---|
433
+ | A01 Broken Access Control | `CanActivate` / `CanMatch` guards + role check; server enforces same — frontend is convenience, never the source of truth |
434
+ | A02 Cryptographic Failures | HTTPS only (HSTS via backend); JWT signed RS256; Subresource Integrity (SRI) hashes on any CDN-loaded script |
435
+ | A03 Injection / XSS | Template auto-escape; ban `[innerHTML]` with untrusted input; sanitize via DOMPurify + DomSanitizer when unavoidable |
436
+ | A04 Insecure Design | Threat model documented per feature; Server Component boundaries explicit in ADR |
437
+ | A05 Security Misconfiguration | CSP strict (no `unsafe-inline`, no `unsafe-eval`); `X-Frame-Options: DENY`; debug builds blocked from prod via env check |
438
+ | A06 Vulnerable Components | `npm audit --audit-level=high` in CI; Renovate / Dependabot weekly bumps; npm provenance enforced for critical deps |
439
+ | A07 Auth Failures | Backend rate limit + account lockout; frontend disables submit during request; MFA UI for critical flows |
440
+ | A08 Data Integrity | SRI hashes on CDN scripts; supply chain via npm provenance + SBOM (CycloneDX); lockfile committed and verified in CI |
441
+ | A09 Logging Failures | Browser logs scrubbed for PII; correlation ID propagated via header from interceptor; never log tokens or full request bodies |
442
+ | A10 SSRF | N/A for pure client; Server Components with `fetch()` MUST validate target URL against allowlist before request |
443
+
444
+ ### 7.7 LGPD / GDPR specifics
445
+
446
+ - **Opt-in cookies** — banner gates non-essential cookies; user choice persisted and respected by analytics modules (lazy-load analytics only after consent).
447
+ - **PII tagging** — mark fields containing PII (email, CPF, phone) with a const map; logging interceptor strips tagged fields before sending to observability sink.
448
+ - **Data subject access** — UI route `/account/data-export` triggers backend export; `account/delete` triggers soft-delete + audit log.
449
+ - **Consent versioning** — track which consent version the user accepted; re-prompt on material policy changes.
450
+
451
+ ## 8. Anti-patterns (block in code-review)
452
+
453
+ | Bad | Good | Why |
454
+ |---|---|---|
455
+ | Inline `template:` / `styles:` in `.ts` | Three separate files (`.ts` + `.html` + `.scss`) | HARD rule — non-negotiable, overrides any prior threshold-based policy |
456
+ | `NgModule` | `standalone: true` components | NgModule deprecated in 21; new code must be standalone |
457
+ | `any` in TypeScript | Explicit type or `unknown` + narrow | Strict mode mandatory — `any` defeats the type system |
458
+ | `@Output() event = new EventEmitter()` | `event = output<T>()` | Decorator is deprecated; function form is typed and SSR-friendly |
459
+ | Reactive Forms in new code | Signal Forms (`signalForm({...})`) | Signal Forms are stable in 21 and aligned with the rest of the stack |
460
+ | Manual `isLoading` signal for HTTP | `resource({ loader })` | `resource()` owns lifecycle, error, refetch, abort |
461
+ | `provideZoneChangeDetection()` in new project | `provideZonelessChangeDetection()` | Zoneless is the greenfield default |
462
+ | `items().push(x)` (mutation) | `items.update(arr => [...arr, x])` | Mutation breaks signal change tracking |
463
+ | `[innerHTML]="userInput"` | DomSanitizer + DOMPurify | XSS vector |
464
+ | `ChangeDetectionStrategy.Default` | `ChangeDetectionStrategy.OnPush` | OnPush is mandatory — Default re-renders entire tree |
465
+ | Routes-only lazy loading for heavy panels | `@defer (on viewport)` for sections, `loadComponent` for routes | Combine both for max bundle savings |
466
+ | `browser` (webpack) builder | `application` (esbuild) builder | Webpack builder removed; esbuild is mandatory |
467
+ | `localStorage.setItem('token', ...)` | In-memory signal OR httpOnly cookie | localStorage is XSS-accessible |
468
+ | `@Autowired`-style field assignment via constructor without `inject()` for new code | `inject(Service)` | Functional `inject()` is idiomatic and works in interceptors/guards |
469
+ | Eager loading every feature in `app.routes.ts` | `loadComponent: () => import(...)` | Bundle bloat — eager routes break LCP |
470
+
471
+ ## 9. Migration hints — Angular 21 → 22 (future)
472
+
473
+ When `angular-22.md` lands, expect (subject to actual release notes):
474
+
475
+ - TypeScript 5.7+ baseline.
476
+ - Further evolutions of Server Components (likely streaming improvements and more `use server` directives).
477
+ - Additional signal-based primitives (e.g. richer `linkedSignal`, async signals).
478
+ - Possible deprecation of remaining Zone.js compat shims.
479
+ - Stricter ESLint rules around `@Output` (likely removal) and NgModule (likely removal).
480
+ - Use `migrator` agent — it knows both packs and proposes an ADR per cross.
481
+
482
+ ## 10. References
483
+
484
+ - ADR-007 (Senior+ gate thresholds — a11y, Lighthouse, pyramid)
485
+ - ADR-026 (Generic agents + stack packs architecture)
486
+ - ADR-027 (Pack governance — frontmatter + security + CODEOWNERS)
487
+ - ADR-029 (Canonical pack format — 10 sections)
488
+ - Angular 21 release notes: https://blog.angular.dev/
489
+ - Angular Update Guide: https://angular.dev/update-guide
490
+ - Signal Forms spec: https://angular.dev/guide/signal-forms
491
+ - Resource API spec: https://angular.dev/guide/signals/resource
492
+ - Server Components spec: https://angular.dev/guide/ssr
493
+ - Lighthouse CI: https://github.com/GoogleChrome/lighthouse-ci
494
+ - OWASP Top 10 (2021): https://owasp.org/Top10/