swarm-orchestrator 7.0.0-alpha.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 (174) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +199 -0
  3. package/agents/reviewer.agent.md +50 -0
  4. package/agents/worker.agent.md +41 -0
  5. package/config/default-agents.yaml +56 -0
  6. package/config/pm-agent.yaml +36 -0
  7. package/config/quality-gates.yaml +88 -0
  8. package/config/repair-agent.yaml +34 -0
  9. package/config/semgrep-rules/complexity-mismatch.yaml +6 -0
  10. package/config/semgrep-rules/exception-swallowing.yaml +13 -0
  11. package/config/semgrep-rules/hardcoded-answer.yaml +6 -0
  12. package/config/semgrep-rules/mock-mutation.yaml +6 -0
  13. package/config/semgrep-rules/test-modification.yaml +6 -0
  14. package/config/user-agents.yaml +23 -0
  15. package/dist/src/adapters/adapter-factory.js +29 -0
  16. package/dist/src/adapters/agent-adapter.js +32 -0
  17. package/dist/src/adapters/claude-code-adapter.js +126 -0
  18. package/dist/src/adapters/claude-code-teams.js +119 -0
  19. package/dist/src/adapters/codex-adapter.js +118 -0
  20. package/dist/src/adapters/copilot-adapter.js +224 -0
  21. package/dist/src/adapters/fatal-error-classifier.js +88 -0
  22. package/dist/src/adapters/index.js +44 -0
  23. package/dist/src/adapters/persistent-session.js +204 -0
  24. package/dist/src/adapters/process-supervisor.js +199 -0
  25. package/dist/src/agents-exporter.js +398 -0
  26. package/dist/src/analytics-log.js +161 -0
  27. package/dist/src/baseline-scanner.js +65 -0
  28. package/dist/src/bootstrap-evidence.js +88 -0
  29. package/dist/src/bootstrap-orchestrator.js +205 -0
  30. package/dist/src/bootstrap-types.js +5 -0
  31. package/dist/src/branch-merger.js +427 -0
  32. package/dist/src/cli/attest-handlers.js +44 -0
  33. package/dist/src/cli/cost-prompt.js +51 -0
  34. package/dist/src/cli/demo-handlers.js +274 -0
  35. package/dist/src/cli/flags.js +152 -0
  36. package/dist/src/cli/index.js +57 -0
  37. package/dist/src/cli/live-status.js +186 -0
  38. package/dist/src/cli/misc-handlers.js +242 -0
  39. package/dist/src/cli/plan-handlers.js +350 -0
  40. package/dist/src/cli/share-handlers.js +163 -0
  41. package/dist/src/cli/status-handlers.js +457 -0
  42. package/dist/src/cli/swarm-handlers.js +638 -0
  43. package/dist/src/cli/usage.js +72 -0
  44. package/dist/src/cli.js +217 -0
  45. package/dist/src/commit-pattern-detector.js +143 -0
  46. package/dist/src/commit-quality-analyzer.js +38 -0
  47. package/dist/src/config-loader.js +339 -0
  48. package/dist/src/context-broker.js +358 -0
  49. package/dist/src/copilot-cli-wrapper.js +258 -0
  50. package/dist/src/cost-estimator.js +241 -0
  51. package/dist/src/defaults.js +18 -0
  52. package/dist/src/demo-mode.js +180 -0
  53. package/dist/src/deployment-handler.js +78 -0
  54. package/dist/src/deployment-manager.js +228 -0
  55. package/dist/src/execution-queue.js +196 -0
  56. package/dist/src/external-tool-manager.js +184 -0
  57. package/dist/src/gate-prompt-builder.js +117 -0
  58. package/dist/src/gate-remediation.js +173 -0
  59. package/dist/src/github-issues-ingester.js +152 -0
  60. package/dist/src/github-mcp-integrator.js +164 -0
  61. package/dist/src/hook-generator.js +229 -0
  62. package/dist/src/index.js +48 -0
  63. package/dist/src/knowledge-base.js +300 -0
  64. package/dist/src/logger.js +111 -0
  65. package/dist/src/meta-analyzer.js +208 -0
  66. package/dist/src/metrics-collector.js +209 -0
  67. package/dist/src/metrics-types.js +5 -0
  68. package/dist/src/multi-repo-coordinator.js +179 -0
  69. package/dist/src/orchestrator/async-meta-analysis.js +84 -0
  70. package/dist/src/orchestrator/fatal-run-error.js +43 -0
  71. package/dist/src/orchestrator/final-gates-remediation.js +258 -0
  72. package/dist/src/orchestrator/git-state-utils.js +119 -0
  73. package/dist/src/orchestrator/pause-controller.js +55 -0
  74. package/dist/src/orchestrator/replan-runner.js +258 -0
  75. package/dist/src/orchestrator/step-executor.js +391 -0
  76. package/dist/src/orchestrator/wave-scheduler-loop.js +204 -0
  77. package/dist/src/owasp-mapper.js +153 -0
  78. package/dist/src/owasp-report-renderer.js +39 -0
  79. package/dist/src/plan-files.js +142 -0
  80. package/dist/src/plan-generator.js +1207 -0
  81. package/dist/src/pm-agent.js +296 -0
  82. package/dist/src/post-run-reporter.js +203 -0
  83. package/dist/src/pr-automation.js +103 -0
  84. package/dist/src/pr-manager.js +318 -0
  85. package/dist/src/presenter/index.js +209 -0
  86. package/dist/src/prompt-builder.js +173 -0
  87. package/dist/src/quality-gates/config-loader.js +196 -0
  88. package/dist/src/quality-gates/default-config.js +243 -0
  89. package/dist/src/quality-gates/file-utils.js +152 -0
  90. package/dist/src/quality-gates/gate-runner.js +171 -0
  91. package/dist/src/quality-gates/gates/accessibility.js +355 -0
  92. package/dist/src/quality-gates/gates/duplicate-blocks.js +144 -0
  93. package/dist/src/quality-gates/gates/hardcoded-config.js +52 -0
  94. package/dist/src/quality-gates/gates/readme-claims.js +62 -0
  95. package/dist/src/quality-gates/gates/runtime-checks.js +265 -0
  96. package/dist/src/quality-gates/gates/scaffold-defaults.js +108 -0
  97. package/dist/src/quality-gates/gates/test-coverage.js +240 -0
  98. package/dist/src/quality-gates/gates/test-file-protection.js +85 -0
  99. package/dist/src/quality-gates/gates/test-isolation.js +74 -0
  100. package/dist/src/quality-gates/index.js +17 -0
  101. package/dist/src/quality-gates/registry.js +205 -0
  102. package/dist/src/quality-gates/types.js +2 -0
  103. package/dist/src/quick-fix-mode.js +302 -0
  104. package/dist/src/recipe-loader.js +122 -0
  105. package/dist/src/repair-agent.js +396 -0
  106. package/dist/src/repo-analyzer.js +342 -0
  107. package/dist/src/report-generator.js +146 -0
  108. package/dist/src/report-renderer.js +90 -0
  109. package/dist/src/requirement-filter.js +52 -0
  110. package/dist/src/sarif-formatter.js +140 -0
  111. package/dist/src/scheduling/dependency-analyzer.js +178 -0
  112. package/dist/src/scheduling/work-stealing-queue.js +123 -0
  113. package/dist/src/secret-redactor.js +90 -0
  114. package/dist/src/session-executor.js +587 -0
  115. package/dist/src/session-manager.js +221 -0
  116. package/dist/src/share/transcript-verification.js +128 -0
  117. package/dist/src/share-parser.js +477 -0
  118. package/dist/src/spinner.js +146 -0
  119. package/dist/src/step-runner.js +365 -0
  120. package/dist/src/swarm-orchestrator.js +686 -0
  121. package/dist/src/task-classifier.js +134 -0
  122. package/dist/src/test-command-discovery.js +123 -0
  123. package/dist/src/text-similarity.js +24 -0
  124. package/dist/src/tier-maps.js +158 -0
  125. package/dist/src/types.js +5 -0
  126. package/dist/src/url-shortener.js +135 -0
  127. package/dist/src/verification/attestation.js +166 -0
  128. package/dist/src/verification/cheat-detector.js +240 -0
  129. package/dist/src/verification/command-runner.js +70 -0
  130. package/dist/src/verification/composite-score.js +157 -0
  131. package/dist/src/verification/cosign-attestation.js +137 -0
  132. package/dist/src/verification/diff-analysis.js +89 -0
  133. package/dist/src/verification/differential-gate.js +173 -0
  134. package/dist/src/verification/index.js +41 -0
  135. package/dist/src/verification/mutation-gate.js +242 -0
  136. package/dist/src/verification/property-gate.js +201 -0
  137. package/dist/src/verification/test-synthesizer.js +243 -0
  138. package/dist/src/verifier/outcome-checks.js +396 -0
  139. package/dist/src/verifier/transcript-checks.js +207 -0
  140. package/dist/src/verifier/verification-reporters.js +148 -0
  141. package/dist/src/verifier-engine.js +491 -0
  142. package/dist/src/wave-resizer.js +131 -0
  143. package/dist/src/wave-scheduler.js +44 -0
  144. package/dist/src/worktree-manager.js +368 -0
  145. package/dist/src/worktree-reserved-paths.js +97 -0
  146. package/package.json +80 -0
  147. package/plugin/agents/.gitkeep +0 -0
  148. package/plugin/agents/backend_master.agent.md +72 -0
  149. package/plugin/agents/devops_pro.agent.md +45 -0
  150. package/plugin/agents/frontend_expert.agent.md +59 -0
  151. package/plugin/agents/integrator_finalizer.agent.md +44 -0
  152. package/plugin/agents/meta_reviewer.agent.md +44 -0
  153. package/plugin/agents/security_auditor.agent.md +43 -0
  154. package/plugin/agents/tester_elite.agent.md +43 -0
  155. package/plugin/hooks/hooks.json +19 -0
  156. package/plugin/hooks/post-tool-use.json +9 -0
  157. package/plugin/hooks/pre-tool-use.json +9 -0
  158. package/plugin/plugin.json +47 -0
  159. package/plugin/skills/swarm-gates/SKILL.md +53 -0
  160. package/plugin/skills/swarm-orchestrate/SKILL.md +34 -0
  161. package/plugin/skills/swarm-verify/SKILL.md +44 -0
  162. package/templates/README.md +35 -0
  163. package/templates/cli-tool.json +31 -0
  164. package/templates/fullstack.json +52 -0
  165. package/templates/library.json +38 -0
  166. package/templates/react-app.json +45 -0
  167. package/templates/recipes/add-api-docs.json +35 -0
  168. package/templates/recipes/add-auth.json +47 -0
  169. package/templates/recipes/add-ci.json +39 -0
  170. package/templates/recipes/add-tests.json +39 -0
  171. package/templates/recipes/migrate-to-ts.json +42 -0
  172. package/templates/recipes/refactor-modularize.json +44 -0
  173. package/templates/recipes/security-audit.json +35 -0
  174. package/templates/rest-api.json +38 -0
@@ -0,0 +1,148 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.generateVerificationReport = generateVerificationReport;
37
+ exports.commitVerificationReport = commitVerificationReport;
38
+ const child_process_1 = require("child_process");
39
+ const fs = __importStar(require("fs"));
40
+ const path = __importStar(require("path"));
41
+ async function runGitCommand(workingDir, args) {
42
+ return new Promise((resolve, reject) => {
43
+ const proc = (0, child_process_1.spawn)('git', args, { cwd: workingDir });
44
+ let stdout = '';
45
+ let stderr = '';
46
+ proc.stdout?.on('data', (data) => {
47
+ stdout += data.toString();
48
+ });
49
+ proc.stderr?.on('data', (data) => {
50
+ stderr += data.toString();
51
+ });
52
+ proc.on('close', (code) => {
53
+ if (code === 0)
54
+ resolve(stdout);
55
+ else
56
+ reject(new Error(stderr || `Git command failed with code ${code}`));
57
+ });
58
+ proc.on('error', (err) => reject(err));
59
+ });
60
+ }
61
+ /**
62
+ * Render a VerificationResult as a Markdown report and persist it.
63
+ */
64
+ async function generateVerificationReport(result, outputPath) {
65
+ const lines = [];
66
+ lines.push('# Verification Report');
67
+ lines.push('');
68
+ lines.push(`**Step**: ${result.stepNumber}`);
69
+ lines.push(`**Agent**: ${result.agentName}`);
70
+ lines.push(`**Status**: ${result.passed ? '✅ PASSED' : '❌ FAILED'}`);
71
+ lines.push(`**Timestamp**: ${result.timestamp}`);
72
+ lines.push(`**Transcript**: ${result.transcriptPath}`);
73
+ lines.push('');
74
+ lines.push('## Verification Checks');
75
+ lines.push('');
76
+ result.checks.forEach((check) => {
77
+ const icon = check.passed ? '✅' : '❌';
78
+ const req = check.required ? '(required)' : '(optional)';
79
+ lines.push(`### ${icon} ${check.description} ${req}`);
80
+ lines.push('');
81
+ lines.push(`**Type**: ${check.type}`);
82
+ lines.push(`**Passed**: ${check.passed}`);
83
+ if (check.evidence)
84
+ lines.push(`**Evidence**: ${check.evidence}`);
85
+ if (check.reason)
86
+ lines.push(`**Reason**: ${check.reason}`);
87
+ lines.push('');
88
+ });
89
+ if (result.unverifiedClaims.length > 0) {
90
+ lines.push('## ⚠️ Unverified Claims (Drift Detection)');
91
+ lines.push('');
92
+ lines.push('The following claims were made without supporting evidence:');
93
+ lines.push('');
94
+ result.unverifiedClaims.forEach((claim) => {
95
+ lines.push(`- ${claim}`);
96
+ });
97
+ lines.push('');
98
+ }
99
+ lines.push('## Summary');
100
+ lines.push('');
101
+ const passedCount = result.checks.filter((c) => c.passed).length;
102
+ const totalCount = result.checks.length;
103
+ lines.push(`**Checks Passed**: ${passedCount}/${totalCount}`);
104
+ lines.push(`**Unverified Claims**: ${result.unverifiedClaims.length}`);
105
+ lines.push('');
106
+ if (!result.passed) {
107
+ lines.push('**Action Required**: This step failed verification. Review the issues above and retry.');
108
+ }
109
+ else {
110
+ lines.push('**Result**: All required checks passed. Step verified successfully.');
111
+ }
112
+ const reportContent = lines.join('\n');
113
+ const dir = path.dirname(outputPath);
114
+ if (!fs.existsSync(dir)) {
115
+ fs.mkdirSync(dir, { recursive: true });
116
+ }
117
+ fs.writeFileSync(outputPath, reportContent, 'utf8');
118
+ }
119
+ /**
120
+ * Commit the verification report with a natural-language message.
121
+ * Skips silently when the report lives outside the target worktree
122
+ * (e.g. bootstrap mode writing into the orchestrator's own runs/).
123
+ */
124
+ async function commitVerificationReport(reportPath, workingDir, stepNumber, agentName, passed, logger) {
125
+ const status = passed ? 'verified' : 'failed verification';
126
+ const messages = [
127
+ `verify step ${stepNumber} (${agentName}) - ${status}`,
128
+ `add verification report for step ${stepNumber}`,
129
+ `verification: step ${stepNumber} ${status}`,
130
+ `step ${stepNumber} verification ${passed ? 'passed' : 'failed'}`,
131
+ ];
132
+ const message = messages[Math.floor(Math.random() * messages.length)];
133
+ const resolvedReport = path.resolve(reportPath);
134
+ const resolvedWorkDir = path.resolve(workingDir);
135
+ if (!resolvedReport.startsWith(resolvedWorkDir + path.sep)) {
136
+ return;
137
+ }
138
+ try {
139
+ await runGitCommand(workingDir, ['add', '-f', reportPath]);
140
+ await runGitCommand(workingDir, ['commit', '-m', message]);
141
+ }
142
+ catch (err) {
143
+ const error = err;
144
+ if (!error.message.includes('nothing to commit')) {
145
+ logger.warn(` ⚠️ Could not commit verification report: ${error.message.split('\n')[0]}`);
146
+ }
147
+ }
148
+ }
@@ -0,0 +1,491 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.VerifierEngine = void 0;
40
+ exports.last20Lines = last20Lines;
41
+ exports.buildFailureContext = buildFailureContext;
42
+ const child_process_1 = require("child_process");
43
+ const fs = __importStar(require("fs"));
44
+ const path = __importStar(require("path"));
45
+ const share_parser_1 = __importDefault(require("./share-parser"));
46
+ const defaults_1 = require("./defaults");
47
+ const outcome_checks_1 = require("./verifier/outcome-checks");
48
+ const transcript_checks_1 = require("./verifier/transcript-checks");
49
+ const verification_reporters_1 = require("./verifier/verification-reporters");
50
+ const logger_1 = require("./logger");
51
+ const logger = (0, logger_1.getLogger)('verifier-engine');
52
+ /**
53
+ * Truncate output to the last 20 lines for error display.
54
+ * Returns the full string if it has 20 lines or fewer.
55
+ */
56
+ function last20Lines(output) {
57
+ const lines = output.split('\n');
58
+ if (lines.length <= 20)
59
+ return output;
60
+ return '...\n' + lines.slice(-20).join('\n');
61
+ }
62
+ // Ordering for failure context: more actionable types first
63
+ const FAILURE_TYPE_PRIORITY = {
64
+ file_existence: 0,
65
+ test_exec: 1,
66
+ build_exec: 2,
67
+ git_diff: 3,
68
+ };
69
+ /**
70
+ * Build a structured failure context string from failed checks.
71
+ * Ordered by actionability and truncated to ~500 tokens (2000 chars).
72
+ */
73
+ function buildFailureContext(checks) {
74
+ const failed = checks.filter(c => !c.passed);
75
+ if (failed.length === 0)
76
+ return '';
77
+ failed.sort((a, b) => {
78
+ const pa = FAILURE_TYPE_PRIORITY[a.type] ?? 10;
79
+ const pb = FAILURE_TYPE_PRIORITY[b.type] ?? 10;
80
+ return pa - pb;
81
+ });
82
+ const lines = failed.map(c => `- ${c.type}: ${c.reason || c.description}`);
83
+ const joined = lines.join('\n');
84
+ // ~500 tokens at 4 chars/token = 2000 chars
85
+ if (joined.length > defaults_1.DEFAULT_FAILURE_CONTEXT_CHARS) {
86
+ return joined.slice(0, defaults_1.DEFAULT_FAILURE_CONTEXT_CHARS) + '\n... (truncated)';
87
+ }
88
+ return joined;
89
+ }
90
+ /**
91
+ * Verifier Engine - proactive real-time verification and drift prevention
92
+ * Validates agent claims against actual evidence in transcripts
93
+ */
94
+ class VerifierEngine {
95
+ shareParser;
96
+ workingDir;
97
+ constructor(workingDir) {
98
+ this.workingDir = workingDir || process.cwd();
99
+ this.shareParser = new share_parser_1.default();
100
+ }
101
+ /**
102
+ * Verify a completed step by analyzing its transcript
103
+ */
104
+ async verifyStep(stepNumber, agentName, transcriptPath, requirements, preParsedIndex, evidenceLogPath, outcomeOpts) {
105
+ const checks = [];
106
+ const unverifiedClaims = [];
107
+ // Read and parse transcript
108
+ if (!fs.existsSync(transcriptPath)) {
109
+ return {
110
+ stepNumber,
111
+ agentName,
112
+ passed: false,
113
+ checks: [{
114
+ type: 'claim',
115
+ description: 'Transcript file exists',
116
+ required: true,
117
+ passed: false,
118
+ reason: `Transcript not found: ${transcriptPath}`
119
+ }],
120
+ unverifiedClaims: [],
121
+ timestamp: new Date().toISOString(),
122
+ transcriptPath
123
+ };
124
+ }
125
+ // Skip re-parse when the caller already parsed the transcript
126
+ const shareIndex = preParsedIndex ?? (() => {
127
+ const transcriptContent = fs.readFileSync(transcriptPath, 'utf8');
128
+ return this.shareParser.parse(transcriptContent);
129
+ })();
130
+ // Verify tests if required
131
+ if (requirements?.requireTests) {
132
+ const testCheck = this.verifyTests(shareIndex);
133
+ checks.push(testCheck);
134
+ }
135
+ // Verify build if required
136
+ if (requirements?.requireBuild) {
137
+ const buildCheck = this.verifyBuild(shareIndex);
138
+ checks.push(buildCheck);
139
+ }
140
+ // Verify commits if required
141
+ if (requirements?.requireCommits) {
142
+ const commitCheck = this.verifyCommits(shareIndex);
143
+ checks.push(commitCheck);
144
+ }
145
+ // Check for unverified claims (drift detection) - these are warnings only
146
+ const claimCheck = this.verifyAllClaims(shareIndex);
147
+ checks.push(...claimCheck.checks);
148
+ unverifiedClaims.push(...claimCheck.unverifiedClaims);
149
+ // Cross-reference hook evidence against transcript claims (defense in depth)
150
+ if (evidenceLogPath) {
151
+ const hookEvidence = this.crossReferenceEvidence(shareIndex, evidenceLogPath);
152
+ checks.push(...hookEvidence);
153
+ }
154
+ // Outcome-based checks: run when the caller provides worktree info
155
+ if (outcomeOpts) {
156
+ const outcomeChecks = this.runOutcomeChecks(outcomeOpts);
157
+ checks.push(...outcomeChecks);
158
+ // Demote transcript-parsed test/build checks when real execution checks exist
159
+ const hasOutcomeExecChecks = checks.some(c => c.type === 'build_exec' || c.type === 'test_exec');
160
+ if (hasOutcomeExecChecks) {
161
+ for (const c of checks) {
162
+ if (c.type === 'build' || c.type === 'test') {
163
+ c.required = false;
164
+ }
165
+ }
166
+ }
167
+ }
168
+ // Overall pass/fail: every required check must pass (vacuously true when none are required)
169
+ const passed = checks.every(c => !c.required || c.passed);
170
+ // Build failure context for repair agent consumption
171
+ const failureContext = passed ? undefined : buildFailureContext(checks);
172
+ const summary = this.buildSummary(checks, passed);
173
+ // Apply graceful degradation if enabled and verification failed
174
+ let gracefulFailure = false;
175
+ let degradationReason;
176
+ if (!passed && requirements?.gracefulDegradation) {
177
+ gracefulFailure = true;
178
+ const failedRequired = checks.filter(c => c.required && !c.passed);
179
+ degradationReason = `Verification failed but graceful degradation enabled. Failed checks: ${failedRequired.map(c => c.type).join(', ')}`;
180
+ }
181
+ const result = {
182
+ stepNumber,
183
+ agentName,
184
+ passed: passed || gracefulFailure,
185
+ checks,
186
+ unverifiedClaims,
187
+ timestamp: new Date().toISOString(),
188
+ transcriptPath,
189
+ gracefulFailure,
190
+ degradationReason,
191
+ failureContext,
192
+ summary,
193
+ baseSha: outcomeOpts?.baseSha,
194
+ };
195
+ return result;
196
+ }
197
+ runOutcomeChecks(opts) {
198
+ return (0, outcome_checks_1.runOutcomeChecks)(opts, this.workingDir);
199
+ }
200
+ buildSummary(checks, passed) {
201
+ return (0, transcript_checks_1.buildSummary)(checks, passed);
202
+ }
203
+ verifyTests(shareIndex) {
204
+ return (0, transcript_checks_1.verifyTests)(shareIndex);
205
+ }
206
+ verifyBuild(shareIndex) {
207
+ return (0, transcript_checks_1.verifyBuild)(shareIndex);
208
+ }
209
+ verifyCommits(shareIndex) {
210
+ return (0, transcript_checks_1.verifyCommits)(shareIndex);
211
+ }
212
+ verifyAllClaims(shareIndex) {
213
+ return (0, transcript_checks_1.verifyAllClaims)(shareIndex);
214
+ }
215
+ /**
216
+ * Verify expected outputs by checking files and content in the worktree.
217
+ * Each expectedOutput string is parsed for file paths and key terms,
218
+ * then verified against the actual file system.
219
+ */
220
+ verifyExpectedOutputs(stepNumber, agentName, expectedOutputs, worktreeDir) {
221
+ const checks = [];
222
+ // Regex to extract file paths from expected output descriptions
223
+ const filePathRegex = /\b((?:src|test|tests|server|config|public)\/[\w./-]+\.\w+|[\w-]+\.\w{2,4})\b/g;
224
+ // Key terms that can be grepped in source files
225
+ const termPatterns = [
226
+ { term: 'aria-label', regex: /aria-label/i },
227
+ { term: 'aria-live', regex: /aria-live/i },
228
+ { term: 'role="alert"', regex: /role\s*=\s*["']alert["']/i },
229
+ { term: 'focus-visible', regex: /:focus-visible/i },
230
+ { term: 'skip-link', regex: /skip-link|skip.to.content|#main-content/i },
231
+ { term: 'ErrorBoundary', regex: /ErrorBoundary/i },
232
+ { term: 'AbortController', regex: /AbortController/i },
233
+ { term: 'retry', regex: /retry|backoff/i },
234
+ { term: 'responsive breakpoint', regex: /@media\s*\(/i },
235
+ { term: 'heading hierarchy', regex: /<h[12]/i },
236
+ { term: 'skeleton shimmer', regex: /skeleton|shimmer/i },
237
+ { term: 'animation', regex: /animation\s*[:={]/i }
238
+ ];
239
+ for (const expected of expectedOutputs) {
240
+ const filePaths = [];
241
+ let match;
242
+ const pathRegex = new RegExp(filePathRegex.source, 'g');
243
+ while ((match = pathRegex.exec(expected)) !== null) {
244
+ // Skip common false positives like version numbers
245
+ if (/^\d+\.\d+/.test(match[1]))
246
+ continue;
247
+ filePaths.push(match[1]);
248
+ }
249
+ if (filePaths.length > 0) {
250
+ // File-existence check: verify at least one mentioned file exists
251
+ const existingFiles = filePaths.filter(fp => {
252
+ const fullPath = path.join(worktreeDir, fp);
253
+ return fs.existsSync(fullPath);
254
+ });
255
+ if (existingFiles.length > 0) {
256
+ checks.push({
257
+ type: 'output',
258
+ description: `Expected files present: ${existingFiles.join(', ')}`,
259
+ required: false,
260
+ passed: true,
261
+ evidence: `${existingFiles.length}/${filePaths.length} expected files found in worktree`
262
+ });
263
+ }
264
+ else {
265
+ checks.push({
266
+ type: 'output',
267
+ description: `Expected files: ${filePaths.join(', ')}`,
268
+ required: false,
269
+ passed: false,
270
+ reason: `None of the expected files found in ${worktreeDir}`,
271
+ evidence: `Looked for: ${filePaths.join(', ')}`
272
+ });
273
+ }
274
+ // Content check: grep existing files for key terms mentioned in expected output
275
+ const lowerExpected = expected.toLowerCase();
276
+ for (const { term, regex } of termPatterns) {
277
+ if (!lowerExpected.includes(term.toLowerCase().split(/[="]/)[0]))
278
+ continue;
279
+ const found = existingFiles.some(fp => {
280
+ try {
281
+ const content = fs.readFileSync(path.join(worktreeDir, fp), 'utf8');
282
+ return regex.test(content);
283
+ }
284
+ catch {
285
+ // File may be binary or permission-restricted; skip it
286
+ return false;
287
+ }
288
+ });
289
+ if (found) {
290
+ checks.push({
291
+ type: 'output',
292
+ description: `"${term}" found in output files`,
293
+ required: false,
294
+ passed: true,
295
+ evidence: `Matched ${term} pattern in generated code`
296
+ });
297
+ }
298
+ }
299
+ }
300
+ else {
301
+ // No file paths extracted; try to match key terms across all changed files
302
+ const lowerExpected = expected.toLowerCase();
303
+ const matchedTerms = [];
304
+ for (const { term, regex } of termPatterns) {
305
+ if (!lowerExpected.includes(term.toLowerCase().split(/[="]/)[0]))
306
+ continue;
307
+ // Search all non-binary files in worktree src/ and test/
308
+ const searchDirs = ['src', 'test', 'tests', 'server'].map(d => path.join(worktreeDir, d));
309
+ let found = false;
310
+ for (const dir of searchDirs) {
311
+ if (!fs.existsSync(dir))
312
+ continue;
313
+ try {
314
+ const files = fs.readdirSync(dir, { recursive: true, encoding: 'utf8' });
315
+ for (const file of files) {
316
+ const fullPath = path.join(dir, file);
317
+ try {
318
+ const stat = fs.statSync(fullPath);
319
+ if (!stat.isFile() || stat.size > 256 * 1024)
320
+ continue;
321
+ const content = fs.readFileSync(fullPath, 'utf8');
322
+ if (regex.test(content)) {
323
+ found = true;
324
+ break;
325
+ }
326
+ }
327
+ catch {
328
+ // Binary or permission-restricted file; skip
329
+ }
330
+ }
331
+ }
332
+ catch {
333
+ // Directory unreadable (permissions, broken symlink); skip
334
+ }
335
+ if (found)
336
+ break;
337
+ }
338
+ if (found)
339
+ matchedTerms.push(term);
340
+ }
341
+ if (matchedTerms.length > 0) {
342
+ checks.push({
343
+ type: 'output',
344
+ description: `Expected features verified: ${matchedTerms.join(', ')}`,
345
+ required: false,
346
+ passed: true,
347
+ evidence: `Found evidence for: ${matchedTerms.join(', ')}`
348
+ });
349
+ }
350
+ }
351
+ }
352
+ return checks;
353
+ }
354
+ /**
355
+ * Generate verification report and save to file
356
+ */
357
+ async generateVerificationReport(result, outputPath) {
358
+ return (0, verification_reporters_1.generateVerificationReport)(result, outputPath);
359
+ }
360
+ /**
361
+ * Rollback changes from a failed step
362
+ */
363
+ async rollback(stepNumber, branchName, filesChanged, baseBranch) {
364
+ const filesRestored = [];
365
+ try {
366
+ // If on a branch, switch back and delete the branch. Use the caller's
367
+ // explicit base branch when provided; otherwise resolve it from origin/HEAD
368
+ // so this works on `master` repos too, not just `main`. Never resolve to
369
+ // the branch we're about to delete — git refuses to delete the current
370
+ // branch, so the fallback must exclude it.
371
+ if (branchName) {
372
+ const target = baseBranch ?? this.resolveBaseBranch(branchName);
373
+ await this.runGitCommand(['checkout', target]);
374
+ await this.runGitCommand(['branch', '-D', branchName]);
375
+ }
376
+ // Reset any uncommitted changes
377
+ await this.runGitCommand(['reset', '--hard', 'HEAD']);
378
+ // If specific files were changed, restore them
379
+ if (filesChanged && filesChanged.length > 0) {
380
+ for (const file of filesChanged) {
381
+ try {
382
+ await this.runGitCommand(['checkout', 'HEAD', '--', file]);
383
+ filesRestored.push(file);
384
+ }
385
+ catch {
386
+ // File does not exist in HEAD (new file from the agent); nothing to restore
387
+ }
388
+ }
389
+ }
390
+ return {
391
+ success: true,
392
+ filesRestored
393
+ };
394
+ }
395
+ catch (error) {
396
+ const err = error;
397
+ return {
398
+ success: false,
399
+ filesRestored,
400
+ error: err.message
401
+ };
402
+ }
403
+ }
404
+ /**
405
+ * Resolve the repo's integration branch synchronously. Mirrors
406
+ * WorktreeManager.resolveDefaultBranch's contract: origin/HEAD first, then
407
+ * the currently checked-out branch, then the literal "main" as a last resort.
408
+ *
409
+ * @param excludeBranch When rollback() is deleting a branch, the caller
410
+ * passes that branch here so the current-branch fallback never
411
+ * resolves to it (git refuses to delete the checked-out branch).
412
+ */
413
+ resolveBaseBranch(excludeBranch) {
414
+ try {
415
+ const head = (0, child_process_1.execSync)('git symbolic-ref refs/remotes/origin/HEAD', {
416
+ cwd: this.workingDir,
417
+ encoding: 'utf8',
418
+ stdio: ['pipe', 'pipe', 'pipe'],
419
+ }).trim();
420
+ const prefix = 'refs/remotes/origin/';
421
+ if (head.startsWith(prefix))
422
+ return head.slice(prefix.length);
423
+ }
424
+ catch {
425
+ // origin/HEAD not set; fall through
426
+ }
427
+ try {
428
+ const current = (0, child_process_1.execSync)('git branch --show-current', {
429
+ cwd: this.workingDir,
430
+ encoding: 'utf8',
431
+ stdio: ['pipe', 'pipe', 'pipe'],
432
+ }).trim();
433
+ if (current && current !== excludeBranch)
434
+ return current;
435
+ }
436
+ catch {
437
+ // fall through
438
+ }
439
+ return 'main';
440
+ }
441
+ /**
442
+ * Run a git command
443
+ */
444
+ async runGitCommand(args) {
445
+ return new Promise((resolve, reject) => {
446
+ const proc = (0, child_process_1.spawn)('git', args, {
447
+ cwd: this.workingDir
448
+ });
449
+ let stdout = '';
450
+ let stderr = '';
451
+ if (proc.stdout) {
452
+ proc.stdout.on('data', (data) => {
453
+ stdout += data.toString();
454
+ });
455
+ }
456
+ if (proc.stderr) {
457
+ proc.stderr.on('data', (data) => {
458
+ stderr += data.toString();
459
+ });
460
+ }
461
+ proc.on('close', (code) => {
462
+ if (code === 0) {
463
+ resolve(stdout);
464
+ }
465
+ else {
466
+ reject(new Error(stderr || `Git command failed with code ${code}`));
467
+ }
468
+ });
469
+ proc.on('error', (err) => {
470
+ reject(err);
471
+ });
472
+ });
473
+ }
474
+ /**
475
+ * Commit verification report with natural message
476
+ * Skips if file is in a gitignored path (e.g., runs/)
477
+ */
478
+ async commitVerificationReport(reportPath, stepNumber, agentName, passed) {
479
+ return (0, verification_reporters_1.commitVerificationReport)(reportPath, this.workingDir, stepNumber, agentName, passed, logger);
480
+ }
481
+ /**
482
+ * Cross-reference hook-captured evidence with transcript-based claims.
483
+ * If hooks recorded errors or scope violations that the transcript does not
484
+ * mention, the step fails (defense in depth).
485
+ */
486
+ crossReferenceEvidence(shareIndex, evidenceLogPath) {
487
+ return (0, transcript_checks_1.crossReferenceEvidence)(shareIndex, evidenceLogPath);
488
+ }
489
+ }
490
+ exports.VerifierEngine = VerifierEngine;
491
+ exports.default = VerifierEngine;