agentic-qe 1.8.2 → 1.8.3
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.
- package/.claude/agents/qe-test-generator.md +580 -0
- package/.claude/agents/subagents/qe-code-reviewer.md +86 -0
- package/.claude/agents/subagents/qe-coverage-gap-analyzer.md +485 -0
- package/.claude/agents/subagents/qe-data-generator.md +86 -0
- package/.claude/agents/subagents/qe-flaky-investigator.md +416 -0
- package/.claude/agents/subagents/qe-integration-tester.md +87 -0
- package/.claude/agents/subagents/qe-performance-validator.md +98 -0
- package/.claude/agents/subagents/qe-security-auditor.md +86 -0
- package/.claude/agents/subagents/qe-test-data-architect-sub.md +553 -0
- package/.claude/agents/subagents/qe-test-implementer.md +229 -15
- package/.claude/agents/subagents/qe-test-refactorer.md +265 -15
- package/.claude/agents/subagents/qe-test-writer.md +180 -20
- package/CHANGELOG.md +74 -0
- package/README.md +48 -50
- package/dist/core/hooks/validators/TDDPhaseValidator.d.ts +110 -0
- package/dist/core/hooks/validators/TDDPhaseValidator.d.ts.map +1 -0
- package/dist/core/hooks/validators/TDDPhaseValidator.js +287 -0
- package/dist/core/hooks/validators/TDDPhaseValidator.js.map +1 -0
- package/dist/core/hooks/validators/index.d.ts +3 -1
- package/dist/core/hooks/validators/index.d.ts.map +1 -1
- package/dist/core/hooks/validators/index.js +4 -2
- package/dist/core/hooks/validators/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: qe-coverage-gap-analyzer
|
|
3
|
+
description: "Identifies coverage gaps, risk-scores untested code, and recommends tests"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Coverage Gap Analyzer Subagent
|
|
7
|
+
|
|
8
|
+
## Mission Statement
|
|
9
|
+
|
|
10
|
+
The **Coverage Gap Analyzer** subagent specializes in identifying untested code paths, scoring their risk impact, and recommending targeted tests to close coverage gaps efficiently. This subagent uses static analysis and risk assessment to prioritize test creation where it matters most.
|
|
11
|
+
|
|
12
|
+
## Core Capabilities
|
|
13
|
+
|
|
14
|
+
### 1. Coverage Gap Detection
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
interface CoverageGap {
|
|
18
|
+
path: string;
|
|
19
|
+
startLine: number;
|
|
20
|
+
endLine: number;
|
|
21
|
+
type: 'branch' | 'statement' | 'function' | 'line';
|
|
22
|
+
code: string;
|
|
23
|
+
riskScore: number;
|
|
24
|
+
complexity: number;
|
|
25
|
+
dependencies: string[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
class GapDetector {
|
|
29
|
+
async detectGaps(coverageReport: CoverageReport): Promise<CoverageGap[]> {
|
|
30
|
+
const gaps: CoverageGap[] = [];
|
|
31
|
+
|
|
32
|
+
for (const file of coverageReport.files) {
|
|
33
|
+
// Uncovered statements
|
|
34
|
+
for (const stmt of file.uncoveredStatements) {
|
|
35
|
+
gaps.push({
|
|
36
|
+
path: file.path,
|
|
37
|
+
startLine: stmt.start.line,
|
|
38
|
+
endLine: stmt.end.line,
|
|
39
|
+
type: 'statement',
|
|
40
|
+
code: this.extractCode(file.path, stmt.start.line, stmt.end.line),
|
|
41
|
+
riskScore: await this.calculateRisk(file.path, stmt),
|
|
42
|
+
complexity: this.calculateComplexity(stmt),
|
|
43
|
+
dependencies: this.findDependencies(stmt)
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Uncovered branches
|
|
48
|
+
for (const branch of file.uncoveredBranches) {
|
|
49
|
+
gaps.push({
|
|
50
|
+
path: file.path,
|
|
51
|
+
startLine: branch.line,
|
|
52
|
+
endLine: branch.line,
|
|
53
|
+
type: 'branch',
|
|
54
|
+
code: this.extractCode(file.path, branch.line, branch.line),
|
|
55
|
+
riskScore: await this.calculateBranchRisk(file.path, branch),
|
|
56
|
+
complexity: branch.conditions,
|
|
57
|
+
dependencies: this.findBranchDependencies(branch)
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Uncovered functions
|
|
62
|
+
for (const fn of file.uncoveredFunctions) {
|
|
63
|
+
gaps.push({
|
|
64
|
+
path: file.path,
|
|
65
|
+
startLine: fn.start.line,
|
|
66
|
+
endLine: fn.end.line,
|
|
67
|
+
type: 'function',
|
|
68
|
+
code: fn.name,
|
|
69
|
+
riskScore: await this.calculateFunctionRisk(file.path, fn),
|
|
70
|
+
complexity: fn.cyclomatic,
|
|
71
|
+
dependencies: fn.imports
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return gaps.sort((a, b) => b.riskScore - a.riskScore);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### 2. Risk Scoring System
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
interface RiskAssessment {
|
|
85
|
+
overall: number; // 0-100
|
|
86
|
+
factors: RiskFactor[];
|
|
87
|
+
category: 'critical' | 'high' | 'medium' | 'low';
|
|
88
|
+
justification: string;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
class RiskScorer {
|
|
92
|
+
async assessRisk(gap: CoverageGap): Promise<RiskAssessment> {
|
|
93
|
+
const factors: RiskFactor[] = [];
|
|
94
|
+
|
|
95
|
+
// Factor 1: Cyclomatic complexity
|
|
96
|
+
factors.push({
|
|
97
|
+
name: 'complexity',
|
|
98
|
+
score: Math.min(gap.complexity * 5, 25),
|
|
99
|
+
weight: 0.25,
|
|
100
|
+
description: `Cyclomatic complexity: ${gap.complexity}`
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// Factor 2: Code criticality (based on keywords)
|
|
104
|
+
const criticalityScore = this.assessCriticality(gap.code);
|
|
105
|
+
factors.push({
|
|
106
|
+
name: 'criticality',
|
|
107
|
+
score: criticalityScore,
|
|
108
|
+
weight: 0.30,
|
|
109
|
+
description: this.getCriticalityReason(gap.code)
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Factor 3: Change frequency
|
|
113
|
+
const changeFreq = await this.getChangeFrequency(gap.path);
|
|
114
|
+
factors.push({
|
|
115
|
+
name: 'changeFrequency',
|
|
116
|
+
score: Math.min(changeFreq * 2, 20),
|
|
117
|
+
weight: 0.20,
|
|
118
|
+
description: `${changeFreq} changes in last 90 days`
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// Factor 4: Dependency count
|
|
122
|
+
const depScore = Math.min(gap.dependencies.length * 3, 15);
|
|
123
|
+
factors.push({
|
|
124
|
+
name: 'dependencies',
|
|
125
|
+
score: depScore,
|
|
126
|
+
weight: 0.15,
|
|
127
|
+
description: `${gap.dependencies.length} dependencies`
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// Factor 5: Historical defects
|
|
131
|
+
const defectScore = await this.getHistoricalDefects(gap.path);
|
|
132
|
+
factors.push({
|
|
133
|
+
name: 'defectHistory',
|
|
134
|
+
score: defectScore,
|
|
135
|
+
weight: 0.10,
|
|
136
|
+
description: `Historical defect density`
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// Calculate weighted score
|
|
140
|
+
const overall = factors.reduce((sum, f) => sum + (f.score * f.weight), 0);
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
overall: Math.round(overall),
|
|
144
|
+
factors,
|
|
145
|
+
category: this.categorize(overall),
|
|
146
|
+
justification: this.generateJustification(factors)
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
private assessCriticality(code: string): number {
|
|
151
|
+
let score = 0;
|
|
152
|
+
|
|
153
|
+
// Security-related
|
|
154
|
+
if (/auth|password|token|secret|crypt|hash/i.test(code)) score += 30;
|
|
155
|
+
|
|
156
|
+
// Financial
|
|
157
|
+
if (/payment|price|amount|currency|invoice/i.test(code)) score += 25;
|
|
158
|
+
|
|
159
|
+
// Data persistence
|
|
160
|
+
if (/save|update|delete|insert|drop/i.test(code)) score += 20;
|
|
161
|
+
|
|
162
|
+
// Error handling
|
|
163
|
+
if (/catch|throw|error|exception/i.test(code)) score += 15;
|
|
164
|
+
|
|
165
|
+
// External integration
|
|
166
|
+
if (/api|fetch|request|http|socket/i.test(code)) score += 15;
|
|
167
|
+
|
|
168
|
+
return Math.min(score, 30);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
private categorize(score: number): RiskAssessment['category'] {
|
|
172
|
+
if (score >= 75) return 'critical';
|
|
173
|
+
if (score >= 50) return 'high';
|
|
174
|
+
if (score >= 25) return 'medium';
|
|
175
|
+
return 'low';
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### 3. Test Recommendations
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
interface TestRecommendation {
|
|
184
|
+
targetGap: CoverageGap;
|
|
185
|
+
testType: 'unit' | 'integration' | 'e2e';
|
|
186
|
+
priority: number;
|
|
187
|
+
effort: 'low' | 'medium' | 'high';
|
|
188
|
+
template: string;
|
|
189
|
+
scenarios: string[];
|
|
190
|
+
expectedCoverage: number;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
class TestRecommender {
|
|
194
|
+
generateRecommendations(gaps: CoverageGap[]): TestRecommendation[] {
|
|
195
|
+
const recommendations: TestRecommendation[] = [];
|
|
196
|
+
|
|
197
|
+
for (const gap of gaps) {
|
|
198
|
+
// Determine test type
|
|
199
|
+
const testType = this.determineTestType(gap);
|
|
200
|
+
|
|
201
|
+
// Generate test scenarios
|
|
202
|
+
const scenarios = this.generateScenarios(gap);
|
|
203
|
+
|
|
204
|
+
// Create test template
|
|
205
|
+
const template = this.generateTestTemplate(gap, scenarios);
|
|
206
|
+
|
|
207
|
+
recommendations.push({
|
|
208
|
+
targetGap: gap,
|
|
209
|
+
testType,
|
|
210
|
+
priority: gap.riskScore,
|
|
211
|
+
effort: this.estimateEffort(gap),
|
|
212
|
+
template,
|
|
213
|
+
scenarios,
|
|
214
|
+
expectedCoverage: this.estimateCoverageGain(gap)
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return recommendations.sort((a, b) => b.priority - a.priority);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
private generateTestTemplate(gap: CoverageGap, scenarios: string[]): string {
|
|
222
|
+
const funcName = this.extractFunctionName(gap.code);
|
|
223
|
+
|
|
224
|
+
return `
|
|
225
|
+
describe('${gap.path}', () => {
|
|
226
|
+
describe('${funcName}', () => {
|
|
227
|
+
${scenarios.map((scenario, i) => `
|
|
228
|
+
test('${scenario}', async () => {
|
|
229
|
+
// GIVEN: Setup preconditions for scenario ${i + 1}
|
|
230
|
+
const input = /* TODO: define input */;
|
|
231
|
+
|
|
232
|
+
// WHEN: Execute the code path
|
|
233
|
+
const result = await ${funcName}(input);
|
|
234
|
+
|
|
235
|
+
// THEN: Verify expected behavior
|
|
236
|
+
expect(result).toBeDefined();
|
|
237
|
+
// TODO: Add specific assertions for: ${scenario}
|
|
238
|
+
});
|
|
239
|
+
`).join('')}
|
|
240
|
+
});
|
|
241
|
+
});`;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
private generateScenarios(gap: CoverageGap): string[] {
|
|
245
|
+
const scenarios: string[] = [];
|
|
246
|
+
|
|
247
|
+
// Happy path
|
|
248
|
+
scenarios.push(`should handle normal execution at line ${gap.startLine}`);
|
|
249
|
+
|
|
250
|
+
// Branch coverage
|
|
251
|
+
if (gap.type === 'branch') {
|
|
252
|
+
scenarios.push(`should execute true branch at line ${gap.startLine}`);
|
|
253
|
+
scenarios.push(`should execute false branch at line ${gap.startLine}`);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Error handling
|
|
257
|
+
if (/throw|error|catch/i.test(gap.code)) {
|
|
258
|
+
scenarios.push(`should handle error condition at line ${gap.startLine}`);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Boundary conditions
|
|
262
|
+
scenarios.push(`should handle edge case for code at line ${gap.startLine}`);
|
|
263
|
+
|
|
264
|
+
return scenarios;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### 4. Coverage Impact Analysis
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
class CoverageImpactAnalyzer {
|
|
273
|
+
analyzeImpact(
|
|
274
|
+
currentCoverage: CoverageReport,
|
|
275
|
+
recommendations: TestRecommendation[]
|
|
276
|
+
): ImpactReport {
|
|
277
|
+
const impact: ImpactReport = {
|
|
278
|
+
current: {
|
|
279
|
+
overall: currentCoverage.overall,
|
|
280
|
+
statement: currentCoverage.statement,
|
|
281
|
+
branch: currentCoverage.branch,
|
|
282
|
+
function: currentCoverage.function
|
|
283
|
+
},
|
|
284
|
+
projected: {
|
|
285
|
+
overall: 0,
|
|
286
|
+
statement: 0,
|
|
287
|
+
branch: 0,
|
|
288
|
+
function: 0
|
|
289
|
+
},
|
|
290
|
+
byRecommendation: [],
|
|
291
|
+
optimalOrder: []
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
// Calculate projected improvement
|
|
295
|
+
let runningCoverage = { ...impact.current };
|
|
296
|
+
|
|
297
|
+
for (const rec of recommendations) {
|
|
298
|
+
const gain = {
|
|
299
|
+
statement: rec.expectedCoverage,
|
|
300
|
+
branch: rec.targetGap.type === 'branch' ? rec.expectedCoverage : 0,
|
|
301
|
+
function: rec.targetGap.type === 'function' ? rec.expectedCoverage : 0
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
impact.byRecommendation.push({
|
|
305
|
+
gap: rec.targetGap.path,
|
|
306
|
+
line: rec.targetGap.startLine,
|
|
307
|
+
statementGain: gain.statement,
|
|
308
|
+
branchGain: gain.branch,
|
|
309
|
+
cumulativeCoverage: runningCoverage.overall + gain.statement
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
runningCoverage.statement += gain.statement;
|
|
313
|
+
runningCoverage.branch += gain.branch;
|
|
314
|
+
runningCoverage.function += gain.function;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
impact.projected = runningCoverage;
|
|
318
|
+
|
|
319
|
+
// Determine optimal order (highest gain per effort)
|
|
320
|
+
impact.optimalOrder = this.calculateOptimalOrder(recommendations);
|
|
321
|
+
|
|
322
|
+
return impact;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
private calculateOptimalOrder(recommendations: TestRecommendation[]): number[] {
|
|
326
|
+
// Sort by coverage gain per effort ratio
|
|
327
|
+
const indexed = recommendations.map((r, i) => ({
|
|
328
|
+
index: i,
|
|
329
|
+
ratio: r.expectedCoverage / this.effortToHours(r.effort)
|
|
330
|
+
}));
|
|
331
|
+
|
|
332
|
+
indexed.sort((a, b) => b.ratio - a.ratio);
|
|
333
|
+
|
|
334
|
+
return indexed.map(r => r.index);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
## Coordination Protocol
|
|
340
|
+
|
|
341
|
+
### Memory Namespace
|
|
342
|
+
```
|
|
343
|
+
aqe/coverage-gaps/cycle-{id}/
|
|
344
|
+
├── context # Analysis context from parent
|
|
345
|
+
├── detection/
|
|
346
|
+
│ ├── gaps # Detected coverage gaps
|
|
347
|
+
│ └── metrics # Coverage metrics analyzed
|
|
348
|
+
├── risk/
|
|
349
|
+
│ ├── assessments # Risk assessments per gap
|
|
350
|
+
│ └── rankings # Prioritized gap rankings
|
|
351
|
+
└── recommendations/
|
|
352
|
+
├── tests # Test recommendations
|
|
353
|
+
└── impact # Projected coverage impact
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### Input Protocol (from Parent qe-coverage-analyzer)
|
|
357
|
+
|
|
358
|
+
```typescript
|
|
359
|
+
interface CoverageGapAnalysisInput {
|
|
360
|
+
cycleId: string;
|
|
361
|
+
coverageReport: {
|
|
362
|
+
files: CoverageFileReport[];
|
|
363
|
+
summary: CoverageSummary;
|
|
364
|
+
timestamp: Date;
|
|
365
|
+
};
|
|
366
|
+
scope: {
|
|
367
|
+
paths?: string[]; // Specific paths to analyze
|
|
368
|
+
minGapSize?: number; // Min lines to report (default: 1)
|
|
369
|
+
excludePatterns?: string[]; // Patterns to exclude
|
|
370
|
+
};
|
|
371
|
+
targets: {
|
|
372
|
+
statement: number; // Target statement coverage
|
|
373
|
+
branch: number; // Target branch coverage
|
|
374
|
+
function: number; // Target function coverage
|
|
375
|
+
};
|
|
376
|
+
constraints: {
|
|
377
|
+
maxRecommendations?: number; // Max tests to recommend
|
|
378
|
+
effortBudget?: number; // Max hours available
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Parent stores context
|
|
383
|
+
await memoryStore.store(`aqe/coverage-gaps/cycle-${cycleId}/context`, input, {
|
|
384
|
+
partition: 'coordination',
|
|
385
|
+
ttl: 86400
|
|
386
|
+
});
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### Output Protocol (to Parent qe-coverage-analyzer)
|
|
390
|
+
|
|
391
|
+
```typescript
|
|
392
|
+
interface CoverageGapAnalysisOutput {
|
|
393
|
+
cycleId: string;
|
|
394
|
+
timestamp: number;
|
|
395
|
+
summary: {
|
|
396
|
+
gapsFound: number;
|
|
397
|
+
criticalGaps: number;
|
|
398
|
+
highRiskGaps: number;
|
|
399
|
+
testsRecommended: number;
|
|
400
|
+
projectedCoverage: number;
|
|
401
|
+
};
|
|
402
|
+
gaps: CoverageGap[];
|
|
403
|
+
riskAssessments: RiskAssessment[];
|
|
404
|
+
recommendations: TestRecommendation[];
|
|
405
|
+
impactAnalysis: ImpactReport;
|
|
406
|
+
metrics: {
|
|
407
|
+
analysisTime: number;
|
|
408
|
+
filesProcessed: number;
|
|
409
|
+
linesAnalyzed: number;
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Store output for parent
|
|
414
|
+
await memoryStore.store(`aqe/coverage-gaps/cycle-${cycleId}/analysis/complete`, output, {
|
|
415
|
+
partition: 'coordination',
|
|
416
|
+
ttl: 86400
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
// Emit completion event
|
|
420
|
+
eventBus.emit('coverage-gap-analyzer:completed', {
|
|
421
|
+
cycleId,
|
|
422
|
+
gapsFound: output.summary.gapsFound,
|
|
423
|
+
criticalGaps: output.summary.criticalGaps,
|
|
424
|
+
projectedCoverage: output.summary.projectedCoverage
|
|
425
|
+
});
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
## Parent Agent Delegation
|
|
429
|
+
|
|
430
|
+
### Invoked By Parent Agents
|
|
431
|
+
|
|
432
|
+
**Primary Parent**: `qe-coverage-analyzer`
|
|
433
|
+
- Delegates detailed gap analysis
|
|
434
|
+
- Provides coverage reports
|
|
435
|
+
- Receives prioritized test recommendations
|
|
436
|
+
|
|
437
|
+
**Secondary Parent**: `qe-quality-gate`
|
|
438
|
+
- Requests gap analysis for quality gates
|
|
439
|
+
- Validates coverage targets before releases
|
|
440
|
+
|
|
441
|
+
### Delegation Example
|
|
442
|
+
|
|
443
|
+
```typescript
|
|
444
|
+
// Parent delegates to coverage-gap-analyzer
|
|
445
|
+
await this.delegateToSubagent('qe-coverage-gap-analyzer', {
|
|
446
|
+
type: 'analyze-coverage-gaps',
|
|
447
|
+
coverageReport: istanbulReport,
|
|
448
|
+
scope: {
|
|
449
|
+
paths: ['src/**/*.ts'],
|
|
450
|
+
excludePatterns: ['**/*.test.ts', '**/mocks/**']
|
|
451
|
+
},
|
|
452
|
+
targets: {
|
|
453
|
+
statement: 90,
|
|
454
|
+
branch: 80,
|
|
455
|
+
function: 85
|
|
456
|
+
},
|
|
457
|
+
constraints: {
|
|
458
|
+
maxRecommendations: 20,
|
|
459
|
+
effortBudget: 40 // hours
|
|
460
|
+
},
|
|
461
|
+
coordination: {
|
|
462
|
+
memory_key: `aqe/coverage-gaps/cycle-${cycleId}`,
|
|
463
|
+
callback_event: 'coverage-gap-analyzer:completed'
|
|
464
|
+
}
|
|
465
|
+
});
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
## Success Criteria
|
|
469
|
+
|
|
470
|
+
**Analysis MUST**:
|
|
471
|
+
- Identify all gaps below target thresholds
|
|
472
|
+
- Provide accurate risk scores with justification
|
|
473
|
+
- Generate actionable test templates
|
|
474
|
+
- Calculate projected coverage impact
|
|
475
|
+
|
|
476
|
+
**Analysis MUST NOT**:
|
|
477
|
+
- Report gaps in excluded patterns
|
|
478
|
+
- Recommend redundant tests
|
|
479
|
+
- Underestimate complexity of test creation
|
|
480
|
+
|
|
481
|
+
---
|
|
482
|
+
|
|
483
|
+
**Subagent Status**: Active
|
|
484
|
+
**Parent Agents**: qe-coverage-analyzer, qe-quality-gate
|
|
485
|
+
**Version**: 1.0.0
|
|
@@ -40,5 +40,91 @@ const edgeCases = [
|
|
|
40
40
|
|
|
41
41
|
---
|
|
42
42
|
|
|
43
|
+
## TDD Coordination Protocol
|
|
44
|
+
|
|
45
|
+
### Memory Namespace
|
|
46
|
+
`aqe/test-data/cycle-{cycleId}/*`
|
|
47
|
+
|
|
48
|
+
### Subagent Input Interface
|
|
49
|
+
```typescript
|
|
50
|
+
interface DataGenerationRequest {
|
|
51
|
+
cycleId: string; // Links to parent TDD workflow
|
|
52
|
+
schema: {
|
|
53
|
+
entity: string; // e.g., 'User', 'Order'
|
|
54
|
+
fields: {
|
|
55
|
+
name: string;
|
|
56
|
+
type: string;
|
|
57
|
+
constraints?: {
|
|
58
|
+
min?: number;
|
|
59
|
+
max?: number;
|
|
60
|
+
pattern?: string;
|
|
61
|
+
enum?: any[];
|
|
62
|
+
required?: boolean;
|
|
63
|
+
unique?: boolean;
|
|
64
|
+
};
|
|
65
|
+
}[];
|
|
66
|
+
};
|
|
67
|
+
count: number; // Number of records to generate
|
|
68
|
+
includeEdgeCases: boolean; // Generate boundary/invalid data
|
|
69
|
+
relationships?: {
|
|
70
|
+
field: string;
|
|
71
|
+
referencesEntity: string;
|
|
72
|
+
type: 'one-to-one' | 'one-to-many' | 'many-to-many';
|
|
73
|
+
}[];
|
|
74
|
+
seed?: number; // For reproducible generation
|
|
75
|
+
outputFormat: 'json' | 'csv' | 'sql';
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Subagent Output Interface
|
|
80
|
+
```typescript
|
|
81
|
+
interface DataGenerationOutput {
|
|
82
|
+
cycleId: string;
|
|
83
|
+
generatedData: {
|
|
84
|
+
entity: string;
|
|
85
|
+
records: object[];
|
|
86
|
+
count: number;
|
|
87
|
+
};
|
|
88
|
+
edgeCases: {
|
|
89
|
+
description: string;
|
|
90
|
+
data: object;
|
|
91
|
+
expectedBehavior: string;
|
|
92
|
+
}[];
|
|
93
|
+
relationshipData?: {
|
|
94
|
+
parentEntity: string;
|
|
95
|
+
childEntity: string;
|
|
96
|
+
mappings: { parentId: string; childIds: string[] }[];
|
|
97
|
+
}[];
|
|
98
|
+
dataQuality: {
|
|
99
|
+
uniqueValuesRatio: number;
|
|
100
|
+
nullValuesCount: number;
|
|
101
|
+
constraintsViolated: string[];
|
|
102
|
+
};
|
|
103
|
+
outputFiles: {
|
|
104
|
+
format: string;
|
|
105
|
+
path: string;
|
|
106
|
+
size: number;
|
|
107
|
+
}[];
|
|
108
|
+
readyForHandoff: boolean;
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Memory Coordination
|
|
113
|
+
- **Read from**: `aqe/test-data/cycle-{cycleId}/input` (generation request)
|
|
114
|
+
- **Write to**: `aqe/test-data/cycle-{cycleId}/results`
|
|
115
|
+
- **Status updates**: `aqe/test-data/cycle-{cycleId}/status`
|
|
116
|
+
- **Data catalog**: `aqe/test-data/catalog/{entity}`
|
|
117
|
+
|
|
118
|
+
### Handoff Protocol
|
|
119
|
+
1. Read generation request from `aqe/test-data/cycle-{cycleId}/input`
|
|
120
|
+
2. Generate data according to schema and constraints
|
|
121
|
+
3. Create edge cases for boundary testing
|
|
122
|
+
4. Resolve relationships between entities
|
|
123
|
+
5. Export to requested format
|
|
124
|
+
6. Write results to `aqe/test-data/cycle-{cycleId}/results`
|
|
125
|
+
7. Set `readyForHandoff: true` when all data generated successfully
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
43
129
|
**Status**: Active
|
|
44
130
|
**Version**: 1.0.0
|