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,577 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import * as fs from 'fs';
3
+ import * as path from 'path';
4
+ import * as os from 'os';
5
+ import {
6
+ executeNocturnalReflection,
7
+ executeNocturnalReflectionAsync,
8
+ listApprovedNocturnalArtifacts,
9
+ } from '../../src/service/nocturnal-service.js';
10
+ import { createNocturnalTrajectoryExtractor } from '../../src/core/nocturnal-trajectory-extractor.js';
11
+ import { TrajectoryDatabase, TrajectoryRegistry } from '../../src/core/trajectory.js';
12
+ import {
13
+ checkWorkspaceIdle,
14
+ clearAllCooldowns,
15
+ recordRunStart,
16
+ recordRunEnd,
17
+ } from '../../src/service/nocturnal-runtime.js';
18
+ import { NocturnalPathResolver } from '../../src/core/nocturnal-paths.js';
19
+ import { seedSessionForTest, clearSession, listSessions } from '../../src/core/session-tracker.js';
20
+
21
+ /**
22
+ * NocturnalService Integration Tests
23
+ *
24
+ * NOTE: These tests have complex setup due to:
25
+ * 1. TrajectoryRegistry singleton - must use same instance as service
26
+ * 2. Async cleanup on Windows - wrapped in try-catch
27
+ * 3. Fire-and-forget cooldowns - use awaitClearAllCooldowns helper
28
+ */
29
+ describe('NocturnalService', () => {
30
+ let tmpDir: string;
31
+ let workspaceDir: string;
32
+ let stateDir: string;
33
+ let trajectory: TrajectoryDatabase;
34
+
35
+ beforeEach(() => {
36
+ // Create a clean temp directory structure:
37
+ // tmpDir/
38
+ // workspace/ ← workspaceDir
39
+ // state/ ← stateDir
40
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pd-nocturnal-service-test-'));
41
+ workspaceDir = path.join(tmpDir, 'workspace');
42
+ stateDir = path.join(tmpDir, 'state');
43
+ fs.mkdirSync(workspaceDir, { recursive: true });
44
+ fs.mkdirSync(stateDir, { recursive: true });
45
+
46
+ // Initialize trajectory DB and prime the TrajectoryRegistry singleton.
47
+ // The service uses createNocturnalTrajectoryExtractor which internally
48
+ // calls TrajectoryRegistry.get(workspaceDir). We call it here so that
49
+ // the service sees the SAME instance we seed with test data.
50
+ trajectory = new TrajectoryDatabase({ workspaceDir });
51
+
52
+ // Prime the singleton so the service gets our seeded instance
53
+ const extractor = createNocturnalTrajectoryExtractor(workspaceDir);
54
+ // extractor holds the same TrajectoryDatabase instance via singleton
55
+
56
+ // Also clear any residual SessionTracker sessions from previous tests
57
+ // (SessionTracker uses an in-memory Map that persists across tests)
58
+ for (const session of listSessions()) {
59
+ clearSession(session.sessionId);
60
+ }
61
+ });
62
+
63
+ afterEach(() => {
64
+ // Dispose in correct order: trajectory first, then registry.
65
+ // On Windows, file handles may not be released immediately, so wrap in try-catch.
66
+ try {
67
+ trajectory.dispose();
68
+ } catch {
69
+ // Best effort
70
+ }
71
+ try {
72
+ TrajectoryRegistry.dispose(workspaceDir);
73
+ } catch {
74
+ // Best effort
75
+ }
76
+ try {
77
+ fs.rmSync(tmpDir, { recursive: true, force: true });
78
+ } catch {
79
+ // On Windows, some file handles may still be open
80
+ }
81
+ });
82
+
83
+ // -------------------------------------------------------------------------
84
+ // Helper: awaitable clear cooldowns (sync version writes directly)
85
+ // -------------------------------------------------------------------------
86
+
87
+ function clearCooldownsSync(): void {
88
+ // Write empty cooldown state directly without async
89
+ const runtimePath = path.join(stateDir, 'nocturnal-runtime.json');
90
+ const defaultState = {
91
+ principleCooldowns: {},
92
+ recentRunTimestamps: [],
93
+ };
94
+ fs.writeFileSync(runtimePath, JSON.stringify(defaultState, null, 2), 'utf-8');
95
+ }
96
+
97
+ // -------------------------------------------------------------------------
98
+ // Helper: create a properly idle override (bypasses real idle check)
99
+ // -------------------------------------------------------------------------
100
+
101
+ function makeIdleResult(): ReturnType<typeof checkWorkspaceIdle> {
102
+ return {
103
+ isIdle: true,
104
+ mostRecentActivityAt: Date.now() - 2 * 60 * 60 * 1000, // 2 hours ago
105
+ idleForMs: 2 * 60 * 60 * 1000,
106
+ userActiveSessions: 0,
107
+ abandonedSessionIds: [],
108
+ trajectoryGuardrailConfirmsIdle: true,
109
+ reason: 'test override — workspace considered idle',
110
+ };
111
+ }
112
+
113
+ // -------------------------------------------------------------------------
114
+ // Helper: seed a minimal trajectory
115
+ // -------------------------------------------------------------------------
116
+
117
+ function seedSession(
118
+ sessionId: string,
119
+ startedAt: string,
120
+ opts: {
121
+ withToolCalls?: number;
122
+ withPain?: boolean;
123
+ withGateBlock?: boolean;
124
+ outcome?: 'success' | 'failure';
125
+ } = {}
126
+ ): void {
127
+ trajectory.recordSession({ sessionId, startedAt });
128
+ const { withToolCalls = 1, withPain = false, withGateBlock = false, outcome = 'failure' } = opts;
129
+
130
+ for (let i = 0; i < withToolCalls; i++) {
131
+ trajectory.recordToolCall({
132
+ sessionId,
133
+ toolName: 'Bash',
134
+ outcome,
135
+ errorMessage: outcome === 'failure' ? 'Command failed: exit code 1' : null,
136
+ });
137
+ }
138
+
139
+ if (withPain) {
140
+ trajectory.recordPainEvent({
141
+ sessionId,
142
+ source: 'test',
143
+ score: 50,
144
+ reason: 'Test pain event',
145
+ });
146
+ }
147
+
148
+ if (withGateBlock) {
149
+ trajectory.recordGateBlock({
150
+ sessionId,
151
+ toolName: 'Edit',
152
+ reason: 'Safety check: RISK_PATH modification requires PLAN.md',
153
+ riskLevel: 'medium',
154
+ });
155
+ }
156
+
157
+ // Also seed SessionTracker.sessions so checkWorkspaceIdle can see the session
158
+ // (SessionTracker is separate from TrajectoryDatabase)
159
+ const lastActivityAt = new Date(startedAt).getTime();
160
+ seedSessionForTest(sessionId, workspaceDir, lastActivityAt);
161
+ }
162
+
163
+ // -------------------------------------------------------------------------
164
+ // Helper: seed evaluable principles (T-08 for most tests)
165
+ // -------------------------------------------------------------------------
166
+
167
+ function seedPrinciples(): void {
168
+ const trainingStatesPath = path.join(stateDir, 'principle_training_state.json');
169
+ // T-01 has low compliance so T-08 wins selection (higher compliance = lower score priority)
170
+ const data = {
171
+ 'T-01': {
172
+ principleId: 'T-01',
173
+ principleName: 'Map Before Territory',
174
+ evaluability: 'deterministic',
175
+ complianceRate: 0.3,
176
+ violationTrend: 1,
177
+ observedViolationCount: 3,
178
+ applicableOpportunityCount: 10,
179
+ generatedSampleCount: 1,
180
+ cooldownUntil: null,
181
+ internalizationStatus: 'internalized',
182
+ },
183
+ 'T-08': {
184
+ principleId: 'T-08',
185
+ principleName: 'Pain as Signal',
186
+ evaluability: 'deterministic',
187
+ complianceRate: 0.8,
188
+ violationTrend: 1,
189
+ observedViolationCount: 5,
190
+ applicableOpportunityCount: 8,
191
+ generatedSampleCount: 0,
192
+ cooldownUntil: null,
193
+ internalizationStatus: 'internalized',
194
+ },
195
+ };
196
+ fs.writeFileSync(trainingStatesPath, JSON.stringify(data, null, 2), 'utf-8');
197
+ }
198
+
199
+ // -------------------------------------------------------------------------
200
+ // Helper: force workspace to be "idle"
201
+ // -------------------------------------------------------------------------
202
+
203
+ function forceIdleWorkspace(): void {
204
+ // Workspace is idle when there are no recent sessions or sessions are old
205
+ // A session from 2+ hours ago will be considered abandoned
206
+ const oldSessionId = 'session-old-abandoned';
207
+ const twoHoursAgo = new Date(Date.now() - 2 * 60 * 60 * 1000 - 1000).toISOString();
208
+ seedSession(oldSessionId, twoHoursAgo, { withToolCalls: 0 });
209
+ }
210
+
211
+ // -------------------------------------------------------------------------
212
+ // Tests: executeNocturnalReflection — successful run
213
+ // -------------------------------------------------------------------------
214
+
215
+ describe('executeNocturnalReflection — successful run', () => {
216
+ it('produces an approved artifact when all conditions are met', () => {
217
+ // Setup: idle workspace + evaluable principles + clear cooldowns + violating session
218
+ forceIdleWorkspace();
219
+ seedPrinciples();
220
+ clearCooldownsSync();
221
+
222
+ const recentSessionId = 'session-recent-violation';
223
+ const now = new Date().toISOString();
224
+ seedSession(recentSessionId, now, { withToolCalls: 3, withPain: true, outcome: 'failure' });
225
+
226
+ const idleResult = makeIdleResult();
227
+ const result = executeNocturnalReflection(workspaceDir, stateDir, { idleCheckOverride: idleResult });
228
+
229
+ expect(result.success).toBe(true);
230
+ expect(result.artifact).toBeDefined();
231
+ expect(result.artifact?.principleId).toBe('T-08');
232
+ expect(result.artifact?.sessionId).toBe(recentSessionId);
233
+ expect(result.artifact?.badDecision).toBeTruthy();
234
+ expect(result.artifact?.betterDecision).toBeTruthy();
235
+ expect(result.artifact?.rationale).toBeTruthy();
236
+ expect(result.diagnostics.persisted).toBe(true);
237
+ expect(result.diagnostics.persistedPath).toBeDefined();
238
+ });
239
+
240
+ it('persists artifact to the samples directory', () => {
241
+ forceIdleWorkspace();
242
+ seedPrinciples();
243
+ clearCooldownsSync();
244
+
245
+ const recentSessionId = 'session-recent-2';
246
+ const now = new Date().toISOString();
247
+ seedSession(recentSessionId, now, { withToolCalls: 2, withPain: true, outcome: 'failure' });
248
+
249
+ const idleResult = makeIdleResult();
250
+ const result = executeNocturnalReflection(workspaceDir, stateDir, { idleCheckOverride: idleResult });
251
+ expect(result.success).toBe(true);
252
+ expect(result.diagnostics.persistedPath).toBeDefined();
253
+
254
+ // Verify file exists
255
+ const persistedPath = result.diagnostics.persistedPath!;
256
+ expect(fs.existsSync(persistedPath)).toBe(true);
257
+
258
+ // Verify content
259
+ const content = JSON.parse(fs.readFileSync(persistedPath, 'utf-8'));
260
+ expect(content.status).toBe('approved');
261
+ expect(content.artifactId).toBe(result.artifact?.artifactId);
262
+ expect(content.boundedAction).toBeDefined();
263
+ });
264
+
265
+ it('returns a boundedAction in the artifact', () => {
266
+ forceIdleWorkspace();
267
+ seedPrinciples();
268
+ clearCooldownsSync();
269
+
270
+ const recentSessionId = 'session-recent-3';
271
+ const now = new Date().toISOString();
272
+ // Seed with pain=true so T-08 is violated (pain+failure needed for T-08)
273
+ seedSession(recentSessionId, now, { withToolCalls: 3, withPain: true, outcome: 'failure' });
274
+
275
+ const idleResult = makeIdleResult();
276
+ const result = executeNocturnalReflection(workspaceDir, stateDir, { idleCheckOverride: idleResult });
277
+ expect(result.success).toBe(true);
278
+ expect(result.artifact?.boundedAction).toBeDefined();
279
+ expect(result.artifact?.boundedAction?.verb).toBeTruthy();
280
+ expect(result.artifact?.boundedAction?.target).toBeTruthy();
281
+ });
282
+ });
283
+
284
+ // -------------------------------------------------------------------------
285
+ // Tests: executeNocturnalReflection — skip conditions
286
+ // -------------------------------------------------------------------------
287
+
288
+ describe('executeNocturnalReflection — skip conditions', () => {
289
+ it('skips when workspace is not idle', () => {
290
+ // Setup: NO forceIdleWorkspace - workspace has a very recent session
291
+ seedPrinciples();
292
+ clearCooldownsSync();
293
+
294
+ // Create a session that started JUST NOW (non-idle)
295
+ const activeSessionId = 'session-active';
296
+ const justNow = new Date().toISOString();
297
+ seedSession(activeSessionId, justNow, { withToolCalls: 1 });
298
+
299
+ const result = executeNocturnalReflection(workspaceDir, stateDir);
300
+
301
+ // Workspace is NOT idle, so preflight should block
302
+ expect(result.success).toBe(false);
303
+ expect(result.noTargetSelected).toBe(false); // preflight blocked
304
+ });
305
+
306
+ it('skips when no evaluable principles exist', () => {
307
+ forceIdleWorkspace();
308
+ clearCooldownsSync();
309
+ // Don't seed any principles
310
+
311
+ const recentSessionId = 'session-recent';
312
+ const now = new Date().toISOString();
313
+ seedSession(recentSessionId, now, { withToolCalls: 3, withPain: true, outcome: 'failure' });
314
+
315
+ const idleResult = makeIdleResult();
316
+ const result = executeNocturnalReflection(workspaceDir, stateDir, { idleCheckOverride: idleResult });
317
+ expect(result.success).toBe(false);
318
+ expect(result.noTargetSelected).toBe(true);
319
+ expect(result.skipReason).toBe('no_evaluable_principles');
320
+ });
321
+
322
+ it('skips when no violating sessions found (only successful sessions)', () => {
323
+ forceIdleWorkspace();
324
+ seedPrinciples();
325
+ clearCooldownsSync();
326
+
327
+ // Only successful sessions (no violations)
328
+ const sessionId = 'session-success-only';
329
+ const now = new Date().toISOString();
330
+ seedSession(sessionId, now, { withToolCalls: 3, outcome: 'success' });
331
+
332
+ const idleResult = makeIdleResult();
333
+ const result = executeNocturnalReflection(workspaceDir, stateDir, { idleCheckOverride: idleResult });
334
+ expect(result.success).toBe(false);
335
+ expect(result.noTargetSelected).toBe(true);
336
+ expect(result.skipReason).toBe('no_violating_sessions');
337
+ });
338
+
339
+ it('returns failure when snapshot extraction fails', () => {
340
+ forceIdleWorkspace();
341
+ seedPrinciples();
342
+ clearCooldownsSync();
343
+
344
+ // Create a session with no tool calls - will fail snapshot extraction
345
+ const sessionId = 'session-no-toolcalls';
346
+ const now = new Date().toISOString();
347
+ seedSession(sessionId, now, { withToolCalls: 0, withPain: false, outcome: 'success' });
348
+
349
+ const idleResult = makeIdleResult();
350
+ const result = executeNocturnalReflection(workspaceDir, stateDir, { idleCheckOverride: idleResult });
351
+ // Session has no tool calls, so minToolCalls=1 filter removes it
352
+ // No sessions available → selection skips with insufficient_snapshot_data
353
+ expect(result.success).toBe(false);
354
+ expect(result.noTargetSelected).toBe(true);
355
+ });
356
+ });
357
+
358
+ // -------------------------------------------------------------------------
359
+ // Tests: executeNocturnalReflection — reflector override
360
+ // -------------------------------------------------------------------------
361
+
362
+ describe('executeNocturnalReflection — reflector override', () => {
363
+ it('uses reflectorOutputOverride when skipReflector is true', () => {
364
+ forceIdleWorkspace();
365
+ seedPrinciples();
366
+ clearCooldownsSync();
367
+
368
+ const recentSessionId = 'session-override-test';
369
+ const now = new Date().toISOString();
370
+ seedSession(recentSessionId, now, { withToolCalls: 3, withPain: true, outcome: 'failure' });
371
+
372
+ const overrideArtifact = {
373
+ artifactId: '11111111-2222-4333-aaaa-bbbbbbbbbbbb',
374
+ sessionId: recentSessionId,
375
+ principleId: 'T-08',
376
+ sourceSnapshotRef: 'snapshot-override',
377
+ badDecision: 'Overridden bad decision',
378
+ betterDecision: 'Read the error message before retrying the bash command',
379
+ rationale: 'Overridden rationale for testing purposes',
380
+ createdAt: new Date().toISOString(),
381
+ };
382
+
383
+ const idleResult = makeIdleResult();
384
+ const result = executeNocturnalReflection(workspaceDir, stateDir, {
385
+ skipReflector: true,
386
+ reflectorOutputOverride: JSON.stringify(overrideArtifact),
387
+ idleCheckOverride: idleResult,
388
+ });
389
+
390
+ expect(result.success).toBe(true);
391
+ expect(result.artifact?.artifactId).toBe('11111111-2222-4333-aaaa-bbbbbbbbbbbb');
392
+ expect(result.artifact?.badDecision).toBe('Overridden bad decision');
393
+ });
394
+
395
+ it('fails if skipReflector is true but no override provided', () => {
396
+ forceIdleWorkspace();
397
+ seedPrinciples();
398
+ clearCooldownsSync();
399
+
400
+ // Seed a session so selector finds a target, then validation fails because no override
401
+ const recentSessionId = 'session-no-override';
402
+ const now = new Date().toISOString();
403
+ seedSession(recentSessionId, now, { withToolCalls: 3, withPain: true, outcome: 'failure' });
404
+
405
+ const idleResult = makeIdleResult();
406
+ const result = executeNocturnalReflection(workspaceDir, stateDir, {
407
+ skipReflector: true,
408
+ idleCheckOverride: idleResult,
409
+ // no reflectorOutputOverride
410
+ });
411
+
412
+ expect(result.success).toBe(false);
413
+ expect(result.validationFailed).toBe(true);
414
+ expect(result.validationFailures.some(f => f.includes('reflectorOutputOverride'))).toBe(true);
415
+ });
416
+
417
+ it('rejects invalid JSON in reflectorOutputOverride', () => {
418
+ forceIdleWorkspace();
419
+ seedPrinciples();
420
+ clearCooldownsSync();
421
+
422
+ const recentSessionId = 'session-bad-override';
423
+ const now = new Date().toISOString();
424
+ seedSession(recentSessionId, now, { withToolCalls: 3, withPain: true, outcome: 'failure' });
425
+
426
+ const idleResult = makeIdleResult();
427
+ const result = executeNocturnalReflection(workspaceDir, stateDir, {
428
+ skipReflector: true,
429
+ reflectorOutputOverride: 'not valid json',
430
+ idleCheckOverride: idleResult,
431
+ });
432
+
433
+ expect(result.success).toBe(false);
434
+ expect(result.validationFailed).toBe(true);
435
+ expect(result.validationFailures.some(f => f.includes('parse'))).toBe(true);
436
+ });
437
+ });
438
+
439
+ // -------------------------------------------------------------------------
440
+ // Tests: executeNocturnalReflection — arbiter/executability rejection
441
+ // -------------------------------------------------------------------------
442
+
443
+ describe('executeNocturnalReflection — validation rejection', () => {
444
+ it('rejects artifact with vague verb in betterDecision', () => {
445
+ forceIdleWorkspace();
446
+ seedPrinciples();
447
+ clearCooldownsSync();
448
+
449
+ const recentSessionId = 'session-vague';
450
+ const now = new Date().toISOString();
451
+ seedSession(recentSessionId, now, { withToolCalls: 3, withPain: true, outcome: 'failure' });
452
+
453
+ const overrideArtifact = {
454
+ artifactId: '22222222-3333-4444-aaaa-cccccccccccc',
455
+ sessionId: recentSessionId,
456
+ principleId: 'T-08',
457
+ sourceSnapshotRef: 'snapshot-vague',
458
+ badDecision: 'Made a bad decision',
459
+ betterDecision: 'Understand the error first',
460
+ rationale: 'Testing executability rejection for vague verbs',
461
+ createdAt: new Date().toISOString(),
462
+ };
463
+
464
+ const idleResult = makeIdleResult();
465
+ const result = executeNocturnalReflection(workspaceDir, stateDir, {
466
+ skipReflector: true,
467
+ reflectorOutputOverride: JSON.stringify(overrideArtifact),
468
+ idleCheckOverride: idleResult,
469
+ });
470
+
471
+ expect(result.success).toBe(false);
472
+ expect(result.validationFailed).toBe(true);
473
+ expect(result.validationFailures.some(f => f.includes('vague verb'))).toBe(true);
474
+ });
475
+ });
476
+
477
+ // -------------------------------------------------------------------------
478
+ // Tests: listApprovedNocturnalArtifacts
479
+ // -------------------------------------------------------------------------
480
+
481
+ describe('listApprovedNocturnalArtifacts', () => {
482
+ it('returns empty array when no artifacts exist', () => {
483
+ const artifacts = listApprovedNocturnalArtifacts(workspaceDir);
484
+ expect(artifacts).toHaveLength(0);
485
+ });
486
+
487
+ it('returns approved artifacts sorted by createdAt descending', () => {
488
+ // Create some sample artifacts
489
+ const samples = [
490
+ {
491
+ artifactId: 'older-artifact',
492
+ sessionId: 'session-1',
493
+ principleId: 'T-08',
494
+ sourceSnapshotRef: 'snap-1',
495
+ badDecision: 'Bad decision 1',
496
+ betterDecision: 'Better decision 1',
497
+ rationale: 'Rationale 1',
498
+ createdAt: '2026-03-27T10:00:00.000Z',
499
+ persistedAt: '2026-03-27T10:00:00.000Z',
500
+ status: 'approved',
501
+ },
502
+ {
503
+ artifactId: 'newer-artifact',
504
+ sessionId: 'session-2',
505
+ principleId: 'T-08',
506
+ sourceSnapshotRef: 'snap-2',
507
+ badDecision: 'Bad decision 2',
508
+ betterDecision: 'Better decision 2',
509
+ rationale: 'Rationale 2',
510
+ createdAt: '2026-03-27T12:00:00.000Z',
511
+ persistedAt: '2026-03-27T12:00:00.000Z',
512
+ status: 'approved',
513
+ },
514
+ {
515
+ artifactId: 'rejected-artifact',
516
+ sessionId: 'session-3',
517
+ principleId: 'T-08',
518
+ sourceSnapshotRef: 'snap-3',
519
+ badDecision: 'Bad decision 3',
520
+ betterDecision: 'Better decision 3',
521
+ rationale: 'Rationale 3',
522
+ createdAt: '2026-03-27T14:00:00.000Z',
523
+ persistedAt: '2026-03-27T14:00:00.000Z',
524
+ status: 'rejected', // Should be filtered out
525
+ },
526
+ ];
527
+
528
+ for (const sample of samples) {
529
+ const samplePath = NocturnalPathResolver.samplePath(workspaceDir, sample.artifactId);
530
+ const sampleDir = path.dirname(samplePath);
531
+ if (!fs.existsSync(sampleDir)) {
532
+ fs.mkdirSync(sampleDir, { recursive: true });
533
+ }
534
+ fs.writeFileSync(samplePath, JSON.stringify(sample), 'utf-8');
535
+ }
536
+
537
+ const artifacts = listApprovedNocturnalArtifacts(workspaceDir);
538
+ expect(artifacts).toHaveLength(2);
539
+ // Should be sorted by createdAt descending (newer first)
540
+ expect(artifacts[0].artifactId).toBe('newer-artifact');
541
+ expect(artifacts[1].artifactId).toBe('older-artifact');
542
+ });
543
+
544
+ it('skips malformed JSON files', () => {
545
+ const samplePath = NocturnalPathResolver.samplePath(workspaceDir, 'malformed');
546
+ const sampleDir = path.dirname(samplePath);
547
+ if (!fs.existsSync(sampleDir)) {
548
+ fs.mkdirSync(sampleDir, { recursive: true });
549
+ }
550
+ fs.writeFileSync(samplePath, 'not valid json {{{', 'utf-8');
551
+
552
+ const artifacts = listApprovedNocturnalArtifacts(workspaceDir);
553
+ expect(artifacts).toHaveLength(0);
554
+ });
555
+ });
556
+
557
+ // -------------------------------------------------------------------------
558
+ // Tests: executeNocturnalReflectionAsync
559
+ // -------------------------------------------------------------------------
560
+
561
+ describe('executeNocturnalReflectionAsync', () => {
562
+ it('returns a Promise that resolves to the same result as sync version', async () => {
563
+ forceIdleWorkspace();
564
+ seedPrinciples();
565
+ clearCooldownsSync();
566
+
567
+ const recentSessionId = 'session-async-test';
568
+ const now = new Date().toISOString();
569
+ seedSession(recentSessionId, now, { withToolCalls: 3, withPain: true, outcome: 'failure' });
570
+
571
+ const idleResult = makeIdleResult();
572
+ const result = await executeNocturnalReflectionAsync(workspaceDir, stateDir, { idleCheckOverride: idleResult });
573
+ expect(result.success).toBe(true);
574
+ expect(result.artifact).toBeDefined();
575
+ });
576
+ });
577
+ });