fraim-framework 2.0.36 → 2.0.37

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 (165) hide show
  1. package/bin/fraim.js +5 -52
  2. package/dist/registry/scripts/build-scripts-generator.js +205 -0
  3. package/dist/registry/scripts/fraim-config.js +61 -0
  4. package/dist/registry/scripts/generic-issues-api.js +100 -0
  5. package/dist/registry/scripts/openapi-generator.js +664 -0
  6. package/dist/registry/scripts/performance/profile-server.js +390 -0
  7. package/dist/scripts/build-stub-registry.js +108 -0
  8. package/dist/src/cli/commands/doctor.js +5 -5
  9. package/dist/src/cli/commands/sync.js +33 -19
  10. package/dist/test-utils.js +96 -0
  11. package/dist/tests/esm-compat.js +11 -0
  12. package/dist/tests/test-chalk-esm-issue.js +159 -0
  13. package/dist/tests/test-chalk-real-world.js +265 -0
  14. package/dist/tests/test-chalk-regression.js +2 -18
  15. package/dist/tests/test-chalk-resolution-issue.js +304 -0
  16. package/dist/tests/test-fraim-install-chalk-issue.js +254 -0
  17. package/dist/tests/test-npm-resolution-diagnostic.js +140 -0
  18. package/dist/tests/test-package-size.js +88 -0
  19. package/dist/tests/test-prep-issue.js +34 -1
  20. package/dist/tests/test-stub-registry.js +120 -0
  21. package/dist/tests/test-sync-stubs.js +143 -0
  22. package/package.json +6 -8
  23. package/registry/agent-guardrails.md +62 -62
  24. package/registry/scripts/code-quality-check.sh +559 -559
  25. package/registry/scripts/detect-tautological-tests.sh +38 -38
  26. package/registry/scripts/prep-issue.sh +61 -30
  27. package/registry/scripts/validate-openapi-limits.ts +366 -366
  28. package/registry/scripts/validate-test-coverage.ts +280 -280
  29. package/registry/scripts/verify-pr-comments.sh +70 -70
  30. package/registry/stubs/workflows/bootstrap/create-architecture.md +11 -0
  31. package/registry/stubs/workflows/bootstrap/detect-broken-windows.md +11 -0
  32. package/registry/stubs/workflows/bootstrap/evaluate-code-quality.md +11 -0
  33. package/registry/stubs/workflows/bootstrap/verify-test-coverage.md +11 -0
  34. package/registry/stubs/workflows/business-development/create-business-plan.md +11 -0
  35. package/registry/stubs/workflows/business-development/ideate-business-opportunity.md +11 -0
  36. package/registry/stubs/workflows/business-development/price-product.md +18 -0
  37. package/registry/stubs/workflows/convert-to-pdf.md +11 -0
  38. package/registry/stubs/workflows/customer-development/insight-analysis.md +11 -0
  39. package/registry/stubs/workflows/customer-development/insight-triage.md +11 -0
  40. package/registry/stubs/workflows/customer-development/interview-preparation.md +11 -0
  41. package/registry/stubs/workflows/customer-development/linkedin-outreach.md +11 -0
  42. package/registry/stubs/workflows/customer-development/strategic-brainstorming.md +11 -0
  43. package/registry/stubs/workflows/customer-development/thank-customers.md +11 -0
  44. package/registry/stubs/workflows/customer-development/weekly-newsletter.md +11 -0
  45. package/registry/stubs/workflows/deploy/cloud-deployment.md +11 -0
  46. package/registry/stubs/workflows/improve-fraim/contribute.md +11 -0
  47. package/registry/stubs/workflows/improve-fraim/file-issue.md +11 -0
  48. package/registry/stubs/workflows/marketing/content-creation.md +11 -0
  49. package/registry/stubs/workflows/marketing/hbr-article.md +11 -0
  50. package/registry/stubs/workflows/marketing/launch-checklist.md +11 -0
  51. package/registry/stubs/workflows/marketing/marketing-strategy.md +11 -0
  52. package/registry/stubs/workflows/marketing/storytelling.md +11 -0
  53. package/registry/stubs/workflows/performance/analyze-performance.md +11 -0
  54. package/registry/stubs/workflows/product-building/design.md +11 -0
  55. package/registry/stubs/workflows/product-building/implement.md +12 -0
  56. package/registry/stubs/workflows/product-building/iterate-on-pr-comments.md +11 -0
  57. package/registry/stubs/workflows/product-building/prep-issue.md +11 -0
  58. package/registry/stubs/workflows/product-building/prototype.md +11 -0
  59. package/registry/stubs/workflows/product-building/resolve.md +11 -0
  60. package/registry/stubs/workflows/product-building/retrospect.md +11 -0
  61. package/registry/stubs/workflows/product-building/spec.md +11 -0
  62. package/registry/stubs/workflows/product-building/test.md +11 -0
  63. package/registry/stubs/workflows/quality-assurance/browser-validation.md +11 -0
  64. package/registry/stubs/workflows/quality-assurance/iterative-improvement-cycle.md +11 -0
  65. package/registry/stubs/workflows/replicate/replicate-discovery.md +11 -0
  66. package/registry/stubs/workflows/replicate/replicate-to-issues.md +11 -0
  67. package/registry/stubs/workflows/reviewer/review-implementation-vs-design-spec.md +11 -0
  68. package/registry/stubs/workflows/reviewer/review-implementation-vs-feature-spec.md +11 -0
  69. package/registry/stubs/workflows/startup-credits/aws-activate-application.md +11 -0
  70. package/registry/stubs/workflows/startup-credits/google-cloud-application.md +11 -0
  71. package/registry/stubs/workflows/startup-credits/microsoft-azure-application.md +11 -0
  72. package/.github/workflows/ci.yml +0 -65
  73. package/.github/workflows/deploy-fraim.yml +0 -87
  74. package/.github/workflows/phase-change.yml +0 -251
  75. package/.github/workflows/status-change.yml +0 -68
  76. package/.github/workflows/sync-on-pr-review.yml +0 -66
  77. package/examples/simple-webapp/TESTING.md +0 -62
  78. package/examples/simple-webapp/example-test.ts +0 -186
  79. package/registry/github/workflows/ci.yml +0 -51
  80. package/registry/github/workflows/phase-change.yml +0 -251
  81. package/registry/github/workflows/status-change.yml +0 -68
  82. package/registry/github/workflows/sync-on-pr-review.yml +0 -66
  83. package/registry/mcp-template.jsonc +0 -29
  84. package/registry/rules/agent-success-criteria.md +0 -52
  85. package/registry/rules/agent-testing-guidelines.md +0 -502
  86. package/registry/rules/architecture.md +0 -52
  87. package/registry/rules/communication.md +0 -122
  88. package/registry/rules/continuous-learning.md +0 -55
  89. package/registry/rules/debugging-multitenancy-issues.md +0 -85
  90. package/registry/rules/ephemeral-execution.md +0 -57
  91. package/registry/rules/git-safe-commands.md +0 -34
  92. package/registry/rules/hitl-ppe-record-analysis.md +0 -302
  93. package/registry/rules/integrity-and-test-ethics.md +0 -275
  94. package/registry/rules/local-development.md +0 -254
  95. package/registry/rules/merge-requirements.md +0 -231
  96. package/registry/rules/simplicity.md +0 -118
  97. package/registry/rules/software-development-lifecycle.md +0 -105
  98. package/registry/rules/spike-first-development.md +0 -205
  99. package/registry/rules/successful-debugging-patterns.md +0 -491
  100. package/registry/rules/telemetry.md +0 -67
  101. package/registry/templates/bootstrap/ARCHITECTURE-TEMPLATE.md +0 -53
  102. package/registry/templates/bootstrap/CODE-QUALITY-REPORT-TEMPLATE.md +0 -37
  103. package/registry/templates/bootstrap/TEST-COVERAGE-REPORT-TEMPLATE.md +0 -35
  104. package/registry/templates/business-development/IDEATION-REPORT-TEMPLATE.md +0 -29
  105. package/registry/templates/business-development/PRICING-STRATEGY-TEMPLATE.md +0 -126
  106. package/registry/templates/customer-development/customer-interview-template.md +0 -99
  107. package/registry/templates/customer-development/follow-up-email-templates.md +0 -132
  108. package/registry/templates/customer-development/insight-analysis-template.md +0 -74
  109. package/registry/templates/customer-development/strategic-recommendations-template.md +0 -53
  110. package/registry/templates/customer-development/thank-you-email-template.html +0 -124
  111. package/registry/templates/customer-development/thank-you-note-template.md +0 -16
  112. package/registry/templates/customer-development/triage-log-template.md +0 -278
  113. package/registry/templates/customer-development/weekly-newsletter-template.html +0 -204
  114. package/registry/templates/evidence/Design-Evidence.md +0 -30
  115. package/registry/templates/evidence/Implementation-BugEvidence.md +0 -86
  116. package/registry/templates/evidence/Implementation-FeatureEvidence.md +0 -121
  117. package/registry/templates/evidence/Spec-Evidence.md +0 -19
  118. package/registry/templates/help/HelpNeeded.md +0 -14
  119. package/registry/templates/marketing/HBR-ARTICLE-TEMPLATE.md +0 -66
  120. package/registry/templates/replicate/implementation-checklist.md +0 -39
  121. package/registry/templates/replicate/use-cases-template.md +0 -88
  122. package/registry/templates/retrospective/RETROSPECTIVE-TEMPLATE.md +0 -55
  123. package/registry/templates/specs/BUGSPEC-TEMPLATE.md +0 -37
  124. package/registry/templates/specs/FEATURESPEC-TEMPLATE.md +0 -29
  125. package/registry/templates/specs/TECHSPEC-TEMPLATE.md +0 -39
  126. package/registry/workflows/bootstrap/create-architecture.md +0 -38
  127. package/registry/workflows/bootstrap/evaluate-code-quality.md +0 -36
  128. package/registry/workflows/bootstrap/verify-test-coverage.md +0 -37
  129. package/registry/workflows/business-development/create-business-plan.md +0 -737
  130. package/registry/workflows/business-development/ideate-business-opportunity.md +0 -55
  131. package/registry/workflows/business-development/price-product.md +0 -325
  132. package/registry/workflows/convert-to-pdf.md +0 -235
  133. package/registry/workflows/customer-development/insight-analysis.md +0 -156
  134. package/registry/workflows/customer-development/insight-triage.md +0 -933
  135. package/registry/workflows/customer-development/interview-preparation.md +0 -421
  136. package/registry/workflows/customer-development/linkedin-outreach.md +0 -593
  137. package/registry/workflows/customer-development/strategic-brainstorming.md +0 -146
  138. package/registry/workflows/customer-development/thank-customers.md +0 -203
  139. package/registry/workflows/customer-development/weekly-newsletter.md +0 -366
  140. package/registry/workflows/deploy/cloud-deployment.md +0 -310
  141. package/registry/workflows/improve-fraim/contribute.md +0 -32
  142. package/registry/workflows/improve-fraim/file-issue.md +0 -32
  143. package/registry/workflows/marketing/content-creation.md +0 -37
  144. package/registry/workflows/marketing/hbr-article.md +0 -73
  145. package/registry/workflows/marketing/launch-checklist.md +0 -37
  146. package/registry/workflows/marketing/marketing-strategy.md +0 -45
  147. package/registry/workflows/performance/analyze-performance.md +0 -65
  148. package/registry/workflows/product-building/design.md +0 -130
  149. package/registry/workflows/product-building/implement.md +0 -315
  150. package/registry/workflows/product-building/iterate-on-pr-comments.md +0 -70
  151. package/registry/workflows/product-building/prep-issue.md +0 -43
  152. package/registry/workflows/product-building/prototype.md +0 -60
  153. package/registry/workflows/product-building/resolve.md +0 -164
  154. package/registry/workflows/product-building/retrospect.md +0 -86
  155. package/registry/workflows/product-building/spec.md +0 -117
  156. package/registry/workflows/product-building/test.md +0 -120
  157. package/registry/workflows/quality-assurance/browser-validation.md +0 -221
  158. package/registry/workflows/quality-assurance/iterative-improvement-cycle.md +0 -562
  159. package/registry/workflows/replicate/replicate-discovery.md +0 -336
  160. package/registry/workflows/replicate/replicate-to-issues.md +0 -319
  161. package/registry/workflows/reviewer/review-implementation-vs-design-spec.md +0 -632
  162. package/registry/workflows/reviewer/review-implementation-vs-feature-spec.md +0 -669
  163. package/registry/workflows/startup-credits/aws-activate-application.md +0 -535
  164. package/registry/workflows/startup-credits/google-cloud-application.md +0 -647
  165. package/registry/workflows/startup-credits/microsoft-azure-application.md +0 -538
@@ -1,280 +1,280 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Validation Plan Coverage Checker
4
- *
5
- * Extracts validation plan from spec/design documents and verifies evidence coverage.
6
- * Used by Gate 9 in code-quality-check.sh
7
- *
8
- * Usage: npx tsx <this-script> <issue-number>
9
- */
10
-
11
- import * as fs from 'fs';
12
- import * as path from 'path';
13
-
14
- interface ValidationScenario {
15
- userScenario: string;
16
- expectedOutcome: string;
17
- validationMethod: string;
18
- }
19
-
20
- interface CoverageResult {
21
- complete: boolean;
22
- totalScenarios: number;
23
- coveredScenarios: number;
24
- missingScenarios: ValidationScenario[];
25
- coverage: string; // "X/Y scenarios (Z%)"
26
- }
27
-
28
- /**
29
- * Extract issue number from branch name
30
- */
31
- function extractIssueNumberFromBranch(): number | null {
32
- const branchName = process.env.GIT_BRANCH || '';
33
- const match = branchName.match(/feature\/(\d+)/);
34
- return match ? parseInt(match[1]) : null;
35
- }
36
-
37
- /**
38
- * Find spec or design file for an issue
39
- */
40
- function findSpecOrDesignFile(issueNumber: number): string | null {
41
- const specDir = path.join(process.cwd(), 'docs', 'feature specs');
42
- const rfcDir = path.join(process.cwd(), 'docs', 'rfcs');
43
-
44
- // Try feature specs first
45
- if (fs.existsSync(specDir)) {
46
- const files = fs.readdirSync(specDir);
47
- const specFile = files.find(f => f.startsWith(`${issueNumber}-`) && f.endsWith('.md'));
48
- if (specFile) {
49
- return path.join(specDir, specFile);
50
- }
51
- }
52
-
53
- // Then try RFCs
54
- if (fs.existsSync(rfcDir)) {
55
- const files = fs.readdirSync(rfcDir);
56
- const rfcFile = files.find(f => f.startsWith(`${issueNumber}-`) && f.endsWith('.md'));
57
- if (rfcFile) {
58
- return path.join(rfcDir, rfcFile);
59
- }
60
- }
61
-
62
- return null;
63
- }
64
-
65
- /**
66
- * Extract validation plan table from markdown file
67
- */
68
- function extractValidationPlan(filePath: string): ValidationScenario[] {
69
- const content = fs.readFileSync(filePath, 'utf-8');
70
- const scenarios: ValidationScenario[] = [];
71
-
72
- // Find "## Validation Plan" section (not "Validation Plan Coverage")
73
- const validationPlanMatch = content.match(/##\s+Validation Plan\s*\n[\s\S]*?(?=\n##|$)/i);
74
- if (!validationPlanMatch) {
75
- return scenarios;
76
- }
77
-
78
- const validationSection = validationPlanMatch[0];
79
-
80
- // Look for markdown table - find table header row with "User Scenario"
81
- // Pattern: | User Scenario | Expected outcome | Validation method |
82
- const tablePattern = /\|[\s\*]*User Scenario[\s\*]*\|[\s\*]*Expected outcome[\s\*]*\|[\s\*]*Validation method[\s\*]*\|[\s]*\n\|[\s:\-|]+\|[\s]*\n((?:\|.*?\|[\s]*\n)+)/i;
83
- const tableMatch = validationSection.match(tablePattern);
84
-
85
- if (!tableMatch) {
86
- return scenarios;
87
- }
88
-
89
- const tableRows = tableMatch[1].trim().split('\n').filter(row => row.trim().startsWith('|'));
90
-
91
- for (const row of tableRows) {
92
- // Skip separator rows (all dashes/pipes)
93
- if (row.match(/^\|[\s:\-|]+\|$/)) {
94
- continue;
95
- }
96
-
97
- // Parse table row: | Scenario | Outcome | Method |
98
- // Handle both formats: | **bold** | text | and | text | text |
99
- const cells = row.split('|').map(c => c.trim()).filter(c => c && c !== '');
100
-
101
- if (cells.length >= 2) {
102
- // Remove markdown bold (**text**) from cells
103
- const cleanCell = (cell: string): string => {
104
- return cell
105
- .replace(/^\*\*/, '')
106
- .replace(/\*\*$/, '')
107
- .replace(/^\*\*/g, '')
108
- .replace(/\*\*/g, '')
109
- .trim();
110
- };
111
-
112
- const userScenario = cleanCell(cells[0] || '');
113
- const expectedOutcome = cleanCell(cells[1] || '');
114
- const validationMethod = cleanCell(cells[2] || 'Manual validation');
115
-
116
- // Skip header rows
117
- const scenarioLower = userScenario.toLowerCase();
118
- const outcomeLower = expectedOutcome.toLowerCase();
119
- if (scenarioLower.includes('user scenario') ||
120
- (scenarioLower.includes('scenario') && outcomeLower.includes('expected')) ||
121
- scenarioLower === 'user scenario' ||
122
- outcomeLower === 'expected outcome') {
123
- continue;
124
- }
125
-
126
- // Skip empty rows or separator rows
127
- if (!userScenario || userScenario === '-' || userScenario.length === 0) {
128
- continue;
129
- }
130
-
131
- scenarios.push({
132
- userScenario,
133
- expectedOutcome,
134
- validationMethod
135
- });
136
- }
137
- }
138
-
139
- return scenarios;
140
- }
141
-
142
- /**
143
- * Check if evidence exists for a scenario
144
- * Evidence can be in PR comments, test.log, or other sources
145
- */
146
- function checkEvidenceForScenario(scenario: ValidationScenario, issueNumber: number): boolean {
147
- // Extract key terms from scenario for searching
148
- const scenarioKeywords = scenario.userScenario
149
- .toLowerCase()
150
- .replace(/\*\*/g, '')
151
- .split(/\s+/)
152
- .filter(w => w.length > 3 && !['agent', 'the', 'with', 'without'].includes(w));
153
-
154
- const outcomeKeywords = scenario.expectedOutcome
155
- .toLowerCase()
156
- .replace(/\*\*/g, '')
157
- .split(/\s+/)
158
- .filter(w => w.length > 3);
159
-
160
- // Method 1: Check test.log for scenario keywords
161
- const testLogPath = path.join(process.cwd(), 'test.log');
162
- if (fs.existsSync(testLogPath)) {
163
- const testLogContent = fs.readFileSync(testLogPath, 'utf-8').toLowerCase();
164
- // Check if multiple keywords from scenario appear
165
- const matchingKeywords = scenarioKeywords.filter(kw => testLogContent.includes(kw));
166
- if (matchingKeywords.length >= 2) {
167
- return true;
168
- }
169
- }
170
-
171
- // Method 2: Check for evidence file (created by agent with scenario validation)
172
- const evidenceFiles = ['test-evidence.md', 'validation-evidence.md', 'implementation-evidence.md'];
173
- for (const evidenceFile of evidenceFiles) {
174
- const evidencePath = path.join(process.cwd(), evidenceFile);
175
- if (fs.existsSync(evidencePath)) {
176
- const evidenceContent = fs.readFileSync(evidencePath, 'utf-8').toLowerCase();
177
- const matchingKeywords = scenarioKeywords.filter(kw => evidenceContent.includes(kw));
178
- if (matchingKeywords.length >= 2) {
179
- return true;
180
- }
181
- }
182
- }
183
-
184
- // Method 3: Check server.log for scenario-related activity
185
- const serverLogPath = path.join(process.cwd(), 'server.log');
186
- if (fs.existsSync(serverLogPath)) {
187
- const serverLogContent = fs.readFileSync(serverLogPath, 'utf-8').toLowerCase();
188
- // Look for validation-related keywords
189
- if (scenarioKeywords.some(kw => serverLogContent.includes(kw)) ||
190
- outcomeKeywords.some(kw => serverLogContent.includes('gate') && serverLogContent.includes(kw))) {
191
- return true;
192
- }
193
- }
194
-
195
- // Note: PR comment checking is done in the shell script (Gate 9)
196
- // This TypeScript tool focuses on file-based evidence
197
-
198
- return false; // Conservative: require evidence
199
- }
200
-
201
- /**
202
- * Main function
203
- */
204
- function main(): void {
205
- const issueNumberArg = process.argv[2];
206
- const issueNumber = issueNumberArg ? parseInt(issueNumberArg) : extractIssueNumberFromBranch();
207
-
208
- if (!issueNumber) {
209
- console.error('❌ ERROR: Could not determine issue number');
210
- console.error(' Usage: npx tsx validate-coverage.ts <issue-number>');
211
- console.error(' Or set GIT_BRANCH environment variable with feature/N-* format');
212
- process.exit(1);
213
- }
214
-
215
- const specFile = findSpecOrDesignFile(issueNumber);
216
-
217
- if (!specFile) {
218
- // No spec/design found - Gate 9 warns but doesn't block
219
- console.log('ℹ️ No spec/design found for issue #' + issueNumber);
220
- console.log(' Skipping validation plan coverage check');
221
- console.log(' Gate 9: Validation Plan Coverage - SKIPPED (no spec/design)');
222
- process.exit(0);
223
- }
224
-
225
- console.log(`Found spec/design: ${specFile}`);
226
-
227
- const scenarios = extractValidationPlan(specFile);
228
-
229
- if (scenarios.length === 0) {
230
- console.log('ℹ️ No validation plan table found in spec/design');
231
- console.log(' Gate 9: Validation Plan Coverage - SKIPPED (no validation plan)');
232
- process.exit(0);
233
- }
234
-
235
- console.log(`Found ${scenarios.length} validation scenario(s)`);
236
-
237
- // Check evidence for each scenario
238
- const covered: ValidationScenario[] = [];
239
- const missing: ValidationScenario[] = [];
240
-
241
- for (const scenario of scenarios) {
242
- if (checkEvidenceForScenario(scenario, issueNumber)) {
243
- covered.push(scenario);
244
- } else {
245
- missing.push(scenario);
246
- }
247
- }
248
-
249
- const coverage: CoverageResult = {
250
- complete: missing.length === 0,
251
- totalScenarios: scenarios.length,
252
- coveredScenarios: covered.length,
253
- missingScenarios: missing,
254
- coverage: `${covered.length}/${scenarios.length} scenarios (${Math.round(covered.length / scenarios.length * 100)}%)`
255
- };
256
-
257
- // Output results
258
- console.log('\n📊 Validation Plan Coverage:');
259
- console.log(` ${coverage.coverage}`);
260
-
261
- if (coverage.complete) {
262
- console.log('✅ PASSED: All validation scenarios have evidence');
263
- console.log(' Gate 9: Validation Plan Coverage - PASSED');
264
- process.exit(0);
265
- } else {
266
- console.log('❌ FAILED: Missing evidence for validation scenarios:');
267
- for (const scenario of missing) {
268
- console.log(` - "${scenario.userScenario}"`);
269
- console.log(` Expected: ${scenario.expectedOutcome}`);
270
- console.log(` Method: ${scenario.validationMethod}`);
271
- }
272
- console.log('\n Required: Provide evidence for all validation scenarios before marking PR ready');
273
- console.log(' Evidence can be: PR comment, test output, manual verification log');
274
- console.log(' Gate 9: Validation Plan Coverage - FAILED');
275
- process.exit(1);
276
- }
277
- }
278
-
279
- // Run main function
280
- main();
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Validation Plan Coverage Checker
4
+ *
5
+ * Extracts validation plan from spec/design documents and verifies evidence coverage.
6
+ * Used by Gate 9 in code-quality-check.sh
7
+ *
8
+ * Usage: npx tsx <this-script> <issue-number>
9
+ */
10
+
11
+ import * as fs from 'fs';
12
+ import * as path from 'path';
13
+
14
+ interface ValidationScenario {
15
+ userScenario: string;
16
+ expectedOutcome: string;
17
+ validationMethod: string;
18
+ }
19
+
20
+ interface CoverageResult {
21
+ complete: boolean;
22
+ totalScenarios: number;
23
+ coveredScenarios: number;
24
+ missingScenarios: ValidationScenario[];
25
+ coverage: string; // "X/Y scenarios (Z%)"
26
+ }
27
+
28
+ /**
29
+ * Extract issue number from branch name
30
+ */
31
+ function extractIssueNumberFromBranch(): number | null {
32
+ const branchName = process.env.GIT_BRANCH || '';
33
+ const match = branchName.match(/feature\/(\d+)/);
34
+ return match ? parseInt(match[1]) : null;
35
+ }
36
+
37
+ /**
38
+ * Find spec or design file for an issue
39
+ */
40
+ function findSpecOrDesignFile(issueNumber: number): string | null {
41
+ const specDir = path.join(process.cwd(), 'docs', 'feature specs');
42
+ const rfcDir = path.join(process.cwd(), 'docs', 'rfcs');
43
+
44
+ // Try feature specs first
45
+ if (fs.existsSync(specDir)) {
46
+ const files = fs.readdirSync(specDir);
47
+ const specFile = files.find(f => f.startsWith(`${issueNumber}-`) && f.endsWith('.md'));
48
+ if (specFile) {
49
+ return path.join(specDir, specFile);
50
+ }
51
+ }
52
+
53
+ // Then try RFCs
54
+ if (fs.existsSync(rfcDir)) {
55
+ const files = fs.readdirSync(rfcDir);
56
+ const rfcFile = files.find(f => f.startsWith(`${issueNumber}-`) && f.endsWith('.md'));
57
+ if (rfcFile) {
58
+ return path.join(rfcDir, rfcFile);
59
+ }
60
+ }
61
+
62
+ return null;
63
+ }
64
+
65
+ /**
66
+ * Extract validation plan table from markdown file
67
+ */
68
+ function extractValidationPlan(filePath: string): ValidationScenario[] {
69
+ const content = fs.readFileSync(filePath, 'utf-8');
70
+ const scenarios: ValidationScenario[] = [];
71
+
72
+ // Find "## Validation Plan" section (not "Validation Plan Coverage")
73
+ const validationPlanMatch = content.match(/##\s+Validation Plan\s*\n[\s\S]*?(?=\n##|$)/i);
74
+ if (!validationPlanMatch) {
75
+ return scenarios;
76
+ }
77
+
78
+ const validationSection = validationPlanMatch[0];
79
+
80
+ // Look for markdown table - find table header row with "User Scenario"
81
+ // Pattern: | User Scenario | Expected outcome | Validation method |
82
+ const tablePattern = /\|[\s\*]*User Scenario[\s\*]*\|[\s\*]*Expected outcome[\s\*]*\|[\s\*]*Validation method[\s\*]*\|[\s]*\n\|[\s:\-|]+\|[\s]*\n((?:\|.*?\|[\s]*\n)+)/i;
83
+ const tableMatch = validationSection.match(tablePattern);
84
+
85
+ if (!tableMatch) {
86
+ return scenarios;
87
+ }
88
+
89
+ const tableRows = tableMatch[1].trim().split('\n').filter(row => row.trim().startsWith('|'));
90
+
91
+ for (const row of tableRows) {
92
+ // Skip separator rows (all dashes/pipes)
93
+ if (row.match(/^\|[\s:\-|]+\|$/)) {
94
+ continue;
95
+ }
96
+
97
+ // Parse table row: | Scenario | Outcome | Method |
98
+ // Handle both formats: | **bold** | text | and | text | text |
99
+ const cells = row.split('|').map(c => c.trim()).filter(c => c && c !== '');
100
+
101
+ if (cells.length >= 2) {
102
+ // Remove markdown bold (**text**) from cells
103
+ const cleanCell = (cell: string): string => {
104
+ return cell
105
+ .replace(/^\*\*/, '')
106
+ .replace(/\*\*$/, '')
107
+ .replace(/^\*\*/g, '')
108
+ .replace(/\*\*/g, '')
109
+ .trim();
110
+ };
111
+
112
+ const userScenario = cleanCell(cells[0] || '');
113
+ const expectedOutcome = cleanCell(cells[1] || '');
114
+ const validationMethod = cleanCell(cells[2] || 'Manual validation');
115
+
116
+ // Skip header rows
117
+ const scenarioLower = userScenario.toLowerCase();
118
+ const outcomeLower = expectedOutcome.toLowerCase();
119
+ if (scenarioLower.includes('user scenario') ||
120
+ (scenarioLower.includes('scenario') && outcomeLower.includes('expected')) ||
121
+ scenarioLower === 'user scenario' ||
122
+ outcomeLower === 'expected outcome') {
123
+ continue;
124
+ }
125
+
126
+ // Skip empty rows or separator rows
127
+ if (!userScenario || userScenario === '-' || userScenario.length === 0) {
128
+ continue;
129
+ }
130
+
131
+ scenarios.push({
132
+ userScenario,
133
+ expectedOutcome,
134
+ validationMethod
135
+ });
136
+ }
137
+ }
138
+
139
+ return scenarios;
140
+ }
141
+
142
+ /**
143
+ * Check if evidence exists for a scenario
144
+ * Evidence can be in PR comments, test.log, or other sources
145
+ */
146
+ function checkEvidenceForScenario(scenario: ValidationScenario, issueNumber: number): boolean {
147
+ // Extract key terms from scenario for searching
148
+ const scenarioKeywords = scenario.userScenario
149
+ .toLowerCase()
150
+ .replace(/\*\*/g, '')
151
+ .split(/\s+/)
152
+ .filter(w => w.length > 3 && !['agent', 'the', 'with', 'without'].includes(w));
153
+
154
+ const outcomeKeywords = scenario.expectedOutcome
155
+ .toLowerCase()
156
+ .replace(/\*\*/g, '')
157
+ .split(/\s+/)
158
+ .filter(w => w.length > 3);
159
+
160
+ // Method 1: Check test.log for scenario keywords
161
+ const testLogPath = path.join(process.cwd(), 'test.log');
162
+ if (fs.existsSync(testLogPath)) {
163
+ const testLogContent = fs.readFileSync(testLogPath, 'utf-8').toLowerCase();
164
+ // Check if multiple keywords from scenario appear
165
+ const matchingKeywords = scenarioKeywords.filter(kw => testLogContent.includes(kw));
166
+ if (matchingKeywords.length >= 2) {
167
+ return true;
168
+ }
169
+ }
170
+
171
+ // Method 2: Check for evidence file (created by agent with scenario validation)
172
+ const evidenceFiles = ['test-evidence.md', 'validation-evidence.md', 'implementation-evidence.md'];
173
+ for (const evidenceFile of evidenceFiles) {
174
+ const evidencePath = path.join(process.cwd(), evidenceFile);
175
+ if (fs.existsSync(evidencePath)) {
176
+ const evidenceContent = fs.readFileSync(evidencePath, 'utf-8').toLowerCase();
177
+ const matchingKeywords = scenarioKeywords.filter(kw => evidenceContent.includes(kw));
178
+ if (matchingKeywords.length >= 2) {
179
+ return true;
180
+ }
181
+ }
182
+ }
183
+
184
+ // Method 3: Check server.log for scenario-related activity
185
+ const serverLogPath = path.join(process.cwd(), 'server.log');
186
+ if (fs.existsSync(serverLogPath)) {
187
+ const serverLogContent = fs.readFileSync(serverLogPath, 'utf-8').toLowerCase();
188
+ // Look for validation-related keywords
189
+ if (scenarioKeywords.some(kw => serverLogContent.includes(kw)) ||
190
+ outcomeKeywords.some(kw => serverLogContent.includes('gate') && serverLogContent.includes(kw))) {
191
+ return true;
192
+ }
193
+ }
194
+
195
+ // Note: PR comment checking is done in the shell script (Gate 9)
196
+ // This TypeScript tool focuses on file-based evidence
197
+
198
+ return false; // Conservative: require evidence
199
+ }
200
+
201
+ /**
202
+ * Main function
203
+ */
204
+ function main(): void {
205
+ const issueNumberArg = process.argv[2];
206
+ const issueNumber = issueNumberArg ? parseInt(issueNumberArg) : extractIssueNumberFromBranch();
207
+
208
+ if (!issueNumber) {
209
+ console.error('❌ ERROR: Could not determine issue number');
210
+ console.error(' Usage: npx tsx validate-coverage.ts <issue-number>');
211
+ console.error(' Or set GIT_BRANCH environment variable with feature/N-* format');
212
+ process.exit(1);
213
+ }
214
+
215
+ const specFile = findSpecOrDesignFile(issueNumber);
216
+
217
+ if (!specFile) {
218
+ // No spec/design found - Gate 9 warns but doesn't block
219
+ console.log('ℹ️ No spec/design found for issue #' + issueNumber);
220
+ console.log(' Skipping validation plan coverage check');
221
+ console.log(' Gate 9: Validation Plan Coverage - SKIPPED (no spec/design)');
222
+ process.exit(0);
223
+ }
224
+
225
+ console.log(`Found spec/design: ${specFile}`);
226
+
227
+ const scenarios = extractValidationPlan(specFile);
228
+
229
+ if (scenarios.length === 0) {
230
+ console.log('ℹ️ No validation plan table found in spec/design');
231
+ console.log(' Gate 9: Validation Plan Coverage - SKIPPED (no validation plan)');
232
+ process.exit(0);
233
+ }
234
+
235
+ console.log(`Found ${scenarios.length} validation scenario(s)`);
236
+
237
+ // Check evidence for each scenario
238
+ const covered: ValidationScenario[] = [];
239
+ const missing: ValidationScenario[] = [];
240
+
241
+ for (const scenario of scenarios) {
242
+ if (checkEvidenceForScenario(scenario, issueNumber)) {
243
+ covered.push(scenario);
244
+ } else {
245
+ missing.push(scenario);
246
+ }
247
+ }
248
+
249
+ const coverage: CoverageResult = {
250
+ complete: missing.length === 0,
251
+ totalScenarios: scenarios.length,
252
+ coveredScenarios: covered.length,
253
+ missingScenarios: missing,
254
+ coverage: `${covered.length}/${scenarios.length} scenarios (${Math.round(covered.length / scenarios.length * 100)}%)`
255
+ };
256
+
257
+ // Output results
258
+ console.log('\n📊 Validation Plan Coverage:');
259
+ console.log(` ${coverage.coverage}`);
260
+
261
+ if (coverage.complete) {
262
+ console.log('✅ PASSED: All validation scenarios have evidence');
263
+ console.log(' Gate 9: Validation Plan Coverage - PASSED');
264
+ process.exit(0);
265
+ } else {
266
+ console.log('❌ FAILED: Missing evidence for validation scenarios:');
267
+ for (const scenario of missing) {
268
+ console.log(` - "${scenario.userScenario}"`);
269
+ console.log(` Expected: ${scenario.expectedOutcome}`);
270
+ console.log(` Method: ${scenario.validationMethod}`);
271
+ }
272
+ console.log('\n Required: Provide evidence for all validation scenarios before marking PR ready');
273
+ console.log(' Evidence can be: PR comment, test output, manual verification log');
274
+ console.log(' Gate 9: Validation Plan Coverage - FAILED');
275
+ process.exit(1);
276
+ }
277
+ }
278
+
279
+ // Run main function
280
+ main();