onto-mcp 0.3.2 → 0.4.1

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 (300) hide show
  1. package/.onto/processes/reconstruct/actionable-ontology-seed-recomposition-design.md +447 -0
  2. package/.onto/processes/reconstruct/foundry-style-ontology-seed-contract.md +934 -0
  3. package/.onto/processes/reconstruct/reconstruct-boundary-contract.md +303 -725
  4. package/.onto/processes/reconstruct/reconstruct-contract-registry.yaml +1645 -0
  5. package/.onto/processes/reconstruct/reconstruct-execution-ux-contract.md +26 -22
  6. package/.onto/processes/reconstruct/source-profile-contract.md +49 -23
  7. package/.onto/processes/reconstruct/source-profiles/code.md +6 -3
  8. package/.onto/processes/reconstruct/source-profiles/database.md +5 -2
  9. package/.onto/processes/reconstruct/source-profiles/document.md +5 -2
  10. package/.onto/processes/reconstruct/source-profiles/spreadsheet.md +5 -4
  11. package/.onto/processes/review/review-execution-ux-contract.md +40 -0
  12. package/.onto/processes/shared/pipeline-execution-ledger-contract.md +26 -10
  13. package/.onto/processes/shared/target-material-kind-contract.md +29 -16
  14. package/AGENTS.md +6 -4
  15. package/README.md +149 -76
  16. package/dist/cli.js +8 -8
  17. package/dist/core-api/reconstruct-api.js +117 -31
  18. package/dist/core-api/review-api.js +47 -0
  19. package/dist/core-runtime/cli/codex-review-unit-executor.js +39 -2
  20. package/dist/core-runtime/cli/complete-review-session.js +2 -2
  21. package/dist/core-runtime/cli/mock-review-unit-executor.js +1 -1
  22. package/dist/core-runtime/cli/review-invoke.js +9 -9
  23. package/dist/core-runtime/cli/run-review-prompt-execution.js +39 -5
  24. package/dist/core-runtime/cli/spawn-watcher.js +266 -47
  25. package/dist/core-runtime/cli/start-review-session.js +3 -3
  26. package/dist/core-runtime/llm/llm-caller.js +11 -0
  27. package/dist/core-runtime/llm/llm-tool-loop.js +2 -0
  28. package/dist/core-runtime/observability/runtime-stream-observation.js +118 -0
  29. package/dist/core-runtime/onboard/cli-host.js +174 -0
  30. package/dist/core-runtime/onboard/host-target.js +22 -0
  31. package/dist/core-runtime/onboard/json-config-host.js +122 -0
  32. package/dist/core-runtime/onboard/path-scan.js +26 -0
  33. package/dist/core-runtime/onboard/prompt.js +51 -0
  34. package/dist/core-runtime/onboard/register.js +214 -0
  35. package/dist/core-runtime/onboard/types.js +27 -0
  36. package/dist/core-runtime/reconstruct/actionable-seed-validation.js +1777 -0
  37. package/dist/core-runtime/reconstruct/artifact-types.js +10 -4
  38. package/dist/core-runtime/reconstruct/contract-registry.js +623 -0
  39. package/dist/core-runtime/reconstruct/domain-id.js +10 -0
  40. package/dist/core-runtime/reconstruct/governing-snapshot.js +716 -0
  41. package/dist/core-runtime/reconstruct/material-profile-validation.js +191 -0
  42. package/dist/core-runtime/reconstruct/materialize-preparation.js +49 -11
  43. package/dist/core-runtime/reconstruct/pipeline-execution-ledger.js +269 -79
  44. package/dist/core-runtime/reconstruct/post-seed-validation.js +1194 -51
  45. package/dist/core-runtime/reconstruct/record.js +104 -20
  46. package/dist/core-runtime/reconstruct/run.js +2107 -413
  47. package/dist/core-runtime/reconstruct/seed-claim-projections.js +268 -0
  48. package/dist/core-runtime/reconstruct/source-profiles.js +93 -4
  49. package/dist/core-runtime/reconstruct/terminal-validation.js +807 -0
  50. package/dist/core-runtime/review/review-invocation-runner.js +4 -4
  51. package/dist/mcp/server.js +110 -38
  52. package/dist/mcp/tool-schemas.js +20 -6
  53. package/package.json +8 -17
  54. package/scripts/onto-review-watch.sh +486 -0
  55. package/scripts/onto-runtime-watch.sh +122 -0
  56. package/scripts/postinstall-hint.js +22 -0
  57. package/.onto/processes/reconstruct/top-level-concept-discovery-contract.md +0 -387
  58. package/dist/core-runtime/cli/bootstrap-review-binding.js +0 -186
  59. package/dist/core-runtime/cli/codex-nested-dispatch.test.js +0 -390
  60. package/dist/core-runtime/cli/codex-nested-teamlead-executor.test.js +0 -335
  61. package/dist/core-runtime/cli/coordinator-helpers.js +0 -583
  62. package/dist/core-runtime/cli/coordinator-state-machine-deliberation.test.js +0 -167
  63. package/dist/core-runtime/cli/coordinator-state-machine.js +0 -794
  64. package/dist/core-runtime/cli/e2e-codex-multi-agent-fixes.test.js +0 -615
  65. package/dist/core-runtime/cli/e2e-start-review-session.test.js +0 -312
  66. package/dist/core-runtime/cli/health.js +0 -44
  67. package/dist/core-runtime/cli/inline-http-review-unit-executor.test.js +0 -567
  68. package/dist/core-runtime/cli/materialize-review-execution-preparation.js +0 -104
  69. package/dist/core-runtime/cli/migrate-session-roots.js +0 -118
  70. package/dist/core-runtime/cli/repo-layout-migration-replace.smoke.test.js +0 -106
  71. package/dist/core-runtime/cli/review-invoke-auto-resolution.test.js +0 -268
  72. package/dist/core-runtime/cli/review-invoke-coordinator-topology.test.js +0 -136
  73. package/dist/core-runtime/cli/review-invoke-resolver-caching.test.js +0 -201
  74. package/dist/core-runtime/cli/review-invoke-topology-dispatch.test.js +0 -192
  75. package/dist/core-runtime/cli/session-root-guard.js +0 -168
  76. package/dist/core-runtime/cli/spawn-watcher.test.js +0 -457
  77. package/dist/core-runtime/cli/strip-wrapping-code-fence.test.js +0 -79
  78. package/dist/core-runtime/cli/teamcreate-lens-deliberation-executor.js +0 -412
  79. package/dist/core-runtime/cli/teamcreate-lens-deliberation-executor.test.js +0 -351
  80. package/dist/core-runtime/cli/topology-executor-mapping.js +0 -139
  81. package/dist/core-runtime/cli/topology-executor-mapping.test.js +0 -173
  82. package/dist/core-runtime/cli/write-review-interpretation.js +0 -81
  83. package/dist/core-runtime/config/onto-config-cli.js +0 -278
  84. package/dist/core-runtime/config/onto-config-key-path.js +0 -288
  85. package/dist/core-runtime/config/onto-config-key-path.test.js +0 -195
  86. package/dist/core-runtime/config/onto-config-preview.js +0 -108
  87. package/dist/core-runtime/config/onto-config-preview.test.js +0 -132
  88. package/dist/core-runtime/discovery/config-chain.js +0 -118
  89. package/dist/core-runtime/discovery/config-chain.test.js +0 -103
  90. package/dist/core-runtime/discovery/config-profile.js +0 -199
  91. package/dist/core-runtime/discovery/config-profile.test.js +0 -233
  92. package/dist/core-runtime/discovery/host-detection.test.js +0 -186
  93. package/dist/core-runtime/discovery/installation-paths.test.js +0 -65
  94. package/dist/core-runtime/discovery/lens-registry.test.js +0 -81
  95. package/dist/core-runtime/discovery/path-normalization.test.js +0 -22
  96. package/dist/core-runtime/discovery/plugin-path.js +0 -72
  97. package/dist/core-runtime/discovery/plugin-path.test.js +0 -95
  98. package/dist/core-runtime/evolve/adapters/code-product/compile/compile-defense.js +0 -344
  99. package/dist/core-runtime/evolve/adapters/code-product/compile/compile-defense.test.js +0 -915
  100. package/dist/core-runtime/evolve/adapters/code-product/compile/compile.js +0 -564
  101. package/dist/core-runtime/evolve/adapters/code-product/compile/compile.test.js +0 -708
  102. package/dist/core-runtime/evolve/adapters/code-product/parsers/brief-parser.js +0 -165
  103. package/dist/core-runtime/evolve/adapters/code-product/parsers/brief-parser.test.js +0 -227
  104. package/dist/core-runtime/evolve/adapters/code-product/validators/validate.js +0 -59
  105. package/dist/core-runtime/evolve/adapters/code-product/validators/validate.test.js +0 -205
  106. package/dist/core-runtime/evolve/adapters/methodology/adapter.js +0 -16
  107. package/dist/core-runtime/evolve/adapters/methodology/adapter.test.js +0 -9
  108. package/dist/core-runtime/evolve/adapters/methodology/perspectives/authority-consistency.js +0 -298
  109. package/dist/core-runtime/evolve/adapters/methodology/perspectives/authority-consistency.test.js +0 -70
  110. package/dist/core-runtime/evolve/adapters/methodology/scope-types/process.js +0 -46
  111. package/dist/core-runtime/evolve/adapters/methodology/scope-types/process.test.js +0 -73
  112. package/dist/core-runtime/evolve/adapters/registry.js +0 -47
  113. package/dist/core-runtime/evolve/adapters/registry.test.js +0 -67
  114. package/dist/core-runtime/evolve/cli.js +0 -256
  115. package/dist/core-runtime/evolve/commands/align.js +0 -194
  116. package/dist/core-runtime/evolve/commands/align.test.js +0 -82
  117. package/dist/core-runtime/evolve/commands/apply.js +0 -161
  118. package/dist/core-runtime/evolve/commands/apply.test.js +0 -138
  119. package/dist/core-runtime/evolve/commands/close.js +0 -39
  120. package/dist/core-runtime/evolve/commands/close.test.js +0 -99
  121. package/dist/core-runtime/evolve/commands/defer.js +0 -40
  122. package/dist/core-runtime/evolve/commands/defer.test.js +0 -134
  123. package/dist/core-runtime/evolve/commands/draft.js +0 -323
  124. package/dist/core-runtime/evolve/commands/draft.test.js +0 -178
  125. package/dist/core-runtime/evolve/commands/e2e-evolve-full-cycle.test.js +0 -208
  126. package/dist/core-runtime/evolve/commands/error-messages.js +0 -125
  127. package/dist/core-runtime/evolve/commands/error-messages.test.js +0 -167
  128. package/dist/core-runtime/evolve/commands/propose-align.js +0 -222
  129. package/dist/core-runtime/evolve/commands/propose-align.test.js +0 -136
  130. package/dist/core-runtime/evolve/commands/reconstruct.js +0 -330
  131. package/dist/core-runtime/evolve/commands/reconstruct.test.js +0 -278
  132. package/dist/core-runtime/evolve/commands/shared.js +0 -22
  133. package/dist/core-runtime/evolve/commands/stale-check.js +0 -103
  134. package/dist/core-runtime/evolve/commands/stale-check.test.js +0 -84
  135. package/dist/core-runtime/evolve/commands/start.js +0 -887
  136. package/dist/core-runtime/evolve/commands/start.test.js +0 -396
  137. package/dist/core-runtime/evolve/config/project-config.js +0 -99
  138. package/dist/core-runtime/evolve/config/project-config.test.js +0 -170
  139. package/dist/core-runtime/evolve/renderers/align-packet.js +0 -280
  140. package/dist/core-runtime/evolve/renderers/align-packet.test.js +0 -332
  141. package/dist/core-runtime/evolve/renderers/draft-packet.js +0 -303
  142. package/dist/core-runtime/evolve/renderers/draft-packet.test.js +0 -377
  143. package/dist/core-runtime/evolve/renderers/format.js +0 -5
  144. package/dist/core-runtime/evolve/renderers/scope-md.js +0 -237
  145. package/dist/core-runtime/evolve/renderers/scope-md.test.js +0 -306
  146. package/dist/core-runtime/govern/cli.js +0 -369
  147. package/dist/core-runtime/govern/cli.test.js +0 -314
  148. package/dist/core-runtime/govern/drift-engine.js +0 -103
  149. package/dist/core-runtime/govern/drift-engine.test.js +0 -319
  150. package/dist/core-runtime/govern/promote-principle.js +0 -206
  151. package/dist/core-runtime/govern/promote-principle.test.js +0 -368
  152. package/dist/core-runtime/govern/queue.js +0 -81
  153. package/dist/core-runtime/govern/types.js +0 -16
  154. package/dist/core-runtime/install/cli.js +0 -530
  155. package/dist/core-runtime/install/detect.js +0 -128
  156. package/dist/core-runtime/install/detect.test.js +0 -155
  157. package/dist/core-runtime/install/gitignore-update.js +0 -74
  158. package/dist/core-runtime/install/gitignore-update.test.js +0 -64
  159. package/dist/core-runtime/install/install-integration.test.js +0 -373
  160. package/dist/core-runtime/install/prompts.js +0 -389
  161. package/dist/core-runtime/install/prompts.test.js +0 -293
  162. package/dist/core-runtime/install/types.js +0 -26
  163. package/dist/core-runtime/install/validation.js +0 -295
  164. package/dist/core-runtime/install/validation.test.js +0 -313
  165. package/dist/core-runtime/install/writer.js +0 -254
  166. package/dist/core-runtime/install/writer.test.js +0 -218
  167. package/dist/core-runtime/learning/extractor.js +0 -461
  168. package/dist/core-runtime/learning/feedback.js +0 -179
  169. package/dist/core-runtime/learning/health-report.js +0 -165
  170. package/dist/core-runtime/learning/health-report.test.js +0 -169
  171. package/dist/core-runtime/learning/loader.js +0 -388
  172. package/dist/core-runtime/learning/loader.test.js +0 -102
  173. package/dist/core-runtime/learning/promote/apply-state.js +0 -240
  174. package/dist/core-runtime/learning/promote/audit-obligation.js +0 -195
  175. package/dist/core-runtime/learning/promote/collector.js +0 -432
  176. package/dist/core-runtime/learning/promote/degraded-state.js +0 -125
  177. package/dist/core-runtime/learning/promote/domain-doc-proposer.js +0 -166
  178. package/dist/core-runtime/learning/promote/e2e-promote.test.js +0 -6385
  179. package/dist/core-runtime/learning/promote/health-snapshot.js +0 -150
  180. package/dist/core-runtime/learning/promote/insight-reclassifier.js +0 -544
  181. package/dist/core-runtime/learning/promote/judgment-auditor.js +0 -517
  182. package/dist/core-runtime/learning/promote/panel-reviewer.js +0 -1158
  183. package/dist/core-runtime/learning/promote/promote-executor.js +0 -1675
  184. package/dist/core-runtime/learning/promote/promoter.js +0 -307
  185. package/dist/core-runtime/learning/promote/retirement.js +0 -122
  186. package/dist/core-runtime/learning/promote/types.js +0 -23
  187. package/dist/core-runtime/learning/prompt-sections.js +0 -51
  188. package/dist/core-runtime/learning/shared/artifact-registry-init.js +0 -45
  189. package/dist/core-runtime/learning/shared/artifact-registry.js +0 -254
  190. package/dist/core-runtime/learning/shared/audit-obligation-kernel.js +0 -73
  191. package/dist/core-runtime/learning/shared/audit-state.js +0 -99
  192. package/dist/core-runtime/learning/shared/duplicate-check.js +0 -28
  193. package/dist/core-runtime/learning/shared/llm-caller.js +0 -831
  194. package/dist/core-runtime/learning/shared/llm-caller.test.js +0 -601
  195. package/dist/core-runtime/learning/shared/llm-tool-loop.js +0 -393
  196. package/dist/core-runtime/learning/shared/mode.js +0 -25
  197. package/dist/core-runtime/learning/shared/paths.js +0 -84
  198. package/dist/core-runtime/learning/shared/paths.test.js +0 -79
  199. package/dist/core-runtime/learning/shared/patterns.js +0 -37
  200. package/dist/core-runtime/learning/shared/recoverability.js +0 -355
  201. package/dist/core-runtime/learning/shared/recovery-context.js +0 -374
  202. package/dist/core-runtime/learning/shared/scope.js +0 -1
  203. package/dist/core-runtime/learning/shared/semantic-classifier.js +0 -94
  204. package/dist/core-runtime/learning/shared/specs/apply-execution-state-spec.js +0 -42
  205. package/dist/core-runtime/learning/shared/specs/audit-state-spec.js +0 -37
  206. package/dist/core-runtime/learning/shared/specs/backup-metadata-spec.js +0 -39
  207. package/dist/core-runtime/learning/shared/specs/emergency-log-spec.js +0 -41
  208. package/dist/core-runtime/learning/shared/specs/layout-version-spec.js +0 -38
  209. package/dist/core-runtime/learning/shared/specs/promote-decisions-spec.js +0 -43
  210. package/dist/core-runtime/learning/shared/specs/promote-report-spec.js +0 -113
  211. package/dist/core-runtime/learning/shared/specs/prune-log-spec.js +0 -36
  212. package/dist/core-runtime/learning/shared/specs/recovery-resolution-spec.js +0 -48
  213. package/dist/core-runtime/learning/shared/specs/restore-manifest-spec.js +0 -43
  214. package/dist/core-runtime/learning/shared/specs/spec-helpers.js +0 -64
  215. package/dist/core-runtime/learning/usage-tracker.js +0 -190
  216. package/dist/core-runtime/learning/usage-tracker.test.js +0 -176
  217. package/dist/core-runtime/onboard/detect-review-axes.js +0 -122
  218. package/dist/core-runtime/onboard/detect-review-axes.test.js +0 -127
  219. package/dist/core-runtime/onboard/write-review-block.js +0 -188
  220. package/dist/core-runtime/onboard/write-review-block.test.js +0 -240
  221. package/dist/core-runtime/readers/brownfield-builder.js +0 -150
  222. package/dist/core-runtime/readers/brownfield-builder.test.js +0 -136
  223. package/dist/core-runtime/readers/code-chunk-collector.js +0 -53
  224. package/dist/core-runtime/readers/code-chunk-collector.test.js +0 -136
  225. package/dist/core-runtime/readers/file-utils.js +0 -240
  226. package/dist/core-runtime/readers/file-utils.test.js +0 -146
  227. package/dist/core-runtime/readers/lexicon-citation-check.js +0 -93
  228. package/dist/core-runtime/readers/lexicon-citation-check.test.js +0 -77
  229. package/dist/core-runtime/readers/mcp-figma.js +0 -30
  230. package/dist/core-runtime/readers/mcp-figma.test.js +0 -82
  231. package/dist/core-runtime/readers/mcp-generic.js +0 -31
  232. package/dist/core-runtime/readers/mcp-generic.test.js +0 -76
  233. package/dist/core-runtime/readers/ontology-index.js +0 -148
  234. package/dist/core-runtime/readers/ontology-index.test.js +0 -245
  235. package/dist/core-runtime/readers/ontology-query.js +0 -168
  236. package/dist/core-runtime/readers/ontology-query.test.js +0 -311
  237. package/dist/core-runtime/readers/ontology-resolve.js +0 -48
  238. package/dist/core-runtime/readers/ontology-resolve.test.js +0 -48
  239. package/dist/core-runtime/readers/patterns/index.js +0 -7
  240. package/dist/core-runtime/readers/review-log.js +0 -213
  241. package/dist/core-runtime/readers/review-log.test.js +0 -313
  242. package/dist/core-runtime/readers/scan-local.js +0 -102
  243. package/dist/core-runtime/readers/scan-local.test.js +0 -102
  244. package/dist/core-runtime/readers/scan-tarball.js +0 -121
  245. package/dist/core-runtime/readers/scan-tarball.test.js +0 -283
  246. package/dist/core-runtime/readers/scan-vault.js +0 -34
  247. package/dist/core-runtime/readers/scan-vault.test.js +0 -81
  248. package/dist/core-runtime/readers/types.js +0 -42
  249. package/dist/core-runtime/readers/types.test.js +0 -94
  250. package/dist/core-runtime/readers/viewpoint-collectors.js +0 -229
  251. package/dist/core-runtime/reconstruct/seed-candidate-validation.js +0 -385
  252. package/dist/core-runtime/review/citation-audit.test.js +0 -165
  253. package/dist/core-runtime/review/execution-plan-resolver.js +0 -247
  254. package/dist/core-runtime/review/execution-plan-resolver.test.js +0 -243
  255. package/dist/core-runtime/review/execution-topology-resolver-axis-first.test.js +0 -246
  256. package/dist/core-runtime/review/execution-topology-resolver.js +0 -401
  257. package/dist/core-runtime/review/execution-topology-resolver.test.js +0 -315
  258. package/dist/core-runtime/review/inline-context-embedder.test.js +0 -154
  259. package/dist/core-runtime/review/legacy-mode-policy.js +0 -88
  260. package/dist/core-runtime/review/materializers-effort-persist.test.js +0 -79
  261. package/dist/core-runtime/review/ontology-path-classifier.js +0 -179
  262. package/dist/core-runtime/review/ontology-path-classifier.test.js +0 -216
  263. package/dist/core-runtime/review/packet-boundary-policy.test.js +0 -107
  264. package/dist/core-runtime/review/participating-lens-paths.test.js +0 -73
  265. package/dist/core-runtime/review/review-config-legacy-translate.js +0 -244
  266. package/dist/core-runtime/review/review-config-legacy-translate.test.js +0 -161
  267. package/dist/core-runtime/review/review-config-validator.js +0 -289
  268. package/dist/core-runtime/review/review-config-validator.test.js +0 -236
  269. package/dist/core-runtime/review/shape-pipeline-audit.test.js +0 -311
  270. package/dist/core-runtime/review/shape-to-topology-id.js +0 -117
  271. package/dist/core-runtime/review/shape-to-topology-id.test.js +0 -132
  272. package/dist/core-runtime/review/topology-shape-derivation.js +0 -155
  273. package/dist/core-runtime/review/topology-shape-derivation.test.js +0 -195
  274. package/dist/core-runtime/scope-runtime/constants.js +0 -12
  275. package/dist/core-runtime/scope-runtime/constraint-pool.js +0 -166
  276. package/dist/core-runtime/scope-runtime/constraint-pool.test.js +0 -674
  277. package/dist/core-runtime/scope-runtime/domain-validation-log.js +0 -135
  278. package/dist/core-runtime/scope-runtime/domain-validation-log.test.js +0 -156
  279. package/dist/core-runtime/scope-runtime/eval-persistence.js +0 -65
  280. package/dist/core-runtime/scope-runtime/eval-persistence.test.js +0 -84
  281. package/dist/core-runtime/scope-runtime/event-pipeline.js +0 -64
  282. package/dist/core-runtime/scope-runtime/event-pipeline.test.js +0 -450
  283. package/dist/core-runtime/scope-runtime/event-store.js +0 -39
  284. package/dist/core-runtime/scope-runtime/event-store.test.js +0 -95
  285. package/dist/core-runtime/scope-runtime/gate-guard.js +0 -348
  286. package/dist/core-runtime/scope-runtime/gate-guard.test.js +0 -1047
  287. package/dist/core-runtime/scope-runtime/hash.js +0 -4
  288. package/dist/core-runtime/scope-runtime/hash.test.js +0 -33
  289. package/dist/core-runtime/scope-runtime/id.js +0 -4
  290. package/dist/core-runtime/scope-runtime/id.test.js +0 -17
  291. package/dist/core-runtime/scope-runtime/reducer.js +0 -297
  292. package/dist/core-runtime/scope-runtime/reducer.test.js +0 -759
  293. package/dist/core-runtime/scope-runtime/scope-manager.js +0 -161
  294. package/dist/core-runtime/scope-runtime/state-machine.js +0 -309
  295. package/dist/core-runtime/scope-runtime/state-machine.test.js +0 -704
  296. package/dist/core-runtime/scope-runtime/types.js +0 -116
  297. package/dist/core-runtime/scope-runtime/types.test.js +0 -69
  298. package/dist/core-runtime/translate/render-for-user.js +0 -169
  299. package/dist/core-runtime/translate/render-for-user.test.js +0 -122
  300. package/dist/providers/capability-contract.js +0 -1
@@ -1,244 +0,0 @@
1
- /**
2
- * Review UX Redesign P1 — legacy → v3 axis translation (pure functions).
3
- *
4
- * # What this module is
5
- *
6
- * Two pure translators that convert **legacy** config representations
7
- * (`execution_topology_priority` entries, or legacy provider-profile YAML)
8
- * into the new `OntoReviewConfig` axis form:
9
- *
10
- * - `legacyTopologyIdToAxes(id)` — single topology id → partial axes.
11
- * - `legacyFieldsToReviewConfig(raw)` — top-level YAML (possibly holding
12
- * `execution_topology_priority`) → partial `OntoReviewConfig`.
13
- *
14
- * # Why it exists
15
- *
16
- * P4 (onboard) and P5 (`onto config`) migrate existing configs to the new
17
- * schema. Doing the translation in a pure, side-effect-free module means
18
- * both flows share identical semantics and P1 can ship coverage tests
19
- * without any runtime wiring. P1 does NOT call these functions from
20
- * runtime — they are library-only.
21
- *
22
- * # How it relates
23
- *
24
- * - Input: legacy identifier(s) from the existing sketch-v3 schema.
25
- * - Output: `Partial<OntoReviewConfig>` — caller overlays remaining
26
- * user preferences (effort, concurrency) on top.
27
- * - Mapping table: see design doc §7.2 (authoritative).
28
- *
29
- * # Scope note (post-P9.2, 2026-04-21)
30
- *
31
- * P9.2 removed `execution_topology_priority` from the `OntoConfig` type.
32
- * In-process code can no longer carry this field, so this translator's
33
- * only remaining caller is **disk-based legacy YAML migration** (onboard
34
- * re-detect reading an old `.onto/config.yml` before any typing pass).
35
- * The `raw` parameter stays typed as `Record<string, unknown>` precisely
36
- * to support that pre-type reading — the input shape is what YAML
37
- * produced, not a current OntoConfig.
38
- */
39
- // ---------------------------------------------------------------------------
40
- // Legacy topology id → axes
41
- // ---------------------------------------------------------------------------
42
- /**
43
- * Translate a single legacy topology id (from `execution_topology_priority`)
44
- * to partial axis config.
45
- *
46
- * Returns `null` for:
47
- * - Unknown ids.
48
- * - `generic-*` ids (explicitly marked "unimplemented / invalid" in
49
- * design doc §7.2, dropped from the v3 catalog).
50
- *
51
- * Mapping table is the authoritative design doc §7.2.
52
- */
53
- export function legacyTopologyIdToAxes(id) {
54
- switch (id) {
55
- case "cc-main-agent-subagent":
56
- return {
57
- teamlead: { model: "main" },
58
- subagent: { provider: "main-native" },
59
- runtime_preconditions: {},
60
- };
61
- case "cc-main-codex-subprocess":
62
- return {
63
- teamlead: { model: "main" },
64
- subagent: {
65
- provider: "codex",
66
- // model_id is required by the discriminated union. Legacy configs
67
- // did not carry it at this level (codex.* block held it). Caller
68
- // overlays from `config.codex.model` if available; absent that,
69
- // the validator will later flag this partial translation.
70
- model_id: "",
71
- },
72
- runtime_preconditions: {},
73
- };
74
- case "cc-teams-agent-subagent":
75
- return {
76
- teamlead: { model: "main" },
77
- subagent: { provider: "main-native" },
78
- runtime_preconditions: {
79
- requires_agent_teams_env: true,
80
- },
81
- };
82
- case "cc-teams-codex-subprocess":
83
- return {
84
- teamlead: { model: "main" },
85
- subagent: {
86
- provider: "codex",
87
- model_id: "",
88
- },
89
- runtime_preconditions: {
90
- requires_agent_teams_env: true,
91
- },
92
- };
93
- case "cc-teams-litellm-sessions":
94
- return {
95
- teamlead: { model: "main" },
96
- subagent: {
97
- provider: "litellm",
98
- model_id: "",
99
- },
100
- runtime_preconditions: {
101
- requires_agent_teams_env: true,
102
- },
103
- };
104
- case "cc-teams-lens-agent-deliberation":
105
- return {
106
- teamlead: { model: "main" },
107
- subagent: { provider: "main-native" },
108
- lens_deliberation: "sendmessage-a2a",
109
- runtime_preconditions: {
110
- requires_agent_teams_env: true,
111
- },
112
- };
113
- case "codex-main-subprocess":
114
- return {
115
- teamlead: { model: "main" },
116
- subagent: { provider: "main-native" },
117
- runtime_preconditions: {
118
- implies_host_codex: true,
119
- },
120
- };
121
- case "codex-nested-subprocess":
122
- return {
123
- teamlead: {
124
- model: {
125
- provider: "codex",
126
- model_id: "",
127
- },
128
- },
129
- subagent: {
130
- provider: "codex",
131
- model_id: "",
132
- },
133
- runtime_preconditions: {
134
- implies_host_plain_terminal: true,
135
- },
136
- };
137
- // generic-nested-subagent / generic-main-subagent: dropped per §7.2.
138
- default:
139
- return null;
140
- }
141
- }
142
- /**
143
- * Translate a legacy-shaped YAML object (top-level, post-cast) to partial
144
- * OntoReviewConfig. Consults `execution_topology_priority` first entry and
145
- * overlays model/effort from per-provider blocks (codex.*, litellm.*, etc.)
146
- * when available.
147
- *
148
- * Returns `null` when no legacy signal is present (no
149
- * `execution_topology_priority` entry and no recognized provider block).
150
- */
151
- export function legacyFieldsToReviewConfig(raw) {
152
- if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
153
- return null;
154
- }
155
- const obj = raw;
156
- const priority = obj.execution_topology_priority;
157
- if (!Array.isArray(priority) || priority.length === 0) {
158
- return null;
159
- }
160
- const firstId = priority[0];
161
- if (typeof firstId !== "string") {
162
- return null;
163
- }
164
- const axes = legacyTopologyIdToAxes(firstId);
165
- if (!axes) {
166
- return null;
167
- }
168
- const warnings = [];
169
- const out = {};
170
- if (axes.teamlead) {
171
- out.teamlead = overlayTeamleadModelFields(axes.teamlead, obj, warnings);
172
- }
173
- if (axes.subagent) {
174
- out.subagent = overlaySubagentModelFields(axes.subagent, obj, warnings);
175
- }
176
- if (axes.lens_deliberation) {
177
- out.lens_deliberation = axes.lens_deliberation;
178
- }
179
- return {
180
- config: out,
181
- source_topology_id: firstId,
182
- runtime_preconditions: axes.runtime_preconditions,
183
- warnings,
184
- };
185
- }
186
- function overlayTeamleadModelFields(teamlead, raw, warnings) {
187
- if (teamlead.model === "main") {
188
- return teamlead;
189
- }
190
- // External teamlead — overlay model_id / effort from legacy per-provider
191
- // block (only codex is reachable via current legacy topology ids).
192
- const provider = teamlead.model.provider;
193
- const block = readProviderBlock(raw, provider);
194
- const modelId = teamlead.model.model_id || readModelIdFromBlock(block) || "";
195
- const effort = teamlead.model.effort || readEffortFromBlock(block);
196
- if (!modelId) {
197
- warnings.push(`teamlead.model.model_id 미지정 — 기존 config 의 ${provider}.model 이 없습니다. ` +
198
- "새 config 의 teamlead.model.model_id 를 직접 채워주세요.");
199
- }
200
- return {
201
- model: {
202
- provider,
203
- model_id: modelId,
204
- ...(effort ? { effort } : {}),
205
- },
206
- };
207
- }
208
- function overlaySubagentModelFields(subagent, raw, warnings) {
209
- if (subagent.provider === "main-native") {
210
- return subagent;
211
- }
212
- const provider = subagent.provider;
213
- const block = readProviderBlock(raw, provider);
214
- const modelId = subagent.model_id || readModelIdFromBlock(block) || "";
215
- const effort = subagent.effort || readEffortFromBlock(block);
216
- if (!modelId) {
217
- warnings.push(`subagent.model_id 미지정 — 기존 config 의 ${provider}.model 이 없습니다. ` +
218
- "새 config 의 subagent.model_id 를 직접 채워주세요.");
219
- }
220
- return {
221
- provider,
222
- model_id: modelId,
223
- ...(effort ? { effort } : {}),
224
- };
225
- }
226
- function readProviderBlock(raw, provider) {
227
- const block = raw[provider];
228
- if (typeof block === "object" && block !== null && !Array.isArray(block)) {
229
- return block;
230
- }
231
- return null;
232
- }
233
- function readModelIdFromBlock(block) {
234
- if (!block)
235
- return null;
236
- const model = block.model;
237
- return typeof model === "string" && model.length > 0 ? model : null;
238
- }
239
- function readEffortFromBlock(block) {
240
- if (!block)
241
- return null;
242
- const effort = block.effort;
243
- return typeof effort === "string" && effort.length > 0 ? effort : null;
244
- }
@@ -1,161 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { legacyFieldsToReviewConfig, legacyTopologyIdToAxes, } from "./review-config-legacy-translate.js";
3
- // ---------------------------------------------------------------------------
4
- // legacy translation — Review UX Redesign P1 (2026-04-20)
5
- // ---------------------------------------------------------------------------
6
- //
7
- // Authoritative mapping table: design doc §7.2.
8
- // - 8 known legacy topology ids → partial axes (each with specific runtime
9
- // preconditions where applicable).
10
- // - 2 generic-* ids → null (dropped from v3 catalog).
11
- // - unknown ids → null.
12
- //
13
- // legacyFieldsToReviewConfig also overlays model_id / effort from legacy
14
- // per-provider blocks (codex.* / litellm.* / etc.) into the translated axes.
15
- // ---------------------------------------------------------------------------
16
- describe("legacyTopologyIdToAxes — §7.2 mapping table", () => {
17
- it("cc-main-agent-subagent → teamlead=main, subagent=main-native", () => {
18
- const a = legacyTopologyIdToAxes("cc-main-agent-subagent");
19
- expect(a).not.toBeNull();
20
- expect(a?.teamlead).toEqual({ model: "main" });
21
- expect(a?.subagent).toEqual({ provider: "main-native" });
22
- expect(a?.runtime_preconditions).toEqual({});
23
- });
24
- it("cc-main-codex-subprocess → teamlead=main, subagent=codex", () => {
25
- const a = legacyTopologyIdToAxes("cc-main-codex-subprocess");
26
- expect(a?.subagent).toMatchObject({ provider: "codex" });
27
- expect(a?.runtime_preconditions).toEqual({});
28
- });
29
- it("cc-teams-agent-subagent → requires_agent_teams_env=true", () => {
30
- const a = legacyTopologyIdToAxes("cc-teams-agent-subagent");
31
- expect(a?.subagent).toEqual({ provider: "main-native" });
32
- expect(a?.runtime_preconditions.requires_agent_teams_env).toBe(true);
33
- });
34
- it("cc-teams-codex-subprocess → requires_agent_teams_env=true + codex", () => {
35
- const a = legacyTopologyIdToAxes("cc-teams-codex-subprocess");
36
- expect(a?.subagent).toMatchObject({ provider: "codex" });
37
- expect(a?.runtime_preconditions.requires_agent_teams_env).toBe(true);
38
- });
39
- it("cc-teams-litellm-sessions → requires_agent_teams_env=true + litellm", () => {
40
- const a = legacyTopologyIdToAxes("cc-teams-litellm-sessions");
41
- expect(a?.subagent).toMatchObject({ provider: "litellm" });
42
- expect(a?.runtime_preconditions.requires_agent_teams_env).toBe(true);
43
- });
44
- it("cc-teams-lens-agent-deliberation → lens_deliberation=sendmessage-a2a", () => {
45
- const a = legacyTopologyIdToAxes("cc-teams-lens-agent-deliberation");
46
- expect(a?.subagent).toEqual({ provider: "main-native" });
47
- expect(a?.lens_deliberation).toBe("sendmessage-a2a");
48
- expect(a?.runtime_preconditions.requires_agent_teams_env).toBe(true);
49
- });
50
- it("codex-main-subprocess → implies_host_codex=true", () => {
51
- const a = legacyTopologyIdToAxes("codex-main-subprocess");
52
- expect(a?.subagent).toEqual({ provider: "main-native" });
53
- expect(a?.runtime_preconditions.implies_host_codex).toBe(true);
54
- });
55
- it("codex-nested-subprocess → external teamlead=codex + plain-terminal host", () => {
56
- const a = legacyTopologyIdToAxes("codex-nested-subprocess");
57
- expect(a?.teamlead?.model).toMatchObject({ provider: "codex" });
58
- expect(a?.subagent).toMatchObject({ provider: "codex" });
59
- expect(a?.runtime_preconditions.implies_host_plain_terminal).toBe(true);
60
- });
61
- it("generic-nested-subagent → null (dropped from v3 catalog)", () => {
62
- expect(legacyTopologyIdToAxes("generic-nested-subagent")).toBeNull();
63
- });
64
- it("generic-main-subagent → null (dropped from v3 catalog)", () => {
65
- expect(legacyTopologyIdToAxes("generic-main-subagent")).toBeNull();
66
- });
67
- it("unknown id → null", () => {
68
- expect(legacyTopologyIdToAxes("not-a-real-topology")).toBeNull();
69
- expect(legacyTopologyIdToAxes("")).toBeNull();
70
- });
71
- });
72
- describe("legacyFieldsToReviewConfig — overlay provider blocks", () => {
73
- it("returns null when no execution_topology_priority", () => {
74
- expect(legacyFieldsToReviewConfig({})).toBeNull();
75
- expect(legacyFieldsToReviewConfig({ codex: { model: "gpt-5.4" } })).toBeNull();
76
- });
77
- it("returns null for empty priority array", () => {
78
- expect(legacyFieldsToReviewConfig({ execution_topology_priority: [] })).toBeNull();
79
- });
80
- it("uses first entry of priority array", () => {
81
- const r = legacyFieldsToReviewConfig({
82
- execution_topology_priority: [
83
- "cc-main-codex-subprocess",
84
- "codex-main-subprocess",
85
- ],
86
- codex: { model: "gpt-5.4", effort: "high" },
87
- });
88
- expect(r).not.toBeNull();
89
- expect(r?.source_topology_id).toBe("cc-main-codex-subprocess");
90
- expect(r?.config.subagent).toEqual({
91
- provider: "codex",
92
- model_id: "gpt-5.4",
93
- effort: "high",
94
- });
95
- });
96
- it("overlays codex.model + codex.effort into subagent axes", () => {
97
- const r = legacyFieldsToReviewConfig({
98
- execution_topology_priority: ["cc-teams-codex-subprocess"],
99
- codex: { model: "gpt-5.4-preview", effort: "xhigh" },
100
- });
101
- expect(r?.config.subagent).toEqual({
102
- provider: "codex",
103
- model_id: "gpt-5.4-preview",
104
- effort: "xhigh",
105
- });
106
- expect(r?.runtime_preconditions.requires_agent_teams_env).toBe(true);
107
- });
108
- it("overlays litellm.model into litellm subagent", () => {
109
- const r = legacyFieldsToReviewConfig({
110
- execution_topology_priority: ["cc-teams-litellm-sessions"],
111
- litellm: { model: "llama-8b" },
112
- });
113
- expect(r?.config.subagent).toMatchObject({
114
- provider: "litellm",
115
- model_id: "llama-8b",
116
- });
117
- });
118
- it("warns when foreign provider block has no model", () => {
119
- const r = legacyFieldsToReviewConfig({
120
- execution_topology_priority: ["cc-main-codex-subprocess"],
121
- // codex block absent — no model to overlay
122
- });
123
- expect(r).not.toBeNull();
124
- expect(r?.warnings.some((w) => w.includes("model_id"))).toBe(true);
125
- });
126
- it("preserves lens_deliberation for deliberation topology", () => {
127
- const r = legacyFieldsToReviewConfig({
128
- execution_topology_priority: ["cc-teams-lens-agent-deliberation"],
129
- });
130
- expect(r?.config.lens_deliberation).toBe("sendmessage-a2a");
131
- });
132
- it("codex-nested overlays codex.model into both teamlead and subagent", () => {
133
- const r = legacyFieldsToReviewConfig({
134
- execution_topology_priority: ["codex-nested-subprocess"],
135
- codex: { model: "gpt-5.4", effort: "high" },
136
- });
137
- expect(r?.config.teamlead?.model).toEqual({
138
- provider: "codex",
139
- model_id: "gpt-5.4",
140
- effort: "high",
141
- });
142
- expect(r?.config.subagent).toEqual({
143
- provider: "codex",
144
- model_id: "gpt-5.4",
145
- effort: "high",
146
- });
147
- expect(r?.runtime_preconditions.implies_host_plain_terminal).toBe(true);
148
- });
149
- it("returns null when first priority entry is generic-*", () => {
150
- const r = legacyFieldsToReviewConfig({
151
- execution_topology_priority: ["generic-main-subagent"],
152
- });
153
- expect(r).toBeNull();
154
- });
155
- it("rejects non-string first entry gracefully", () => {
156
- const r = legacyFieldsToReviewConfig({
157
- execution_topology_priority: [42, "cc-main-agent-subagent"],
158
- });
159
- expect(r).toBeNull();
160
- });
161
- });
@@ -1,289 +0,0 @@
1
- /**
2
- * Review UX Redesign P1 — pure validator for the `review:` config block.
3
- *
4
- * # What this module is
5
- *
6
- * A pure function suite that accepts an `unknown` (the shape YAML casting
7
- * produces under `OntoConfig.review`) and returns either a typed
8
- * `OntoReviewConfig` or a list of structured validation errors. The
9
- * validator enforces the **discriminated union semantics** that YAML
10
- * cast-reads cannot verify — e.g. `subagent.provider = "main-native"` must
11
- * not carry `model_id` / `effort`, and `subagent.provider` of a foreign
12
- * provider must carry `model_id`.
13
- *
14
- * # Why it exists
15
- *
16
- * `readConfigAt` in `config-chain.ts` casts raw YAML into `OntoConfig`
17
- * without runtime structure checks. Downstream consumers (P2 topology
18
- * derivation, P4 onboard, P5 config CLI) need a single seat that both
19
- * (a) surfaces config errors with precise `review.*.path` location and
20
- * (b) narrows the discriminated union so compile-time type guards hold
21
- * for the rest of the pipeline.
22
- *
23
- * # How it relates
24
- *
25
- * - Input: raw `review` block (from YAML cast). Permissively typed.
26
- * - Output: `{ ok: true, config: OntoReviewConfig }` or
27
- * `{ ok: false, errors: ValidationError[] }`.
28
- * - P1 does NOT call this validator from runtime — it is pure/library.
29
- * P2 wires the call inside the review-invoke path.
30
- */
31
- // Domain tables kept next to the validator — same file changes together with
32
- // schema changes, so drift between type and validator is impossible.
33
- const FOREIGN_PROVIDERS = ["codex"];
34
- const SUBAGENT_PROVIDERS = ["main-native", ...FOREIGN_PROVIDERS];
35
- const LENS_DELIBERATIONS = [
36
- "controlled-lens-deliberation",
37
- ];
38
- /**
39
- * Validate a raw `review` block (as cast from YAML) and return either the
40
- * narrowed OntoReviewConfig or a structured error list.
41
- *
42
- * `undefined` / missing block → `{ ok: true, config: {}, errors: [] }`.
43
- * Consumers should treat `{}` as the review defaults.
44
- */
45
- export function validateReviewConfig(raw) {
46
- const errors = [];
47
- if (raw === undefined || raw === null) {
48
- return { ok: true, config: {}, errors: [] };
49
- }
50
- if (typeof raw !== "object" || Array.isArray(raw)) {
51
- errors.push({
52
- path: "review",
53
- message: "review block must be a YAML mapping (object), not scalar/array.",
54
- });
55
- return { ok: false, errors };
56
- }
57
- const obj = raw;
58
- const out = {};
59
- rejectUnknownKeys(obj, ["teamlead", "subagent", "max_concurrent_lenses", "lens_deliberation"], "review", errors);
60
- if ("teamlead" in obj) {
61
- const teamlead = validateTeamlead(obj.teamlead, errors);
62
- if (teamlead)
63
- out.teamlead = teamlead;
64
- }
65
- if ("subagent" in obj) {
66
- const subagent = validateSubagent(obj.subagent, errors);
67
- if (subagent)
68
- out.subagent = subagent;
69
- }
70
- if ("max_concurrent_lenses" in obj) {
71
- const concurrency = validateConcurrency(obj.max_concurrent_lenses, errors);
72
- if (concurrency !== null)
73
- out.max_concurrent_lenses = concurrency;
74
- }
75
- if ("lens_deliberation" in obj) {
76
- const deliberation = validateDeliberation(obj.lens_deliberation, errors);
77
- if (deliberation)
78
- out.lens_deliberation = deliberation;
79
- }
80
- if (errors.length > 0) {
81
- return { ok: false, errors };
82
- }
83
- return { ok: true, config: out, errors: [] };
84
- }
85
- // ---------------------------------------------------------------------------
86
- // Teamlead
87
- // ---------------------------------------------------------------------------
88
- function validateTeamlead(raw, errors) {
89
- if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
90
- errors.push({
91
- path: "review.teamlead",
92
- message: "teamlead must be a mapping with a `model` field.",
93
- });
94
- return null;
95
- }
96
- const obj = raw;
97
- rejectUnknownKeys(obj, ["model"], "review.teamlead", errors);
98
- if (!("model" in obj)) {
99
- errors.push({
100
- path: "review.teamlead.model",
101
- message: "teamlead.model is required when teamlead block is declared.",
102
- });
103
- return null;
104
- }
105
- const model = obj.model;
106
- if (model === "main") {
107
- return { model: "main" };
108
- }
109
- const explicit = validateExplicitModel(model, "review.teamlead.model", errors);
110
- if (!explicit)
111
- return null;
112
- return { model: explicit };
113
- }
114
- function validateExplicitModel(raw, pathPrefix, errors) {
115
- if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
116
- errors.push({
117
- path: pathPrefix,
118
- message: 'model must be either "main" (string) or a mapping ' +
119
- "{provider, model_id, effort?}.",
120
- });
121
- return null;
122
- }
123
- const obj = raw;
124
- rejectUnknownKeys(obj, ["provider", "model_id", "effort"], pathPrefix, errors);
125
- const provider = obj.provider;
126
- if (typeof provider !== "string") {
127
- errors.push({
128
- path: `${pathPrefix}.provider`,
129
- message: "provider must be a string.",
130
- });
131
- return null;
132
- }
133
- if (!FOREIGN_PROVIDERS.includes(provider)) {
134
- errors.push({
135
- path: `${pathPrefix}.provider`,
136
- message: `provider must be one of: ${FOREIGN_PROVIDERS.join(", ")} (got "${provider}"). "main-native" is only valid under subagent, not under an explicit model spec.`,
137
- });
138
- return null;
139
- }
140
- const modelId = obj.model_id;
141
- if (typeof modelId !== "string" || modelId.length === 0) {
142
- errors.push({
143
- path: `${pathPrefix}.model_id`,
144
- message: "model_id is required (non-empty string) for foreign providers.",
145
- });
146
- return null;
147
- }
148
- const spec = {
149
- provider: provider,
150
- model_id: modelId,
151
- };
152
- if ("effort" in obj) {
153
- if (typeof obj.effort !== "string" || obj.effort.length === 0) {
154
- errors.push({
155
- path: `${pathPrefix}.effort`,
156
- message: "effort must be a non-empty string when present.",
157
- });
158
- return null;
159
- }
160
- spec.effort = obj.effort;
161
- }
162
- return spec;
163
- }
164
- // ---------------------------------------------------------------------------
165
- // Subagent
166
- // ---------------------------------------------------------------------------
167
- function validateSubagent(raw, errors) {
168
- if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
169
- errors.push({
170
- path: "review.subagent",
171
- message: "subagent must be a mapping with a `provider` field.",
172
- });
173
- return null;
174
- }
175
- const obj = raw;
176
- rejectUnknownKeys(obj, ["provider", "model_id", "effort"], "review.subagent", errors);
177
- const provider = obj.provider;
178
- if (typeof provider !== "string") {
179
- errors.push({
180
- path: "review.subagent.provider",
181
- message: "subagent.provider is required and must be a string.",
182
- });
183
- return null;
184
- }
185
- if (!SUBAGENT_PROVIDERS.includes(provider)) {
186
- errors.push({
187
- path: "review.subagent.provider",
188
- message: `subagent.provider must be one of: ${SUBAGENT_PROVIDERS.join(", ")} (got "${provider}").`,
189
- });
190
- return null;
191
- }
192
- if (provider === "main-native") {
193
- // Discriminated-union enforcement: main-native branch must not carry
194
- // model_id / effort (those are foreign-provider-only).
195
- if ("model_id" in obj) {
196
- errors.push({
197
- path: "review.subagent.model_id",
198
- message: "model_id is not allowed when subagent.provider=main-native. " +
199
- "main-native resolves via the host's native subagent mechanism.",
200
- });
201
- }
202
- if ("effort" in obj) {
203
- errors.push({
204
- path: "review.subagent.effort",
205
- message: "effort is not allowed when subagent.provider=main-native. " +
206
- "Effort configuration for main-native is host-managed (e.g. Claude Code Agent tool).",
207
- });
208
- }
209
- if (errors.some((e) => e.path.startsWith("review.subagent"))) {
210
- return null;
211
- }
212
- return { provider: "main-native" };
213
- }
214
- // Foreign branch
215
- const modelId = obj.model_id;
216
- if (typeof modelId !== "string" || modelId.length === 0) {
217
- errors.push({
218
- path: "review.subagent.model_id",
219
- message: `model_id is required (non-empty string) when subagent.provider=${provider}.`,
220
- });
221
- return null;
222
- }
223
- const spec = {
224
- provider: provider,
225
- model_id: modelId,
226
- };
227
- if ("effort" in obj) {
228
- if (typeof obj.effort !== "string" || obj.effort.length === 0) {
229
- errors.push({
230
- path: "review.subagent.effort",
231
- message: "effort must be a non-empty string when present.",
232
- });
233
- return null;
234
- }
235
- spec.effort = obj.effort;
236
- }
237
- return spec;
238
- }
239
- // ---------------------------------------------------------------------------
240
- // Scalar axes
241
- // ---------------------------------------------------------------------------
242
- function validateConcurrency(raw, errors) {
243
- if (typeof raw !== "number" || !Number.isFinite(raw)) {
244
- errors.push({
245
- path: "review.max_concurrent_lenses",
246
- message: "max_concurrent_lenses must be a finite number.",
247
- });
248
- return null;
249
- }
250
- if (!Number.isInteger(raw) || raw < 1) {
251
- errors.push({
252
- path: "review.max_concurrent_lenses",
253
- message: "max_concurrent_lenses must be a positive integer (>= 1).",
254
- });
255
- return null;
256
- }
257
- return raw;
258
- }
259
- function validateDeliberation(raw, errors) {
260
- if (typeof raw !== "string") {
261
- errors.push({
262
- path: "review.lens_deliberation",
263
- message: "lens_deliberation must be a string.",
264
- });
265
- return null;
266
- }
267
- if (!LENS_DELIBERATIONS.includes(raw)) {
268
- errors.push({
269
- path: "review.lens_deliberation",
270
- message: `lens_deliberation must be one of: ${LENS_DELIBERATIONS.join(", ")} (got "${raw}").`,
271
- });
272
- return null;
273
- }
274
- return raw;
275
- }
276
- // ---------------------------------------------------------------------------
277
- // Shared helpers
278
- // ---------------------------------------------------------------------------
279
- function rejectUnknownKeys(obj, allowed, pathPrefix, errors) {
280
- const allowedSet = new Set(allowed);
281
- for (const key of Object.keys(obj)) {
282
- if (!allowedSet.has(key)) {
283
- errors.push({
284
- path: `${pathPrefix}.${key}`,
285
- message: `Unknown field. Allowed: ${allowed.join(", ")}.`,
286
- });
287
- }
288
- }
289
- }