principles-disciple 1.8.1 → 1.8.3

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 (508) hide show
  1. package/ADVANCED_CONFIG_ZH.md +97 -0
  2. package/AGENT_INSTALL.md +173 -0
  3. package/AGENT_INSTALL_EN.md +173 -0
  4. package/INSTALL.md +256 -0
  5. package/SKILL.md +63 -0
  6. package/docs/COMMAND_REFERENCE.md +76 -0
  7. package/docs/COMMAND_REFERENCE_EN.md +79 -0
  8. package/esbuild.config.js +75 -0
  9. package/openclaw.plugin.json +4 -4
  10. package/package.json +11 -13
  11. package/scripts/build-web.mjs +46 -0
  12. package/scripts/install-dependencies.cjs +47 -0
  13. package/scripts/sync-plugin.mjs +802 -0
  14. package/scripts/verify-build.mjs +109 -0
  15. package/src/agents/nocturnal-dreamer.md +152 -0
  16. package/src/agents/nocturnal-philosopher.md +138 -0
  17. package/src/agents/nocturnal-reflector.md +126 -0
  18. package/src/agents/nocturnal-scribe.md +164 -0
  19. package/src/commands/capabilities.ts +85 -0
  20. package/{dist/commands/context.js → src/commands/context.ts} +78 -38
  21. package/src/commands/evolution-status.ts +146 -0
  22. package/src/commands/export.ts +111 -0
  23. package/src/commands/focus.ts +533 -0
  24. package/src/commands/nocturnal-review.ts +311 -0
  25. package/src/commands/nocturnal-rollout.ts +763 -0
  26. package/src/commands/nocturnal-train.ts +1002 -0
  27. package/{dist/commands/pain.js → src/commands/pain.ts} +68 -49
  28. package/src/commands/principle-rollback.ts +27 -0
  29. package/{dist/commands/rollback.js → src/commands/rollback.ts} +44 -12
  30. package/src/commands/samples.ts +60 -0
  31. package/src/commands/strategy.ts +38 -0
  32. package/{dist/commands/thinking-os.js → src/commands/thinking-os.ts} +59 -36
  33. package/src/commands/workflow-debug.ts +128 -0
  34. package/{dist/config/defaults/runtime.js → src/config/defaults/runtime.ts} +12 -5
  35. package/src/config/errors.ts +163 -0
  36. package/{dist/config/index.d.ts → src/config/index.ts} +2 -1
  37. package/src/constants/diagnostician.ts +66 -0
  38. package/src/constants/tools.ts +62 -0
  39. package/src/core/adaptive-thresholds.ts +476 -0
  40. package/{dist/core/config-service.js → src/core/config-service.ts} +7 -4
  41. package/{dist/core/config.js → src/core/config.ts} +158 -46
  42. package/src/core/control-ui-db.ts +435 -0
  43. package/{dist/core/detection-funnel.js → src/core/detection-funnel.ts} +36 -21
  44. package/{dist/core/detection-service.js → src/core/detection-service.ts} +7 -4
  45. package/{dist/core/dictionary-service.js → src/core/dictionary-service.ts} +7 -4
  46. package/{dist/core/dictionary.js → src/core/dictionary.ts} +57 -34
  47. package/src/core/empathy-keyword-matcher.ts +327 -0
  48. package/src/core/empathy-types.ts +218 -0
  49. package/src/core/event-log.ts +544 -0
  50. package/src/core/evolution-engine.ts +612 -0
  51. package/src/core/evolution-logger.ts +353 -0
  52. package/src/core/evolution-migration.ts +77 -0
  53. package/src/core/evolution-reducer.ts +731 -0
  54. package/src/core/evolution-types.ts +456 -0
  55. package/src/core/external-training-contract.ts +527 -0
  56. package/src/core/focus-history.ts +1458 -0
  57. package/src/core/hygiene/tracker.ts +117 -0
  58. package/{dist/core/init.js → src/core/init.ts} +39 -26
  59. package/src/core/local-worker-routing.ts +617 -0
  60. package/{dist/core/migration.js → src/core/migration.ts} +18 -11
  61. package/src/core/model-deployment-registry.ts +722 -0
  62. package/src/core/model-training-registry.ts +813 -0
  63. package/src/core/nocturnal-arbiter.ts +706 -0
  64. package/src/core/nocturnal-candidate-scoring.ts +392 -0
  65. package/src/core/nocturnal-compliance.ts +1075 -0
  66. package/src/core/nocturnal-dataset.ts +668 -0
  67. package/src/core/nocturnal-executability.ts +428 -0
  68. package/src/core/nocturnal-export.ts +390 -0
  69. package/{dist/core/nocturnal-paths.js → src/core/nocturnal-paths.ts} +49 -23
  70. package/src/core/nocturnal-trajectory-extractor.ts +484 -0
  71. package/src/core/nocturnal-trinity.ts +1384 -0
  72. package/src/core/pain.ts +122 -0
  73. package/{dist/core/path-resolver.js → src/core/path-resolver.ts} +157 -36
  74. package/{dist/core/paths.js → src/core/paths.ts} +13 -4
  75. package/src/core/principle-training-state.ts +450 -0
  76. package/src/core/profile.ts +226 -0
  77. package/src/core/promotion-gate.ts +822 -0
  78. package/{dist/core/risk-calculator.js → src/core/risk-calculator.ts} +42 -16
  79. package/{dist/core/session-tracker.js → src/core/session-tracker.ts} +175 -62
  80. package/src/core/shadow-observation-registry.ts +534 -0
  81. package/{dist/core/system-logger.js → src/core/system-logger.ts} +9 -5
  82. package/src/core/thinking-models.ts +217 -0
  83. package/src/core/training-program.ts +630 -0
  84. package/src/core/trajectory-types.ts +243 -0
  85. package/src/core/trajectory.ts +1673 -0
  86. package/{dist/core/workspace-context.js → src/core/workspace-context.ts} +57 -32
  87. package/src/hooks/bash-risk.ts +171 -0
  88. package/src/hooks/edit-verification.ts +295 -0
  89. package/src/hooks/gate-block-helper.ts +160 -0
  90. package/src/hooks/gate.ts +210 -0
  91. package/src/hooks/gfi-gate.ts +177 -0
  92. package/src/hooks/lifecycle.ts +326 -0
  93. package/{dist/hooks/llm.js → src/hooks/llm.ts} +160 -80
  94. package/src/hooks/message-sanitize.ts +45 -0
  95. package/src/hooks/pain.ts +384 -0
  96. package/src/hooks/progressive-trust-gate.ts +174 -0
  97. package/src/hooks/prompt.ts +920 -0
  98. package/src/hooks/subagent.ts +207 -0
  99. package/src/hooks/thinking-checkpoint.ts +73 -0
  100. package/src/hooks/trajectory-collector.ts +290 -0
  101. package/src/http/principles-console-route.ts +716 -0
  102. package/src/i18n/commands.ts +117 -0
  103. package/src/index.ts +694 -0
  104. package/src/service/central-database.ts +831 -0
  105. package/src/service/control-ui-query-service.ts +888 -0
  106. package/src/service/evolution-query-service.ts +405 -0
  107. package/src/service/evolution-worker.ts +1646 -0
  108. package/src/service/health-query-service.ts +836 -0
  109. package/{dist/service/nocturnal-runtime.js → src/service/nocturnal-runtime.ts} +235 -79
  110. package/src/service/nocturnal-service.ts +1015 -0
  111. package/src/service/nocturnal-target-selector.ts +532 -0
  112. package/src/service/phase3-input-filter.ts +237 -0
  113. package/src/service/runtime-summary-service.ts +757 -0
  114. package/src/service/subagent-workflow/deep-reflect-workflow-manager.ts +513 -0
  115. package/{dist/service/subagent-workflow/empathy-observer-workflow-manager.js → src/service/subagent-workflow/empathy-observer-workflow-manager.ts} +240 -117
  116. package/src/service/subagent-workflow/index.ts +51 -0
  117. package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +856 -0
  118. package/src/service/subagent-workflow/runtime-direct-driver.ts +166 -0
  119. package/{dist/service/subagent-workflow/types.d.ts → src/service/subagent-workflow/types.ts} +137 -18
  120. package/src/service/subagent-workflow/workflow-store.ts +328 -0
  121. package/src/service/trajectory-service.ts +15 -0
  122. package/{dist/tools/critique-prompt.js → src/tools/critique-prompt.ts} +25 -8
  123. package/src/tools/deep-reflect.ts +349 -0
  124. package/{dist/tools/model-index.js → src/tools/model-index.ts} +33 -17
  125. package/src/types/event-types.ts +453 -0
  126. package/src/types/hygiene-types.ts +31 -0
  127. package/src/types/principle-tree-schema.ts +244 -0
  128. package/src/types/runtime-summary.ts +49 -0
  129. package/src/types.ts +74 -0
  130. package/src/utils/file-lock.ts +391 -0
  131. package/{dist/utils/glob-match.js → src/utils/glob-match.ts} +21 -20
  132. package/{dist/utils/hashing.js → src/utils/hashing.ts} +6 -4
  133. package/src/utils/io.ts +110 -0
  134. package/{dist/utils/nlp.js → src/utils/nlp.ts} +19 -12
  135. package/{dist/utils/plugin-logger.js → src/utils/plugin-logger.ts} +33 -8
  136. package/src/utils/subagent-probe.ts +94 -0
  137. package/templates/langs/en/skills/ai-sprint-orchestration/EXAMPLES.md +63 -0
  138. package/templates/langs/en/skills/ai-sprint-orchestration/REFERENCE.md +136 -0
  139. package/templates/langs/en/skills/ai-sprint-orchestration/SKILL.md +67 -0
  140. package/templates/langs/en/skills/ai-sprint-orchestration/references/agent-registry.json +214 -0
  141. package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/bugfix-complex-template.json +107 -0
  142. package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/feature-complex-template.json +107 -0
  143. package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal-verify.json +105 -0
  144. package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal.json +108 -0
  145. package/templates/langs/en/skills/ai-sprint-orchestration/references/workflow-v1-acceptance-checklist.md +58 -0
  146. package/templates/langs/en/skills/ai-sprint-orchestration/references/workflow-v1.4-work-unit-handoff.md +190 -0
  147. package/templates/langs/en/skills/ai-sprint-orchestration/runtime/.gitignore +2 -0
  148. package/templates/langs/en/skills/ai-sprint-orchestration/scripts/lib/archive.mjs +310 -0
  149. package/templates/langs/en/skills/ai-sprint-orchestration/scripts/lib/contract-enforcement.mjs +683 -0
  150. package/templates/langs/en/skills/ai-sprint-orchestration/scripts/lib/decision.mjs +604 -0
  151. package/templates/langs/en/skills/ai-sprint-orchestration/scripts/lib/state-store.mjs +32 -0
  152. package/templates/langs/en/skills/ai-sprint-orchestration/scripts/lib/task-specs.mjs +707 -0
  153. package/templates/langs/en/skills/ai-sprint-orchestration/scripts/run.mjs +3419 -0
  154. package/templates/langs/zh/skills/ai-sprint-orchestration/EXAMPLES.md +63 -0
  155. package/templates/langs/zh/skills/ai-sprint-orchestration/REFERENCE.md +136 -0
  156. package/templates/langs/zh/skills/ai-sprint-orchestration/SKILL.md +67 -0
  157. package/templates/langs/zh/skills/ai-sprint-orchestration/references/agent-registry.json +214 -0
  158. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/bugfix-complex-template.json +107 -0
  159. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/feature-complex-template.json +107 -0
  160. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal-verify.json +105 -0
  161. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal.json +108 -0
  162. package/templates/langs/zh/skills/ai-sprint-orchestration/references/workflow-v1-acceptance-checklist.md +58 -0
  163. package/templates/langs/zh/skills/ai-sprint-orchestration/references/workflow-v1.4-work-unit-handoff.md +190 -0
  164. package/templates/langs/zh/skills/ai-sprint-orchestration/runtime/.gitignore +2 -0
  165. package/templates/langs/zh/skills/ai-sprint-orchestration/scripts/lib/archive.mjs +310 -0
  166. package/templates/langs/zh/skills/ai-sprint-orchestration/scripts/lib/contract-enforcement.mjs +683 -0
  167. package/templates/langs/zh/skills/ai-sprint-orchestration/scripts/lib/decision.mjs +604 -0
  168. package/templates/langs/zh/skills/ai-sprint-orchestration/scripts/lib/state-store.mjs +32 -0
  169. package/templates/langs/zh/skills/ai-sprint-orchestration/scripts/lib/task-specs.mjs +707 -0
  170. package/templates/langs/zh/skills/ai-sprint-orchestration/scripts/run.mjs +3419 -0
  171. package/templates/langs/zh/skills/ai-sprint-orchestration/test/archive.test.mjs +230 -0
  172. package/templates/langs/zh/skills/ai-sprint-orchestration/test/contract-enforcement.test.mjs +672 -0
  173. package/templates/langs/zh/skills/ai-sprint-orchestration/test/decision.test.mjs +1321 -0
  174. package/templates/langs/zh/skills/ai-sprint-orchestration/test/run.test.mjs +1419 -0
  175. package/templates/langs/zh/skills/pd-diagnostician/SKILL.md +70 -1
  176. package/templates/pain_settings.json +2 -1
  177. package/tests/README.md +120 -0
  178. package/tests/build-artifacts.test.ts +111 -0
  179. package/tests/commands/evolution-status.test.ts +222 -0
  180. package/tests/commands/evolver.test.ts +22 -0
  181. package/tests/commands/export.test.ts +78 -0
  182. package/tests/commands/nocturnal-review.test.ts +448 -0
  183. package/tests/commands/nocturnal-train.test.ts +97 -0
  184. package/tests/commands/pain.test.ts +108 -0
  185. package/tests/commands/samples.test.ts +65 -0
  186. package/tests/commands/strategy.test.ts +34 -0
  187. package/tests/commands/thinking-os.test.ts +88 -0
  188. package/tests/core/adaptive-thresholds.test.ts +261 -0
  189. package/tests/core/config-service.test.ts +89 -0
  190. package/tests/core/config.test.ts +90 -0
  191. package/tests/core/control-ui-db.test.ts +75 -0
  192. package/tests/core/core-template-guidance.test.ts +21 -0
  193. package/tests/core/detection-funnel.test.ts +63 -0
  194. package/tests/core/detection-service.test.ts +50 -0
  195. package/tests/core/dictionary-service.test.ts +116 -0
  196. package/tests/core/dictionary.test.ts +168 -0
  197. package/tests/core/empathy-keyword-matcher.test.ts +209 -0
  198. package/tests/core/event-log.test.ts +181 -0
  199. package/tests/core/evolution-e2e.test.ts +58 -0
  200. package/tests/core/evolution-engine-gate-integration.test.ts +543 -0
  201. package/tests/core/evolution-engine.test.ts +562 -0
  202. package/tests/core/evolution-logger.test.ts +148 -0
  203. package/tests/core/evolution-migration.test.ts +50 -0
  204. package/tests/core/evolution-paths.test.ts +21 -0
  205. package/tests/core/evolution-reducer.detector-metadata.test.ts +602 -0
  206. package/tests/core/evolution-reducer.test.ts +180 -0
  207. package/tests/core/evolution-types-loop.test.ts +48 -0
  208. package/tests/core/evolution-user-stories.e2e.test.ts +249 -0
  209. package/tests/core/external-training-contract.test.ts +463 -0
  210. package/tests/core/focus-history.test.ts +682 -0
  211. package/tests/core/init-flatten.test.ts +69 -0
  212. package/tests/core/init-refactor.test.ts +87 -0
  213. package/tests/core/init-v1.3.test.ts +46 -0
  214. package/tests/core/init.test.ts +190 -0
  215. package/tests/core/local-worker-routing.test.ts +757 -0
  216. package/tests/core/migration.test.ts +84 -0
  217. package/tests/core/model-deployment-registry.test.ts +845 -0
  218. package/tests/core/model-training-registry.test.ts +889 -0
  219. package/tests/core/nocturnal-arbiter.test.ts +494 -0
  220. package/tests/core/nocturnal-candidate-scoring.test.ts +400 -0
  221. package/tests/core/nocturnal-compliance.test.ts +646 -0
  222. package/tests/core/nocturnal-dataset.test.ts +892 -0
  223. package/tests/core/nocturnal-executability.test.ts +357 -0
  224. package/tests/core/nocturnal-export.test.ts +462 -0
  225. package/tests/core/nocturnal-reviewed-subset-comparison.test.ts +428 -0
  226. package/tests/core/nocturnal-trajectory-extractor.test.ts +634 -0
  227. package/tests/core/nocturnal-trinity.test.ts +953 -0
  228. package/tests/core/pain.test.ts +33 -0
  229. package/tests/core/path-resolver.test.ts +57 -0
  230. package/tests/core/paths-refactor.test.ts +42 -0
  231. package/tests/core/phase7-rollout-integration.test.ts +477 -0
  232. package/tests/core/principle-training-state.test.ts +712 -0
  233. package/tests/core/profile.test.ts +56 -0
  234. package/tests/core/promotion-gate.test.ts +556 -0
  235. package/tests/core/risk-calculator.test.ts +168 -0
  236. package/tests/core/session-tracker.test.ts +191 -0
  237. package/tests/core/training-program.test.ts +472 -0
  238. package/tests/core/trajectory.test.ts +265 -0
  239. package/tests/core/workspace-context-factory.test.ts +18 -0
  240. package/tests/core/workspace-context.test.ts +134 -0
  241. package/tests/fixtures/nocturnal-reviewed-subset.json +183 -0
  242. package/tests/fixtures/production-compatibility.test.ts +147 -0
  243. package/tests/fixtures/production-mock-generator.ts +282 -0
  244. package/tests/hooks/bash-risk-integration.test.ts +137 -0
  245. package/tests/hooks/bash-risk.test.ts +81 -0
  246. package/tests/hooks/edit-verification.test.ts +678 -0
  247. package/tests/hooks/gate-edit-verification-p1.test.ts +632 -0
  248. package/tests/hooks/gate-edit-verification.test.ts +435 -0
  249. package/tests/hooks/gate-pipeline-integration.test.ts +404 -0
  250. package/tests/hooks/gate.test.ts +271 -0
  251. package/tests/hooks/gfi-gate-unit.test.ts +422 -0
  252. package/tests/hooks/gfi-gate.test.ts +669 -0
  253. package/tests/hooks/lifecycle.test.ts +248 -0
  254. package/tests/hooks/llm.test.ts +308 -0
  255. package/tests/hooks/message-sanitize.test.ts +36 -0
  256. package/tests/hooks/pain.test.ts +141 -0
  257. package/tests/hooks/progressive-trust-gate.test.ts +277 -0
  258. package/tests/hooks/prompt.test.ts +1411 -0
  259. package/tests/hooks/subagent.test.ts +467 -0
  260. package/tests/hooks/thinking-gate.test.ts +313 -0
  261. package/tests/http/principles-console-route.test.ts +140 -0
  262. package/tests/hygiene-tracker.test.ts +77 -0
  263. package/tests/index.integration.test.ts +179 -0
  264. package/tests/index.shadow-routing.integration.test.ts +140 -0
  265. package/tests/index.test.ts +9 -0
  266. package/tests/integration/empathy-workflow-integration.test.ts +627 -0
  267. package/tests/service/control-ui-query-service.test.ts +121 -0
  268. package/tests/service/empathy-observer-workflow-manager.test.ts +176 -0
  269. package/tests/service/evolution-worker.test.ts +585 -0
  270. package/tests/service/nocturnal-runtime.test.ts +470 -0
  271. package/tests/service/nocturnal-service.test.ts +577 -0
  272. package/tests/service/nocturnal-target-selector.test.ts +615 -0
  273. package/tests/service/nocturnal-workflow-manager.test.ts +439 -0
  274. package/tests/service/phase3-input-filter.test.ts +289 -0
  275. package/tests/service/runtime-summary-service.test.ts +919 -0
  276. package/tests/task-compliance.test.ts +166 -0
  277. package/tests/test-utils.ts +48 -0
  278. package/tests/tools/critique-prompt.test.ts +260 -0
  279. package/tests/tools/deep-reflect.test.ts +232 -0
  280. package/tests/tools/model-index.test.ts +246 -0
  281. package/tests/ui/app.test.tsx +114 -0
  282. package/tests/utils/file-lock.test.ts +407 -0
  283. package/tests/utils/hashing.test.ts +32 -0
  284. package/tests/utils/io.test.ts +39 -0
  285. package/tests/utils/nlp.test.ts +53 -0
  286. package/tests/utils/plugin-logger.test.ts +156 -0
  287. package/tsconfig.json +16 -0
  288. package/tsconfig.tsbuildinfo +1 -0
  289. package/ui/src/App.tsx +45 -0
  290. package/ui/src/api.ts +216 -0
  291. package/ui/src/charts.tsx +586 -0
  292. package/ui/src/components/ErrorState.tsx +6 -0
  293. package/ui/src/components/Loading.tsx +13 -0
  294. package/ui/src/components/ProtectedRoute.tsx +12 -0
  295. package/ui/src/components/Shell.tsx +91 -0
  296. package/ui/src/components/WorkspaceConfig.tsx +146 -0
  297. package/ui/src/components/index.ts +5 -0
  298. package/ui/src/context/auth.tsx +80 -0
  299. package/ui/src/context/theme.tsx +66 -0
  300. package/ui/src/hooks/useAutoRefresh.ts +39 -0
  301. package/ui/src/i18n/ui.ts +363 -0
  302. package/ui/src/main.tsx +16 -0
  303. package/ui/src/pages/EvolutionPage.tsx +352 -0
  304. package/ui/src/pages/FeedbackPage.tsx +140 -0
  305. package/ui/src/pages/GateMonitorPage.tsx +136 -0
  306. package/ui/src/pages/LoginPage.tsx +88 -0
  307. package/ui/src/pages/OverviewPage.tsx +238 -0
  308. package/ui/src/pages/SamplesPage.tsx +174 -0
  309. package/ui/src/pages/ThinkingModelsPage.tsx +127 -0
  310. package/ui/src/styles.css +1661 -0
  311. package/ui/src/types.ts +368 -0
  312. package/ui/src/utils/format.ts +15 -0
  313. package/vitest.config.ts +23 -0
  314. package/dist/commands/capabilities.d.ts +0 -3
  315. package/dist/commands/capabilities.js +0 -73
  316. package/dist/commands/context.d.ts +0 -5
  317. package/dist/commands/evolution-status.d.ts +0 -4
  318. package/dist/commands/evolution-status.js +0 -117
  319. package/dist/commands/evolver.d.ts +0 -9
  320. package/dist/commands/evolver.js +0 -26
  321. package/dist/commands/export.d.ts +0 -2
  322. package/dist/commands/export.js +0 -98
  323. package/dist/commands/focus.d.ts +0 -14
  324. package/dist/commands/focus.js +0 -457
  325. package/dist/commands/nocturnal-review.d.ts +0 -24
  326. package/dist/commands/nocturnal-review.js +0 -265
  327. package/dist/commands/nocturnal-rollout.d.ts +0 -27
  328. package/dist/commands/nocturnal-rollout.js +0 -671
  329. package/dist/commands/nocturnal-train.d.ts +0 -25
  330. package/dist/commands/nocturnal-train.js +0 -919
  331. package/dist/commands/pain.d.ts +0 -5
  332. package/dist/commands/principle-rollback.d.ts +0 -4
  333. package/dist/commands/principle-rollback.js +0 -22
  334. package/dist/commands/rollback.d.ts +0 -19
  335. package/dist/commands/samples.d.ts +0 -2
  336. package/dist/commands/samples.js +0 -55
  337. package/dist/commands/strategy.d.ts +0 -3
  338. package/dist/commands/strategy.js +0 -29
  339. package/dist/commands/thinking-os.d.ts +0 -2
  340. package/dist/config/defaults/runtime.d.ts +0 -40
  341. package/dist/config/errors.d.ts +0 -84
  342. package/dist/config/errors.js +0 -94
  343. package/dist/config/index.js +0 -7
  344. package/dist/constants/diagnostician.d.ts +0 -12
  345. package/dist/constants/diagnostician.js +0 -56
  346. package/dist/constants/tools.d.ts +0 -17
  347. package/dist/constants/tools.js +0 -54
  348. package/dist/core/adaptive-thresholds.d.ts +0 -186
  349. package/dist/core/adaptive-thresholds.js +0 -300
  350. package/dist/core/config-service.d.ts +0 -15
  351. package/dist/core/config.d.ts +0 -129
  352. package/dist/core/control-ui-db.d.ts +0 -95
  353. package/dist/core/control-ui-db.js +0 -292
  354. package/dist/core/detection-funnel.d.ts +0 -33
  355. package/dist/core/detection-service.d.ts +0 -15
  356. package/dist/core/dictionary-service.d.ts +0 -15
  357. package/dist/core/dictionary.d.ts +0 -38
  358. package/dist/core/event-log.d.ts +0 -82
  359. package/dist/core/event-log.js +0 -463
  360. package/dist/core/evolution-engine.d.ts +0 -118
  361. package/dist/core/evolution-engine.js +0 -464
  362. package/dist/core/evolution-logger.d.ts +0 -137
  363. package/dist/core/evolution-logger.js +0 -256
  364. package/dist/core/evolution-migration.d.ts +0 -5
  365. package/dist/core/evolution-migration.js +0 -65
  366. package/dist/core/evolution-reducer.d.ts +0 -98
  367. package/dist/core/evolution-reducer.js +0 -465
  368. package/dist/core/evolution-types.d.ts +0 -287
  369. package/dist/core/evolution-types.js +0 -78
  370. package/dist/core/external-training-contract.d.ts +0 -276
  371. package/dist/core/external-training-contract.js +0 -269
  372. package/dist/core/focus-history.d.ts +0 -210
  373. package/dist/core/focus-history.js +0 -1185
  374. package/dist/core/hygiene/tracker.d.ts +0 -22
  375. package/dist/core/hygiene/tracker.js +0 -106
  376. package/dist/core/init.d.ts +0 -12
  377. package/dist/core/local-worker-routing.d.ts +0 -175
  378. package/dist/core/local-worker-routing.js +0 -525
  379. package/dist/core/migration.d.ts +0 -6
  380. package/dist/core/model-deployment-registry.d.ts +0 -218
  381. package/dist/core/model-deployment-registry.js +0 -503
  382. package/dist/core/model-training-registry.d.ts +0 -295
  383. package/dist/core/model-training-registry.js +0 -475
  384. package/dist/core/nocturnal-arbiter.d.ts +0 -159
  385. package/dist/core/nocturnal-arbiter.js +0 -534
  386. package/dist/core/nocturnal-candidate-scoring.d.ts +0 -137
  387. package/dist/core/nocturnal-candidate-scoring.js +0 -266
  388. package/dist/core/nocturnal-compliance.d.ts +0 -175
  389. package/dist/core/nocturnal-compliance.js +0 -824
  390. package/dist/core/nocturnal-dataset.d.ts +0 -224
  391. package/dist/core/nocturnal-dataset.js +0 -443
  392. package/dist/core/nocturnal-executability.d.ts +0 -85
  393. package/dist/core/nocturnal-executability.js +0 -331
  394. package/dist/core/nocturnal-export.d.ts +0 -124
  395. package/dist/core/nocturnal-export.js +0 -275
  396. package/dist/core/nocturnal-paths.d.ts +0 -124
  397. package/dist/core/nocturnal-trajectory-extractor.d.ts +0 -242
  398. package/dist/core/nocturnal-trajectory-extractor.js +0 -307
  399. package/dist/core/nocturnal-trinity.d.ts +0 -311
  400. package/dist/core/nocturnal-trinity.js +0 -880
  401. package/dist/core/pain.d.ts +0 -4
  402. package/dist/core/pain.js +0 -70
  403. package/dist/core/path-resolver.d.ts +0 -46
  404. package/dist/core/paths.d.ts +0 -65
  405. package/dist/core/principle-training-state.d.ts +0 -121
  406. package/dist/core/principle-training-state.js +0 -321
  407. package/dist/core/profile.d.ts +0 -62
  408. package/dist/core/profile.js +0 -210
  409. package/dist/core/promotion-gate.d.ts +0 -238
  410. package/dist/core/promotion-gate.js +0 -529
  411. package/dist/core/risk-calculator.d.ts +0 -22
  412. package/dist/core/session-tracker.d.ts +0 -101
  413. package/dist/core/shadow-observation-registry.d.ts +0 -217
  414. package/dist/core/shadow-observation-registry.js +0 -308
  415. package/dist/core/system-logger.d.ts +0 -8
  416. package/dist/core/thinking-models.d.ts +0 -38
  417. package/dist/core/thinking-models.js +0 -170
  418. package/dist/core/training-program.d.ts +0 -233
  419. package/dist/core/training-program.js +0 -433
  420. package/dist/core/trajectory.d.ts +0 -411
  421. package/dist/core/trajectory.js +0 -1307
  422. package/dist/core/workspace-context.d.ts +0 -71
  423. package/dist/hooks/bash-risk.d.ts +0 -57
  424. package/dist/hooks/bash-risk.js +0 -137
  425. package/dist/hooks/edit-verification.d.ts +0 -62
  426. package/dist/hooks/edit-verification.js +0 -256
  427. package/dist/hooks/gate-block-helper.d.ts +0 -44
  428. package/dist/hooks/gate-block-helper.js +0 -119
  429. package/dist/hooks/gate.d.ts +0 -24
  430. package/dist/hooks/gate.js +0 -173
  431. package/dist/hooks/gfi-gate.d.ts +0 -40
  432. package/dist/hooks/gfi-gate.js +0 -113
  433. package/dist/hooks/lifecycle.d.ts +0 -5
  434. package/dist/hooks/lifecycle.js +0 -284
  435. package/dist/hooks/llm.d.ts +0 -13
  436. package/dist/hooks/message-sanitize.d.ts +0 -3
  437. package/dist/hooks/message-sanitize.js +0 -37
  438. package/dist/hooks/pain.d.ts +0 -5
  439. package/dist/hooks/pain.js +0 -301
  440. package/dist/hooks/progressive-trust-gate.d.ts +0 -52
  441. package/dist/hooks/progressive-trust-gate.js +0 -134
  442. package/dist/hooks/prompt.d.ts +0 -49
  443. package/dist/hooks/prompt.js +0 -905
  444. package/dist/hooks/subagent.d.ts +0 -10
  445. package/dist/hooks/subagent.js +0 -387
  446. package/dist/hooks/thinking-checkpoint.d.ts +0 -37
  447. package/dist/hooks/thinking-checkpoint.js +0 -51
  448. package/dist/hooks/trajectory-collector.d.ts +0 -32
  449. package/dist/hooks/trajectory-collector.js +0 -256
  450. package/dist/http/principles-console-route.d.ts +0 -9
  451. package/dist/http/principles-console-route.js +0 -681
  452. package/dist/i18n/commands.d.ts +0 -26
  453. package/dist/i18n/commands.js +0 -116
  454. package/dist/index.d.ts +0 -7
  455. package/dist/index.js +0 -581
  456. package/dist/service/central-database.d.ts +0 -104
  457. package/dist/service/central-database.js +0 -649
  458. package/dist/service/control-ui-query-service.d.ts +0 -221
  459. package/dist/service/control-ui-query-service.js +0 -543
  460. package/dist/service/empathy-observer-manager.d.ts +0 -88
  461. package/dist/service/empathy-observer-manager.js +0 -414
  462. package/dist/service/evolution-query-service.d.ts +0 -155
  463. package/dist/service/evolution-query-service.js +0 -258
  464. package/dist/service/evolution-worker.d.ts +0 -101
  465. package/dist/service/evolution-worker.js +0 -975
  466. package/dist/service/health-query-service.d.ts +0 -170
  467. package/dist/service/health-query-service.js +0 -662
  468. package/dist/service/nocturnal-runtime.d.ts +0 -183
  469. package/dist/service/nocturnal-service.d.ts +0 -163
  470. package/dist/service/nocturnal-service.js +0 -787
  471. package/dist/service/nocturnal-target-selector.d.ts +0 -145
  472. package/dist/service/nocturnal-target-selector.js +0 -315
  473. package/dist/service/phase3-input-filter.d.ts +0 -73
  474. package/dist/service/phase3-input-filter.js +0 -172
  475. package/dist/service/runtime-summary-service.d.ts +0 -122
  476. package/dist/service/runtime-summary-service.js +0 -485
  477. package/dist/service/subagent-workflow/empathy-observer-workflow-manager.d.ts +0 -48
  478. package/dist/service/subagent-workflow/index.d.ts +0 -4
  479. package/dist/service/subagent-workflow/index.js +0 -3
  480. package/dist/service/subagent-workflow/runtime-direct-driver.d.ts +0 -77
  481. package/dist/service/subagent-workflow/runtime-direct-driver.js +0 -75
  482. package/dist/service/subagent-workflow/types.js +0 -11
  483. package/dist/service/subagent-workflow/workflow-store.d.ts +0 -26
  484. package/dist/service/subagent-workflow/workflow-store.js +0 -165
  485. package/dist/service/trajectory-service.d.ts +0 -2
  486. package/dist/service/trajectory-service.js +0 -15
  487. package/dist/tools/critique-prompt.d.ts +0 -14
  488. package/dist/tools/deep-reflect.d.ts +0 -39
  489. package/dist/tools/deep-reflect.js +0 -350
  490. package/dist/tools/model-index.d.ts +0 -9
  491. package/dist/types/event-types.d.ts +0 -306
  492. package/dist/types/event-types.js +0 -106
  493. package/dist/types/hygiene-types.d.ts +0 -20
  494. package/dist/types/hygiene-types.js +0 -12
  495. package/dist/types/runtime-summary.d.ts +0 -47
  496. package/dist/types/runtime-summary.js +0 -1
  497. package/dist/types.d.ts +0 -50
  498. package/dist/types.js +0 -22
  499. package/dist/utils/file-lock.d.ts +0 -71
  500. package/dist/utils/file-lock.js +0 -309
  501. package/dist/utils/glob-match.d.ts +0 -28
  502. package/dist/utils/hashing.d.ts +0 -9
  503. package/dist/utils/io.d.ts +0 -6
  504. package/dist/utils/io.js +0 -106
  505. package/dist/utils/nlp.d.ts +0 -9
  506. package/dist/utils/plugin-logger.d.ts +0 -39
  507. package/dist/utils/subagent-probe.d.ts +0 -34
  508. package/dist/utils/subagent-probe.js +0 -81
@@ -0,0 +1,646 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import {
3
+ detectOpportunity,
4
+ detectViolation,
5
+ computeCompliance,
6
+ computeAllCompliance,
7
+ groupEventsIntoSessions,
8
+ type SessionEvents,
9
+ type RawEventEntry,
10
+ } from '../../src/core/nocturnal-compliance.js';
11
+
12
+ // ---------------------------------------------------------------------------
13
+ // Test Utilities
14
+ // ---------------------------------------------------------------------------
15
+
16
+ function makeSession(overrides: Partial<SessionEvents> = {}): SessionEvents {
17
+ return {
18
+ sessionId: overrides.sessionId ?? 'session-1',
19
+ toolCalls: overrides.toolCalls ?? [],
20
+ painSignals: overrides.painSignals ?? [],
21
+ gateBlocks: overrides.gateBlocks ?? [],
22
+ userCorrections: overrides.userCorrections ?? [],
23
+ planApprovals: overrides.planApprovals ?? [],
24
+ };
25
+ }
26
+
27
+ // ---------------------------------------------------------------------------
28
+ // detectOpportunity — T-01
29
+ // ---------------------------------------------------------------------------
30
+
31
+ describe('detectOpportunity — T-01', () => {
32
+ it('returns applicable on edit operations', () => {
33
+ const session = makeSession({
34
+ toolCalls: [{ toolName: 'edit_file', filePath: 'src/main.ts', outcome: 'success' }],
35
+ });
36
+ const result = detectOpportunity('T-01', session);
37
+ expect(result.applicable).toBe(true);
38
+ });
39
+
40
+ it('returns applicable on write_to_file', () => {
41
+ const session = makeSession({
42
+ toolCalls: [{ toolName: 'write_to_file', filePath: 'src/new.ts', outcome: 'success' }],
43
+ });
44
+ expect(detectOpportunity('T-01', session).applicable).toBe(true);
45
+ });
46
+
47
+ it('returns not applicable when only read operations', () => {
48
+ const session = makeSession({
49
+ toolCalls: [{ toolName: 'read_file', filePath: 'src/main.ts', outcome: 'success' }],
50
+ });
51
+ const result = detectOpportunity('T-01', session);
52
+ expect(result.applicable).toBe(false);
53
+ });
54
+
55
+ it('returns not applicable on empty session', () => {
56
+ const session = makeSession({ toolCalls: [] });
57
+ expect(detectOpportunity('T-01', session).applicable).toBe(false);
58
+ });
59
+ });
60
+
61
+ // ---------------------------------------------------------------------------
62
+ // detectOpportunity — T-05
63
+ // ---------------------------------------------------------------------------
64
+
65
+ describe('detectOpportunity — T-05', () => {
66
+ it('returns applicable when gate block fires', () => {
67
+ const session = makeSession({
68
+ gateBlocks: [{ toolName: 'delete_file', filePath: 'src/main.ts', reason: 'risky operation' }],
69
+ });
70
+ const result = detectOpportunity('T-05', session);
71
+ expect(result.applicable).toBe(true);
72
+ });
73
+
74
+ it('returns applicable when risky tool is attempted', () => {
75
+ const session = makeSession({
76
+ toolCalls: [{ toolName: 'delete_file', outcome: 'blocked' }],
77
+ });
78
+ expect(detectOpportunity('T-05', session).applicable).toBe(true);
79
+ });
80
+
81
+ it('returns applicable for dangerous bash command', () => {
82
+ const session = makeSession({
83
+ toolCalls: [{ toolName: 'bash', outcome: 'failure', errorMessage: 'rm -rf /home' }],
84
+ });
85
+ expect(detectOpportunity('T-05', session).applicable).toBe(true);
86
+ });
87
+
88
+ it('returns not applicable when no risky operations', () => {
89
+ const session = makeSession({
90
+ toolCalls: [{ toolName: 'read_file', outcome: 'success' }],
91
+ });
92
+ const result = detectOpportunity('T-05', session);
93
+ expect(result.applicable).toBe(false);
94
+ });
95
+ });
96
+
97
+ // ---------------------------------------------------------------------------
98
+ // detectOpportunity — T-09
99
+ // ---------------------------------------------------------------------------
100
+
101
+ describe('detectOpportunity — T-09', () => {
102
+ it('returns applicable when session has 5+ tool calls', () => {
103
+ const calls = Array.from({ length: 6 }, (_, i) => ({
104
+ toolName: 'read_file' as const,
105
+ filePath: `src/file${i}.ts`,
106
+ outcome: 'success' as const,
107
+ }));
108
+ const session = makeSession({ toolCalls: calls });
109
+ expect(detectOpportunity('T-09', session).applicable).toBe(true);
110
+ });
111
+
112
+ it('returns applicable when 3+ files touched', () => {
113
+ const session = makeSession({
114
+ toolCalls: [
115
+ { toolName: 'read_file', filePath: 'src/a.ts', outcome: 'success' },
116
+ { toolName: 'read_file', filePath: 'src/b.ts', outcome: 'success' },
117
+ { toolName: 'edit_file', filePath: 'src/c.ts', outcome: 'success' },
118
+ ],
119
+ });
120
+ expect(detectOpportunity('T-09', session).applicable).toBe(true);
121
+ });
122
+
123
+ it('returns applicable when pain present on complex task', () => {
124
+ const session = makeSession({
125
+ toolCalls: [
126
+ { toolName: 'edit_file', filePath: 'src/main.ts', outcome: 'failure' },
127
+ ],
128
+ painSignals: [{ source: 'edit', score: 60 }],
129
+ });
130
+ expect(detectOpportunity('T-09', session).applicable).toBe(true);
131
+ });
132
+
133
+ it('returns not applicable for short sessions', () => {
134
+ const session = makeSession({
135
+ toolCalls: [
136
+ { toolName: 'read_file', filePath: 'src/main.ts', outcome: 'success' },
137
+ { toolName: 'edit_file', filePath: 'src/main.ts', outcome: 'success' },
138
+ ],
139
+ });
140
+ expect(detectOpportunity('T-09', session).applicable).toBe(false);
141
+ });
142
+ });
143
+
144
+ // ---------------------------------------------------------------------------
145
+ // detectViolation — T-01
146
+ // ---------------------------------------------------------------------------
147
+
148
+ describe('detectViolation — T-01', () => {
149
+ it('returns violated when editing unread file followed by pain', () => {
150
+ const session = makeSession({
151
+ toolCalls: [
152
+ { toolName: 'edit_file', filePath: 'src/main.ts', outcome: 'failure' },
153
+ ],
154
+ painSignals: [
155
+ { source: 'src/main.ts', score: 50, reason: 'edit without understanding structure' },
156
+ ],
157
+ });
158
+ const result = detectViolation('T-01', session);
159
+ expect(result.violated).toBe(true);
160
+ });
161
+
162
+ it('returns NOT violated when file was read before edit', () => {
163
+ const session = makeSession({
164
+ toolCalls: [
165
+ { toolName: 'read_file', filePath: 'src/main.ts', outcome: 'success' },
166
+ { toolName: 'edit_file', filePath: 'src/main.ts', outcome: 'success' },
167
+ ],
168
+ });
169
+ const result = detectViolation('T-01', session);
170
+ expect(result.violated).toBe(false);
171
+ });
172
+
173
+ it('returns violated when editing unread file followed by tool failure', () => {
174
+ const session = makeSession({
175
+ toolCalls: [
176
+ { toolName: 'edit_file', filePath: 'src/main.ts', outcome: 'failure' },
177
+ ],
178
+ });
179
+ const result = detectViolation('T-01', session);
180
+ expect(result.violated).toBe(true);
181
+ expect(result.reason).toContain('without understanding');
182
+ });
183
+
184
+ it('returns NOT violated when edit succeeds without prior read (but no pain)', () => {
185
+ // No pain signal, no failure → can't confirm violation
186
+ const session = makeSession({
187
+ toolCalls: [
188
+ { toolName: 'edit_file', filePath: 'src/main.ts', outcome: 'success' },
189
+ ],
190
+ });
191
+ const result = detectViolation('T-01', session);
192
+ expect(result.violated).toBe(false);
193
+ });
194
+ });
195
+
196
+ // ---------------------------------------------------------------------------
197
+ // detectViolation — T-05
198
+ // ---------------------------------------------------------------------------
199
+
200
+ describe('detectViolation — T-05', () => {
201
+ it('returns violated when gate block fires on risky operation', () => {
202
+ const session = makeSession({
203
+ gateBlocks: [{ toolName: 'bash', reason: 'rm -rf attempted', filePath: '/' }],
204
+ });
205
+ const result = detectViolation('T-05', session);
206
+ expect(result.violated).toBe(true);
207
+ expect(result.reason).toContain('safety rail not');
208
+ });
209
+
210
+ it('returns violated when gate block fires on delete_file', () => {
211
+ const session = makeSession({
212
+ gateBlocks: [{ toolName: 'delete_file', reason: 'risky', filePath: 'src/old.ts' }],
213
+ });
214
+ expect(detectViolation('T-05', session).violated).toBe(true);
215
+ });
216
+
217
+ it('returns NOT violated when no gate blocks', () => {
218
+ const session = makeSession({
219
+ toolCalls: [{ toolName: 'delete_file', outcome: 'success' }],
220
+ });
221
+ expect(detectViolation('T-05', session).violated).toBe(false);
222
+ });
223
+ });
224
+
225
+ // ---------------------------------------------------------------------------
226
+ // detectViolation — T-09
227
+ // ---------------------------------------------------------------------------
228
+
229
+ describe('detectViolation — T-09', () => {
230
+ it('returns violated on complex task with failure and no planning', () => {
231
+ const calls = Array.from({ length: 6 }, (_, i) => ({
232
+ toolName: 'edit_file' as const,
233
+ filePath: `src/file${i}.ts`,
234
+ outcome: 'failure' as const,
235
+ }));
236
+ const session = makeSession({ toolCalls: calls });
237
+ const result = detectViolation('T-09', session);
238
+ expect(result.violated).toBe(true);
239
+ });
240
+
241
+ it('returns NOT violated on complex task that has plan approval', () => {
242
+ const calls = Array.from({ length: 6 }, (_, i) => ({
243
+ toolName: 'edit_file' as const,
244
+ filePath: `src/file${i}.ts`,
245
+ outcome: 'failure' as const,
246
+ }));
247
+ const session = makeSession({
248
+ toolCalls: calls,
249
+ planApprovals: [{ toolName: 'edit_file', filePath: 'src/main.ts' }],
250
+ });
251
+ expect(detectViolation('T-09', session).violated).toBe(false);
252
+ });
253
+
254
+ it('returns NOT violated on non-complex session', () => {
255
+ const session = makeSession({
256
+ toolCalls: [
257
+ { toolName: 'read_file', filePath: 'src/main.ts', outcome: 'success' },
258
+ { toolName: 'edit_file', filePath: 'src/main.ts', outcome: 'success' },
259
+ ],
260
+ });
261
+ expect(detectViolation('T-09', session).violated).toBe(false);
262
+ });
263
+ });
264
+
265
+ // ---------------------------------------------------------------------------
266
+ // computeCompliance — basic
267
+ // ---------------------------------------------------------------------------
268
+
269
+ describe('computeCompliance — basic', () => {
270
+ it('returns zero compliance when no sessions provided', () => {
271
+ const result = computeCompliance('T-01', []);
272
+ expect(result.principleId).toBe('T-01');
273
+ expect(result.applicableOpportunityCount).toBe(0);
274
+ expect(result.observedViolationCount).toBe(0);
275
+ expect(result.complianceRate).toBe(0);
276
+ expect(result.violationTrend).toBe(0);
277
+ });
278
+
279
+ it('returns compliance 1.0 when all opportunities compliant', () => {
280
+ // T-01 applicable (has edit) but no violation (file was read first)
281
+ const session = makeSession({
282
+ toolCalls: [
283
+ { toolName: 'read_file', filePath: 'src/main.ts', outcome: 'success' },
284
+ { toolName: 'edit_file', filePath: 'src/main.ts', outcome: 'success' },
285
+ ],
286
+ });
287
+ const result = computeCompliance('T-01', [session]);
288
+ expect(result.complianceRate).toBe(1.0);
289
+ expect(result.applicableOpportunityCount).toBe(1);
290
+ expect(result.observedViolationCount).toBe(0);
291
+ });
292
+
293
+ it('returns compliance 0.0 when all opportunities violated', () => {
294
+ // T-05: gate block fires (applicable) AND violated
295
+ const session = makeSession({
296
+ gateBlocks: [{ toolName: 'delete_file', reason: 'risky', filePath: 'src/old.ts' }],
297
+ });
298
+ const result = computeCompliance('T-05', [session]);
299
+ expect(result.complianceRate).toBe(0);
300
+ expect(result.applicableOpportunityCount).toBe(1);
301
+ expect(result.observedViolationCount).toBe(1);
302
+ });
303
+
304
+ it('computes partial compliance correctly', () => {
305
+ const compliant = makeSession({
306
+ toolCalls: [
307
+ { toolName: 'read_file', filePath: 'src/main.ts', outcome: 'success' },
308
+ { toolName: 'edit_file', filePath: 'src/main.ts', outcome: 'success' },
309
+ ],
310
+ });
311
+ const violated = makeSession({
312
+ sessionId: 'session-2',
313
+ toolCalls: [
314
+ { toolName: 'edit_file', filePath: 'src/main.ts', outcome: 'failure' },
315
+ ],
316
+ });
317
+ const result = computeCompliance('T-01', [compliant, violated]);
318
+ // 2 applicable, 1 violated → compliance = (2-1)/2 = 0.5
319
+ expect(result.complianceRate).toBe(0.5);
320
+ expect(result.applicableOpportunityCount).toBe(2);
321
+ expect(result.observedViolationCount).toBe(1);
322
+ });
323
+ });
324
+
325
+ // ---------------------------------------------------------------------------
326
+ // computeCompliance — dilution prevention
327
+ // ---------------------------------------------------------------------------
328
+
329
+ describe('computeCompliance — dilution prevention', () => {
330
+ /**
331
+ * The dilution prevention scenario:
332
+ * T-05 is a LOW-frequency, HIGH-severity principle.
333
+ * If we compute compliance over ALL sessions (including ones with no risky ops),
334
+ * the compliance rate would be inflated because non-applicable sessions
335
+ * count as "compliant by default" — which is WRONG.
336
+ *
337
+ * Our engine ONLY counts sessions where T-05 was applicable.
338
+ */
339
+ it('T-05 compliance ignores sessions with no risky operations', () => {
340
+ // Session A: T-05 violated (gate block on delete)
341
+ const sessionA = makeSession({
342
+ sessionId: 'A',
343
+ gateBlocks: [{ toolName: 'delete_file', reason: 'risky', filePath: 'src/old.ts' }],
344
+ });
345
+
346
+ // Session B: No gate blocks, no risky ops — T-05 NOT APPLICABLE
347
+ const sessionB = makeSession({
348
+ sessionId: 'B',
349
+ toolCalls: [{ toolName: 'read_file', outcome: 'success' }],
350
+ });
351
+
352
+ // Session C: Another non-applicable session (only read ops)
353
+ const sessionC = makeSession({
354
+ sessionId: 'C',
355
+ toolCalls: [{ toolName: 'grep', outcome: 'success' }],
356
+ });
357
+
358
+ // WRONG approach (session-average): (1 + 0 + 0) / 3 = 33% compliance
359
+ // CORRECT approach (opportunity-based): only session A counts
360
+ // Session A: applicable + violated → 0% compliance
361
+ const result = computeCompliance('T-05', [sessionA, sessionB, sessionC]);
362
+
363
+ expect(result.applicableOpportunityCount).toBe(1); // Only session A
364
+ expect(result.observedViolationCount).toBe(1); // Session A violated
365
+ expect(result.complianceRate).toBe(0); // 0% — not diluted by B and C
366
+
367
+ // Explanation must mention dilution prevention
368
+ expect(result.explanation).toContain('applicable opportunities');
369
+ });
370
+
371
+ it('T-01 compliance ignores sessions with no edit operations', () => {
372
+ // Session A: T-01 applicable + violated
373
+ const sessionA = makeSession({
374
+ sessionId: 'A',
375
+ toolCalls: [{ toolName: 'edit_file', filePath: 'src/main.ts', outcome: 'failure' }],
376
+ });
377
+
378
+ // Session B: No edits — T-01 NOT APPLICABLE
379
+ const sessionB = makeSession({
380
+ sessionId: 'B',
381
+ toolCalls: [{ toolName: 'read_file', outcome: 'success' }],
382
+ });
383
+
384
+ // T-01 compliance = 0% (1 applicable, 1 violated), session B doesn't dilute
385
+ const result = computeCompliance('T-01', [sessionA, sessionB]);
386
+
387
+ expect(result.applicableOpportunityCount).toBe(1);
388
+ expect(result.observedViolationCount).toBe(1);
389
+ expect(result.complianceRate).toBe(0);
390
+ });
391
+
392
+ it('high-frequency principle (T-01) still gets high opportunity count across diverse sessions', () => {
393
+ // Sessions with edit ops — all T-01 applicable
394
+ const sessions = [
395
+ makeSession({ sessionId: '1', toolCalls: [{ toolName: 'edit_file', filePath: 'a.ts', outcome: 'success' }] }),
396
+ makeSession({ sessionId: '2', toolCalls: [{ toolName: 'edit_file', filePath: 'b.ts', outcome: 'failure' }] }),
397
+ makeSession({ sessionId: '3', toolCalls: [{ toolName: 'write_to_file', filePath: 'c.ts', outcome: 'success' }] }),
398
+ ];
399
+
400
+ // Session with no edits — T-01 not applicable
401
+ const noEdit = makeSession({ sessionId: '4', toolCalls: [{ toolName: 'grep', outcome: 'success' }] });
402
+
403
+ const result = computeCompliance('T-01', [...sessions, noEdit]);
404
+ expect(result.applicableOpportunityCount).toBe(3); // Only sessions with edits
405
+ });
406
+ });
407
+
408
+ // ---------------------------------------------------------------------------
409
+ // computeCompliance — violationTrend
410
+ // ---------------------------------------------------------------------------
411
+
412
+ describe('computeCompliance — violationTrend', () => {
413
+ function t01(opportunity: 'violated' | 'compliant'): SessionEvents {
414
+ if (opportunity === 'violated') {
415
+ return makeSession({
416
+ sessionId: `s-${opportunity}`,
417
+ toolCalls: [{ toolName: 'edit_file', filePath: 'src/main.ts', outcome: 'failure' }],
418
+ });
419
+ }
420
+ return makeSession({
421
+ sessionId: `s-${opportunity}`,
422
+ toolCalls: [
423
+ { toolName: 'read_file', filePath: 'src/main.ts', outcome: 'success' },
424
+ { toolName: 'edit_file', filePath: 'src/main.ts', outcome: 'success' },
425
+ ],
426
+ });
427
+ }
428
+
429
+ it('returns trend = +1 (improving) when recent violations decrease', () => {
430
+ // Most recent: compliant, compliant
431
+ // Previous: violated, violated, violated
432
+ // Recent rate = 0/2 = 0, Previous rate = 3/3 = 1
433
+ // delta = 1 - 0 > 0.1 → improving (+1)
434
+ const sessions = [
435
+ t01('compliant'), // index 0 (most recent in input order)
436
+ t01('compliant'), // index 1
437
+ t01('violated'), // index 2
438
+ t01('violated'), // index 3
439
+ t01('violated'), // index 4
440
+ ];
441
+ const result = computeCompliance('T-01', sessions, { trendWindowSize: 2 });
442
+ expect(result.violationTrend).toBe(1);
443
+ });
444
+
445
+ it('returns trend = -1 (worsening) when recent violations increase', () => {
446
+ // Most recent: violated, violated
447
+ // Previous: compliant, compliant
448
+ // Recent rate = 2/2 = 1, Previous rate = 0/2 = 0
449
+ // delta = 0 - 1 = -1 < -0.1 → worsening (-1)
450
+ const sessions = [
451
+ t01('violated'),
452
+ t01('violated'),
453
+ t01('compliant'),
454
+ t01('compliant'),
455
+ ];
456
+ const result = computeCompliance('T-01', sessions, { trendWindowSize: 2 });
457
+ expect(result.violationTrend).toBe(-1);
458
+ });
459
+
460
+ it('returns trend = 0 when stable', () => {
461
+ // 2 compliant, then 2 compliant
462
+ const sessions = [t01('compliant'), t01('compliant'), t01('compliant'), t01('compliant')];
463
+ const result = computeCompliance('T-01', sessions, { trendWindowSize: 2 });
464
+ expect(result.violationTrend).toBe(0);
465
+ });
466
+
467
+ it('returns trend = 0 when only 1 applicable session', () => {
468
+ const sessions = [t01('violated')];
469
+ const result = computeCompliance('T-01', sessions);
470
+ expect(result.violationTrend).toBe(0);
471
+ });
472
+ });
473
+
474
+ // ---------------------------------------------------------------------------
475
+ // computeAllCompliance
476
+ // ---------------------------------------------------------------------------
477
+
478
+ describe('computeAllCompliance', () => {
479
+ it('returns results for all T-01 through T-09', () => {
480
+ const results = computeAllCompliance([]);
481
+ const ids = results.map((r) => r.principleId);
482
+ expect(ids).toEqual(['T-01', 'T-02', 'T-03', 'T-04', 'T-05', 'T-06', 'T-07', 'T-08', 'T-09']);
483
+ });
484
+
485
+ it('each result has all required fields', () => {
486
+ const results = computeAllCompliance([]);
487
+ for (const result of results) {
488
+ expect(result.principleId).toBeDefined();
489
+ expect(result.applicableOpportunityCount).toBe(0);
490
+ expect(result.observedViolationCount).toBe(0);
491
+ expect(result.complianceRate).toBe(0);
492
+ expect(result.violationTrend).toBe(0);
493
+ expect(result.explanation).toBeDefined();
494
+ }
495
+ });
496
+ });
497
+
498
+ // ---------------------------------------------------------------------------
499
+ // groupEventsIntoSessions
500
+ // ---------------------------------------------------------------------------
501
+
502
+ describe('groupEventsIntoSessions', () => {
503
+ function event(type: string, sessionId: string, data: Record<string, unknown> = {}): RawEventEntry {
504
+ return { ts: '2026-03-27T12:00:00Z', type, sessionId, data };
505
+ }
506
+
507
+ it('groups events by sessionId', () => {
508
+ const events: RawEventEntry[] = [
509
+ event('tool_call', 's1', { toolName: 'read_file', filePath: 'a.ts' }),
510
+ event('tool_call', 's1', { toolName: 'edit_file', filePath: 'b.ts' }),
511
+ event('tool_call', 's2', { toolName: 'read_file', filePath: 'c.ts' }),
512
+ ];
513
+ const sessions = groupEventsIntoSessions(events);
514
+ expect(sessions.get('s1')!.toolCalls).toHaveLength(2);
515
+ expect(sessions.get('s2')!.toolCalls).toHaveLength(1);
516
+ });
517
+
518
+ it('maps pain_signal events', () => {
519
+ const events: RawEventEntry[] = [
520
+ event('pain_signal', 's1', { source: 'edit', score: 50, severity: 'moderate' }),
521
+ ];
522
+ const sessions = groupEventsIntoSessions(events);
523
+ expect(sessions.get('s1')!.painSignals).toHaveLength(1);
524
+ expect(sessions.get('s1')!.painSignals[0].source).toBe('edit');
525
+ expect(sessions.get('s1')!.painSignals[0].severity).toBe('moderate');
526
+ });
527
+
528
+ it('maps gate_block events', () => {
529
+ const events: RawEventEntry[] = [
530
+ event('gate_block', 's1', { toolName: 'bash', reason: 'dangerous command' }),
531
+ ];
532
+ const sessions = groupEventsIntoSessions(events);
533
+ expect(sessions.get('s1')!.gateBlocks).toHaveLength(1);
534
+ expect(sessions.get('s1')!.gateBlocks[0].toolName).toBe('bash');
535
+ });
536
+
537
+ it('maps plan_approval events', () => {
538
+ const events: RawEventEntry[] = [
539
+ event('plan_approval', 's1', { toolName: 'edit_file', filePath: 'src/main.ts' }),
540
+ ];
541
+ const sessions = groupEventsIntoSessions(events);
542
+ expect(sessions.get('s1')!.planApprovals).toHaveLength(1);
543
+ });
544
+
545
+ it('groups events without sessionId into "unknown"', () => {
546
+ const events: RawEventEntry[] = [
547
+ { ts: '2026-03-27T12:00:00Z', type: 'tool_call', data: { toolName: 'read_file' } },
548
+ ];
549
+ const sessions = groupEventsIntoSessions(events);
550
+ expect(sessions.get('unknown')!.toolCalls).toHaveLength(1);
551
+ });
552
+
553
+ it('correctly maps error field to outcome', () => {
554
+ const events: RawEventEntry[] = [
555
+ event('tool_call', 's1', { toolName: 'edit_file', error: 'file not found' }),
556
+ event('tool_call', 's2', { toolName: 'read_file' }), // no error → success
557
+ ];
558
+ const sessions = groupEventsIntoSessions(events);
559
+ expect(sessions.get('s1')!.toolCalls[0].outcome).toBe('failure');
560
+ expect(sessions.get('s2')!.toolCalls[0].outcome).toBe('success');
561
+ });
562
+ });
563
+
564
+ // ---------------------------------------------------------------------------
565
+ // Explanation is human-readable
566
+ // ---------------------------------------------------------------------------
567
+
568
+ describe('ComplianceResult — explanation', () => {
569
+ it('explanation includes compliance rate and trend', () => {
570
+ const session = makeSession({
571
+ toolCalls: [
572
+ { toolName: 'read_file', filePath: 'src/main.ts', outcome: 'success' },
573
+ { toolName: 'edit_file', filePath: 'src/main.ts', outcome: 'success' },
574
+ ],
575
+ });
576
+ const result = computeCompliance('T-01', [session]);
577
+ expect(result.explanation).toContain('T-01');
578
+ expect(result.explanation).toContain('applicable opportunities');
579
+ expect(result.explanation).toContain('100.0%'); // compliance rate
580
+ });
581
+
582
+ it('explanation notes when no opportunities exist', () => {
583
+ const result = computeCompliance('T-05', [
584
+ makeSession({ toolCalls: [{ toolName: 'read_file', outcome: 'success' }] }),
585
+ ]);
586
+ expect(result.explanation).toContain('No applicable opportunities');
587
+ });
588
+
589
+ it('explanation includes sample violation reasons', () => {
590
+ const session = makeSession({
591
+ gateBlocks: [{ toolName: 'delete_file', reason: 'risky', filePath: 'src/old.ts' }],
592
+ });
593
+ const result = computeCompliance('T-05', [session]);
594
+ expect(result.explanation).toContain('violation');
595
+ expect(result.explanation).toContain('safety rail not');
596
+ });
597
+ });
598
+
599
+ // ---------------------------------------------------------------------------
600
+ // Integration: full session list
601
+ // ---------------------------------------------------------------------------
602
+
603
+ describe('Full session integration — T-05 dilution scenario', () => {
604
+ /**
605
+ * Real-world scenario: 20 sessions in a day.
606
+ * Only 2 sessions involve risky operations (T-05 applicable).
607
+ * Both had gate blocks (violations).
608
+ * 18 sessions had no risky operations (T-05 not applicable).
609
+ *
610
+ * If we averaged all 20 sessions: 2 violations / 20 = 90% compliance (wrong!)
611
+ * With opportunity-based: 2 applicable / 2 violated = 0% compliance (correct!)
612
+ */
613
+ it('does not dilute low-frequency high-severity principle compliance', () => {
614
+ function gateBlockSession(id: string): SessionEvents {
615
+ return makeSession({
616
+ sessionId: id,
617
+ gateBlocks: [{ toolName: 'bash', reason: 'rm -rf attempted', filePath: '/' }],
618
+ });
619
+ }
620
+
621
+ function safeSession(id: string): SessionEvents {
622
+ return makeSession({
623
+ sessionId: id,
624
+ toolCalls: [{ toolName: 'read_file', outcome: 'success' }],
625
+ });
626
+ }
627
+
628
+ const sessions: SessionEvents[] = [
629
+ // Only 2 sessions where T-05 is applicable
630
+ gateBlockSession('risky-1'),
631
+ gateBlockSession('risky-2'),
632
+ // 18 sessions where T-05 is NOT applicable (safe operations)
633
+ ...Array.from({ length: 18 }, (_, i) => safeSession(`safe-${i}`)),
634
+ ];
635
+
636
+ const result = computeCompliance('T-05', sessions);
637
+
638
+ // Only 2 applicable opportunities
639
+ expect(result.applicableOpportunityCount).toBe(2);
640
+ // Both were violated
641
+ expect(result.observedViolationCount).toBe(2);
642
+ // Compliance = 0% — NOT 90%
643
+ expect(result.complianceRate).toBe(0);
644
+ expect(result.explanation).toContain('applicable opportunities');
645
+ });
646
+ });