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,311 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { buildOntologyIndex } from "./ontology-index.js";
3
- import { queryOntology } from "./ontology-query.js";
4
- // ─── Fixtures ───
5
- const ONTOLOGY_YAML = `
6
- glossary:
7
- - canonical: Lesson
8
- meaning: "수업"
9
- legacy_aliases: [Class, Lecture]
10
- code_entity: Lecture
11
- db_table: GT_CLASS
12
- fk_variants: [classId, class_id, lectureId]
13
- - canonical: Tutor
14
- meaning: "튜터"
15
- legacy_aliases: [Teacher]
16
- code_entity: Tutor
17
- db_table: GT_TUTOR
18
- fk_variants: [tutorId, TUTOR_ID]
19
- - canonical: Student
20
- meaning: "학생"
21
- legacy_aliases: [User]
22
- code_entity: User
23
- db_table: GT_USER
24
- fk_variants: [studentId, userId]
25
- - canonical: Ticket
26
- meaning: "수강권"
27
- legacy_aliases: [ClassTicket, 레슨권]
28
- code_entity: Ticket
29
- db_table: le_ticket
30
- fk_variants: [classTicketId, ticketId]
31
- - canonical: Subscribe
32
- meaning: "구독"
33
- legacy_aliases: [Subscription]
34
- code_entity: Subscribe
35
- db_table: le_subscribe
36
- fk_variants: [subscribeId]
37
- `;
38
- const ACTIONS_YAML = `
39
- write_actions:
40
- - id: AUTH-1
41
- name: SocialLogin
42
- display_name: "소셜 로그인"
43
- domain: auth
44
- actor: Student
45
- target_entities: [Student, UserOauthMapping]
46
- source_code: "AuthenticationGateway.oauthLogin()"
47
- - id: LEC-1
48
- name: CreateLecture
49
- display_name: "수업 생성 (일반)"
50
- domain: lecture
51
- actor: Student
52
- target_entities: [Lesson, LectureOnline]
53
- source_code: "LectureCommandServiceImpl.createNewPodoLecture()"
54
- - id: SCHEDULE-1
55
- name: MatchTutor
56
- display_name: "튜터 매칭"
57
- domain: schedule
58
- actor: Student
59
- target_entities: [Lesson]
60
- source_code: "PodoScheduleServiceImplV2.match()"
61
- - id: TICKET-1
62
- name: CreateTicket
63
- display_name: "수강권 생성"
64
- domain: ticket
65
- actor: System
66
- target_entities: [Ticket]
67
- source_code: "TicketService.create()"
68
- read_actions:
69
- - id: LEC-R1
70
- name: GetLessonDetail
71
- display_name: "수업 상세 조회"
72
- domain: lecture
73
- actor: Student
74
- target_entities: [Lesson]
75
- source_code: "LectureGateway.getLectureDetail()"
76
- `;
77
- const TRANSITIONS_YAML = `
78
- entities:
79
- - name: Lesson
80
- state_fields:
81
- - field_name: invoice_status
82
- transitions:
83
- - id: L1
84
- from: null
85
- to: CREATED
86
- trigger: "학생이 수업 생성"
87
- source_code: "LectureCommandServiceImpl.createNewPodoLecture()"
88
- - id: L2
89
- from: CREATED
90
- to: RESERVED
91
- trigger: "학생이 시간 선택 + 튜터 매칭"
92
- source_code: "PodoScheduleServiceImplV2.match()"
93
- - id: L4
94
- from: RESERVED
95
- to: CANCEL
96
- trigger: "학생이 수업 취소"
97
- source_code: "PodoScheduleServiceImplV2.cancel()"
98
- - name: Ticket
99
- state_fields:
100
- - field_name: status
101
- transitions:
102
- - id: T1
103
- from: null
104
- to: ACTIVE
105
- trigger: "수강권 활성화"
106
- source_code: "TicketService.activate()"
107
- - id: T2
108
- from: ACTIVE
109
- to: EXPIRED
110
- trigger: "수강권 만료"
111
- source_code: "TicketBatchService.expire()"
112
- `;
113
- function buildFixtureIndex() {
114
- return buildOntologyIndex(ONTOLOGY_YAML, ACTIONS_YAML, TRANSITIONS_YAML);
115
- }
116
- // ─── Tests ───
117
- describe("queryOntology", () => {
118
- describe("키워드 매칭: 정확 매칭", () => {
119
- it("canonical name으로 정확히 매칭한다", () => {
120
- const index = buildFixtureIndex();
121
- const result = queryOntology(index, ["Lesson"]);
122
- expect(result.matched_entities).toContain("Lesson");
123
- expect(result.matched_entities).not.toContain("Tutor");
124
- });
125
- it("legacy alias로 매칭한다", () => {
126
- const index = buildFixtureIndex();
127
- const result = queryOntology(index, ["Teacher"]);
128
- expect(result.matched_entities).toContain("Tutor");
129
- });
130
- });
131
- describe("키워드 매칭: 부분 매칭", () => {
132
- it("canonical name의 부분 문자열로 매칭한다", () => {
133
- const index = buildFixtureIndex();
134
- const result = queryOntology(index, ["less"]);
135
- expect(result.matched_entities).toContain("Lesson");
136
- });
137
- it("legacy alias의 부분 문자열로 매칭한다", () => {
138
- const index = buildFixtureIndex();
139
- const result = queryOntology(index, ["Teach"]);
140
- expect(result.matched_entities).toContain("Tutor");
141
- });
142
- it("meaning 필드의 부분 문자열로 매칭한다", () => {
143
- const index = buildFixtureIndex();
144
- const result = queryOntology(index, ["수업"]);
145
- expect(result.matched_entities).toContain("Lesson");
146
- });
147
- });
148
- describe("키워드 매칭: 대소문자 무시", () => {
149
- it("소문자 키워드로 대문자 canonical name을 매칭한다", () => {
150
- const index = buildFixtureIndex();
151
- const result = queryOntology(index, ["lesson"]);
152
- expect(result.matched_entities).toContain("Lesson");
153
- });
154
- it("대문자 키워드로 소문자가 포함된 legacy alias를 매칭한다", () => {
155
- const index = buildFixtureIndex();
156
- const result = queryOntology(index, ["TEACHER"]);
157
- expect(result.matched_entities).toContain("Tutor");
158
- });
159
- it("혼합 대소문자 키워드로 매칭한다", () => {
160
- const index = buildFixtureIndex();
161
- const result = queryOntology(index, ["lEsSON"]);
162
- expect(result.matched_entities).toContain("Lesson");
163
- });
164
- });
165
- describe("한국어 키워드 매칭 (meaning 필드)", () => {
166
- it("한국어 meaning으로 매칭한다: 수업 → Lesson", () => {
167
- const index = buildFixtureIndex();
168
- const result = queryOntology(index, ["수업"]);
169
- expect(result.matched_entities).toContain("Lesson");
170
- expect(result.db_tables).toContain("GT_CLASS");
171
- });
172
- it("한국어 meaning으로 매칭한다: 튜터 → Tutor", () => {
173
- const index = buildFixtureIndex();
174
- const result = queryOntology(index, ["튜터"]);
175
- expect(result.matched_entities).toContain("Tutor");
176
- expect(result.db_tables).toContain("GT_TUTOR");
177
- });
178
- it("한국어 meaning으로 매칭한다: 수강권 → Ticket", () => {
179
- const index = buildFixtureIndex();
180
- const result = queryOntology(index, ["수강권"]);
181
- expect(result.matched_entities).toContain("Ticket");
182
- });
183
- it("한국어 meaning 부분 매칭: 수강 → Ticket (수강권)", () => {
184
- const index = buildFixtureIndex();
185
- const result = queryOntology(index, ["수강"]);
186
- expect(result.matched_entities).toContain("Ticket");
187
- });
188
- it("한국어 legacy alias로 매칭한다: 레슨권 → Ticket", () => {
189
- const index = buildFixtureIndex();
190
- const result = queryOntology(index, ["레슨권"]);
191
- expect(result.matched_entities).toContain("Ticket");
192
- });
193
- });
194
- describe("관련 actions 수집", () => {
195
- it("매칭된 entity를 target으로 가진 action을 수집한다", () => {
196
- const index = buildFixtureIndex();
197
- const result = queryOntology(index, ["Lesson"]);
198
- const actionIds = result.related_actions.map((a) => a.id);
199
- expect(actionIds).toContain("LEC-1");
200
- expect(actionIds).toContain("SCHEDULE-1");
201
- expect(actionIds).toContain("LEC-R1");
202
- // AUTH-1은 Student를 target으로 가지므로 포함되지 않아야 함
203
- expect(actionIds).not.toContain("AUTH-1");
204
- });
205
- it("action summary에 display_name과 source_code가 포함된다", () => {
206
- const index = buildFixtureIndex();
207
- const result = queryOntology(index, ["Lesson"]);
208
- const lec1 = result.related_actions.find((a) => a.id === "LEC-1");
209
- expect(lec1).toBeDefined();
210
- expect(lec1.display_name).toBe("수업 생성 (일반)");
211
- expect(lec1.source_code).toBe("LectureCommandServiceImpl.createNewPodoLecture()");
212
- });
213
- });
214
- describe("관련 transitions 수집", () => {
215
- it("매칭된 entity의 transition을 수집한다", () => {
216
- const index = buildFixtureIndex();
217
- const result = queryOntology(index, ["Lesson"]);
218
- expect(result.related_transitions.length).toBe(3);
219
- const triggers = result.related_transitions.map((t) => t.trigger);
220
- expect(triggers).toContain("학생이 수업 생성");
221
- expect(triggers).toContain("학생이 시간 선택 + 튜터 매칭");
222
- expect(triggers).toContain("학생이 수업 취소");
223
- });
224
- it("transition summary에 entity, from, to, trigger, source_code가 포함된다", () => {
225
- const index = buildFixtureIndex();
226
- const result = queryOntology(index, ["Ticket"]);
227
- expect(result.related_transitions.length).toBe(2);
228
- const t1 = result.related_transitions[0];
229
- expect(t1.entity).toBe("Ticket");
230
- expect(t1.from).toBe("(none)");
231
- expect(t1.to).toBe("ACTIVE");
232
- expect(t1.source_code).toBe("TicketService.activate()");
233
- });
234
- });
235
- describe("code_locations 수집", () => {
236
- it("관련 action의 source_code에서 CodeLocation을 추출한다", () => {
237
- const index = buildFixtureIndex();
238
- const result = queryOntology(index, ["Lesson"]);
239
- expect(result.code_locations.length).toBeGreaterThan(0);
240
- const refs = result.code_locations.map((cl) => cl.reference);
241
- expect(refs).toContain("LectureCommandServiceImpl.createNewPodoLecture()");
242
- expect(refs).toContain("PodoScheduleServiceImplV2.match()");
243
- });
244
- it("code_location의 context에 action id와 display_name이 포함된다", () => {
245
- const index = buildFixtureIndex();
246
- const result = queryOntology(index, ["Lesson"]);
247
- const lec1Location = result.code_locations.find((cl) => cl.reference === "LectureCommandServiceImpl.createNewPodoLecture()");
248
- expect(lec1Location).toBeDefined();
249
- expect(lec1Location.context).toBe("Action LEC-1: 수업 생성 (일반)");
250
- });
251
- });
252
- describe("db_tables 수집", () => {
253
- it("매칭된 entity의 db_table을 수집한다", () => {
254
- const index = buildFixtureIndex();
255
- const result = queryOntology(index, ["Lesson"]);
256
- expect(result.db_tables).toEqual(["GT_CLASS"]);
257
- });
258
- it("복수 entity 매칭 시 모든 db_table을 수집한다", () => {
259
- const index = buildFixtureIndex();
260
- // "수" 키워드는 "수업"(Lesson), "수강권"(Ticket) 모두 매칭
261
- const result = queryOntology(index, ["Lesson", "Tutor"]);
262
- expect(result.db_tables).toContain("GT_CLASS");
263
- expect(result.db_tables).toContain("GT_TUTOR");
264
- });
265
- });
266
- describe("복수 키워드", () => {
267
- it("여러 키워드 중 하나라도 매칭되면 해당 entity를 포함한다", () => {
268
- const index = buildFixtureIndex();
269
- const result = queryOntology(index, ["Lesson", "Tutor"]);
270
- expect(result.matched_entities).toContain("Lesson");
271
- expect(result.matched_entities).toContain("Tutor");
272
- });
273
- it("각 entity의 관련 action과 transition을 모두 수집한다", () => {
274
- const index = buildFixtureIndex();
275
- const result = queryOntology(index, ["Lesson", "Ticket"]);
276
- const actionIds = result.related_actions.map((a) => a.id);
277
- expect(actionIds).toContain("LEC-1");
278
- expect(actionIds).toContain("TICKET-1");
279
- const entities = result.related_transitions.map((t) => t.entity);
280
- expect(entities).toContain("Lesson");
281
- expect(entities).toContain("Ticket");
282
- });
283
- });
284
- describe("0건 매칭 → 빈 결과", () => {
285
- it("매칭되는 entity가 없으면 모든 배열이 빈 상태이다", () => {
286
- const index = buildFixtureIndex();
287
- const result = queryOntology(index, ["nonexistent"]);
288
- expect(result.matched_entities).toEqual([]);
289
- expect(result.code_locations).toEqual([]);
290
- expect(result.db_tables).toEqual([]);
291
- expect(result.related_actions).toEqual([]);
292
- expect(result.related_transitions).toEqual([]);
293
- });
294
- it("빈 키워드 배열이면 빈 결과를 반환한다", () => {
295
- const index = buildFixtureIndex();
296
- const result = queryOntology(index, []);
297
- expect(result.matched_entities).toEqual([]);
298
- expect(result.code_locations).toEqual([]);
299
- expect(result.db_tables).toEqual([]);
300
- expect(result.related_actions).toEqual([]);
301
- expect(result.related_transitions).toEqual([]);
302
- });
303
- it("빈 인덱스에 키워드를 전달하면 빈 결과를 반환한다", () => {
304
- const index = buildOntologyIndex("", "", "");
305
- const result = queryOntology(index, ["Lesson"]);
306
- expect(result.matched_entities).toEqual([]);
307
- expect(result.related_actions).toEqual([]);
308
- expect(result.related_transitions).toEqual([]);
309
- });
310
- });
311
- });
@@ -1,48 +0,0 @@
1
- /**
2
- * source_code 문자열을 실제 파일 경로로 해석(resolve)한다.
3
- *
4
- * ActionEntry.source_code와 TransitionEntry.source_code는 자연어 문자열이다.
5
- * 예: "PodoScheduleServiceImplV2.match()"
6
- * 이 문자열에서 클래스명을 추출하고, ScanResult.files에서 해당 파일을 찾는다.
7
- */
8
- // ─── Resolve ───
9
- /**
10
- * CodeLocation 배열의 source_code 참조를 실제 파일 경로로 해석한다.
11
- *
12
- * 해석 방법:
13
- * 1. reference에서 클래스명 추출: "PodoScheduleServiceImplV2.match()" → "PodoScheduleServiceImplV2"
14
- * 2. files에서 해당 클래스명을 포함하는 파일 경로 검색
15
- * 3. 매칭 실패 시 resolution_method: "unresolved"
16
- */
17
- export function resolveCodeLocations(locations, files) {
18
- return locations.map((loc) => {
19
- const className = extractClassName(loc.reference);
20
- if (!className) {
21
- return { reference: loc.reference, resolved_files: [], resolution_method: "unresolved", entity: loc.entity || undefined };
22
- }
23
- const matched = files
24
- .filter((f) => f.path.includes(className))
25
- .map((f) => f.path);
26
- return {
27
- reference: loc.reference,
28
- resolved_files: matched,
29
- resolution_method: matched.length > 0 ? "filename" : "unresolved",
30
- entity: loc.entity || undefined,
31
- };
32
- });
33
- }
34
- // ─── Helpers ───
35
- /**
36
- * source_code 문자열에서 클래스명을 추출한다.
37
- *
38
- * 예:
39
- * "PodoScheduleServiceImplV2.match()" → "PodoScheduleServiceImplV2"
40
- * "LectureCommandServiceImpl.createNewPodoLecture()" → "LectureCommandServiceImpl"
41
- * "AuthenticationGateway.oauthLogin(), OauthService.authenticate()" → "AuthenticationGateway"
42
- * (첫 번째 클래스명만 추출 — 복수 참조는 호출자가 locations를 분리하여 전달)
43
- */
44
- function extractClassName(reference) {
45
- // "ClassName.method()" 또는 "ClassName" 패턴
46
- const match = reference.match(/^([A-Z][A-Za-z0-9]+)/);
47
- return match ? match[1] : null;
48
- }
@@ -1,48 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { resolveCodeLocations } from "./ontology-resolve.js";
3
- const FILES = [
4
- { path: "src/main/java/com/app/PodoScheduleServiceImplV2.java", category: "source", language: "java", size_bytes: 5000 },
5
- { path: "src/main/java/com/app/LectureCommandServiceImpl.java", category: "source", language: "java", size_bytes: 3000 },
6
- { path: "src/main/java/com/app/TrialLectureCommandServiceImpl.java", category: "source", language: "java", size_bytes: 2000 },
7
- { path: "src/main/java/com/app/UserGateway.java", category: "source", language: "java", size_bytes: 4000 },
8
- { path: "src/test/java/com/app/LectureCommandServiceTest.java", category: "test", language: "java", size_bytes: 1500 },
9
- ];
10
- describe("resolveCodeLocations", () => {
11
- it("클래스명이 파일 경로에 포함되면 매칭한다", () => {
12
- const locations = [
13
- { reference: "PodoScheduleServiceImplV2.match()", context: "Action", entity: "Lesson" },
14
- ];
15
- const result = resolveCodeLocations(locations, FILES);
16
- expect(result).toHaveLength(1);
17
- expect(result[0].resolved_files).toEqual(["src/main/java/com/app/PodoScheduleServiceImplV2.java"]);
18
- expect(result[0].resolution_method).toBe("filename");
19
- });
20
- it("매칭 실패 시 unresolved를 반환한다", () => {
21
- const locations = [
22
- { reference: "NonExistentService.call()", context: "Action", entity: "Unknown" },
23
- ];
24
- const result = resolveCodeLocations(locations, FILES);
25
- expect(result[0].resolved_files).toEqual([]);
26
- expect(result[0].resolution_method).toBe("unresolved");
27
- });
28
- it("복수 파일이 매칭될 수 있다 (클래스명이 부분 매칭)", () => {
29
- const locations = [
30
- { reference: "LectureCommandServiceImpl.create()", context: "Action", entity: "Lesson" },
31
- ];
32
- const result = resolveCodeLocations(locations, FILES);
33
- // LectureCommandServiceImpl과 TrialLectureCommandServiceImpl 모두 매칭
34
- expect(result[0].resolved_files.length).toBeGreaterThanOrEqual(1);
35
- expect(result[0].resolution_method).toBe("filename");
36
- });
37
- it("빈 locations 입력 시 빈 배열을 반환한다", () => {
38
- const result = resolveCodeLocations([], FILES);
39
- expect(result).toEqual([]);
40
- });
41
- it("reference에서 첫 번째 클래스명만 추출한다", () => {
42
- const locations = [
43
- { reference: "UserGateway.findUser(), OtherService.call()", context: "Action", entity: "User" },
44
- ];
45
- const result = resolveCodeLocations(locations, FILES);
46
- expect(result[0].resolved_files).toContain("src/main/java/com/app/UserGateway.java");
47
- });
48
- });
@@ -1,7 +0,0 @@
1
- /**
2
- * Stub: pattern detection is deferred (code parser infrastructure not yet absorbed).
3
- * Returns empty results for all categories.
4
- */
5
- export function detectPatterns(_content, _filePath) {
6
- return { deps: [], apis: [], schemas: [], configs: [], docs: [] };
7
- }
@@ -1,213 +0,0 @@
1
- /**
2
- * review-log.ts — Review 실행 로그 수집 및 점진성(progressiveness) metric 산출
3
- *
4
- * W-A-71: DL-017 점진성 seat.
5
- * .onto/review/* 세션 디렉터리에서 execution-result.yaml + review-record.yaml 을 읽어
6
- * 세션별 타이밍 요약과 반복 review 간 비용·시간 변화를 계측한다.
7
- *
8
- * 소비자: W-A-75 (review-r+), W-A-76 (review-r−), onto:health (W-A-59)
9
- */
10
- import { readdirSync, readFileSync, statSync } from "node:fs";
11
- import { join, resolve } from "node:path";
12
- import { parse as yamlParse } from "yaml";
13
- import { getLogger } from "../logger.js";
14
- function normalizeRef(ref, projectRoot) {
15
- if (ref.startsWith("/")) {
16
- // 절대 경로 → 프로젝트 루트 기준 상대 경로로 변환
17
- const rel = ref.replace(projectRoot + "/", "");
18
- return rel.startsWith("/") ? ref : rel;
19
- }
20
- return ref;
21
- }
22
- function classifyProvenance(p) {
23
- if (p === "runner_wallclock" ||
24
- p === "coordinator_derived" ||
25
- p === "batch_window") {
26
- return p;
27
- }
28
- return "unknown";
29
- }
30
- function emptyProvenanceSummary() {
31
- return { runner_wallclock: 0, coordinator_derived: 0, batch_window: 0, unknown: 0 };
32
- }
33
- function parseYamlFile(filePath) {
34
- try {
35
- const raw = readFileSync(filePath, "utf-8");
36
- return yamlParse(raw);
37
- }
38
- catch {
39
- getLogger().debug("review-log: failed to parse YAML", { path: filePath });
40
- return null;
41
- }
42
- }
43
- function isDirectory(path) {
44
- try {
45
- return statSync(path).isDirectory();
46
- }
47
- catch {
48
- return false;
49
- }
50
- }
51
- // ─── Core API ───
52
- /**
53
- * .onto/review/ 이하 모든 세션의 실행 로그를 수집한다.
54
- *
55
- * @param reviewRoot - .onto/review 디렉터리 경로
56
- * @param projectRoot - 프로젝트 루트 (절대 경로 정규화용)
57
- * @returns ReviewLogSummary — 세션별 로그 + 점진성 metric
58
- */
59
- export function collectReviewLogs(reviewRoot, projectRoot) {
60
- const absReviewRoot = resolve(reviewRoot);
61
- const absProjectRoot = resolve(projectRoot);
62
- let sessionDirs;
63
- try {
64
- sessionDirs = readdirSync(absReviewRoot).filter((name) => {
65
- // session 디렉터리: YYYYMMDD-hexid 패턴
66
- return /^\d{8}-[0-9a-f]+$/.test(name) && isDirectory(join(absReviewRoot, name));
67
- });
68
- }
69
- catch {
70
- getLogger().warn("review-log: review root not found", { path: absReviewRoot });
71
- return {
72
- collected_at: new Date().toISOString(),
73
- review_root: absReviewRoot,
74
- total_sessions: 0,
75
- total_duration_ms: 0,
76
- entries: [],
77
- progressiveness: [],
78
- };
79
- }
80
- const entries = [];
81
- for (const dirName of sessionDirs) {
82
- const sessionPath = join(absReviewRoot, dirName);
83
- const entry = parseSession(sessionPath, absProjectRoot);
84
- if (entry) {
85
- entries.push(entry);
86
- }
87
- }
88
- // 시간순 정렬 (오래된 것 먼저). undefined-safe comparator.
89
- entries.sort((a, b) => (a.created_at ?? "").localeCompare(b.created_at ?? ""));
90
- const totalDuration = entries.reduce((sum, e) => sum + e.total_duration_ms, 0);
91
- const progressiveness = computeProgressiveness(entries);
92
- return {
93
- collected_at: new Date().toISOString(),
94
- review_root: absReviewRoot,
95
- total_sessions: entries.length,
96
- total_duration_ms: totalDuration,
97
- entries,
98
- progressiveness,
99
- };
100
- }
101
- /**
102
- * 단일 세션 디렉터리에서 ReviewLogEntry 를 추출한다.
103
- */
104
- function parseSession(sessionPath, projectRoot) {
105
- const execResultPath = join(sessionPath, "execution-result.yaml");
106
- const reviewRecordPath = join(sessionPath, "review-record.yaml");
107
- const bindingPath = join(sessionPath, "binding.yaml");
108
- const execResult = parseYamlFile(execResultPath);
109
- if (!execResult) {
110
- // execution-result.yaml 없으면 미완료 세션 — skip
111
- return null;
112
- }
113
- const reviewRecord = parseYamlFile(reviewRecordPath);
114
- const binding = parseYamlFile(bindingPath);
115
- // target refs 수집: binding 우선, record는 보조 자료.
116
- let targetRefs = [];
117
- if (binding?.resolved_target_scope?.resolved_refs) {
118
- targetRefs = binding.resolved_target_scope.resolved_refs.map((r) => normalizeRef(r, projectRoot));
119
- }
120
- // per-lens duration + provenance summary
121
- const provSummary = emptyProvenanceSummary();
122
- const perLensDuration = [];
123
- for (const unit of execResult.lens_execution_results ?? []) {
124
- const prov = classifyProvenance(unit.timestamp_provenance);
125
- provSummary[prov]++;
126
- perLensDuration.push({
127
- lens_id: unit.unit_id,
128
- duration_ms: unit.duration_ms,
129
- provenance: prov,
130
- });
131
- }
132
- // synthesis
133
- let synthesisDuration = null;
134
- if (execResult.synthesize_execution_result) {
135
- synthesisDuration = execResult.synthesize_execution_result.duration_ms;
136
- const synthProv = classifyProvenance(execResult.synthesize_execution_result.timestamp_provenance);
137
- provSummary[synthProv]++;
138
- }
139
- return {
140
- session_id: execResult.session_id,
141
- created_at: reviewRecord?.created_at ?? execResult.execution_started_at,
142
- review_target_refs: targetRefs,
143
- request_text: reviewRecord?.request_text ?? "",
144
- review_mode: requireReviewMode(execResult.review_mode),
145
- execution_realization: execResult.execution_realization,
146
- host_runtime: execResult.host_runtime,
147
- execution_status: execResult.execution_status,
148
- total_duration_ms: execResult.total_duration_ms,
149
- lens_count: execResult.executed_lens_count,
150
- participating_lens_ids: execResult.participating_lens_ids ?? [],
151
- degraded_lens_ids: execResult.degraded_lens_ids ?? [],
152
- per_lens_duration: perLensDuration,
153
- synthesis_duration_ms: synthesisDuration,
154
- provenance_summary: provSummary,
155
- };
156
- }
157
- function requireReviewMode(value) {
158
- if (value === "core-axis" || value === "full")
159
- return value;
160
- throw new Error(`review-log: unsupported review_mode in execution-result.yaml: ${value}`);
161
- }
162
- // ─── Progressiveness ───
163
- /**
164
- * 동일 대상을 반복 review 한 세션을 그룹화하고 점진성 metric 을 산출한다.
165
- *
166
- * 그룹화 기준: review_target_refs 를 정렬 후 join 한 문자열이 일치하면 동일 대상.
167
- * 세션이 2건 미만인 그룹은 점진성 측정 불가 → 제외.
168
- */
169
- export function computeProgressiveness(entries) {
170
- const groups = new Map();
171
- for (const entry of entries) {
172
- if (entry.review_target_refs.length === 0)
173
- continue;
174
- const key = [...entry.review_target_refs].sort().join("\n");
175
- const group = groups.get(key) ?? [];
176
- group.push(entry);
177
- groups.set(key, group);
178
- }
179
- const metrics = [];
180
- for (const [key, group] of groups) {
181
- if (group.length < 2)
182
- continue;
183
- // 시간순 정렬 (이미 정렬됐을 수 있으나 안전)
184
- group.sort((a, b) => a.created_at.localeCompare(b.created_at));
185
- const first = group[0];
186
- const latest = group[group.length - 1];
187
- const totalDuration = group.reduce((s, e) => s + e.total_duration_ms, 0);
188
- const avgDuration = Math.round(totalDuration / group.length);
189
- const deltaDuration = latest.total_duration_ms - first.total_duration_ms;
190
- const deltaRatio = first.total_duration_ms > 0
191
- ? deltaDuration / first.total_duration_ms
192
- : 0;
193
- metrics.push({
194
- target_group_key: key,
195
- target_refs: first.review_target_refs,
196
- sessions: group.map((e) => ({
197
- session_id: e.session_id,
198
- created_at: e.created_at,
199
- total_duration_ms: e.total_duration_ms,
200
- execution_status: e.execution_status,
201
- })),
202
- session_count: group.length,
203
- first_duration_ms: first.total_duration_ms,
204
- latest_duration_ms: latest.total_duration_ms,
205
- duration_delta_ms: deltaDuration,
206
- duration_delta_ratio: Math.round(deltaRatio * 1000) / 1000,
207
- avg_duration_ms: avgDuration,
208
- });
209
- }
210
- // 세션 수 내림차순
211
- metrics.sort((a, b) => b.session_count - a.session_count);
212
- return metrics;
213
- }