@velum-labs/routekit-eval-setup 1.3.2 → 1.5.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 (342) hide show
  1. package/dist/adapters/authoring-responses-request.d.ts +5 -0
  2. package/dist/adapters/authoring-responses-request.js +38 -0
  3. package/dist/adapters/evaluation-evidence-freshness.d.ts +7 -0
  4. package/dist/adapters/evaluation-evidence-freshness.js +76 -0
  5. package/dist/adapters/git-task-history.d.ts +67 -0
  6. package/dist/adapters/git-task-history.js +171 -0
  7. package/dist/adapters/integrated-repository-history.d.ts +21 -0
  8. package/dist/adapters/integrated-repository-history.js +175 -0
  9. package/dist/adapters/repository-command-diagnostic.d.ts +8 -0
  10. package/dist/adapters/repository-command-diagnostic.js +46 -0
  11. package/dist/adapters/repository-command-evidence.d.ts +13 -0
  12. package/dist/adapters/repository-command-evidence.js +102 -0
  13. package/dist/adapters/repository-command-runner.d.ts +238 -0
  14. package/dist/adapters/repository-command-runner.js +1483 -0
  15. package/dist/adapters/repository-import-context.d.ts +47 -0
  16. package/dist/adapters/repository-import-context.js +469 -0
  17. package/dist/adapters/repository-node-test-reporter.d.ts +3 -0
  18. package/dist/adapters/repository-node-test-reporter.js +27 -0
  19. package/dist/adapters/repository-review-evidence.d.ts +39 -0
  20. package/dist/adapters/repository-review-evidence.js +632 -0
  21. package/dist/adapters/repository-seed-selection.d.ts +7 -0
  22. package/dist/adapters/repository-seed-selection.js +79 -0
  23. package/dist/adapters/repository-solution-edits.d.ts +49 -0
  24. package/dist/adapters/repository-solution-edits.js +136 -0
  25. package/dist/adapters/repository-vitest-phase-adapter.d.ts +8 -0
  26. package/dist/adapters/repository-vitest-phase-adapter.js +310 -0
  27. package/dist/adapters/repository-vitest-reporter.d.ts +24 -0
  28. package/dist/adapters/repository-vitest-reporter.js +314 -0
  29. package/dist/adapters/strict-authoring-schema.d.ts +5 -0
  30. package/dist/adapters/strict-authoring-schema.js +158 -0
  31. package/dist/adapters/test-discovery.d.ts +30 -0
  32. package/dist/adapters/test-discovery.js +124 -0
  33. package/dist/adapters/typescript-repository-index.d.ts +51 -0
  34. package/dist/adapters/typescript-repository-index.js +226 -0
  35. package/dist/agentic-capabilities-protocol.d.ts +1373 -0
  36. package/dist/agentic-capabilities-protocol.js +786 -0
  37. package/dist/case-checkpoint-store.d.ts +29 -0
  38. package/dist/case-checkpoint-store.js +133 -0
  39. package/dist/case-pipeline-protocol-v2.d.ts +184 -0
  40. package/dist/case-pipeline-protocol-v2.js +193 -0
  41. package/dist/case-pipeline-protocol.d.ts +2626 -0
  42. package/dist/case-pipeline-protocol.js +371 -0
  43. package/dist/effect-api.d.ts +74 -10
  44. package/dist/effect-api.js +56 -6
  45. package/dist/errors.d.ts +31 -0
  46. package/dist/errors.js +10 -0
  47. package/dist/eval-capability-execution-envelope.d.ts +64 -0
  48. package/dist/eval-capability-execution-envelope.js +98 -0
  49. package/dist/eval-capability-policy.d.ts +90 -0
  50. package/dist/eval-capability-policy.js +107 -0
  51. package/dist/eval-event-log.d.ts +140 -0
  52. package/dist/eval-event-log.js +220 -0
  53. package/dist/evaluation-authoring-policy.d.ts +18 -0
  54. package/dist/evaluation-authoring-policy.js +19 -0
  55. package/dist/evaluation-authoring-validation.d.ts +22 -0
  56. package/dist/evaluation-authoring-validation.js +72 -0
  57. package/dist/evaluation-evidence.d.ts +20 -0
  58. package/dist/evaluation-evidence.js +319 -0
  59. package/dist/evaluation-grader-calibration-protocol.d.ts +108 -0
  60. package/dist/evaluation-grader-calibration-protocol.js +80 -0
  61. package/dist/evaluation-grader-calibration.d.ts +18 -0
  62. package/dist/evaluation-grader-calibration.js +334 -0
  63. package/dist/evaluation-grading-policy.d.ts +24 -0
  64. package/dist/evaluation-grading-policy.js +54 -0
  65. package/dist/evaluation-proposal-policy.d.ts +4 -0
  66. package/dist/evaluation-proposal-policy.js +91 -0
  67. package/dist/evaluation-source-retrieval.d.ts +68 -0
  68. package/dist/evaluation-source-retrieval.js +513 -0
  69. package/dist/evaluation-structure-policy.d.ts +29 -0
  70. package/dist/evaluation-structure-policy.js +138 -0
  71. package/dist/index.d.ts +124 -17
  72. package/dist/index.js +69 -11
  73. package/dist/inspection.js +2 -3
  74. package/dist/project-artifacts.d.ts +7 -2
  75. package/dist/project-artifacts.js +49 -136
  76. package/dist/project-authoring.d.ts +66 -5
  77. package/dist/project-authoring.js +783 -109
  78. package/dist/project-contracts.d.ts +419 -84
  79. package/dist/project-contracts.js +160 -52
  80. package/dist/project-store.js +2 -1
  81. package/dist/project-workflow.d.ts +5 -4
  82. package/dist/project-workflow.js +154 -35
  83. package/dist/repository-adversary-protocol.d.ts +64 -0
  84. package/dist/repository-adversary-protocol.js +105 -0
  85. package/dist/repository-behavior-protocol.d.ts +188 -0
  86. package/dist/repository-behavior-protocol.js +202 -0
  87. package/dist/repository-benchmark-protocol.d.ts +487 -0
  88. package/dist/repository-benchmark-protocol.js +96 -0
  89. package/dist/repository-execution-protocol.d.ts +150 -0
  90. package/dist/repository-execution-protocol.js +38 -0
  91. package/dist/repository-fixture-instructions.d.ts +3 -0
  92. package/dist/repository-fixture-instructions.js +91 -0
  93. package/dist/repository-fixture-protocol.d.ts +79 -0
  94. package/dist/repository-fixture-protocol.js +79 -0
  95. package/dist/repository-foundry-plan-protocol.d.ts +118 -0
  96. package/dist/repository-foundry-plan-protocol.js +296 -0
  97. package/dist/repository-foundry-progress-protocol.d.ts +52 -0
  98. package/dist/repository-foundry-progress-protocol.js +52 -0
  99. package/dist/repository-improvement-protocol.d.ts +100 -0
  100. package/dist/repository-improvement-protocol.js +106 -0
  101. package/dist/repository-language-model-protocol.d.ts +43 -0
  102. package/dist/repository-language-model-protocol.js +146 -0
  103. package/dist/repository-oracle-coverage-protocol.d.ts +18 -0
  104. package/dist/repository-oracle-coverage-protocol.js +39 -0
  105. package/dist/repository-oracle-execution-binding.d.ts +27 -0
  106. package/dist/repository-oracle-execution-binding.js +59 -0
  107. package/dist/repository-oracle-protocol.d.ts +230 -0
  108. package/dist/repository-oracle-protocol.js +156 -0
  109. package/dist/repository-oracle-scope-policy.d.ts +22 -0
  110. package/dist/repository-oracle-scope-policy.js +92 -0
  111. package/dist/repository-quality-policy.d.ts +15 -0
  112. package/dist/repository-quality-policy.js +357 -0
  113. package/dist/repository-routing-benchmark-protocol.d.ts +176 -0
  114. package/dist/repository-routing-benchmark-protocol.js +103 -0
  115. package/dist/repository-routing-model-protocol.d.ts +36 -0
  116. package/dist/repository-routing-model-protocol.js +89 -0
  117. package/dist/repository-routing-plan-protocol.d.ts +112 -0
  118. package/dist/repository-routing-plan-protocol.js +58 -0
  119. package/dist/repository-routing-quality-policy.d.ts +9 -0
  120. package/dist/repository-routing-quality-policy.js +191 -0
  121. package/dist/repository-seed-qualification-progress-protocol.d.ts +205 -0
  122. package/dist/repository-seed-qualification-progress-protocol.js +28 -0
  123. package/dist/repository-semantic-calibration-protocol.d.ts +768 -0
  124. package/dist/repository-semantic-calibration-protocol.js +276 -0
  125. package/dist/repository-semantic-calibration.d.ts +163 -0
  126. package/dist/repository-semantic-calibration.js +581 -0
  127. package/dist/repository-specification-contract-facts-protocol.d.ts +224 -0
  128. package/dist/repository-specification-contract-facts-protocol.js +276 -0
  129. package/dist/repository-specification-critique-protocol.d.ts +189 -0
  130. package/dist/repository-specification-critique-protocol.js +103 -0
  131. package/dist/repository-task-family-protocol.d.ts +24 -0
  132. package/dist/repository-task-family-protocol.js +37 -0
  133. package/dist/repository-task-seed-protocol.d.ts +384 -0
  134. package/dist/repository-task-seed-protocol.js +236 -0
  135. package/dist/repository-trajectory-protocol.d.ts +20 -0
  136. package/dist/repository-trajectory-protocol.js +42 -0
  137. package/dist/service.js +1 -1
  138. package/dist/services/adversary/service.d.ts +64 -0
  139. package/dist/services/adversary/service.js +330 -0
  140. package/dist/services/benchmark-compiler/service.d.ts +450 -0
  141. package/dist/services/benchmark-compiler/service.js +9 -0
  142. package/dist/services/budgeted-model/service.d.ts +118 -0
  143. package/dist/services/budgeted-model/service.js +460 -0
  144. package/dist/services/case-authoring/service.d.ts +163 -0
  145. package/dist/services/case-authoring/service.js +1456 -0
  146. package/dist/services/case-finalization/service.d.ts +283 -0
  147. package/dist/services/case-finalization/service.js +370 -0
  148. package/dist/services/case-generation/service.d.ts +619 -0
  149. package/dist/services/case-generation/service.js +2628 -0
  150. package/dist/services/case-pipeline/service.d.ts +31 -0
  151. package/dist/services/case-pipeline/service.js +485 -0
  152. package/dist/services/case-pipeline-v2/service.d.ts +70 -0
  153. package/dist/services/case-pipeline-v2/service.js +477 -0
  154. package/dist/services/command-observability/service.d.ts +13 -0
  155. package/dist/services/command-observability/service.js +3 -0
  156. package/dist/services/dimension-labeling/service.d.ts +77 -0
  157. package/dist/services/dimension-labeling/service.js +188 -0
  158. package/dist/services/eval-candidate/service.d.ts +208 -0
  159. package/dist/services/eval-candidate/service.js +64 -0
  160. package/dist/services/eval-capabilities/service.d.ts +183 -0
  161. package/dist/services/eval-capabilities/service.js +1433 -0
  162. package/dist/services/eval-environment/service.d.ts +173 -0
  163. package/dist/services/eval-environment/service.js +127 -0
  164. package/dist/services/evidence-reconstruction/service.d.ts +36 -0
  165. package/dist/services/evidence-reconstruction/service.js +145 -0
  166. package/dist/services/fixture-builder/service.d.ts +62 -0
  167. package/dist/services/fixture-builder/service.js +36 -0
  168. package/dist/services/fixture-validation/service.d.ts +75 -0
  169. package/dist/services/fixture-validation/service.js +295 -0
  170. package/dist/services/foundry/service.d.ts +831 -0
  171. package/dist/services/foundry/service.js +442 -0
  172. package/dist/services/foundry-progress/service.d.ts +62 -0
  173. package/dist/services/foundry-progress/service.js +149 -0
  174. package/dist/services/foundry-v2/service.d.ts +54 -0
  175. package/dist/services/foundry-v2/service.js +28 -0
  176. package/dist/services/grounded-authoring/service.d.ts +126 -0
  177. package/dist/services/grounded-authoring/service.js +822 -0
  178. package/dist/services/historical-case/service.d.ts +722 -0
  179. package/dist/services/historical-case/service.js +177 -0
  180. package/dist/services/improvement-loop/service.d.ts +59 -0
  181. package/dist/services/improvement-loop/service.js +176 -0
  182. package/dist/services/language-model/service.d.ts +52 -0
  183. package/dist/services/language-model/service.js +194 -0
  184. package/dist/services/oracle-builder/service.d.ts +146 -0
  185. package/dist/services/oracle-builder/service.js +513 -0
  186. package/dist/services/oracle-coverage/service.d.ts +28 -0
  187. package/dist/services/oracle-coverage/service.js +50 -0
  188. package/dist/services/oracle-coverage-witness/service.d.ts +130 -0
  189. package/dist/services/oracle-coverage-witness/service.js +538 -0
  190. package/dist/services/pipeline-challenge/service.d.ts +551 -0
  191. package/dist/services/pipeline-challenge/service.js +427 -0
  192. package/dist/services/pipeline-controls/service.d.ts +130 -0
  193. package/dist/services/pipeline-controls/service.js +483 -0
  194. package/dist/services/pipeline-oracle/service.d.ts +8 -0
  195. package/dist/services/pipeline-oracle/service.js +256 -0
  196. package/dist/services/pipeline-seed/service.d.ts +298 -0
  197. package/dist/services/pipeline-seed/service.js +428 -0
  198. package/dist/services/pipeline-spec/service.d.ts +103 -0
  199. package/dist/services/pipeline-spec/service.js +619 -0
  200. package/dist/services/pipeline-tournament/service.d.ts +258 -0
  201. package/dist/services/pipeline-tournament/service.js +476 -0
  202. package/dist/services/quality-gate/service.d.ts +233 -0
  203. package/dist/services/quality-gate/service.js +136 -0
  204. package/dist/services/repository-bundle/service.d.ts +33 -0
  205. package/dist/services/repository-bundle/service.js +114 -0
  206. package/dist/services/repository-model/service.d.ts +105 -0
  207. package/dist/services/repository-model/service.js +250 -0
  208. package/dist/services/repository-public-artifact/service.d.ts +133 -0
  209. package/dist/services/repository-public-artifact/service.js +330 -0
  210. package/dist/services/routing-benchmark/service.d.ts +362 -0
  211. package/dist/services/routing-benchmark/service.js +96 -0
  212. package/dist/services/specification-critic/service.d.ts +92 -0
  213. package/dist/services/specification-critic/service.js +172 -0
  214. package/dist/services/task-family/service.d.ts +40 -0
  215. package/dist/services/task-family/service.js +55 -0
  216. package/dist/services/task-seed/service.d.ts +906 -0
  217. package/dist/services/task-seed/service.js +1406 -0
  218. package/dist/services/task-specification/service.d.ts +27 -0
  219. package/dist/services/task-specification/service.js +40 -0
  220. package/dist/services/trajectory-policy/service.d.ts +110 -0
  221. package/dist/services/trajectory-policy/service.js +216 -0
  222. package/dist/test/agentic-capabilities-protocol.test.d.ts +1 -0
  223. package/dist/test/agentic-capabilities-protocol.test.js +570 -0
  224. package/dist/test/agentic-capabilities.test.d.ts +1 -0
  225. package/dist/test/agentic-capabilities.test.js +1461 -0
  226. package/dist/test/agentic-environment.test.d.ts +1 -0
  227. package/dist/test/agentic-environment.test.js +213 -0
  228. package/dist/test/case-pipeline-foundation.test.d.ts +1 -0
  229. package/dist/test/case-pipeline-foundation.test.js +535 -0
  230. package/dist/test/case-pipeline-protocol-v2.test.d.ts +1 -0
  231. package/dist/test/case-pipeline-protocol-v2.test.js +124 -0
  232. package/dist/test/case-pipeline-v2.test.d.ts +1 -0
  233. package/dist/test/case-pipeline-v2.test.js +286 -0
  234. package/dist/test/case-pipeline.test.d.ts +1 -0
  235. package/dist/test/case-pipeline.test.js +851 -0
  236. package/dist/test/eval-capability-policy.test.d.ts +1 -0
  237. package/dist/test/eval-capability-policy.test.js +50 -0
  238. package/dist/test/eval-event-log.test.d.ts +1 -0
  239. package/dist/test/eval-event-log.test.js +125 -0
  240. package/dist/test/evaluation-evidence-freshness.test.d.ts +1 -0
  241. package/dist/test/evaluation-evidence-freshness.test.js +44 -0
  242. package/dist/test/evaluation-evidence.test.d.ts +1 -0
  243. package/dist/test/evaluation-evidence.test.js +230 -0
  244. package/dist/test/evaluation-grader-calibration.test.d.ts +1 -0
  245. package/dist/test/evaluation-grader-calibration.test.js +373 -0
  246. package/dist/test/evaluation-proposal-digest.test.d.ts +1 -0
  247. package/dist/test/evaluation-proposal-digest.test.js +187 -0
  248. package/dist/test/evaluation-source-retrieval.test.d.ts +1 -0
  249. package/dist/test/evaluation-source-retrieval.test.js +237 -0
  250. package/dist/test/evaluation-structure-policy.test.d.ts +1 -0
  251. package/dist/test/evaluation-structure-policy.test.js +196 -0
  252. package/dist/test/fixtures/repository-resource-panel.d.ts +39 -0
  253. package/dist/test/fixtures/repository-resource-panel.js +111 -0
  254. package/dist/test/fixtures/vitest-boundary-panel.d.ts +84 -0
  255. package/dist/test/fixtures/vitest-boundary-panel.js +120 -0
  256. package/dist/test/fixtures/vitest-phase-panel.d.ts +135 -0
  257. package/dist/test/fixtures/vitest-phase-panel.js +213 -0
  258. package/dist/test/fixtures/vitest-reporter-results.d.ts +76 -0
  259. package/dist/test/fixtures/vitest-reporter-results.js +94 -0
  260. package/dist/test/grounded-authoring.test.d.ts +1 -0
  261. package/dist/test/grounded-authoring.test.js +565 -0
  262. package/dist/test/integrated-repository-history.test.d.ts +1 -0
  263. package/dist/test/integrated-repository-history.test.js +227 -0
  264. package/dist/test/project-authoring.test.js +593 -43
  265. package/dist/test/project-workflow.test.js +419 -40
  266. package/dist/test/repository-authoring-artifacts.test.d.ts +1 -0
  267. package/dist/test/repository-authoring-artifacts.test.js +185 -0
  268. package/dist/test/repository-bundle.test.d.ts +1 -0
  269. package/dist/test/repository-bundle.test.js +52 -0
  270. package/dist/test/repository-case-generation.test.d.ts +1 -0
  271. package/dist/test/repository-case-generation.test.js +3465 -0
  272. package/dist/test/repository-command-diagnostic.test.d.ts +1 -0
  273. package/dist/test/repository-command-diagnostic.test.js +55 -0
  274. package/dist/test/repository-command-signals.test.d.ts +1 -0
  275. package/dist/test/repository-command-signals.test.js +124 -0
  276. package/dist/test/repository-fixture-scope-coverage.test.d.ts +1 -0
  277. package/dist/test/repository-fixture-scope-coverage.test.js +127 -0
  278. package/dist/test/repository-fixture-validation.test.d.ts +1 -0
  279. package/dist/test/repository-fixture-validation.test.js +362 -0
  280. package/dist/test/repository-foundry-progress.test.d.ts +1 -0
  281. package/dist/test/repository-foundry-progress.test.js +110 -0
  282. package/dist/test/repository-foundry-quality.test.d.ts +1 -0
  283. package/dist/test/repository-foundry-quality.test.js +1138 -0
  284. package/dist/test/repository-import-context.test.d.ts +1 -0
  285. package/dist/test/repository-import-context.test.js +354 -0
  286. package/dist/test/repository-model-authoring.test.d.ts +1 -0
  287. package/dist/test/repository-model-authoring.test.js +544 -0
  288. package/dist/test/repository-model.test.d.ts +1 -0
  289. package/dist/test/repository-model.test.js +2195 -0
  290. package/dist/test/repository-node-test-reporter.test.d.ts +1 -0
  291. package/dist/test/repository-node-test-reporter.test.js +104 -0
  292. package/dist/test/repository-oracle-concurrency.test.d.ts +1 -0
  293. package/dist/test/repository-oracle-concurrency.test.js +542 -0
  294. package/dist/test/repository-oracle-coverage-witness.test.d.ts +1 -0
  295. package/dist/test/repository-oracle-coverage-witness.test.js +511 -0
  296. package/dist/test/repository-oracle-coverage.test.d.ts +1 -0
  297. package/dist/test/repository-oracle-coverage.test.js +168 -0
  298. package/dist/test/repository-oracle-evidence.test.d.ts +1 -0
  299. package/dist/test/repository-oracle-evidence.test.js +185 -0
  300. package/dist/test/repository-oracle-plan.test.d.ts +1 -0
  301. package/dist/test/repository-oracle-plan.test.js +176 -0
  302. package/dist/test/repository-overlay-isolation.test.d.ts +1 -0
  303. package/dist/test/repository-overlay-isolation.test.js +85 -0
  304. package/dist/test/repository-preparation-cache.test.d.ts +1 -0
  305. package/dist/test/repository-preparation-cache.test.js +414 -0
  306. package/dist/test/repository-public-artifact.test.d.ts +1 -0
  307. package/dist/test/repository-public-artifact.test.js +273 -0
  308. package/dist/test/repository-qualification-diagnostics.test.d.ts +1 -0
  309. package/dist/test/repository-qualification-diagnostics.test.js +524 -0
  310. package/dist/test/repository-reference-authoring.test.d.ts +1 -0
  311. package/dist/test/repository-reference-authoring.test.js +1633 -0
  312. package/dist/test/repository-review-evidence-v2.test.d.ts +1 -0
  313. package/dist/test/repository-review-evidence-v2.test.js +183 -0
  314. package/dist/test/repository-review-evidence.test.d.ts +1 -0
  315. package/dist/test/repository-review-evidence.test.js +124 -0
  316. package/dist/test/repository-seed-exclusions.test.d.ts +1 -0
  317. package/dist/test/repository-seed-exclusions.test.js +96 -0
  318. package/dist/test/repository-seed-selection.test.d.ts +1 -0
  319. package/dist/test/repository-seed-selection.test.js +504 -0
  320. package/dist/test/repository-semantic-calibration.test.d.ts +1 -0
  321. package/dist/test/repository-semantic-calibration.test.js +688 -0
  322. package/dist/test/repository-solution-edits.test.d.ts +1 -0
  323. package/dist/test/repository-solution-edits.test.js +377 -0
  324. package/dist/test/repository-specification-budget.test.d.ts +1 -0
  325. package/dist/test/repository-specification-budget.test.js +171 -0
  326. package/dist/test/repository-specification-contract-checkpoint.test.d.ts +1 -0
  327. package/dist/test/repository-specification-contract-checkpoint.test.js +228 -0
  328. package/dist/test/repository-specification-contract-facts.test.d.ts +1 -0
  329. package/dist/test/repository-specification-contract-facts.test.js +177 -0
  330. package/dist/test/repository-trajectory-authoring.test.d.ts +1 -0
  331. package/dist/test/repository-trajectory-authoring.test.js +176 -0
  332. package/dist/test/repository-valid-control-plan.test.d.ts +1 -0
  333. package/dist/test/repository-valid-control-plan.test.js +45 -0
  334. package/dist/test/repository-vitest-phase.test.d.ts +1 -0
  335. package/dist/test/repository-vitest-phase.test.js +848 -0
  336. package/dist/test/repository-vitest-reporter.test.d.ts +1 -0
  337. package/dist/test/repository-vitest-reporter.test.js +158 -0
  338. package/dist/test/repository-workspace-build.test.d.ts +1 -0
  339. package/dist/test/repository-workspace-build.test.js +160 -0
  340. package/dist/test/strict-authoring-schema.test.d.ts +1 -0
  341. package/dist/test/strict-authoring-schema.test.js +169 -0
  342. package/package.json +48 -6
@@ -0,0 +1,1406 @@
1
+ import { posix as path } from "node:path";
2
+ import { Clock, Context, Effect, Layer } from "effect";
3
+ import { captureGitTreeSnapshotV1, readGitTreeFileV1 } from "../../adapters/git-task-history.js";
4
+ import { repositoryCommandTestEvidenceV1 } from "../../adapters/repository-command-evidence.js";
5
+ import { encodeRepositoryCommandDiagnosticV1, sanitizeRepositoryCommandDiagnosticV1 } from "../../adapters/repository-command-diagnostic.js";
6
+ import { makeRepositoryPreparationCacheV1, replayRepositoryCommandsV1 } from "../../adapters/repository-command-runner.js";
7
+ import { selectTypeScriptWorkspaceManifestPathsV1 } from "../../adapters/typescript-repository-index.js";
8
+ import { RepositoryFoundryError } from "../../errors.js";
9
+ import { assertRepositoryHiddenFixtureSuiteV1 } from "../../repository-fixture-protocol.js";
10
+ import { assertRepositorySeedCrossBatchQualificationV1, assertRepositoryTaskSeedV1 } from "../../repository-task-seed-protocol.js";
11
+ import { RepositoryFoundryEvidenceReconstruction } from "../evidence-reconstruction/service.js";
12
+ import { reportFoundryAuthoringArtifactV1, reportFoundryHeartbeatV1 } from "../foundry-progress/service.js";
13
+ const failure = (detail) => new RepositoryFoundryError({
14
+ operation: "extract-task-seeds",
15
+ detail
16
+ });
17
+ /**
18
+ * Qualification and oracle admission consume the same executed test evidence.
19
+ * Passing baseline/reference builds cannot turn an unexecuted pre-change grader
20
+ * into a behavioral failure, even when an overlay imports a newly added API.
21
+ */
22
+ const qualificationExecutionBlocked = (result) => repositoryCommandTestEvidenceV1(result, result.recipeId).outcome === "unknown";
23
+ const qualificationExecutionFailure = (phase, result) => {
24
+ const diagnostic = sanitizeRepositoryCommandDiagnosticV1(result.stdout, result.stderr, result.outputTruncated);
25
+ return new RepositoryFoundryError({
26
+ operation: "extract-task-seeds",
27
+ classification: "environment_blocked",
28
+ detail: `historical qualification stopped after operational failure prevented grading: phase=${phase}; stage=${result.stage}; recipe=${result.recipeId}; exit=${String(result.exitCode)}; timeout=${String(result.timedOut)}; outputTruncated=${String(result.outputTruncated)}${diagnostic === undefined ? "" :
29
+ `; diagnostic=${encodeRepositoryCommandDiagnosticV1(diagnostic.text)}; diagnosticTruncated=${String(diagnostic.truncated)}`}; evidence=${repositoryCommandTestEvidenceV1(result, result.recipeId).detail}; unstarted phases skipped; later batches were not started. Qualification requires executable reviewed tests on both revisions and attributable pre-change assertion failures. Repair the execution environment or select another candidate; repeating this unchanged plan cannot establish qualification.`
30
+ });
31
+ };
32
+ const hiddenQualificationOverlays = Effect.fnUntraced(function* (seed, fixture) {
33
+ const hiddenTestOverlays = seed.capabilityEvidence
34
+ .filter((evidence) => evidence.kind === "test" && evidence.path !== undefined)
35
+ .map((evidence) => ({
36
+ path: evidence.path,
37
+ sourceCommit: seed.referenceCommit
38
+ }));
39
+ const hiddenContentOverlays = fixture?.overlays.map((overlay) => ({
40
+ path: overlay.path,
41
+ content: overlay.content
42
+ })) ?? [];
43
+ if (fixture !== undefined) {
44
+ assertRepositoryHiddenFixtureSuiteV1(fixture);
45
+ const reviewedTestPaths = new Set(seed.capabilityEvidence.flatMap((evidence) => evidence.kind === "test" && evidence.path !== undefined ? [evidence.path] : []));
46
+ const outOfScopePath = fixture.overlays.find((overlay) => !reviewedTestPaths.has(overlay.path))?.path;
47
+ if (outOfScopePath !== undefined) {
48
+ return yield* failure(`qualification fixture targets a path outside the reviewed test scope: ${outOfScopePath}`);
49
+ }
50
+ }
51
+ return { hiddenTestOverlays, hiddenContentOverlays };
52
+ });
53
+ const packageRoot = (map, paths) => [...map.packages]
54
+ .filter((entry) => paths.some((path) => entry.path === "." || path === entry.path || path.startsWith(`${entry.path}/`)))
55
+ .sort((left, right) => right.path.length - left.path.length)[0]?.path;
56
+ const commandsFor = (map, testPaths, changedPaths) => {
57
+ const commandIds = new Set(map.tests.filter((test) => testPaths.includes(test.path)).flatMap((test) => test.commandIds));
58
+ const bound = map.commands
59
+ .filter((command) => command.kind === "test" && commandIds.has(command.id))
60
+ .sort((left, right) => left.cwd.localeCompare(right.cwd) || left.id.localeCompare(right.id));
61
+ if (bound.length > 0) {
62
+ const scoped = bound.filter((command) => command.cwd !== ".");
63
+ // A root workspace test command is a broad fallback, not an additional
64
+ // package-level grade. Keeping it beside specifically bound package tests
65
+ // expands replay preparation to unrelated deployment applications (and
66
+ // their environment-only builds), making a focused historical seed depend
67
+ // on credentials that are intentionally unavailable in the sandbox.
68
+ return scoped.length > 0 ? scoped : bound;
69
+ }
70
+ const root = packageRoot(map, changedPaths);
71
+ const fallback = map.commands.find((command) => command.kind === "test" && command.cwd === root);
72
+ return fallback === undefined ? [] : [fallback];
73
+ };
74
+ const manifestPathFor = (cwd) => cwd === "." ? "package.json" : `${cwd}/package.json`;
75
+ const manifestRootFor = (path) => path === "package.json" ? "." : path.slice(0, -"/package.json".length);
76
+ const recipeSafeId = (value) => value
77
+ .replace(/^@/u, "")
78
+ .replace(/[^A-Za-z0-9]+/gu, "-")
79
+ .replace(/^-|-$/gu, "");
80
+ /**
81
+ * Historical qualification replays one reviewed grade at a time inside a
82
+ * ten-minute resumable capability slice. Package discovery's two-minute test
83
+ * default is appropriate for a quick repository probe, but it is too short
84
+ * for a focused test file that legitimately contains several integration
85
+ * cases. Leave two minutes in the slice for preparation, durable checkpoint
86
+ * persistence, and command teardown.
87
+ */
88
+ const HISTORICAL_QUALIFICATION_GRADE_TIMEOUT_MS_V1 = 8 * 60_000;
89
+ const qualificationGradeRecipe = (recipe) => recipe.kind === "test"
90
+ ? {
91
+ ...recipe,
92
+ timeoutMs: Math.max(recipe.timeoutMs, HISTORICAL_QUALIFICATION_GRADE_TIMEOUT_MS_V1)
93
+ }
94
+ : recipe;
95
+ const recipeReadsEmittedArtifacts = (recipe) => recipe.args.some((argument) => /(?:^|[/\\])dist(?:[/\\]|$)/u.test(argument));
96
+ const candidateValidationScript = (input) => {
97
+ const build = input.scripts.build;
98
+ if (typeof build !== "string")
99
+ return undefined;
100
+ if (!input.targetRoots.has(input.root) ||
101
+ !/\bnext\s+build\b/u.test(build) ||
102
+ input.gradeRecipes.some((recipe) => recipe.cwd === input.root && recipeReadsEmittedArtifacts(recipe))) {
103
+ return "build";
104
+ }
105
+ return (["types:check", "typecheck"].find((script) => typeof input.scripts[script] === "string") ??
106
+ "build");
107
+ };
108
+ const compiledTestExtensions = {
109
+ ".ts": ".js",
110
+ ".tsx": ".js",
111
+ ".mts": ".mjs",
112
+ ".cts": ".cjs"
113
+ };
114
+ const compiledNodeTestPath = (relativeTestPath) => {
115
+ if (!relativeTestPath.startsWith("src/test/"))
116
+ return undefined;
117
+ const extension = path.extname(relativeTestPath);
118
+ const outputExtension = compiledTestExtensions[extension];
119
+ if (outputExtension === undefined)
120
+ return undefined;
121
+ return `dist/${relativeTestPath.slice("src/".length, -extension.length)}${outputExtension}`;
122
+ };
123
+ const historicalTestScript = (manifest) => {
124
+ if (manifest?.scripts === null || typeof manifest?.scripts !== "object")
125
+ return undefined;
126
+ const test = manifest.scripts.test;
127
+ return typeof test === "string" ? test : undefined;
128
+ };
129
+ const recognizedPnpmTestRunner = (testScript) => {
130
+ if (testScript === undefined)
131
+ return undefined;
132
+ const normalized = testScript.trim().replace(/\s+/gu, " ");
133
+ if (normalized === "vp test run") {
134
+ return { argv: ["vp", "test", "run"] };
135
+ }
136
+ if (normalized === "pnpm vitest run") {
137
+ return {
138
+ argv: ["vitest", "run"],
139
+ runFromRepositoryRoot: true
140
+ };
141
+ }
142
+ if (normalized === "vitest" || normalized === "vitest run") {
143
+ return { argv: ["vitest", "run"] };
144
+ }
145
+ if (!normalized.startsWith("node --test") && !normalized.startsWith("tsx --test")) {
146
+ return undefined;
147
+ }
148
+ const concurrencyFlag = /^(?:node|tsx) --test (--test-concurrency=[1-9]\d{0,2})(?= |$)/u.exec(normalized)?.[1];
149
+ const suiteScript = concurrencyFlag === undefined ? normalized : normalized.replace(` ${concurrencyFlag}`, "");
150
+ const nodeRunner = normalized.startsWith("tsx ") ? "tsx" : "node";
151
+ const compiledSuite = /^(?:node|tsx) --test (?:"|')?dist\/test\/\*\.test\.js(?:"|')?$/u.test(suiteScript);
152
+ const sourceSuite = suiteScript === "node --test" ||
153
+ suiteScript === "tsx --test" ||
154
+ /^(?:node|tsx) --test (?!["']?-)(?:[\w./*?{}-]+|"[\w./*?{}-]+"|'[\w./*?{}-]+')(?: (?!["']?-)(?:[\w./*?{}-]+|"[\w./*?{}-]+"|'[\w./*?{}-]+'))*$/u.test(suiteScript);
155
+ if (!compiledSuite && !sourceSuite)
156
+ return undefined;
157
+ return {
158
+ argv: concurrencyFlag === undefined
159
+ ? [nodeRunner, "--test"]
160
+ : [nodeRunner, "--test", concurrencyFlag],
161
+ compiledTests: compiledSuite ? true : undefined
162
+ };
163
+ };
164
+ const relativeRecipeTestPaths = (input) => input.testPaths
165
+ .filter((testPath) => input.recipe.cwd === "." || testPath.startsWith(`${input.recipe.cwd}/`))
166
+ .map((testPath) => input.recipe.cwd === "." ? testPath : path.relative(input.recipe.cwd, testPath))
167
+ .filter((testPath) => testPath.length > 0 && testPath !== ".." && !testPath.startsWith("../"))
168
+ .sort();
169
+ const targetedPnpmTestRecipe = (input) => {
170
+ const testPaths = input.runner.runFromRepositoryRoot
171
+ ? [...input.testPaths].sort()
172
+ : relativeRecipeTestPaths(input);
173
+ if (testPaths.length === 0)
174
+ return undefined;
175
+ const projectedTestPaths = input.runner.compiledTests
176
+ ? testPaths.map(compiledNodeTestPath)
177
+ : testPaths;
178
+ if (!projectedTestPaths.every((testPath) => testPath !== undefined)) {
179
+ return undefined;
180
+ }
181
+ let executable = "node";
182
+ let args = [...input.runner.argv.slice(1), ...projectedTestPaths];
183
+ if (input.packageManagerVersion !== undefined) {
184
+ executable = "corepack";
185
+ args = [
186
+ `pnpm@${input.packageManagerVersion}`,
187
+ "exec",
188
+ ...input.runner.argv,
189
+ ...projectedTestPaths
190
+ ];
191
+ }
192
+ return qualificationGradeRecipe({
193
+ ...input.recipe,
194
+ id: input.id,
195
+ cwd: input.runner.runFromRepositoryRoot ? "." : input.recipe.cwd,
196
+ executable,
197
+ args
198
+ });
199
+ };
200
+ const narrowPnpmTestRecipe = (input) => {
201
+ if (input.recipe.kind !== "test") {
202
+ return input.recipe;
203
+ }
204
+ const runner = recognizedPnpmTestRunner(historicalTestScript(input.manifest));
205
+ if (runner === undefined)
206
+ return qualificationGradeRecipe(input.recipe);
207
+ if (input.packageManagerVersion === undefined && runner.argv[0] !== "node")
208
+ return qualificationGradeRecipe(input.recipe);
209
+ return targetedPnpmTestRecipe({
210
+ recipe: input.recipe,
211
+ id: `${input.recipe.id}-targeted`,
212
+ runner,
213
+ packageManagerVersion: input.packageManagerVersion,
214
+ testPaths: input.testPaths
215
+ }) ?? qualificationGradeRecipe(input.recipe);
216
+ };
217
+ const historicalBaselineRecipe = (input) => {
218
+ const baselineId = `${input.recipe.id}-baseline-control`;
219
+ const testScript = historicalTestScript(input.manifest);
220
+ const runner = recognizedPnpmTestRunner(testScript);
221
+ const baselineRecipe = qualificationGradeRecipe({ ...input.recipe, id: baselineId });
222
+ if (input.recipe.kind !== "test" ||
223
+ (input.packageManagerVersion === undefined && runner?.argv[0] !== "node") ||
224
+ testScript === undefined) {
225
+ return baselineRecipe;
226
+ }
227
+ if (runner !== undefined) {
228
+ const targeted = targetedPnpmTestRecipe({
229
+ recipe: input.recipe,
230
+ id: baselineId,
231
+ runner,
232
+ packageManagerVersion: input.packageManagerVersion,
233
+ testPaths: input.controlTestPaths
234
+ });
235
+ if (targeted !== undefined)
236
+ return targeted;
237
+ }
238
+ if (input.packageManagerVersion === undefined)
239
+ return baselineRecipe;
240
+ return qualificationGradeRecipe({
241
+ ...input.recipe,
242
+ id: baselineId,
243
+ executable: "corepack",
244
+ args: [`pnpm@${input.packageManagerVersion}`, "run", "test"]
245
+ });
246
+ };
247
+ const repositoryTestPath = (value) => {
248
+ const lower = value.toLowerCase();
249
+ const base = lower.split("/").at(-1) ?? lower;
250
+ return (/\.(?:[cm]?[jt]sx?|py|go|rs)$/u.test(lower) &&
251
+ (/(?:^|[._-])(?:test|spec)(?:[._-]|$)/u.test(base) ||
252
+ /(^|\/)(?:test|tests|__tests__)(\/|$)/u.test(lower)));
253
+ };
254
+ const semanticTokens = (content) => {
255
+ const ignored = new Set([
256
+ "assert",
257
+ "async",
258
+ "await",
259
+ "boolean",
260
+ "catch",
261
+ "class",
262
+ "const",
263
+ "describe",
264
+ "effect",
265
+ "else",
266
+ "error",
267
+ "expect",
268
+ "export",
269
+ "false",
270
+ "finally",
271
+ "from",
272
+ "function",
273
+ "import",
274
+ "input",
275
+ "interface",
276
+ "length",
277
+ "message",
278
+ "never",
279
+ "node",
280
+ "null",
281
+ "number",
282
+ "object",
283
+ "process",
284
+ "readonly",
285
+ "return",
286
+ "shared",
287
+ "string",
288
+ "sync",
289
+ "test",
290
+ "t3tools",
291
+ "true",
292
+ "type",
293
+ "undefined",
294
+ "unknown",
295
+ "value",
296
+ "void",
297
+ "while",
298
+ "yield"
299
+ ]);
300
+ return new Set([...content.matchAll(/[A-Za-z_$][A-Za-z0-9_$]*/gu)]
301
+ .map((match) => match[0].toLowerCase())
302
+ .filter((token) => token.length >= 3 && !ignored.has(token)));
303
+ };
304
+ const selectHistoricalBaselineControlPathsV1 = Effect.fn("TaskSeed.selectHistoricalBaselineControls")(function* (input) {
305
+ const withinRecipe = (candidate) => input.recipe.cwd === "." || candidate.startsWith(`${input.recipe.cwd}/`);
306
+ const changedSources = input.changedPaths.filter((candidate) => withinRecipe(candidate) &&
307
+ input.snapshot.files.includes(candidate) &&
308
+ !repositoryTestPath(candidate));
309
+ const existingChangedTests = input.testPaths.filter((candidate) => withinRecipe(candidate) &&
310
+ input.snapshot.files.includes(candidate) &&
311
+ repositoryTestPath(candidate));
312
+ if (existingChangedTests.length > 0) {
313
+ return [...new Set(existingChangedTests)].sort();
314
+ }
315
+ const allPackageTests = input.snapshot.files.filter((candidate) => withinRecipe(candidate) && repositoryTestPath(candidate));
316
+ if (allPackageTests.length === 0)
317
+ return [];
318
+ const changedDirectories = new Set(changedSources.map((candidate) => path.dirname(candidate)));
319
+ const sameDirectoryTests = allPackageTests.filter((candidate) => changedDirectories.has(path.dirname(candidate)));
320
+ const candidates = sameDirectoryTests.length > 0 ? sameDirectoryTests : allPackageTests;
321
+ const changedContents = yield* Effect.forEach(changedSources, (candidate) => readGitTreeFileV1({
322
+ snapshot: input.snapshot,
323
+ path: candidate
324
+ }));
325
+ const sourceTokens = semanticTokens(changedContents.join("\n"));
326
+ const candidateContents = yield* Effect.forEach(candidates, (candidate) => readGitTreeFileV1({
327
+ snapshot: input.snapshot,
328
+ path: candidate
329
+ }).pipe(Effect.map((content) => ({
330
+ path: candidate,
331
+ content,
332
+ tokens: semanticTokens(content)
333
+ }))));
334
+ const documentFrequency = new Map();
335
+ for (const candidate of candidateContents) {
336
+ for (const token of candidate.tokens) {
337
+ documentFrequency.set(token, (documentFrequency.get(token) ?? 0) + 1);
338
+ }
339
+ }
340
+ const score = (candidate) => {
341
+ const semantic = [...sourceTokens]
342
+ .filter((token) => candidate.tokens.has(token))
343
+ .reduce((total, token) => total +
344
+ Math.log((candidateContents.length + 1) / ((documentFrequency.get(token) ?? 0) + 1)) +
345
+ 1, 0);
346
+ return semantic / Math.sqrt(Math.max(1, candidate.content.length));
347
+ };
348
+ return candidateContents
349
+ .sort((left, right) => score(right) - score(left) ||
350
+ left.content.length - right.content.length ||
351
+ left.path.localeCompare(right.path))
352
+ .slice(0, 1)
353
+ .map((candidate) => candidate.path);
354
+ });
355
+ const readJsonObject = Effect.fn("TaskSeed.readEnvironmentManifest")(function* (input) {
356
+ if (!input.snapshot.files.includes(input.path))
357
+ return undefined;
358
+ const content = yield* readGitTreeFileV1(input);
359
+ return yield* Effect.try({
360
+ try: () => JSON.parse(content),
361
+ catch: () => failure(`historical environment manifest is invalid JSON: ${input.path}`)
362
+ });
363
+ });
364
+ const stripJsonComments = (content) => {
365
+ let result = "";
366
+ let inString = false;
367
+ let escaped = false;
368
+ for (let index = 0; index < content.length; index += 1) {
369
+ const character = content[index];
370
+ const next = content[index + 1];
371
+ if (inString) {
372
+ result += character;
373
+ if (escaped) {
374
+ escaped = false;
375
+ }
376
+ else if (character === "\\") {
377
+ escaped = true;
378
+ }
379
+ else if (character === '"') {
380
+ inString = false;
381
+ }
382
+ continue;
383
+ }
384
+ if (character === '"') {
385
+ inString = true;
386
+ result += character;
387
+ continue;
388
+ }
389
+ if (character === "/" && next === "/") {
390
+ result += " ";
391
+ index += 1;
392
+ while (index + 1 < content.length && content[index + 1] !== "\n") {
393
+ result += " ";
394
+ index += 1;
395
+ }
396
+ continue;
397
+ }
398
+ if (character === "/" && next === "*") {
399
+ result += " ";
400
+ index += 1;
401
+ let closed = false;
402
+ while (index + 1 < content.length) {
403
+ const commentCharacter = content[index + 1];
404
+ const commentNext = content[index + 2];
405
+ if (commentCharacter === "*" && commentNext === "/") {
406
+ result += " ";
407
+ index += 2;
408
+ closed = true;
409
+ break;
410
+ }
411
+ result += commentCharacter === "\n" ? "\n" : " ";
412
+ index += 1;
413
+ }
414
+ if (!closed) {
415
+ throw new Error("unterminated block comment");
416
+ }
417
+ continue;
418
+ }
419
+ result += character;
420
+ }
421
+ return result;
422
+ };
423
+ const stripTrailingJsonCommas = (content) => {
424
+ let result = "";
425
+ let inString = false;
426
+ let escaped = false;
427
+ for (let index = 0; index < content.length; index += 1) {
428
+ const character = content[index];
429
+ if (inString) {
430
+ result += character;
431
+ if (escaped) {
432
+ escaped = false;
433
+ }
434
+ else if (character === "\\") {
435
+ escaped = true;
436
+ }
437
+ else if (character === '"') {
438
+ inString = false;
439
+ }
440
+ continue;
441
+ }
442
+ if (character === '"') {
443
+ inString = true;
444
+ result += character;
445
+ continue;
446
+ }
447
+ if (character === ",") {
448
+ let lookahead = index + 1;
449
+ while (lookahead < content.length && /\s/u.test(content[lookahead])) {
450
+ lookahead += 1;
451
+ }
452
+ if (content[lookahead] === "]" || content[lookahead] === "}") {
453
+ continue;
454
+ }
455
+ }
456
+ result += character;
457
+ }
458
+ return result;
459
+ };
460
+ const readJsoncObject = Effect.fn("TaskSeed.readTypeScriptProjectConfig")(function* (input) {
461
+ if (!input.snapshot.files.includes(input.path))
462
+ return undefined;
463
+ const content = yield* readGitTreeFileV1(input);
464
+ return yield* Effect.try({
465
+ try: () => JSON.parse(stripTrailingJsonCommas(stripJsonComments(content))),
466
+ catch: () => failure(`historical TypeScript project config is invalid JSONC: ${input.path}`)
467
+ });
468
+ });
469
+ const normalizeProjectReference = (configPath, referencePath) => {
470
+ const portableReference = referencePath.replaceAll("\\", "/").trim();
471
+ if (portableReference.length === 0 ||
472
+ portableReference.includes("\0") ||
473
+ path.isAbsolute(portableReference) ||
474
+ /^[A-Za-z]:\//u.test(portableReference)) {
475
+ throw failure(`historical TypeScript project reference is not repository-relative: ${configPath} -> ${JSON.stringify(referencePath)}`);
476
+ }
477
+ const resolved = path.normalize(path.join(path.dirname(configPath), portableReference));
478
+ if (resolved === ".." || resolved.startsWith("../")) {
479
+ throw failure(`historical TypeScript project reference escapes the repository: ${configPath} -> ${JSON.stringify(referencePath)}`);
480
+ }
481
+ return resolved.endsWith(".json") ? resolved : path.join(resolved, "tsconfig.json");
482
+ };
483
+ export const deriveHistoricalSeedEnvironmentV1 = Effect.fn("TaskSeed.deriveHistoricalEnvironment")(function* (input) {
484
+ const snapshot = yield* captureGitTreeSnapshotV1({
485
+ repositoryRoot: input.repositoryRoot,
486
+ requestedRef: input.commit
487
+ });
488
+ const { selectedManifestPaths } = yield* selectTypeScriptWorkspaceManifestPathsV1(snapshot);
489
+ const selectedManifestPathSet = new Set(selectedManifestPaths);
490
+ const rootManifest = yield* readJsonObject({
491
+ snapshot,
492
+ path: "package.json"
493
+ });
494
+ const targetManifests = yield* Effect.forEach(input.gradeRecipes, (gradeRecipe) => readJsonObject({
495
+ snapshot,
496
+ path: manifestPathFor(gradeRecipe.cwd)
497
+ }).pipe(Effect.map((manifest) => ({
498
+ root: gradeRecipe.cwd,
499
+ manifest
500
+ }))));
501
+ const packageManager = typeof rootManifest?.packageManager === "string"
502
+ ? /^pnpm@((?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-[0-9A-Za-z.-]+)?)$/u.exec(rootManifest.packageManager)
503
+ : null;
504
+ const lockfilePath = snapshot.files.includes("pnpm-lock.yaml") ? "pnpm-lock.yaml" : undefined;
505
+ const gradeRecipes = input.gradeRecipes.map((recipe) => narrowPnpmTestRecipe({
506
+ recipe,
507
+ manifest: targetManifests.find((target) => target.root === recipe.cwd)?.manifest,
508
+ packageManagerVersion: packageManager?.[1],
509
+ testPaths: input.testPaths
510
+ }));
511
+ const baselineGradeRecipes = yield* Effect.forEach(input.gradeRecipes, (recipe) => selectHistoricalBaselineControlPathsV1({
512
+ snapshot,
513
+ recipe,
514
+ changedPaths: input.changedPaths,
515
+ testPaths: input.testPaths
516
+ }).pipe(Effect.map((controlTestPaths) => historicalBaselineRecipe({
517
+ recipe,
518
+ manifest: targetManifests.find((target) => target.root === recipe.cwd)?.manifest,
519
+ packageManagerVersion: packageManager?.[1],
520
+ controlTestPaths
521
+ }))));
522
+ const trustedPreparationRecipes = [];
523
+ const candidateValidationRecipes = [];
524
+ const referencedProjectConfigPaths = new Set();
525
+ if (packageManager?.[1] !== undefined && lockfilePath !== undefined) {
526
+ const pnpm = `pnpm@${packageManager[1]}`;
527
+ const packageManifests = new Map();
528
+ for (const path of snapshot.files.filter((candidate) => selectedManifestPathSet.has(candidate))) {
529
+ const manifest = yield* readJsonObject({ snapshot, path });
530
+ if (typeof manifest?.name !== "string")
531
+ continue;
532
+ const manifestScripts = manifest.scripts !== null && typeof manifest.scripts === "object"
533
+ ? manifest.scripts
534
+ : {};
535
+ const dependencies = [
536
+ manifest.dependencies,
537
+ manifest.devDependencies,
538
+ manifest.optionalDependencies,
539
+ manifest.peerDependencies
540
+ ].flatMap((value) => value !== null && typeof value === "object"
541
+ ? Object.keys(value)
542
+ : []);
543
+ packageManifests.set(manifest.name, {
544
+ root: manifestRootFor(path),
545
+ scripts: manifestScripts,
546
+ manifestDependencies: dependencies,
547
+ emitsArtifacts: /(?:^|[\/])(?:dist|build|lib)[\/]/u.test(JSON.stringify([manifest.main, manifest.module, manifest.exports]))
548
+ });
549
+ }
550
+ const packageByRoot = new Map([...packageManifests].map(([name, manifest]) => [manifest.root, name]));
551
+ const owningPackageName = (projectConfigPath) => [...packageByRoot]
552
+ .filter(([root]) => root === "."
553
+ ? !projectConfigPath.includes("/")
554
+ : projectConfigPath === `${root}/tsconfig.json` ||
555
+ projectConfigPath.startsWith(`${root}/`))
556
+ .sort(([left], [right]) => right.length - left.length || left.localeCompare(right))[0]?.[1];
557
+ const projectReferences = new Map();
558
+ const loadProjectReferences = Effect.fn("TaskSeed.loadTypeScriptProjectReferences")(function* (configPath) {
559
+ const existing = projectReferences.get(configPath);
560
+ if (existing !== undefined)
561
+ return existing;
562
+ const config = yield* readJsoncObject({
563
+ snapshot,
564
+ path: configPath
565
+ });
566
+ if (config === undefined) {
567
+ return yield* failure(`historical TypeScript project reference target is missing: ${configPath}`);
568
+ }
569
+ if (config.references !== undefined && !Array.isArray(config.references)) {
570
+ return yield* failure(`historical TypeScript project references must be an array: ${configPath}`);
571
+ }
572
+ const references = [];
573
+ for (const [index, entry] of (config.references ?? []).entries()) {
574
+ if (entry === null ||
575
+ typeof entry !== "object" ||
576
+ typeof entry.path !== "string") {
577
+ return yield* failure(`historical TypeScript project reference ${String(index)} is malformed: ${configPath}`);
578
+ }
579
+ const referencedConfigPath = yield* Effect.try({
580
+ try: () => normalizeProjectReference(configPath, entry.path),
581
+ catch: (cause) => cause instanceof RepositoryFoundryError
582
+ ? cause
583
+ : failure(`failed to resolve historical TypeScript project reference ${String(index)}: ${configPath}`)
584
+ });
585
+ if (!snapshot.files.includes(referencedConfigPath)) {
586
+ return yield* failure(`historical TypeScript project reference target is missing: ${configPath} -> ${referencedConfigPath}`);
587
+ }
588
+ references.push(referencedConfigPath);
589
+ }
590
+ const ordered = [...new Set(references)].sort();
591
+ projectReferences.set(configPath, ordered);
592
+ referencedProjectConfigPaths.add(configPath);
593
+ for (const reference of ordered) {
594
+ referencedProjectConfigPaths.add(reference);
595
+ }
596
+ return ordered;
597
+ });
598
+ const projectDependencies = new Map();
599
+ const visitedProjectConfigs = new Set();
600
+ const visitingProjectConfigs = [];
601
+ const visitProjectConfig = (configPath, inheritedPackageName) => Effect.gen(function* () {
602
+ const sourcePackageName = owningPackageName(configPath) ?? inheritedPackageName;
603
+ const visitKey = `${sourcePackageName}\0${configPath}`;
604
+ if (visitedProjectConfigs.has(visitKey))
605
+ return;
606
+ const cycleIndex = visitingProjectConfigs.indexOf(visitKey);
607
+ if (cycleIndex >= 0) {
608
+ return yield* failure(`historical TypeScript project references contain a cycle: ${[
609
+ ...visitingProjectConfigs.slice(cycleIndex),
610
+ visitKey
611
+ ]
612
+ .map((entry) => entry.split("\0")[1])
613
+ .join(" -> ")}`);
614
+ }
615
+ visitingProjectConfigs.push(visitKey);
616
+ const references = yield* loadProjectReferences(configPath);
617
+ for (const referencedConfigPath of references) {
618
+ const dependencyPackageName = owningPackageName(referencedConfigPath);
619
+ if (sourcePackageName !== undefined &&
620
+ dependencyPackageName !== undefined &&
621
+ sourcePackageName !== dependencyPackageName) {
622
+ const dependencies = projectDependencies.get(sourcePackageName) ?? new Set();
623
+ dependencies.add(dependencyPackageName);
624
+ projectDependencies.set(sourcePackageName, dependencies);
625
+ }
626
+ yield* visitProjectConfig(referencedConfigPath, dependencyPackageName ?? sourcePackageName);
627
+ }
628
+ visitingProjectConfigs.pop();
629
+ visitedProjectConfigs.add(visitKey);
630
+ });
631
+ const targetPackageNames = targetManifests.flatMap(({ manifest }) => typeof manifest?.name === "string" ? [manifest.name] : []);
632
+ const targetRoots = new Set(targetManifests.map(({ root }) => root));
633
+ const orderedPackageNames = [];
634
+ const visited = new Set();
635
+ const visiting = [];
636
+ const visit = (name) => Effect.gen(function* () {
637
+ if (visited.has(name))
638
+ return;
639
+ const cycleIndex = visiting.indexOf(name);
640
+ if (cycleIndex >= 0) {
641
+ return yield* failure(`historical workspace build graph contains a cycle: ${[
642
+ ...visiting.slice(cycleIndex),
643
+ name
644
+ ].join(" -> ")}`);
645
+ }
646
+ const manifest = packageManifests.get(name);
647
+ if (manifest === undefined)
648
+ return;
649
+ visiting.push(name);
650
+ const configPath = manifest.root === "." ? "tsconfig.json" : `${manifest.root}/tsconfig.json`;
651
+ if (snapshot.files.includes(configPath)) {
652
+ yield* visitProjectConfig(configPath, name);
653
+ }
654
+ const dependencies = [
655
+ ...new Set([...manifest.manifestDependencies, ...(projectDependencies.get(name) ?? [])])
656
+ ]
657
+ .filter((candidate) => packageManifests.has(candidate))
658
+ .sort();
659
+ for (const dependency of dependencies) {
660
+ yield* visit(dependency);
661
+ }
662
+ visiting.pop();
663
+ visited.add(name);
664
+ orderedPackageNames.push(name);
665
+ });
666
+ for (const name of [...new Set(targetPackageNames)].sort()) {
667
+ yield* visit(name);
668
+ }
669
+ // The topological traversal already includes both manifest dependencies and
670
+ // TypeScript references. Framework apps may have no project references,
671
+ // while their workspace dependencies still export generated build artifacts.
672
+ const validationPackageNames = new Set(orderedPackageNames);
673
+ trustedPreparationRecipes.push({
674
+ id: "command-environment-pnpm-frozen-install",
675
+ kind: "custom",
676
+ cwd: ".",
677
+ executable: "corepack",
678
+ args: [
679
+ pnpm,
680
+ ...(input.executionMode === undefined
681
+ ? []
682
+ : targetPackageNames.flatMap((name) => ["--filter", `${name}...`])),
683
+ "install",
684
+ "--prefer-offline",
685
+ "--frozen-lockfile",
686
+ "--ignore-pnpmfile",
687
+ "--ignore-scripts"
688
+ ],
689
+ timeoutMs: 10 * 60_000
690
+ });
691
+ for (const name of orderedPackageNames) {
692
+ if (!validationPackageNames.has(name))
693
+ continue;
694
+ const manifest = packageManifests.get(name);
695
+ if (input.executionMode !== undefined) {
696
+ const target = targetRoots.has(manifest.root);
697
+ const targetNeedsBuild = input.executionMode === "emitted" ||
698
+ gradeRecipes.some((recipe) => recipe.cwd === manifest.root && recipeReadsEmittedArtifacts(recipe));
699
+ // Source-capable targets do not need their own compiler. Dependencies
700
+ // still build when their package surface resolves to emitted artifacts.
701
+ if (target ? !targetNeedsBuild : !manifest.emitsArtifacts)
702
+ continue;
703
+ }
704
+ const script = candidateValidationScript({
705
+ root: manifest.root,
706
+ scripts: manifest.scripts,
707
+ targetRoots,
708
+ gradeRecipes
709
+ });
710
+ if (script === undefined)
711
+ continue;
712
+ candidateValidationRecipes.push({
713
+ id: `command-environment-${recipeSafeId(script)}-${recipeSafeId(name)}`,
714
+ kind: "build",
715
+ cwd: manifest.root,
716
+ executable: "corepack",
717
+ args: [pnpm, "run", script],
718
+ timeoutMs: 10 * 60_000
719
+ });
720
+ }
721
+ }
722
+ const environment = {
723
+ ...(packageManager?.[1] === undefined || lockfilePath === undefined
724
+ ? {}
725
+ : {
726
+ packageManager: {
727
+ name: "pnpm",
728
+ version: packageManager[1]
729
+ },
730
+ lockfilePath
731
+ }),
732
+ protectedControlPaths: [
733
+ ...new Set([
734
+ "package.json",
735
+ "pnpm-lock.yaml",
736
+ "pnpm-workspace.yaml",
737
+ ".npmrc",
738
+ "turbo.json",
739
+ "tsconfig.base.json",
740
+ ...[
741
+ ...input.gradeRecipes.map((recipe) => recipe.cwd),
742
+ ...candidateValidationRecipes.map((recipe) => recipe.cwd)
743
+ ].flatMap((cwd) => [
744
+ manifestPathFor(cwd),
745
+ cwd === "." ? "tsconfig.json" : `${cwd}/tsconfig.json`
746
+ ]),
747
+ ...referencedProjectConfigPaths
748
+ ].filter((path) => snapshot.files.includes(path) && !input.changedPaths.includes(path)))
749
+ ].sort(),
750
+ trustedPreparationRecipes,
751
+ candidateValidationRecipes,
752
+ baselineGradeRecipes,
753
+ gradeRecipes
754
+ };
755
+ return environment;
756
+ });
757
+ const historicalSourceKind = (subject) => {
758
+ if (/^(?:feat)(?:\([^)]+\))?[!:]/iu.test(subject) || /^(?:add|implement)\b/iu.test(subject)) {
759
+ return "historical-feature";
760
+ }
761
+ if (/^(?:refactor)(?:\([^)]+\))?[!:]/iu.test(subject) ||
762
+ /^(?:refactor|migrate|restructure)\b/iu.test(subject)) {
763
+ return "historical-refactor";
764
+ }
765
+ return "historical-fix";
766
+ };
767
+ export class TaskSeed extends Context.Service()("@velum-labs/routekit-eval-setup/TaskSeed") {
768
+ }
769
+ export const extractHistoricalTaskSeedsV1 = Effect.fn("TaskSeed.extractHistorical")(function* (map, options) {
770
+ const seeds = [];
771
+ for (const episode of map.historyEpisodes.filter((entry) => entry.seedEligible)) {
772
+ if (episode.parentCommit === undefined)
773
+ continue;
774
+ const gradeRecipes = commandsFor(map, episode.testPaths, [
775
+ ...episode.implementationPaths,
776
+ ...episode.protocolPaths,
777
+ ...episode.testPaths
778
+ ]);
779
+ if (gradeRecipes.length === 0) {
780
+ continue;
781
+ }
782
+ const environment = options?.deferEnvironment === true
783
+ ? {
784
+ protectedControlPaths: [],
785
+ trustedPreparationRecipes: [],
786
+ candidateValidationRecipes: [],
787
+ baselineGradeRecipes: [],
788
+ gradeRecipes
789
+ }
790
+ : yield* deriveHistoricalSeedEnvironmentV1({
791
+ repositoryRoot: map.repository.root,
792
+ commit: episode.parentCommit,
793
+ gradeRecipes,
794
+ testPaths: episode.testPaths,
795
+ changedPaths: episode.changedPaths.flatMap(({ path, previousPath }) => previousPath === undefined ? [path] : [previousPath, path])
796
+ });
797
+ const testIds = map.tests
798
+ .filter((test) => episode.testPaths.includes(test.path))
799
+ .map((test) => test.id);
800
+ const changedProductionPaths = [...episode.implementationPaths, ...episode.protocolPaths];
801
+ const owningPackageIds = new Set(map.packages
802
+ .filter((entry) => changedProductionPaths.some((changedPath) => entry.path === "." ||
803
+ changedPath === entry.path ||
804
+ changedPath.startsWith(`${entry.path}/`)))
805
+ .map((entry) => entry.id));
806
+ const publicSurfaceEvidence = map.publicSurfaces
807
+ .filter((surface) => surface.packageId !== undefined &&
808
+ owningPackageIds.has(surface.packageId) &&
809
+ !episode.testPaths.includes(surface.path))
810
+ .map((surface) => ({
811
+ kind: "public-surface",
812
+ id: surface.id,
813
+ path: surface.path
814
+ }));
815
+ const targetEvidence = [
816
+ ...episode.implementationPaths.map((path) => ({
817
+ kind: "symbol",
818
+ id: `path-${path}`,
819
+ path
820
+ })),
821
+ ...episode.protocolPaths.map((path) => ({
822
+ kind: "protocol",
823
+ id: `path-${path}`,
824
+ path
825
+ })),
826
+ ...episode.testPaths.map((path) => ({
827
+ kind: "test",
828
+ id: map.tests.find((test) => test.path === path)?.id ?? `path-${path}`,
829
+ path
830
+ })),
831
+ ...publicSurfaceEvidence
832
+ ];
833
+ const seed = {
834
+ version: 1,
835
+ id: `seed-${episode.commit}`,
836
+ status: "candidate",
837
+ source: {
838
+ kind: historicalSourceKind(episode.subject),
839
+ changeEpisodeId: episode.id
840
+ },
841
+ summary: episode.subject,
842
+ capabilityEvidence: [{ kind: "history-episode", id: episode.id }, ...targetEvidence],
843
+ initialState: { commit: episode.parentCommit },
844
+ targetBehavior: [
845
+ {
846
+ id: "historical-regression",
847
+ description: episode.subject.trim().length > 0
848
+ ? episode.subject
849
+ : "Preserve the behavior established by the historical change.",
850
+ critical: true,
851
+ evidence: targetEvidence
852
+ }
853
+ ],
854
+ referenceCommit: episode.commit,
855
+ baselineObservations: [],
856
+ preChangeObservations: [],
857
+ postChangeObservations: [],
858
+ candidateTestIds: testIds,
859
+ environment,
860
+ confidence: testIds.length > 0 ? "medium" : "review-required",
861
+ risks: ["review-required", ...(testIds.length > 0 ? [] : ["weak-test-delta"])],
862
+ rejectionReasons: []
863
+ };
864
+ // Source drafts intentionally have no selected baseline environment yet.
865
+ // Only legacy/executable seeds satisfy the stronger environment assertions.
866
+ if (options?.deferEnvironment !== true)
867
+ assertRepositoryTaskSeedV1(seed);
868
+ seeds.push(seed);
869
+ }
870
+ if (seeds.length === 0 && map.historyEpisodes.length === 0) {
871
+ return yield* failure("repository contains no historical episodes");
872
+ }
873
+ return seeds;
874
+ });
875
+ export const qualifyHistoricalTaskSeedV1 = Effect.fn("TaskSeed.qualifyHistorical")(function* (input) {
876
+ if ((input.seed.source.kind !== "historical-fix" &&
877
+ input.seed.source.kind !== "historical-feature" &&
878
+ input.seed.source.kind !== "historical-refactor") ||
879
+ input.seed.referenceCommit === undefined) {
880
+ return yield* failure("historical qualification requires a historical change seed with a reference commit");
881
+ }
882
+ const { hiddenTestOverlays, hiddenContentOverlays } = yield* hiddenQualificationOverlays(input.seed, input.hiddenFixtureSuite);
883
+ const operationalFailures = [];
884
+ const replayErrors = [];
885
+ const replayPhase = Effect.fnUntraced(function* (phase, replayInput) {
886
+ // Already-started phases finish normally. Only queued phases are skipped,
887
+ // so their sibling's failure cannot discard replay rows or cleanup work.
888
+ if (operationalFailures.length > 0 || replayErrors.length > 0)
889
+ return undefined;
890
+ const startedAt = yield* Clock.currentTimeMillis;
891
+ const replay = yield* replayRepositoryCommandsV1(replayInput).pipe(Effect.match({
892
+ onFailure: (error) => ({ results: [], error }),
893
+ onSuccess: (results) => ({ results, error: undefined })
894
+ }));
895
+ if (replay.error !== undefined)
896
+ replayErrors.push(replay.error);
897
+ for (const result of replay.results) {
898
+ if (qualificationExecutionBlocked(result)) {
899
+ const diagnostic = sanitizeRepositoryCommandDiagnosticV1(result.stdout, result.stderr, result.outputTruncated);
900
+ const failure = {
901
+ phase,
902
+ recipeId: result.recipeId,
903
+ repetition: result.repetition,
904
+ stage: result.stage,
905
+ exitCode: result.exitCode,
906
+ timedOut: result.timedOut,
907
+ evidenceDetail: repositoryCommandTestEvidenceV1(result, result.recipeId).detail,
908
+ ...(diagnostic === undefined
909
+ ? {}
910
+ : {
911
+ commandDiagnostic: diagnostic.text,
912
+ diagnosticTruncated: diagnostic.truncated
913
+ })
914
+ };
915
+ operationalFailures.push(failure);
916
+ }
917
+ }
918
+ // The scoped replay has completed its finalizers before these unchanged
919
+ // rows are recorded, so isolation timings include checkout cleanup.
920
+ const wallMs = (yield* Clock.currentTimeMillis) - startedAt;
921
+ const batch = input.diagnosticBatch ?? 1;
922
+ const report = {
923
+ diagnosticOnly: true,
924
+ admissionEvidence: false,
925
+ seedId: input.seed.id,
926
+ referenceCommit: input.seed.referenceCommit,
927
+ batch,
928
+ phase,
929
+ wallMs,
930
+ ...replay
931
+ };
932
+ yield* reportFoundryAuthoringArtifactV1({
933
+ version: 1,
934
+ operationId: `seed-qualification:${input.seed.id}:${input.seed.referenceCommit}:batch-${batch}:${phase}`,
935
+ role: "seed-qualifier",
936
+ model: "local-replay",
937
+ reasoningEffort: "none",
938
+ schemaName: "routekit_repository_seed_qualification_phase_v1",
939
+ validation: "validated",
940
+ request: {
941
+ instructions: "Private qualification phase diagnostics; no model request or independent admission evidence.",
942
+ input: replayInput,
943
+ jsonSchema: { type: "object", properties: {}, additionalProperties: false },
944
+ maximumOutputTokens: 0
945
+ },
946
+ response: { text: JSON.stringify(report) },
947
+ value: report
948
+ });
949
+ yield* reportFoundryHeartbeatV1({
950
+ role: "seed-qualifier",
951
+ detail: `qualification phase complete; batch=${batch}; phase=${phase}; results=${replay.results.length}; wallMs=${wallMs}`
952
+ });
953
+ return replay.results;
954
+ });
955
+ // Repository validation commands can be full production builds. A standard
956
+ // sandbox has enough memory for one such build, but not two independent
957
+ // phase replays at once. Keep phases sequential within each already
958
+ // sequential batch; campaign-level sandbox concurrency remains unchanged.
959
+ const [baseline, pre, post] = yield* Effect.all([
960
+ replayPhase("baseline", {
961
+ repositoryRoot: input.repositoryRoot,
962
+ commit: input.seed.initialState.commit,
963
+ recipes: input.seed.environment.baselineGradeRecipes,
964
+ trustedPreparationRecipes: input.seed.environment.trustedPreparationRecipes,
965
+ candidateValidationRecipes: input.seed.environment.candidateValidationRecipes,
966
+ ...(input.preparationCache === undefined
967
+ ? {}
968
+ : { preparationCache: input.preparationCache }),
969
+ ...(input.repetitions === undefined ? {} : { repetitions: input.repetitions })
970
+ }),
971
+ replayPhase("pre-change", {
972
+ repositoryRoot: input.repositoryRoot,
973
+ commit: input.seed.initialState.commit,
974
+ recipes: input.seed.environment.gradeRecipes,
975
+ trustedPreparationRecipes: input.seed.environment.trustedPreparationRecipes,
976
+ candidateValidationRecipes: input.seed.environment.candidateValidationRecipes,
977
+ overlays: hiddenTestOverlays,
978
+ contentOverlays: hiddenContentOverlays,
979
+ ...(input.preparationCache === undefined
980
+ ? {}
981
+ : { preparationCache: input.preparationCache }),
982
+ ...(input.repetitions === undefined ? {} : { repetitions: input.repetitions })
983
+ }),
984
+ replayPhase("post-change", {
985
+ repositoryRoot: input.repositoryRoot,
986
+ commit: input.seed.referenceCommit,
987
+ recipes: input.seed.environment.gradeRecipes,
988
+ trustedPreparationRecipes: input.seed.environment.trustedPreparationRecipes,
989
+ candidateValidationRecipes: input.seed.environment.candidateValidationRecipes,
990
+ overlays: hiddenTestOverlays,
991
+ contentOverlays: hiddenContentOverlays,
992
+ ...(input.preparationCache === undefined
993
+ ? {}
994
+ : { preparationCache: input.preparationCache }),
995
+ ...(input.repetitions === undefined ? {} : { repetitions: input.repetitions })
996
+ })
997
+ ], { concurrency: 1 });
998
+ // Throw only after Effect.all joins every started phase and its diagnostic
999
+ // recorder. A partial run must never be presented as completed qualification.
1000
+ if (operationalFailures.length > 0) {
1001
+ const reasons = [
1002
+ ...new Set(operationalFailures.map((entry) => `phase=${entry.phase}; stage=${entry.stage}; recipe=${entry.recipeId}; exit=${String(entry.exitCode)}; timeout=${String(entry.timedOut)}${entry.commandDiagnostic === undefined
1003
+ ? ""
1004
+ : `; diagnostic=${encodeRepositoryCommandDiagnosticV1(entry.commandDiagnostic)}; diagnosticTruncated=${String(entry.diagnosticTruncated ?? false)}`}; evidence=${entry.evidenceDetail}`))
1005
+ ];
1006
+ const detail = `historical qualification stopped after operational failure prevented grading: ${reasons.join(" | ")}; unstarted phases skipped=${[baseline, pre, post].filter((results) => results === undefined).length}; later batches were not started; qualification requires executable reviewed tests on both revisions and attributable pre-change assertion failures`;
1007
+ yield* reportFoundryHeartbeatV1({ role: "seed-qualifier", detail });
1008
+ return yield* new RepositoryFoundryError({
1009
+ operation: "extract-task-seeds",
1010
+ classification: "environment_blocked",
1011
+ detail,
1012
+ cause: { operationalFailures, ...(replayErrors.length === 0 ? {} : { replayErrors }) }
1013
+ });
1014
+ }
1015
+ if (replayErrors.length > 0)
1016
+ return yield* replayErrors[0];
1017
+ if (baseline === undefined || pre === undefined || post === undefined) {
1018
+ return yield* failure("historical qualification stopped before all required phases completed");
1019
+ }
1020
+ const resultSignature = (result) => [
1021
+ result.recipeId,
1022
+ result.stage,
1023
+ String(result.exitCode),
1024
+ String(result.timedOut),
1025
+ String(result.gradeExecuted),
1026
+ repositoryCommandTestEvidenceV1(result, result.recipeId).outcome
1027
+ ].join(":");
1028
+ const stableByRecipe = (results, recipeId) => new Set(results.filter((result) => result.recipeId === recipeId).map(resultSignature))
1029
+ .size === 1;
1030
+ const allBaselineGradeRecipeIds = input.seed.environment.baselineGradeRecipes.map((recipe) => recipe.id);
1031
+ const allGradeRecipeIds = input.seed.environment.gradeRecipes.map((recipe) => recipe.id);
1032
+ const baselineStable = allBaselineGradeRecipeIds.every((recipeId) => stableByRecipe(baseline, recipeId));
1033
+ const postStable = allGradeRecipeIds.every((recipeId) => stableByRecipe(post, recipeId));
1034
+ const prePrepared = pre.every((result) => result.stage !== "trusted-preparation" ||
1035
+ (result.gradeExecuted && result.exitCode === 0 && !result.timedOut));
1036
+ const postPrepared = post.every((result) => result.stage !== "trusted-preparation" ||
1037
+ (result.gradeExecuted && result.exitCode === 0 && !result.timedOut));
1038
+ const everyRecipeHasOutcome = (results, recipeIds, outcome) => recipeIds.every((recipeId) => {
1039
+ const observations = results.filter((result) => result.recipeId === recipeId);
1040
+ return (observations.length > 0 &&
1041
+ observations.every((result) => repositoryCommandTestEvidenceV1(result, recipeId).outcome === outcome));
1042
+ });
1043
+ const baselinePasses = everyRecipeHasOutcome(baseline, allBaselineGradeRecipeIds, "pass");
1044
+ const postPasses = everyRecipeHasOutcome(post, allGradeRecipeIds, "pass");
1045
+ const preStable = allGradeRecipeIds.every((recipeId) => stableByRecipe(pre, recipeId));
1046
+ const preFails = everyRecipeHasOutcome(pre, allGradeRecipeIds, "fail");
1047
+ const preInconclusive = pre.some((result) => repositoryCommandTestEvidenceV1(result, result.recipeId).outcome === "unknown");
1048
+ const rejectionReasons = [
1049
+ ...(baselineStable ? [] : ["one or more initial baseline commands are unstable"]),
1050
+ ...(baselinePasses
1051
+ ? []
1052
+ : ["initial baseline does not pass before reviewed hidden evidence is overlaid"]),
1053
+ ...(prePrepared ? [] : ["pre-change trusted preparation failed"]),
1054
+ ...(postPrepared ? [] : ["post-change trusted preparation failed"]),
1055
+ ...(preStable ? [] : ["one or more pre-change commands are unstable"]),
1056
+ ...(postStable ? [] : ["one or more post-change commands are unstable"]),
1057
+ ...(preFails ? [] : ["every reviewed grade command must reproduce the pre-change failure"]),
1058
+ ...(preInconclusive
1059
+ ? [
1060
+ "pre-change grading is inconclusive; setup, compilation, runtime, and discovery failures do not establish a behavioral regression"
1061
+ ]
1062
+ : []),
1063
+ ...(postPasses ? [] : ["one or more post-change commands do not pass"])
1064
+ ];
1065
+ const observation = (phase, results) => results.flatMap((result) => {
1066
+ const diagnostic = result.exitCode === 0 && !result.timedOut
1067
+ ? undefined
1068
+ : sanitizeRepositoryCommandDiagnosticV1(result.stdout, result.stderr, result.outputTruncated)?.text;
1069
+ const expectedRecipeIds = phase === "baseline" ? allBaselineGradeRecipeIds : allGradeRecipeIds;
1070
+ const projectedRecipeIds = result.gradeExecuted ? [result.recipeId] : expectedRecipeIds;
1071
+ return projectedRecipeIds.map((recipeId) => {
1072
+ const evidence = repositoryCommandTestEvidenceV1(result, recipeId);
1073
+ return {
1074
+ id: `${phase}-${recipeId}-${String(result.repetition)}`,
1075
+ kind: "test-command",
1076
+ subjectId: recipeId,
1077
+ outcome: evidence.outcome,
1078
+ detail: [
1079
+ `exit=${String(result.exitCode)}`,
1080
+ `stage=${result.stage}`,
1081
+ `gradeExecuted=${String(result.gradeExecuted)}`,
1082
+ `evidence=${evidence.detail}`,
1083
+ `timeout=${String(result.timedOut)}`,
1084
+ `durationMs=${String(result.durationMs)}`,
1085
+ ...(result.gradeExecuted ? [] : [`terminalRecipe=${result.recipeId}`]),
1086
+ ...result.trustedPreparationResults.map((preparation) => `trustedPreparation=${preparation.recipeId}:exit=${String(preparation.exitCode)}:timeout=${String(preparation.timedOut)}`),
1087
+ ...result.candidateValidationResults.map((validation) => `candidateValidation=${validation.recipeId}:exit=${String(validation.exitCode)}:timeout=${String(validation.timedOut)}`),
1088
+ ...(result.outputTruncated ? ["output=truncated"] : []),
1089
+ ...(diagnostic === undefined ? [] : [`diagnostic=${JSON.stringify(diagnostic)}`])
1090
+ ].join("; ")
1091
+ };
1092
+ });
1093
+ });
1094
+ const qualified = {
1095
+ ...input.seed,
1096
+ status: rejectionReasons.length === 0 ? "qualified" : "rejected",
1097
+ baselineObservations: observation("baseline", baseline),
1098
+ preChangeObservations: observation("pre", pre),
1099
+ postChangeObservations: observation("post", post),
1100
+ confidence: rejectionReasons.length === 0 ? "high" : "review-required",
1101
+ risks: rejectionReasons.length === 0
1102
+ ? input.seed.risks.filter((risk) => risk !== "review-required")
1103
+ : [...new Set([...input.seed.risks, "review-required"])],
1104
+ rejectionReasons
1105
+ };
1106
+ assertRepositoryTaskSeedV1(qualified);
1107
+ return qualified;
1108
+ });
1109
+ const observationSignature = (observation) => {
1110
+ const detail = observation.detail;
1111
+ const value = (name) => new RegExp(`(?:^|; )${name}=([^;]+)`, "u").exec(detail)?.[1] ?? "unknown";
1112
+ return [
1113
+ observation.subjectId,
1114
+ observation.outcome,
1115
+ `stage=${value("stage")}`,
1116
+ `exit=${value("exit")}`,
1117
+ `gradeExecuted=${value("gradeExecuted")}`,
1118
+ `timeout=${value("timeout")}`
1119
+ ].join(":");
1120
+ };
1121
+ const phaseObservations = (seed, phase) => phase === "baseline"
1122
+ ? seed.baselineObservations
1123
+ : phase === "pre-change"
1124
+ ? seed.preChangeObservations
1125
+ : seed.postChangeObservations;
1126
+ const qualificationPhases = [
1127
+ "baseline",
1128
+ "pre-change",
1129
+ "post-change"
1130
+ ];
1131
+ const assembleCrossBatchQualificationV1 = (input) => {
1132
+ const { qualifiedBatches, batchesRequired, repetitionsPerBatch } = input;
1133
+ const batches = qualifiedBatches.map((seed, index) => ({
1134
+ batch: index + 1,
1135
+ status: seed.status === "qualified" ? "qualified" : "rejected",
1136
+ phaseSignatures: qualificationPhases.map((phase) => ({
1137
+ phase,
1138
+ signatures: [...new Set(phaseObservations(seed, phase).map(observationSignature))].sort()
1139
+ })),
1140
+ rejectionReasons: [...seed.rejectionReasons]
1141
+ }));
1142
+ const divergentPhases = qualificationPhases.filter((phase) => {
1143
+ const signatures = batches.map((batch) => JSON.stringify(batch.phaseSignatures.find((entry) => entry.phase === phase)?.signatures ?? []));
1144
+ return new Set(signatures).size > 1;
1145
+ });
1146
+ const failureSignatureCounts = new Map();
1147
+ for (const seed of qualifiedBatches) {
1148
+ for (const phase of qualificationPhases) {
1149
+ for (const observation of phaseObservations(seed, phase)) {
1150
+ if (observation.outcome !== "fail")
1151
+ continue;
1152
+ const signature = `${phase}:${observationSignature(observation)}`;
1153
+ failureSignatureCounts.set(signature, (failureSignatureCounts.get(signature) ?? 0) + 1);
1154
+ }
1155
+ }
1156
+ }
1157
+ const rejectionReasons = [
1158
+ ...qualifiedBatches.flatMap((seed, index) => seed.rejectionReasons.map((reason) => `batch ${String(index + 1)}: ${reason}`)),
1159
+ ...divergentPhases.map((phase) => `cross-batch qualification diverged for ${phase}`)
1160
+ ];
1161
+ const stable = rejectionReasons.length === 0;
1162
+ const observations = (select) => qualifiedBatches.flatMap((seed, index) => select(seed).map((observation) => ({
1163
+ ...observation,
1164
+ id: `batch-${String(index + 1)}-${observation.id}`
1165
+ })));
1166
+ const seed = {
1167
+ ...qualifiedBatches.at(-1),
1168
+ status: stable ? "qualified" : "rejected",
1169
+ baselineObservations: observations((qualified) => qualified.baselineObservations),
1170
+ preChangeObservations: observations((qualified) => qualified.preChangeObservations),
1171
+ postChangeObservations: observations((qualified) => qualified.postChangeObservations),
1172
+ confidence: stable ? "high" : "review-required",
1173
+ risks: stable
1174
+ ? input.sourceSeed.risks.filter((risk) => risk !== "review-required")
1175
+ : [
1176
+ ...new Set([
1177
+ ...input.sourceSeed.risks,
1178
+ "cross-batch-instability",
1179
+ "review-required"
1180
+ ])
1181
+ ],
1182
+ rejectionReasons
1183
+ };
1184
+ const qualification = {
1185
+ seed,
1186
+ stability: {
1187
+ version: 1,
1188
+ seedId: seed.id,
1189
+ batchesRequired,
1190
+ repetitionsPerBatch,
1191
+ stable,
1192
+ batches,
1193
+ divergentPhases,
1194
+ failureSignatures: [...failureSignatureCounts]
1195
+ .sort((left, right) => left[0].localeCompare(right[0]))
1196
+ .map(([signature, count]) => ({ signature, count })),
1197
+ rejectionReasons
1198
+ }
1199
+ };
1200
+ assertRepositoryTaskSeedV1(seed);
1201
+ assertRepositorySeedCrossBatchQualificationV1(qualification);
1202
+ return qualification;
1203
+ };
1204
+ export const qualifyHistoricalTaskSeedAcrossBatchesV1 = Effect.fn("TaskSeed.qualifyHistoricalAcrossBatches")(function* (input) {
1205
+ const batchesRequired = Math.max(2, Math.min(input.batches ?? 2, 5));
1206
+ const repetitionsPerBatch = Math.max(2, Math.min(input.repetitionsPerBatch ?? 2, 5));
1207
+ const qualifiedBatches = yield* Effect.scoped(Effect.gen(function* () {
1208
+ const preparationCache = yield* makeRepositoryPreparationCacheV1({
1209
+ repositoryRoot: input.repositoryRoot,
1210
+ commits: [
1211
+ input.seed.initialState.commit,
1212
+ ...(input.seed.referenceCommit === undefined ? [] : [input.seed.referenceCommit])
1213
+ ],
1214
+ trustedPreparationRecipes: input.seed.environment.trustedPreparationRecipes
1215
+ });
1216
+ const qualified = [];
1217
+ for (let batch = 1; batch <= batchesRequired; batch += 1) {
1218
+ qualified.push(yield* qualifyHistoricalTaskSeedV1({
1219
+ repositoryRoot: input.repositoryRoot,
1220
+ seed: input.seed,
1221
+ ...(input.hiddenFixtureSuite === undefined
1222
+ ? {}
1223
+ : { hiddenFixtureSuite: input.hiddenFixtureSuite }),
1224
+ repetitions: repetitionsPerBatch,
1225
+ diagnosticBatch: batch,
1226
+ preparationCache
1227
+ }));
1228
+ }
1229
+ return qualified;
1230
+ }));
1231
+ return assembleCrossBatchQualificationV1({
1232
+ sourceSeed: input.seed,
1233
+ qualifiedBatches,
1234
+ batchesRequired,
1235
+ repetitionsPerBatch
1236
+ });
1237
+ });
1238
+ const qualificationUnitKey = (unit) => `${String(unit.batch)}:${unit.phase}:${String(unit.repetition)}`;
1239
+ export const repositorySeedQualificationProgressSummaryV1 = (progress) => ({
1240
+ completedBatches: progress.completedBatches.length,
1241
+ batchesRequired: progress.batchesRequired,
1242
+ completedUnits: progress.activeUnits.length,
1243
+ unitsPerBatch: qualificationPhases.length * progress.repetitionsPerBatch
1244
+ });
1245
+ /**
1246
+ * Resume reference qualification only at completed replay boundaries. Every
1247
+ * started replay still owns and closes its checkout scope; interruption may
1248
+ * repeat the in-flight replay, but never a previously checkpointed repetition.
1249
+ */
1250
+ export const qualifyHistoricalTaskSeedAcrossBatchesResumableV1 = Effect.fn("TaskSeed.qualifyHistoricalAcrossBatchesResumable")(function* (input) {
1251
+ const batchesRequired = Math.max(2, Math.min(input.batches ?? 2, 5));
1252
+ const repetitionsPerBatch = Math.max(2, Math.min(input.repetitionsPerBatch ?? 2, 5));
1253
+ if ((input.seed.source.kind !== "historical-fix" &&
1254
+ input.seed.source.kind !== "historical-feature" &&
1255
+ input.seed.source.kind !== "historical-refactor") ||
1256
+ input.seed.referenceCommit === undefined) {
1257
+ return yield* failure("historical qualification requires a historical change seed with a reference commit");
1258
+ }
1259
+ const emptyProgress = () => ({
1260
+ version: 1,
1261
+ bindingDigest: input.bindingDigest,
1262
+ batchesRequired,
1263
+ repetitionsPerBatch,
1264
+ completedBatches: [],
1265
+ activeUnits: []
1266
+ });
1267
+ let progress = input.progress?.bindingDigest === input.bindingDigest &&
1268
+ input.progress.batchesRequired === batchesRequired &&
1269
+ input.progress.repetitionsPerBatch === repetitionsPerBatch
1270
+ ? input.progress
1271
+ : emptyProgress();
1272
+ const expectedCompletedBatches = progress.completedBatches.map((_, index) => index + 1);
1273
+ const activeBatch = progress.completedBatches.length + 1;
1274
+ const activeKeys = progress.activeUnits.map(qualificationUnitKey);
1275
+ if (progress.completedBatches.length > batchesRequired ||
1276
+ progress.completedBatches.some((entry, index) => entry.batch !== expectedCompletedBatches[index] ||
1277
+ (entry.seed.status !== "qualified" && entry.seed.status !== "rejected")) ||
1278
+ progress.activeUnits.some((unit) => unit.batch !== activeBatch ||
1279
+ unit.repetition < 1 ||
1280
+ unit.repetition > repetitionsPerBatch ||
1281
+ unit.results.some((result) => result.repetition !== unit.repetition)) ||
1282
+ new Set(activeKeys).size !== activeKeys.length) {
1283
+ return yield* failure("retained historical qualification progress violates its batch, phase, or repetition binding");
1284
+ }
1285
+ // Completed batches are persisted verdicts, not permission to bypass the
1286
+ // current evidence contract. Reject legacy compile-only qualifications before
1287
+ // any preparation or queued replay can run.
1288
+ for (const entry of progress.completedBatches) {
1289
+ yield* Effect.try({
1290
+ try: () => assertRepositoryTaskSeedV1(entry.seed),
1291
+ catch: (cause) => new RepositoryFoundryError({
1292
+ operation: "extract-task-seeds",
1293
+ classification: "environment_blocked",
1294
+ detail: "retained qualification does not satisfy the executed behavioral evidence contract; select another candidate or repair the grading environment",
1295
+ cause
1296
+ })
1297
+ });
1298
+ }
1299
+ // An incomplete or unattributable grade is retained diagnostic evidence, not useful
1300
+ // forward execution. Reopening the same binding must report it without
1301
+ // replaying it or spending on queued phases.
1302
+ for (const unit of progress.activeUnits) {
1303
+ const blocked = unit.results.find((result) => qualificationExecutionBlocked(result));
1304
+ if (blocked !== undefined)
1305
+ return yield* qualificationExecutionFailure(unit.phase, blocked);
1306
+ }
1307
+ const { hiddenTestOverlays, hiddenContentOverlays } = yield* hiddenQualificationOverlays(input.seed, input.hiddenFixtureSuite);
1308
+ const qualification = yield* Effect.scoped(Effect.gen(function* () {
1309
+ const preparationCache = yield* makeRepositoryPreparationCacheV1({
1310
+ repositoryRoot: input.repositoryRoot,
1311
+ commits: [input.seed.initialState.commit, input.seed.referenceCommit],
1312
+ trustedPreparationRecipes: input.seed.environment.trustedPreparationRecipes
1313
+ });
1314
+ for (let batch = progress.completedBatches.length + 1; batch <= batchesRequired; batch += 1) {
1315
+ for (const phase of qualificationPhases) {
1316
+ for (let repetition = 1; repetition <= repetitionsPerBatch; repetition += 1) {
1317
+ const key = qualificationUnitKey({ batch, phase, repetition });
1318
+ if (progress.activeUnits.some((unit) => qualificationUnitKey(unit) === key))
1319
+ continue;
1320
+ const common = {
1321
+ repositoryRoot: input.repositoryRoot,
1322
+ trustedPreparationRecipes: input.seed.environment.trustedPreparationRecipes,
1323
+ candidateValidationRecipes: input.seed.environment.candidateValidationRecipes,
1324
+ preparationCache,
1325
+ repetitions: 1,
1326
+ repetitionStart: repetition
1327
+ };
1328
+ const replayInput = phase === "baseline"
1329
+ ? {
1330
+ ...common,
1331
+ commit: input.seed.initialState.commit,
1332
+ recipes: input.seed.environment.baselineGradeRecipes
1333
+ }
1334
+ : {
1335
+ ...common,
1336
+ commit: phase === "pre-change"
1337
+ ? input.seed.initialState.commit
1338
+ : input.seed.referenceCommit,
1339
+ recipes: input.seed.environment.gradeRecipes,
1340
+ overlays: hiddenTestOverlays,
1341
+ contentOverlays: hiddenContentOverlays
1342
+ };
1343
+ const results = yield* replayRepositoryCommandsV1(replayInput);
1344
+ progress = {
1345
+ ...progress,
1346
+ activeUnits: [...progress.activeUnits, { batch, phase, repetition, results }]
1347
+ };
1348
+ yield* input.checkpoint(progress);
1349
+ const operational = results.find((result) => qualificationExecutionBlocked(result));
1350
+ if (operational !== undefined)
1351
+ return yield* qualificationExecutionFailure(phase, operational);
1352
+ yield* reportFoundryHeartbeatV1({
1353
+ role: "seed-qualifier",
1354
+ detail: `qualification replay checkpointed; batch=${String(batch)}/${String(batchesRequired)}; phase=${phase}; repetition=${String(repetition)}/${String(repetitionsPerBatch)}`
1355
+ });
1356
+ }
1357
+ }
1358
+ const phaseResults = qualificationPhases.map((phase) => progress.activeUnits
1359
+ .filter((unit) => unit.batch === batch && unit.phase === phase)
1360
+ .sort((left, right) => left.repetition - right.repetition)
1361
+ .flatMap((unit) => unit.results));
1362
+ let cursor = 0;
1363
+ const reconstruction = {
1364
+ commands: () => {
1365
+ const results = phaseResults[cursor++];
1366
+ return results === undefined
1367
+ ? Effect.fail(failure("retained qualification phase evidence was exhausted early"))
1368
+ : Effect.succeed(results);
1369
+ },
1370
+ oracle: () => Effect.fail(failure("seed qualification reconstruction cannot supply oracle evidence")),
1371
+ assertConsumed: Effect.suspend(() => cursor === phaseResults.length
1372
+ ? Effect.void
1373
+ : Effect.fail(failure("retained qualification phase evidence was not fully consumed")))
1374
+ };
1375
+ const seed = yield* qualifyHistoricalTaskSeedV1({
1376
+ repositoryRoot: input.repositoryRoot,
1377
+ seed: input.seed,
1378
+ ...(input.hiddenFixtureSuite === undefined
1379
+ ? {}
1380
+ : { hiddenFixtureSuite: input.hiddenFixtureSuite }),
1381
+ repetitions: repetitionsPerBatch,
1382
+ diagnosticBatch: batch
1383
+ }).pipe(Effect.provideService(RepositoryFoundryEvidenceReconstruction, reconstruction));
1384
+ yield* reconstruction.assertConsumed;
1385
+ progress = {
1386
+ ...progress,
1387
+ completedBatches: [...progress.completedBatches, { batch, seed }],
1388
+ activeUnits: []
1389
+ };
1390
+ yield* input.checkpoint(progress);
1391
+ }
1392
+ return assembleCrossBatchQualificationV1({
1393
+ sourceSeed: input.seed,
1394
+ qualifiedBatches: progress.completedBatches.map((entry) => entry.seed),
1395
+ batchesRequired,
1396
+ repetitionsPerBatch
1397
+ });
1398
+ }));
1399
+ return { qualification, progress };
1400
+ });
1401
+ export const makeTaskSeed = Effect.succeed(TaskSeed.of({
1402
+ extractHistorical: extractHistoricalTaskSeedsV1,
1403
+ qualifyHistorical: qualifyHistoricalTaskSeedV1,
1404
+ qualifyHistoricalAcrossBatches: qualifyHistoricalTaskSeedAcrossBatchesV1
1405
+ }));
1406
+ export const TaskSeedLive = Layer.effect(TaskSeed, makeTaskSeed);