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,199 @@
1
+ /**
2
+ * Atomic profile adoption for review config chain.
3
+ *
4
+ * # What this module is
5
+ *
6
+ * A policy module that enforces **atomic profile ownership** when merging
7
+ * project `.onto/config.yml` over global `~/.onto/config.yml`:
8
+ *
9
+ * - Provider-coupled fields (`llm`, `main_llm`, `lens_agent_teams_mode`)
10
+ * belong to ONE
11
+ * source only. Either the project owns the whole profile, or the global
12
+ * does — never a frankenstein merge.
13
+ * - Orthogonal fields (output_language, domains, review_mode, etc.) remain
14
+ * free to merge field-by-field because they carry no cross-provider
15
+ * semantics.
16
+ *
17
+ * # How it relates
18
+ *
19
+ * `adoptProfile()` is called from `resolveConfigChain()`. It returns the
20
+ * adopted profile fields (one source, or empty when neither side declared
21
+ * any). `resolveConfigChain()` is the single integration seat for review
22
+ * callers.
23
+ *
24
+ * The atomic-ownership principle is preserved: `extractProfileFields`
25
+ * still transfers PROFILE_FIELDS as a group, so frankenstein merges
26
+ * remain impossible.
27
+ */
28
+ // ---------------------------------------------------------------------------
29
+ // Profile vs orthogonal field partitioning
30
+ // ---------------------------------------------------------------------------
31
+ /**
32
+ * Top-level OntoConfig keys whose semantics are coupled to the provider choice.
33
+ * A single source owns the entire set per adoption cycle.
34
+ */
35
+ export const PROFILE_FIELDS = new Set([
36
+ "llm",
37
+ "main_llm",
38
+ "lens_agent_teams_mode",
39
+ ]);
40
+ /**
41
+ * Orthogonal fields (output language, domain scope, listing limits, etc.)
42
+ * continue to merge last-wins across home → project. These do not encode
43
+ * provider-coupled semantics.
44
+ */
45
+ function isOrthogonalField(key) {
46
+ return !PROFILE_FIELDS.has(key);
47
+ }
48
+ /**
49
+ * SSOT predicate for "does this config declare a non-empty `review:` axis
50
+ * block?". This is consumed only by `claimsProfileOwnership`
51
+ * below (the atomic-adoption ownership signal).
52
+ *
53
+ * A block counts as declared when it is a non-null object with at least
54
+ * one key. An empty `review: {}` does not count.
55
+ *
56
+ * Accepts either a typed `OntoConfig` or a raw `Record<string, unknown>`
57
+ * for compatibility with callers that read YAML before typing.
58
+ */
59
+ export function hasReviewBlock(config) {
60
+ if (config === undefined || config === null)
61
+ return false;
62
+ const review = config.review;
63
+ if (typeof review !== "object" || review === null)
64
+ return false;
65
+ return Object.keys(review).length > 0;
66
+ }
67
+ // ---------------------------------------------------------------------------
68
+ // Profile field presence
69
+ // ---------------------------------------------------------------------------
70
+ /**
71
+ * Does this config declare ANY PROFILE_FIELDS value? Used by `adoptProfile`
72
+ * to decide which side owns the profile slice. Returns true when at least
73
+ * one profile field is set to a non-empty value.
74
+ */
75
+ export function hasAnyProfileField(config) {
76
+ for (const field of PROFILE_FIELDS) {
77
+ const value = config[field];
78
+ if (value === undefined || value === null)
79
+ continue;
80
+ if (typeof value === "object" && !Array.isArray(value)) {
81
+ if (Object.keys(value).length === 0)
82
+ continue;
83
+ }
84
+ return true;
85
+ }
86
+ return false;
87
+ }
88
+ // ---------------------------------------------------------------------------
89
+ // Profile extraction
90
+ // ---------------------------------------------------------------------------
91
+ /**
92
+ * Extract only profile-scoped fields from a config. Orthogonal fields are
93
+ * dropped — this is the "profile slice" that adoption transfers atomically.
94
+ */
95
+ export function extractProfileFields(config) {
96
+ const out = {};
97
+ for (const field of PROFILE_FIELDS) {
98
+ const value = config[field];
99
+ if (value !== undefined && value !== null) {
100
+ out[field] = value;
101
+ }
102
+ }
103
+ return out;
104
+ }
105
+ /**
106
+ * Does this config claim profile ownership? A side claims ownership when it
107
+ * declares either any PROFILE_FIELDS value OR a non-empty `review:` axis
108
+ * block. The axis block is orthogonal-merged (not part of the profile slice
109
+ * itself), but its presence signals "I opted in to declaring this review
110
+ * setup" — which is strong enough to commit that side to owning whatever
111
+ * profile fields accompany it.
112
+ */
113
+ function claimsProfileOwnership(config) {
114
+ return hasAnyProfileField(config) || hasReviewBlock(config);
115
+ }
116
+ /**
117
+ * Adopt exactly one profile atomically.
118
+ *
119
+ * # Decision table
120
+ *
121
+ * sameRoot → home/project are the same file, treat as global
122
+ * project claims ownership → adopt project
123
+ * project does not, home claims → adopt home
124
+ * neither claims → empty profile, source="none"
125
+ *
126
+ * # What "claims ownership" means
127
+ *
128
+ * Either a non-empty `review:` axis block OR any PROFILE_FIELDS value.
129
+ * See `claimsProfileOwnership` above.
130
+ *
131
+ * # Atomic ownership invariant
132
+ *
133
+ * When `source === "project"`, home's profile fields never appear in
134
+ * `profile`. When `source === "global"`, project's profile fields never
135
+ * appear. `extractProfileFields` is the enforcement point — it takes
136
+ * from one source only. Frankenstein merges remain impossible.
137
+ */
138
+ export function adoptProfile(args) {
139
+ if (args.sameRoot) {
140
+ // Single-root case: home === project, so home's profile IS project's.
141
+ // Report as "global" for traceability (the home path is the canonical
142
+ // path for same-root runs).
143
+ if (claimsProfileOwnership(args.home)) {
144
+ return {
145
+ profile: extractProfileFields(args.home),
146
+ source: "global",
147
+ source_path: args.homePath,
148
+ };
149
+ }
150
+ return { profile: {}, source: "none", source_path: null };
151
+ }
152
+ if (claimsProfileOwnership(args.project)) {
153
+ return {
154
+ profile: extractProfileFields(args.project),
155
+ source: "project",
156
+ source_path: args.projectPath,
157
+ };
158
+ }
159
+ if (claimsProfileOwnership(args.home)) {
160
+ return {
161
+ profile: extractProfileFields(args.home),
162
+ source: "global",
163
+ source_path: args.homePath,
164
+ };
165
+ }
166
+ return { profile: {}, source: "none", source_path: null };
167
+ }
168
+ // ---------------------------------------------------------------------------
169
+ // Orthogonal merge utility (used by resolveConfigChain)
170
+ // ---------------------------------------------------------------------------
171
+ /**
172
+ * Merge orthogonal (non-profile) fields from home and project with last-wins
173
+ * semantics. `excluded_names` uses union merge.
174
+ */
175
+ export function mergeOrthogonalFields(home, project) {
176
+ const merged = {};
177
+ for (const [key, value] of Object.entries(home)) {
178
+ if (!isOrthogonalField(key) || value === undefined || value === null)
179
+ continue;
180
+ merged[key] = value;
181
+ }
182
+ for (const [key, value] of Object.entries(project)) {
183
+ if (!isOrthogonalField(key) || value === undefined || value === null)
184
+ continue;
185
+ if (key === "excluded_names") {
186
+ const homeNames = Array.isArray(home.excluded_names)
187
+ ? home.excluded_names
188
+ : [];
189
+ const projectNames = Array.isArray(value) ? value : [];
190
+ merged[key] = [
191
+ ...new Set([...homeNames, ...projectNames]),
192
+ ];
193
+ }
194
+ else {
195
+ merged[key] = value;
196
+ }
197
+ }
198
+ return merged;
199
+ }
@@ -0,0 +1,233 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { adoptProfile, extractProfileFields, hasAnyProfileField, hasReviewBlock, mergeOrthogonalFields, } from "./config-profile.js";
3
+ // ---------------------------------------------------------------------------
4
+ // Atomic profile adoption invariants (post-P9.4, 2026-04-21).
5
+ // ---------------------------------------------------------------------------
6
+ //
7
+ // These tests assert two invariants:
8
+ //
9
+ // (1) Ownership is atomic — one source owns the full profile slice.
10
+ // No frankenstein merges where profile fields from different
11
+ // sources coexist in the adopted result.
12
+ //
13
+ // (2) The adoption layer does NOT decide whether a profile is "runnable".
14
+ // Empty profiles and single-side-only profiles both return cleanly;
15
+ // the topology resolver's universal `main_native` degrade owns the
16
+ // "no viable host" fail-fast (P9.3).
17
+ // ---------------------------------------------------------------------------
18
+ // ─── hasAnyProfileField ───
19
+ describe("hasAnyProfileField", () => {
20
+ it("empty config → false", () => {
21
+ expect(hasAnyProfileField({})).toBe(false);
22
+ });
23
+ it("only orthogonal fields → false", () => {
24
+ expect(hasAnyProfileField({
25
+ output_language: "ko",
26
+ review_mode: "full",
27
+ domains: ["se"],
28
+ })).toBe(false);
29
+ });
30
+ it("per-provider block set → true", () => {
31
+ expect(hasAnyProfileField({ codex: { model: "gpt-5.4" } })).toBe(true);
32
+ });
33
+ it("top-level model set → true", () => {
34
+ expect(hasAnyProfileField({ model: "gpt-5.4" })).toBe(true);
35
+ });
36
+ it("empty per-provider block (e.g. `codex: {}`) → false", () => {
37
+ // YAML authors who wrote `codex:` without a body should not flip
38
+ // profile ownership.
39
+ expect(hasAnyProfileField({ codex: {} })).toBe(false);
40
+ });
41
+ });
42
+ // ─── hasReviewBlock (surviving legacy-bypass SSOT) ───
43
+ describe("hasReviewBlock", () => {
44
+ it("undefined / empty → false", () => {
45
+ expect(hasReviewBlock(undefined)).toBe(false);
46
+ expect(hasReviewBlock({})).toBe(false);
47
+ });
48
+ it("review block with one key → true", () => {
49
+ expect(hasReviewBlock({
50
+ review: { subagent: { provider: "main-native" } },
51
+ })).toBe(true);
52
+ });
53
+ it("empty `review: {}` → false (prevents accidental opt-in)", () => {
54
+ expect(hasReviewBlock({ review: {} })).toBe(false);
55
+ });
56
+ });
57
+ // ─── extractProfileFields ───
58
+ describe("extractProfileFields", () => {
59
+ it("returns only profile fields, not orthogonal", () => {
60
+ const cfg = {
61
+ anthropic: { model: "claude-haiku-4-5" },
62
+ reasoning_effort: "high",
63
+ output_language: "ko", // orthogonal
64
+ domains: ["se"], // orthogonal
65
+ review_mode: "full", // orthogonal
66
+ };
67
+ const extracted = extractProfileFields(cfg);
68
+ expect(extracted.anthropic).toEqual({ model: "claude-haiku-4-5" });
69
+ expect(extracted.reasoning_effort).toBe("high");
70
+ expect(extracted.output_language).toBeUndefined();
71
+ expect(extracted.domains).toBeUndefined();
72
+ expect(extracted.review_mode).toBeUndefined();
73
+ });
74
+ });
75
+ // ─── adoptProfile — decision table (post-P9.4) ───
76
+ const HOME_PATH = "/home/.onto/config.yml";
77
+ const PROJECT_PATH = "/project/.onto/config.yml";
78
+ function buildArgs(home, project) {
79
+ return { home, project, homePath: HOME_PATH, projectPath: PROJECT_PATH, sameRoot: false };
80
+ }
81
+ describe("adoptProfile — decision branches", () => {
82
+ it("project has profile → source=project", () => {
83
+ const adoption = adoptProfile(buildArgs({
84
+ review: { subagent: { provider: "codex", model_id: "gpt-5.4" } },
85
+ codex: { model: "gpt-5.4" },
86
+ reasoning_effort: "high",
87
+ }, {
88
+ review: { subagent: { provider: "main-native" } },
89
+ anthropic: { model: "claude-haiku-4-5" },
90
+ }));
91
+ expect(adoption.source).toBe("project");
92
+ expect(adoption.source_path).toBe(PROJECT_PATH);
93
+ expect(adoption.profile.anthropic).toEqual({ model: "claude-haiku-4-5" });
94
+ // Atomic ownership invariant: home's codex-flavored fields must NOT appear.
95
+ expect(adoption.profile.reasoning_effort).toBeUndefined();
96
+ expect(adoption.profile.codex).toBeUndefined();
97
+ });
98
+ it("project has ONLY review block (no profile fields) → source=project (ownership claimed)", () => {
99
+ // P9.4 invariant: a non-empty `review:` axis block alone signals
100
+ // profile ownership even without any PROFILE_FIELDS. This preserves
101
+ // B-5 class behaviour where a project declares `review:` + empty
102
+ // `codex: {}` and expects the codex namespace slot to survive
103
+ // adoption (belonging to the project, not inherited from home).
104
+ const adoption = adoptProfile(buildArgs({
105
+ review: { subagent: { provider: "main-native" } },
106
+ anthropic: { model: "claude-haiku-4-5" },
107
+ }, {
108
+ review: { subagent: { provider: "codex", model_id: "gpt-5.4" } },
109
+ codex: {}, // empty namespace — not counted by hasAnyProfileField,
110
+ // but the review block still claims ownership.
111
+ }));
112
+ expect(adoption.source).toBe("project");
113
+ // Home's anthropic namespace MUST NOT leak into the adopted profile.
114
+ expect(adoption.profile.anthropic).toBeUndefined();
115
+ });
116
+ it("project has profile without review block → still source=project", () => {
117
+ // P9.4 change: a project with only a `codex:` namespace (no `review:`
118
+ // axis block) now owns the profile. Previously this was treated as
119
+ // "touched-but-incomplete" and deferred to home.
120
+ const adoption = adoptProfile(buildArgs({
121
+ review: { subagent: { provider: "main-native" } },
122
+ anthropic: { model: "claude-haiku-4-5" },
123
+ }, { codex: { model: "gpt-5.4" } /* no review block */ }));
124
+ expect(adoption.source).toBe("project");
125
+ expect(adoption.profile.codex).toEqual({ model: "gpt-5.4" });
126
+ expect(adoption.profile.anthropic).toBeUndefined();
127
+ });
128
+ it("project has nothing, home has ONLY review block (no profile fields) → source=global", () => {
129
+ // Symmetric to the project-review-block-only case. `claimsProfileOwnership`
130
+ // must recognize a non-empty review block on the home side as an
131
+ // ownership claim, so home config authors can stage review settings
132
+ // without declaring a provider namespace.
133
+ const adoption = adoptProfile(buildArgs({
134
+ review: { subagent: { provider: "codex", model_id: "gpt-5.4" } },
135
+ // No PROFILE_FIELDS — only the review block.
136
+ }, {}));
137
+ expect(adoption.source).toBe("global");
138
+ expect(adoption.source_path).toBe(HOME_PATH);
139
+ // Adopted profile is empty (no PROFILE_FIELDS in home), but
140
+ // ownership was still claimed so we don't fall to source=none.
141
+ expect(adoption.profile).toEqual({});
142
+ });
143
+ it("project has no profile fields, home has → source=global", () => {
144
+ const adoption = adoptProfile(buildArgs({
145
+ review: { subagent: { provider: "codex", model_id: "gpt-5.4" } },
146
+ codex: { model: "gpt-5.4" },
147
+ }, {}));
148
+ expect(adoption.source).toBe("global");
149
+ expect(adoption.source_path).toBe(HOME_PATH);
150
+ expect(adoption.profile.codex).toEqual({ model: "gpt-5.4" });
151
+ });
152
+ it("project has no profile, home has only orthogonal fields → source=none", () => {
153
+ const adoption = adoptProfile(buildArgs({ output_language: "ko" /* orthogonal only */ }, {}));
154
+ expect(adoption.source).toBe("none");
155
+ expect(adoption.source_path).toBeNull();
156
+ expect(adoption.profile).toEqual({});
157
+ });
158
+ it("both absent → source=none (no throw — resolver handles it)", () => {
159
+ const adoption = adoptProfile(buildArgs({}, {}));
160
+ expect(adoption.source).toBe("none");
161
+ expect(adoption.profile).toEqual({});
162
+ });
163
+ it("sameRoot + home has profile → source=global", () => {
164
+ const adoption = adoptProfile({
165
+ home: {
166
+ review: { subagent: { provider: "codex", model_id: "gpt-5.4" } },
167
+ codex: { model: "gpt-5.4" },
168
+ },
169
+ // sameRoot: project is the same as home, so same object content.
170
+ project: {
171
+ review: { subagent: { provider: "codex", model_id: "gpt-5.4" } },
172
+ codex: { model: "gpt-5.4" },
173
+ },
174
+ homePath: HOME_PATH,
175
+ projectPath: PROJECT_PATH,
176
+ sameRoot: true,
177
+ });
178
+ expect(adoption.source).toBe("global");
179
+ expect(adoption.source_path).toBe(HOME_PATH);
180
+ expect(adoption.profile.codex).toEqual({ model: "gpt-5.4" });
181
+ });
182
+ it("sameRoot + no profile → source=none", () => {
183
+ const adoption = adoptProfile({
184
+ home: {},
185
+ project: {},
186
+ homePath: HOME_PATH,
187
+ projectPath: PROJECT_PATH,
188
+ sameRoot: true,
189
+ });
190
+ expect(adoption.source).toBe("none");
191
+ expect(adoption.profile).toEqual({});
192
+ });
193
+ });
194
+ // ─── mergeOrthogonalFields ───
195
+ describe("mergeOrthogonalFields", () => {
196
+ it("last-wins for scalars (project over home)", () => {
197
+ const merged = mergeOrthogonalFields({ output_language: "en", review_mode: "core-axis" }, { output_language: "ko" });
198
+ expect(merged.output_language).toBe("ko");
199
+ expect(merged.review_mode).toBe("core-axis");
200
+ });
201
+ it("union-merges excluded_names", () => {
202
+ const merged = mergeOrthogonalFields({ excluded_names: ["node_modules", ".git"] }, { excluded_names: [".git", "dist"] });
203
+ expect(merged.excluded_names).toEqual(expect.arrayContaining(["node_modules", ".git", "dist"]));
204
+ expect(merged.excluded_names?.length).toBe(3);
205
+ });
206
+ it("drops profile fields entirely", () => {
207
+ const merged = mergeOrthogonalFields({ codex: { model: "gpt-5.4" }, output_language: "ko" }, { anthropic: { model: "claude-haiku-4-5" }, review_mode: "full" });
208
+ expect(merged.codex).toBeUndefined();
209
+ expect(merged.anthropic).toBeUndefined();
210
+ expect(merged.output_language).toBe("ko");
211
+ expect(merged.review_mode).toBe("full");
212
+ });
213
+ it("passes review block through as orthogonal", () => {
214
+ const merged = mergeOrthogonalFields({ review: { subagent: { provider: "codex", model_id: "gpt-5.4" } } }, { review: { subagent: { provider: "main-native" } } });
215
+ // Last-wins: project overrides home.
216
+ expect(merged.review).toEqual({ subagent: { provider: "main-native" } });
217
+ });
218
+ });
219
+ // ─── Regression: migrated Review UX Redesign config ───
220
+ describe("regression — migrated Review UX Redesign config", () => {
221
+ it("project declares review block + codex block → project owns", () => {
222
+ const adoption = adoptProfile(buildArgs({}, // empty global
223
+ {
224
+ review: {
225
+ subagent: { provider: "codex", model_id: "gpt-5.4", effort: "medium" },
226
+ },
227
+ codex: { model: "gpt-5.4", effort: "medium" },
228
+ review_mode: "core-axis",
229
+ }));
230
+ expect(adoption.source).toBe("project");
231
+ expect(adoption.profile.codex).toEqual({ model: "gpt-5.4", effort: "medium" });
232
+ });
233
+ });
@@ -0,0 +1,33 @@
1
+ import fsSync from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ const CODEX_ENV_SIGNALS = ["CODEX_THREAD_ID", "CODEX_CI"];
5
+ /** True when the current process is running under a Codex-owned session. */
6
+ export function detectCodexEnvSignal() {
7
+ for (const name of CODEX_ENV_SIGNALS) {
8
+ if (process.env[name])
9
+ return true;
10
+ }
11
+ return false;
12
+ }
13
+ /**
14
+ * True when the Codex worker path can be used from this process.
15
+ *
16
+ * Both the executable and an auth file are required. This keeps OAuth review
17
+ * execution fail-loud when the host-bound worker is unavailable.
18
+ */
19
+ export function detectCodexBinaryAvailable() {
20
+ const pathEnv = process.env.PATH ?? "";
21
+ let codexOnPath = false;
22
+ for (const dir of pathEnv.split(path.delimiter)) {
23
+ if (!dir)
24
+ continue;
25
+ if (fsSync.existsSync(path.join(dir, "codex"))) {
26
+ codexOnPath = true;
27
+ break;
28
+ }
29
+ }
30
+ if (!codexOnPath)
31
+ return false;
32
+ return fsSync.existsSync(path.join(os.homedir(), ".codex", "auth.json"));
33
+ }
@@ -0,0 +1,186 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import { detectHostRuntime, detectHostRuntimeCategory, detectHostCapabilities, detectClaudeCodeEnvSignal, detectCodexEnvSignal, isClaudeCodeHost, isCodexHost, ENV_ONTO_HOST_RUNTIME, } from "./host-detection.js";
3
+ const HOST_ENV_VARS = [
4
+ ENV_ONTO_HOST_RUNTIME,
5
+ "CLAUDECODE",
6
+ "CLAUDE_PROJECT_DIR",
7
+ "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS",
8
+ "CODEX_THREAD_ID",
9
+ "CODEX_CI",
10
+ "ANTHROPIC_API_KEY",
11
+ "OPENAI_API_KEY",
12
+ "LITELLM_BASE_URL",
13
+ ];
14
+ let savedEnv = {};
15
+ function savePotentiallyConflictingEnv() {
16
+ savedEnv = {};
17
+ for (const key of HOST_ENV_VARS) {
18
+ savedEnv[key] = process.env[key];
19
+ delete process.env[key];
20
+ }
21
+ // Make PATH effectively empty so codex binary detection cannot
22
+ // accidentally trigger on the test runner's shell PATH.
23
+ savedEnv.PATH = process.env.PATH;
24
+ process.env.PATH = "";
25
+ }
26
+ function restoreEnv() {
27
+ for (const [key, value] of Object.entries(savedEnv)) {
28
+ if (value === undefined) {
29
+ delete process.env[key];
30
+ }
31
+ else {
32
+ process.env[key] = value;
33
+ }
34
+ }
35
+ }
36
+ beforeEach(() => {
37
+ savePotentiallyConflictingEnv();
38
+ });
39
+ afterEach(() => {
40
+ restoreEnv();
41
+ });
42
+ describe("detectHostRuntime — priority order", () => {
43
+ it("Priority 1: ONTO_HOST_RUNTIME env override wins over all signals", () => {
44
+ process.env[ENV_ONTO_HOST_RUNTIME] = "standalone";
45
+ process.env.CLAUDECODE = "1";
46
+ process.env.CODEX_THREAD_ID = "abc";
47
+ const result = detectHostRuntime({ host_runtime: "claude" });
48
+ expect(result.hostRuntime).toBe("standalone");
49
+ expect(result.detectionSource).toBe("env_override");
50
+ });
51
+ it("Priority 2: config.host_runtime wins over env signals (no env override)", () => {
52
+ process.env.CLAUDECODE = "1";
53
+ process.env.CODEX_THREAD_ID = "abc";
54
+ const result = detectHostRuntime({ host_runtime: "codex" });
55
+ expect(result.hostRuntime).toBe("codex");
56
+ expect(result.detectionSource).toBe("config_override");
57
+ });
58
+ it("Priority 3: Claude env signal → claude when no override", () => {
59
+ process.env.CLAUDECODE = "1";
60
+ const result = detectHostRuntime({});
61
+ expect(result.hostRuntime).toBe("claude");
62
+ expect(result.detectionSource).toBe("claude_env_signal");
63
+ });
64
+ it("Priority 4: Codex env signal → codex when no Claude signal", () => {
65
+ process.env.CODEX_THREAD_ID = "thread-123";
66
+ const result = detectHostRuntime({});
67
+ expect(result.hostRuntime).toBe("codex");
68
+ expect(result.detectionSource).toBe("codex_env_signal");
69
+ });
70
+ it("Priority 6: standalone default when no signals", () => {
71
+ const result = detectHostRuntime({});
72
+ expect(result.hostRuntime).toBe("standalone");
73
+ expect(result.detectionSource).toBe("standalone_default");
74
+ });
75
+ it("ONTO_HOST_RUNTIME accepts case-insensitive values", () => {
76
+ process.env[ENV_ONTO_HOST_RUNTIME] = " CLAUDE ";
77
+ const result = detectHostRuntime({});
78
+ expect(result.hostRuntime).toBe("claude");
79
+ expect(result.detectionSource).toBe("env_override");
80
+ });
81
+ it("ONTO_HOST_RUNTIME with invalid value is ignored, falls through to next priority", () => {
82
+ process.env[ENV_ONTO_HOST_RUNTIME] = "invalid_host";
83
+ process.env.CLAUDECODE = "1";
84
+ const result = detectHostRuntime({});
85
+ expect(result.hostRuntime).toBe("claude");
86
+ expect(result.detectionSource).toBe("claude_env_signal");
87
+ });
88
+ });
89
+ describe("detectHostRuntime — Claude env signal variants", () => {
90
+ it("CLAUDECODE alone triggers claude", () => {
91
+ process.env.CLAUDECODE = "1";
92
+ expect(detectHostRuntimeCategory({})).toBe("claude");
93
+ });
94
+ it("CLAUDE_PROJECT_DIR alone triggers claude", () => {
95
+ process.env.CLAUDE_PROJECT_DIR = "/some/project";
96
+ expect(detectHostRuntimeCategory({})).toBe("claude");
97
+ });
98
+ it("CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS alone triggers claude", () => {
99
+ process.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS = "1";
100
+ expect(detectHostRuntimeCategory({})).toBe("claude");
101
+ });
102
+ it("Claude takes priority over codex when both env signals present", () => {
103
+ process.env.CLAUDECODE = "1";
104
+ process.env.CODEX_THREAD_ID = "thread-x";
105
+ expect(detectHostRuntimeCategory({})).toBe("claude");
106
+ });
107
+ });
108
+ describe("detectHostCapabilities — capability matrix", () => {
109
+ it("claude host: hasTeamCreate + hasAgentSpawn = true", () => {
110
+ const caps = detectHostCapabilities("claude");
111
+ expect(caps.hasTeamCreate).toBe(true);
112
+ expect(caps.hasAgentSpawn).toBe(true);
113
+ });
114
+ it("codex host: hasTeamCreate + hasAgentSpawn = false", () => {
115
+ const caps = detectHostCapabilities("codex");
116
+ expect(caps.hasTeamCreate).toBe(false);
117
+ expect(caps.hasAgentSpawn).toBe(false);
118
+ });
119
+ it("standalone host: hasTeamCreate + hasAgentSpawn = false", () => {
120
+ const caps = detectHostCapabilities("standalone");
121
+ expect(caps.hasTeamCreate).toBe(false);
122
+ expect(caps.hasAgentSpawn).toBe(false);
123
+ });
124
+ it("LLM provider capabilities are environmental, independent of host", () => {
125
+ process.env.ANTHROPIC_API_KEY = "sk-ant-test";
126
+ process.env.LITELLM_BASE_URL = "http://localhost:4000/v1";
127
+ const claudeCaps = detectHostCapabilities("claude");
128
+ const standaloneCaps = detectHostCapabilities("standalone");
129
+ expect(claudeCaps.hasAnthropicApiKey).toBe(true);
130
+ expect(claudeCaps.hasLiteLlmEndpoint).toBe(true);
131
+ expect(standaloneCaps.hasAnthropicApiKey).toBe(true);
132
+ expect(standaloneCaps.hasLiteLlmEndpoint).toBe(true);
133
+ });
134
+ it("Cross-host LLM combo: Claude main + LiteLLM subagent (capability indicates feasible)", () => {
135
+ process.env.CLAUDECODE = "1";
136
+ process.env.LITELLM_BASE_URL = "http://localhost:4000/v1";
137
+ const result = detectHostRuntime({});
138
+ expect(result.hostRuntime).toBe("claude");
139
+ expect(result.capabilities.hasTeamCreate).toBe(true); // main can orchestrate
140
+ expect(result.capabilities.hasLiteLlmEndpoint).toBe(true); // subagent can use LiteLLM
141
+ });
142
+ });
143
+ describe("Low-level signal detectors", () => {
144
+ it("detectClaudeCodeEnvSignal: returns false when no Claude env vars", () => {
145
+ expect(detectClaudeCodeEnvSignal()).toBe(false);
146
+ });
147
+ it("detectClaudeCodeEnvSignal: returns true on any of the 3 signals", () => {
148
+ process.env.CLAUDECODE = "1";
149
+ expect(detectClaudeCodeEnvSignal()).toBe(true);
150
+ delete process.env.CLAUDECODE;
151
+ process.env.CLAUDE_PROJECT_DIR = "/x";
152
+ expect(detectClaudeCodeEnvSignal()).toBe(true);
153
+ });
154
+ it("detectCodexEnvSignal: returns false when no codex env", () => {
155
+ expect(detectCodexEnvSignal()).toBe(false);
156
+ });
157
+ it("detectCodexEnvSignal: returns true on CODEX_THREAD_ID", () => {
158
+ process.env.CODEX_THREAD_ID = "x";
159
+ expect(detectCodexEnvSignal()).toBe(true);
160
+ });
161
+ it("detectCodexEnvSignal: returns true on CODEX_CI", () => {
162
+ process.env.CODEX_CI = "1";
163
+ expect(detectCodexEnvSignal()).toBe(true);
164
+ });
165
+ });
166
+ describe("Backward-compat shims", () => {
167
+ it("isClaudeCodeHost returns true when CLAUDECODE is set", () => {
168
+ process.env.CLAUDECODE = "1";
169
+ expect(isClaudeCodeHost({})).toBe(true);
170
+ });
171
+ it("isClaudeCodeHost returns false in standalone mode", () => {
172
+ expect(isClaudeCodeHost({})).toBe(false);
173
+ });
174
+ it("isCodexHost returns true when CODEX_THREAD_ID is set", () => {
175
+ process.env.CODEX_THREAD_ID = "x";
176
+ expect(isCodexHost({})).toBe(true);
177
+ });
178
+ it("isCodexHost returns false in standalone mode", () => {
179
+ expect(isCodexHost({})).toBe(false);
180
+ });
181
+ it("env override beats env signal in shim", () => {
182
+ process.env[ENV_ONTO_HOST_RUNTIME] = "standalone";
183
+ process.env.CLAUDECODE = "1";
184
+ expect(isClaudeCodeHost({})).toBe(false);
185
+ });
186
+ });
@@ -0,0 +1,21 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ const NEW_LAYOUT_ROOT = ".onto";
4
+ const cache = new Map();
5
+ export function resolveInstallationPath(kind, installRoot) {
6
+ const cacheKey = `${installRoot}::${kind}`;
7
+ const cached = cache.get(cacheKey);
8
+ if (cached !== undefined)
9
+ return cached;
10
+ const canonicalPath = path.join(installRoot, NEW_LAYOUT_ROOT, kind);
11
+ if (fs.existsSync(canonicalPath)) {
12
+ cache.set(cacheKey, canonicalPath);
13
+ return canonicalPath;
14
+ }
15
+ throw new Error(`[installation-paths] .onto/${kind}/ not found under ${installRoot}. ` +
16
+ `Installation resources must live under the .onto/ layout.`);
17
+ }
18
+ /** Test helper — clears the cache so tests can swap fixture installations. */
19
+ export function __resetInstallationPathCacheForTesting() {
20
+ cache.clear();
21
+ }