ruvnet-kb-first 5.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 (52) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +674 -0
  3. package/SKILL.md +740 -0
  4. package/bin/kb-first.js +123 -0
  5. package/install/init-project.sh +435 -0
  6. package/install/install-global.sh +257 -0
  7. package/install/kb-first-autodetect.sh +108 -0
  8. package/install/kb-first-command.md +80 -0
  9. package/install/kb-first-skill.md +262 -0
  10. package/package.json +87 -0
  11. package/phases/00-assessment.md +529 -0
  12. package/phases/01-storage.md +194 -0
  13. package/phases/01.5-hooks-setup.md +521 -0
  14. package/phases/02-kb-creation.md +413 -0
  15. package/phases/03-persistence.md +125 -0
  16. package/phases/04-visualization.md +170 -0
  17. package/phases/05-integration.md +114 -0
  18. package/phases/06-scaffold.md +130 -0
  19. package/phases/07-build.md +493 -0
  20. package/phases/08-verification.md +597 -0
  21. package/phases/09-security.md +512 -0
  22. package/phases/10-documentation.md +613 -0
  23. package/phases/11-deployment.md +670 -0
  24. package/phases/testing.md +713 -0
  25. package/scripts/1.5-hooks-verify.sh +252 -0
  26. package/scripts/8.1-code-scan.sh +58 -0
  27. package/scripts/8.2-import-check.sh +42 -0
  28. package/scripts/8.3-source-returns.sh +52 -0
  29. package/scripts/8.4-startup-verify.sh +65 -0
  30. package/scripts/8.5-fallback-check.sh +63 -0
  31. package/scripts/8.6-attribution.sh +56 -0
  32. package/scripts/8.7-confidence.sh +56 -0
  33. package/scripts/8.8-gap-logging.sh +70 -0
  34. package/scripts/9-security-audit.sh +202 -0
  35. package/scripts/init-project.sh +395 -0
  36. package/scripts/verify-enforcement.sh +167 -0
  37. package/src/commands/hooks.js +361 -0
  38. package/src/commands/init.js +315 -0
  39. package/src/commands/phase.js +372 -0
  40. package/src/commands/score.js +380 -0
  41. package/src/commands/status.js +193 -0
  42. package/src/commands/verify.js +286 -0
  43. package/src/index.js +56 -0
  44. package/src/mcp-server.js +412 -0
  45. package/templates/attention-router.ts +534 -0
  46. package/templates/code-analysis.ts +683 -0
  47. package/templates/federated-kb-learner.ts +649 -0
  48. package/templates/gnn-engine.ts +1091 -0
  49. package/templates/intentions.md +277 -0
  50. package/templates/kb-client.ts +905 -0
  51. package/templates/schema.sql +303 -0
  52. package/templates/sona-config.ts +312 -0
@@ -0,0 +1,713 @@
1
+ Updated: 2026-01-01 20:10:00 EST | Version 1.0.0
2
+ Created: 2026-01-01 20:10:00 EST
3
+
4
+ # Testing Strategy for KB-First Applications
5
+
6
+ ## Purpose
7
+
8
+ Comprehensive testing ensures your KB-First application:
9
+ - Returns accurate, source-backed responses
10
+ - Handles edge cases gracefully
11
+ - Maintains KB integrity over time
12
+ - Meets performance requirements
13
+
14
+ ---
15
+
16
+ ## Testing Pyramid for KB-First
17
+
18
+ ```
19
+ ┌─────────────────┐
20
+ │ E2E Tests │ ← Few, slow, high confidence
21
+ │ (10-20) │
22
+ ├─────────────────┤
23
+ │ Integration │ ← Medium count
24
+ │ Tests (50-100) │
25
+ ├─────────────────┤
26
+ │ KB Quality │ ← KB-specific layer
27
+ │ Tests (100+) │
28
+ ├─────────────────┤
29
+ │ Unit Tests │ ← Many, fast, isolated
30
+ │ (200-500) │
31
+ └─────────────────┘
32
+ ```
33
+
34
+ ---
35
+
36
+ ## Test Categories
37
+
38
+ | Category | Purpose | When to Run | Target |
39
+ |----------|---------|-------------|--------|
40
+ | Unit | Individual functions | Every commit | 200+ tests |
41
+ | KB Quality | Search accuracy, coverage | Daily | 100+ queries |
42
+ | Integration | API + KB + Domain | Every PR | 50+ scenarios |
43
+ | E2E | Full user flows | Before deploy | 10-20 journeys |
44
+ | Performance | Response times | Weekly | <500ms p95 |
45
+ | Regression | Prevent regressions | Every release | All critical paths |
46
+
47
+ ---
48
+
49
+ ## 1. Unit Testing
50
+
51
+ ### What to Test
52
+
53
+ ```typescript
54
+ // src/domain/__tests__/retirement.test.ts
55
+
56
+ import { calculateWithdrawal } from '../retirement';
57
+ import { kb } from '../../kb';
58
+
59
+ // Mock KB for unit tests
60
+ jest.mock('../../kb');
61
+
62
+ describe('calculateWithdrawal', () => {
63
+ beforeEach(() => {
64
+ (kb.search as jest.Mock).mockResolvedValue({
65
+ value: 0.04,
66
+ sources: [{
67
+ nodeId: 'wr-001',
68
+ expert: 'William Bengen',
69
+ confidence: 0.95,
70
+ url: 'https://example.com/bengen-1994'
71
+ }]
72
+ });
73
+ });
74
+
75
+ test('returns withdrawal amount with sources', async () => {
76
+ const result = await calculateWithdrawal(1000000);
77
+
78
+ expect(result.data.amount).toBe(40000);
79
+ expect(result.kbSources).toHaveLength(1);
80
+ expect(result.kbSources[0].expert).toBe('William Bengen');
81
+ });
82
+
83
+ test('throws when KB unavailable', async () => {
84
+ (kb.search as jest.Mock).mockRejectedValue(new Error('KB unavailable'));
85
+
86
+ await expect(calculateWithdrawal(1000000)).rejects.toThrow('KB unavailable');
87
+ });
88
+
89
+ test('never returns hardcoded values', async () => {
90
+ // Even if KB returns unexpected format, don't fall back to hardcoded
91
+ (kb.search as jest.Mock).mockResolvedValue({ value: null, sources: [] });
92
+
93
+ await expect(calculateWithdrawal(1000000)).rejects.toThrow();
94
+ });
95
+ });
96
+ ```
97
+
98
+ ### Unit Test Requirements
99
+
100
+ ```
101
+ [ ] Every domain function has tests
102
+ [ ] KB is mocked in unit tests
103
+ [ ] Tests verify kbSources are returned
104
+ [ ] Tests verify no hardcoded fallbacks
105
+ [ ] Tests cover error paths
106
+ [ ] Coverage target: ≥90%
107
+ ```
108
+
109
+ ---
110
+
111
+ ## 2. KB Quality Testing
112
+
113
+ ### 2.1 Search Accuracy Tests
114
+
115
+ ```typescript
116
+ // tests/kb-quality/search-accuracy.test.ts
117
+
118
+ describe('KB Search Accuracy', () => {
119
+ const testQueries = [
120
+ {
121
+ query: 'safe withdrawal rate retirement',
122
+ expectedTopics: ['4% rule', 'Bengen', 'withdrawal'],
123
+ minConfidence: 0.7
124
+ },
125
+ {
126
+ query: 'social security claiming age',
127
+ expectedTopics: ['62', '67', 'FRA', 'delayed credits'],
128
+ minConfidence: 0.7
129
+ },
130
+ {
131
+ query: 'Roth conversion strategy',
132
+ expectedTopics: ['tax bracket', 'IRMAA', 'conversion ladder'],
133
+ minConfidence: 0.6
134
+ }
135
+ ];
136
+
137
+ testQueries.forEach(({ query, expectedTopics, minConfidence }) => {
138
+ test(`"${query}" returns relevant results`, async () => {
139
+ const results = await kb.search(query);
140
+
141
+ // Must return results
142
+ expect(results.length).toBeGreaterThan(0);
143
+
144
+ // Top result must have minimum confidence
145
+ expect(results[0].confidence).toBeGreaterThanOrEqual(minConfidence);
146
+
147
+ // Results must contain expected topics
148
+ const content = results.map(r => r.content.toLowerCase()).join(' ');
149
+ const foundTopics = expectedTopics.filter(t =>
150
+ content.includes(t.toLowerCase())
151
+ );
152
+ expect(foundTopics.length).toBeGreaterThanOrEqual(1);
153
+
154
+ // All results must have sources
155
+ results.forEach(r => {
156
+ expect(r.sourceExpert).toBeDefined();
157
+ expect(r.sourceExpert).not.toBe('');
158
+ });
159
+ });
160
+ });
161
+ });
162
+ ```
163
+
164
+ ### 2.2 Coverage Tests
165
+
166
+ ```typescript
167
+ // tests/kb-quality/coverage.test.ts
168
+
169
+ describe('KB Coverage', () => {
170
+ const requiredTopics = [
171
+ 'withdrawal strategies',
172
+ 'social security optimization',
173
+ 'Medicare planning',
174
+ 'tax-efficient withdrawal',
175
+ 'Roth conversions',
176
+ 'required minimum distributions',
177
+ 'estate planning basics',
178
+ 'inflation protection',
179
+ 'sequence of returns risk',
180
+ 'bucket strategy'
181
+ ];
182
+
183
+ requiredTopics.forEach(topic => {
184
+ test(`KB covers "${topic}"`, async () => {
185
+ const results = await kb.search(topic);
186
+
187
+ expect(results.length).toBeGreaterThan(0);
188
+ expect(results[0].confidence).toBeGreaterThan(0.5);
189
+ });
190
+ });
191
+
192
+ test('KB has minimum node count', async () => {
193
+ const stats = await kb.getStats();
194
+ expect(stats.totalNodes).toBeGreaterThanOrEqual(500);
195
+ });
196
+
197
+ test('KB has minimum expert count', async () => {
198
+ const stats = await kb.getStats();
199
+ expect(stats.uniqueExperts).toBeGreaterThanOrEqual(50);
200
+ });
201
+
202
+ test('average confidence is acceptable', async () => {
203
+ const stats = await kb.getStats();
204
+ expect(stats.avgConfidence).toBeGreaterThanOrEqual(0.7);
205
+ });
206
+ });
207
+ ```
208
+
209
+ ### 2.3 Gap Detection Tests
210
+
211
+ ```typescript
212
+ // tests/kb-quality/gaps.test.ts
213
+
214
+ describe('KB Gap Detection', () => {
215
+ const edgeCaseQueries = [
216
+ 'retirement planning for freelancers',
217
+ 'pension vs lump sum decision',
218
+ 'international retirement tax treaties',
219
+ 'cryptocurrency in retirement portfolio'
220
+ ];
221
+
222
+ edgeCaseQueries.forEach(query => {
223
+ test(`handles "${query}" gracefully`, async () => {
224
+ const results = await kb.search(query);
225
+
226
+ if (results.length === 0 || results[0].confidence < 0.5) {
227
+ // Should log gap
228
+ const gaps = await kb.getRecentGaps();
229
+ const gapLogged = gaps.some(g => g.query.includes(query));
230
+ expect(gapLogged).toBe(true);
231
+ }
232
+ });
233
+ });
234
+ });
235
+ ```
236
+
237
+ ---
238
+
239
+ ## 3. Integration Testing
240
+
241
+ ### 3.1 API + KB Integration
242
+
243
+ ```typescript
244
+ // tests/integration/api-kb.test.ts
245
+
246
+ describe('API-KB Integration', () => {
247
+ let app: Express;
248
+
249
+ beforeAll(async () => {
250
+ // Start app with real KB connection
251
+ app = await startApp();
252
+ });
253
+
254
+ afterAll(async () => {
255
+ await stopApp();
256
+ });
257
+
258
+ test('POST /api/calculate-withdrawal returns sources', async () => {
259
+ const response = await request(app)
260
+ .post('/api/calculate-withdrawal')
261
+ .send({ portfolio: 1000000, age: 65 });
262
+
263
+ expect(response.status).toBe(200);
264
+ expect(response.body.withdrawal).toBeDefined();
265
+ expect(response.body.sources).toBeInstanceOf(Array);
266
+ expect(response.body.sources.length).toBeGreaterThan(0);
267
+ expect(response.body.sources[0].expert).toBeDefined();
268
+ });
269
+
270
+ test('API fails gracefully when KB unavailable', async () => {
271
+ // Temporarily disconnect KB
272
+ await kb.disconnect();
273
+
274
+ const response = await request(app)
275
+ .post('/api/calculate-withdrawal')
276
+ .send({ portfolio: 1000000 });
277
+
278
+ expect(response.status).toBe(503);
279
+ expect(response.body.error).toContain('KB');
280
+
281
+ // Reconnect
282
+ await kb.connect();
283
+ });
284
+
285
+ test('all endpoints return sources array', async () => {
286
+ const endpoints = [
287
+ { method: 'post', path: '/api/calculate-withdrawal', body: { portfolio: 1000000 } },
288
+ { method: 'post', path: '/api/recommend-strategy', body: { age: 65, risk: 'moderate' } },
289
+ { method: 'get', path: '/api/ss-claiming-age?birthYear=1960' }
290
+ ];
291
+
292
+ for (const endpoint of endpoints) {
293
+ const response = await request(app)[endpoint.method](endpoint.path)
294
+ .send(endpoint.body);
295
+
296
+ expect(response.body.sources).toBeDefined();
297
+ expect(Array.isArray(response.body.sources)).toBe(true);
298
+ }
299
+ });
300
+ });
301
+ ```
302
+
303
+ ### 3.2 Intelligence Layer Integration
304
+
305
+ ```typescript
306
+ // tests/integration/gnn-integration.test.ts
307
+
308
+ describe('GNN Integration', () => {
309
+ test('decision simulation returns cascade effects', async () => {
310
+ const result = await api.post('/api/simulate-decision', {
311
+ variable: 'ss_claiming_age',
312
+ newValue: 70
313
+ });
314
+
315
+ expect(result.body.effects).toBeInstanceOf(Array);
316
+ expect(result.body.effects.length).toBeGreaterThan(0);
317
+
318
+ // Should affect related variables
319
+ const affectedVars = result.body.effects.map(e => e.variable);
320
+ expect(affectedVars).toContain('tax_bracket');
321
+ expect(affectedVars).toContain('roth_conversion_space');
322
+ });
323
+ });
324
+ ```
325
+
326
+ ---
327
+
328
+ ## 4. End-to-End Testing
329
+
330
+ ### 4.1 User Journey Tests
331
+
332
+ ```typescript
333
+ // tests/e2e/user-journeys.test.ts
334
+
335
+ describe('User Journeys', () => {
336
+ test('complete retirement planning flow', async () => {
337
+ const browser = await chromium.launch();
338
+ const page = await browser.newPage();
339
+
340
+ // 1. Load app
341
+ await page.goto('http://localhost:3000');
342
+ await expect(page.locator('h1')).toContainText('Retirement Advisor');
343
+
344
+ // 2. Enter profile
345
+ await page.fill('[name="age"]', '60');
346
+ await page.fill('[name="portfolio"]', '1000000');
347
+ await page.selectOption('[name="risk"]', 'moderate');
348
+ await page.click('button[type="submit"]');
349
+
350
+ // 3. Verify recommendations appear with sources
351
+ await expect(page.locator('.recommendation')).toBeVisible();
352
+ await expect(page.locator('.sources')).toBeVisible();
353
+ await expect(page.locator('.source-expert')).toHaveCount({ min: 1 });
354
+
355
+ // 4. Run simulation
356
+ await page.click('[data-test="simulate-ss-70"]');
357
+ await expect(page.locator('.cascade-effects')).toBeVisible();
358
+
359
+ // 5. Verify all displayed values have sources
360
+ const values = await page.locator('.domain-value').all();
361
+ for (const value of values) {
362
+ const sources = await value.locator('..').locator('.sources');
363
+ await expect(sources).toBeVisible();
364
+ }
365
+
366
+ await browser.close();
367
+ });
368
+
369
+ test('handles errors gracefully', async () => {
370
+ const browser = await chromium.launch();
371
+ const page = await browser.newPage();
372
+
373
+ // Simulate KB failure
374
+ await page.route('**/api/**', route => {
375
+ route.fulfill({ status: 503, body: JSON.stringify({ error: 'KB unavailable' }) });
376
+ });
377
+
378
+ await page.goto('http://localhost:3000');
379
+ await page.fill('[name="age"]', '60');
380
+ await page.click('button[type="submit"]');
381
+
382
+ // Should show error, not crash
383
+ await expect(page.locator('.error-message')).toContainText('unavailable');
384
+
385
+ await browser.close();
386
+ });
387
+ });
388
+ ```
389
+
390
+ ---
391
+
392
+ ## 5. Performance Testing
393
+
394
+ ### 5.1 Response Time Tests
395
+
396
+ ```typescript
397
+ // tests/performance/response-times.test.ts
398
+
399
+ describe('Performance', () => {
400
+ const TIMEOUT_MS = 500; // p95 target
401
+
402
+ test('KB search completes within SLA', async () => {
403
+ const times: number[] = [];
404
+
405
+ for (let i = 0; i < 100; i++) {
406
+ const start = Date.now();
407
+ await kb.search('withdrawal rate');
408
+ times.push(Date.now() - start);
409
+ }
410
+
411
+ times.sort((a, b) => a - b);
412
+ const p95 = times[Math.floor(times.length * 0.95)];
413
+
414
+ expect(p95).toBeLessThan(TIMEOUT_MS);
415
+ });
416
+
417
+ test('API endpoints meet SLA', async () => {
418
+ const endpoints = [
419
+ '/api/calculate-withdrawal',
420
+ '/api/recommend-strategy',
421
+ '/api/simulate-decision'
422
+ ];
423
+
424
+ for (const endpoint of endpoints) {
425
+ const times: number[] = [];
426
+
427
+ for (let i = 0; i < 50; i++) {
428
+ const start = Date.now();
429
+ await request(app).post(endpoint).send({ portfolio: 1000000 });
430
+ times.push(Date.now() - start);
431
+ }
432
+
433
+ times.sort((a, b) => a - b);
434
+ const p95 = times[Math.floor(times.length * 0.95)];
435
+
436
+ expect(p95).toBeLessThan(1000); // 1s for API
437
+ }
438
+ });
439
+ });
440
+ ```
441
+
442
+ ### 5.2 Load Testing
443
+
444
+ ```bash
445
+ #!/bin/bash
446
+ # scripts/load-test.sh
447
+
448
+ echo "=== KB-First Load Test ==="
449
+
450
+ # Use k6 or artillery
451
+ k6 run - <<EOF
452
+ import http from 'k6/http';
453
+ import { check, sleep } from 'k6';
454
+
455
+ export const options = {
456
+ vus: 50, // 50 virtual users
457
+ duration: '5m', // 5 minutes
458
+ thresholds: {
459
+ http_req_duration: ['p(95)<1000'], // 95% under 1s
460
+ http_req_failed: ['rate<0.01'], // <1% errors
461
+ },
462
+ };
463
+
464
+ export default function () {
465
+ const res = http.post('http://localhost:3000/api/calculate-withdrawal',
466
+ JSON.stringify({ portfolio: 1000000 }),
467
+ { headers: { 'Content-Type': 'application/json' } }
468
+ );
469
+
470
+ check(res, {
471
+ 'status is 200': (r) => r.status === 200,
472
+ 'has sources': (r) => JSON.parse(r.body).sources?.length > 0,
473
+ });
474
+
475
+ sleep(1);
476
+ }
477
+ EOF
478
+ ```
479
+
480
+ ---
481
+
482
+ ## 6. Regression Testing
483
+
484
+ ### 6.1 Golden File Tests
485
+
486
+ ```typescript
487
+ // tests/regression/golden-files.test.ts
488
+
489
+ describe('Regression - Golden Files', () => {
490
+ const goldenCases = [
491
+ {
492
+ name: 'standard-withdrawal',
493
+ input: { portfolio: 1000000, age: 65, risk: 'moderate' },
494
+ goldenFile: 'tests/golden/standard-withdrawal.json'
495
+ },
496
+ {
497
+ name: 'early-retirement',
498
+ input: { portfolio: 2000000, age: 55, risk: 'aggressive' },
499
+ goldenFile: 'tests/golden/early-retirement.json'
500
+ }
501
+ ];
502
+
503
+ goldenCases.forEach(({ name, input, goldenFile }) => {
504
+ test(`regression: ${name}`, async () => {
505
+ const result = await api.post('/api/calculate-withdrawal', input);
506
+ const golden = JSON.parse(fs.readFileSync(goldenFile, 'utf-8'));
507
+
508
+ // Values should be within 5% of golden
509
+ expect(result.body.withdrawal).toBeCloseTo(golden.withdrawal, -2);
510
+
511
+ // Sources should include same experts
512
+ const resultExperts = result.body.sources.map(s => s.expert);
513
+ const goldenExperts = golden.sources.map(s => s.expert);
514
+ expect(resultExperts).toEqual(expect.arrayContaining(goldenExperts));
515
+ });
516
+ });
517
+ });
518
+ ```
519
+
520
+ ### 6.2 Update Golden Files Script
521
+
522
+ ```bash
523
+ #!/bin/bash
524
+ # scripts/update-golden-files.sh
525
+
526
+ echo "Updating golden files..."
527
+ echo "⚠️ Only run this after verifying new behavior is correct!"
528
+
529
+ read -p "Are you sure? (type 'yes'): " confirm
530
+ if [ "$confirm" != "yes" ]; then
531
+ echo "Aborted"
532
+ exit 1
533
+ fi
534
+
535
+ # Generate new golden files
536
+ node tests/generate-golden.js
537
+ echo "✅ Golden files updated"
538
+ ```
539
+
540
+ ---
541
+
542
+ ## 7. Test Configuration
543
+
544
+ ### Jest Configuration
545
+
546
+ ```javascript
547
+ // jest.config.js
548
+ module.exports = {
549
+ projects: [
550
+ {
551
+ displayName: 'unit',
552
+ testMatch: ['<rootDir>/src/**/__tests__/**/*.test.ts'],
553
+ setupFilesAfterEnv: ['<rootDir>/tests/setup/unit.ts'],
554
+ },
555
+ {
556
+ displayName: 'kb-quality',
557
+ testMatch: ['<rootDir>/tests/kb-quality/**/*.test.ts'],
558
+ setupFilesAfterEnv: ['<rootDir>/tests/setup/kb.ts'],
559
+ },
560
+ {
561
+ displayName: 'integration',
562
+ testMatch: ['<rootDir>/tests/integration/**/*.test.ts'],
563
+ setupFilesAfterEnv: ['<rootDir>/tests/setup/integration.ts'],
564
+ },
565
+ {
566
+ displayName: 'e2e',
567
+ testMatch: ['<rootDir>/tests/e2e/**/*.test.ts'],
568
+ setupFilesAfterEnv: ['<rootDir>/tests/setup/e2e.ts'],
569
+ },
570
+ ],
571
+ coverageThreshold: {
572
+ global: {
573
+ branches: 80,
574
+ functions: 90,
575
+ lines: 90,
576
+ statements: 90,
577
+ },
578
+ },
579
+ };
580
+ ```
581
+
582
+ ### Run Commands
583
+
584
+ ```bash
585
+ # All tests
586
+ npm test
587
+
588
+ # Specific suites
589
+ npm run test:unit
590
+ npm run test:kb-quality
591
+ npm run test:integration
592
+ npm run test:e2e
593
+
594
+ # With coverage
595
+ npm run test:coverage
596
+
597
+ # Watch mode
598
+ npm run test:watch
599
+
600
+ # CI mode (no watch, fail fast)
601
+ npm run test:ci
602
+ ```
603
+
604
+ ---
605
+
606
+ ## 8. Test Requirements Checklist
607
+
608
+ Before marking Phase 7.7 complete:
609
+
610
+ ```
611
+ UNIT TESTS:
612
+ [ ] All domain functions tested
613
+ [ ] KB mocked in unit tests
614
+ [ ] Coverage ≥90%
615
+ [ ] No hardcoded value tests pass
616
+
617
+ KB QUALITY TESTS:
618
+ [ ] Search accuracy tests (100+ queries)
619
+ [ ] Coverage tests (all required topics)
620
+ [ ] Gap detection verified
621
+
622
+ INTEGRATION TESTS:
623
+ [ ] All API endpoints tested
624
+ [ ] KB integration verified
625
+ [ ] Intelligence layer integration tested
626
+ [ ] Error handling verified
627
+
628
+ E2E TESTS:
629
+ [ ] Core user journeys pass
630
+ [ ] Sources displayed in UI
631
+ [ ] Error states handled gracefully
632
+
633
+ PERFORMANCE TESTS:
634
+ [ ] p95 response time <500ms (KB search)
635
+ [ ] p95 response time <1000ms (API)
636
+ [ ] Load test passes (50 VUs, 5 min)
637
+
638
+ REGRESSION TESTS:
639
+ [ ] Golden file tests pass
640
+ [ ] No regressions from previous version
641
+ ```
642
+
643
+ ---
644
+
645
+ ## 9. Continuous Testing
646
+
647
+ ### Pre-commit Hook
648
+
649
+ ```bash
650
+ #!/bin/bash
651
+ # .husky/pre-commit
652
+
653
+ npm run test:unit -- --bail
654
+ npm run lint
655
+ ```
656
+
657
+ ### PR Checks
658
+
659
+ ```yaml
660
+ # .github/workflows/test.yml
661
+ name: Tests
662
+ on: [pull_request]
663
+
664
+ jobs:
665
+ test:
666
+ runs-on: ubuntu-latest
667
+ steps:
668
+ - uses: actions/checkout@v4
669
+ - uses: actions/setup-node@v4
670
+ - run: npm ci
671
+ - run: npm run test:unit
672
+ - run: npm run test:kb-quality
673
+ - run: npm run test:integration
674
+ - run: npm run test:coverage
675
+ - uses: codecov/codecov-action@v3
676
+ ```
677
+
678
+ ### Nightly Full Suite
679
+
680
+ ```yaml
681
+ # .github/workflows/nightly.yml
682
+ name: Nightly Tests
683
+ on:
684
+ schedule:
685
+ - cron: '0 2 * * *' # 2 AM daily
686
+
687
+ jobs:
688
+ full-suite:
689
+ runs-on: ubuntu-latest
690
+ steps:
691
+ - uses: actions/checkout@v4
692
+ - uses: actions/setup-node@v4
693
+ - run: npm ci
694
+ - run: npm run test:all
695
+ - run: npm run test:e2e
696
+ - run: npm run test:performance
697
+ ```
698
+
699
+ ---
700
+
701
+ ## Exit Criteria
702
+
703
+ Phase 7.7 (Testing) is complete when:
704
+
705
+ ```
706
+ [ ] Unit test coverage ≥90%
707
+ [ ] All KB quality tests pass
708
+ [ ] All integration tests pass
709
+ [ ] E2E tests cover core journeys
710
+ [ ] Performance tests meet SLA
711
+ [ ] CI/CD pipeline configured
712
+ [ ] Pre-commit hooks installed
713
+ ```