onto-mcp 0.3.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 (447) hide show
  1. package/.onto/authority/core-lens-registry.yaml +134 -0
  2. package/.onto/authority/core-lexicon.yaml +1562 -0
  3. package/.onto/authority/diagnostic-codes.yaml +94 -0
  4. package/.onto/domains/accounting/competency_qs.md +384 -0
  5. package/.onto/domains/accounting/concepts.md +186 -0
  6. package/.onto/domains/accounting/conciseness_rules.md +160 -0
  7. package/.onto/domains/accounting/dependency_rules.md +239 -0
  8. package/.onto/domains/accounting/domain_scope.md +213 -0
  9. package/.onto/domains/accounting/extension_cases.md +416 -0
  10. package/.onto/domains/accounting/logic_rules.md +226 -0
  11. package/.onto/domains/accounting/structure_spec.md +298 -0
  12. package/.onto/domains/accounting-kr/competency_qs.md +562 -0
  13. package/.onto/domains/accounting-kr/concepts.md +187 -0
  14. package/.onto/domains/accounting-kr/conciseness_rules.md +125 -0
  15. package/.onto/domains/accounting-kr/dependency_rules.md +93 -0
  16. package/.onto/domains/accounting-kr/domain_scope.md +140 -0
  17. package/.onto/domains/accounting-kr/extension_cases.md +343 -0
  18. package/.onto/domains/accounting-kr/logic_rules.md +160 -0
  19. package/.onto/domains/accounting-kr/structure_spec.md +85 -0
  20. package/.onto/domains/business/competency_qs.md +263 -0
  21. package/.onto/domains/business/concepts.md +200 -0
  22. package/.onto/domains/business/conciseness_rules.md +135 -0
  23. package/.onto/domains/business/dependency_rules.md +113 -0
  24. package/.onto/domains/business/domain_scope.md +240 -0
  25. package/.onto/domains/business/extension_cases.md +249 -0
  26. package/.onto/domains/business/logic_rules.md +134 -0
  27. package/.onto/domains/business/structure_spec.md +114 -0
  28. package/.onto/domains/finance/competency_qs.md +362 -0
  29. package/.onto/domains/finance/concepts.md +194 -0
  30. package/.onto/domains/finance/conciseness_rules.md +155 -0
  31. package/.onto/domains/finance/dependency_rules.md +171 -0
  32. package/.onto/domains/finance/domain_scope.md +215 -0
  33. package/.onto/domains/finance/extension_cases.md +350 -0
  34. package/.onto/domains/finance/logic_rules.md +191 -0
  35. package/.onto/domains/finance/structure_spec.md +182 -0
  36. package/.onto/domains/llm-native-development/competency_qs.md +430 -0
  37. package/.onto/domains/llm-native-development/concepts.md +242 -0
  38. package/.onto/domains/llm-native-development/conciseness_rules.md +163 -0
  39. package/.onto/domains/llm-native-development/dependency_rules.md +216 -0
  40. package/.onto/domains/llm-native-development/domain_scope.md +197 -0
  41. package/.onto/domains/llm-native-development/extension_cases.md +474 -0
  42. package/.onto/domains/llm-native-development/logic_rules.md +123 -0
  43. package/.onto/domains/llm-native-development/prompt_interface.md +49 -0
  44. package/.onto/domains/llm-native-development/structure_spec.md +245 -0
  45. package/.onto/domains/market-intelligence/competency_qs.md +274 -0
  46. package/.onto/domains/market-intelligence/concepts.md +233 -0
  47. package/.onto/domains/market-intelligence/conciseness_rules.md +165 -0
  48. package/.onto/domains/market-intelligence/dependency_rules.md +197 -0
  49. package/.onto/domains/market-intelligence/domain_scope.md +231 -0
  50. package/.onto/domains/market-intelligence/extension_cases.md +425 -0
  51. package/.onto/domains/market-intelligence/logic_rules.md +247 -0
  52. package/.onto/domains/market-intelligence/structure_spec.md +209 -0
  53. package/.onto/domains/ontology/competency_qs.md +394 -0
  54. package/.onto/domains/ontology/concepts.md +172 -0
  55. package/.onto/domains/ontology/conciseness_rules.md +134 -0
  56. package/.onto/domains/ontology/dependency_rules.md +125 -0
  57. package/.onto/domains/ontology/domain_scope.md +114 -0
  58. package/.onto/domains/ontology/extension_cases.md +501 -0
  59. package/.onto/domains/ontology/logic_rules.md +114 -0
  60. package/.onto/domains/ontology/problem_framing_profile.md +67 -0
  61. package/.onto/domains/ontology/structure_spec.md +115 -0
  62. package/.onto/domains/palantir-foundry/RESEARCH_NOTES.md +911 -0
  63. package/.onto/domains/palantir-foundry/competency_qs.md +191 -0
  64. package/.onto/domains/palantir-foundry/competitive_comparison.md +329 -0
  65. package/.onto/domains/palantir-foundry/concepts.md +197 -0
  66. package/.onto/domains/palantir-foundry/conciseness_rules.md +245 -0
  67. package/.onto/domains/palantir-foundry/dependency_rules.md +135 -0
  68. package/.onto/domains/palantir-foundry/domain_scope.md +395 -0
  69. package/.onto/domains/palantir-foundry/extension_cases.md +210 -0
  70. package/.onto/domains/palantir-foundry/logic_rules.md +172 -0
  71. package/.onto/domains/palantir-foundry/structure_spec.md +291 -0
  72. package/.onto/domains/software-engineering/competency_qs.md +538 -0
  73. package/.onto/domains/software-engineering/concepts.md +238 -0
  74. package/.onto/domains/software-engineering/conciseness_rules.md +167 -0
  75. package/.onto/domains/software-engineering/dependency_rules.md +216 -0
  76. package/.onto/domains/software-engineering/domain_scope.md +183 -0
  77. package/.onto/domains/software-engineering/extension_cases.md +551 -0
  78. package/.onto/domains/software-engineering/logic_rules.md +240 -0
  79. package/.onto/domains/software-engineering/problem_framing_profile.md +68 -0
  80. package/.onto/domains/software-engineering/structure_spec.md +185 -0
  81. package/.onto/domains/ui-design/competency_qs.md +567 -0
  82. package/.onto/domains/ui-design/concepts.md +194 -0
  83. package/.onto/domains/ui-design/conciseness_rules.md +190 -0
  84. package/.onto/domains/ui-design/dependency_rules.md +323 -0
  85. package/.onto/domains/ui-design/domain_scope.md +340 -0
  86. package/.onto/domains/ui-design/extension_cases.md +563 -0
  87. package/.onto/domains/ui-design/logic_rules.md +349 -0
  88. package/.onto/domains/ui-design/structure_spec.md +252 -0
  89. package/.onto/domains/visual-design/competency_qs.md +472 -0
  90. package/.onto/domains/visual-design/concepts.md +147 -0
  91. package/.onto/domains/visual-design/conciseness_rules.md +186 -0
  92. package/.onto/domains/visual-design/dependency_rules.md +282 -0
  93. package/.onto/domains/visual-design/domain_scope.md +290 -0
  94. package/.onto/domains/visual-design/extension_cases.md +480 -0
  95. package/.onto/domains/visual-design/logic_rules.md +232 -0
  96. package/.onto/domains/visual-design/structure_spec.md +213 -0
  97. package/.onto/principles/llm-native-development-guideline.md +401 -0
  98. package/.onto/principles/llm-runtime-interface-principles.md +665 -0
  99. package/.onto/principles/non-specialist-communication-guideline.md +74 -0
  100. package/.onto/principles/ontology-as-code-guideline.md +243 -0
  101. package/.onto/principles/ontology-as-code-naming-charter.md +130 -0
  102. package/.onto/principles/product-locality-principle.md +129 -0
  103. package/.onto/principles/productization-charter.md +569 -0
  104. package/.onto/processes/evolve/material-kind-adapter-contract.md +113 -0
  105. package/.onto/processes/reconstruct/reconstruct-boundary-contract.md +366 -0
  106. package/.onto/processes/reconstruct/source-profile-contract.md +107 -0
  107. package/.onto/processes/reconstruct/source-profiles/code.md +72 -0
  108. package/.onto/processes/reconstruct/source-profiles/database.md +74 -0
  109. package/.onto/processes/reconstruct/source-profiles/document.md +71 -0
  110. package/.onto/processes/reconstruct/source-profiles/spreadsheet.md +79 -0
  111. package/.onto/processes/review/binding-contract.md +270 -0
  112. package/.onto/processes/review/execution-preparation-artifacts.md +281 -0
  113. package/.onto/processes/review/interpretation-contract.md +245 -0
  114. package/.onto/processes/review/issue-stance-deliberation-contract.md +761 -0
  115. package/.onto/processes/review/lens-prompt-contract.md +402 -0
  116. package/.onto/processes/review/lens-registry.md +127 -0
  117. package/.onto/processes/review/pre-dispatch-contracts.md +428 -0
  118. package/.onto/processes/review/productized-live-path.md +398 -0
  119. package/.onto/processes/review/prompt-execution-runner-contract.md +187 -0
  120. package/.onto/processes/review/record-contract.md +427 -0
  121. package/.onto/processes/review/record-field-mapping.md +337 -0
  122. package/.onto/processes/review/review-context-manifest-contract.md +356 -0
  123. package/.onto/processes/review/review-execution-ux-contract.md +809 -0
  124. package/.onto/processes/review/review-target-profile-contract.md +259 -0
  125. package/.onto/processes/review/shared-phenomenon-contract.md +129 -0
  126. package/.onto/processes/review/synthesize-prompt-contract.md +343 -0
  127. package/.onto/processes/shared/target-material-kind-contract.md +198 -0
  128. package/.onto/roles/axiology.md +81 -0
  129. package/.onto/roles/conciseness.md +36 -0
  130. package/.onto/roles/coverage.md +34 -0
  131. package/.onto/roles/dependency.md +37 -0
  132. package/.onto/roles/evolution.md +35 -0
  133. package/.onto/roles/logic.md +104 -0
  134. package/.onto/roles/pragmatics.md +32 -0
  135. package/.onto/roles/semantics.md +36 -0
  136. package/.onto/roles/structure.md +33 -0
  137. package/.onto/roles/synthesize.md +92 -0
  138. package/AGENTS.md +240 -0
  139. package/CLAUDE.md +39 -0
  140. package/README.md +287 -0
  141. package/bin/onto +92 -0
  142. package/dist/cli.js +101 -0
  143. package/dist/core-api/reconstruct-api.js +222 -0
  144. package/dist/core-api/review-api.js +1271 -0
  145. package/dist/core-runtime/cli/assemble-review-record.js +431 -0
  146. package/dist/core-runtime/cli/bootstrap-review-binding.js +186 -0
  147. package/dist/core-runtime/cli/codex-nested-dispatch.js +226 -0
  148. package/dist/core-runtime/cli/codex-nested-dispatch.test.js +390 -0
  149. package/dist/core-runtime/cli/codex-nested-teamlead-executor.js +464 -0
  150. package/dist/core-runtime/cli/codex-nested-teamlead-executor.test.js +335 -0
  151. package/dist/core-runtime/cli/codex-review-unit-executor.js +228 -0
  152. package/dist/core-runtime/cli/complete-review-session.js +64 -0
  153. package/dist/core-runtime/cli/complexity-assessment.js +153 -0
  154. package/dist/core-runtime/cli/coordinator-helpers.js +583 -0
  155. package/dist/core-runtime/cli/coordinator-state-machine-deliberation.test.js +167 -0
  156. package/dist/core-runtime/cli/coordinator-state-machine.js +794 -0
  157. package/dist/core-runtime/cli/e2e-codex-multi-agent-fixes.test.js +615 -0
  158. package/dist/core-runtime/cli/e2e-start-review-session.test.js +312 -0
  159. package/dist/core-runtime/cli/health.js +44 -0
  160. package/dist/core-runtime/cli/inline-http-review-unit-executor.js +656 -0
  161. package/dist/core-runtime/cli/inline-http-review-unit-executor.test.js +567 -0
  162. package/dist/core-runtime/cli/materialize-review-execution-preparation.js +104 -0
  163. package/dist/core-runtime/cli/materialize-review-prompt-packets.js +952 -0
  164. package/dist/core-runtime/cli/migrate-session-roots.js +118 -0
  165. package/dist/core-runtime/cli/mock-review-unit-executor.js +285 -0
  166. package/dist/core-runtime/cli/onto-tools.js +369 -0
  167. package/dist/core-runtime/cli/prepare-review-session.js +272 -0
  168. package/dist/core-runtime/cli/render-review-final-output.js +350 -0
  169. package/dist/core-runtime/cli/repo-layout-migration-replace.smoke.test.js +106 -0
  170. package/dist/core-runtime/cli/review-invoke-auto-resolution.test.js +268 -0
  171. package/dist/core-runtime/cli/review-invoke-coordinator-topology.test.js +136 -0
  172. package/dist/core-runtime/cli/review-invoke-resolver-caching.test.js +201 -0
  173. package/dist/core-runtime/cli/review-invoke-topology-dispatch.test.js +192 -0
  174. package/dist/core-runtime/cli/review-invoke.js +2030 -0
  175. package/dist/core-runtime/cli/run-review-prompt-execution.js +2152 -0
  176. package/dist/core-runtime/cli/session-root-guard.js +168 -0
  177. package/dist/core-runtime/cli/spawn-watcher.js +173 -0
  178. package/dist/core-runtime/cli/spawn-watcher.test.js +457 -0
  179. package/dist/core-runtime/cli/start-review-session.js +68 -0
  180. package/dist/core-runtime/cli/strip-wrapping-code-fence.js +56 -0
  181. package/dist/core-runtime/cli/strip-wrapping-code-fence.test.js +79 -0
  182. package/dist/core-runtime/cli/teamcreate-lens-deliberation-executor.js +412 -0
  183. package/dist/core-runtime/cli/teamcreate-lens-deliberation-executor.test.js +351 -0
  184. package/dist/core-runtime/cli/topology-executor-mapping.js +139 -0
  185. package/dist/core-runtime/cli/topology-executor-mapping.test.js +173 -0
  186. package/dist/core-runtime/cli/write-review-interpretation.js +81 -0
  187. package/dist/core-runtime/config/onto-config-cli.js +278 -0
  188. package/dist/core-runtime/config/onto-config-key-path.js +288 -0
  189. package/dist/core-runtime/config/onto-config-key-path.test.js +195 -0
  190. package/dist/core-runtime/config/onto-config-preview.js +108 -0
  191. package/dist/core-runtime/config/onto-config-preview.test.js +132 -0
  192. package/dist/core-runtime/discovery/config-chain.js +118 -0
  193. package/dist/core-runtime/discovery/config-chain.test.js +103 -0
  194. package/dist/core-runtime/discovery/config-profile.js +199 -0
  195. package/dist/core-runtime/discovery/config-profile.test.js +233 -0
  196. package/dist/core-runtime/discovery/host-detection.js +33 -0
  197. package/dist/core-runtime/discovery/host-detection.test.js +186 -0
  198. package/dist/core-runtime/discovery/installation-paths.js +21 -0
  199. package/dist/core-runtime/discovery/installation-paths.test.js +65 -0
  200. package/dist/core-runtime/discovery/lens-registry.js +60 -0
  201. package/dist/core-runtime/discovery/lens-registry.test.js +81 -0
  202. package/dist/core-runtime/discovery/onto-home.js +71 -0
  203. package/dist/core-runtime/discovery/path-normalization.js +28 -0
  204. package/dist/core-runtime/discovery/path-normalization.test.js +22 -0
  205. package/dist/core-runtime/discovery/plugin-path.js +72 -0
  206. package/dist/core-runtime/discovery/plugin-path.test.js +95 -0
  207. package/dist/core-runtime/discovery/project-root.js +47 -0
  208. package/dist/core-runtime/discovery/settings-chain.js +353 -0
  209. package/dist/core-runtime/discovery/walk-up.js +17 -0
  210. package/dist/core-runtime/evolve/adapters/code-product/compile/compile-defense.js +344 -0
  211. package/dist/core-runtime/evolve/adapters/code-product/compile/compile-defense.test.js +915 -0
  212. package/dist/core-runtime/evolve/adapters/code-product/compile/compile.js +564 -0
  213. package/dist/core-runtime/evolve/adapters/code-product/compile/compile.test.js +708 -0
  214. package/dist/core-runtime/evolve/adapters/code-product/parsers/brief-parser.js +165 -0
  215. package/dist/core-runtime/evolve/adapters/code-product/parsers/brief-parser.test.js +227 -0
  216. package/dist/core-runtime/evolve/adapters/code-product/validators/validate.js +59 -0
  217. package/dist/core-runtime/evolve/adapters/code-product/validators/validate.test.js +205 -0
  218. package/dist/core-runtime/evolve/adapters/methodology/adapter.js +16 -0
  219. package/dist/core-runtime/evolve/adapters/methodology/adapter.test.js +9 -0
  220. package/dist/core-runtime/evolve/adapters/methodology/perspectives/authority-consistency.js +298 -0
  221. package/dist/core-runtime/evolve/adapters/methodology/perspectives/authority-consistency.test.js +70 -0
  222. package/dist/core-runtime/evolve/adapters/methodology/scope-types/process.js +46 -0
  223. package/dist/core-runtime/evolve/adapters/methodology/scope-types/process.test.js +73 -0
  224. package/dist/core-runtime/evolve/adapters/registry.js +47 -0
  225. package/dist/core-runtime/evolve/adapters/registry.test.js +67 -0
  226. package/dist/core-runtime/evolve/cli.js +256 -0
  227. package/dist/core-runtime/evolve/commands/align.js +194 -0
  228. package/dist/core-runtime/evolve/commands/align.test.js +82 -0
  229. package/dist/core-runtime/evolve/commands/apply.js +161 -0
  230. package/dist/core-runtime/evolve/commands/apply.test.js +138 -0
  231. package/dist/core-runtime/evolve/commands/close.js +39 -0
  232. package/dist/core-runtime/evolve/commands/close.test.js +99 -0
  233. package/dist/core-runtime/evolve/commands/defer.js +40 -0
  234. package/dist/core-runtime/evolve/commands/defer.test.js +134 -0
  235. package/dist/core-runtime/evolve/commands/draft.js +323 -0
  236. package/dist/core-runtime/evolve/commands/draft.test.js +178 -0
  237. package/dist/core-runtime/evolve/commands/e2e-evolve-full-cycle.test.js +208 -0
  238. package/dist/core-runtime/evolve/commands/error-messages.js +125 -0
  239. package/dist/core-runtime/evolve/commands/error-messages.test.js +167 -0
  240. package/dist/core-runtime/evolve/commands/propose-align.js +222 -0
  241. package/dist/core-runtime/evolve/commands/propose-align.test.js +136 -0
  242. package/dist/core-runtime/evolve/commands/reconstruct.js +330 -0
  243. package/dist/core-runtime/evolve/commands/reconstruct.test.js +278 -0
  244. package/dist/core-runtime/evolve/commands/shared.js +22 -0
  245. package/dist/core-runtime/evolve/commands/stale-check.js +103 -0
  246. package/dist/core-runtime/evolve/commands/stale-check.test.js +84 -0
  247. package/dist/core-runtime/evolve/commands/start.js +887 -0
  248. package/dist/core-runtime/evolve/commands/start.test.js +396 -0
  249. package/dist/core-runtime/evolve/config/project-config.js +99 -0
  250. package/dist/core-runtime/evolve/config/project-config.test.js +170 -0
  251. package/dist/core-runtime/evolve/renderers/align-packet.js +280 -0
  252. package/dist/core-runtime/evolve/renderers/align-packet.test.js +332 -0
  253. package/dist/core-runtime/evolve/renderers/draft-packet.js +303 -0
  254. package/dist/core-runtime/evolve/renderers/draft-packet.test.js +377 -0
  255. package/dist/core-runtime/evolve/renderers/format.js +5 -0
  256. package/dist/core-runtime/evolve/renderers/scope-md.js +237 -0
  257. package/dist/core-runtime/evolve/renderers/scope-md.test.js +306 -0
  258. package/dist/core-runtime/govern/cli.js +369 -0
  259. package/dist/core-runtime/govern/cli.test.js +314 -0
  260. package/dist/core-runtime/govern/drift-engine.js +103 -0
  261. package/dist/core-runtime/govern/drift-engine.test.js +319 -0
  262. package/dist/core-runtime/govern/promote-principle.js +206 -0
  263. package/dist/core-runtime/govern/promote-principle.test.js +368 -0
  264. package/dist/core-runtime/govern/queue.js +81 -0
  265. package/dist/core-runtime/govern/types.js +16 -0
  266. package/dist/core-runtime/install/cli.js +530 -0
  267. package/dist/core-runtime/install/detect.js +128 -0
  268. package/dist/core-runtime/install/detect.test.js +155 -0
  269. package/dist/core-runtime/install/gitignore-update.js +74 -0
  270. package/dist/core-runtime/install/gitignore-update.test.js +64 -0
  271. package/dist/core-runtime/install/install-integration.test.js +373 -0
  272. package/dist/core-runtime/install/prompts.js +389 -0
  273. package/dist/core-runtime/install/prompts.test.js +293 -0
  274. package/dist/core-runtime/install/types.js +26 -0
  275. package/dist/core-runtime/install/validation.js +295 -0
  276. package/dist/core-runtime/install/validation.test.js +313 -0
  277. package/dist/core-runtime/install/writer.js +254 -0
  278. package/dist/core-runtime/install/writer.test.js +218 -0
  279. package/dist/core-runtime/learning/extractor.js +461 -0
  280. package/dist/core-runtime/learning/feedback.js +179 -0
  281. package/dist/core-runtime/learning/health-report.js +165 -0
  282. package/dist/core-runtime/learning/health-report.test.js +169 -0
  283. package/dist/core-runtime/learning/loader.js +388 -0
  284. package/dist/core-runtime/learning/loader.test.js +102 -0
  285. package/dist/core-runtime/learning/promote/apply-state.js +240 -0
  286. package/dist/core-runtime/learning/promote/audit-obligation.js +195 -0
  287. package/dist/core-runtime/learning/promote/collector.js +432 -0
  288. package/dist/core-runtime/learning/promote/degraded-state.js +125 -0
  289. package/dist/core-runtime/learning/promote/domain-doc-proposer.js +166 -0
  290. package/dist/core-runtime/learning/promote/e2e-promote.test.js +6385 -0
  291. package/dist/core-runtime/learning/promote/health-snapshot.js +150 -0
  292. package/dist/core-runtime/learning/promote/insight-reclassifier.js +544 -0
  293. package/dist/core-runtime/learning/promote/judgment-auditor.js +517 -0
  294. package/dist/core-runtime/learning/promote/panel-reviewer.js +1158 -0
  295. package/dist/core-runtime/learning/promote/promote-executor.js +1675 -0
  296. package/dist/core-runtime/learning/promote/promoter.js +307 -0
  297. package/dist/core-runtime/learning/promote/retirement.js +122 -0
  298. package/dist/core-runtime/learning/promote/types.js +23 -0
  299. package/dist/core-runtime/learning/prompt-sections.js +51 -0
  300. package/dist/core-runtime/learning/shared/artifact-registry-init.js +45 -0
  301. package/dist/core-runtime/learning/shared/artifact-registry.js +254 -0
  302. package/dist/core-runtime/learning/shared/audit-obligation-kernel.js +73 -0
  303. package/dist/core-runtime/learning/shared/audit-state.js +99 -0
  304. package/dist/core-runtime/learning/shared/duplicate-check.js +28 -0
  305. package/dist/core-runtime/learning/shared/llm-caller.js +831 -0
  306. package/dist/core-runtime/learning/shared/llm-caller.test.js +601 -0
  307. package/dist/core-runtime/learning/shared/llm-tool-loop.js +393 -0
  308. package/dist/core-runtime/learning/shared/mode.js +25 -0
  309. package/dist/core-runtime/learning/shared/paths.js +84 -0
  310. package/dist/core-runtime/learning/shared/paths.test.js +79 -0
  311. package/dist/core-runtime/learning/shared/patterns.js +37 -0
  312. package/dist/core-runtime/learning/shared/recoverability.js +355 -0
  313. package/dist/core-runtime/learning/shared/recovery-context.js +374 -0
  314. package/dist/core-runtime/learning/shared/scope.js +1 -0
  315. package/dist/core-runtime/learning/shared/semantic-classifier.js +94 -0
  316. package/dist/core-runtime/learning/shared/specs/apply-execution-state-spec.js +42 -0
  317. package/dist/core-runtime/learning/shared/specs/audit-state-spec.js +37 -0
  318. package/dist/core-runtime/learning/shared/specs/backup-metadata-spec.js +39 -0
  319. package/dist/core-runtime/learning/shared/specs/emergency-log-spec.js +41 -0
  320. package/dist/core-runtime/learning/shared/specs/layout-version-spec.js +38 -0
  321. package/dist/core-runtime/learning/shared/specs/promote-decisions-spec.js +43 -0
  322. package/dist/core-runtime/learning/shared/specs/promote-report-spec.js +113 -0
  323. package/dist/core-runtime/learning/shared/specs/prune-log-spec.js +36 -0
  324. package/dist/core-runtime/learning/shared/specs/recovery-resolution-spec.js +48 -0
  325. package/dist/core-runtime/learning/shared/specs/restore-manifest-spec.js +43 -0
  326. package/dist/core-runtime/learning/shared/specs/spec-helpers.js +64 -0
  327. package/dist/core-runtime/learning/usage-tracker.js +190 -0
  328. package/dist/core-runtime/learning/usage-tracker.test.js +176 -0
  329. package/dist/core-runtime/llm/llm-caller.js +649 -0
  330. package/dist/core-runtime/llm/llm-tool-loop.js +401 -0
  331. package/dist/core-runtime/llm/model-switcher.js +62 -0
  332. package/dist/core-runtime/logger.js +22 -0
  333. package/dist/core-runtime/onboard/detect-review-axes.js +122 -0
  334. package/dist/core-runtime/onboard/detect-review-axes.test.js +127 -0
  335. package/dist/core-runtime/onboard/write-review-block.js +188 -0
  336. package/dist/core-runtime/onboard/write-review-block.test.js +240 -0
  337. package/dist/core-runtime/readers/brownfield-builder.js +150 -0
  338. package/dist/core-runtime/readers/brownfield-builder.test.js +136 -0
  339. package/dist/core-runtime/readers/code-chunk-collector.js +53 -0
  340. package/dist/core-runtime/readers/code-chunk-collector.test.js +136 -0
  341. package/dist/core-runtime/readers/file-utils.js +240 -0
  342. package/dist/core-runtime/readers/file-utils.test.js +146 -0
  343. package/dist/core-runtime/readers/lexicon-citation-check.js +93 -0
  344. package/dist/core-runtime/readers/lexicon-citation-check.test.js +77 -0
  345. package/dist/core-runtime/readers/mcp-figma.js +30 -0
  346. package/dist/core-runtime/readers/mcp-figma.test.js +82 -0
  347. package/dist/core-runtime/readers/mcp-generic.js +31 -0
  348. package/dist/core-runtime/readers/mcp-generic.test.js +76 -0
  349. package/dist/core-runtime/readers/ontology-index.js +148 -0
  350. package/dist/core-runtime/readers/ontology-index.test.js +245 -0
  351. package/dist/core-runtime/readers/ontology-query.js +168 -0
  352. package/dist/core-runtime/readers/ontology-query.test.js +311 -0
  353. package/dist/core-runtime/readers/ontology-resolve.js +48 -0
  354. package/dist/core-runtime/readers/ontology-resolve.test.js +48 -0
  355. package/dist/core-runtime/readers/patterns/index.js +7 -0
  356. package/dist/core-runtime/readers/review-log.js +213 -0
  357. package/dist/core-runtime/readers/review-log.test.js +313 -0
  358. package/dist/core-runtime/readers/scan-local.js +102 -0
  359. package/dist/core-runtime/readers/scan-local.test.js +102 -0
  360. package/dist/core-runtime/readers/scan-tarball.js +121 -0
  361. package/dist/core-runtime/readers/scan-tarball.test.js +283 -0
  362. package/dist/core-runtime/readers/scan-vault.js +34 -0
  363. package/dist/core-runtime/readers/scan-vault.test.js +81 -0
  364. package/dist/core-runtime/readers/types.js +42 -0
  365. package/dist/core-runtime/readers/types.test.js +94 -0
  366. package/dist/core-runtime/readers/viewpoint-collectors.js +229 -0
  367. package/dist/core-runtime/reconstruct/artifact-types.js +1 -0
  368. package/dist/core-runtime/reconstruct/directive-validation.js +123 -0
  369. package/dist/core-runtime/reconstruct/materialize-preparation.js +251 -0
  370. package/dist/core-runtime/reconstruct/record.js +198 -0
  371. package/dist/core-runtime/reconstruct/run.js +578 -0
  372. package/dist/core-runtime/reconstruct/seed-candidate-validation.js +356 -0
  373. package/dist/core-runtime/reconstruct/source-observations.js +62 -0
  374. package/dist/core-runtime/reconstruct/source-profiles.js +73 -0
  375. package/dist/core-runtime/release-channel/release-channel.js +90 -0
  376. package/dist/core-runtime/review/artifact-types.js +13 -0
  377. package/dist/core-runtime/review/citation-audit.js +204 -0
  378. package/dist/core-runtime/review/citation-audit.test.js +165 -0
  379. package/dist/core-runtime/review/controlled-lens-deliberation.js +125 -0
  380. package/dist/core-runtime/review/execution-plan-resolver.js +247 -0
  381. package/dist/core-runtime/review/execution-plan-resolver.test.js +243 -0
  382. package/dist/core-runtime/review/execution-topology-resolver-axis-first.test.js +246 -0
  383. package/dist/core-runtime/review/execution-topology-resolver.js +401 -0
  384. package/dist/core-runtime/review/execution-topology-resolver.test.js +315 -0
  385. package/dist/core-runtime/review/failure-records.js +57 -0
  386. package/dist/core-runtime/review/inline-context-embedder.js +141 -0
  387. package/dist/core-runtime/review/inline-context-embedder.test.js +154 -0
  388. package/dist/core-runtime/review/issue-artifact-runtime.js +859 -0
  389. package/dist/core-runtime/review/legacy-mode-policy.js +88 -0
  390. package/dist/core-runtime/review/lens-completion-policy.js +17 -0
  391. package/dist/core-runtime/review/materializers-effort-persist.test.js +79 -0
  392. package/dist/core-runtime/review/materializers.js +963 -0
  393. package/dist/core-runtime/review/ontology-path-classifier.js +179 -0
  394. package/dist/core-runtime/review/ontology-path-classifier.test.js +216 -0
  395. package/dist/core-runtime/review/packet-boundary-policy.js +215 -0
  396. package/dist/core-runtime/review/packet-boundary-policy.test.js +107 -0
  397. package/dist/core-runtime/review/participating-lens-paths.js +61 -0
  398. package/dist/core-runtime/review/participating-lens-paths.test.js +73 -0
  399. package/dist/core-runtime/review/review-artifact-utils.js +287 -0
  400. package/dist/core-runtime/review/review-config-legacy-translate.js +244 -0
  401. package/dist/core-runtime/review/review-config-legacy-translate.test.js +161 -0
  402. package/dist/core-runtime/review/review-config-validator.js +289 -0
  403. package/dist/core-runtime/review/review-config-validator.test.js +236 -0
  404. package/dist/core-runtime/review/review-execution-profile.js +193 -0
  405. package/dist/core-runtime/review/review-execution-route.js +52 -0
  406. package/dist/core-runtime/review/review-progress-contract.js +123 -0
  407. package/dist/core-runtime/review/review-record-validation.js +251 -0
  408. package/dist/core-runtime/review/review-result-classification.js +379 -0
  409. package/dist/core-runtime/review/review-state-machine.js +39 -0
  410. package/dist/core-runtime/review/route-visibility.js +125 -0
  411. package/dist/core-runtime/review/shape-pipeline-audit.test.js +311 -0
  412. package/dist/core-runtime/review/shape-to-topology-id.js +117 -0
  413. package/dist/core-runtime/review/shape-to-topology-id.test.js +132 -0
  414. package/dist/core-runtime/review/topology-shape-derivation.js +155 -0
  415. package/dist/core-runtime/review/topology-shape-derivation.test.js +195 -0
  416. package/dist/core-runtime/scope-runtime/constants.js +12 -0
  417. package/dist/core-runtime/scope-runtime/constraint-pool.js +166 -0
  418. package/dist/core-runtime/scope-runtime/constraint-pool.test.js +674 -0
  419. package/dist/core-runtime/scope-runtime/domain-validation-log.js +135 -0
  420. package/dist/core-runtime/scope-runtime/domain-validation-log.test.js +156 -0
  421. package/dist/core-runtime/scope-runtime/eval-persistence.js +65 -0
  422. package/dist/core-runtime/scope-runtime/eval-persistence.test.js +84 -0
  423. package/dist/core-runtime/scope-runtime/event-pipeline.js +64 -0
  424. package/dist/core-runtime/scope-runtime/event-pipeline.test.js +450 -0
  425. package/dist/core-runtime/scope-runtime/event-store.js +39 -0
  426. package/dist/core-runtime/scope-runtime/event-store.test.js +95 -0
  427. package/dist/core-runtime/scope-runtime/gate-guard.js +348 -0
  428. package/dist/core-runtime/scope-runtime/gate-guard.test.js +1047 -0
  429. package/dist/core-runtime/scope-runtime/hash.js +4 -0
  430. package/dist/core-runtime/scope-runtime/hash.test.js +33 -0
  431. package/dist/core-runtime/scope-runtime/id.js +4 -0
  432. package/dist/core-runtime/scope-runtime/id.test.js +17 -0
  433. package/dist/core-runtime/scope-runtime/reducer.js +297 -0
  434. package/dist/core-runtime/scope-runtime/reducer.test.js +759 -0
  435. package/dist/core-runtime/scope-runtime/scope-manager.js +161 -0
  436. package/dist/core-runtime/scope-runtime/state-machine.js +309 -0
  437. package/dist/core-runtime/scope-runtime/state-machine.test.js +704 -0
  438. package/dist/core-runtime/scope-runtime/types.js +116 -0
  439. package/dist/core-runtime/scope-runtime/types.test.js +69 -0
  440. package/dist/core-runtime/target-material-kind.js +256 -0
  441. package/dist/core-runtime/translate/render-for-user.js +169 -0
  442. package/dist/core-runtime/translate/render-for-user.test.js +122 -0
  443. package/dist/mcp/server.js +1011 -0
  444. package/dist/mcp/tool-schemas.js +93 -0
  445. package/dist/providers/capability-contract.js +1 -0
  446. package/package.json +68 -0
  447. package/settings.example.json +33 -0
@@ -0,0 +1,2152 @@
1
+ #!/usr/bin/env node
2
+ import crypto from "node:crypto";
3
+ import { spawn } from "node:child_process";
4
+ import fs from "node:fs/promises";
5
+ import path from "node:path";
6
+ import { parseArgs } from "node:util";
7
+ import { pathToFileURL } from "node:url";
8
+ import { appendMarkdownLogEntry, fileExists, isoFromTimestamp, parseMarkdownFrontmatter, removeFileIfExists, readYamlDocument, writeYamlDocument, } from "../review/review-artifact-utils.js";
9
+ import { PRE_DELIBERATION_ISSUE_ARTIFACT_IDS, issueArtifactConsumerId, issueArtifactSpec, renderIssueArtifactContext, renderIssueArtifactRefs, resolveProblemFramingProfileRef, validateIssueArtifactOnDisk, writeIssueArtifactPromptPacket, } from "../review/issue-artifact-runtime.js";
10
+ import { buildReviewExecutionRoute } from "../review/review-execution-route.js";
11
+ import { buildLensControlledDeliberationPrompt, buildTeamleadControlledDeliberationPrompt, } from "../review/controlled-lens-deliberation.js";
12
+ import { resolveRequiredParticipatingLensCount } from "../review/lens-completion-policy.js";
13
+ import { REVIEW_EXECUTION_STEP_IDS, REVIEW_PROGRESS_TOTAL_STEPS, } from "../review/review-progress-contract.js";
14
+ import { printOntoReleaseChannelNotice } from "../release-channel/release-channel.js";
15
+ import { executeReviewViaCodexNested } from "./codex-nested-dispatch.js";
16
+ import { ReviewStructuredFailureError, writeAndThrowStructuredFailureRecord, } from "../review/failure-records.js";
17
+ function errorMessage(error) {
18
+ return error instanceof Error ? error.message : String(error);
19
+ }
20
+ function isSuccessfulOutcome(outcome) {
21
+ return outcome !== undefined && outcome.success;
22
+ }
23
+ function isFailureOutcome(outcome) {
24
+ return (outcome !== undefined &&
25
+ !outcome.success &&
26
+ outcome.failure !== undefined);
27
+ }
28
+ async function appendExecutionProgress(errorLogPath, title, bodyLines) {
29
+ await appendMarkdownLogEntry(errorLogPath, title, bodyLines.join("\n"));
30
+ }
31
+ async function emitReviewProgress(args) {
32
+ const detailText = args.details && args.details.length > 0
33
+ ? ` - ${args.details.join("; ")}`
34
+ : "";
35
+ console.log(`[review progress] ${args.step}/${REVIEW_PROGRESS_TOTAL_STEPS} ${args.label}${detailText}`);
36
+ await appendExecutionProgress(args.executionPlan.error_log_path, `review progress ${args.step}/${REVIEW_PROGRESS_TOTAL_STEPS}: ${args.label}`, args.details ?? []);
37
+ }
38
+ function renderEffectiveBoundaryStateLog(effectiveBoundaryState) {
39
+ return [
40
+ `web_research: requested=${effectiveBoundaryState.web_research.requested_policy}, effective=${effectiveBoundaryState.web_research.effective_policy}, guarantee=${effectiveBoundaryState.web_research.guarantee_level}`,
41
+ `repo_exploration: requested=${effectiveBoundaryState.repo_exploration.requested_policy}, effective=${effectiveBoundaryState.repo_exploration.effective_policy}, guarantee=${effectiveBoundaryState.repo_exploration.guarantee_level}`,
42
+ `recursive_reference_expansion: requested=${effectiveBoundaryState.recursive_reference_expansion.requested_policy}, effective=${effectiveBoundaryState.recursive_reference_expansion.effective_policy}, guarantee=${effectiveBoundaryState.recursive_reference_expansion.guarantee_level}`,
43
+ `source_mutation: requested=${effectiveBoundaryState.source_mutation.requested_policy}, effective=${effectiveBoundaryState.source_mutation.effective_policy}, guarantee=${effectiveBoundaryState.source_mutation.guarantee_level}`,
44
+ `filesystem_scope_effective: ${effectiveBoundaryState.filesystem_scope.effective_allowed_roots.join(", ")}`,
45
+ `filesystem_scope_guarantee: ${effectiveBoundaryState.filesystem_scope.guarantee_level}`,
46
+ ...effectiveBoundaryState.web_research.notes.map((note) => `note.web_research: ${note}`),
47
+ ...effectiveBoundaryState.repo_exploration.notes.map((note) => `note.repo_exploration: ${note}`),
48
+ ...effectiveBoundaryState.recursive_reference_expansion.notes.map((note) => `note.recursive_reference_expansion: ${note}`),
49
+ ...effectiveBoundaryState.source_mutation.notes.map((note) => `note.source_mutation: ${note}`),
50
+ ...effectiveBoundaryState.filesystem_scope.notes.map((note) => `note.filesystem_scope: ${note}`),
51
+ ].join("\n");
52
+ }
53
+ function renderLensOutputRefsSection(projectRoot, lensDispatches) {
54
+ const sections = lensDispatches.map((dispatch) => {
55
+ const relativeOutputPath = path.relative(projectRoot, dispatch.output_path);
56
+ return `- ${dispatch.unit_id}: ${relativeOutputPath}`;
57
+ });
58
+ return `## Runtime Participating Lens Outputs\n${sections.join("\n")}\n`;
59
+ }
60
+ function renderDegradedLensFailuresSection(failures) {
61
+ if (failures.length === 0) {
62
+ return "";
63
+ }
64
+ return `## Degraded Lens Failures\n${failures
65
+ .map((failure) => `- ${failure.unit_id}: ${failure.message.replaceAll("\n", " ").trim()}`)
66
+ .join("\n")}\n`;
67
+ }
68
+ async function writeLensCompletionBarrier(args) {
69
+ const plannedLensIds = args.lensDispatches.map((dispatch) => dispatch.unit_id);
70
+ const completedLensIds = args.successfulLensDispatches.map((dispatch) => dispatch.unit_id);
71
+ const failedLensIds = args.executionFailures
72
+ .filter((failure) => failure.unit_kind === "lens")
73
+ .map((failure) => failure.unit_id);
74
+ const missingLensIds = plannedLensIds.filter((lensId) => !completedLensIds.includes(lensId) && !failedLensIds.includes(lensId));
75
+ const degradedLensIds = plannedLensIds.filter((lensId) => !completedLensIds.includes(lensId));
76
+ const downstreamAllowed = completedLensIds.length >= args.minimumParticipatingLenses;
77
+ const status = downstreamAllowed && degradedLensIds.length === 0
78
+ ? "passed"
79
+ : downstreamAllowed
80
+ ? "passed_with_degradation"
81
+ : "failed";
82
+ const barrier = {
83
+ schema_version: "1",
84
+ session_id: args.executionPlan.session_id,
85
+ created_at: isoFromTimestamp(Date.now()),
86
+ observed_dispatch_width: args.observedDispatchWidth,
87
+ minimum_participating_lenses: args.minimumParticipatingLenses,
88
+ planned_lens_ids: plannedLensIds,
89
+ completed_lens_ids: completedLensIds,
90
+ failed_lens_ids: failedLensIds,
91
+ missing_lens_ids: missingLensIds,
92
+ degraded_lens_ids: degradedLensIds,
93
+ status,
94
+ downstream_allowed: downstreamAllowed,
95
+ downstream_reason: downstreamAllowed
96
+ ? "selected lens completion threshold satisfied"
97
+ : "selected lens completion threshold not satisfied",
98
+ };
99
+ const barrierPath = args.executionPlan.lens_completion_barrier_path ??
100
+ path.join(args.executionPlan.session_root, "lens-completion-barrier.yaml");
101
+ await writeYamlDocument(barrierPath, barrier);
102
+ await appendExecutionProgress(args.executionPlan.error_log_path, "runner lens completion barrier", [
103
+ `status: ${barrier.status}`,
104
+ `observed_dispatch_width: ${barrier.observed_dispatch_width}`,
105
+ `completed_lens_count: ${barrier.completed_lens_ids.length}`,
106
+ `degraded_lens_count: ${barrier.degraded_lens_ids.length}`,
107
+ `downstream_allowed: ${String(barrier.downstream_allowed)}`,
108
+ ]);
109
+ return barrier;
110
+ }
111
+ function renderControlledDeliberationRefsSection(projectRoot, executionPlan, deliberationDispatches) {
112
+ return [
113
+ "## Controlled Lens Deliberation Result",
114
+ `- teamlead result: ${path.relative(projectRoot, executionPlan.deliberation_output_path)}`,
115
+ "",
116
+ "## Lens Deliberation Responses",
117
+ ...deliberationDispatches.map((dispatch) => `- ${dispatch.unit_id.replace(/^deliberation-/, "")}: ${path.relative(projectRoot, dispatch.output_path)}`),
118
+ "",
119
+ ].join("\n");
120
+ }
121
+ function requireString(value, optionName) {
122
+ if (typeof value !== "string" || value.length === 0) {
123
+ throw new Error(`Missing required option --${optionName}`);
124
+ }
125
+ return value;
126
+ }
127
+ /** Default stagger delay between successive lens dispatches (ms).
128
+ * Spreads initial burst to avoid thundering-herd on the external API. */
129
+ function defaultStaggerDelayMsForExecutorConfig(executorConfig) {
130
+ if (executorConfig.bin === "npm" || executorConfig.bin.endsWith("npm.cmd")) {
131
+ if (executorConfig.args.includes("review:codex-unit-executor")) {
132
+ // codex executor spawns an external process → API request per lens
133
+ return 1500;
134
+ }
135
+ }
136
+ return 0;
137
+ }
138
+ /** Default retry count for individual lens execution failures.
139
+ * Set high (10) to absorb transient network timeouts and CLI crashes
140
+ * without losing a lens to degraded status. Retries use a bounded linear
141
+ * delay based on the attempt number. */
142
+ const DEFAULT_LENS_MAX_RETRIES = 10;
143
+ /** Base delay for bounded linear retries. Synthesize reuses the base delay. */
144
+ const DEFAULT_LENS_RETRY_INITIAL_DELAY_MS = 8000;
145
+ const DEFAULT_REVIEW_UNIT_TIMEOUT_MS = 600_000;
146
+ class ReviewUnitTimeoutError extends Error {
147
+ timeoutMs;
148
+ constructor(unitId, timeoutMs) {
149
+ super(`Review unit ${unitId} timed out after ${timeoutMs}ms.`);
150
+ this.name = "ReviewUnitTimeoutError";
151
+ this.timeoutMs = timeoutMs;
152
+ }
153
+ }
154
+ function isReviewUnitTimeoutError(error) {
155
+ return error instanceof ReviewUnitTimeoutError;
156
+ }
157
+ class ReviewIssueArtifactDispatchError extends Error {
158
+ outcome;
159
+ originalError;
160
+ constructor(message, outcome, originalError) {
161
+ super(message);
162
+ this.name = "ReviewIssueArtifactDispatchError";
163
+ this.outcome = outcome;
164
+ this.originalError = originalError;
165
+ }
166
+ }
167
+ function issueArtifactOutcomeFromError(error) {
168
+ if (error instanceof ReviewIssueArtifactDispatchError) {
169
+ return error.outcome;
170
+ }
171
+ return null;
172
+ }
173
+ class ReviewControlledDeliberationDispatchError extends Error {
174
+ outcomes;
175
+ failedOutcome;
176
+ constructor(message, outcomes, failedOutcome) {
177
+ super(message);
178
+ this.name = "ReviewControlledDeliberationDispatchError";
179
+ this.outcomes = outcomes;
180
+ this.failedOutcome = failedOutcome;
181
+ }
182
+ }
183
+ function controlledDeliberationOutcomesFromError(error) {
184
+ if (error instanceof ReviewControlledDeliberationDispatchError) {
185
+ return error.outcomes;
186
+ }
187
+ return [];
188
+ }
189
+ function controlledDeliberationFailedOutcomeFromError(error) {
190
+ if (error instanceof ReviewControlledDeliberationDispatchError) {
191
+ return error.failedOutcome;
192
+ }
193
+ return null;
194
+ }
195
+ function haltLensIdFromOutcome(outcome) {
196
+ if (!outcome)
197
+ return null;
198
+ if (outcome.dispatch.unit_kind === "lens") {
199
+ return outcome.dispatch.unit_id;
200
+ }
201
+ if (outcome.dispatch.unit_kind === "deliberation" &&
202
+ outcome.dispatch.unit_id.startsWith("deliberation-")) {
203
+ return outcome.dispatch.unit_id.replace(/^deliberation-/, "");
204
+ }
205
+ return null;
206
+ }
207
+ function haltArtifactFields(haltPhase, outcome) {
208
+ return {
209
+ halt_phase: haltPhase,
210
+ halt_unit_id: outcome?.dispatch.unit_id ?? null,
211
+ halt_unit_kind: outcome?.dispatch.unit_kind ?? null,
212
+ halt_lens_id: haltLensIdFromOutcome(outcome),
213
+ };
214
+ }
215
+ function sleep(ms) {
216
+ return new Promise((resolve) => setTimeout(resolve, ms));
217
+ }
218
+ async function ensureNonEmptyOutputFile(outputPath) {
219
+ if (!(await fileExists(outputPath))) {
220
+ throw new Error(`Executor did not create output file: ${outputPath}`);
221
+ }
222
+ const fileText = await fs.readFile(outputPath, "utf8");
223
+ if (fileText.trim().length === 0) {
224
+ throw new Error(`Executor created empty output file: ${outputPath}`);
225
+ }
226
+ }
227
+ async function invokeExecutor(executorConfig, projectRoot, sessionRoot, dispatch, timeoutMs = DEFAULT_REVIEW_UNIT_TIMEOUT_MS) {
228
+ await fs.mkdir(path.dirname(dispatch.output_path), { recursive: true });
229
+ const detached = process.platform !== "win32";
230
+ const child = spawn(executorConfig.bin, [
231
+ ...executorConfig.args,
232
+ "--project-root",
233
+ projectRoot,
234
+ "--session-root",
235
+ sessionRoot,
236
+ "--unit-id",
237
+ dispatch.unit_id,
238
+ "--unit-kind",
239
+ dispatch.unit_kind,
240
+ "--packet-path",
241
+ dispatch.packet_path,
242
+ "--output-path",
243
+ dispatch.output_path,
244
+ ], {
245
+ cwd: projectRoot,
246
+ stdio: ["ignore", "pipe", "pipe"],
247
+ detached,
248
+ env: {
249
+ ...process.env,
250
+ ...(process.env.ONTO_HOME ? { ONTO_HOME: process.env.ONTO_HOME } : {}),
251
+ },
252
+ });
253
+ let stdout = "";
254
+ let stderr = "";
255
+ child.stdout.on("data", (chunk) => {
256
+ stdout += String(chunk);
257
+ });
258
+ child.stderr.on("data", (chunk) => {
259
+ stderr += String(chunk);
260
+ });
261
+ let timedOut = false;
262
+ let forceKillTimer = null;
263
+ const terminateChild = (signal) => {
264
+ if (child.pid === undefined)
265
+ return;
266
+ try {
267
+ if (detached) {
268
+ process.kill(-child.pid, signal);
269
+ }
270
+ else {
271
+ child.kill(signal);
272
+ }
273
+ }
274
+ catch {
275
+ // Process may have exited between timeout and signal delivery.
276
+ }
277
+ };
278
+ const exitCode = await new Promise((resolve, reject) => {
279
+ const timeoutTimer = setTimeout(() => {
280
+ timedOut = true;
281
+ terminateChild("SIGTERM");
282
+ forceKillTimer = setTimeout(() => terminateChild("SIGKILL"), 2_000);
283
+ }, timeoutMs);
284
+ child.on("error", (error) => {
285
+ clearTimeout(timeoutTimer);
286
+ if (forceKillTimer)
287
+ clearTimeout(forceKillTimer);
288
+ reject(error);
289
+ });
290
+ child.on("close", (code) => {
291
+ clearTimeout(timeoutTimer);
292
+ if (forceKillTimer)
293
+ clearTimeout(forceKillTimer);
294
+ resolve(code ?? 1);
295
+ });
296
+ });
297
+ if (timedOut) {
298
+ await removeFileIfExists(dispatch.output_path);
299
+ throw new ReviewUnitTimeoutError(dispatch.unit_id, timeoutMs);
300
+ }
301
+ if (exitCode !== 0) {
302
+ const stderrMessage = stderr.trim();
303
+ const stdoutMessage = stdout.trim();
304
+ const combinedMessage = [stderrMessage, stdoutMessage]
305
+ .filter((message) => message.length > 0)
306
+ .join("\n");
307
+ throw new Error(combinedMessage.length > 0
308
+ ? combinedMessage
309
+ : `Executor exited with code ${exitCode} for ${dispatch.unit_id}`);
310
+ }
311
+ await ensureNonEmptyOutputFile(dispatch.output_path);
312
+ }
313
+ function toUnitExecutionResult(outcome) {
314
+ return {
315
+ unit_id: outcome.dispatch.unit_id,
316
+ unit_kind: outcome.dispatch.unit_kind,
317
+ packet_path: outcome.dispatch.packet_path,
318
+ output_path: outcome.dispatch.output_path,
319
+ status: outcome.success ? "completed" : "failed",
320
+ started_at: isoFromTimestamp(outcome.startedAtMs),
321
+ completed_at: isoFromTimestamp(outcome.completedAtMs),
322
+ duration_ms: Math.max(0, outcome.completedAtMs - outcome.startedAtMs),
323
+ // TS runner measures process wall-clock via Date.now() around
324
+ // invokeExecutor; both ends are exact to millisecond precision.
325
+ timestamp_provenance: "runner_wallclock",
326
+ failure_message: outcome.failure?.message ?? null,
327
+ };
328
+ }
329
+ function deriveExecutionStatus(params) {
330
+ if (!params.synthesisExecuted) {
331
+ return "halted_partial";
332
+ }
333
+ if (params.degradedLensIds.length > 0) {
334
+ return "completed_with_degradation";
335
+ }
336
+ return "completed";
337
+ }
338
+ async function readStructuredDeliberationStatus(executionPlan, synthesizeOutputPath) {
339
+ if (!(await fileExists(executionPlan.deliberation_output_path))) {
340
+ throw new Error(`Missing controlled deliberation result: ${executionPlan.deliberation_output_path}`);
341
+ }
342
+ const deliberationText = await fs.readFile(executionPlan.deliberation_output_path, "utf8");
343
+ if (deliberationText.trim().length === 0) {
344
+ throw new Error(`Controlled deliberation result is empty: ${executionPlan.deliberation_output_path}`);
345
+ }
346
+ const deliberationFrontmatter = parseMarkdownFrontmatter(deliberationText).metadata?.deliberation_status;
347
+ if (deliberationFrontmatter !== "performed") {
348
+ throw new Error(`Controlled deliberation result must declare deliberation_status: performed in ${executionPlan.deliberation_output_path}`);
349
+ }
350
+ if (!(await fileExists(synthesizeOutputPath))) {
351
+ throw new Error(`Missing synthesize output: ${synthesizeOutputPath}`);
352
+ }
353
+ const synthesizeText = await fs.readFile(synthesizeOutputPath, "utf8");
354
+ const parsed = parseMarkdownFrontmatter(synthesizeText);
355
+ const frontmatterStatus = parsed.metadata?.deliberation_status;
356
+ if (frontmatterStatus === "performed")
357
+ return "performed";
358
+ throw new Error(`Synthesize output must acknowledge controlled deliberation with deliberation_status: performed in ${synthesizeOutputPath}`);
359
+ }
360
+ function degradationSummaryPathForSession(sessionRoot) {
361
+ return path.join(sessionRoot, "degradation-summary.yaml");
362
+ }
363
+ function inferFailureLensId(artifact, result) {
364
+ if (result.unit_kind === "lens")
365
+ return result.unit_id;
366
+ if (artifact.halt_unit_id === result.unit_id) {
367
+ return artifact.halt_lens_id ?? null;
368
+ }
369
+ if (result.unit_kind === "deliberation" &&
370
+ result.unit_id.startsWith("deliberation-")) {
371
+ return result.unit_id.slice("deliberation-".length) || null;
372
+ }
373
+ return null;
374
+ }
375
+ function collectFailedUnits(artifact) {
376
+ const synthesizeResult = artifact.synthesize_execution_result ?? null;
377
+ const unitResults = [
378
+ ...artifact.lens_execution_results,
379
+ ...(artifact.issue_artifact_execution_results ?? []),
380
+ ...(artifact.deliberation_execution_results ?? []),
381
+ ...(synthesizeResult ? [synthesizeResult] : []),
382
+ ];
383
+ return unitResults
384
+ .filter((result) => result.status === "failed")
385
+ .map((result) => ({
386
+ unit_id: result.unit_id,
387
+ unit_kind: result.unit_kind,
388
+ lens_id: inferFailureLensId(artifact, result),
389
+ packet_path: result.packet_path,
390
+ output_path: result.output_path,
391
+ failure_message: result.failure_message ?? "unknown failure",
392
+ }));
393
+ }
394
+ function degradationKindsFor(artifact, failedUnits) {
395
+ const kinds = [];
396
+ if (artifact.degraded_lens_ids.length > 0)
397
+ kinds.push("lens_degradation");
398
+ if (artifact.execution_status === "halted_partial")
399
+ kinds.push("halted_partial");
400
+ if (failedUnits.length > 0)
401
+ kinds.push("unit_failure");
402
+ return kinds;
403
+ }
404
+ async function writeDegradationSummaryArtifact(executionPlan, artifact) {
405
+ const summaryPath = degradationSummaryPathForSession(executionPlan.session_root);
406
+ const failedUnits = collectFailedUnits(artifact);
407
+ const degradationKinds = degradationKindsFor(artifact, failedUnits);
408
+ if (degradationKinds.length === 0) {
409
+ await removeFileIfExists(summaryPath);
410
+ return;
411
+ }
412
+ const summary = {
413
+ schema_version: "1",
414
+ session_id: artifact.session_id,
415
+ created_at: artifact.execution_completed_at,
416
+ source_execution_result_ref: executionPlan.execution_result_path,
417
+ source_error_log_ref: (await fileExists(executionPlan.error_log_path))
418
+ ? executionPlan.error_log_path
419
+ : null,
420
+ execution_status: artifact.execution_status,
421
+ degradation_kinds: degradationKinds,
422
+ degraded_lens_ids: artifact.degraded_lens_ids,
423
+ excluded_lens_ids: artifact.excluded_lens_ids,
424
+ halt_reason: artifact.halt_reason ?? null,
425
+ halt_phase: artifact.halt_phase ?? null,
426
+ halt_unit_id: artifact.halt_unit_id ?? null,
427
+ halt_unit_kind: artifact.halt_unit_kind ?? null,
428
+ halt_lens_id: artifact.halt_lens_id ?? null,
429
+ failed_units: failedUnits,
430
+ };
431
+ await writeYamlDocument(summaryPath, summary);
432
+ }
433
+ async function writeExecutionResultArtifact(executionPlan, artifact, reviewExecutionProfile) {
434
+ try {
435
+ await writeYamlDocument(executionPlan.execution_result_path, artifact);
436
+ await writeDegradationSummaryArtifact(executionPlan, artifact);
437
+ await writeReviewRunManifest(executionPlan, artifact, reviewExecutionProfile);
438
+ }
439
+ catch (error) {
440
+ await writeAndThrowStructuredFailureRecord({
441
+ sessionRoot: executionPlan.session_root,
442
+ phase: "execution.artifact_write",
443
+ reasonCode: "review_execution_artifact_write_failed",
444
+ humanMessage: "Runtime failed to write a required review execution artifact.",
445
+ requiredUserAction: "Restore write access to the review session artifact path, then rerun the review.",
446
+ retrySafety: "safe_after_environment_change",
447
+ artifactTrust: "execution_artifacts_partial",
448
+ dispatchState: "dispatched",
449
+ artifactRefs: {
450
+ execution_plan: path.join(executionPlan.session_root, "execution-plan.yaml"),
451
+ execution_result: executionPlan.execution_result_path,
452
+ degradation_summary: degradationSummaryPathForSession(executionPlan.session_root),
453
+ review_run_manifest: path.join(executionPlan.session_root, "review-run-manifest.yaml"),
454
+ },
455
+ mcpErrorCode: "ONTO_REVIEW_ARTIFACT_WRITE_FAILED",
456
+ detailsKind: "artifact_write",
457
+ details: {
458
+ execution_result_path: executionPlan.execution_result_path,
459
+ degradation_summary_path: degradationSummaryPathForSession(executionPlan.session_root),
460
+ review_run_manifest_path: path.join(executionPlan.session_root, "review-run-manifest.yaml"),
461
+ error_message: error instanceof Error ? error.message : String(error),
462
+ },
463
+ });
464
+ }
465
+ }
466
+ async function throwMalformedOutputFailure(args) {
467
+ return writeAndThrowStructuredFailureRecord({
468
+ sessionRoot: args.executionPlan.session_root,
469
+ phase: args.phase,
470
+ reasonCode: "review_unit_malformed_output",
471
+ humanMessage: args.humanMessage,
472
+ requiredUserAction: "Regenerate the review unit output with the current prompt contract.",
473
+ retrySafety: "safe_after_input_change",
474
+ artifactTrust: "execution_artifacts_partial",
475
+ dispatchState: "dispatched",
476
+ artifactRefs: {
477
+ execution_plan: path.join(args.executionPlan.session_root, "execution-plan.yaml"),
478
+ packet: args.packetPath,
479
+ output: args.outputPath,
480
+ error_log: args.executionPlan.error_log_path,
481
+ },
482
+ mcpErrorCode: "ONTO_REVIEW_MALFORMED_OUTPUT",
483
+ detailsKind: "malformed_output",
484
+ details: {
485
+ unit_id: args.unitId,
486
+ unit_kind: args.unitKind,
487
+ packet_path: args.packetPath,
488
+ output_path: args.outputPath,
489
+ error_message: args.error instanceof Error ? args.error.message : String(args.error),
490
+ },
491
+ });
492
+ }
493
+ async function optionalFileDigest(filePath) {
494
+ if (!(await fileExists(filePath)))
495
+ return null;
496
+ const content = await fs.readFile(filePath);
497
+ return crypto.createHash("sha256").update(content).digest("hex");
498
+ }
499
+ function consumerIdForLens(lensId) {
500
+ return `lens:${lensId}`;
501
+ }
502
+ function deriveContextAccessMatrix(contextSources) {
503
+ const matrix = {};
504
+ for (const source of contextSources) {
505
+ for (const consumerId of source.allowed_consumers) {
506
+ matrix[consumerId] = [
507
+ ...(matrix[consumerId] ?? []),
508
+ source.context_source_id,
509
+ ];
510
+ }
511
+ }
512
+ return Object.fromEntries(Object.entries(matrix).map(([consumerId, sourceIds]) => [
513
+ consumerId,
514
+ [...new Set(sourceIds)].sort(),
515
+ ]));
516
+ }
517
+ function sortedJson(values) {
518
+ return JSON.stringify([...new Set(values)].sort());
519
+ }
520
+ async function throwManifestValidationFailure(args) {
521
+ return writeAndThrowStructuredFailureRecord({
522
+ sessionRoot: args.executionPlan.session_root,
523
+ phase: "packets_materialized.manifest_validation",
524
+ reasonCode: args.reasonCode,
525
+ humanMessage: args.humanMessage,
526
+ requiredUserAction: args.requiredUserAction,
527
+ retrySafety: "safe_after_input_change",
528
+ artifactTrust: "manifest_artifacts_trusted",
529
+ dispatchState: "dispatch_blocked",
530
+ artifactRefs: {
531
+ execution_plan: path.join(args.executionPlan.session_root, "execution-plan.yaml"),
532
+ review_context_manifest: args.executionPlan.review_context_manifest_path ??
533
+ path.join(args.executionPlan.session_root, "execution-preparation", "review-context-manifest.yaml"),
534
+ },
535
+ mcpErrorCode: "ONTO_REVIEW_MANIFEST_VALIDATION_FAILED",
536
+ detailsKind: args.detailsKind,
537
+ details: args.details,
538
+ });
539
+ }
540
+ function packetRefByPath(packetRefs) {
541
+ return new Map(packetRefs.map((packetRef) => [path.resolve(packetRef.packet_ref), packetRef]));
542
+ }
543
+ async function reviewContextManifestPath(executionPlan) {
544
+ return (executionPlan.review_context_manifest_path ??
545
+ path.join(executionPlan.session_root, "execution-preparation", "review-context-manifest.yaml"));
546
+ }
547
+ async function readReviewContextManifest(executionPlan) {
548
+ const manifestPath = await reviewContextManifestPath(executionPlan);
549
+ return {
550
+ manifestPath,
551
+ manifest: await readYamlDocument(manifestPath),
552
+ };
553
+ }
554
+ async function validatePromptPacketRefForDispatch(args) {
555
+ const refsByPath = packetRefByPath(args.manifest.packet_refs);
556
+ const ref = refsByPath.get(path.resolve(args.packetPath));
557
+ if (!ref) {
558
+ await throwManifestValidationFailure({
559
+ executionPlan: args.executionPlan,
560
+ reasonCode: "packet_ref_missing",
561
+ humanMessage: "A required prompt packet is absent from the manifest.",
562
+ requiredUserAction: "Regenerate prompt packets before dispatch.",
563
+ detailsKind: "manifest_lifecycle",
564
+ details: {
565
+ consumer_id: args.consumerId,
566
+ packet_path: args.packetPath,
567
+ },
568
+ });
569
+ return;
570
+ }
571
+ if (ref.consumer_id !== args.consumerId) {
572
+ await throwManifestValidationFailure({
573
+ executionPlan: args.executionPlan,
574
+ reasonCode: "packet_consumer_mismatch",
575
+ humanMessage: "A prompt packet is bound to an unexpected consumer.",
576
+ requiredUserAction: "Regenerate actor/consumer bindings and prompt packets before dispatch.",
577
+ detailsKind: "context_eligibility",
578
+ details: {
579
+ packet_path: args.packetPath,
580
+ expected_consumer_id: args.consumerId,
581
+ actual_consumer_id: ref.consumer_id,
582
+ },
583
+ });
584
+ }
585
+ const observedPacketHash = await optionalFileDigest(args.packetPath);
586
+ if (ref.packet_sha256 !== observedPacketHash) {
587
+ await throwManifestValidationFailure({
588
+ executionPlan: args.executionPlan,
589
+ reasonCode: "packet_hash_mismatch",
590
+ humanMessage: "A prompt packet hash changed after manifest dispatch.",
591
+ requiredUserAction: "Regenerate prompt packets or restart review preparation.",
592
+ detailsKind: "manifest_lifecycle",
593
+ details: {
594
+ packet_path: args.packetPath,
595
+ expected_sha256: ref.packet_sha256,
596
+ observed_sha256: observedPacketHash,
597
+ },
598
+ });
599
+ }
600
+ const allowedContext = args.expectedMatrix[ref.consumer_id];
601
+ if (!allowedContext) {
602
+ await throwManifestValidationFailure({
603
+ executionPlan: args.executionPlan,
604
+ reasonCode: "packet_consumer_not_admitted",
605
+ humanMessage: "A prompt packet consumer is not admitted by the review context manifest.",
606
+ requiredUserAction: "Regenerate actor/consumer bindings and prompt packets before dispatch.",
607
+ detailsKind: "context_eligibility",
608
+ details: {
609
+ consumer_id: ref.consumer_id,
610
+ packet_path: ref.packet_ref,
611
+ },
612
+ });
613
+ return;
614
+ }
615
+ const allContextIds = args.manifest.context_sources.map((source) => source.context_source_id);
616
+ const expectedForbiddenContext = allContextIds.filter((sourceId) => !allowedContext.includes(sourceId));
617
+ if (sortedJson(ref.consumed_context_refs) !== sortedJson(allowedContext) ||
618
+ sortedJson(ref.forbidden_context_refs) !==
619
+ sortedJson(expectedForbiddenContext)) {
620
+ await throwManifestValidationFailure({
621
+ executionPlan: args.executionPlan,
622
+ reasonCode: "packet_context_eligibility_mismatch",
623
+ humanMessage: "Prompt packet context refs do not match manifest consumer eligibility.",
624
+ requiredUserAction: "Regenerate prompt packets from the current manifest.",
625
+ detailsKind: "context_eligibility",
626
+ details: {
627
+ consumer_id: ref.consumer_id,
628
+ packet_path: ref.packet_ref,
629
+ expected_consumed_context_refs: allowedContext,
630
+ actual_consumed_context_refs: ref.consumed_context_refs,
631
+ expected_forbidden_context_refs: expectedForbiddenContext,
632
+ actual_forbidden_context_refs: ref.forbidden_context_refs,
633
+ },
634
+ });
635
+ }
636
+ }
637
+ async function validateManifestPacketRefsForDispatch(args) {
638
+ const allContextIds = args.manifest.context_sources.map((source) => source.context_source_id);
639
+ for (const ref of args.manifest.packet_refs) {
640
+ const allowedContext = args.expectedMatrix[ref.consumer_id];
641
+ if (!allowedContext) {
642
+ await throwManifestValidationFailure({
643
+ executionPlan: args.executionPlan,
644
+ reasonCode: "packet_consumer_not_admitted",
645
+ humanMessage: "A prompt packet consumer is not admitted by the review context manifest.",
646
+ requiredUserAction: "Regenerate actor/consumer bindings and prompt packets before dispatch.",
647
+ detailsKind: "context_eligibility",
648
+ details: {
649
+ consumer_id: ref.consumer_id,
650
+ packet_path: ref.packet_ref,
651
+ },
652
+ });
653
+ return;
654
+ }
655
+ const observedPacketHash = await optionalFileDigest(ref.packet_ref);
656
+ if (ref.packet_sha256 !== observedPacketHash) {
657
+ await throwManifestValidationFailure({
658
+ executionPlan: args.executionPlan,
659
+ reasonCode: "packet_hash_mismatch",
660
+ humanMessage: "A prompt packet hash changed after manifest dispatch.",
661
+ requiredUserAction: "Regenerate prompt packets or restart review preparation.",
662
+ detailsKind: "manifest_lifecycle",
663
+ details: {
664
+ packet_path: ref.packet_ref,
665
+ expected_sha256: ref.packet_sha256,
666
+ observed_sha256: observedPacketHash,
667
+ },
668
+ });
669
+ }
670
+ const expectedForbiddenContext = allContextIds.filter((sourceId) => !allowedContext.includes(sourceId));
671
+ if (sortedJson(ref.consumed_context_refs) !== sortedJson(allowedContext) ||
672
+ sortedJson(ref.forbidden_context_refs) !==
673
+ sortedJson(expectedForbiddenContext)) {
674
+ await throwManifestValidationFailure({
675
+ executionPlan: args.executionPlan,
676
+ reasonCode: "packet_context_eligibility_mismatch",
677
+ humanMessage: "Prompt packet context refs do not match manifest consumer eligibility.",
678
+ requiredUserAction: "Regenerate prompt packets from the current manifest.",
679
+ detailsKind: "context_eligibility",
680
+ details: {
681
+ consumer_id: ref.consumer_id,
682
+ packet_path: ref.packet_ref,
683
+ expected_consumed_context_refs: allowedContext,
684
+ actual_consumed_context_refs: ref.consumed_context_refs,
685
+ expected_forbidden_context_refs: expectedForbiddenContext,
686
+ actual_forbidden_context_refs: ref.forbidden_context_refs,
687
+ },
688
+ });
689
+ }
690
+ }
691
+ }
692
+ async function registerGeneratedPromptPacketRefForDispatch(args) {
693
+ const { manifestPath, manifest } = await readReviewContextManifest(args.executionPlan);
694
+ if (manifest.lifecycle_state !== "dispatched") {
695
+ await throwManifestValidationFailure({
696
+ executionPlan: args.executionPlan,
697
+ reasonCode: "review_context_manifest_not_dispatched",
698
+ humanMessage: "Review context manifest must be dispatched before generated packet registration.",
699
+ requiredUserAction: "Regenerate prompt packets from a validated manifest before dispatch.",
700
+ detailsKind: "manifest_lifecycle",
701
+ details: {
702
+ manifest_path: manifestPath,
703
+ lifecycle_state: manifest.lifecycle_state,
704
+ },
705
+ });
706
+ }
707
+ const expectedMatrix = deriveContextAccessMatrix(manifest.context_sources);
708
+ const allowedContext = expectedMatrix[args.consumerId];
709
+ if (!allowedContext) {
710
+ await throwManifestValidationFailure({
711
+ executionPlan: args.executionPlan,
712
+ reasonCode: "packet_consumer_not_admitted",
713
+ humanMessage: "A generated prompt packet consumer is not admitted by the review context manifest.",
714
+ requiredUserAction: "Regenerate actor/consumer bindings and prompt packets before dispatch.",
715
+ detailsKind: "context_eligibility",
716
+ details: {
717
+ consumer_id: args.consumerId,
718
+ packet_path: args.packetPath,
719
+ },
720
+ });
721
+ return;
722
+ }
723
+ const packetSha256 = await optionalFileDigest(args.packetPath);
724
+ if (!packetSha256) {
725
+ await throwManifestValidationFailure({
726
+ executionPlan: args.executionPlan,
727
+ reasonCode: "generated_packet_missing",
728
+ humanMessage: "A generated prompt packet is missing before dispatch.",
729
+ requiredUserAction: "Regenerate the runtime prompt packet before dispatch.",
730
+ detailsKind: "manifest_lifecycle",
731
+ details: {
732
+ consumer_id: args.consumerId,
733
+ packet_path: args.packetPath,
734
+ },
735
+ });
736
+ return;
737
+ }
738
+ const allContextIds = manifest.context_sources.map((source) => source.context_source_id);
739
+ const packetRef = {
740
+ consumer_id: args.consumerId,
741
+ packet_ref: args.packetPath,
742
+ packet_sha256: packetSha256,
743
+ consumed_context_refs: allowedContext,
744
+ forbidden_context_refs: allContextIds.filter((sourceId) => !allowedContext.includes(sourceId)),
745
+ };
746
+ const packetPath = path.resolve(args.packetPath);
747
+ const updatedManifest = {
748
+ ...manifest,
749
+ packet_refs: [
750
+ ...manifest.packet_refs.filter((ref) => path.resolve(ref.packet_ref) !== packetPath),
751
+ packetRef,
752
+ ],
753
+ validation_results: [
754
+ ...new Set([
755
+ ...manifest.validation_results,
756
+ `generated_packet_ref_registered:${args.consumerId}`,
757
+ ]),
758
+ ],
759
+ };
760
+ await writeYamlDocument(manifestPath, updatedManifest);
761
+ await validatePromptPacketRefForDispatch({
762
+ executionPlan: args.executionPlan,
763
+ manifest: updatedManifest,
764
+ expectedMatrix,
765
+ consumerId: args.consumerId,
766
+ packetPath: args.packetPath,
767
+ });
768
+ }
769
+ async function pruneGeneratedPromptPacketRefs(executionPlan) {
770
+ const { manifestPath, manifest } = await readReviewContextManifest(executionPlan);
771
+ const staticPacketPaths = new Set([
772
+ ...executionPlan.lens_prompt_packet_seats.map((seat) => seat.packet_path),
773
+ executionPlan.synthesize_prompt_packet_path,
774
+ ].map((packetPath) => path.resolve(packetPath)));
775
+ const staticPacketRefs = manifest.packet_refs.filter((packetRef) => staticPacketPaths.has(path.resolve(packetRef.packet_ref)));
776
+ if (staticPacketRefs.length === manifest.packet_refs.length)
777
+ return;
778
+ await writeYamlDocument(manifestPath, {
779
+ ...manifest,
780
+ packet_refs: staticPacketRefs,
781
+ validation_results: [
782
+ ...new Set([
783
+ ...manifest.validation_results,
784
+ "generated_packet_refs_pruned_for_new_execution",
785
+ ]),
786
+ ],
787
+ });
788
+ }
789
+ async function ensureReviewContextManifestReadyForDispatch(executionPlan) {
790
+ const manifestPath = await reviewContextManifestPath(executionPlan);
791
+ if (!(await fileExists(manifestPath))) {
792
+ await throwManifestValidationFailure({
793
+ executionPlan,
794
+ reasonCode: "review_context_manifest_missing",
795
+ humanMessage: "Review context manifest is missing before lens dispatch.",
796
+ requiredUserAction: "Run review preparation again so the manifest and prompt packet refs are materialized.",
797
+ detailsKind: "manifest_lifecycle",
798
+ details: { manifest_path: manifestPath },
799
+ });
800
+ }
801
+ const manifest = await readYamlDocument(manifestPath);
802
+ if (manifest.schema_version !== "1") {
803
+ await throwManifestValidationFailure({
804
+ executionPlan,
805
+ reasonCode: "review_context_manifest_schema_unsupported",
806
+ humanMessage: "Review context manifest schema version is unsupported.",
807
+ requiredUserAction: "Regenerate the review context manifest with the current runtime.",
808
+ detailsKind: "schema_validation",
809
+ details: {
810
+ manifest_path: manifestPath,
811
+ expected_schema_version: "1",
812
+ actual_schema_version: manifest.schema_version,
813
+ },
814
+ });
815
+ }
816
+ if (manifest.lifecycle_state !== "dispatched") {
817
+ await throwManifestValidationFailure({
818
+ executionPlan,
819
+ reasonCode: "review_context_manifest_not_dispatched",
820
+ humanMessage: "Review context manifest must be dispatched before lens execution.",
821
+ requiredUserAction: "Regenerate prompt packets from a validated manifest before dispatch.",
822
+ detailsKind: "manifest_lifecycle",
823
+ details: {
824
+ manifest_path: manifestPath,
825
+ lifecycle_state: manifest.lifecycle_state,
826
+ },
827
+ });
828
+ }
829
+ const expectedMatrix = deriveContextAccessMatrix(manifest.context_sources);
830
+ if (JSON.stringify(expectedMatrix) !==
831
+ JSON.stringify(manifest.derived_context_access_matrix)) {
832
+ await throwManifestValidationFailure({
833
+ executionPlan,
834
+ reasonCode: "review_context_matrix_mismatch",
835
+ humanMessage: "Review context manifest access matrix does not match allowed consumers.",
836
+ requiredUserAction: "Regenerate the review context manifest from runtime-owned context sources.",
837
+ detailsKind: "context_eligibility",
838
+ details: {
839
+ expected_matrix: expectedMatrix,
840
+ actual_matrix: manifest.derived_context_access_matrix,
841
+ },
842
+ });
843
+ }
844
+ for (const source of manifest.context_sources) {
845
+ const resolvedSourcePath = path.resolve(source.source_ref);
846
+ if (source.required && !(await fileExists(resolvedSourcePath))) {
847
+ await throwManifestValidationFailure({
848
+ executionPlan,
849
+ reasonCode: "required_context_source_missing",
850
+ humanMessage: "A required review context source is missing.",
851
+ requiredUserAction: "Restore the required artifact or restart review preparation.",
852
+ detailsKind: "context_eligibility",
853
+ details: {
854
+ context_source_id: source.context_source_id,
855
+ source_ref: source.source_ref,
856
+ },
857
+ });
858
+ }
859
+ const observedHash = await optionalFileDigest(resolvedSourcePath);
860
+ if (source.source_sha256 !== observedHash) {
861
+ await throwManifestValidationFailure({
862
+ executionPlan,
863
+ reasonCode: "context_source_hash_mismatch",
864
+ humanMessage: "A review context source hash changed after manifest validation.",
865
+ requiredUserAction: "Restart review preparation so context hashes and prompt packets match the target state.",
866
+ detailsKind: "context_eligibility",
867
+ details: {
868
+ context_source_id: source.context_source_id,
869
+ source_ref: source.source_ref,
870
+ expected_sha256: source.source_sha256,
871
+ observed_sha256: observedHash,
872
+ },
873
+ });
874
+ }
875
+ }
876
+ await validateManifestPacketRefsForDispatch({
877
+ executionPlan,
878
+ manifest,
879
+ expectedMatrix,
880
+ });
881
+ const requiredPackets = [
882
+ ...executionPlan.lens_prompt_packet_seats.map((seat) => ({
883
+ consumer_id: consumerIdForLens(seat.lens_id),
884
+ packet_path: seat.packet_path,
885
+ })),
886
+ {
887
+ consumer_id: "synthesize",
888
+ packet_path: executionPlan.synthesize_prompt_packet_path,
889
+ },
890
+ ];
891
+ for (const requiredPacket of requiredPackets) {
892
+ await validatePromptPacketRefForDispatch({
893
+ executionPlan,
894
+ manifest,
895
+ expectedMatrix,
896
+ consumerId: requiredPacket.consumer_id,
897
+ packetPath: requiredPacket.packet_path,
898
+ });
899
+ }
900
+ }
901
+ async function unitManifestEntry(result) {
902
+ return {
903
+ unit_id: result.unit_id,
904
+ unit_kind: result.unit_kind,
905
+ packet_path: result.packet_path,
906
+ packet_sha256: await optionalFileDigest(result.packet_path),
907
+ output_path: result.output_path,
908
+ output_sha256: await optionalFileDigest(result.output_path),
909
+ status: result.status,
910
+ started_at: result.started_at,
911
+ completed_at: result.completed_at,
912
+ duration_ms: result.duration_ms,
913
+ timestamp_provenance: result.timestamp_provenance ?? "unknown",
914
+ failure_message: result.failure_message ?? null,
915
+ };
916
+ }
917
+ async function writeReviewRunManifest(executionPlan, artifact, reviewExecutionProfile) {
918
+ const synthesizeResult = artifact.synthesize_execution_result ?? null;
919
+ const manifestPath = path.join(executionPlan.session_root, "review-run-manifest.yaml");
920
+ const unitResults = [
921
+ ...artifact.lens_execution_results,
922
+ ...(artifact.issue_artifact_execution_results ?? []),
923
+ ...(artifact.deliberation_execution_results ?? []),
924
+ ...(synthesizeResult ? [synthesizeResult] : []),
925
+ ];
926
+ const workerUnits = [];
927
+ for (const result of unitResults) {
928
+ workerUnits.push(await unitManifestEntry(result));
929
+ }
930
+ const resumeToken = crypto
931
+ .createHash("sha256")
932
+ .update([
933
+ executionPlan.session_id,
934
+ executionPlan.execution_result_path,
935
+ artifact.execution_started_at,
936
+ ].join("\n"))
937
+ .digest("hex");
938
+ await writeYamlDocument(manifestPath, {
939
+ schema_version: "1",
940
+ session_id: executionPlan.session_id,
941
+ created_at: artifact.execution_completed_at,
942
+ execution_contract: {
943
+ schema_version: "1",
944
+ execution_step_ids: [...REVIEW_EXECUTION_STEP_IDS],
945
+ progress_total_steps: REVIEW_PROGRESS_TOTAL_STEPS,
946
+ resume_token: resumeToken,
947
+ resume_token_ref: "review-run-manifest.execution_contract.resume_token",
948
+ idempotency_scope: "session_id",
949
+ idempotency_key: executionPlan.session_id,
950
+ duplicate_dispatch_policy: "session_id_collision_blocks",
951
+ },
952
+ review_execution_profile: reviewExecutionProfile
953
+ ? (() => {
954
+ const route = buildReviewExecutionRoute(reviewExecutionProfile);
955
+ return {
956
+ mode: reviewExecutionProfile.mode,
957
+ teamlead: reviewExecutionProfile.teamlead,
958
+ lens: reviewExecutionProfile.lens,
959
+ synthesize: reviewExecutionProfile.synthesize,
960
+ deliberation: reviewExecutionProfile.deliberation,
961
+ runtime_route: {
962
+ execution_realization: executionPlan.execution_realization,
963
+ host_runtime: route.artifact_host_runtime,
964
+ worker_executor: route.executor,
965
+ runtime_provider: route.resolved_provider,
966
+ auth_mode: route.auth_mode,
967
+ },
968
+ model: reviewExecutionProfile.model ?? null,
969
+ effort: reviewExecutionProfile.effort ?? null,
970
+ service_tier: reviewExecutionProfile.service_tier ?? null,
971
+ base_url: reviewExecutionProfile.base_url ?? null,
972
+ trace: reviewExecutionProfile.trace,
973
+ };
974
+ })()
975
+ : null,
976
+ artifact_refs: {
977
+ session_metadata: executionPlan.session_metadata_path,
978
+ interpretation: executionPlan.interpretation_artifact_path,
979
+ binding: executionPlan.binding_output_path,
980
+ execution_plan: path.join(executionPlan.session_root, "execution-plan.yaml"),
981
+ execution_result: executionPlan.execution_result_path,
982
+ degradation_summary: degradationKindsFor(artifact, collectFailedUnits(artifact)).length > 0
983
+ ? degradationSummaryPathForSession(executionPlan.session_root)
984
+ : null,
985
+ actor_invocation_profiles: executionPlan.actor_invocation_profiles_path ?? null,
986
+ actor_consumer_bindings: executionPlan.actor_consumer_bindings_path ?? null,
987
+ domain_binding: executionPlan.domain_binding_path ?? null,
988
+ review_target_profile: executionPlan.review_target_profile_path,
989
+ review_value_alignment_criteria: executionPlan.review_value_alignment_criteria_path ?? null,
990
+ review_context_manifest: executionPlan.review_context_manifest_path ?? null,
991
+ lens_completion_barrier: executionPlan.lens_completion_barrier_path ?? null,
992
+ final_output: executionPlan.final_output_path,
993
+ review_record: executionPlan.review_record_path,
994
+ synthesis_output: executionPlan.synthesis_output_path,
995
+ deliberation_output: executionPlan.deliberation_output_path,
996
+ finding_ledger: executionPlan.finding_ledger_path,
997
+ finding_relation_graph: executionPlan.finding_relation_graph_path,
998
+ issue_ledger: executionPlan.issue_ledger_path,
999
+ issue_stance_matrix: executionPlan.issue_stance_matrix_path,
1000
+ problem_framing: executionPlan.problem_framing_path,
1001
+ },
1002
+ worker_units: workerUnits,
1003
+ halt: artifact.halt_reason
1004
+ ? {
1005
+ phase: artifact.halt_phase ?? null,
1006
+ unit_id: artifact.halt_unit_id ?? null,
1007
+ unit_kind: artifact.halt_unit_kind ?? null,
1008
+ lens_id: artifact.halt_lens_id ?? null,
1009
+ reason: artifact.halt_reason,
1010
+ }
1011
+ : null,
1012
+ synthesis_provenance: {
1013
+ synthesis_executed: artifact.synthesis_executed,
1014
+ synthesis_output_path: executionPlan.synthesis_output_path,
1015
+ synthesis_output_sha256: await optionalFileDigest(executionPlan.synthesis_output_path),
1016
+ deliberation_status: artifact.deliberation_status ?? null,
1017
+ deliberation_output_path: executionPlan.deliberation_output_path,
1018
+ deliberation_output_sha256: await optionalFileDigest(executionPlan.deliberation_output_path),
1019
+ participating_lens_ids: artifact.participating_lens_ids,
1020
+ degraded_lens_ids: artifact.degraded_lens_ids,
1021
+ observed_dispatch_width: artifact.observed_dispatch_width ?? artifact.max_concurrent_lenses,
1022
+ lens_completion_barrier_ref: artifact.lens_completion_barrier_ref ?? null,
1023
+ issue_artifact_refs: Object.fromEntries(executionPlan.issue_artifact_prompt_packet_seats.map((seat) => [
1024
+ issueArtifactSpec(seat.artifact_id).ref_key,
1025
+ seat.output_path,
1026
+ ])),
1027
+ },
1028
+ });
1029
+ }
1030
+ async function resetExecutionOutputs(executionPlan) {
1031
+ const pathsToClear = [
1032
+ executionPlan.execution_result_path,
1033
+ degradationSummaryPathForSession(executionPlan.session_root),
1034
+ executionPlan.error_log_path,
1035
+ executionPlan.synthesis_output_path,
1036
+ executionPlan.deliberation_output_path,
1037
+ executionPlan.finding_ledger_path,
1038
+ executionPlan.finding_relation_graph_path,
1039
+ executionPlan.issue_ledger_path,
1040
+ executionPlan.issue_stance_matrix_path,
1041
+ executionPlan.deliberation_plan_path,
1042
+ executionPlan.problem_framing_path,
1043
+ executionPlan.final_output_path,
1044
+ executionPlan.lens_completion_barrier_path ??
1045
+ path.join(executionPlan.session_root, "lens-completion-barrier.yaml"),
1046
+ executionPlan.teamlead_deliberation_prompt_packet_path,
1047
+ path.join(executionPlan.prompt_packets_root, "synthesize.runtime.prompt.md"),
1048
+ ...executionPlan.lens_execution_seats.map((seat) => seat.output_path),
1049
+ ...executionPlan.issue_artifact_prompt_packet_seats.map((seat) => seat.packet_path),
1050
+ ...executionPlan.issue_artifact_prompt_packet_seats.map((seat) => seat.output_path),
1051
+ ...executionPlan.lens_deliberation_prompt_packet_seats.map((seat) => seat.packet_path),
1052
+ ...executionPlan.lens_deliberation_prompt_packet_seats.map((seat) => seat.output_path),
1053
+ ];
1054
+ await Promise.all(pathsToClear.map((targetPath) => removeFileIfExists(targetPath)));
1055
+ }
1056
+ async function appendExecutionFailure(errorLogPath, failure, effectiveBoundaryState) {
1057
+ await appendMarkdownLogEntry(errorLogPath, `${failure.unit_kind} failure: ${failure.unit_id}`, [
1058
+ `unit_id: ${failure.unit_id}`,
1059
+ `unit_kind: ${failure.unit_kind}`,
1060
+ `packet_path: ${failure.packet_path}`,
1061
+ `output_path: ${failure.output_path}`,
1062
+ `message: ${failure.message}`,
1063
+ ...(effectiveBoundaryState
1064
+ ? [
1065
+ "",
1066
+ "[effective_boundary_state]",
1067
+ renderEffectiveBoundaryStateLog(effectiveBoundaryState),
1068
+ ]
1069
+ : []),
1070
+ ].join("\n"));
1071
+ }
1072
+ async function runSingleDispatchWithRetries(args) {
1073
+ const { projectRoot, sessionRoot, executionPlan, executorConfig, dispatch, maxRetries, retryInitialDelayMs, unitTimeoutMs = DEFAULT_REVIEW_UNIT_TIMEOUT_MS, } = args;
1074
+ console.log(`[review runner] starting ${dispatch.unit_kind}: ${dispatch.unit_id}`);
1075
+ await appendExecutionProgress(executionPlan.error_log_path, `runner dispatch started: ${dispatch.unit_id}`, [
1076
+ `unit_id: ${dispatch.unit_id}`,
1077
+ `unit_kind: ${dispatch.unit_kind}`,
1078
+ `packet_path: ${dispatch.packet_path}`,
1079
+ `output_path: ${dispatch.output_path}`,
1080
+ ]);
1081
+ const startedAtMs = Date.now();
1082
+ let lastError = undefined;
1083
+ for (let attempt = 0; attempt <= maxRetries; attempt += 1) {
1084
+ try {
1085
+ await invokeExecutor(executorConfig, projectRoot, sessionRoot, dispatch, unitTimeoutMs);
1086
+ const completedAtMs = Date.now();
1087
+ console.log(`[review runner] completed ${dispatch.unit_kind}: ${dispatch.unit_id}`);
1088
+ await appendExecutionProgress(executionPlan.error_log_path, `runner dispatch completed: ${dispatch.unit_id}`, [
1089
+ `unit_id: ${dispatch.unit_id}`,
1090
+ `unit_kind: ${dispatch.unit_kind}`,
1091
+ `output_path: ${dispatch.output_path}`,
1092
+ ]);
1093
+ return {
1094
+ dispatch,
1095
+ success: true,
1096
+ startedAtMs,
1097
+ completedAtMs,
1098
+ };
1099
+ }
1100
+ catch (error) {
1101
+ lastError = error;
1102
+ if (!isReviewUnitTimeoutError(error) && attempt < maxRetries) {
1103
+ const retryDelay = retryInitialDelayMs * (attempt + 1);
1104
+ console.log(`[review runner] ${dispatch.unit_id} attempt ${attempt + 1} failed, retrying in ${retryDelay}ms...`);
1105
+ await appendExecutionProgress(executionPlan.error_log_path, `runner dispatch retry: ${dispatch.unit_id}`, [
1106
+ `attempt: ${attempt + 1}/${maxRetries}`,
1107
+ `retry_delay_ms: ${retryDelay}`,
1108
+ `error: ${error instanceof Error ? error.message.slice(0, 200) : String(error).slice(0, 200)}`,
1109
+ ]);
1110
+ await sleep(retryDelay);
1111
+ }
1112
+ if (isReviewUnitTimeoutError(error))
1113
+ break;
1114
+ }
1115
+ }
1116
+ const completedAtMs = Date.now();
1117
+ const failure = {
1118
+ unit_id: dispatch.unit_id,
1119
+ unit_kind: dispatch.unit_kind,
1120
+ packet_path: dispatch.packet_path,
1121
+ output_path: dispatch.output_path,
1122
+ message: lastError instanceof Error ? lastError.message : String(lastError),
1123
+ };
1124
+ await removeFileIfExists(dispatch.output_path);
1125
+ await appendExecutionFailure(executionPlan.error_log_path, failure, executionPlan.effective_boundary_state);
1126
+ return {
1127
+ dispatch,
1128
+ success: false,
1129
+ startedAtMs,
1130
+ completedAtMs,
1131
+ failure,
1132
+ };
1133
+ }
1134
+ async function readLensOutputsForDeliberation(dispatches) {
1135
+ return Promise.all(dispatches.map(async (dispatch) => ({
1136
+ lens_id: dispatch.unit_id,
1137
+ output_path: dispatch.output_path,
1138
+ content: await fs.readFile(dispatch.output_path, "utf8"),
1139
+ })));
1140
+ }
1141
+ function requireDeliberationSeat(executionPlan, lensId) {
1142
+ const seat = executionPlan.lens_deliberation_prompt_packet_seats.find((candidate) => candidate.lens_id === lensId);
1143
+ if (!seat) {
1144
+ throw new Error(`Missing deliberation prompt seat for lens: ${lensId}`);
1145
+ }
1146
+ return seat;
1147
+ }
1148
+ function issueArtifactProgress(artifactId) {
1149
+ const spec = issueArtifactSpec(artifactId);
1150
+ return { step: spec.progress_step, label: spec.progress_label };
1151
+ }
1152
+ async function runIssueArtifactDispatch(args) {
1153
+ const seat = await writeIssueArtifactPromptPacket({
1154
+ artifactId: args.artifactId,
1155
+ sessionId: args.executionPlan.session_id,
1156
+ projectRoot: args.projectRoot,
1157
+ executionPlan: args.executionPlan,
1158
+ lensOutputPaths: args.lensOutputPaths,
1159
+ ...(args.deliberationResponsePaths
1160
+ ? { deliberationResponsePaths: args.deliberationResponsePaths }
1161
+ : {}),
1162
+ ...(args.deliberationOutputPath
1163
+ ? { deliberationOutputPath: args.deliberationOutputPath }
1164
+ : {}),
1165
+ ...(args.problemFramingProfileRef !== undefined
1166
+ ? { problemFramingProfileRef: args.problemFramingProfileRef }
1167
+ : {}),
1168
+ });
1169
+ const dispatch = {
1170
+ unit_id: args.artifactId,
1171
+ unit_kind: "issue_artifact",
1172
+ packet_path: seat.packet_path,
1173
+ output_path: seat.output_path,
1174
+ };
1175
+ const progress = issueArtifactProgress(args.artifactId);
1176
+ await emitReviewProgress({
1177
+ executionPlan: args.executionPlan,
1178
+ step: progress.step,
1179
+ label: progress.label,
1180
+ details: [`artifact=${args.artifactId}`],
1181
+ });
1182
+ const participatingLensIds = args.lensOutputPaths.map((lensPath) => path.basename(lensPath, ".md"));
1183
+ let lastOutcome = null;
1184
+ let lastValidationError = null;
1185
+ for (let attempt = 0; attempt < 2; attempt += 1) {
1186
+ if (attempt > 0) {
1187
+ await fs.appendFile(seat.packet_path, [
1188
+ "",
1189
+ "## Validation Error To Correct",
1190
+ "The previous artifact output was rejected by the runtime validator.",
1191
+ "Rewrite the entire YAML artifact. Preserve the same contract and quote string scalars.",
1192
+ "",
1193
+ "```text",
1194
+ lastValidationError instanceof Error
1195
+ ? lastValidationError.message
1196
+ : String(lastValidationError),
1197
+ "```",
1198
+ "",
1199
+ ].join("\n"), "utf8");
1200
+ }
1201
+ await registerGeneratedPromptPacketRefForDispatch({
1202
+ executionPlan: args.executionPlan,
1203
+ consumerId: issueArtifactConsumerId(args.artifactId),
1204
+ packetPath: seat.packet_path,
1205
+ });
1206
+ const outcome = await runSingleDispatchWithRetries({
1207
+ projectRoot: args.projectRoot,
1208
+ sessionRoot: args.sessionRoot,
1209
+ executionPlan: args.executionPlan,
1210
+ executorConfig: args.executorConfig,
1211
+ dispatch,
1212
+ maxRetries: 1,
1213
+ retryInitialDelayMs: DEFAULT_LENS_RETRY_INITIAL_DELAY_MS,
1214
+ unitTimeoutMs: args.unitTimeoutMs,
1215
+ });
1216
+ lastOutcome = outcome;
1217
+ if (!outcome.success) {
1218
+ throw new ReviewIssueArtifactDispatchError(`Issue artifact generation failed for ${args.artifactId}: ${outcome.failure?.message ?? "unknown error"}`, outcome, outcome.failure?.message ?? "unknown error");
1219
+ }
1220
+ try {
1221
+ await validateIssueArtifactOnDisk({
1222
+ executionPlan: args.executionPlan,
1223
+ artifactId: args.artifactId,
1224
+ participatingLensIds,
1225
+ });
1226
+ return outcome;
1227
+ }
1228
+ catch (error) {
1229
+ lastValidationError = error;
1230
+ console.warn(`[review progress] ${progress.step}/${REVIEW_PROGRESS_TOTAL_STEPS} ${args.artifactId} validation failed on attempt ${attempt + 1}/2: ${error instanceof Error ? error.message : String(error)}`);
1231
+ await removeFileIfExists(seat.output_path);
1232
+ }
1233
+ }
1234
+ const failureMessage = "An issue artifact review unit produced malformed output after validation retry.";
1235
+ const failedOutcome = lastOutcome === null
1236
+ ? null
1237
+ : {
1238
+ dispatch,
1239
+ success: false,
1240
+ startedAtMs: lastOutcome.startedAtMs,
1241
+ completedAtMs: Date.now(),
1242
+ failure: {
1243
+ unit_id: dispatch.unit_id,
1244
+ unit_kind: dispatch.unit_kind,
1245
+ packet_path: dispatch.packet_path,
1246
+ output_path: dispatch.output_path,
1247
+ message: `${failureMessage}: ${errorMessage(lastValidationError)}`,
1248
+ },
1249
+ };
1250
+ try {
1251
+ return await throwMalformedOutputFailure({
1252
+ executionPlan: args.executionPlan,
1253
+ phase: `execution.issue_artifact.${args.artifactId}`,
1254
+ unitId: args.artifactId,
1255
+ unitKind: "issue_artifact",
1256
+ packetPath: seat.packet_path,
1257
+ outputPath: seat.output_path,
1258
+ humanMessage: failureMessage,
1259
+ error: lastValidationError,
1260
+ });
1261
+ }
1262
+ catch (error) {
1263
+ throw new ReviewIssueArtifactDispatchError(`${failureMessage}: ${errorMessage(error)}`, failedOutcome, error);
1264
+ }
1265
+ }
1266
+ async function runControlledLensDeliberation(args) {
1267
+ const { projectRoot, sessionRoot, executionPlan, lensExecutorConfig, teamleadExecutorConfig, successfulLensDispatches, maxConcurrentLenses, unitTimeoutMs, issueArtifactContext, } = args;
1268
+ if (executionPlan.deliberation_mode !== "controlled-lens-deliberation") {
1269
+ throw new Error(`Unsupported review deliberation mode: ${executionPlan.deliberation_mode}`);
1270
+ }
1271
+ await appendExecutionProgress(executionPlan.error_log_path, "runner controlled lens deliberation started", [
1272
+ `deliberation_mode: ${executionPlan.deliberation_mode}`,
1273
+ `participating_lens_count: ${successfulLensDispatches.length}`,
1274
+ ]);
1275
+ const lensOutputs = await readLensOutputsForDeliberation(successfulLensDispatches);
1276
+ const lensOutputById = new Map(lensOutputs.map((lensOutput) => [lensOutput.lens_id, lensOutput]));
1277
+ const deliberationDispatches = successfulLensDispatches.map((dispatch) => {
1278
+ const seat = requireDeliberationSeat(executionPlan, dispatch.unit_id);
1279
+ return {
1280
+ unit_id: `deliberation-${dispatch.unit_id}`,
1281
+ unit_kind: "deliberation",
1282
+ packet_path: seat.packet_path,
1283
+ output_path: seat.output_path,
1284
+ };
1285
+ });
1286
+ await emitReviewProgress({
1287
+ executionPlan,
1288
+ step: 9,
1289
+ label: "lens deliberation responses",
1290
+ details: [`participating_lens_count=${deliberationDispatches.length}`],
1291
+ });
1292
+ for (const dispatch of deliberationDispatches) {
1293
+ const lensId = dispatch.unit_id.replace(/^deliberation-/, "");
1294
+ const ownOutput = lensOutputById.get(lensId);
1295
+ if (!ownOutput) {
1296
+ throw new Error(`Missing primary lens output for deliberation: ${lensId}`);
1297
+ }
1298
+ const otherOutputs = lensOutputs.filter((lens) => lens.lens_id !== lensId);
1299
+ const packetText = buildLensControlledDeliberationPrompt({
1300
+ session_id: executionPlan.session_id,
1301
+ lens_id: lensId,
1302
+ output_path: dispatch.output_path,
1303
+ own_output: ownOutput,
1304
+ other_outputs: otherOutputs,
1305
+ ...(issueArtifactContext ? { issue_artifact_context: issueArtifactContext } : {}),
1306
+ });
1307
+ await fs.mkdir(path.dirname(dispatch.packet_path), { recursive: true });
1308
+ await fs.writeFile(dispatch.packet_path, `${packetText.trimEnd()}\n`, "utf8");
1309
+ await registerGeneratedPromptPacketRefForDispatch({
1310
+ executionPlan,
1311
+ consumerId: `deliberation:${lensId}`,
1312
+ packetPath: dispatch.packet_path,
1313
+ });
1314
+ }
1315
+ const deliberationOutcomes = new Array(deliberationDispatches.length);
1316
+ let nextDeliberationIndex = 0;
1317
+ async function runDeliberationWorker() {
1318
+ while (true) {
1319
+ const currentIndex = nextDeliberationIndex;
1320
+ nextDeliberationIndex += 1;
1321
+ if (currentIndex >= deliberationDispatches.length)
1322
+ return;
1323
+ const dispatch = deliberationDispatches[currentIndex];
1324
+ if (!dispatch)
1325
+ return;
1326
+ deliberationOutcomes[currentIndex] = await runSingleDispatchWithRetries({
1327
+ projectRoot,
1328
+ sessionRoot,
1329
+ executionPlan,
1330
+ executorConfig: lensExecutorConfig,
1331
+ dispatch,
1332
+ maxRetries: DEFAULT_LENS_MAX_RETRIES,
1333
+ retryInitialDelayMs: DEFAULT_LENS_RETRY_INITIAL_DELAY_MS,
1334
+ unitTimeoutMs,
1335
+ });
1336
+ }
1337
+ }
1338
+ await Promise.all(Array.from({ length: Math.min(maxConcurrentLenses, deliberationDispatches.length) }, async () => runDeliberationWorker()));
1339
+ const completedDeliberationOutcomes = deliberationOutcomes.filter((outcome) => outcome !== undefined);
1340
+ const failedDeliberation = completedDeliberationOutcomes.find((outcome) => !outcome.success);
1341
+ if (failedDeliberation?.failure) {
1342
+ throw new ReviewControlledDeliberationDispatchError(`Controlled lens deliberation failed for ${failedDeliberation.dispatch.unit_id}: ${failedDeliberation.failure.message}`, completedDeliberationOutcomes, failedDeliberation);
1343
+ }
1344
+ const lensDeliberationResponses = await Promise.all(deliberationDispatches.map(async (dispatch) => {
1345
+ const lensId = dispatch.unit_id.replace(/^deliberation-/, "");
1346
+ return {
1347
+ lens_id: lensId,
1348
+ response_path: dispatch.output_path,
1349
+ content: await fs.readFile(dispatch.output_path, "utf8"),
1350
+ };
1351
+ }));
1352
+ const teamleadPacketText = buildTeamleadControlledDeliberationPrompt({
1353
+ session_id: executionPlan.session_id,
1354
+ output_path: executionPlan.deliberation_output_path,
1355
+ lens_outputs: lensOutputs,
1356
+ lens_deliberation_responses: lensDeliberationResponses,
1357
+ ...(issueArtifactContext ? { issue_artifact_context: issueArtifactContext } : {}),
1358
+ });
1359
+ await fs.writeFile(executionPlan.teamlead_deliberation_prompt_packet_path, `${teamleadPacketText.trimEnd()}\n`, "utf8");
1360
+ await registerGeneratedPromptPacketRefForDispatch({
1361
+ executionPlan,
1362
+ consumerId: "controlled-deliberation",
1363
+ packetPath: executionPlan.teamlead_deliberation_prompt_packet_path,
1364
+ });
1365
+ const teamleadDispatch = {
1366
+ unit_id: "controlled-deliberation",
1367
+ unit_kind: "deliberation",
1368
+ packet_path: executionPlan.teamlead_deliberation_prompt_packet_path,
1369
+ output_path: executionPlan.deliberation_output_path,
1370
+ };
1371
+ await emitReviewProgress({
1372
+ executionPlan,
1373
+ step: 10,
1374
+ label: "teamlead controlled deliberation",
1375
+ details: [`output_path=${executionPlan.deliberation_output_path}`],
1376
+ });
1377
+ const teamleadOutcome = await runSingleDispatchWithRetries({
1378
+ projectRoot,
1379
+ sessionRoot,
1380
+ executionPlan,
1381
+ executorConfig: teamleadExecutorConfig,
1382
+ dispatch: teamleadDispatch,
1383
+ maxRetries: 1,
1384
+ retryInitialDelayMs: DEFAULT_LENS_RETRY_INITIAL_DELAY_MS,
1385
+ unitTimeoutMs,
1386
+ });
1387
+ if (!teamleadOutcome.success) {
1388
+ throw new ReviewControlledDeliberationDispatchError(`Teamlead controlled deliberation failed: ${teamleadOutcome.failure?.message ?? "unknown error"}`, [...completedDeliberationOutcomes, teamleadOutcome], teamleadOutcome);
1389
+ }
1390
+ await appendExecutionProgress(executionPlan.error_log_path, "runner controlled lens deliberation completed", [
1391
+ `deliberation_output_path: ${executionPlan.deliberation_output_path}`,
1392
+ `lens_deliberation_response_count: ${deliberationDispatches.length}`,
1393
+ ]);
1394
+ return {
1395
+ deliberationDispatches,
1396
+ deliberationOutcomes: completedDeliberationOutcomes,
1397
+ teamleadOutcome,
1398
+ };
1399
+ }
1400
+ export async function executeReviewPromptExecution(params) {
1401
+ const projectRoot = path.resolve(params.projectRoot);
1402
+ const sessionRoot = path.resolve(params.sessionRoot);
1403
+ const executionPlanPath = path.join(sessionRoot, "execution-plan.yaml");
1404
+ const executionPlan = await readYamlDocument(executionPlanPath);
1405
+ const executionStartedAtMs = Date.now();
1406
+ await resetExecutionOutputs(executionPlan);
1407
+ await pruneGeneratedPromptPacketRefs(executionPlan);
1408
+ await ensureReviewContextManifestReadyForDispatch(executionPlan);
1409
+ await emitReviewProgress({
1410
+ executionPlan,
1411
+ step: 1,
1412
+ label: "load execution plan",
1413
+ details: [`session_id=${executionPlan.session_id}`],
1414
+ });
1415
+ await appendMarkdownLogEntry(executionPlan.error_log_path, "runner boundary state", renderEffectiveBoundaryStateLog(executionPlan.effective_boundary_state));
1416
+ await emitReviewProgress({
1417
+ executionPlan,
1418
+ step: 2,
1419
+ label: "record effective boundary",
1420
+ details: [
1421
+ `web=${executionPlan.effective_boundary_state.web_research.effective_policy}`,
1422
+ `repo=${executionPlan.effective_boundary_state.repo_exploration.effective_policy}`,
1423
+ ],
1424
+ });
1425
+ const defaultExecutorConfig = params.defaultExecutorConfig;
1426
+ const teamleadExecutorConfig = params.teamleadExecutorConfig ?? defaultExecutorConfig;
1427
+ const synthesizeExecutorConfig = params.synthesizeExecutorConfig ?? defaultExecutorConfig;
1428
+ const unitTimeoutMs = params.unitTimeoutMs ?? DEFAULT_REVIEW_UNIT_TIMEOUT_MS;
1429
+ const lensDispatches = executionPlan.lens_prompt_packet_seats.map((seat) => ({
1430
+ unit_id: seat.lens_id,
1431
+ unit_kind: "lens",
1432
+ packet_path: seat.packet_path,
1433
+ output_path: seat.output_path,
1434
+ }));
1435
+ const maxConcurrentLenses = Math.max(1, lensDispatches.length);
1436
+ const observedDispatchWidth = lensDispatches.length;
1437
+ await emitReviewProgress({
1438
+ executionPlan,
1439
+ step: 3,
1440
+ label: "isolated lens execution",
1441
+ details: [
1442
+ `planned_lens_count=${lensDispatches.length}`,
1443
+ `max_concurrent=${maxConcurrentLenses}`,
1444
+ ],
1445
+ });
1446
+ console.log(`[review runner] parallel lens dispatch enabled: max_concurrent=${maxConcurrentLenses}`);
1447
+ await appendExecutionProgress(executionPlan.error_log_path, "runner parallel dispatch policy", [`max_concurrent_lenses: ${maxConcurrentLenses}`]);
1448
+ const staggerDelayMs = defaultStaggerDelayMsForExecutorConfig(defaultExecutorConfig);
1449
+ const maxRetries = DEFAULT_LENS_MAX_RETRIES;
1450
+ const retryInitialDelayMs = DEFAULT_LENS_RETRY_INITIAL_DELAY_MS;
1451
+ if (staggerDelayMs > 0) {
1452
+ console.log(`[review runner] stagger delay: ${staggerDelayMs}ms between successive lens dispatches`);
1453
+ }
1454
+ const executionOutcomes = new Array(lensDispatches.length);
1455
+ let nextLensIndex = 0;
1456
+ async function runLensWorker(workerIndex) {
1457
+ // Stagger initial dispatch to avoid thundering-herd on external APIs.
1458
+ // Only the very first dispatch of each worker is staggered; subsequent
1459
+ // picks (after a lens completes) are not staggered since the burst has
1460
+ // already been spread out by then.
1461
+ if (staggerDelayMs > 0 && workerIndex > 0) {
1462
+ await sleep(workerIndex * staggerDelayMs);
1463
+ }
1464
+ while (true) {
1465
+ const currentIndex = nextLensIndex;
1466
+ nextLensIndex += 1;
1467
+ if (currentIndex >= lensDispatches.length) {
1468
+ return;
1469
+ }
1470
+ const dispatch = lensDispatches[currentIndex];
1471
+ if (!dispatch) {
1472
+ return;
1473
+ }
1474
+ console.log(`[review runner] starting ${dispatch.unit_kind}: ${dispatch.unit_id}`);
1475
+ await appendExecutionProgress(executionPlan.error_log_path, `runner dispatch started: ${dispatch.unit_id}`, [
1476
+ `unit_id: ${dispatch.unit_id}`,
1477
+ `unit_kind: ${dispatch.unit_kind}`,
1478
+ `packet_path: ${dispatch.packet_path}`,
1479
+ `output_path: ${dispatch.output_path}`,
1480
+ ]);
1481
+ const startedAtMs = Date.now();
1482
+ let lastError = undefined;
1483
+ let succeeded = false;
1484
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
1485
+ try {
1486
+ await invokeExecutor(defaultExecutorConfig, projectRoot, sessionRoot, dispatch, unitTimeoutMs);
1487
+ succeeded = true;
1488
+ break;
1489
+ }
1490
+ catch (error) {
1491
+ lastError = error;
1492
+ if (!isReviewUnitTimeoutError(error) && attempt < maxRetries) {
1493
+ const retryDelay = retryInitialDelayMs * (attempt + 1);
1494
+ console.log(`[review runner] ${dispatch.unit_id} attempt ${attempt + 1} failed, retrying in ${retryDelay}ms...`);
1495
+ await appendExecutionProgress(executionPlan.error_log_path, `runner dispatch retry: ${dispatch.unit_id}`, [
1496
+ `attempt: ${attempt + 1}/${maxRetries}`,
1497
+ `retry_delay_ms: ${retryDelay}`,
1498
+ `error: ${error instanceof Error ? error.message.slice(0, 200) : String(error).slice(0, 200)}`,
1499
+ ]);
1500
+ await sleep(retryDelay);
1501
+ }
1502
+ if (isReviewUnitTimeoutError(error))
1503
+ break;
1504
+ }
1505
+ }
1506
+ if (succeeded) {
1507
+ const completedAtMs = Date.now();
1508
+ console.log(`[review runner] completed ${dispatch.unit_kind}: ${dispatch.unit_id}`);
1509
+ await appendExecutionProgress(executionPlan.error_log_path, `runner dispatch completed: ${dispatch.unit_id}`, [
1510
+ `unit_id: ${dispatch.unit_id}`,
1511
+ `unit_kind: ${dispatch.unit_kind}`,
1512
+ `output_path: ${dispatch.output_path}`,
1513
+ ]);
1514
+ executionOutcomes[currentIndex] = {
1515
+ dispatch,
1516
+ success: true,
1517
+ startedAtMs,
1518
+ completedAtMs,
1519
+ };
1520
+ }
1521
+ else {
1522
+ const completedAtMs = Date.now();
1523
+ const failure = {
1524
+ unit_id: dispatch.unit_id,
1525
+ unit_kind: dispatch.unit_kind,
1526
+ packet_path: dispatch.packet_path,
1527
+ output_path: dispatch.output_path,
1528
+ message: lastError instanceof Error ? lastError.message : String(lastError),
1529
+ };
1530
+ await removeFileIfExists(dispatch.output_path);
1531
+ await appendExecutionFailure(executionPlan.error_log_path, failure, executionPlan.effective_boundary_state);
1532
+ executionOutcomes[currentIndex] = {
1533
+ dispatch,
1534
+ success: false,
1535
+ startedAtMs,
1536
+ completedAtMs,
1537
+ failure,
1538
+ };
1539
+ }
1540
+ }
1541
+ }
1542
+ if (params.reviewExecutionProfile?.mode === "nested-workers" &&
1543
+ params.reviewExecutionProfile.worker_executor === "codex") {
1544
+ console.log("[review runner] mode=nested-workers worker_executor=codex");
1545
+ await appendExecutionProgress(executionPlan.error_log_path, "runner profile dispatch: nested-workers", [
1546
+ `teamlead_seat: ${params.reviewExecutionProfile.teamlead.seat}`,
1547
+ `lens_seat: ${params.reviewExecutionProfile.lens.seat}`,
1548
+ `worker_executor: ${params.reviewExecutionProfile.worker_executor}`,
1549
+ `planned_lens_count: ${lensDispatches.length}`,
1550
+ ]);
1551
+ const nestedStartedAtMs = Date.now();
1552
+ const nestedResult = await executeReviewViaCodexNested({
1553
+ sessionRoot,
1554
+ projectRoot,
1555
+ ontoConfig: params.ontoConfig ?? {},
1556
+ });
1557
+ const nestedCompletedAtMs = Date.now();
1558
+ // Map nested-dispatch outcomes into executionOutcomes[] in lensDispatches order.
1559
+ // `participating_lens_ids` is the authoritative success set (orchestrator
1560
+ // ok AND output file exists + non-empty). Missing / failed → record as
1561
+ // ExecutionFailure; also remove empty output files for consistency with
1562
+ // worker-pool cleanup path.
1563
+ for (let i = 0; i < lensDispatches.length; i += 1) {
1564
+ const dispatch = lensDispatches[i];
1565
+ const reported = nestedResult.nested_raw.outcomes[i];
1566
+ const participating = nestedResult.participating_lens_ids.includes(dispatch.unit_id);
1567
+ if (participating) {
1568
+ console.log(`[review runner] completed ${dispatch.unit_kind}: ${dispatch.unit_id}`);
1569
+ await appendExecutionProgress(executionPlan.error_log_path, `runner nested dispatch completed: ${dispatch.unit_id}`, [
1570
+ `unit_id: ${dispatch.unit_id}`,
1571
+ `unit_kind: ${dispatch.unit_kind}`,
1572
+ `output_path: ${dispatch.output_path}`,
1573
+ ]);
1574
+ executionOutcomes[i] = {
1575
+ dispatch,
1576
+ success: true,
1577
+ startedAtMs: nestedStartedAtMs,
1578
+ completedAtMs: nestedCompletedAtMs,
1579
+ };
1580
+ }
1581
+ else {
1582
+ const message = reported?.status === "fail" && reported.error
1583
+ ? reported.error
1584
+ : nestedResult.halt_reason ??
1585
+ "nested worker dispatch failed (output missing or orchestrator rejected)";
1586
+ const failure = {
1587
+ unit_id: dispatch.unit_id,
1588
+ unit_kind: dispatch.unit_kind,
1589
+ packet_path: dispatch.packet_path,
1590
+ output_path: dispatch.output_path,
1591
+ message,
1592
+ };
1593
+ await removeFileIfExists(dispatch.output_path);
1594
+ await appendExecutionFailure(executionPlan.error_log_path, failure, executionPlan.effective_boundary_state);
1595
+ executionOutcomes[i] = {
1596
+ dispatch,
1597
+ success: false,
1598
+ startedAtMs: nestedStartedAtMs,
1599
+ completedAtMs: nestedCompletedAtMs,
1600
+ failure,
1601
+ };
1602
+ }
1603
+ }
1604
+ // Capture outer teamlead halt_reason for the post-dispatch halt check
1605
+ // (synthesize may still run if enough lenses participated, matching the
1606
+ // worker-pool semantics).
1607
+ if (nestedResult.halt_reason) {
1608
+ await appendExecutionProgress(executionPlan.error_log_path, "runner nested teamlead halt", [nestedResult.halt_reason]);
1609
+ }
1610
+ }
1611
+ else {
1612
+ await Promise.all(Array.from({ length: Math.min(maxConcurrentLenses, lensDispatches.length) }, async (_, workerIndex) => runLensWorker(workerIndex)));
1613
+ }
1614
+ const successfulLensDispatches = executionOutcomes
1615
+ .filter(isSuccessfulOutcome)
1616
+ .map((outcome) => outcome.dispatch);
1617
+ const executionFailures = executionOutcomes
1618
+ .filter(isFailureOutcome)
1619
+ .map((outcome) => outcome.failure);
1620
+ const minimumParticipatingLenses = resolveRequiredParticipatingLensCount(executionPlan);
1621
+ const lensCompletionBarrier = await writeLensCompletionBarrier({
1622
+ executionPlan,
1623
+ observedDispatchWidth,
1624
+ minimumParticipatingLenses,
1625
+ lensDispatches,
1626
+ successfulLensDispatches,
1627
+ executionFailures,
1628
+ });
1629
+ if (!lensCompletionBarrier.downstream_allowed) {
1630
+ const haltReason = successfulLensDispatches.length === 0
1631
+ ? "No participating lens outputs were produced."
1632
+ : `Selected lens completion barrier failed: ${lensCompletionBarrier.completed_lens_ids.length}/${lensCompletionBarrier.planned_lens_ids.length} planned lenses completed.`;
1633
+ await appendMarkdownLogEntry(executionPlan.error_log_path, "runner halted before synthesize", `${haltReason}\n\n[effective_boundary_state]\n${renderEffectiveBoundaryStateLog(executionPlan.effective_boundary_state)}`);
1634
+ const degradedLensIds = executionFailures
1635
+ .filter((failure) => failure.unit_kind === "lens")
1636
+ .map((failure) => failure.unit_id);
1637
+ const executionCompletedAtMs = Date.now();
1638
+ await writeExecutionResultArtifact(executionPlan, {
1639
+ session_id: executionPlan.session_id,
1640
+ session_root: sessionRoot,
1641
+ execution_realization: executionPlan.execution_realization,
1642
+ host_runtime: executionPlan.host_runtime,
1643
+ review_mode: executionPlan.review_mode,
1644
+ execution_status: deriveExecutionStatus({
1645
+ synthesisExecuted: false,
1646
+ degradedLensIds,
1647
+ }),
1648
+ execution_started_at: isoFromTimestamp(executionStartedAtMs),
1649
+ execution_completed_at: isoFromTimestamp(executionCompletedAtMs),
1650
+ total_duration_ms: Math.max(0, executionCompletedAtMs - executionStartedAtMs),
1651
+ max_concurrent_lenses: maxConcurrentLenses,
1652
+ observed_dispatch_width: observedDispatchWidth,
1653
+ planned_lens_ids: lensDispatches.map((dispatch) => dispatch.unit_id),
1654
+ participating_lens_ids: successfulLensDispatches.map((dispatch) => dispatch.unit_id),
1655
+ degraded_lens_ids: degradedLensIds,
1656
+ excluded_lens_ids: lensDispatches
1657
+ .map((dispatch) => dispatch.unit_id)
1658
+ .filter((lensId) => !successfulLensDispatches.some((dispatch) => dispatch.unit_id === lensId) &&
1659
+ !degradedLensIds.includes(lensId)),
1660
+ executed_lens_count: successfulLensDispatches.length,
1661
+ synthesis_executed: false,
1662
+ deliberation_status: "not_performed",
1663
+ halt_reason: haltReason,
1664
+ ...haltArtifactFields("lens_completion_barrier", null),
1665
+ error_log_path: executionPlan.error_log_path,
1666
+ lens_completion_barrier_ref: executionPlan.lens_completion_barrier_path ??
1667
+ path.join(sessionRoot, "lens-completion-barrier.yaml"),
1668
+ lens_execution_results: executionOutcomes
1669
+ .filter((outcome) => outcome !== undefined)
1670
+ .map(toUnitExecutionResult),
1671
+ synthesize_execution_result: null,
1672
+ }, params.reviewExecutionProfile);
1673
+ return {
1674
+ session_root: sessionRoot,
1675
+ executed_lens_count: successfulLensDispatches.length,
1676
+ synthesis_output_path: executionPlan.synthesis_output_path,
1677
+ participating_lens_ids: successfulLensDispatches.map((dispatch) => dispatch.unit_id),
1678
+ degraded_lens_ids: executionFailures
1679
+ .filter((failure) => failure.unit_kind === "lens")
1680
+ .map((failure) => failure.unit_id),
1681
+ synthesis_executed: false,
1682
+ error_log_path: executionPlan.error_log_path,
1683
+ halt_reason: haltReason,
1684
+ ...haltArtifactFields("lens_completion_barrier", null),
1685
+ };
1686
+ }
1687
+ const issueArtifactOutcomes = [];
1688
+ const lensOutputPaths = successfulLensDispatches.map((dispatch) => dispatch.output_path);
1689
+ const haltAfterIssueArtifactFailure = async (args) => {
1690
+ const failureOutcome = issueArtifactOutcomeFromError(args.error);
1691
+ if (failureOutcome &&
1692
+ !issueArtifactOutcomes.some((outcome) => outcome.dispatch.unit_id === failureOutcome.dispatch.unit_id)) {
1693
+ issueArtifactOutcomes.push(failureOutcome);
1694
+ }
1695
+ const failureMessage = errorMessage(args.error);
1696
+ await appendMarkdownLogEntry(executionPlan.error_log_path, "runner halted during issue artifact generation", failureMessage);
1697
+ const degradedLensIds = executionFailures
1698
+ .filter((failure) => failure.unit_kind === "lens")
1699
+ .map((failure) => failure.unit_id);
1700
+ const executionCompletedAtMs = Date.now();
1701
+ const haltReason = `Issue artifact generation failed: ${failureMessage}`;
1702
+ await writeExecutionResultArtifact(executionPlan, {
1703
+ session_id: executionPlan.session_id,
1704
+ session_root: sessionRoot,
1705
+ execution_realization: executionPlan.execution_realization,
1706
+ host_runtime: executionPlan.host_runtime,
1707
+ review_mode: executionPlan.review_mode,
1708
+ execution_status: deriveExecutionStatus({
1709
+ synthesisExecuted: false,
1710
+ degradedLensIds,
1711
+ }),
1712
+ execution_started_at: isoFromTimestamp(executionStartedAtMs),
1713
+ execution_completed_at: isoFromTimestamp(executionCompletedAtMs),
1714
+ total_duration_ms: Math.max(0, executionCompletedAtMs - executionStartedAtMs),
1715
+ max_concurrent_lenses: maxConcurrentLenses,
1716
+ observed_dispatch_width: observedDispatchWidth,
1717
+ planned_lens_ids: lensDispatches.map((dispatch) => dispatch.unit_id),
1718
+ participating_lens_ids: successfulLensDispatches.map((dispatch) => dispatch.unit_id),
1719
+ degraded_lens_ids: degradedLensIds,
1720
+ excluded_lens_ids: lensDispatches
1721
+ .map((dispatch) => dispatch.unit_id)
1722
+ .filter((lensId) => !successfulLensDispatches.some((dispatch) => dispatch.unit_id === lensId) &&
1723
+ !degradedLensIds.includes(lensId)),
1724
+ executed_lens_count: successfulLensDispatches.length,
1725
+ synthesis_executed: false,
1726
+ deliberation_status: args.deliberationStatus,
1727
+ halt_reason: haltReason,
1728
+ ...haltArtifactFields("issue_artifact", failureOutcome),
1729
+ error_log_path: executionPlan.error_log_path,
1730
+ lens_completion_barrier_ref: executionPlan.lens_completion_barrier_path ??
1731
+ path.join(sessionRoot, "lens-completion-barrier.yaml"),
1732
+ lens_execution_results: executionOutcomes
1733
+ .filter((outcome) => outcome !== undefined)
1734
+ .map(toUnitExecutionResult),
1735
+ issue_artifact_execution_results: issueArtifactOutcomes.map(toUnitExecutionResult),
1736
+ deliberation_execution_results: args.deliberationExecutionResults?.map(toUnitExecutionResult) ?? [],
1737
+ synthesize_execution_result: null,
1738
+ }, params.reviewExecutionProfile);
1739
+ const originalError = args.error instanceof ReviewIssueArtifactDispatchError
1740
+ ? args.error.originalError
1741
+ : args.error;
1742
+ if (originalError instanceof ReviewStructuredFailureError) {
1743
+ throw originalError;
1744
+ }
1745
+ return {
1746
+ session_root: sessionRoot,
1747
+ executed_lens_count: successfulLensDispatches.length,
1748
+ synthesis_output_path: executionPlan.synthesis_output_path,
1749
+ participating_lens_ids: successfulLensDispatches.map((dispatch) => dispatch.unit_id),
1750
+ degraded_lens_ids: degradedLensIds,
1751
+ synthesis_executed: false,
1752
+ error_log_path: executionPlan.error_log_path,
1753
+ halt_reason: haltReason,
1754
+ ...haltArtifactFields("issue_artifact", failureOutcome),
1755
+ };
1756
+ };
1757
+ for (const artifactId of PRE_DELIBERATION_ISSUE_ARTIFACT_IDS) {
1758
+ try {
1759
+ issueArtifactOutcomes.push(await runIssueArtifactDispatch({
1760
+ projectRoot,
1761
+ sessionRoot,
1762
+ executionPlan,
1763
+ executorConfig: teamleadExecutorConfig,
1764
+ artifactId,
1765
+ lensOutputPaths,
1766
+ unitTimeoutMs,
1767
+ }));
1768
+ }
1769
+ catch (error) {
1770
+ return haltAfterIssueArtifactFailure({
1771
+ error,
1772
+ deliberationStatus: "not_performed",
1773
+ });
1774
+ }
1775
+ }
1776
+ const issueArtifactContext = await renderIssueArtifactContext({
1777
+ projectRoot,
1778
+ executionPlan,
1779
+ });
1780
+ let controlledDeliberation;
1781
+ try {
1782
+ controlledDeliberation = await runControlledLensDeliberation({
1783
+ projectRoot,
1784
+ sessionRoot,
1785
+ executionPlan,
1786
+ lensExecutorConfig: defaultExecutorConfig,
1787
+ teamleadExecutorConfig,
1788
+ successfulLensDispatches,
1789
+ maxConcurrentLenses,
1790
+ unitTimeoutMs,
1791
+ issueArtifactContext,
1792
+ });
1793
+ }
1794
+ catch (error) {
1795
+ const failureMessage = error instanceof Error ? error.message : String(error);
1796
+ const deliberationExecutionOutcomes = controlledDeliberationOutcomesFromError(error);
1797
+ const failedDeliberationOutcome = controlledDeliberationFailedOutcomeFromError(error);
1798
+ await appendMarkdownLogEntry(executionPlan.error_log_path, "runner halted during controlled deliberation", [
1799
+ failureMessage,
1800
+ "",
1801
+ "halt_phase: controlled_lens_deliberation",
1802
+ `halt_unit_id: ${failedDeliberationOutcome?.dispatch.unit_id ?? "unknown"}`,
1803
+ `halt_unit_kind: ${failedDeliberationOutcome?.dispatch.unit_kind ?? "unknown"}`,
1804
+ `halt_lens_id: ${haltLensIdFromOutcome(failedDeliberationOutcome) ?? "none"}`,
1805
+ ].join("\n"));
1806
+ const degradedLensIds = executionFailures
1807
+ .filter((failure) => failure.unit_kind === "lens")
1808
+ .map((failure) => failure.unit_id);
1809
+ const executionCompletedAtMs = Date.now();
1810
+ await writeExecutionResultArtifact(executionPlan, {
1811
+ session_id: executionPlan.session_id,
1812
+ session_root: sessionRoot,
1813
+ execution_realization: executionPlan.execution_realization,
1814
+ host_runtime: executionPlan.host_runtime,
1815
+ review_mode: executionPlan.review_mode,
1816
+ execution_status: deriveExecutionStatus({
1817
+ synthesisExecuted: false,
1818
+ degradedLensIds,
1819
+ }),
1820
+ execution_started_at: isoFromTimestamp(executionStartedAtMs),
1821
+ execution_completed_at: isoFromTimestamp(executionCompletedAtMs),
1822
+ total_duration_ms: Math.max(0, executionCompletedAtMs - executionStartedAtMs),
1823
+ max_concurrent_lenses: maxConcurrentLenses,
1824
+ observed_dispatch_width: observedDispatchWidth,
1825
+ planned_lens_ids: lensDispatches.map((dispatch) => dispatch.unit_id),
1826
+ participating_lens_ids: successfulLensDispatches.map((dispatch) => dispatch.unit_id),
1827
+ degraded_lens_ids: degradedLensIds,
1828
+ excluded_lens_ids: lensDispatches
1829
+ .map((dispatch) => dispatch.unit_id)
1830
+ .filter((lensId) => !successfulLensDispatches.some((dispatch) => dispatch.unit_id === lensId) &&
1831
+ !degradedLensIds.includes(lensId)),
1832
+ executed_lens_count: successfulLensDispatches.length,
1833
+ synthesis_executed: false,
1834
+ deliberation_status: "not_performed",
1835
+ halt_reason: `Controlled lens deliberation failed: ${failureMessage}`,
1836
+ ...haltArtifactFields("controlled_lens_deliberation", failedDeliberationOutcome),
1837
+ error_log_path: executionPlan.error_log_path,
1838
+ lens_completion_barrier_ref: executionPlan.lens_completion_barrier_path ??
1839
+ path.join(sessionRoot, "lens-completion-barrier.yaml"),
1840
+ lens_execution_results: executionOutcomes
1841
+ .filter((outcome) => outcome !== undefined)
1842
+ .map(toUnitExecutionResult),
1843
+ issue_artifact_execution_results: issueArtifactOutcomes.map(toUnitExecutionResult),
1844
+ deliberation_execution_results: deliberationExecutionOutcomes.map(toUnitExecutionResult),
1845
+ synthesize_execution_result: null,
1846
+ }, params.reviewExecutionProfile);
1847
+ return {
1848
+ session_root: sessionRoot,
1849
+ executed_lens_count: successfulLensDispatches.length,
1850
+ synthesis_output_path: executionPlan.synthesis_output_path,
1851
+ participating_lens_ids: successfulLensDispatches.map((dispatch) => dispatch.unit_id),
1852
+ degraded_lens_ids: degradedLensIds,
1853
+ synthesis_executed: false,
1854
+ error_log_path: executionPlan.error_log_path,
1855
+ halt_reason: `Controlled lens deliberation failed: ${failureMessage}`,
1856
+ ...haltArtifactFields("controlled_lens_deliberation", failedDeliberationOutcome),
1857
+ };
1858
+ }
1859
+ try {
1860
+ issueArtifactOutcomes.push(await runIssueArtifactDispatch({
1861
+ projectRoot,
1862
+ sessionRoot,
1863
+ executionPlan,
1864
+ executorConfig: teamleadExecutorConfig,
1865
+ artifactId: "problem-framing",
1866
+ lensOutputPaths,
1867
+ deliberationResponsePaths: controlledDeliberation.deliberationDispatches.map((dispatch) => dispatch.output_path),
1868
+ deliberationOutputPath: executionPlan.deliberation_output_path,
1869
+ problemFramingProfileRef: await resolveProblemFramingProfileRef({
1870
+ projectRoot,
1871
+ executionPlan,
1872
+ }),
1873
+ unitTimeoutMs,
1874
+ }));
1875
+ }
1876
+ catch (error) {
1877
+ return haltAfterIssueArtifactFailure({
1878
+ error,
1879
+ deliberationStatus: "performed",
1880
+ deliberationExecutionResults: [
1881
+ ...controlledDeliberation.deliberationOutcomes,
1882
+ controlledDeliberation.teamleadOutcome,
1883
+ ],
1884
+ });
1885
+ }
1886
+ const synthesizePacketRuntimePath = path.join(executionPlan.prompt_packets_root, "synthesize.runtime.prompt.md");
1887
+ const synthesizePacketText = await fs.readFile(executionPlan.synthesize_prompt_packet_path, "utf8");
1888
+ const enrichedSynthesizePacketText = `${synthesizePacketText.trimEnd()}\n\n${renderLensOutputRefsSection(projectRoot, successfulLensDispatches)}\n${renderControlledDeliberationRefsSection(projectRoot, executionPlan, controlledDeliberation.deliberationDispatches)}\n## Issue-Stance Closure Artifacts\n${renderIssueArtifactRefs(projectRoot, executionPlan, [
1889
+ "finding-ledger",
1890
+ "finding-relation-graph",
1891
+ "issue-ledger",
1892
+ "issue-stance-matrix",
1893
+ "deliberation-plan",
1894
+ "problem-framing",
1895
+ ])}\n\n${renderDegradedLensFailuresSection(executionFailures.filter((failure) => failure.unit_kind === "lens"))}`;
1896
+ await fs.writeFile(synthesizePacketRuntimePath, enrichedSynthesizePacketText.trimEnd() + "\n", "utf8");
1897
+ await registerGeneratedPromptPacketRefForDispatch({
1898
+ executionPlan,
1899
+ consumerId: "synthesize",
1900
+ packetPath: synthesizePacketRuntimePath,
1901
+ });
1902
+ const synthesizeDispatch = {
1903
+ unit_id: "synthesize",
1904
+ unit_kind: "synthesize",
1905
+ packet_path: synthesizePacketRuntimePath,
1906
+ output_path: executionPlan.synthesis_output_path,
1907
+ };
1908
+ await emitReviewProgress({
1909
+ executionPlan,
1910
+ step: 12,
1911
+ label: "synthesize and write execution result",
1912
+ details: [`participating_lens_count=${successfulLensDispatches.length}`],
1913
+ });
1914
+ console.log("[review runner] starting synthesize: synthesize");
1915
+ await appendExecutionProgress(executionPlan.error_log_path, "runner dispatch started: synthesize", [
1916
+ `unit_id: ${synthesizeDispatch.unit_id}`,
1917
+ `unit_kind: ${synthesizeDispatch.unit_kind}`,
1918
+ `packet_path: ${synthesizeDispatch.packet_path}`,
1919
+ `output_path: ${synthesizeDispatch.output_path}`,
1920
+ ]);
1921
+ const synthesizeStartedAtMs = Date.now();
1922
+ const synthesizeMaxRetries = 1;
1923
+ let synthesizeOutcome = null;
1924
+ let synthesizeLastError = undefined;
1925
+ let synthesizeSucceeded = false;
1926
+ for (let attempt = 0; attempt <= synthesizeMaxRetries; attempt++) {
1927
+ try {
1928
+ await invokeExecutor(synthesizeExecutorConfig, projectRoot, sessionRoot, synthesizeDispatch, unitTimeoutMs);
1929
+ synthesizeSucceeded = true;
1930
+ break;
1931
+ }
1932
+ catch (error) {
1933
+ synthesizeLastError = error;
1934
+ if (!isReviewUnitTimeoutError(error) && attempt < synthesizeMaxRetries) {
1935
+ const retryDelay = DEFAULT_LENS_RETRY_INITIAL_DELAY_MS;
1936
+ console.log(`[review runner] synthesize attempt ${attempt + 1} failed, retrying in ${retryDelay}ms...`);
1937
+ await appendExecutionProgress(executionPlan.error_log_path, "runner synthesize retry", [
1938
+ `attempt: ${attempt + 1}/${synthesizeMaxRetries}`,
1939
+ `retry_delay_ms: ${retryDelay}`,
1940
+ `error: ${error instanceof Error ? error.message.slice(0, 200) : String(error).slice(0, 200)}`,
1941
+ ]);
1942
+ await sleep(retryDelay);
1943
+ }
1944
+ if (isReviewUnitTimeoutError(error))
1945
+ break;
1946
+ }
1947
+ }
1948
+ if (synthesizeSucceeded) {
1949
+ synthesizeOutcome = {
1950
+ dispatch: synthesizeDispatch,
1951
+ success: true,
1952
+ startedAtMs: synthesizeStartedAtMs,
1953
+ completedAtMs: Date.now(),
1954
+ };
1955
+ }
1956
+ if (!synthesizeSucceeded) {
1957
+ const error = synthesizeLastError;
1958
+ const failure = {
1959
+ unit_id: synthesizeDispatch.unit_id,
1960
+ unit_kind: synthesizeDispatch.unit_kind,
1961
+ packet_path: synthesizeDispatch.packet_path,
1962
+ output_path: synthesizeDispatch.output_path,
1963
+ message: error instanceof Error ? error.message : String(error),
1964
+ };
1965
+ synthesizeOutcome = {
1966
+ dispatch: synthesizeDispatch,
1967
+ success: false,
1968
+ startedAtMs: synthesizeStartedAtMs,
1969
+ completedAtMs: Date.now(),
1970
+ failure,
1971
+ };
1972
+ executionFailures.push(failure);
1973
+ await removeFileIfExists(synthesizeDispatch.output_path);
1974
+ await appendExecutionFailure(executionPlan.error_log_path, failure, executionPlan.effective_boundary_state);
1975
+ const degradedLensIds = executionFailures
1976
+ .filter((recordedFailure) => recordedFailure.unit_kind === "lens")
1977
+ .map((recordedFailure) => recordedFailure.unit_id);
1978
+ const executionCompletedAtMs = Date.now();
1979
+ await writeExecutionResultArtifact(executionPlan, {
1980
+ session_id: executionPlan.session_id,
1981
+ session_root: sessionRoot,
1982
+ execution_realization: executionPlan.execution_realization,
1983
+ host_runtime: executionPlan.host_runtime,
1984
+ review_mode: executionPlan.review_mode,
1985
+ execution_status: deriveExecutionStatus({
1986
+ synthesisExecuted: false,
1987
+ degradedLensIds,
1988
+ }),
1989
+ execution_started_at: isoFromTimestamp(executionStartedAtMs),
1990
+ execution_completed_at: isoFromTimestamp(executionCompletedAtMs),
1991
+ total_duration_ms: Math.max(0, executionCompletedAtMs - executionStartedAtMs),
1992
+ max_concurrent_lenses: maxConcurrentLenses,
1993
+ observed_dispatch_width: observedDispatchWidth,
1994
+ planned_lens_ids: lensDispatches.map((dispatch) => dispatch.unit_id),
1995
+ participating_lens_ids: successfulLensDispatches.map((dispatch) => dispatch.unit_id),
1996
+ degraded_lens_ids: degradedLensIds,
1997
+ excluded_lens_ids: lensDispatches
1998
+ .map((dispatch) => dispatch.unit_id)
1999
+ .filter((lensId) => !successfulLensDispatches.some((dispatch) => dispatch.unit_id === lensId) &&
2000
+ !degradedLensIds.includes(lensId)),
2001
+ executed_lens_count: successfulLensDispatches.length,
2002
+ synthesis_executed: false,
2003
+ deliberation_status: "performed",
2004
+ halt_reason: `Synthesize execution failed: ${failure.message}`,
2005
+ ...haltArtifactFields("synthesize", synthesizeOutcome),
2006
+ error_log_path: executionPlan.error_log_path,
2007
+ lens_completion_barrier_ref: executionPlan.lens_completion_barrier_path ??
2008
+ path.join(sessionRoot, "lens-completion-barrier.yaml"),
2009
+ lens_execution_results: executionOutcomes
2010
+ .filter((outcome) => outcome !== undefined)
2011
+ .map(toUnitExecutionResult),
2012
+ issue_artifact_execution_results: issueArtifactOutcomes.map(toUnitExecutionResult),
2013
+ deliberation_execution_results: [
2014
+ ...controlledDeliberation.deliberationOutcomes,
2015
+ controlledDeliberation.teamleadOutcome,
2016
+ ].map(toUnitExecutionResult),
2017
+ synthesize_execution_result: synthesizeOutcome
2018
+ ? toUnitExecutionResult(synthesizeOutcome)
2019
+ : null,
2020
+ }, params.reviewExecutionProfile);
2021
+ return {
2022
+ session_root: sessionRoot,
2023
+ executed_lens_count: successfulLensDispatches.length,
2024
+ synthesis_output_path: executionPlan.synthesis_output_path,
2025
+ participating_lens_ids: successfulLensDispatches.map((dispatch) => dispatch.unit_id),
2026
+ degraded_lens_ids: executionFailures
2027
+ .filter((recordedFailure) => recordedFailure.unit_kind === "lens")
2028
+ .map((recordedFailure) => recordedFailure.unit_id),
2029
+ synthesis_executed: false,
2030
+ error_log_path: executionPlan.error_log_path,
2031
+ halt_reason: `Synthesize execution failed: ${failure.message}`,
2032
+ ...haltArtifactFields("synthesize", synthesizeOutcome),
2033
+ };
2034
+ }
2035
+ console.log("[review runner] completed synthesize: synthesize");
2036
+ await appendExecutionProgress(executionPlan.error_log_path, "runner dispatch completed: synthesize", [
2037
+ `unit_id: ${synthesizeDispatch.unit_id}`,
2038
+ `unit_kind: ${synthesizeDispatch.unit_kind}`,
2039
+ `output_path: ${synthesizeDispatch.output_path}`,
2040
+ ]);
2041
+ const degradedLensIds = executionFailures
2042
+ .filter((failure) => failure.unit_kind === "lens")
2043
+ .map((failure) => failure.unit_id);
2044
+ const executionCompletedAtMs = Date.now();
2045
+ const deliberationStatus = await readStructuredDeliberationStatus(executionPlan, executionPlan.synthesis_output_path).catch(async (error) => throwMalformedOutputFailure({
2046
+ executionPlan,
2047
+ phase: "execution.synthesize.validation",
2048
+ unitId: synthesizeDispatch.unit_id,
2049
+ unitKind: synthesizeDispatch.unit_kind,
2050
+ packetPath: synthesizeDispatch.packet_path,
2051
+ outputPath: synthesizeDispatch.output_path,
2052
+ humanMessage: "Synthesize output did not satisfy the controlled deliberation output contract.",
2053
+ error,
2054
+ }));
2055
+ await writeExecutionResultArtifact(executionPlan, {
2056
+ session_id: executionPlan.session_id,
2057
+ session_root: sessionRoot,
2058
+ execution_realization: executionPlan.execution_realization,
2059
+ host_runtime: executionPlan.host_runtime,
2060
+ review_mode: executionPlan.review_mode,
2061
+ execution_status: deriveExecutionStatus({
2062
+ synthesisExecuted: true,
2063
+ degradedLensIds,
2064
+ }),
2065
+ execution_started_at: isoFromTimestamp(executionStartedAtMs),
2066
+ execution_completed_at: isoFromTimestamp(executionCompletedAtMs),
2067
+ total_duration_ms: Math.max(0, executionCompletedAtMs - executionStartedAtMs),
2068
+ max_concurrent_lenses: maxConcurrentLenses,
2069
+ observed_dispatch_width: observedDispatchWidth,
2070
+ planned_lens_ids: lensDispatches.map((dispatch) => dispatch.unit_id),
2071
+ participating_lens_ids: successfulLensDispatches.map((dispatch) => dispatch.unit_id),
2072
+ degraded_lens_ids: degradedLensIds,
2073
+ excluded_lens_ids: lensDispatches
2074
+ .map((dispatch) => dispatch.unit_id)
2075
+ .filter((lensId) => !successfulLensDispatches.some((dispatch) => dispatch.unit_id === lensId) &&
2076
+ !degradedLensIds.includes(lensId)),
2077
+ executed_lens_count: successfulLensDispatches.length,
2078
+ synthesis_executed: true,
2079
+ deliberation_status: deliberationStatus,
2080
+ halt_reason: null,
2081
+ error_log_path: executionPlan.error_log_path,
2082
+ lens_completion_barrier_ref: executionPlan.lens_completion_barrier_path ??
2083
+ path.join(sessionRoot, "lens-completion-barrier.yaml"),
2084
+ lens_execution_results: executionOutcomes
2085
+ .filter((outcome) => outcome !== undefined)
2086
+ .map(toUnitExecutionResult),
2087
+ issue_artifact_execution_results: issueArtifactOutcomes.map(toUnitExecutionResult),
2088
+ deliberation_execution_results: [
2089
+ ...controlledDeliberation.deliberationOutcomes,
2090
+ controlledDeliberation.teamleadOutcome,
2091
+ ].map(toUnitExecutionResult),
2092
+ synthesize_execution_result: synthesizeOutcome
2093
+ ? toUnitExecutionResult(synthesizeOutcome)
2094
+ : null,
2095
+ }, params.reviewExecutionProfile);
2096
+ return {
2097
+ session_root: sessionRoot,
2098
+ executed_lens_count: successfulLensDispatches.length,
2099
+ synthesis_output_path: executionPlan.synthesis_output_path,
2100
+ participating_lens_ids: successfulLensDispatches.map((dispatch) => dispatch.unit_id),
2101
+ degraded_lens_ids: degradedLensIds,
2102
+ synthesis_executed: true,
2103
+ error_log_path: executionPlan.error_log_path,
2104
+ };
2105
+ }
2106
+ export async function runReviewPromptExecution(argv) {
2107
+ const { values } = parseArgs({
2108
+ options: {
2109
+ "project-root": { type: "string", default: "." },
2110
+ "session-root": { type: "string" },
2111
+ "executor-bin": { type: "string" },
2112
+ "executor-arg": { type: "string", multiple: true, default: [] },
2113
+ "synthesize-executor-bin": { type: "string" },
2114
+ "synthesize-executor-arg": { type: "string", multiple: true, default: [] },
2115
+ },
2116
+ strict: true,
2117
+ allowPositionals: false,
2118
+ args: argv,
2119
+ });
2120
+ const defaultExecutorConfig = {
2121
+ bin: requireString(values["executor-bin"], "executor-bin"),
2122
+ args: values["executor-arg"],
2123
+ };
2124
+ const synthesizeExecutorConfig = typeof values["synthesize-executor-bin"] === "string" &&
2125
+ values["synthesize-executor-bin"].length > 0
2126
+ ? {
2127
+ bin: values["synthesize-executor-bin"],
2128
+ args: values["synthesize-executor-arg"],
2129
+ }
2130
+ : defaultExecutorConfig;
2131
+ return executeReviewPromptExecution({
2132
+ projectRoot: requireString(values["project-root"], "project-root"),
2133
+ sessionRoot: requireString(values["session-root"], "session-root"),
2134
+ defaultExecutorConfig,
2135
+ synthesizeExecutorConfig,
2136
+ });
2137
+ }
2138
+ export async function runReviewPromptExecutionCli(argv) {
2139
+ const result = await runReviewPromptExecution(argv);
2140
+ console.log(JSON.stringify(result, null, 2));
2141
+ return 0;
2142
+ }
2143
+ async function main() {
2144
+ await printOntoReleaseChannelNotice();
2145
+ return runReviewPromptExecutionCli(process.argv.slice(2));
2146
+ }
2147
+ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
2148
+ main().then((exitCode) => process.exit(exitCode), (error) => {
2149
+ console.error(error instanceof Error ? error.message : String(error));
2150
+ process.exit(1);
2151
+ });
2152
+ }