@qball-inc/the-bulwark 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 (175) hide show
  1. package/.claude-plugin/plugin.json +43 -0
  2. package/agents/bulwark-fix-validator.md +633 -0
  3. package/agents/bulwark-implementer.md +391 -0
  4. package/agents/bulwark-issue-analyzer.md +308 -0
  5. package/agents/bulwark-standards-reviewer.md +221 -0
  6. package/agents/plan-creation-architect.md +323 -0
  7. package/agents/plan-creation-eng-lead.md +352 -0
  8. package/agents/plan-creation-po.md +300 -0
  9. package/agents/plan-creation-qa-critic.md +334 -0
  10. package/agents/product-ideation-competitive-analyzer.md +298 -0
  11. package/agents/product-ideation-idea-validator.md +268 -0
  12. package/agents/product-ideation-market-researcher.md +292 -0
  13. package/agents/product-ideation-pattern-documenter.md +308 -0
  14. package/agents/product-ideation-segment-analyzer.md +303 -0
  15. package/agents/product-ideation-strategist.md +259 -0
  16. package/agents/statusline-setup.md +97 -0
  17. package/hooks/hooks.json +59 -0
  18. package/package.json +45 -0
  19. package/scripts/hooks/cleanup-stale.sh +13 -0
  20. package/scripts/hooks/enforce-quality.sh +166 -0
  21. package/scripts/hooks/implementer-quality.sh +256 -0
  22. package/scripts/hooks/inject-protocol.sh +52 -0
  23. package/scripts/hooks/suggest-pipeline.sh +175 -0
  24. package/scripts/hooks/track-pipeline-start.sh +37 -0
  25. package/scripts/hooks/track-pipeline-stop.sh +52 -0
  26. package/scripts/init-rules.sh +35 -0
  27. package/scripts/init.sh +151 -0
  28. package/skills/anthropic-validator/SKILL.md +607 -0
  29. package/skills/anthropic-validator/references/agents-checklist.md +131 -0
  30. package/skills/anthropic-validator/references/commands-checklist.md +102 -0
  31. package/skills/anthropic-validator/references/hooks-checklist.md +151 -0
  32. package/skills/anthropic-validator/references/mcp-checklist.md +136 -0
  33. package/skills/anthropic-validator/references/plugins-checklist.md +148 -0
  34. package/skills/anthropic-validator/references/skills-checklist.md +85 -0
  35. package/skills/assertion-patterns/SKILL.md +296 -0
  36. package/skills/bug-magnet-data/SKILL.md +284 -0
  37. package/skills/bug-magnet-data/context/cli-args.md +91 -0
  38. package/skills/bug-magnet-data/context/db-query.md +104 -0
  39. package/skills/bug-magnet-data/context/file-contents.md +103 -0
  40. package/skills/bug-magnet-data/context/http-body.md +91 -0
  41. package/skills/bug-magnet-data/context/process-spawn.md +123 -0
  42. package/skills/bug-magnet-data/data/booleans/boundaries.yaml +143 -0
  43. package/skills/bug-magnet-data/data/collections/arrays.yaml +114 -0
  44. package/skills/bug-magnet-data/data/collections/objects.yaml +123 -0
  45. package/skills/bug-magnet-data/data/concurrency/race-conditions.yaml +118 -0
  46. package/skills/bug-magnet-data/data/concurrency/state-machines.yaml +115 -0
  47. package/skills/bug-magnet-data/data/dates/boundaries.yaml +137 -0
  48. package/skills/bug-magnet-data/data/dates/invalid.yaml +132 -0
  49. package/skills/bug-magnet-data/data/dates/timezone.yaml +118 -0
  50. package/skills/bug-magnet-data/data/encoding/charset.yaml +79 -0
  51. package/skills/bug-magnet-data/data/encoding/normalization.yaml +105 -0
  52. package/skills/bug-magnet-data/data/formats/email.yaml +154 -0
  53. package/skills/bug-magnet-data/data/formats/json.yaml +187 -0
  54. package/skills/bug-magnet-data/data/formats/url.yaml +165 -0
  55. package/skills/bug-magnet-data/data/language-specific/javascript.yaml +182 -0
  56. package/skills/bug-magnet-data/data/language-specific/python.yaml +174 -0
  57. package/skills/bug-magnet-data/data/language-specific/rust.yaml +148 -0
  58. package/skills/bug-magnet-data/data/numbers/boundaries.yaml +161 -0
  59. package/skills/bug-magnet-data/data/numbers/precision.yaml +89 -0
  60. package/skills/bug-magnet-data/data/numbers/special.yaml +69 -0
  61. package/skills/bug-magnet-data/data/strings/boundaries.yaml +109 -0
  62. package/skills/bug-magnet-data/data/strings/injection.yaml +208 -0
  63. package/skills/bug-magnet-data/data/strings/special-chars.yaml +190 -0
  64. package/skills/bug-magnet-data/data/strings/unicode.yaml +139 -0
  65. package/skills/bug-magnet-data/references/external-lists.md +115 -0
  66. package/skills/bulwark-brainstorm/SKILL.md +563 -0
  67. package/skills/bulwark-brainstorm/references/at-teammate-prompts.md +60 -0
  68. package/skills/bulwark-brainstorm/references/role-critical-analyst.md +78 -0
  69. package/skills/bulwark-brainstorm/references/role-development-lead.md +66 -0
  70. package/skills/bulwark-brainstorm/references/role-product-delivery-lead.md +79 -0
  71. package/skills/bulwark-brainstorm/references/role-product-manager.md +62 -0
  72. package/skills/bulwark-brainstorm/references/role-project-sme.md +59 -0
  73. package/skills/bulwark-brainstorm/references/role-technical-architect.md +66 -0
  74. package/skills/bulwark-research/SKILL.md +298 -0
  75. package/skills/bulwark-research/references/viewpoint-contrarian.md +63 -0
  76. package/skills/bulwark-research/references/viewpoint-direct-investigation.md +62 -0
  77. package/skills/bulwark-research/references/viewpoint-first-principles.md +65 -0
  78. package/skills/bulwark-research/references/viewpoint-practitioner.md +62 -0
  79. package/skills/bulwark-research/references/viewpoint-prior-art.md +66 -0
  80. package/skills/bulwark-scaffold/SKILL.md +330 -0
  81. package/skills/bulwark-statusline/SKILL.md +161 -0
  82. package/skills/bulwark-statusline/scripts/statusline.sh +144 -0
  83. package/skills/bulwark-verify/SKILL.md +519 -0
  84. package/skills/code-review/SKILL.md +428 -0
  85. package/skills/code-review/examples/anti-patterns/linting.ts +181 -0
  86. package/skills/code-review/examples/anti-patterns/security.ts +91 -0
  87. package/skills/code-review/examples/anti-patterns/standards.ts +195 -0
  88. package/skills/code-review/examples/anti-patterns/type-safety.ts +108 -0
  89. package/skills/code-review/examples/recommended/linting.ts +195 -0
  90. package/skills/code-review/examples/recommended/security.ts +154 -0
  91. package/skills/code-review/examples/recommended/standards.ts +231 -0
  92. package/skills/code-review/examples/recommended/type-safety.ts +181 -0
  93. package/skills/code-review/frameworks/angular.md +218 -0
  94. package/skills/code-review/frameworks/django.md +235 -0
  95. package/skills/code-review/frameworks/express.md +207 -0
  96. package/skills/code-review/frameworks/flask.md +298 -0
  97. package/skills/code-review/frameworks/generic.md +146 -0
  98. package/skills/code-review/frameworks/react.md +152 -0
  99. package/skills/code-review/frameworks/vue.md +244 -0
  100. package/skills/code-review/references/linting-patterns.md +221 -0
  101. package/skills/code-review/references/security-patterns.md +125 -0
  102. package/skills/code-review/references/standards-patterns.md +246 -0
  103. package/skills/code-review/references/type-safety-patterns.md +130 -0
  104. package/skills/component-patterns/SKILL.md +131 -0
  105. package/skills/component-patterns/references/pattern-cli-command.md +118 -0
  106. package/skills/component-patterns/references/pattern-database.md +166 -0
  107. package/skills/component-patterns/references/pattern-external-api.md +139 -0
  108. package/skills/component-patterns/references/pattern-file-parser.md +168 -0
  109. package/skills/component-patterns/references/pattern-http-server.md +162 -0
  110. package/skills/component-patterns/references/pattern-process-spawner.md +133 -0
  111. package/skills/continuous-feedback/SKILL.md +327 -0
  112. package/skills/continuous-feedback/references/collect-instructions.md +81 -0
  113. package/skills/continuous-feedback/references/specialize-code-review.md +82 -0
  114. package/skills/continuous-feedback/references/specialize-general.md +98 -0
  115. package/skills/continuous-feedback/references/specialize-test-audit.md +81 -0
  116. package/skills/create-skill/SKILL.md +359 -0
  117. package/skills/create-skill/references/agent-conventions.md +194 -0
  118. package/skills/create-skill/references/agent-template.md +195 -0
  119. package/skills/create-skill/references/content-guidance.md +291 -0
  120. package/skills/create-skill/references/decision-framework.md +124 -0
  121. package/skills/create-skill/references/template-pipeline.md +217 -0
  122. package/skills/create-skill/references/template-reference-heavy.md +111 -0
  123. package/skills/create-skill/references/template-research.md +210 -0
  124. package/skills/create-skill/references/template-script-driven.md +172 -0
  125. package/skills/create-skill/references/template-simple.md +80 -0
  126. package/skills/create-subagent/SKILL.md +353 -0
  127. package/skills/create-subagent/references/agent-conventions.md +268 -0
  128. package/skills/create-subagent/references/content-guidance.md +232 -0
  129. package/skills/create-subagent/references/decision-framework.md +134 -0
  130. package/skills/create-subagent/references/template-single-agent.md +192 -0
  131. package/skills/fix-bug/SKILL.md +241 -0
  132. package/skills/governance-protocol/SKILL.md +116 -0
  133. package/skills/init/SKILL.md +341 -0
  134. package/skills/issue-debugging/SKILL.md +385 -0
  135. package/skills/issue-debugging/references/anti-patterns.md +245 -0
  136. package/skills/issue-debugging/references/debug-report-schema.md +227 -0
  137. package/skills/mock-detection/SKILL.md +511 -0
  138. package/skills/mock-detection/references/false-positive-prevention.md +402 -0
  139. package/skills/mock-detection/references/stub-patterns.md +236 -0
  140. package/skills/pipeline-templates/SKILL.md +215 -0
  141. package/skills/pipeline-templates/references/code-change-workflow.md +277 -0
  142. package/skills/pipeline-templates/references/code-review.md +336 -0
  143. package/skills/pipeline-templates/references/fix-validation.md +421 -0
  144. package/skills/pipeline-templates/references/new-feature.md +335 -0
  145. package/skills/pipeline-templates/references/research-brainstorm.md +161 -0
  146. package/skills/pipeline-templates/references/research-planning.md +257 -0
  147. package/skills/pipeline-templates/references/test-audit.md +389 -0
  148. package/skills/pipeline-templates/references/test-execution-fix.md +238 -0
  149. package/skills/plan-creation/SKILL.md +497 -0
  150. package/skills/product-ideation/SKILL.md +372 -0
  151. package/skills/product-ideation/references/analysis-frameworks.md +161 -0
  152. package/skills/session-handoff/SKILL.md +139 -0
  153. package/skills/session-handoff/references/examples.md +223 -0
  154. package/skills/setup-lsp/SKILL.md +312 -0
  155. package/skills/setup-lsp/references/server-registry.md +85 -0
  156. package/skills/setup-lsp/references/troubleshooting.md +135 -0
  157. package/skills/subagent-output-templating/SKILL.md +415 -0
  158. package/skills/subagent-output-templating/references/examples.md +440 -0
  159. package/skills/subagent-prompting/SKILL.md +364 -0
  160. package/skills/subagent-prompting/references/examples.md +342 -0
  161. package/skills/test-audit/SKILL.md +531 -0
  162. package/skills/test-audit/references/known-limitations.md +41 -0
  163. package/skills/test-audit/references/priority-classification.md +30 -0
  164. package/skills/test-audit/references/prompts/deep-mode-detection.md +83 -0
  165. package/skills/test-audit/references/prompts/synthesis.md +57 -0
  166. package/skills/test-audit/references/rewrite-instructions.md +46 -0
  167. package/skills/test-audit/references/schemas/audit-output.yaml +100 -0
  168. package/skills/test-audit/references/schemas/diagnostic-output.yaml +49 -0
  169. package/skills/test-audit/scripts/data-flow-analyzer.ts +509 -0
  170. package/skills/test-audit/scripts/integration-mock-detector.ts +462 -0
  171. package/skills/test-audit/scripts/package.json +20 -0
  172. package/skills/test-audit/scripts/skip-detector.ts +211 -0
  173. package/skills/test-audit/scripts/verification-counter.ts +295 -0
  174. package/skills/test-classification/SKILL.md +310 -0
  175. package/skills/test-fixture-creation/SKILL.md +295 -0
@@ -0,0 +1,295 @@
1
+ import { Project, SyntaxKind, Node, CallExpression, SourceFile } from 'ts-morph';
2
+ import * as path from 'path';
3
+ import * as fs from 'fs';
4
+
5
+ // --- Types ---
6
+
7
+ interface FileMetrics {
8
+ file: string;
9
+ metrics: {
10
+ total_lines: number;
11
+ test_logic_lines: number;
12
+ assertion_lines: number;
13
+ setup_lines: number;
14
+ effectiveness_percent: number;
15
+ framework_detected: string;
16
+ };
17
+ breakdown: {
18
+ imports: number;
19
+ comments: number;
20
+ empty_lines: number;
21
+ boilerplate: number;
22
+ test_logic: number;
23
+ };
24
+ }
25
+
26
+ interface ErrorOutput {
27
+ error: string;
28
+ file: string;
29
+ }
30
+
31
+ // --- Constants ---
32
+
33
+ const TEST_FRAMEWORK_WRAPPERS = new Set([
34
+ 'describe', 'it', 'test', 'beforeEach', 'afterEach', 'beforeAll', 'afterAll',
35
+ 'fdescribe', 'fit', 'xdescribe', 'xit',
36
+ ]);
37
+
38
+ const JEST_VITEST_ASSERTION_PATTERNS = [
39
+ /^expect\b/,
40
+ ];
41
+
42
+ const NODE_ASSERT_PATTERNS = [
43
+ /^assert\b/,
44
+ /^assert\./,
45
+ ];
46
+
47
+ const CHAI_ASSERTION_PATTERNS = [
48
+ /\.should\./,
49
+ /\.to\./,
50
+ /\.expect\b/,
51
+ ];
52
+
53
+ // --- Framework Detection ---
54
+
55
+ function detectFramework(sourceFile: SourceFile): string {
56
+ const text = sourceFile.getFullText();
57
+
58
+ if (text.includes('from \'vitest\'') || text.includes('from "vitest"')) {
59
+ return 'vitest';
60
+ }
61
+ if (text.includes('jest.') || text.includes('from \'@jest') || text.includes('from "@jest')) {
62
+ return 'jest';
63
+ }
64
+ if (text.includes('from \'mocha\'') || text.includes('from "mocha"')) {
65
+ return 'mocha';
66
+ }
67
+ if (text.includes('from \'node:test\'') || text.includes('from "node:test"')) {
68
+ return 'node:test';
69
+ }
70
+ // Default: if it uses describe/it/expect, assume jest (most common)
71
+ if (text.includes('describe(') || text.includes('it(') || text.includes('expect(')) {
72
+ return 'jest';
73
+ }
74
+ return 'unknown';
75
+ }
76
+
77
+ // --- Line Classification ---
78
+
79
+ function isEmptyLine(line: string): boolean {
80
+ return line.trim() === '';
81
+ }
82
+
83
+ function isCommentLine(line: string): boolean {
84
+ const trimmed = line.trim();
85
+ return trimmed.startsWith('//') || trimmed.startsWith('/*') || trimmed.startsWith('*') || trimmed.startsWith('*/');
86
+ }
87
+
88
+ function isImportLine(line: string): boolean {
89
+ const trimmed = line.trim();
90
+ return trimmed.startsWith('import ') || trimmed.startsWith('import{') || trimmed.startsWith('from ');
91
+ }
92
+
93
+ // --- AST Analysis ---
94
+
95
+ function isFrameworkWrapperCall(node: CallExpression): boolean {
96
+ const expr = node.getExpression();
97
+ const text = expr.getText();
98
+
99
+ // Direct calls: describe(...), it(...), test(...)
100
+ if (TEST_FRAMEWORK_WRAPPERS.has(text)) {
101
+ return true;
102
+ }
103
+
104
+ // Property access calls: describe.skip(...), it.only(...), test.todo(...)
105
+ if (Node.isPropertyAccessExpression(expr)) {
106
+ const objText = expr.getExpression().getText();
107
+ if (TEST_FRAMEWORK_WRAPPERS.has(objText)) {
108
+ return true;
109
+ }
110
+ }
111
+
112
+ return false;
113
+ }
114
+
115
+ function isAssertionStatement(lineText: string): boolean {
116
+ const trimmed = lineText.trim();
117
+
118
+ for (const pattern of JEST_VITEST_ASSERTION_PATTERNS) {
119
+ if (pattern.test(trimmed)) return true;
120
+ }
121
+ for (const pattern of NODE_ASSERT_PATTERNS) {
122
+ if (pattern.test(trimmed)) return true;
123
+ }
124
+ for (const pattern of CHAI_ASSERTION_PATTERNS) {
125
+ if (pattern.test(trimmed)) return true;
126
+ }
127
+
128
+ return false;
129
+ }
130
+
131
+ function getFrameworkWrapperLines(sourceFile: SourceFile): Set<number> {
132
+ const wrapperLines = new Set<number>();
133
+
134
+ sourceFile.forEachDescendant((node) => {
135
+ if (!Node.isCallExpression(node)) return;
136
+ if (!isFrameworkWrapperCall(node)) return;
137
+
138
+ // The wrapper call line itself (e.g., `describe('Calculator', () => {`)
139
+ const startLine = node.getStartLineNumber();
140
+ wrapperLines.add(startLine);
141
+
142
+ // Closing brace of the callback — find the last argument
143
+ const args = node.getArguments();
144
+ if (args.length > 0) {
145
+ const lastArg = args[args.length - 1];
146
+ if (Node.isArrowFunction(lastArg) || Node.isFunctionExpression(lastArg)) {
147
+ const endLine = lastArg.getEndLineNumber();
148
+ // The closing `});` line is boilerplate
149
+ wrapperLines.add(endLine);
150
+ }
151
+ }
152
+
153
+ // Also mark the closing `});` of the call expression
154
+ const callEndLine = node.getEndLineNumber();
155
+ wrapperLines.add(callEndLine);
156
+ });
157
+
158
+ return wrapperLines;
159
+ }
160
+
161
+ function analyzeFile(filePath: string, project: Project): FileMetrics | ErrorOutput {
162
+ let sourceFile: SourceFile;
163
+ try {
164
+ sourceFile = project.addSourceFileAtPath(filePath);
165
+ } catch (err) {
166
+ return {
167
+ error: `Failed to parse: ${err instanceof Error ? err.message : String(err)}`,
168
+ file: filePath,
169
+ };
170
+ }
171
+
172
+ const lines = sourceFile.getFullText().split('\n');
173
+ const totalLines = lines.length;
174
+ const framework = detectFramework(sourceFile);
175
+ const wrapperLines = getFrameworkWrapperLines(sourceFile);
176
+
177
+ let importCount = 0;
178
+ let commentCount = 0;
179
+ let emptyCount = 0;
180
+ let boilerplateCount = 0;
181
+ let assertionCount = 0;
182
+ let testLogicCount = 0;
183
+
184
+ for (let i = 0; i < lines.length; i++) {
185
+ const line = lines[i];
186
+ const lineNum = i + 1; // 1-indexed
187
+
188
+ if (isEmptyLine(line)) {
189
+ emptyCount++;
190
+ continue;
191
+ }
192
+
193
+ if (isCommentLine(line)) {
194
+ commentCount++;
195
+ continue;
196
+ }
197
+
198
+ if (isImportLine(line)) {
199
+ importCount++;
200
+ continue;
201
+ }
202
+
203
+ if (wrapperLines.has(lineNum)) {
204
+ boilerplateCount++;
205
+ continue;
206
+ }
207
+
208
+ // Remaining lines are test logic
209
+ if (isAssertionStatement(line)) {
210
+ assertionCount++;
211
+ }
212
+ testLogicCount++;
213
+ }
214
+
215
+ // Setup lines = test logic minus assertions
216
+ const setupLines = testLogicCount - assertionCount;
217
+
218
+ // Effectiveness = assertion lines / test logic lines
219
+ const effectivenessPercent = testLogicCount > 0
220
+ ? Math.round((assertionCount / testLogicCount) * 10000) / 100
221
+ : 0;
222
+
223
+ return {
224
+ file: filePath,
225
+ metrics: {
226
+ total_lines: totalLines,
227
+ test_logic_lines: testLogicCount,
228
+ assertion_lines: assertionCount,
229
+ setup_lines: setupLines,
230
+ effectiveness_percent: effectivenessPercent,
231
+ framework_detected: framework,
232
+ },
233
+ breakdown: {
234
+ imports: importCount,
235
+ comments: commentCount,
236
+ empty_lines: emptyCount,
237
+ boilerplate: boilerplateCount,
238
+ test_logic: testLogicCount,
239
+ },
240
+ };
241
+ }
242
+
243
+ // --- Main ---
244
+
245
+ function main(): void {
246
+ const args = process.argv.slice(2);
247
+
248
+ if (args.length === 0) {
249
+ const error: ErrorOutput = {
250
+ error: 'No file paths provided. Usage: verification-counter.ts <file1> [file2] ...',
251
+ file: '',
252
+ };
253
+ process.stderr.write(JSON.stringify(error) + '\n');
254
+ process.exit(1);
255
+ }
256
+
257
+ const project = new Project({
258
+ tsConfigFilePath: undefined,
259
+ skipAddingFilesFromTsConfig: true,
260
+ compilerOptions: {
261
+ allowJs: true,
262
+ checkJs: false,
263
+ noEmit: true,
264
+ strict: false,
265
+ skipLibCheck: true,
266
+ },
267
+ });
268
+
269
+ // Expand directory args to individual test files
270
+ const filePaths: string[] = [];
271
+ for (const arg of args) {
272
+ const resolved = path.resolve(arg);
273
+ if (fs.existsSync(resolved) && fs.statSync(resolved).isDirectory()) {
274
+ const entries = fs.readdirSync(resolved).filter(f => /\.(test|spec)\.[tj]sx?$/.test(f));
275
+ filePaths.push(...entries.map(f => path.join(resolved, f)));
276
+ } else {
277
+ filePaths.push(resolved);
278
+ }
279
+ }
280
+
281
+ const results: (FileMetrics | ErrorOutput)[] = [];
282
+
283
+ for (const filePath of filePaths) {
284
+ const result = analyzeFile(filePath, project);
285
+ results.push(result);
286
+ }
287
+
288
+ if (results.length === 1) {
289
+ process.stdout.write(JSON.stringify(results[0], null, 2) + '\n');
290
+ } else {
291
+ process.stdout.write(JSON.stringify(results, null, 2) + '\n');
292
+ }
293
+ }
294
+
295
+ main();
@@ -0,0 +1,310 @@
1
+ ---
2
+ name: test-classification
3
+ description: Prompt template for test classification stage in Test Audit pipeline
4
+ user-invocable: false
5
+ ---
6
+
7
+ # Test Classification
8
+
9
+ Prompt template for surface-level test classification and triage. Designed for a Haiku sub-agent to quickly categorize test files and flag those needing deep analysis.
10
+
11
+ ---
12
+
13
+ ## When to Use This Skill
14
+
15
+ **This is an internal skill loaded by the orchestrator during Test Audit pipeline.**
16
+
17
+ | Context | Action |
18
+ |---------|--------|
19
+ | `/test-audit` invoked | Orchestrator loads this skill for Stage 1 |
20
+ | Test Audit pipeline triggered by hook | Orchestrator loads this skill for Stage 1 |
21
+ | Need to classify test files | Load directly as prompt template for Haiku |
22
+
23
+ **DO NOT use for:**
24
+ - Direct user invocation (not user-invocable)
25
+ - Mock detection (use `mock-detection` skill)
26
+ - Full audit synthesis (use `test-audit` skill)
27
+
28
+ ---
29
+
30
+ ## Role in Test Audit Pipeline
31
+
32
+ This skill provides the **first stage** prompt template:
33
+
34
+ ```
35
+ test-audit (P0.8) orchestrates:
36
+ Stage 1: test-classification (Haiku) → classification YAML
37
+ Stage 2: mock-detection (Sonnet) → violations YAML
38
+ Stage 3: synthesis (Sonnet) → audit report
39
+ ```
40
+
41
+ The orchestrator loads this skill and constructs a 4-part prompt for a general-purpose Haiku sub-agent.
42
+
43
+ ---
44
+
45
+ ## 4-Part Prompt Template
46
+
47
+ ### GOAL
48
+
49
+ Classify all test files in `{target}` by type and flag files needing deep analysis for mock appropriateness.
50
+
51
+ ### CONSTRAINTS
52
+
53
+ - Do NOT modify any files
54
+ - Classify every test file found
55
+ - Use filename-first classification (content validates)
56
+ - Flag mock+integration mismatches for deep analysis
57
+ - Use AST verification_lines as ground truth when provided in context (do NOT re-count).
58
+ Only fall back to manual counting if AST data is unavailable.
59
+ - Complete within 30 tool calls
60
+
61
+ ### CONTEXT
62
+
63
+ **Target directory:** `{target}`
64
+
65
+ **Test file patterns:** `*.test.*`, `*.spec.*`, `test_*`, `*.integration.*`, `*.e2e.*`
66
+
67
+ **Classification rules:** See "Classification Logic" section below
68
+
69
+ **Deep analysis triggers:** See "needs_deep_analysis Triggers" section below
70
+
71
+ **Line counting rules:** See "Verification Line Counting" section below
72
+
73
+ **AST verification_lines (MANDATORY when available):**
74
+ If the orchestrator provides `ast_verification_lines` per file, use that value directly as `verification_lines` in the output. Do NOT override with your own count. The AST value is deterministic and precise; heuristic counting at scale is error-prone.
75
+
76
+ ### OUTPUT
77
+
78
+ Write classification to: `logs/test-classification-{YYYYMMDD-HHMMSS}.yaml`
79
+
80
+ Write diagnostics to: `logs/diagnostics/test-classification-{YYYYMMDD-HHMMSS}.yaml`
81
+
82
+ Use the schema specified in "Output Schema" section below.
83
+
84
+ ---
85
+
86
+ ## Classification Logic
87
+
88
+ ### 1. Filename Pattern (Primary)
89
+
90
+ | Pattern | Category |
91
+ |---------|----------|
92
+ | `*.integration.*` | integration |
93
+ | `*.e2e.*` | e2e |
94
+ | `*.test.*`, `*.spec.*`, `test_*` | unit (default) |
95
+
96
+ ### 2. Content Validation (Secondary)
97
+
98
+ After filename classification, scan content to validate:
99
+
100
+ | Content Signal | Interpretation |
101
+ |----------------|----------------|
102
+ | Imports test framework (`jest`, `vitest`, `mocha`, `pytest`) | Confirms test file |
103
+ | Imports system modules (`child_process`, `fs`, `http`) | Note for risk assessment |
104
+ | Contains `jest.mock()`, `vi.mock()`, `patch()` | Mock indicator |
105
+ | Contains `describe(`, `it(`, `test(` | Standard test structure |
106
+
107
+ ### 3. Risk Detection
108
+
109
+ | Risk | Condition | Recommendation |
110
+ |------|-----------|----------------|
111
+ | `test_management` | Single file contains multiple test types (unit + integration) | Split into separate files |
112
+
113
+ ---
114
+
115
+ ## needs_deep_analysis Triggers
116
+
117
+ Flag a file for deep analysis when ANY of these conditions are met:
118
+
119
+ ### Always Flag (Regardless of Mock Indicators)
120
+
121
+ | Trigger | Reason |
122
+ |---------|--------|
123
+ | `*.integration.*` file | Integration tests need chain verification - may have T3+ violations without explicit mocks |
124
+ | `*.e2e.*` file | E2E tests should have minimal mocking - verify end-to-end flow |
125
+
126
+ **Rationale:** The absence of `jest.mock()` in an integration test doesn't mean it's clean. T3+ violations (broken integration chains) use inline mock data instead of upstream function outputs. These are only detectable through deep analysis.
127
+
128
+ ### Mock Indicator Triggers
129
+
130
+ | Trigger | Reason |
131
+ |---------|--------|
132
+ | Unit test with any `jest.mock()` / `vi.mock()` on core modules | Potential T1 (mocking SUT) or over-mocking |
133
+ | Unit test with >3 top-level mocks | Unusual mock density suggests over-mocking |
134
+ | Unit test mocking core modules (`spawn`, `fs`, `fetch`, `http`) | Known risky patterns requiring contextual analysis |
135
+
136
+ ---
137
+
138
+ ## Verification Line Counting
139
+
140
+ Count "verification lines" per file for test effectiveness calculation. This count is used by P0.7 to calculate how many effective test lines remain after violations are identified.
141
+
142
+ ### Exclude from Count
143
+
144
+ - Comment lines (`//`, `/* */`, `/** */`, `#`)
145
+ - Import/require statements (`import`, `require`, `from`)
146
+ - Empty/whitespace-only lines
147
+ - Test framework boilerplate:
148
+ - `describe(`, `it(`, `test(`
149
+ - `beforeEach(`, `afterEach(`
150
+ - `beforeAll(`, `afterAll(`
151
+ - `setUp(`, `tearDown(`
152
+
153
+ ### Include in Count
154
+
155
+ - Actual test logic (assertions, function calls, variable assignments within tests)
156
+ - Mock setup lines (these may be marked as violation scope by P0.7)
157
+ - Assertion statements (`expect(`, `assert`, `should`)
158
+ - Setup code within test bodies
159
+
160
+ ---
161
+
162
+ ## Output Schema
163
+
164
+ ```yaml
165
+ metadata:
166
+ skill: test-classification
167
+ timestamp: "{ISO-8601}"
168
+ target: "{directory}"
169
+ model: haiku
170
+
171
+ files:
172
+ - path: tests/proxy.test.ts
173
+ category: unit
174
+ total_lines: 150
175
+ verification_lines: 95 # Use ast_verification_lines if provided; only count manually if unavailable
176
+ mock_indicators:
177
+ - "jest.spyOn(child_process, 'spawn')"
178
+ needs_deep_analysis: true
179
+ deep_analysis_reason: "Unit test mocks core module (spawn)"
180
+
181
+ - path: tests/api.integration.ts
182
+ category: integration
183
+ total_lines: 80
184
+ verification_lines: 55
185
+ mock_indicators:
186
+ - "jest.mock('node-fetch')"
187
+ needs_deep_analysis: true
188
+ deep_analysis_reason: "Integration test contains mocks"
189
+
190
+ - path: tests/utils.test.ts
191
+ category: unit
192
+ total_lines: 60
193
+ verification_lines: 40
194
+ mock_indicators: []
195
+ needs_deep_analysis: false
196
+
197
+ risks:
198
+ test_management:
199
+ - path: tests/everything.test.ts
200
+ reason: "Single file contains unit, integration, and e2e tests"
201
+ recommendation: "Split into separate files by test type"
202
+
203
+ summary:
204
+ total_files: 25
205
+ by_category:
206
+ unit: 15
207
+ integration: 8
208
+ e2e: 2
209
+ total_verification_lines: 1250
210
+ needs_deep_analysis: 5
211
+ test_management_risks: 1
212
+ ```
213
+
214
+ ---
215
+
216
+ ## Diagnostic Output
217
+
218
+ Write diagnostic output to `logs/diagnostics/test-classification-{YYYYMMDD-HHMMSS}.yaml`:
219
+
220
+ ```yaml
221
+ diagnostic:
222
+ skill: test-classification
223
+ timestamp: "{ISO-8601}"
224
+ model: haiku
225
+
226
+ execution:
227
+ tool_calls: 15
228
+ files_scanned: 25
229
+ classification_time_estimate: "surface scan"
230
+
231
+ decisions:
232
+ - file: tests/proxy.test.ts
233
+ decision: needs_deep_analysis
234
+ reason: "Found jest.spyOn on child_process.spawn"
235
+ confidence: high
236
+
237
+ - file: tests/utils.test.ts
238
+ decision: clean
239
+ reason: "No mock indicators found"
240
+ confidence: high
241
+
242
+ errors: []
243
+ ```
244
+
245
+ ---
246
+
247
+ ## Integration Notes
248
+
249
+ ### Orchestrator Usage
250
+
251
+ The orchestrator (P0.8) constructs the full prompt by:
252
+
253
+ 1. Loading this skill content
254
+ 2. Substituting `{target}` with user-provided path or inferred target
255
+ 3. Spawning: `Task(subagent_type="general-purpose", model="haiku", prompt=...)`
256
+ 4. Reading output from `logs/test-classification-{YYYYMMDD-HHMMSS}.yaml`
257
+
258
+ ### Downstream Usage
259
+
260
+ P0.7 (mock-detection) receives:
261
+ - List of files with `needs_deep_analysis: true`
262
+ - `verification_lines` count per file (for effectiveness calculation)
263
+ - `mock_indicators` as starting points for deep analysis
264
+
265
+ ---
266
+
267
+ ## Batching for Scale
268
+
269
+ When processing large test suites (>20 files), the orchestrator must batch classification to avoid context limits.
270
+
271
+ ### Batching Instructions
272
+
273
+ ```
274
+ IF file_count > 20:
275
+ Split files into batches of 20-25
276
+ FOR each batch:
277
+ Spawn Haiku sub-agent with batch file list
278
+ Collect classification YAML for batch
279
+ Merge all batch results into single classification output
280
+ ELSE:
281
+ Process all files in single sub-agent call
282
+ ```
283
+
284
+ ### Batch Merge Strategy
285
+
286
+ When merging batch results:
287
+ 1. Combine all `files` arrays
288
+ 2. Combine all `risks` entries
289
+ 3. Recalculate `summary` totals across all batches
290
+ 4. Preserve individual file classifications exactly
291
+
292
+ ### Parallel Execution
293
+
294
+ For optimal performance, spawn batch sub-agents in parallel:
295
+
296
+ ```
297
+ Task(subagent_type="general-purpose", model="haiku", prompt=batch1_prompt, run_in_background=true)
298
+ Task(subagent_type="general-purpose", model="haiku", prompt=batch2_prompt, run_in_background=true)
299
+ ...
300
+ ```
301
+
302
+ Read all outputs after completion, then merge.
303
+
304
+ ---
305
+
306
+ ## Related Skills
307
+
308
+ - `mock-detection` (P0.7) - Deep analysis of flagged files
309
+ - `test-audit` (P0.8) - Orchestration and synthesis
310
+ - `pipeline-templates` (P0.3) - Test Audit pipeline definition