onto-mcp 0.3.2 → 0.4.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 (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 +135 -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 +149 -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 +207 -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,150 +0,0 @@
1
- /**
2
- * Phase 3 Promote — Collection health snapshot (Step 9d).
3
- *
4
- * Design authority:
5
- * - .onto/processes/learn/promote.md §9 (Collection Health Snapshot)
6
- * - learn-phase3-design-v4.md §1.3 (P-15 metric assembly)
7
- *
8
- * Responsibility:
9
- * - Aggregate axis distribution, purpose distribution, judgment ratio,
10
- * and other operator-facing metrics from the global learning pool.
11
- * - Roll up panel-derived counts (creation gate failures, axis tag
12
- * re-evaluation, applied learnings yes/no) supplied by the orchestrator.
13
- * - Identify agents whose learning files have crossed the separation
14
- * trigger threshold (>100 lines per agent file).
15
- *
16
- * Scope:
17
- * - Pure aggregator. No I/O. No mutation.
18
- * - Uses ParsedLearningItem fields and a few report-side counters that the
19
- * orchestrator computes after panel review and audit completion.
20
- *
21
- * Note: time-series comparison (Previous/Δ) is intentionally NOT implemented
22
- * (promote.md §9 final paragraph). The user manually compares against the
23
- * prior promote report.
24
- */
25
- // ---------------------------------------------------------------------------
26
- // Constants
27
- // ---------------------------------------------------------------------------
28
- /**
29
- * Separation trigger: when an agent's learning file exceeds this row count
30
- * the operator is prompted to consider splitting it (promote.md §9 footnote).
31
- */
32
- export const SEPARATION_TRIGGER_LINE_COUNT = 100;
33
- function classifyAxis(item) {
34
- const hasMethodology = item.applicability_tags.includes("methodology");
35
- const hasDomain = item.applicability_tags.some((t) => t.startsWith("domain/"));
36
- if (hasMethodology && hasDomain)
37
- return "dual";
38
- if (hasMethodology)
39
- return "methodology_only";
40
- if (hasDomain)
41
- return "domain_only";
42
- return null; // unclassified — not counted in distribution
43
- }
44
- function tallyAxis(items) {
45
- const counts = {
46
- methodology_only: 0,
47
- domain_only: 0,
48
- dual: 0,
49
- };
50
- for (const item of items) {
51
- const axis = classifyAxis(item);
52
- if (axis !== null)
53
- counts[axis] += 1;
54
- }
55
- return counts;
56
- }
57
- function tallyPurpose(items) {
58
- const counts = { guardrail: 0, foundation: 0, convention: 0, insight: 0 };
59
- for (const item of items) {
60
- if (item.role === null)
61
- continue;
62
- counts[item.role] += 1;
63
- }
64
- return counts;
65
- }
66
- function judgmentRatio(items) {
67
- if (items.length === 0)
68
- return 0;
69
- const judgmentCount = items.filter((i) => i.type === "judgment").length;
70
- return Math.round((judgmentCount / items.length) * 1000) / 10; // 1 decimal
71
- }
72
- function pct(num, denom) {
73
- if (denom === 0)
74
- return 0;
75
- return Math.round((num / denom) * 1000) / 10;
76
- }
77
- /**
78
- * Identify agents whose learning files have grown past the separation
79
- * trigger threshold. Uses the highest line_number observed per agent in the
80
- * given item pool, or the explicit `agent_line_counts` override when
81
- * provided. The override exists because line_number reflects the position
82
- * of an item, not the file's total line count — for empty/sparse files the
83
- * override gives a more accurate signal.
84
- */
85
- function findSeparationTriggers(items, override) {
86
- const triggered = [];
87
- if (override) {
88
- for (const [agent, count] of override) {
89
- if (count > SEPARATION_TRIGGER_LINE_COUNT)
90
- triggered.push(agent);
91
- }
92
- }
93
- else {
94
- const maxLineByAgent = new Map();
95
- for (const item of items) {
96
- const current = maxLineByAgent.get(item.agent_id) ?? 0;
97
- if (item.line_number > current) {
98
- maxLineByAgent.set(item.agent_id, item.line_number);
99
- }
100
- }
101
- for (const [agent, count] of maxLineByAgent) {
102
- if (count > SEPARATION_TRIGGER_LINE_COUNT)
103
- triggered.push(agent);
104
- }
105
- }
106
- return triggered.sort();
107
- }
108
- /**
109
- * Build the snapshot from already-assembled report components.
110
- *
111
- * Why pass in panel_verdicts/retirement_candidates rather than recomputing:
112
- * - Avoids drifting from the report state. The report is canonical; the
113
- * snapshot is a derived view of it.
114
- * - Lets the orchestrator decide whether to recompute — e.g., a re-run
115
- * after audit changes might want to refresh metrics without re-running
116
- * panel review.
117
- *
118
- * Note on `event_marker_review_candidates`: this is the count of items the
119
- * retirement analyzer surfaced as candidates (i.e., 2+ countable markers).
120
- * It is not the total event marker count across the global pool.
121
- *
122
- * Scope semantics (W-C-06 signal scope 명시):
123
- * - globalItems, retirementCandidates → GLOBAL scope (~/.onto/learnings/)
124
- * - panelVerdicts → PROJECT scope ({project}/.onto/learnings/)
125
- * - crossAgentDedupClusters → GLOBAL scope (cross-agent dedup)
126
- * These scopes are intentionally different: retirement operates on promoted
127
- * (global) items, while panel review operates on project items being promoted.
128
- * The snapshot aggregates both, labeled by their source scope.
129
- */
130
- export function buildHealthSnapshot(config) {
131
- const total = config.globalItems.length;
132
- const axis = tallyAxis(config.globalItems);
133
- const purpose = tallyPurpose(config.globalItems);
134
- return {
135
- total_global_learnings: total,
136
- axis_distribution: {
137
- methodology_only_pct: pct(axis.methodology_only, total),
138
- domain_only_pct: pct(axis.domain_only, total),
139
- dual_pct: pct(axis.dual, total),
140
- },
141
- purpose_distribution: purpose,
142
- judgment_ratio_pct: judgmentRatio(config.globalItems),
143
- cross_agent_dedup_clusters_remaining: config.crossAgentDedupClusters.length,
144
- axis_tag_re_evaluation_changes_this_session: config.extras.axis_tag_re_evaluation_changes_this_session,
145
- event_marker_review_candidates: config.retirementCandidates.length,
146
- creation_gate_failures: config.extras.creation_gate_failures,
147
- applied_learnings_aggregate: config.extras.applied_learnings_aggregate,
148
- separation_trigger_agents: findSeparationTriggers(config.globalItems, config.extras.agent_line_counts),
149
- };
150
- }
@@ -1,544 +0,0 @@
1
- /**
2
- * Phase 3 — Insight Reclassifier (Step 12 + follow-up apply path).
3
- *
4
- * Design authority:
5
- * - learn-phase3-design-v2.md DD-9 (separate insight reclassifier path)
6
- * - learn-phase3-design-v3.md DD-9 (collector mode + lineage)
7
- * - learn-phase3-design-v4.md DD-9 (panel-reviewer reuse, batch-size,
8
- * resumable progress)
9
- *
10
- * Purpose:
11
- * - Phase 1 era left ~462 global learnings tagged `[insight]`. Insight is
12
- * an under-classified label meaning "we don't know if this is a guardrail,
13
- * foundation, or convention". This command walks each insight item, calls
14
- * the LLM, and records a recommended reclassification. A follow-up apply
15
- * path rewrites the role tag in place.
16
- *
17
- * Why a separate path from promotion:
18
- * - Cost separation: 462 × 3 panel members ≈ 1,400 LLM calls. Running this
19
- * on every promote pass would balloon cost.
20
- * - Migration vs steady-state separation: this is a one-shot cleanup that
21
- * ideally finishes once and never runs again. Promote is a recurring
22
- * workflow.
23
- * - Audit trail isolation: insights reclassified here are tracked
24
- * independently of promote outcomes so a reclassification regret can be
25
- * undone without rolling back unrelated promotions.
26
- *
27
- * Phase A (analyze) vs Phase B (apply) split mirrors promote:
28
- * - runInsightReclassifier is Phase A: classify and write report JSON.
29
- * - applyInsightReclassifications is Phase B: read report JSON, rewrite
30
- * the `[insight]` role tag to the proposed role in place, and record
31
- * which entries were applied / skipped / failed.
32
- *
33
- * Apply semantics (Phase B):
34
- * - Idempotent: each report entry carries the raw_line captured at classify
35
- * time. At apply time we look up the line in the file by literal match.
36
- * If the original line is gone (file was already rewritten or edited),
37
- * the entry is recorded as "skipped_already_applied" rather than failing.
38
- * - Three target forms:
39
- * guardrail / foundation / convention → replace [insight] with [{role}]
40
- * drop_role → remove the [insight] bracket
41
- * - Skips entries without a proposed_role (kept as unclassified_pending in
42
- * the Phase A output).
43
- *
44
- * Failure model:
45
- * - Classify LLM unreachable: item is left unclassified, recorded as
46
- * `unclassified_pending`. The operator re-runs after fixing API access.
47
- * - Classify returns unrecognized role: skipped with error.
48
- * - Apply target file missing: applyResult.failed entry with reason.
49
- */
50
- import fs from "node:fs";
51
- import path from "node:path";
52
- import { callLlm, hashPrompt } from "../shared/llm-caller.js";
53
- import { collect } from "./collector.js";
54
- // ---------------------------------------------------------------------------
55
- // Prompt
56
- // ---------------------------------------------------------------------------
57
- const RECLASSIFY_SYSTEM_PROMPT = `You are reclassifying [insight]-tagged learnings into one of three concrete role categories.
58
-
59
- Output ONE JSON object:
60
- {
61
- "proposed_role": "guardrail" | "foundation" | "convention" | "drop_role",
62
- "reason": "<one sentence explaining the choice>"
63
- }
64
-
65
- Definitions (learning-rules.md):
66
- - guardrail: situational warning with corrective action (Situation + Result + Corrective action)
67
- - foundation: reusable structural fact that other reasoning depends on
68
- - convention: formatting/naming/process rule the team follows
69
- - drop_role: the item is too vague or context-specific to fit any of the above —
70
- drop the role tag entirely
71
-
72
- NO markdown fences, JSON only.`;
73
- function buildReclassifyUserPrompt(item) {
74
- return [
75
- `Agent: ${item.agent_id}`,
76
- `Tags: [${item.applicability_tags.join(" ")}]`,
77
- `Type: ${item.type}`,
78
- `Source: ${item.source_project ?? "?"} / ${item.source_domain ?? "?"} / ${item.source_date ?? "?"}`,
79
- "",
80
- "Content:",
81
- item.content,
82
- "",
83
- 'Respond with {"proposed_role": "...", "reason": "..."}.',
84
- ].join("\n");
85
- }
86
- const VALID_ROLES = [
87
- "guardrail",
88
- "foundation",
89
- "convention",
90
- "drop_role",
91
- ];
92
- async function attemptClassify(item, modelId, retryFeedback) {
93
- let userPrompt = buildReclassifyUserPrompt(item);
94
- if (retryFeedback) {
95
- userPrompt +=
96
- `\n\nPrevious attempt was rejected: ${retryFeedback}\n` +
97
- `Fix the issue and respond again.`;
98
- }
99
- let llmText;
100
- let llmModelId;
101
- try {
102
- const result = await callLlm(RECLASSIFY_SYSTEM_PROMPT, userPrompt, {
103
- max_tokens: 512,
104
- ...(modelId ? { model_id: modelId } : {}),
105
- });
106
- llmText = result.text;
107
- llmModelId = result.model_id;
108
- }
109
- catch (error) {
110
- return {
111
- ok: false,
112
- kind: "provider_error",
113
- reason: `LLM unreachable: ${error instanceof Error ? error.message : String(error)}`,
114
- };
115
- }
116
- let parsed;
117
- try {
118
- let cleaned = llmText.trim();
119
- if (cleaned.startsWith("```")) {
120
- cleaned = cleaned.replace(/^```(?:json)?\s*/, "").replace(/```\s*$/, "");
121
- }
122
- parsed = JSON.parse(cleaned);
123
- }
124
- catch (error) {
125
- return {
126
- ok: false,
127
- kind: "validation_error",
128
- reason: `malformed LLM JSON: ${error instanceof Error ? error.message : String(error)}`,
129
- };
130
- }
131
- const role = parsed.proposed_role;
132
- if (!VALID_ROLES.includes(role)) {
133
- return {
134
- ok: false,
135
- kind: "validation_error",
136
- reason: `invalid proposed_role "${String(parsed.proposed_role)}"`,
137
- };
138
- }
139
- return {
140
- ok: true,
141
- role,
142
- reason: typeof parsed.reason === "string" ? parsed.reason : "",
143
- modelId: llmModelId,
144
- };
145
- }
146
- async function classifyOne(item, modelId) {
147
- // 1 retry on validation_error (malformed JSON or invalid role enum).
148
- // Provider errors (network, auth) skip the retry — they're not the
149
- // model's fault and retry won't help.
150
- let llmCalls = 0;
151
- const first = await attemptClassify(item, modelId);
152
- llmCalls += 1;
153
- let final = first;
154
- if (!first.ok && first.kind === "validation_error") {
155
- const second = await attemptClassify(item, modelId, first.reason);
156
- llmCalls += 1;
157
- final = second;
158
- }
159
- if (!final.ok) {
160
- return { ok: false, reason: final.reason, llmCalls };
161
- }
162
- const userPrompt = buildReclassifyUserPrompt(item);
163
- return {
164
- ok: true,
165
- llmCalls,
166
- reclassification: {
167
- agent_id: item.agent_id,
168
- source_path: item.source_path,
169
- line_number: item.line_number,
170
- raw_line: item.raw_line,
171
- current_role: item.role,
172
- proposed_role: final.role,
173
- reason: final.reason,
174
- llm_model_id: final.modelId,
175
- llm_prompt_hash: hashPrompt(RECLASSIFY_SYSTEM_PROMPT + "\n" + userPrompt),
176
- },
177
- };
178
- }
179
- // ---------------------------------------------------------------------------
180
- // Public entry point
181
- // ---------------------------------------------------------------------------
182
- /**
183
- * Run the reclassifier in analyze mode.
184
- *
185
- * Output is written to `<sessionRoot>/insight-reclassification-report.json`
186
- * (unless dryRun). The apply phase (Phase B) is implemented in this same
187
- * module as applyInsightReclassifications — it reads the report JSON and
188
- * rewrites role tags in place. Originally planned to reuse Phase B's
189
- * axis_tag_change applicator via promote-executor, but the standalone
190
- * function path is simpler because single-line role rewrites don't need
191
- * the full PromoteReport + PromoteDecisions infrastructure.
192
- */
193
- export async function runInsightReclassifier(config) {
194
- const sessionRoot = path.join(config.projectRoot, ".onto", "sessions", "reclassify-insights", config.sessionId);
195
- fs.mkdirSync(sessionRoot, { recursive: true });
196
- // Use the collector in reclassify-insights mode so candidate_items is
197
- // pre-filtered to global insight items per DD-18 §SST.
198
- const collection = collect({
199
- mode: "reclassify-insights",
200
- projectRoot: config.projectRoot,
201
- });
202
- let insights = collection.candidate_items;
203
- if (config.targetAgent) {
204
- insights = insights.filter((i) => i.agent_id === config.targetAgent);
205
- }
206
- if (config.batchSize !== undefined) {
207
- insights = insights.slice(0, config.batchSize);
208
- }
209
- const reclassified = [];
210
- const unclassified = [];
211
- let llmCalls = 0;
212
- if (config.dryRun) {
213
- return {
214
- session_id: config.sessionId,
215
- session_root: sessionRoot,
216
- total_insights: insights.length,
217
- reclassified: [],
218
- unclassified_pending: [],
219
- llm_calls: 0,
220
- dry_run: true,
221
- };
222
- }
223
- for (const item of insights) {
224
- const r = await classifyOne(item, config.modelId);
225
- llmCalls += r.llmCalls;
226
- if (r.ok) {
227
- reclassified.push(r.reclassification);
228
- }
229
- else {
230
- unclassified.push({ item, reason: r.reason });
231
- }
232
- }
233
- const report = {
234
- session_id: config.sessionId,
235
- generated_at: new Date().toISOString(),
236
- total_insights: insights.length,
237
- reclassified,
238
- unclassified_pending: unclassified.map((u) => ({
239
- agent_id: u.item.agent_id,
240
- source_path: u.item.source_path,
241
- line_number: u.item.line_number,
242
- reason: u.reason,
243
- })),
244
- llm_calls: llmCalls,
245
- };
246
- const reportPath = path.join(sessionRoot, "insight-reclassification-report.json");
247
- fs.writeFileSync(reportPath, JSON.stringify(report, null, 2), "utf8");
248
- // ontoHome is currently unused but kept on the config so a follow-up Step
249
- // 13 patch can wire it through to the global learning file path resolver
250
- // when the apply phase lands.
251
- void config.ontoHome;
252
- return {
253
- session_id: config.sessionId,
254
- session_root: sessionRoot,
255
- total_insights: insights.length,
256
- reclassified,
257
- unclassified_pending: unclassified,
258
- llm_calls: llmCalls,
259
- dry_run: false,
260
- };
261
- }
262
- /**
263
- * Rewrite a single raw learning line, replacing the `[insight]` role
264
- * bracket with the proposed role (or removing the bracket for drop_role).
265
- *
266
- * Returns null when the raw line does not contain an `[insight]` bracket
267
- * at all — the caller should treat this as "already applied" to stay
268
- * idempotent. Production usage is that the current_role was "insight" at
269
- * classify time, so the bracket should exist unless a previous apply ran.
270
- */
271
- export function rewriteInsightRoleTag(rawLine, proposedRole) {
272
- const bracket = "[insight]";
273
- if (!rawLine.includes(bracket))
274
- return null;
275
- if (proposedRole === "drop_role") {
276
- // Remove the bracket plus one adjacent space so we don't leave a
277
- // double-space in the output. Try trailing space first, then leading.
278
- if (rawLine.includes(`${bracket} `)) {
279
- return rawLine.replace(`${bracket} `, "");
280
- }
281
- if (rawLine.includes(` ${bracket}`)) {
282
- return rawLine.replace(` ${bracket}`, "");
283
- }
284
- return rawLine.replace(bracket, "");
285
- }
286
- return rawLine.replace(bracket, `[${proposedRole}]`);
287
- }
288
- function resolveAnchor(fileLines, rec) {
289
- const preRewrite = rec.raw_line;
290
- const postRewrite = rec.proposed_role !== null
291
- ? rewriteInsightRoleTag(preRewrite, rec.proposed_role)
292
- : null;
293
- // Anchor #1: the 1-indexed line_number pointer.
294
- const anchoredIdx = rec.line_number - 1;
295
- if (anchoredIdx >= 0 && anchoredIdx < fileLines.length) {
296
- const candidate = fileLines[anchoredIdx];
297
- if (candidate === preRewrite) {
298
- return {
299
- kind: "match_original",
300
- lineIndex: anchoredIdx,
301
- anchor: "line_number_and_raw_line",
302
- };
303
- }
304
- if (postRewrite !== null && candidate === postRewrite) {
305
- return {
306
- kind: "already_rewritten",
307
- lineIndex: anchoredIdx,
308
- anchor: "line_number_only",
309
- };
310
- }
311
- }
312
- // Anchor #2: verbatim raw_line scan fallback. Must be UNAMBIGUOUS.
313
- let firstMatch = -1;
314
- let matchCount = 0;
315
- for (let i = 0; i < fileLines.length; i++) {
316
- if (fileLines[i] === preRewrite) {
317
- if (firstMatch === -1)
318
- firstMatch = i;
319
- matchCount += 1;
320
- if (matchCount > 1)
321
- break;
322
- }
323
- }
324
- if (matchCount === 1 && firstMatch !== -1) {
325
- return {
326
- kind: "verbatim_fallback",
327
- lineIndex: firstMatch,
328
- anchor: "raw_line_scan",
329
- };
330
- }
331
- if (matchCount > 1) {
332
- return { kind: "ambiguous_raw_line", lineIndex: null, anchor: "none" };
333
- }
334
- // No anchor hit. File has drifted out from under the report.
335
- return { kind: "drift", lineIndex: null, anchor: "none" };
336
- }
337
- /**
338
- * Apply a reclassification report to the on-disk learning files.
339
- *
340
- * Reads the JSON report, walks each reclassification entry, resolves
341
- * the target line via line_number + raw_line anchors (see resolveAnchor),
342
- * and rewrites the `[insight]` role tag in the source file.
343
- *
344
- * Evidence-based idempotency (C3 + CC1):
345
- * - applied / would_apply — original raw_line was located and rewritten
346
- * (or would have been in dry-run)
347
- * - skipped_already_applied — the proposed rewrite is already present at
348
- * the recorded line_number (not just "line not found")
349
- * - skipped_source_drift — neither original nor rewritten form matched.
350
- * Operator must re-classify to resync.
351
- *
352
- * Atomicity: writes are per-file. Write failures roll back every applied
353
- * entry for that file. Dry-run never writes.
354
- */
355
- export function applyInsightReclassifications(config) {
356
- const reportPath = config.reportPath;
357
- if (!fs.existsSync(reportPath)) {
358
- throw new Error(`Insight reclassification report not found: ${reportPath}`);
359
- }
360
- const raw = fs.readFileSync(reportPath, "utf8");
361
- const report = JSON.parse(raw);
362
- const reclassified = report.reclassified ?? [];
363
- const dryRun = config.dryRun === true;
364
- const entries = [];
365
- let applied = 0;
366
- let wouldApply = 0;
367
- let skippedNoProposal = 0;
368
- let skippedAlreadyApplied = 0;
369
- let skippedSourceDrift = 0;
370
- let failed = 0;
371
- // Group by source_path so we load each file at most once per run.
372
- const byFile = new Map();
373
- for (const r of reclassified) {
374
- const bucket = byFile.get(r.source_path);
375
- if (bucket)
376
- bucket.push(r);
377
- else
378
- byFile.set(r.source_path, [r]);
379
- }
380
- for (const [filePath, items] of byFile) {
381
- let fileLines = null;
382
- if (fs.existsSync(filePath)) {
383
- fileLines = fs.readFileSync(filePath, "utf8").split("\n");
384
- }
385
- for (const r of items) {
386
- if (r.proposed_role === null) {
387
- entries.push({
388
- agent_id: r.agent_id,
389
- source_path: r.source_path,
390
- line_number: r.line_number,
391
- raw_line: r.raw_line,
392
- current_role: r.current_role,
393
- proposed_role: null,
394
- outcome: "skipped_no_proposal",
395
- anchor_resolution: "none",
396
- new_line: null,
397
- error_message: null,
398
- });
399
- skippedNoProposal += 1;
400
- continue;
401
- }
402
- if (fileLines === null) {
403
- entries.push({
404
- agent_id: r.agent_id,
405
- source_path: r.source_path,
406
- line_number: r.line_number,
407
- raw_line: r.raw_line,
408
- current_role: r.current_role,
409
- proposed_role: r.proposed_role,
410
- outcome: "failed",
411
- anchor_resolution: "none",
412
- new_line: null,
413
- error_message: `source file not found: ${filePath}`,
414
- });
415
- failed += 1;
416
- continue;
417
- }
418
- const resolution = resolveAnchor(fileLines, r);
419
- // Already-rewritten: evidence-based idempotency success
420
- if (resolution.kind === "already_rewritten") {
421
- entries.push({
422
- agent_id: r.agent_id,
423
- source_path: r.source_path,
424
- line_number: r.line_number,
425
- raw_line: r.raw_line,
426
- current_role: r.current_role,
427
- proposed_role: r.proposed_role,
428
- outcome: "skipped_already_applied",
429
- anchor_resolution: resolution.anchor,
430
- new_line: null,
431
- error_message: null,
432
- });
433
- skippedAlreadyApplied += 1;
434
- continue;
435
- }
436
- // Drift or ambiguous: fail safe
437
- if (resolution.kind === "drift" ||
438
- resolution.kind === "ambiguous_raw_line") {
439
- entries.push({
440
- agent_id: r.agent_id,
441
- source_path: r.source_path,
442
- line_number: r.line_number,
443
- raw_line: r.raw_line,
444
- current_role: r.current_role,
445
- proposed_role: r.proposed_role,
446
- outcome: "skipped_source_drift",
447
- anchor_resolution: resolution.anchor,
448
- new_line: null,
449
- error_message: resolution.kind === "ambiguous_raw_line"
450
- ? "multiple verbatim matches for raw_line (line_number drifted)"
451
- : "neither line_number anchor nor verbatim raw_line matched",
452
- });
453
- skippedSourceDrift += 1;
454
- continue;
455
- }
456
- // Match confirmed — compute the rewrite
457
- const lineIdx = resolution.lineIndex;
458
- const newLine = rewriteInsightRoleTag(r.raw_line, r.proposed_role);
459
- if (newLine === null) {
460
- // raw_line has no [insight] bracket. The anchor matched so we
461
- // know this is the right line but the rewrite is a no-op —
462
- // treat as already applied for safety.
463
- entries.push({
464
- agent_id: r.agent_id,
465
- source_path: r.source_path,
466
- line_number: r.line_number,
467
- raw_line: r.raw_line,
468
- current_role: r.current_role,
469
- proposed_role: r.proposed_role,
470
- outcome: "skipped_already_applied",
471
- anchor_resolution: resolution.anchor,
472
- new_line: null,
473
- error_message: null,
474
- });
475
- skippedAlreadyApplied += 1;
476
- continue;
477
- }
478
- if (dryRun) {
479
- // Do NOT mutate fileLines in dry-run. Record preview only.
480
- entries.push({
481
- agent_id: r.agent_id,
482
- source_path: r.source_path,
483
- line_number: r.line_number,
484
- raw_line: r.raw_line,
485
- current_role: r.current_role,
486
- proposed_role: r.proposed_role,
487
- outcome: "would_apply",
488
- anchor_resolution: resolution.anchor,
489
- new_line: newLine,
490
- error_message: null,
491
- });
492
- wouldApply += 1;
493
- continue;
494
- }
495
- // Real apply
496
- fileLines[lineIdx] = newLine;
497
- entries.push({
498
- agent_id: r.agent_id,
499
- source_path: r.source_path,
500
- line_number: r.line_number,
501
- raw_line: r.raw_line,
502
- current_role: r.current_role,
503
- proposed_role: r.proposed_role,
504
- outcome: "applied",
505
- anchor_resolution: resolution.anchor,
506
- new_line: newLine,
507
- error_message: null,
508
- });
509
- applied += 1;
510
- }
511
- // Flush the file if any line was mutated AND we're not in dry-run.
512
- if (!dryRun &&
513
- fileLines !== null &&
514
- entries.some((e) => e.source_path === filePath && e.outcome === "applied")) {
515
- try {
516
- fs.writeFileSync(filePath, fileLines.join("\n"), "utf8");
517
- }
518
- catch (error) {
519
- const message = error instanceof Error ? error.message : String(error);
520
- for (const e of entries) {
521
- if (e.source_path === filePath && e.outcome === "applied") {
522
- e.outcome = "failed";
523
- e.error_message = `write failed: ${message}`;
524
- applied -= 1;
525
- failed += 1;
526
- }
527
- }
528
- }
529
- }
530
- }
531
- return {
532
- report_path: reportPath,
533
- report_generated_at: typeof report.generated_at === "string" ? report.generated_at : null,
534
- total_entries: reclassified.length,
535
- applied,
536
- would_apply: wouldApply,
537
- skipped_no_proposal: skippedNoProposal,
538
- skipped_already_applied: skippedAlreadyApplied,
539
- skipped_source_drift: skippedSourceDrift,
540
- failed,
541
- entries,
542
- dry_run: dryRun,
543
- };
544
- }