@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,420 @@
1
+ ---
2
+ stack: typescript/angular-18
3
+ versions_covered: "18.0.x — 18.2.x"
4
+ last_validated: 2026-05-27
5
+ validated_against: "sandbox — Angular 18.2.5 (legacy maintenance)"
6
+ status: active
7
+ pack_owner: "@elton"
8
+ security_review: 2026-05-27
9
+ next_review_due: 2027-05-27
10
+ ---
11
+
12
+ # Angular 18
13
+
14
+ Knowledge pack for projects on Angular 18.x (released May 2024). Covers projects in legacy maintenance that have not yet migrated to Angular 19 (`angular-19.md`) or the harness default Angular 21 (`angular-21.md`). Standalone-first, Signals stable, new control flow stable; NO Signal Forms (only in 19+), NO Resource API (only in 19+).
15
+
16
+ ## 1. When to use this pack
17
+
18
+ - Project declares `Frontend stack: Angular 18.x` in CLAUDE.md `## Project Identity`.
19
+ - `frontend/package.json` declares `@angular/core: ^18.0.0` through `^18.2.x`.
20
+ - **Greenfield: prefer `angular-21.md`** (harness default). For Angular 19 use `angular-19.md`.
21
+ - This pack exists for **maintained legacy** — apps in production not yet migrated.
22
+
23
+ ## 2. Stack baseline (what this pack assumes)
24
+
25
+ | Component | Version range | Notes |
26
+ |---|---|---|
27
+ | Angular | 18.0.x — 18.2.x | Released May 2024; LTS window short — plan upgrade |
28
+ | Node | 20.11+ (LTS) | Node 22 also supported |
29
+ | TypeScript | 5.4.x | Strict mode mandatory |
30
+ | RxJS | 7.8.x | Still required for HTTP / streams |
31
+ | ng-bootstrap | 17.x | NOT all components yet Signal-native |
32
+ | Jest | 29.x via jest-preset-angular 14 | Karma deprecated |
33
+ | @testing-library/angular | 17.x | Render API + userEvent |
34
+ | Playwright | 1.45+ | E2E + Chrome DevTools Protocol |
35
+ | @axe-core/playwright | 4.10+ | A11y on E2E flows |
36
+ | jest-axe | 9.x | A11y per component |
37
+ | @lhci/cli | 0.14+ | Lighthouse CI thresholds per ADR-007 |
38
+ | Zone.js | 0.14.x (still required) | Zoneless is EXPERIMENTAL in 18 — do NOT enable in prod |
39
+
40
+ **Key differences vs Angular 21:**
41
+ - Standalone is default, BUT NgModules still work (legacy apps may mix).
42
+ - Signals stable, but `effect()` still in **developer preview**.
43
+ - New control flow (`@if`, `@for`, `@switch`) stable since 17, mature in 18.
44
+ - **NO Signal Forms** — must use Reactive Forms (typed FormGroup).
45
+ - **NO Resource API** — must use RxJS + signal bridge (`toSignal`).
46
+ - `output()` function still optional — `@Output()` decorator still common.
47
+ - `inject()` preferred over constructor injection but both valid.
48
+
49
+ ## 3. Project structure
50
+
51
+ ```
52
+ frontend/
53
+ ├── src/app/
54
+ │ ├── core/ ← auth, http interceptors, guards, singletons
55
+ │ │ ├── auth/
56
+ │ │ ├── interceptors/
57
+ │ │ └── guards/
58
+ │ ├── shared/ ← reusable standalone components, pipes, directives
59
+ │ ├── features/ ← lazy-loaded feature folders
60
+ │ │ └── products/
61
+ │ │ ├── product-list.component.ts
62
+ │ │ ├── product-list.component.html
63
+ │ │ ├── product-list.component.scss
64
+ │ │ └── product.service.ts
65
+ │ ├── app.config.ts ← root ApplicationConfig (providers)
66
+ │ ├── app.routes.ts ← root routing with loadComponent
67
+ │ └── app.component.{ts,html,scss}
68
+ ├── src/environments/
69
+ │ ├── environment.ts
70
+ │ └── environment.prod.ts
71
+ ├── src/styles/
72
+ └── angular.json
73
+ ```
74
+
75
+ ## 4. Code patterns
76
+
77
+ ### THREE SEPARATE FILES (HARD RULE — harness universal)
78
+
79
+ Every component WITH a template uses 3 physical files: `name.component.ts`, `name.component.html`, `name.component.scss`. **NEVER** inline `template:` or `styles:` / `styleUrls: ['...inline...']` in the `.ts`. No size threshold, no exception. Violation = hard block in `code-reviewer` / `tech-lead`.
80
+
81
+ ### Standalone component with Signal state
82
+
83
+ ```typescript
84
+ // product-list.component.ts
85
+ import { Component, signal, ChangeDetectionStrategy, inject, OnInit } from '@angular/core';
86
+ import { CommonModule } from '@angular/common';
87
+ import { ProductService, Product } from './product.service';
88
+
89
+ @Component({
90
+ selector: 'app-product-list',
91
+ standalone: true,
92
+ imports: [CommonModule],
93
+ changeDetection: ChangeDetectionStrategy.OnPush,
94
+ templateUrl: './product-list.component.html',
95
+ styleUrl: './product-list.component.scss',
96
+ })
97
+ export class ProductListComponent implements OnInit {
98
+ private svc = inject(ProductService);
99
+
100
+ readonly products = signal<Product[]>([]);
101
+ readonly loading = signal(false);
102
+ readonly error = signal<string | null>(null);
103
+
104
+ ngOnInit(): void {
105
+ this.loading.set(true);
106
+ this.svc.list().subscribe({
107
+ next: (data) => { this.products.set(data); this.loading.set(false); },
108
+ error: (e) => { this.error.set(e.message); this.loading.set(false); },
109
+ });
110
+ }
111
+ }
112
+ ```
113
+
114
+ **Rule**: `inject()` over constructor injection; `OnPush` always; `signal()` for local state; never expose `WritableSignal` publicly — wrap or use `asReadonly()`.
115
+
116
+ ### Template using new control flow
117
+
118
+ ```html
119
+ <!-- product-list.component.html -->
120
+ @if (loading()) {
121
+ <p>Loading...</p>
122
+ } @else if (error()) {
123
+ <p class="error">{{ error() }}</p>
124
+ } @else {
125
+ <ul>
126
+ @for (p of products(); track p.id) {
127
+ <li>{{ p.name }} — {{ p.price | currency }}</li>
128
+ } @empty {
129
+ <li>No products.</li>
130
+ }
131
+ </ul>
132
+ }
133
+ ```
134
+
135
+ **Rule**: prefer `@if` / `@for` / `@switch` over `*ngIf` / `*ngFor` (faster, type-narrowing, no extra imports). `track` is **mandatory** on `@for`.
136
+
137
+ ### Reactive Forms (Signal Forms only in 19+)
138
+
139
+ ```typescript
140
+ // product-form.component.ts
141
+ import { FormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
142
+
143
+ @Component({
144
+ selector: 'app-product-form',
145
+ standalone: true,
146
+ imports: [ReactiveFormsModule],
147
+ changeDetection: ChangeDetectionStrategy.OnPush,
148
+ templateUrl: './product-form.component.html',
149
+ styleUrl: './product-form.component.scss',
150
+ })
151
+ export class ProductFormComponent {
152
+ private fb = inject(FormBuilder);
153
+
154
+ readonly form = this.fb.nonNullable.group({
155
+ name: ['', [Validators.required, Validators.maxLength(120)]],
156
+ email: ['', [Validators.required, Validators.email]],
157
+ price: [0, [Validators.required, Validators.min(0)]],
158
+ });
159
+
160
+ submit(): void {
161
+ if (this.form.invalid) { this.form.markAllAsTouched(); return; }
162
+ const payload = this.form.getRawValue();
163
+ // ...dispatch
164
+ }
165
+ }
166
+ ```
167
+
168
+ **Rule**: always `fb.nonNullable.group(...)` for typed forms; never `any`; mark all touched on invalid submit.
169
+
170
+ ### Lazy routing
171
+
172
+ ```typescript
173
+ // app.routes.ts
174
+ import { Routes } from '@angular/router';
175
+
176
+ export const routes: Routes = [
177
+ { path: '', pathMatch: 'full', redirectTo: 'products' },
178
+ {
179
+ path: 'products',
180
+ loadComponent: () =>
181
+ import('./features/products/product-list.component').then(m => m.ProductListComponent),
182
+ },
183
+ {
184
+ path: 'admin',
185
+ canMatch: [adminGuard],
186
+ loadChildren: () => import('./features/admin/admin.routes').then(m => m.routes),
187
+ },
188
+ ];
189
+ ```
190
+
191
+ **Rule**: every feature lazy-loaded via `loadComponent` or `loadChildren`. Never eager-import features into root.
192
+
193
+ ### HttpClient + functional interceptor
194
+
195
+ ```typescript
196
+ // core/interceptors/auth.interceptor.ts
197
+ import { HttpInterceptorFn } from '@angular/common/http';
198
+ import { inject } from '@angular/core';
199
+ import { AuthService } from '../auth/auth.service';
200
+
201
+ export const authInterceptor: HttpInterceptorFn = (req, next) => {
202
+ const token = inject(AuthService).token();
203
+ if (!token) return next(req);
204
+ return next(req.clone({ setHeaders: { Authorization: `Bearer ${token}` } }));
205
+ };
206
+
207
+ // app.config.ts
208
+ import { ApplicationConfig } from '@angular/core';
209
+ import { provideHttpClient, withInterceptors } from '@angular/common/http';
210
+ import { provideRouter, withComponentInputBinding } from '@angular/router';
211
+
212
+ export const appConfig: ApplicationConfig = {
213
+ providers: [
214
+ provideHttpClient(withInterceptors([authInterceptor])),
215
+ provideRouter(routes, withComponentInputBinding()),
216
+ ],
217
+ };
218
+ ```
219
+
220
+ **Rule**: functional interceptors (`HttpInterceptorFn`) over class-based; typed `HttpClient<T>`; never raw `fetch()` in Angular code.
221
+
222
+ ### Bridging RxJS to Signals (`toSignal`)
223
+
224
+ ```typescript
225
+ import { toSignal } from '@angular/core/rxjs-interop';
226
+
227
+ readonly user = toSignal(this.authService.user$, { initialValue: null });
228
+ ```
229
+
230
+ **Rule**: prefer `toSignal` to bridge existing `Observable` services into Signal-based components.
231
+
232
+ ## 5. Testing
233
+
234
+ ### Unit + component (Jest + Testing Library)
235
+
236
+ ```typescript
237
+ import { render, screen } from '@testing-library/angular';
238
+ import userEvent from '@testing-library/user-event';
239
+ import { ProductListComponent } from './product-list.component';
240
+
241
+ describe('ProductListComponent', () => {
242
+ test('renders product names', async () => {
243
+ await render(ProductListComponent, {
244
+ providers: [{ provide: ProductService, useValue: { list: () => of([{ id: '1', name: 'widget', price: 10 }]) } }],
245
+ });
246
+ expect(await screen.findByText('widget')).toBeInTheDocument();
247
+ });
248
+
249
+ test('shows error on failure', async () => {
250
+ await render(ProductListComponent, {
251
+ providers: [{ provide: ProductService, useValue: { list: () => throwError(() => new Error('boom')) } }],
252
+ });
253
+ expect(await screen.findByText(/boom/)).toBeInTheDocument();
254
+ });
255
+ });
256
+ ```
257
+
258
+ ### A11y per component (jest-axe — ADR-007)
259
+
260
+ ```typescript
261
+ import { axe, toHaveNoViolations } from 'jest-axe';
262
+ expect.extend(toHaveNoViolations);
263
+
264
+ test('no a11y violations', async () => {
265
+ const { container } = await render(ProductListComponent, { providers: [/* ... */] });
266
+ expect(await axe(container)).toHaveNoViolations();
267
+ });
268
+ ```
269
+
270
+ ### E2E (Playwright) + @axe-core/playwright
271
+
272
+ ```typescript
273
+ import { test, expect } from '@playwright/test';
274
+ import AxeBuilder from '@axe-core/playwright';
275
+
276
+ test('product list is accessible', async ({ page }) => {
277
+ await page.goto('/products');
278
+ await expect(page.getByRole('list')).toBeVisible();
279
+ const results = await new AxeBuilder({ page }).analyze();
280
+ const serious = results.violations.filter(v => v.impact === 'serious' || v.impact === 'critical');
281
+ expect(serious).toEqual([]);
282
+ });
283
+ ```
284
+
285
+ ### Lighthouse CI
286
+
287
+ `lighthouserc.json` thresholds per ADR-007: performance ≥ 0.80, LCP ≤ 2500ms, CLS ≤ 0.1, TBT ≤ 300ms.
288
+
289
+ ## 6. Build & run commands
290
+
291
+ ```bash
292
+ npm install
293
+ npm start # ng serve, port 4200
294
+ npm run build -- --configuration=production # prod build (AOT + budget)
295
+ npm test -- --watchAll=false --coverage # Jest with coverage
296
+ npx playwright test # E2E
297
+ npx playwright test --ui # E2E debug
298
+ npx lhci autorun # Lighthouse CI thresholds
299
+ npm run lint # ESLint --max-warnings 0
300
+ npm audit --audit-level=high # CVE scan
301
+ ```
302
+
303
+ ## 7. Security (per ADR-007 + ADR-027 — MANDATORY)
304
+
305
+ ### 7.1 Authentication & Authorization
306
+
307
+ - **JWT validation** via functional `HttpInterceptorFn` injecting Authorization header.
308
+ - **Route guards**: `CanActivate` / `CanMatch` functional guards with `inject()`.
309
+ - **Token storage**: prefer `HttpOnly Secure SameSite=Lax` cookies (set by backend). Fall back to `sessionStorage` for short-lived tokens; NEVER `localStorage` for tokens (XSS-readable).
310
+ - **Refresh token**: rotate on every use; revoke on logout.
311
+
312
+ ```typescript
313
+ export const authGuard: CanMatchFn = () => {
314
+ const auth = inject(AuthService);
315
+ const router = inject(Router);
316
+ return auth.isAuthenticated() ? true : router.parseUrl('/login');
317
+ };
318
+ ```
319
+
320
+ ### 7.2 CORS
321
+
322
+ CORS is backend-side. Frontend uses absolute URLs from `environment.apiBaseUrl`. Backend MUST explicit-list `https://app.example.com` — NEVER `*` in production. Reference companion backend pack.
323
+
324
+ ### 7.3 Validation & XSS
325
+
326
+ - Angular **auto-escapes** by default in interpolation (`{{ }}`) and property binding.
327
+ - **NEVER use `[innerHTML]`** with user content unless sanitized via `DomSanitizer.sanitize(SecurityContext.HTML, value)`.
328
+ - **NEVER use `bypassSecurityTrustHtml`** unless absolutely necessary + reviewed by `security-engineer`.
329
+ - Reactive Forms validators on every input. Backend re-validates (defense in depth).
330
+ - File uploads: validate MIME type + size client-side AND server-side.
331
+
332
+ ### 7.4 Secrets management
333
+
334
+ - **NEVER** commit secrets in `environment.ts` / `environment.prod.ts`.
335
+ - Use build-time substitution: `environment.prod.ts` reads from CI env vars (`API_BASE_URL`, etc.) injected during `ng build`.
336
+ - Public values only (API base URL, feature flags). API keys, tokens, secrets stay server-side.
337
+ - Per-developer config: `environment.local.ts` in `.gitignore`.
338
+
339
+ ### 7.5 Rate limiting
340
+
341
+ Frontend cannot enforce rate limiting — it lives in backend / API gateway. Frontend MUST handle 429 responses gracefully:
342
+
343
+ ```typescript
344
+ catchError((err: HttpErrorResponse) => {
345
+ if (err.status === 429) {
346
+ this.toast.warning(`Too many requests. Retry after ${err.headers.get('Retry-After')}s.`);
347
+ }
348
+ return throwError(() => err);
349
+ })
350
+ ```
351
+
352
+ ### 7.6 OWASP Top 10 mapping
353
+
354
+ | OWASP | Mitigation in Angular 18 |
355
+ |---|---|
356
+ | A01 Broken Access Control | Route guards (`CanMatch`/`CanActivate`); UI gating + backend re-check; never trust client RBAC alone |
357
+ | A02 Cryptographic Failures | HTTPS only (HSTS via backend); JWT signed RS256; NEVER store tokens in `localStorage` |
358
+ | A03 Injection | Angular auto-escape; `DomSanitizer` for trusted HTML; never string-build URLs without `encodeURIComponent` |
359
+ | A04 Insecure Design | Threat model per feature; PII flows reviewed by `security-engineer` |
360
+ | A05 Security Misconfiguration | Strict CSP via meta tag or backend header; `Content-Security-Policy: default-src 'self'` |
361
+ | A06 Vulnerable Components | `npm audit --audit-level=high` in CI; Renovate/Dependabot; remove unused deps |
362
+ | A07 Auth Failures | Short-lived access tokens; refresh rotation; auto-logout on 401; MFA flow |
363
+ | A08 Data Integrity | Subresource Integrity (SRI) on external scripts; webpack output integrity hashes |
364
+ | A09 Logging Failures | Never `console.log` PII or tokens; production builds strip `console.*` via `terserOptions.compress.drop_console` |
365
+ | A10 SSRF | N/A frontend; backend enforces outbound allowlist |
366
+
367
+ ### 7.7 LGPD / GDPR specifics
368
+
369
+ - **Cookie consent**: opt-in banner before any non-essential cookie (analytics, ads). Default reject.
370
+ - **PII in logs**: never `console.log` user objects; redact email/CPF/phone via a logging wrapper.
371
+ - **Data subject access**: page that requests user data export from backend; render machine-readable JSON download.
372
+ - **Right to deletion**: UI to request deletion; backend soft-deletes + audits.
373
+ - **Analytics**: anonymize IP; respect `Do Not Track` header.
374
+
375
+ ## 8. Anti-patterns (block in code-review)
376
+
377
+ | Bad | Good | Why |
378
+ |---|---|---|
379
+ | `template:` inline | separate `.html` via `templateUrl` | HARD RULE — harness universal |
380
+ | `styles:` inline | separate `.scss` via `styleUrl` | HARD RULE — harness universal |
381
+ | NgModule for new feature | standalone component | Angular 18 is standalone-first |
382
+ | `any` in TS | explicit type or generic | TS strict mode mandatory |
383
+ | field injection (`@Inject()` on property) | `inject()` in field initializer OR constructor | testable + immutable |
384
+ | `ChangeDetectionStrategy.Default` | `OnPush` always | perf + predictability |
385
+ | `signal().push(item)` mutation | `signal.update(arr => [...arr, item])` | signals require new ref |
386
+ | `[innerHTML]="userContent"` | `DomSanitizer.sanitize(SecurityContext.HTML, value)` | XSS vector |
387
+ | `localStorage.setItem('token', ...)` | HttpOnly cookie OR `sessionStorage` short-lived | XSS-readable storage |
388
+ | `*ngIf` / `*ngFor` in new code | `@if` / `@for` | Faster + type-narrowing |
389
+ | `@for` without `track` | `@for (... ; track item.id)` | Compile error in 18, mandatory |
390
+ | Reactive Forms without `nonNullable` | `fb.nonNullable.group(...)` | typed forms, no `null \| undefined` |
391
+ | `Subject` without `takeUntilDestroyed()` | `takeUntilDestroyed()` (Angular 16+) | memory leaks |
392
+ | direct `fetch()` call | `HttpClient` typed | misses interceptors, retries, error handling |
393
+ | eager-loaded feature in root | `loadComponent` / `loadChildren` lazy | bundle size |
394
+ | Zoneless enabled in prod | keep Zone.js | zoneless is EXPERIMENTAL in 18 |
395
+
396
+ ## 9. Migration hints — Angular 18 → 19
397
+
398
+ When migrating a project from this pack to `angular-19.md`:
399
+
400
+ - **Signal Forms** arrive — alternative to Reactive Forms (opt-in, not breaking).
401
+ - **Resource API** (`resource()`) — async data loading as a signal, replaces many `toSignal(httpService.get(...))` patterns.
402
+ - **`linkedSignal()`** — derived state that can be reset, replaces some `computed` + `effect` patterns.
403
+ - **`effect()` goes stable** — safe to use broadly.
404
+ - **`output()` function** preferred over `@Output()` decorator (cleaner API, no decorator metadata).
405
+ - **Incremental hydration** for SSR — opt-in via `withIncrementalHydration()`.
406
+ - Zoneless leaves dev-preview in 19 — still opt-in; evaluate carefully.
407
+ - **Hot Module Replacement** for templates becomes stable.
408
+ - Use `migrator` agent — it knows both packs and proposes ADR per major cross.
409
+ - Run `ng update @angular/core@19 @angular/cli@19` and review the generated migration log.
410
+
411
+ ## 10. References
412
+
413
+ - ADR-007 (Senior+ gate thresholds — a11y, Lighthouse, pyramid)
414
+ - ADR-026 (Generic agents + packs architecture)
415
+ - ADR-027 (Pack governance — frontmatter, security review cadence)
416
+ - ADR-029 (Canonical pack format)
417
+ - Angular 18 release notes: https://blog.angular.dev/angular-v18-is-now-available-e79d5ac0affe
418
+ - Angular Update Guide: https://angular.dev/update-guide
419
+ - OWASP Top 10 (2021): https://owasp.org/Top10/
420
+ - ng-bootstrap 17 docs: https://ng-bootstrap.github.io/