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,81 +0,0 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
- import { fileURLToPath } from "node:url";
4
- import { describe, it, expect } from "vitest";
5
- import { canonicalizeLensId, loadCoreLensRegistry } from "./lens-registry.js";
6
- function resolveRegistryPath() {
7
- const here = path.dirname(fileURLToPath(import.meta.url));
8
- let cur = here;
9
- const root = path.parse(cur).root;
10
- while (cur !== root) {
11
- const candidate = path.join(cur, ".onto", "authority", "core-lens-registry.yaml");
12
- if (fs.existsSync(candidate))
13
- return candidate;
14
- cur = path.dirname(cur);
15
- }
16
- throw new Error(".onto/authority/core-lens-registry.yaml not found");
17
- }
18
- describe("canonicalizeLensId — Phase 0 dual-read (W-A-01)", () => {
19
- it("strips onto_ prefix from legacy IDs", () => {
20
- expect(canonicalizeLensId("onto_logic")).toBe("logic");
21
- expect(canonicalizeLensId("onto_axiology")).toBe("axiology");
22
- expect(canonicalizeLensId("onto_synthesize")).toBe("synthesize");
23
- });
24
- it("is idempotent — bare IDs pass through unchanged", () => {
25
- expect(canonicalizeLensId("logic")).toBe("logic");
26
- expect(canonicalizeLensId("axiology")).toBe("axiology");
27
- expect(canonicalizeLensId("synthesize")).toBe("synthesize");
28
- });
29
- it("handles all 10 rename targets", () => {
30
- const targets = [
31
- "logic", "structure", "dependency", "semantics", "pragmatics",
32
- "evolution", "coverage", "conciseness", "axiology", "synthesize",
33
- ];
34
- for (const bare of targets) {
35
- expect(canonicalizeLensId(`onto_${bare}`)).toBe(bare);
36
- expect(canonicalizeLensId(bare)).toBe(bare);
37
- }
38
- });
39
- it("does not strip prefix when not exact onto_ match", () => {
40
- expect(canonicalizeLensId("ontology")).toBe("ontology");
41
- expect(canonicalizeLensId("onto-logic")).toBe("onto-logic");
42
- expect(canonicalizeLensId("custom_logic")).toBe("custom_logic");
43
- });
44
- });
45
- describe("loadCoreLensRegistry — core-axis composition contract (v0.2.1)", () => {
46
- // These assertions lock the v0.2.1 cost-constrained Pareto-optimal
47
- // composition into a test so that any future registry edit that changes
48
- // the core-axis set must also update this test (intentional checkpoint).
49
- // SSOT: .onto/authority/core-lens-registry.yaml; empirical basis:
50
- // development-records/benchmark/20260419-lens-contribution-analysis.md.
51
- const registry = loadCoreLensRegistry();
52
- it("core_axis_lens_ids contains exactly the v0.2.1 6-lens set", () => {
53
- expect(registry.core_axis_lens_ids).toHaveLength(6);
54
- expect([...registry.core_axis_lens_ids].sort()).toEqual([
55
- "axiology",
56
- "coverage",
57
- "evolution",
58
- "logic",
59
- "semantics",
60
- "structure",
61
- ]);
62
- });
63
- it("full_review_lens_ids remains 9-lens", () => {
64
- expect(registry.full_review_lens_ids).toHaveLength(9);
65
- });
66
- it("always_include_lens_ids is [axiology]", () => {
67
- expect(registry.always_include_lens_ids).toEqual(["axiology"]);
68
- });
69
- it("every always_include lens is present in core_axis composition", () => {
70
- for (const id of registry.always_include_lens_ids) {
71
- expect(registry.core_axis_lens_ids).toContain(id);
72
- }
73
- });
74
- // F-E1 (2nd review): loader currently does not consume `schema_version`.
75
- // Assert the raw field exists at the registry seat — forces any future
76
- // recomposition to bump the field together with contents.
77
- it("registry file declares schema_version: 2 (raw-text guard)", () => {
78
- const text = fs.readFileSync(resolveRegistryPath(), "utf8");
79
- expect(text).toMatch(/^schema_version:\s*2\b/m);
80
- });
81
- });
@@ -1,22 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { startsWithDirPrefix } from "./path-normalization.js";
3
- describe("startsWithDirPrefix — segment-bound check", () => {
4
- it("matches exact segment boundary with trailing slash", () => {
5
- expect(startsWithDirPrefix(".onto/principles/foo.md", ".onto/principles/")).toBe(true);
6
- expect(startsWithDirPrefix(".onto/principles/sub/bar.md", ".onto/principles")).toBe(true);
7
- });
8
- it("rejects mid-segment prefix collision", () => {
9
- expect(startsWithDirPrefix(".onto/principlesABC/foo.md", ".onto/principles")).toBe(false);
10
- expect(startsWithDirPrefix(".onto/principles_backup/foo.md", ".onto/principles")).toBe(false);
11
- expect(startsWithDirPrefix(".onto/authorityX/foo.md", ".onto/authority")).toBe(false);
12
- });
13
- it("rejects the directory itself (must be a path under the directory)", () => {
14
- expect(startsWithDirPrefix(".onto/principles", ".onto/principles")).toBe(false);
15
- expect(startsWithDirPrefix(".onto/principles/", ".onto/principles")).toBe(false);
16
- });
17
- it("treats trailing slash in `dir` argument uniformly", () => {
18
- const p = ".onto/principles/foo.md";
19
- expect(startsWithDirPrefix(p, ".onto/principles")).toBe(true);
20
- expect(startsWithDirPrefix(p, ".onto/principles/")).toBe(true);
21
- });
22
- });
@@ -1,72 +0,0 @@
1
- /**
2
- * Plugin install path resolver.
3
- *
4
- * onto's canonical authority files (process.md, .onto/processes/, learning-rules.md,
5
- * .onto/roles/, etc.) are read at runtime from the install location. The path
6
- * resolution must support multiple install scenarios:
7
- *
8
- * 1. Claude Code plugin install: `~/.claude/plugins/onto/`
9
- * 2. Standalone clone + symlink: arbitrary path
10
- * 3. Repo development: the repo itself is the plugin
11
- *
12
- * # Resolution priority (highest first)
13
- *
14
- * 1. ONTO_PLUGIN_DIR env var (explicit override)
15
- * 2. ~/.claude/plugins/onto/ (Claude Code install default)
16
- * 3. (None) → returns null; caller decides its explicit default
17
- *
18
- * # Why no automatic repo-relative default
19
- *
20
- * Repo-relative paths are correct only during local dev. In production
21
- * (npm-installed CLI, Claude Code plugin), the plugin lives elsewhere.
22
- * Returning null when no install is detected lets the caller emit a clear
23
- * "set ONTO_PLUGIN_DIR or install via /plugin install" error rather than
24
- * silently using the wrong source tree.
25
- */
26
- import fsSync from "node:fs";
27
- import os from "node:os";
28
- import path from "node:path";
29
- export const ENV_ONTO_PLUGIN_DIR = "ONTO_PLUGIN_DIR";
30
- /** The default Claude Code plugin install location. */
31
- export const CLAUDE_CODE_PLUGIN_DEFAULT_PATH = path.join(os.homedir(), ".claude", "plugins", "onto");
32
- /**
33
- * Resolve the onto plugin install directory.
34
- *
35
- * Returns null when no install is found. Callers in the plugin's own runtime
36
- * (where __dirname can locate the source) may use repo-relative paths as a
37
- * default; documentation reads should error out when this returns null.
38
- */
39
- export function resolvePluginPath() {
40
- const envOverride = process.env[ENV_ONTO_PLUGIN_DIR];
41
- if (typeof envOverride === "string" && envOverride.length > 0) {
42
- const expanded = expandHome(envOverride);
43
- return { pluginDir: expanded, source: "env_override" };
44
- }
45
- if (fsSync.existsSync(CLAUDE_CODE_PLUGIN_DEFAULT_PATH)) {
46
- return {
47
- pluginDir: CLAUDE_CODE_PLUGIN_DEFAULT_PATH,
48
- source: "claude_code_install_default",
49
- };
50
- }
51
- return null;
52
- }
53
- /**
54
- * Resolve a plugin-relative path. Returns null when the plugin install is
55
- * not found.
56
- *
57
- * Example: `resolvePluginRelativePath(".onto/processes/reconstruct.md")` returns
58
- * `/home/user/.claude/plugins/onto/.onto/processes/reconstruct.md` (or whatever
59
- * `ONTO_PLUGIN_DIR` resolves to).
60
- */
61
- export function resolvePluginRelativePath(relativePath) {
62
- const resolution = resolvePluginPath();
63
- if (!resolution)
64
- return null;
65
- return path.join(resolution.pluginDir, relativePath);
66
- }
67
- function expandHome(p) {
68
- if (p.startsWith("~/") || p === "~") {
69
- return path.join(os.homedir(), p.slice(p === "~" ? 1 : 2));
70
- }
71
- return p;
72
- }
@@ -1,95 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
- import { mkdirSync, mkdtempSync, rmSync } from "node:fs";
3
- import { tmpdir } from "node:os";
4
- import path from "node:path";
5
- import { resolvePluginPath, resolvePluginRelativePath, ENV_ONTO_PLUGIN_DIR, } from "./plugin-path.js";
6
- let savedEnvOverride;
7
- let savedHomeOverride;
8
- let scratchDir;
9
- beforeEach(() => {
10
- savedEnvOverride = process.env[ENV_ONTO_PLUGIN_DIR];
11
- delete process.env[ENV_ONTO_PLUGIN_DIR];
12
- // Redirect HOME to a scratch dir so the real ~/.claude/plugins/onto/ doesn't
13
- // accidentally satisfy the default-detection branch.
14
- savedHomeOverride = process.env.HOME;
15
- scratchDir = mkdtempSync(path.join(tmpdir(), "plugin-path-test-"));
16
- process.env.HOME = scratchDir;
17
- });
18
- afterEach(() => {
19
- if (savedEnvOverride === undefined) {
20
- delete process.env[ENV_ONTO_PLUGIN_DIR];
21
- }
22
- else {
23
- process.env[ENV_ONTO_PLUGIN_DIR] = savedEnvOverride;
24
- }
25
- if (savedHomeOverride === undefined) {
26
- delete process.env.HOME;
27
- }
28
- else {
29
- process.env.HOME = savedHomeOverride;
30
- }
31
- try {
32
- rmSync(scratchDir, { recursive: true, force: true });
33
- }
34
- catch { }
35
- });
36
- describe("resolvePluginPath", () => {
37
- it("returns null when no env override and no Claude install present", () => {
38
- // os.homedir() typically caches HOME at process start, so resolvePluginPath
39
- // may still see the original ~/.claude/plugins/onto/ if it exists.
40
- // This test asserts the env-override priority over both, by NOT setting it.
41
- // The base-case "returns null" is harder to assert in environments where
42
- // ~/.claude/plugins/onto/ exists; we only assert the absence of env crash.
43
- const result = resolvePluginPath();
44
- // Either null (clean env) or claude_code_install_default (real install)
45
- if (result !== null) {
46
- expect(result.source).toBe("claude_code_install_default");
47
- }
48
- });
49
- it("env override beats default detection", () => {
50
- const overridePath = mkdtempSync(path.join(tmpdir(), "onto-plugin-override-"));
51
- try {
52
- process.env[ENV_ONTO_PLUGIN_DIR] = overridePath;
53
- const result = resolvePluginPath();
54
- expect(result).not.toBeNull();
55
- expect(result.pluginDir).toBe(overridePath);
56
- expect(result.source).toBe("env_override");
57
- }
58
- finally {
59
- rmSync(overridePath, { recursive: true, force: true });
60
- }
61
- });
62
- it("env override expands ~ to HOME", () => {
63
- // Create a fake plugin dir under the scratched HOME.
64
- const fakePluginDir = path.join(scratchDir, "myplugin");
65
- mkdirSync(fakePluginDir, { recursive: true });
66
- process.env[ENV_ONTO_PLUGIN_DIR] = "~/myplugin";
67
- const result = resolvePluginPath();
68
- expect(result).not.toBeNull();
69
- expect(result.pluginDir).toBe(fakePluginDir);
70
- expect(result.source).toBe("env_override");
71
- });
72
- });
73
- describe("resolvePluginRelativePath", () => {
74
- it("appends relative path to resolved plugin dir", () => {
75
- const overridePath = mkdtempSync(path.join(tmpdir(), "onto-plugin-rel-"));
76
- try {
77
- process.env[ENV_ONTO_PLUGIN_DIR] = overridePath;
78
- const result = resolvePluginRelativePath(".onto/processes/reconstruct.md");
79
- expect(result).toBe(path.join(overridePath, ".onto/processes/reconstruct.md"));
80
- }
81
- finally {
82
- rmSync(overridePath, { recursive: true, force: true });
83
- }
84
- });
85
- it("returns null when plugin path cannot be resolved (env unset, no install)", () => {
86
- // Skip if real ~/.claude/plugins/onto/ exists in the test runner env.
87
- // We can't reliably test the "returns null" branch in CI environments
88
- // where Claude Code is installed.
89
- const result = resolvePluginRelativePath(".onto/processes/reconstruct.md");
90
- if (result !== null) {
91
- // Real install present — verify it ends with the relative path.
92
- expect(result.endsWith(".onto/processes/reconstruct.md")).toBe(true);
93
- }
94
- });
95
- });
@@ -1,344 +0,0 @@
1
- import { isEvidenceUnverified } from "../../../../scope-runtime/types.js";
2
- // ─── Main ───
3
- /**
4
- * Run Compile Defense — 2-layer verification.
5
- *
6
- * Layer 1 (Checklist): Every non-invalidated constraint is in Section 3.
7
- * Layer 2 (Audit Pass): inject reflected, defer non-interfering,
8
- * override non-reflected, traceability chain CST→IMPL→CHG→VAL complete.
9
- *
10
- * Pure function: no side effects.
11
- */
12
- export function compileDefense(state, buildSpec, deltaSet, validationPlan, brownfieldDetail, brownfieldContext) {
13
- const violations = [];
14
- const warnings = [];
15
- checkLayer1(state, buildSpec, violations);
16
- checkLayer2(state, buildSpec, deltaSet, validationPlan, violations);
17
- checkLayer3(state, buildSpec, deltaSet, brownfieldDetail, brownfieldContext, warnings);
18
- const errors = violations;
19
- if (errors.length === 0) {
20
- return { passed: true, warnings };
21
- }
22
- return { passed: false, violations: errors, warnings };
23
- }
24
- // ─── Layer 1: Checklist ───
25
- function checkLayer1(state, buildSpec, violations) {
26
- const section3Ids = new Set(buildSpec.section3.map((e) => e.constraint_id));
27
- for (const c of state.constraint_pool.constraints) {
28
- if (c.status === "invalidated")
29
- continue;
30
- if (!section3Ids.has(c.constraint_id)) {
31
- violations.push({
32
- rule: "L1-checklist",
33
- detail: `${c.constraint_id ?? "UNKNOWN-CST"} is not referenced in Build Spec Section 3`,
34
- });
35
- }
36
- }
37
- }
38
- // ─── Layer 2: Audit Pass ───
39
- function checkLayer2(state, buildSpec, deltaSet, validationPlan, violations) {
40
- const implIds = new Set(buildSpec.section4.map((e) => e.impl_id));
41
- const changeCstIds = new Set(deltaSet.changes.flatMap((c) => c.related_cst));
42
- const changeFilePaths = new Set(deltaSet.changes.map((c) => normalizeFilePath(c.file_path)));
43
- const valCstIds = new Set(validationPlan.map((v) => v.related_cst));
44
- for (const c of state.constraint_pool.constraints) {
45
- if (c.status === "invalidated")
46
- continue;
47
- const decision = c.decision;
48
- if (!decision)
49
- continue;
50
- switch (decision) {
51
- case "inject":
52
- checkInjectReflected(c, buildSpec, implIds, changeCstIds, valCstIds, violations);
53
- break;
54
- case "defer":
55
- checkDeferNonInterfering(c, deltaSet, changeFilePaths, violations);
56
- break;
57
- case "override":
58
- checkOverrideNonReflected(c, deltaSet, changeFilePaths, violations);
59
- break;
60
- default:
61
- violations.push({
62
- rule: "L2-decision-unexpected",
63
- detail: `${c.constraint_id ?? "UNKNOWN-CST"} has unexpected decision "${decision ?? "UNKNOWN"}" at compile time`,
64
- });
65
- break;
66
- }
67
- }
68
- // Traceability chain: every IMPL has at least one CHG
69
- checkImplHasChanges(buildSpec, deltaSet, violations);
70
- // Reverse traceability: every CHG.related_impl references a valid IMPL
71
- checkChangesReferenceValidImpls(buildSpec, deltaSet, implIds, violations);
72
- // inject constraints should have edge cases in validation plan
73
- checkInjectEdgeCases(state, validationPlan, violations);
74
- }
75
- /** inject → must have IMPL, CHG referencing this CST, and VAL item */
76
- function checkInjectReflected(c, buildSpec, implIds, changeCstIds, valCstIds, violations) {
77
- // CST → IMPL
78
- const relatedImpls = buildSpec.section4.filter((impl) => impl.related_cst.includes(c.constraint_id));
79
- if (relatedImpls.length === 0) {
80
- violations.push({
81
- rule: "L2-inject-impl",
82
- detail: `${c.constraint_id ?? "UNKNOWN-CST"} (inject) has no IMPL in Section 4`,
83
- });
84
- }
85
- // CST → CHG
86
- if (!changeCstIds.has(c.constraint_id)) {
87
- violations.push({
88
- rule: "L2-inject-chg",
89
- detail: `${c.constraint_id ?? "UNKNOWN-CST"} (inject) has no CHG in delta-set`,
90
- });
91
- }
92
- // CST → VAL
93
- if (!valCstIds.has(c.constraint_id)) {
94
- violations.push({
95
- rule: "L2-inject-val",
96
- detail: `${c.constraint_id ?? "UNKNOWN-CST"} (inject) has no VAL in validation-plan`,
97
- });
98
- }
99
- }
100
- /** defer → source_refs files must not appear in delta-set changes */
101
- function checkDeferNonInterfering(c, deltaSet, changeFilePaths, violations) {
102
- for (const ref of c.source_refs) {
103
- if (changeFilePaths.has(normalizeFilePath(ref.source))) {
104
- violations.push({
105
- rule: "L2-defer-interfere",
106
- detail: `${c.constraint_id ?? "UNKNOWN-CST"} (defer) source_ref "${ref.source ?? "UNKNOWN"}" is modified in delta-set. 간섭 여부를 확인하세요.`,
107
- });
108
- }
109
- }
110
- }
111
- /** override → source_refs files must not have changes related to this CST */
112
- function checkOverrideNonReflected(c, deltaSet, changeFilePaths, violations) {
113
- for (const change of deltaSet.changes) {
114
- if (change.related_cst.includes(c.constraint_id) &&
115
- c.source_refs.some((ref) => normalizeFilePath(ref.source) === normalizeFilePath(change.file_path))) {
116
- violations.push({
117
- rule: "L2-override-reflected",
118
- detail: `${c.constraint_id ?? "UNKNOWN-CST"} (override) is reflected in delta-set change ${change.change_id ?? "UNKNOWN-CHG"}`,
119
- });
120
- }
121
- }
122
- }
123
- /** Every CHG with non-empty related_impl must reference valid IMPLs in Section 4 */
124
- function checkChangesReferenceValidImpls(buildSpec, deltaSet, implIds, violations) {
125
- for (const change of deltaSet.changes) {
126
- // CHG with empty related_impl is allowed (e.g. defer/override context changes)
127
- if (change.related_impl.length === 0)
128
- continue;
129
- for (const implId of change.related_impl) {
130
- if (!implIds.has(implId)) {
131
- violations.push({
132
- rule: "L2-chg-orphan-impl",
133
- detail: `${change.change_id ?? "UNKNOWN-CHG"} references ${implId ?? "UNKNOWN-IMPL"} which does not exist in Section 4`,
134
- });
135
- }
136
- }
137
- }
138
- }
139
- /** Every IMPL must have at least one CHG */
140
- function checkImplHasChanges(buildSpec, deltaSet, violations) {
141
- const implsWithChanges = new Set(deltaSet.changes.flatMap((c) => c.related_impl));
142
- for (const impl of buildSpec.section4) {
143
- if (!implsWithChanges.has(impl.impl_id)) {
144
- violations.push({
145
- rule: "L2-impl-no-chg",
146
- detail: `${impl.impl_id ?? "UNKNOWN-IMPL"} has no CHG in delta-set`,
147
- });
148
- }
149
- }
150
- }
151
- /** inject constraints should have at least 1 edge_case in validation plan */
152
- function checkInjectEdgeCases(state, validationPlan, violations) {
153
- const injectConstraints = state.constraint_pool.constraints.filter((c) => c.status !== "invalidated" && c.decision === "inject");
154
- for (const c of injectConstraints) {
155
- const valItem = validationPlan.find((v) => v.related_cst === c.constraint_id);
156
- if (valItem && (!valItem.edge_cases || valItem.edge_cases.length === 0)) {
157
- violations.push({
158
- rule: "L2-inject-edge-case",
159
- detail: `${c.constraint_id ?? "UNKNOWN-CST"} (inject) has no edge_cases in validation plan item ${valItem.val_id ?? "UNKNOWN-VAL"}`,
160
- });
161
- }
162
- }
163
- }
164
- // ─── Layer 3: Evidence Quality Warnings (non-blocking) ───
165
- function checkLayer3(state, buildSpec, deltaSet, brownfieldDetail, brownfieldContext, warnings) {
166
- checkUnverifiedInject(state, warnings);
167
- checkPolicyChangeRequired(state, warnings);
168
- checkStateCompleteness(state, buildSpec, brownfieldDetail, warnings);
169
- checkSharedResource(deltaSet, warnings);
170
- checkInvariantCoverage(state, buildSpec, deltaSet, brownfieldDetail, warnings);
171
- checkBrownfieldCoverage(deltaSet, brownfieldContext, warnings);
172
- }
173
- /**
174
- * Warn (not block) when a required+inject constraint has unverified evidence.
175
- */
176
- function checkUnverifiedInject(state, warnings) {
177
- for (const c of state.constraint_pool.constraints) {
178
- if (c.status === "invalidated")
179
- continue;
180
- if (c.decision !== "inject")
181
- continue;
182
- if (c.severity !== "required")
183
- continue;
184
- if (isEvidenceUnverified(c.evidence_status)) {
185
- warnings.push({
186
- rule: "L3-unverified-inject",
187
- detail: `${c.constraint_id} (required, inject) has evidence_status="${c.evidence_status}". 정책 문서에서 확인되지 않은 가정이 구현에 포함됩니다.${c.evidence_note ? ` Note: ${c.evidence_note}` : ""}`,
188
- });
189
- }
190
- }
191
- }
192
- /**
193
- * Warn (not block) when an inject constraint has requires_policy_change=true.
194
- * Independent from L3-unverified-inject (evidence quality).
195
- */
196
- function checkPolicyChangeRequired(state, warnings) {
197
- for (const c of state.constraint_pool.constraints) {
198
- if (c.status === "invalidated")
199
- continue;
200
- if (c.decision !== "inject")
201
- continue;
202
- if (!c.requires_policy_change)
203
- continue;
204
- warnings.push({
205
- rule: "L3-policy-change-required",
206
- detail: `${c.constraint_id ?? "UNKNOWN-CST"} (inject)는 기존 정책 변경을 전제합니다. 구현 전 법무/정책 검토가 필요합니다.${c.evidence_note ? ` 참고: ${c.evidence_note}` : ""}`,
207
- });
208
- }
209
- }
210
- /**
211
- * Warn when brownfieldDetail.enums defines enum values that are not
212
- * mentioned anywhere in the build spec (constraint summaries, IMPL related_cst,
213
- * or brownfield sections). Detects missing state mappings
214
- * (e.g., NOSHOW_BOTH not covered by any implementation).
215
- */
216
- function checkStateCompleteness(state, buildSpec, brownfieldDetail, warnings) {
217
- if (!brownfieldDetail?.enums || brownfieldDetail.enums.length === 0)
218
- return;
219
- // Collect all relevant text from constraints, impl references, and brownfield content
220
- const constraintText = state.constraint_pool.constraints
221
- .map((c) => `${c.summary} ${c.selected_option ?? ""}`)
222
- .join(" ");
223
- const sectionText = brownfieldDetail.sections
224
- .map((s) => s.content)
225
- .join(" ");
226
- const searchText = `${constraintText} ${sectionText}`;
227
- for (const enumDef of brownfieldDetail.enums) {
228
- const uncovered = enumDef.values.filter((val) => !searchText.includes(val));
229
- if (uncovered.length > 0) {
230
- warnings.push({
231
- rule: "L3-state-completeness",
232
- detail: `${enumDef.name} (source: ${enumDef.source})의 다음 값이 구현 계획에서 언급되지 않습니다: ${uncovered.join(", ")}. 상태 매핑이 누락되었을 수 있습니다.`,
233
- });
234
- }
235
- }
236
- }
237
- /**
238
- * Warn when multiple *separate* CHGs targeting the same file come from different CSTs.
239
- * A single CHG referencing multiple CSTs is NOT flagged (one change serving multiple constraints is normal).
240
- */
241
- function checkSharedResource(deltaSet, warnings) {
242
- // Build: file_path → Map<cst_id, Set<change_id>>
243
- const fileCstChanges = new Map();
244
- for (const chg of deltaSet.changes) {
245
- if (chg.related_cst.length === 0)
246
- continue;
247
- if (!fileCstChanges.has(chg.file_path)) {
248
- fileCstChanges.set(chg.file_path, new Map());
249
- }
250
- const cstMap = fileCstChanges.get(chg.file_path);
251
- for (const cst of chg.related_cst) {
252
- if (!cstMap.has(cst))
253
- cstMap.set(cst, new Set());
254
- cstMap.get(cst).add(chg.change_id);
255
- }
256
- }
257
- for (const [filePath, cstMap] of fileCstChanges) {
258
- // Only warn if there are 2+ CSTs AND they come from different CHGs
259
- if (cstMap.size < 2)
260
- continue;
261
- // Check that at least 2 CSTs have non-overlapping CHG sets
262
- const chgSets = [...cstMap.values()];
263
- let hasDistinctSources = false;
264
- for (let i = 0; i < chgSets.length && !hasDistinctSources; i++) {
265
- for (let j = i + 1; j < chgSets.length; j++) {
266
- const overlap = [...chgSets[i]].some(id => chgSets[j].has(id));
267
- if (!overlap) {
268
- hasDistinctSources = true;
269
- break;
270
- }
271
- }
272
- }
273
- if (hasDistinctSources) {
274
- const cstIds = [...cstMap.keys()];
275
- warnings.push({
276
- rule: "L3-shared-resource",
277
- detail: `${filePath}을(를) ${cstIds.length}개 CST가 별개의 변경으로 동시에 수정합니다: ${cstIds.join(", ")}. 조합 충돌 여부를 확인하세요.`,
278
- });
279
- }
280
- }
281
- }
282
- /**
283
- * Warn when delta-set changes modify files listed in brownfieldDetail.invariants
284
- * but the invariant is not mentioned in any constraint or IMPL.
285
- */
286
- function checkInvariantCoverage(state, buildSpec, deltaSet, brownfieldDetail, warnings) {
287
- if (!brownfieldDetail?.invariants || brownfieldDetail.invariants.length === 0)
288
- return;
289
- const changeFilePaths = new Set(deltaSet.changes.map(c => normalizeFilePath(c.file_path)));
290
- // Build search text from constraints + IMPL references in buildSpec
291
- const constraintText = state.constraint_pool.constraints
292
- .map(c => `${c.constraint_id} ${c.summary} ${c.selected_option ?? ""} ${c.decision ?? ""}`)
293
- .join(" ");
294
- const implText = buildSpec.section4
295
- .map(impl => `${impl.impl_id} ${impl.related_cst.join(" ")}`)
296
- .join(" ");
297
- const searchText = `${constraintText} ${implText}`;
298
- for (const inv of brownfieldDetail.invariants) {
299
- // Check if any affected_files are in the delta-set changes
300
- const affected = inv.affected_files?.some(f => changeFilePaths.has(normalizeFilePath(f))) ?? false;
301
- if (!affected)
302
- continue;
303
- // Check if the invariant is mentioned in constraints or IMPL
304
- const mentioned = searchText.includes(inv.name) || searchText.includes(inv.description.slice(0, 30));
305
- if (!mentioned) {
306
- warnings.push({
307
- rule: "L3-invariant-uncovered",
308
- detail: `불변 제약 "${inv.name}" (${inv.description})의 영향 파일이 delta-set에서 변경되지만, 구현 계획에서 언급되지 않습니다.`,
309
- });
310
- }
311
- }
312
- }
313
- // ─── Path normalization for compile-defense ───
314
- /**
315
- * Normalize a file path for string comparison.
316
- * Removes leading ./, trailing /, and collapses consecutive /.
317
- * Does NOT resolve to absolute paths — keeps relative paths as-is.
318
- */
319
- export function normalizeFilePath(p) {
320
- return p
321
- .replace(/^\.\//, "")
322
- .replace(/\/+/g, "/")
323
- .replace(/\/$/, "");
324
- }
325
- // ─── Layer 3: Brownfield Coverage ───
326
- /**
327
- * Warn when delta-set modify/delete targets a file not in brownfield.related_files.
328
- * This indicates the agent planned a change without scanning the existing code.
329
- */
330
- function checkBrownfieldCoverage(deltaSet, brownfieldContext, warnings) {
331
- if (!brownfieldContext?.related_files || brownfieldContext.related_files.length === 0)
332
- return;
333
- const brownfieldPaths = new Set(brownfieldContext.related_files.map((f) => normalizeFilePath(f.path)));
334
- const modifyDeleteChanges = deltaSet.changes.filter((c) => c.action === "modify" || c.action === "delete");
335
- for (const chg of modifyDeleteChanges) {
336
- const normalized = normalizeFilePath(chg.file_path);
337
- if (!brownfieldPaths.has(normalized)) {
338
- warnings.push({
339
- rule: "L3-modify-not-in-brownfield",
340
- detail: `${chg.file_path} (${chg.action})가 brownfield.related_files에 등록되지 않았습니다. 기존 코드 구조를 확인했는지 검증하세요.`,
341
- });
342
- }
343
- }
344
- }