@telora/factory 0.4.5

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 (301) hide show
  1. package/dist/audit.d.ts +69 -0
  2. package/dist/audit.d.ts.map +1 -0
  3. package/dist/audit.js +376 -0
  4. package/dist/audit.js.map +1 -0
  5. package/dist/builder-completion.d.ts +35 -0
  6. package/dist/builder-completion.d.ts.map +1 -0
  7. package/dist/builder-completion.js +375 -0
  8. package/dist/builder-completion.js.map +1 -0
  9. package/dist/builder-spawner.d.ts +40 -0
  10. package/dist/builder-spawner.d.ts.map +1 -0
  11. package/dist/builder-spawner.js +493 -0
  12. package/dist/builder-spawner.js.map +1 -0
  13. package/dist/completion-gate.d.ts +52 -0
  14. package/dist/completion-gate.d.ts.map +1 -0
  15. package/dist/completion-gate.js +336 -0
  16. package/dist/completion-gate.js.map +1 -0
  17. package/dist/completion-report.d.ts +36 -0
  18. package/dist/completion-report.d.ts.map +1 -0
  19. package/dist/completion-report.js +348 -0
  20. package/dist/completion-report.js.map +1 -0
  21. package/dist/completion.d.ts +58 -0
  22. package/dist/completion.d.ts.map +1 -0
  23. package/dist/completion.js +287 -0
  24. package/dist/completion.js.map +1 -0
  25. package/dist/config.d.ts +16 -0
  26. package/dist/config.d.ts.map +1 -0
  27. package/dist/config.js +57 -0
  28. package/dist/config.js.map +1 -0
  29. package/dist/context-manager.d.ts +152 -0
  30. package/dist/context-manager.d.ts.map +1 -0
  31. package/dist/context-manager.js +421 -0
  32. package/dist/context-manager.js.map +1 -0
  33. package/dist/crash-detection.d.ts +70 -0
  34. package/dist/crash-detection.d.ts.map +1 -0
  35. package/dist/crash-detection.js +123 -0
  36. package/dist/crash-detection.js.map +1 -0
  37. package/dist/crash-recovery.d.ts +83 -0
  38. package/dist/crash-recovery.d.ts.map +1 -0
  39. package/dist/crash-recovery.js +522 -0
  40. package/dist/crash-recovery.js.map +1 -0
  41. package/dist/crash-resolution.d.ts +34 -0
  42. package/dist/crash-resolution.d.ts.map +1 -0
  43. package/dist/crash-resolution.js +382 -0
  44. package/dist/crash-resolution.js.map +1 -0
  45. package/dist/escalation.d.ts +150 -0
  46. package/dist/escalation.d.ts.map +1 -0
  47. package/dist/escalation.js +352 -0
  48. package/dist/escalation.js.map +1 -0
  49. package/dist/execution-target.d.ts +31 -0
  50. package/dist/execution-target.d.ts.map +1 -0
  51. package/dist/execution-target.js +71 -0
  52. package/dist/execution-target.js.map +1 -0
  53. package/dist/execution-unit-init.d.ts +28 -0
  54. package/dist/execution-unit-init.d.ts.map +1 -0
  55. package/dist/execution-unit-init.js +115 -0
  56. package/dist/execution-unit-init.js.map +1 -0
  57. package/dist/execution.d.ts +17 -0
  58. package/dist/execution.d.ts.map +1 -0
  59. package/dist/execution.js +20 -0
  60. package/dist/execution.js.map +1 -0
  61. package/dist/factory-engine.d.ts +100 -0
  62. package/dist/factory-engine.d.ts.map +1 -0
  63. package/dist/factory-engine.js +243 -0
  64. package/dist/factory-engine.js.map +1 -0
  65. package/dist/gap-detection.d.ts +43 -0
  66. package/dist/gap-detection.d.ts.map +1 -0
  67. package/dist/gap-detection.js +149 -0
  68. package/dist/gap-detection.js.map +1 -0
  69. package/dist/gate-context.d.ts +23 -0
  70. package/dist/gate-context.d.ts.map +1 -0
  71. package/dist/gate-context.js +63 -0
  72. package/dist/gate-context.js.map +1 -0
  73. package/dist/gate-engine.d.ts +55 -0
  74. package/dist/gate-engine.d.ts.map +1 -0
  75. package/dist/gate-engine.js +191 -0
  76. package/dist/gate-engine.js.map +1 -0
  77. package/dist/gates/adversarial.d.ts +59 -0
  78. package/dist/gates/adversarial.d.ts.map +1 -0
  79. package/dist/gates/adversarial.js +426 -0
  80. package/dist/gates/adversarial.js.map +1 -0
  81. package/dist/gates/adversary-spawner.d.ts +35 -0
  82. package/dist/gates/adversary-spawner.d.ts.map +1 -0
  83. package/dist/gates/adversary-spawner.js +286 -0
  84. package/dist/gates/adversary-spawner.js.map +1 -0
  85. package/dist/gates/adversary-test-dir.d.ts +41 -0
  86. package/dist/gates/adversary-test-dir.d.ts.map +1 -0
  87. package/dist/gates/adversary-test-dir.js +150 -0
  88. package/dist/gates/adversary-test-dir.js.map +1 -0
  89. package/dist/gates/behavioral-parser.d.ts +32 -0
  90. package/dist/gates/behavioral-parser.d.ts.map +1 -0
  91. package/dist/gates/behavioral-parser.js +190 -0
  92. package/dist/gates/behavioral-parser.js.map +1 -0
  93. package/dist/gates/behavioral-runner.d.ts +36 -0
  94. package/dist/gates/behavioral-runner.d.ts.map +1 -0
  95. package/dist/gates/behavioral-runner.js +306 -0
  96. package/dist/gates/behavioral-runner.js.map +1 -0
  97. package/dist/gates/behavioral.d.ts +37 -0
  98. package/dist/gates/behavioral.d.ts.map +1 -0
  99. package/dist/gates/behavioral.js +485 -0
  100. package/dist/gates/behavioral.js.map +1 -0
  101. package/dist/gates/deterministic.d.ts +24 -0
  102. package/dist/gates/deterministic.d.ts.map +1 -0
  103. package/dist/gates/deterministic.js +186 -0
  104. package/dist/gates/deterministic.js.map +1 -0
  105. package/dist/git-factory.d.ts +59 -0
  106. package/dist/git-factory.d.ts.map +1 -0
  107. package/dist/git-factory.js +102 -0
  108. package/dist/git-factory.js.map +1 -0
  109. package/dist/guard-evaluation.d.ts +48 -0
  110. package/dist/guard-evaluation.d.ts.map +1 -0
  111. package/dist/guard-evaluation.js +416 -0
  112. package/dist/guard-evaluation.js.map +1 -0
  113. package/dist/index.d.ts +30 -0
  114. package/dist/index.d.ts.map +1 -0
  115. package/dist/index.js +39 -0
  116. package/dist/index.js.map +1 -0
  117. package/dist/instance-completion.d.ts +34 -0
  118. package/dist/instance-completion.d.ts.map +1 -0
  119. package/dist/instance-completion.js +366 -0
  120. package/dist/instance-completion.js.map +1 -0
  121. package/dist/instance-lifecycle.d.ts +15 -0
  122. package/dist/instance-lifecycle.d.ts.map +1 -0
  123. package/dist/instance-lifecycle.js +18 -0
  124. package/dist/instance-lifecycle.js.map +1 -0
  125. package/dist/instance-phase-dispatch.d.ts +75 -0
  126. package/dist/instance-phase-dispatch.d.ts.map +1 -0
  127. package/dist/instance-phase-dispatch.js +674 -0
  128. package/dist/instance-phase-dispatch.js.map +1 -0
  129. package/dist/instance-poll-loop.d.ts +43 -0
  130. package/dist/instance-poll-loop.d.ts.map +1 -0
  131. package/dist/instance-poll-loop.js +360 -0
  132. package/dist/instance-poll-loop.js.map +1 -0
  133. package/dist/instance-state-machine.d.ts +52 -0
  134. package/dist/instance-state-machine.d.ts.map +1 -0
  135. package/dist/instance-state-machine.js +235 -0
  136. package/dist/instance-state-machine.js.map +1 -0
  137. package/dist/log-manager.d.ts +28 -0
  138. package/dist/log-manager.d.ts.map +1 -0
  139. package/dist/log-manager.js +71 -0
  140. package/dist/log-manager.js.map +1 -0
  141. package/dist/pipeline-evaluator.d.ts +61 -0
  142. package/dist/pipeline-evaluator.d.ts.map +1 -0
  143. package/dist/pipeline-evaluator.js +107 -0
  144. package/dist/pipeline-evaluator.js.map +1 -0
  145. package/dist/pipeline-metrics.d.ts +52 -0
  146. package/dist/pipeline-metrics.d.ts.map +1 -0
  147. package/dist/pipeline-metrics.js +40 -0
  148. package/dist/pipeline-metrics.js.map +1 -0
  149. package/dist/pipeline-traversal.d.ts +43 -0
  150. package/dist/pipeline-traversal.d.ts.map +1 -0
  151. package/dist/pipeline-traversal.js +68 -0
  152. package/dist/pipeline-traversal.js.map +1 -0
  153. package/dist/plan-parser.d.ts +76 -0
  154. package/dist/plan-parser.d.ts.map +1 -0
  155. package/dist/plan-parser.js +223 -0
  156. package/dist/plan-parser.js.map +1 -0
  157. package/dist/planning-phase.d.ts +52 -0
  158. package/dist/planning-phase.d.ts.map +1 -0
  159. package/dist/planning-phase.js +444 -0
  160. package/dist/planning-phase.js.map +1 -0
  161. package/dist/planning-prompt.d.ts +64 -0
  162. package/dist/planning-prompt.d.ts.map +1 -0
  163. package/dist/planning-prompt.js +251 -0
  164. package/dist/planning-prompt.js.map +1 -0
  165. package/dist/planning.d.ts +16 -0
  166. package/dist/planning.d.ts.map +1 -0
  167. package/dist/planning.js +17 -0
  168. package/dist/planning.js.map +1 -0
  169. package/dist/process-runner.d.ts +41 -0
  170. package/dist/process-runner.d.ts.map +1 -0
  171. package/dist/process-runner.js +81 -0
  172. package/dist/process-runner.js.map +1 -0
  173. package/dist/product-config.d.ts +34 -0
  174. package/dist/product-config.d.ts.map +1 -0
  175. package/dist/product-config.js +43 -0
  176. package/dist/product-config.js.map +1 -0
  177. package/dist/queries/cycle-evaluations.d.ts +23 -0
  178. package/dist/queries/cycle-evaluations.d.ts.map +1 -0
  179. package/dist/queries/cycle-evaluations.js +37 -0
  180. package/dist/queries/cycle-evaluations.js.map +1 -0
  181. package/dist/queries/escalations.d.ts +30 -0
  182. package/dist/queries/escalations.d.ts.map +1 -0
  183. package/dist/queries/escalations.js +42 -0
  184. package/dist/queries/escalations.js.map +1 -0
  185. package/dist/queries/execution-units.d.ts +76 -0
  186. package/dist/queries/execution-units.d.ts.map +1 -0
  187. package/dist/queries/execution-units.js +109 -0
  188. package/dist/queries/execution-units.js.map +1 -0
  189. package/dist/queries/gate-results.d.ts +32 -0
  190. package/dist/queries/gate-results.d.ts.map +1 -0
  191. package/dist/queries/gate-results.js +44 -0
  192. package/dist/queries/gate-results.js.map +1 -0
  193. package/dist/queries/instances.d.ts +51 -0
  194. package/dist/queries/instances.d.ts.map +1 -0
  195. package/dist/queries/instances.js +77 -0
  196. package/dist/queries/instances.js.map +1 -0
  197. package/dist/queries/sessions.d.ts +50 -0
  198. package/dist/queries/sessions.d.ts.map +1 -0
  199. package/dist/queries/sessions.js +81 -0
  200. package/dist/queries/sessions.js.map +1 -0
  201. package/dist/queries/shared.d.ts +38 -0
  202. package/dist/queries/shared.d.ts.map +1 -0
  203. package/dist/queries/shared.js +119 -0
  204. package/dist/queries/shared.js.map +1 -0
  205. package/dist/queries/specs.d.ts +12 -0
  206. package/dist/queries/specs.d.ts.map +1 -0
  207. package/dist/queries/specs.js +21 -0
  208. package/dist/queries/specs.js.map +1 -0
  209. package/dist/queries/strategies.d.ts +14 -0
  210. package/dist/queries/strategies.d.ts.map +1 -0
  211. package/dist/queries/strategies.js +18 -0
  212. package/dist/queries/strategies.js.map +1 -0
  213. package/dist/queries/work-units.d.ts +42 -0
  214. package/dist/queries/work-units.d.ts.map +1 -0
  215. package/dist/queries/work-units.js +57 -0
  216. package/dist/queries/work-units.js.map +1 -0
  217. package/dist/queries/workflows.d.ts +29 -0
  218. package/dist/queries/workflows.d.ts.map +1 -0
  219. package/dist/queries/workflows.js +103 -0
  220. package/dist/queries/workflows.js.map +1 -0
  221. package/dist/remediation-units.d.ts +40 -0
  222. package/dist/remediation-units.d.ts.map +1 -0
  223. package/dist/remediation-units.js +263 -0
  224. package/dist/remediation-units.js.map +1 -0
  225. package/dist/replanning.d.ts +72 -0
  226. package/dist/replanning.d.ts.map +1 -0
  227. package/dist/replanning.js +403 -0
  228. package/dist/replanning.js.map +1 -0
  229. package/dist/resource-limits.d.ts +62 -0
  230. package/dist/resource-limits.d.ts.map +1 -0
  231. package/dist/resource-limits.js +322 -0
  232. package/dist/resource-limits.js.map +1 -0
  233. package/dist/scheduler.d.ts +98 -0
  234. package/dist/scheduler.d.ts.map +1 -0
  235. package/dist/scheduler.js +203 -0
  236. package/dist/scheduler.js.map +1 -0
  237. package/dist/session-adapter.d.ts +89 -0
  238. package/dist/session-adapter.d.ts.map +1 -0
  239. package/dist/session-adapter.js +108 -0
  240. package/dist/session-adapter.js.map +1 -0
  241. package/dist/sop-generator.d.ts +29 -0
  242. package/dist/sop-generator.d.ts.map +1 -0
  243. package/dist/sop-generator.js +235 -0
  244. package/dist/sop-generator.js.map +1 -0
  245. package/dist/spec-profiles.d.ts +41 -0
  246. package/dist/spec-profiles.d.ts.map +1 -0
  247. package/dist/spec-profiles.js +131 -0
  248. package/dist/spec-profiles.js.map +1 -0
  249. package/dist/strategy-design-graph.d.ts +23 -0
  250. package/dist/strategy-design-graph.d.ts.map +1 -0
  251. package/dist/strategy-design-graph.js +205 -0
  252. package/dist/strategy-design-graph.js.map +1 -0
  253. package/dist/strategy-design-prompt.d.ts +28 -0
  254. package/dist/strategy-design-prompt.d.ts.map +1 -0
  255. package/dist/strategy-design-prompt.js +108 -0
  256. package/dist/strategy-design-prompt.js.map +1 -0
  257. package/dist/strategy-design-schema.d.ts +767 -0
  258. package/dist/strategy-design-schema.d.ts.map +1 -0
  259. package/dist/strategy-design-schema.js +126 -0
  260. package/dist/strategy-design-schema.js.map +1 -0
  261. package/dist/strategy-design.d.ts +69 -0
  262. package/dist/strategy-design.d.ts.map +1 -0
  263. package/dist/strategy-design.js +411 -0
  264. package/dist/strategy-design.js.map +1 -0
  265. package/dist/strategy-gating.d.ts +31 -0
  266. package/dist/strategy-gating.d.ts.map +1 -0
  267. package/dist/strategy-gating.js +276 -0
  268. package/dist/strategy-gating.js.map +1 -0
  269. package/dist/team-prompt-builder.d.ts +47 -0
  270. package/dist/team-prompt-builder.d.ts.map +1 -0
  271. package/dist/team-prompt-builder.js +362 -0
  272. package/dist/team-prompt-builder.js.map +1 -0
  273. package/dist/trace-engine.d.ts +40 -0
  274. package/dist/trace-engine.d.ts.map +1 -0
  275. package/dist/trace-engine.js +344 -0
  276. package/dist/trace-engine.js.map +1 -0
  277. package/dist/types.d.ts +612 -0
  278. package/dist/types.d.ts.map +1 -0
  279. package/dist/types.js +9 -0
  280. package/dist/types.js.map +1 -0
  281. package/dist/unit-session-lifecycle.d.ts +78 -0
  282. package/dist/unit-session-lifecycle.d.ts.map +1 -0
  283. package/dist/unit-session-lifecycle.js +141 -0
  284. package/dist/unit-session-lifecycle.js.map +1 -0
  285. package/dist/unit-session.d.ts +30 -0
  286. package/dist/unit-session.d.ts.map +1 -0
  287. package/dist/unit-session.js +370 -0
  288. package/dist/unit-session.js.map +1 -0
  289. package/dist/watchdogs.d.ts +33 -0
  290. package/dist/watchdogs.d.ts.map +1 -0
  291. package/dist/watchdogs.js +170 -0
  292. package/dist/watchdogs.js.map +1 -0
  293. package/dist/work-unit-scheduler.d.ts +34 -0
  294. package/dist/work-unit-scheduler.d.ts.map +1 -0
  295. package/dist/work-unit-scheduler.js +91 -0
  296. package/dist/work-unit-scheduler.js.map +1 -0
  297. package/dist/workflow-transition.d.ts +90 -0
  298. package/dist/workflow-transition.d.ts.map +1 -0
  299. package/dist/workflow-transition.js +340 -0
  300. package/dist/workflow-transition.js.map +1 -0
  301. package/package.json +65 -0
@@ -0,0 +1,286 @@
1
+ /**
2
+ * Adversary process spawning and prompt construction.
3
+ *
4
+ * Spawns a separate Claude Code session (the "adversary") that tries to
5
+ * break the builder's implementation by writing Playwright tests. The
6
+ * adversary prompt contains ONLY:
7
+ * 1. The factory specification text
8
+ * 2. The current source file contents from the worktree
9
+ *
10
+ * Builder reasoning, logs, and stream.jsonl are NEVER included.
11
+ */
12
+ import { spawnAsync } from '../process-runner.js';
13
+ import { readdirSync, readFileSync, existsSync } from 'node:fs';
14
+ import { join, relative } from 'node:path';
15
+ import { createFactorySession, updateFactorySession } from '../queries/sessions.js';
16
+ import { getAdversaryTestDir, runAdversaryTests, truncateOutput } from './adversary-test-dir.js';
17
+ // ============================================================================
18
+ // Constants
19
+ // ============================================================================
20
+ /** Timeout for each adversary Claude Code session (5 minutes). */
21
+ const ADVERSARY_SESSION_TIMEOUT_MS = 300_000;
22
+ /** Default max adversary sessions when config is not provided. */
23
+ const DEFAULT_MAX_SESSIONS = 3;
24
+ /** Glob patterns for source files to include in the adversary prompt. */
25
+ const SOURCE_GLOBS = ['src'];
26
+ /** File extensions considered source code. */
27
+ const SOURCE_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx']);
28
+ /** Log prefix for this module. */
29
+ const LOG_PREFIX = '[adversarial]';
30
+ // ============================================================================
31
+ // Environment builder
32
+ // ============================================================================
33
+ /**
34
+ * Build a clean environment for the adversary Claude Code session.
35
+ *
36
+ * Strips inherited CLAUDE_CODE_* and CLAUDECODE* env vars to prevent
37
+ * the child from detecting a parent session and hanging.
38
+ */
39
+ function buildAdversaryEnv() {
40
+ const env = { ...process.env };
41
+ for (const key of Object.keys(env)) {
42
+ if (key === 'CLAUDECODE' || key.startsWith('CLAUDE_CODE_') || key.startsWith('CLAUDECODE')) {
43
+ delete env[key];
44
+ }
45
+ }
46
+ env.CI = 'true';
47
+ return env;
48
+ }
49
+ // ============================================================================
50
+ // Source file collection
51
+ // ============================================================================
52
+ /**
53
+ * Recursively collect source files from a directory.
54
+ *
55
+ * Returns an array of { relativePath, content } objects for files matching
56
+ * the configured source extensions. Skips node_modules, dist, .git, and
57
+ * other common non-source directories.
58
+ */
59
+ function collectSourceFiles(worktreePath) {
60
+ const results = [];
61
+ const skipDirs = new Set(['node_modules', 'dist', '.git', '.telora', 'coverage', '.next', 'build']);
62
+ function walk(dir) {
63
+ let entries;
64
+ try {
65
+ entries = readdirSync(dir, { withFileTypes: true });
66
+ }
67
+ catch {
68
+ // Directory may not be readable -- skip silently
69
+ return;
70
+ }
71
+ for (const entry of entries) {
72
+ const name = String(entry.name);
73
+ if (entry.isDirectory()) {
74
+ if (!skipDirs.has(name)) {
75
+ walk(join(dir, name));
76
+ }
77
+ }
78
+ else if (entry.isFile()) {
79
+ const ext = name.slice(name.lastIndexOf('.'));
80
+ if (SOURCE_EXTENSIONS.has(ext)) {
81
+ const fullPath = join(dir, name);
82
+ try {
83
+ const content = readFileSync(fullPath, 'utf-8');
84
+ const relPath = relative(worktreePath, fullPath);
85
+ results.push({ relativePath: relPath, content });
86
+ }
87
+ catch {
88
+ // Skip unreadable files
89
+ }
90
+ }
91
+ }
92
+ }
93
+ }
94
+ for (const srcDir of SOURCE_GLOBS) {
95
+ const fullSrcDir = join(worktreePath, srcDir);
96
+ if (existsSync(fullSrcDir)) {
97
+ walk(fullSrcDir);
98
+ }
99
+ }
100
+ return results;
101
+ }
102
+ // ============================================================================
103
+ // Prompt builder
104
+ // ============================================================================
105
+ /**
106
+ * Build the adversary prompt.
107
+ *
108
+ * Contains ONLY:
109
+ * 1. The factory specification text
110
+ * 2. Current source file contents from the worktree
111
+ *
112
+ * NEVER includes builder reasoning, logs, or stream.jsonl.
113
+ */
114
+ function buildAdversaryPrompt(specification, sourceFiles, sessionNumber, adversaryTestDir) {
115
+ const parts = [
116
+ 'You are an adversarial tester. Your goal is to find bugs, edge cases,',
117
+ 'and weaknesses in the implementation below by writing Playwright tests.',
118
+ '',
119
+ '## Specification',
120
+ '',
121
+ specification,
122
+ '',
123
+ '## Source Files',
124
+ '',
125
+ ];
126
+ for (const file of sourceFiles) {
127
+ parts.push(`### ${file.relativePath}`);
128
+ parts.push('```');
129
+ parts.push(file.content);
130
+ parts.push('```');
131
+ parts.push('');
132
+ }
133
+ parts.push('## Instructions', '', `This is adversary session ${sessionNumber}. Write Playwright tests that try to break the implementation.`, '', 'Rules:', `- Write test files to the EXTERNAL test directory: \`${adversaryTestDir}/\``, `- Name files descriptively, e.g. \`${adversaryTestDir}/edge-cases-validation.spec.ts\``, '- Each test file must be a valid Playwright test using `@playwright/test`', '- Focus on edge cases, boundary conditions, error handling, and unexpected inputs', '- Try to find real bugs -- tests should expose actual implementation issues', '- Do NOT write tests that pass trivially -- every test should probe a potential weakness', `- Create the \`${adversaryTestDir}/\` directory if it does not exist`, '- Do NOT modify any source files -- only create test files', '- Do NOT write tests inside the project worktree -- use the external directory above', '- Commit is not needed for external test files', '', 'Write aggressive, thorough tests that exercise the code in ways the original', 'developer might not have anticipated.');
134
+ return parts.join('\n');
135
+ }
136
+ // ============================================================================
137
+ // Adversary session runner
138
+ // ============================================================================
139
+ /**
140
+ * Spawn a single adversary Claude Code session.
141
+ *
142
+ * Uses --print mode (one-shot, not stream-json). The adversary writes
143
+ * Playwright test files to the external adversary test directory.
144
+ *
145
+ * @returns The raw stdout output from Claude Code.
146
+ */
147
+ async function spawnAdversarySession(worktreePath, prompt, claudeCodePath) {
148
+ const env = buildAdversaryEnv();
149
+ return spawnAsync(claudeCodePath, ['--print', '--verbose'], {
150
+ input: prompt,
151
+ encoding: 'utf-8',
152
+ timeout: ADVERSARY_SESSION_TIMEOUT_MS,
153
+ cwd: worktreePath,
154
+ env,
155
+ });
156
+ }
157
+ // ============================================================================
158
+ // Exported gate runner
159
+ // ============================================================================
160
+ /**
161
+ * Run adversarial gates for a work unit's implementation.
162
+ *
163
+ * Spawns up to `config.maxSessions` adversary Claude Code sessions, each
164
+ * writing Playwright tests to an external directory outside the worktree.
165
+ * After all sessions complete, runs ALL accumulated adversary tests and
166
+ * returns the results.
167
+ *
168
+ * Each adversary session is tracked with session_type='adversary'.
169
+ *
170
+ * @param worktreePath Absolute path to the factory worktree.
171
+ * @param specification The factory specification text.
172
+ * @param config Adversarial gate config from the blueprint (nullable).
173
+ * @param instanceId Factory instance ID (for session tracking).
174
+ * @param claudeCodePath Path to the Claude Code CLI executable.
175
+ * @returns Overall pass/fail and individual check results.
176
+ */
177
+ export async function runAdversarialGates(worktreePath, specification, config, instanceId, claudeCodePath, governor) {
178
+ const maxSessions = config?.maxSessions ?? DEFAULT_MAX_SESSIONS;
179
+ const results = [];
180
+ // External test directory -- sibling to the worktree, invisible to builder
181
+ const adversaryTestDir = getAdversaryTestDir(worktreePath);
182
+ console.log(`${LOG_PREFIX} Starting adversarial gates (max sessions: ${maxSessions}, ` +
183
+ `worktree: ${worktreePath}, testDir: ${adversaryTestDir})`);
184
+ // Ensure external adversary test directory exists
185
+ const { mkdirSync } = await import('node:fs');
186
+ if (!existsSync(adversaryTestDir)) {
187
+ mkdirSync(adversaryTestDir, { recursive: true });
188
+ }
189
+ // Collect source files once (shared across all sessions)
190
+ const sourceFiles = collectSourceFiles(worktreePath);
191
+ console.log(`${LOG_PREFIX} Collected ${sourceFiles.length} source file(s) for adversary context`);
192
+ // -- Spawn adversary sessions -----------------------------------------------
193
+ for (let sessionNum = 1; sessionNum <= maxSessions; sessionNum++) {
194
+ const sessionStartMs = Date.now();
195
+ console.log(`${LOG_PREFIX} Spawning adversary session ${sessionNum}/${maxSessions}`);
196
+ // Create a tracked session record
197
+ let sessionId;
198
+ try {
199
+ const session = await createFactorySession({
200
+ instanceId,
201
+ sessionType: 'adversary',
202
+ });
203
+ sessionId = session.id;
204
+ await updateFactorySession(sessionId, {
205
+ status: 'running',
206
+ startedAt: new Date().toISOString(),
207
+ });
208
+ }
209
+ catch (err) {
210
+ const errMsg = err instanceof Error ? err.message : String(err);
211
+ console.error(`${LOG_PREFIX} Failed to create adversary session record: ${errMsg}`);
212
+ results.push({
213
+ checkName: `adversary-session-${sessionNum}`,
214
+ passed: false,
215
+ output: truncateOutput(`Failed to create session record: ${errMsg}`),
216
+ durationMs: Date.now() - sessionStartMs,
217
+ isAdversaryGenerated: true,
218
+ });
219
+ continue;
220
+ }
221
+ // Build the prompt and spawn the adversary
222
+ const prompt = buildAdversaryPrompt(specification, sourceFiles, sessionNum, adversaryTestDir);
223
+ if (governor)
224
+ await governor.acquireSlot('factory');
225
+ try {
226
+ const rawOutput = await spawnAdversarySession(worktreePath, prompt, claudeCodePath);
227
+ const durationMs = Date.now() - sessionStartMs;
228
+ await updateFactorySession(sessionId, {
229
+ status: 'completed',
230
+ endedAt: new Date().toISOString(),
231
+ });
232
+ results.push({
233
+ checkName: `adversary-session-${sessionNum}`,
234
+ passed: true,
235
+ output: truncateOutput(rawOutput || '(no output)'),
236
+ durationMs,
237
+ isAdversaryGenerated: true,
238
+ });
239
+ console.log(`${LOG_PREFIX} Adversary session ${sessionNum} completed (${durationMs}ms)`);
240
+ }
241
+ catch (err) {
242
+ const durationMs = Date.now() - sessionStartMs;
243
+ const execErr = err;
244
+ const timedOut = execErr.killed === true || execErr.signal === 'SIGTERM';
245
+ const errDetail = timedOut
246
+ ? `Adversary session timed out after ${ADVERSARY_SESSION_TIMEOUT_MS / 1000}s`
247
+ : `Adversary session failed (exit ${execErr.status ?? 'unknown'}): ${execErr.message ?? 'unknown error'}`;
248
+ await updateFactorySession(sessionId, {
249
+ status: 'failed',
250
+ endedAt: new Date().toISOString(),
251
+ }).catch((updateErr) => {
252
+ console.error(`${LOG_PREFIX} Failed to update session ${sessionId} status: `, updateErr.message);
253
+ });
254
+ // Include any stdout the adversary produced before failing --
255
+ // it may have written test files despite the non-zero exit
256
+ const partialOutput = execErr.stdout ?? '';
257
+ results.push({
258
+ checkName: `adversary-session-${sessionNum}`,
259
+ passed: false,
260
+ output: truncateOutput(partialOutput
261
+ ? `${errDetail}\n---\nPartial output:\n${partialOutput}`
262
+ : errDetail),
263
+ durationMs,
264
+ isAdversaryGenerated: true,
265
+ });
266
+ console.warn(`${LOG_PREFIX} ${errDetail}`);
267
+ // Continue to next session -- a failed adversary session does not
268
+ // mean the implementation failed, it means the adversary failed.
269
+ // We still want to run accumulated tests.
270
+ }
271
+ finally {
272
+ if (governor)
273
+ governor.releaseSlot('factory');
274
+ }
275
+ }
276
+ // -- Run ALL accumulated adversary tests ------------------------------------
277
+ console.log(`${LOG_PREFIX} Running all accumulated adversary tests from ${adversaryTestDir}`);
278
+ const testResult = await runAdversaryTests(worktreePath, adversaryTestDir);
279
+ results.push(testResult);
280
+ // Overall pass/fail is determined by the test run result
281
+ const passed = testResult.passed;
282
+ console.log(`${LOG_PREFIX} Adversarial gates ${passed ? 'PASSED' : 'FAILED'} ` +
283
+ `(${results.length} check(s))`);
284
+ return { passed, results };
285
+ }
286
+ //# sourceMappingURL=adversary-spawner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adversary-spawner.js","sourceRoot":"","sources":["../../src/gates/adversary-spawner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Adversary test directory management and test runner.
3
+ *
4
+ * Manages the external adversary test directory that lives OUTSIDE the
5
+ * worktree so builder agents never see adversary-generated test files.
6
+ *
7
+ * External test directory: `{worktreePath}-adversary/` (sibling to the
8
+ * factory worktree). This directory is created on first adversary run
9
+ * and cleaned up when the instance completes.
10
+ */
11
+ import type { GateCheckResult } from '../types.js';
12
+ /**
13
+ * Derive the external adversary test directory path from a worktree path.
14
+ *
15
+ * Returns `{worktreePath}-adversary/` -- a sibling directory that lives
16
+ * outside the git worktree so builder agents never see adversary tests.
17
+ */
18
+ export declare function getAdversaryTestDir(worktreePath: string): string;
19
+ /**
20
+ * Remove the external adversary test directory for a completed instance.
21
+ *
22
+ * Safe to call even if the directory does not exist. Callers should invoke
23
+ * this alongside worktree removal when an instance reaches a terminal state.
24
+ */
25
+ export declare function cleanupAdversaryTestDir(worktreePath: string): void;
26
+ /**
27
+ * Run all accumulated adversary tests using Playwright.
28
+ *
29
+ * Runs `npx playwright test {externalTestDir}` from the worktree directory.
30
+ * The test files live outside the worktree in the external adversary test
31
+ * directory so builder agents never see them.
32
+ *
33
+ * @param worktreePath CWD for the Playwright process (project root).
34
+ * @param adversaryTestDir Absolute path to the external test directory.
35
+ */
36
+ export declare function runAdversaryTests(worktreePath: string, adversaryTestDir: string): Promise<GateCheckResult>;
37
+ /**
38
+ * Truncate output to the maximum allowed characters.
39
+ */
40
+ export declare function truncateOutput(output: string): string;
41
+ //# sourceMappingURL=adversary-test-dir.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adversary-test-dir.d.ts","sourceRoot":"","sources":["../../src/gates/adversary-test-dir.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAmBnD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAEhE;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAalE;AAMD;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,CACrC,YAAY,EAAE,MAAM,EACpB,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC,eAAe,CAAC,CA8F1B;AAMD;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAKrD"}
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Adversary test directory management and test runner.
3
+ *
4
+ * Manages the external adversary test directory that lives OUTSIDE the
5
+ * worktree so builder agents never see adversary-generated test files.
6
+ *
7
+ * External test directory: `{worktreePath}-adversary/` (sibling to the
8
+ * factory worktree). This directory is created on first adversary run
9
+ * and cleaned up when the instance completes.
10
+ */
11
+ import { spawnAsync } from '../process-runner.js';
12
+ import { readdirSync, existsSync, rmSync } from 'node:fs';
13
+ // ============================================================================
14
+ // Constants
15
+ // ============================================================================
16
+ /** Timeout for running Playwright tests (3 minutes). */
17
+ const PLAYWRIGHT_TIMEOUT_MS = 180_000;
18
+ /** Maximum characters per check output. */
19
+ const MAX_OUTPUT_CHARS = 10_000;
20
+ /** Log prefix for this module. */
21
+ const LOG_PREFIX = '[adversarial]';
22
+ // ============================================================================
23
+ // External test directory
24
+ // ============================================================================
25
+ /**
26
+ * Derive the external adversary test directory path from a worktree path.
27
+ *
28
+ * Returns `{worktreePath}-adversary/` -- a sibling directory that lives
29
+ * outside the git worktree so builder agents never see adversary tests.
30
+ */
31
+ export function getAdversaryTestDir(worktreePath) {
32
+ return `${worktreePath}-adversary`;
33
+ }
34
+ /**
35
+ * Remove the external adversary test directory for a completed instance.
36
+ *
37
+ * Safe to call even if the directory does not exist. Callers should invoke
38
+ * this alongside worktree removal when an instance reaches a terminal state.
39
+ */
40
+ export function cleanupAdversaryTestDir(worktreePath) {
41
+ const testDir = getAdversaryTestDir(worktreePath);
42
+ if (!existsSync(testDir))
43
+ return;
44
+ try {
45
+ rmSync(testDir, { recursive: true, force: true });
46
+ console.log(`${LOG_PREFIX} Cleaned up external adversary test directory: ${testDir}`);
47
+ }
48
+ catch (err) {
49
+ console.warn(`${LOG_PREFIX} Failed to clean up adversary test directory ${testDir}: ` +
50
+ `${err instanceof Error ? err.message : String(err)}`);
51
+ }
52
+ }
53
+ // ============================================================================
54
+ // Test runner
55
+ // ============================================================================
56
+ /**
57
+ * Run all accumulated adversary tests using Playwright.
58
+ *
59
+ * Runs `npx playwright test {externalTestDir}` from the worktree directory.
60
+ * The test files live outside the worktree in the external adversary test
61
+ * directory so builder agents never see them.
62
+ *
63
+ * @param worktreePath CWD for the Playwright process (project root).
64
+ * @param adversaryTestDir Absolute path to the external test directory.
65
+ */
66
+ export async function runAdversaryTests(worktreePath, adversaryTestDir) {
67
+ const startMs = Date.now();
68
+ // If no test directory exists, skip gracefully
69
+ if (!existsSync(adversaryTestDir)) {
70
+ return {
71
+ checkName: 'adversary-tests',
72
+ passed: true,
73
+ output: `No adversary tests found (${adversaryTestDir} does not exist)`,
74
+ durationMs: Date.now() - startMs,
75
+ isAdversaryGenerated: true,
76
+ };
77
+ }
78
+ // Check if there are any test files
79
+ let hasTestFiles = false;
80
+ try {
81
+ const entries = readdirSync(adversaryTestDir);
82
+ hasTestFiles = entries.some((e) => e.endsWith('.spec.ts') || e.endsWith('.spec.js') || e.endsWith('.test.ts') || e.endsWith('.test.js'));
83
+ }
84
+ catch {
85
+ // Directory read failed -- treat as no tests
86
+ }
87
+ if (!hasTestFiles) {
88
+ return {
89
+ checkName: 'adversary-tests',
90
+ passed: true,
91
+ output: `No adversary test files found in ${adversaryTestDir}`,
92
+ durationMs: Date.now() - startMs,
93
+ isAdversaryGenerated: true,
94
+ };
95
+ }
96
+ try {
97
+ const output = await spawnAsync('npx', ['playwright', 'test', adversaryTestDir], {
98
+ input: '',
99
+ encoding: 'utf-8',
100
+ timeout: PLAYWRIGHT_TIMEOUT_MS,
101
+ cwd: worktreePath,
102
+ env: { ...process.env, CI: 'true' },
103
+ });
104
+ const durationMs = Date.now() - startMs;
105
+ return {
106
+ checkName: 'adversary-tests',
107
+ passed: true,
108
+ output: truncateOutput(output),
109
+ durationMs,
110
+ isAdversaryGenerated: true,
111
+ };
112
+ }
113
+ catch (err) {
114
+ const durationMs = Date.now() - startMs;
115
+ const execErr = err;
116
+ if (execErr.killed || execErr.signal === 'SIGTERM') {
117
+ return {
118
+ checkName: 'adversary-tests',
119
+ passed: false,
120
+ output: truncateOutput(`Playwright timed out after ${PLAYWRIGHT_TIMEOUT_MS / 1000}s`),
121
+ durationMs,
122
+ isAdversaryGenerated: true,
123
+ };
124
+ }
125
+ // Playwright returns non-zero exit when tests fail
126
+ const stdout = execErr.stdout ?? '';
127
+ const stderr = execErr.stderr ?? '';
128
+ const combined = [stdout, stderr].filter(Boolean).join('\n---\n');
129
+ return {
130
+ checkName: 'adversary-tests',
131
+ passed: false,
132
+ output: truncateOutput(combined || execErr.message || `Playwright exited with code ${execErr.status ?? 'unknown'}`),
133
+ durationMs,
134
+ isAdversaryGenerated: true,
135
+ };
136
+ }
137
+ }
138
+ // ============================================================================
139
+ // Output truncation
140
+ // ============================================================================
141
+ /**
142
+ * Truncate output to the maximum allowed characters.
143
+ */
144
+ export function truncateOutput(output) {
145
+ if (output.length <= MAX_OUTPUT_CHARS) {
146
+ return output;
147
+ }
148
+ return output.slice(0, MAX_OUTPUT_CHARS - 30) + '\n...[truncated to 10000 chars]';
149
+ }
150
+ //# sourceMappingURL=adversary-test-dir.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adversary-test-dir.js","sourceRoot":"","sources":["../../src/gates/adversary-test-dir.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Playwright JSON report parsing and result extraction.
3
+ *
4
+ * Parses Playwright JSON reporter output into structured GateCheckResult
5
+ * items. Extracts individual test results from the nested suite/spec/test
6
+ * hierarchy, determines pass/fail status, and builds error output.
7
+ */
8
+ import type { GateCheckResult } from '../types.js';
9
+ /**
10
+ * Parse Playwright JSON reporter output into GateCheckResult items.
11
+ *
12
+ * Extracts individual test results from the nested suite/spec/test
13
+ * hierarchy. Each test becomes a separate GateCheckResult entry.
14
+ *
15
+ * If the JSON cannot be parsed, returns a single failed result with
16
+ * the raw output.
17
+ */
18
+ export declare function parsePlaywrightResults(jsonOutput: string, stderrOutput: string, exitCode: number, durationMs: number): GateCheckResult[];
19
+ /**
20
+ * Truncate output to the maximum allowed length.
21
+ * Appends a truncation notice if the output was shortened.
22
+ */
23
+ export declare function truncateOutput(output: string): string;
24
+ /**
25
+ * Parse a shell command string into the command and its arguments.
26
+ *
27
+ * Handles simple cases like "npm run dev" or "node server.js".
28
+ * For complex commands with pipes or redirects, the shell: true
29
+ * option on spawn handles those.
30
+ */
31
+ export declare function parseCommand(command: string): [string, ...string[]];
32
+ //# sourceMappingURL=behavioral-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"behavioral-parser.d.ts","sourceRoot":"","sources":["../../src/gates/behavioral-parser.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAoEnD;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,eAAe,EAAE,CA6DnB;AA2GD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAMrD;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,CAMnE"}