principles-disciple 1.8.0 → 1.8.2

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 (460) 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 +6 -1
  10. package/package.json +13 -15
  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} +185 -63
  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} +166 -139
  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} +263 -36
  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/src/service/subagent-workflow/empathy-observer-workflow-manager.ts +603 -0
  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/src/service/subagent-workflow/types.ts +378 -0
  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/zh/skills/pd-diagnostician/SKILL.md +70 -1
  138. package/templates/pain_settings.json +2 -1
  139. package/tests/README.md +120 -0
  140. package/tests/build-artifacts.test.ts +111 -0
  141. package/tests/commands/evolution-status.test.ts +222 -0
  142. package/tests/commands/evolver.test.ts +22 -0
  143. package/tests/commands/export.test.ts +78 -0
  144. package/tests/commands/nocturnal-review.test.ts +448 -0
  145. package/tests/commands/nocturnal-train.test.ts +97 -0
  146. package/tests/commands/pain.test.ts +108 -0
  147. package/tests/commands/samples.test.ts +65 -0
  148. package/tests/commands/strategy.test.ts +34 -0
  149. package/tests/commands/thinking-os.test.ts +88 -0
  150. package/tests/core/adaptive-thresholds.test.ts +261 -0
  151. package/tests/core/config-service.test.ts +89 -0
  152. package/tests/core/config.test.ts +90 -0
  153. package/tests/core/control-ui-db.test.ts +75 -0
  154. package/tests/core/core-template-guidance.test.ts +21 -0
  155. package/tests/core/detection-funnel.test.ts +63 -0
  156. package/tests/core/detection-service.test.ts +50 -0
  157. package/tests/core/dictionary-service.test.ts +116 -0
  158. package/tests/core/dictionary.test.ts +168 -0
  159. package/tests/core/empathy-keyword-matcher.test.ts +209 -0
  160. package/tests/core/event-log.test.ts +181 -0
  161. package/tests/core/evolution-e2e.test.ts +58 -0
  162. package/tests/core/evolution-engine-gate-integration.test.ts +543 -0
  163. package/tests/core/evolution-engine.test.ts +562 -0
  164. package/tests/core/evolution-logger.test.ts +148 -0
  165. package/tests/core/evolution-migration.test.ts +50 -0
  166. package/tests/core/evolution-paths.test.ts +21 -0
  167. package/tests/core/evolution-reducer.detector-metadata.test.ts +602 -0
  168. package/tests/core/evolution-reducer.test.ts +180 -0
  169. package/tests/core/evolution-types-loop.test.ts +48 -0
  170. package/tests/core/evolution-user-stories.e2e.test.ts +249 -0
  171. package/tests/core/external-training-contract.test.ts +463 -0
  172. package/tests/core/focus-history.test.ts +682 -0
  173. package/tests/core/init-flatten.test.ts +69 -0
  174. package/tests/core/init-refactor.test.ts +87 -0
  175. package/tests/core/init-v1.3.test.ts +46 -0
  176. package/tests/core/init.test.ts +190 -0
  177. package/tests/core/local-worker-routing.test.ts +757 -0
  178. package/tests/core/migration.test.ts +84 -0
  179. package/tests/core/model-deployment-registry.test.ts +845 -0
  180. package/tests/core/model-training-registry.test.ts +889 -0
  181. package/tests/core/nocturnal-arbiter.test.ts +494 -0
  182. package/tests/core/nocturnal-candidate-scoring.test.ts +400 -0
  183. package/tests/core/nocturnal-compliance.test.ts +646 -0
  184. package/tests/core/nocturnal-dataset.test.ts +892 -0
  185. package/tests/core/nocturnal-executability.test.ts +357 -0
  186. package/tests/core/nocturnal-export.test.ts +462 -0
  187. package/tests/core/nocturnal-reviewed-subset-comparison.test.ts +428 -0
  188. package/tests/core/nocturnal-trajectory-extractor.test.ts +634 -0
  189. package/tests/core/nocturnal-trinity.test.ts +953 -0
  190. package/tests/core/pain.test.ts +33 -0
  191. package/tests/core/path-resolver.test.ts +57 -0
  192. package/tests/core/paths-refactor.test.ts +42 -0
  193. package/tests/core/phase7-rollout-integration.test.ts +477 -0
  194. package/tests/core/principle-training-state.test.ts +712 -0
  195. package/tests/core/profile.test.ts +56 -0
  196. package/tests/core/promotion-gate.test.ts +556 -0
  197. package/tests/core/risk-calculator.test.ts +168 -0
  198. package/tests/core/session-tracker.test.ts +191 -0
  199. package/tests/core/training-program.test.ts +472 -0
  200. package/tests/core/trajectory.test.ts +265 -0
  201. package/tests/core/workspace-context-factory.test.ts +18 -0
  202. package/tests/core/workspace-context.test.ts +134 -0
  203. package/tests/fixtures/nocturnal-reviewed-subset.json +183 -0
  204. package/tests/fixtures/production-compatibility.test.ts +147 -0
  205. package/tests/fixtures/production-mock-generator.ts +282 -0
  206. package/tests/hooks/bash-risk-integration.test.ts +137 -0
  207. package/tests/hooks/bash-risk.test.ts +81 -0
  208. package/tests/hooks/edit-verification.test.ts +678 -0
  209. package/tests/hooks/gate-edit-verification-p1.test.ts +632 -0
  210. package/tests/hooks/gate-edit-verification.test.ts +435 -0
  211. package/tests/hooks/gate-pipeline-integration.test.ts +404 -0
  212. package/tests/hooks/gate.test.ts +271 -0
  213. package/tests/hooks/gfi-gate-unit.test.ts +422 -0
  214. package/tests/hooks/gfi-gate.test.ts +669 -0
  215. package/tests/hooks/lifecycle.test.ts +248 -0
  216. package/tests/hooks/llm.test.ts +308 -0
  217. package/tests/hooks/message-sanitize.test.ts +36 -0
  218. package/tests/hooks/pain.test.ts +141 -0
  219. package/tests/hooks/progressive-trust-gate.test.ts +277 -0
  220. package/tests/hooks/prompt.test.ts +1411 -0
  221. package/tests/hooks/subagent.test.ts +467 -0
  222. package/tests/hooks/thinking-gate.test.ts +313 -0
  223. package/tests/http/principles-console-route.test.ts +140 -0
  224. package/tests/hygiene-tracker.test.ts +77 -0
  225. package/tests/index.integration.test.ts +179 -0
  226. package/tests/index.shadow-routing.integration.test.ts +140 -0
  227. package/tests/index.test.ts +9 -0
  228. package/tests/integration/empathy-workflow-integration.test.ts +627 -0
  229. package/tests/service/control-ui-query-service.test.ts +121 -0
  230. package/tests/service/empathy-observer-workflow-manager.test.ts +176 -0
  231. package/tests/service/evolution-worker.test.ts +585 -0
  232. package/tests/service/nocturnal-runtime.test.ts +470 -0
  233. package/tests/service/nocturnal-service.test.ts +577 -0
  234. package/tests/service/nocturnal-target-selector.test.ts +615 -0
  235. package/tests/service/nocturnal-workflow-manager.test.ts +439 -0
  236. package/tests/service/phase3-input-filter.test.ts +289 -0
  237. package/tests/service/runtime-summary-service.test.ts +919 -0
  238. package/tests/task-compliance.test.ts +166 -0
  239. package/tests/test-utils.ts +48 -0
  240. package/tests/tools/critique-prompt.test.ts +260 -0
  241. package/tests/tools/deep-reflect.test.ts +232 -0
  242. package/tests/tools/model-index.test.ts +246 -0
  243. package/tests/ui/app.test.tsx +114 -0
  244. package/tests/utils/file-lock.test.ts +407 -0
  245. package/tests/utils/hashing.test.ts +32 -0
  246. package/tests/utils/io.test.ts +39 -0
  247. package/tests/utils/nlp.test.ts +53 -0
  248. package/tests/utils/plugin-logger.test.ts +156 -0
  249. package/tsconfig.json +16 -0
  250. package/tsconfig.tsbuildinfo +1 -0
  251. package/ui/src/App.tsx +45 -0
  252. package/ui/src/api.ts +216 -0
  253. package/ui/src/charts.tsx +586 -0
  254. package/ui/src/components/ErrorState.tsx +6 -0
  255. package/ui/src/components/Loading.tsx +13 -0
  256. package/ui/src/components/ProtectedRoute.tsx +12 -0
  257. package/ui/src/components/Shell.tsx +91 -0
  258. package/ui/src/components/WorkspaceConfig.tsx +146 -0
  259. package/ui/src/components/index.ts +5 -0
  260. package/ui/src/context/auth.tsx +80 -0
  261. package/ui/src/context/theme.tsx +66 -0
  262. package/ui/src/hooks/useAutoRefresh.ts +39 -0
  263. package/ui/src/i18n/ui.ts +363 -0
  264. package/ui/src/main.tsx +16 -0
  265. package/ui/src/pages/EvolutionPage.tsx +352 -0
  266. package/ui/src/pages/FeedbackPage.tsx +140 -0
  267. package/ui/src/pages/GateMonitorPage.tsx +136 -0
  268. package/ui/src/pages/LoginPage.tsx +88 -0
  269. package/ui/src/pages/OverviewPage.tsx +238 -0
  270. package/ui/src/pages/SamplesPage.tsx +174 -0
  271. package/ui/src/pages/ThinkingModelsPage.tsx +127 -0
  272. package/ui/src/styles.css +1661 -0
  273. package/ui/src/types.ts +368 -0
  274. package/ui/src/utils/format.ts +15 -0
  275. package/vitest.config.ts +23 -0
  276. package/dist/commands/capabilities.d.ts +0 -3
  277. package/dist/commands/capabilities.js +0 -73
  278. package/dist/commands/context.d.ts +0 -5
  279. package/dist/commands/evolution-status.d.ts +0 -4
  280. package/dist/commands/evolution-status.js +0 -117
  281. package/dist/commands/evolver.d.ts +0 -9
  282. package/dist/commands/evolver.js +0 -26
  283. package/dist/commands/export.d.ts +0 -2
  284. package/dist/commands/export.js +0 -98
  285. package/dist/commands/focus.d.ts +0 -14
  286. package/dist/commands/focus.js +0 -457
  287. package/dist/commands/nocturnal-review.d.ts +0 -24
  288. package/dist/commands/nocturnal-review.js +0 -265
  289. package/dist/commands/nocturnal-rollout.d.ts +0 -27
  290. package/dist/commands/nocturnal-rollout.js +0 -671
  291. package/dist/commands/nocturnal-train.d.ts +0 -25
  292. package/dist/commands/nocturnal-train.js +0 -919
  293. package/dist/commands/pain.d.ts +0 -5
  294. package/dist/commands/principle-rollback.d.ts +0 -4
  295. package/dist/commands/principle-rollback.js +0 -22
  296. package/dist/commands/rollback.d.ts +0 -19
  297. package/dist/commands/samples.d.ts +0 -2
  298. package/dist/commands/samples.js +0 -55
  299. package/dist/commands/strategy.d.ts +0 -3
  300. package/dist/commands/strategy.js +0 -29
  301. package/dist/commands/thinking-os.d.ts +0 -2
  302. package/dist/config/defaults/runtime.d.ts +0 -40
  303. package/dist/config/errors.d.ts +0 -84
  304. package/dist/config/errors.js +0 -94
  305. package/dist/config/index.js +0 -7
  306. package/dist/constants/diagnostician.d.ts +0 -12
  307. package/dist/constants/diagnostician.js +0 -56
  308. package/dist/constants/tools.d.ts +0 -17
  309. package/dist/constants/tools.js +0 -54
  310. package/dist/core/adaptive-thresholds.d.ts +0 -186
  311. package/dist/core/adaptive-thresholds.js +0 -300
  312. package/dist/core/config-service.d.ts +0 -15
  313. package/dist/core/config.d.ts +0 -127
  314. package/dist/core/control-ui-db.d.ts +0 -95
  315. package/dist/core/control-ui-db.js +0 -292
  316. package/dist/core/detection-funnel.d.ts +0 -33
  317. package/dist/core/detection-service.d.ts +0 -15
  318. package/dist/core/dictionary-service.d.ts +0 -15
  319. package/dist/core/dictionary.d.ts +0 -38
  320. package/dist/core/event-log.d.ts +0 -82
  321. package/dist/core/event-log.js +0 -463
  322. package/dist/core/evolution-engine.d.ts +0 -118
  323. package/dist/core/evolution-engine.js +0 -464
  324. package/dist/core/evolution-logger.d.ts +0 -137
  325. package/dist/core/evolution-logger.js +0 -256
  326. package/dist/core/evolution-migration.d.ts +0 -5
  327. package/dist/core/evolution-migration.js +0 -65
  328. package/dist/core/evolution-reducer.d.ts +0 -98
  329. package/dist/core/evolution-reducer.js +0 -465
  330. package/dist/core/evolution-types.d.ts +0 -287
  331. package/dist/core/evolution-types.js +0 -78
  332. package/dist/core/external-training-contract.d.ts +0 -276
  333. package/dist/core/external-training-contract.js +0 -269
  334. package/dist/core/focus-history.d.ts +0 -210
  335. package/dist/core/focus-history.js +0 -1185
  336. package/dist/core/hygiene/tracker.d.ts +0 -22
  337. package/dist/core/hygiene/tracker.js +0 -106
  338. package/dist/core/init.d.ts +0 -12
  339. package/dist/core/local-worker-routing.d.ts +0 -175
  340. package/dist/core/local-worker-routing.js +0 -525
  341. package/dist/core/migration.d.ts +0 -6
  342. package/dist/core/model-deployment-registry.d.ts +0 -218
  343. package/dist/core/model-deployment-registry.js +0 -503
  344. package/dist/core/model-training-registry.d.ts +0 -295
  345. package/dist/core/model-training-registry.js +0 -475
  346. package/dist/core/nocturnal-arbiter.d.ts +0 -159
  347. package/dist/core/nocturnal-arbiter.js +0 -534
  348. package/dist/core/nocturnal-candidate-scoring.d.ts +0 -137
  349. package/dist/core/nocturnal-candidate-scoring.js +0 -266
  350. package/dist/core/nocturnal-compliance.d.ts +0 -175
  351. package/dist/core/nocturnal-compliance.js +0 -824
  352. package/dist/core/nocturnal-dataset.d.ts +0 -224
  353. package/dist/core/nocturnal-dataset.js +0 -443
  354. package/dist/core/nocturnal-executability.d.ts +0 -85
  355. package/dist/core/nocturnal-executability.js +0 -331
  356. package/dist/core/nocturnal-export.d.ts +0 -124
  357. package/dist/core/nocturnal-export.js +0 -275
  358. package/dist/core/nocturnal-paths.d.ts +0 -124
  359. package/dist/core/nocturnal-trajectory-extractor.d.ts +0 -242
  360. package/dist/core/nocturnal-trajectory-extractor.js +0 -307
  361. package/dist/core/nocturnal-trinity.d.ts +0 -311
  362. package/dist/core/nocturnal-trinity.js +0 -880
  363. package/dist/core/pain.d.ts +0 -4
  364. package/dist/core/pain.js +0 -70
  365. package/dist/core/path-resolver.d.ts +0 -46
  366. package/dist/core/paths.d.ts +0 -65
  367. package/dist/core/principle-training-state.d.ts +0 -121
  368. package/dist/core/principle-training-state.js +0 -321
  369. package/dist/core/profile.d.ts +0 -62
  370. package/dist/core/profile.js +0 -210
  371. package/dist/core/promotion-gate.d.ts +0 -238
  372. package/dist/core/promotion-gate.js +0 -529
  373. package/dist/core/risk-calculator.d.ts +0 -22
  374. package/dist/core/session-tracker.d.ts +0 -99
  375. package/dist/core/shadow-observation-registry.d.ts +0 -217
  376. package/dist/core/shadow-observation-registry.js +0 -308
  377. package/dist/core/system-logger.d.ts +0 -8
  378. package/dist/core/thinking-models.d.ts +0 -38
  379. package/dist/core/thinking-models.js +0 -170
  380. package/dist/core/training-program.d.ts +0 -233
  381. package/dist/core/training-program.js +0 -433
  382. package/dist/core/trajectory.d.ts +0 -411
  383. package/dist/core/trajectory.js +0 -1307
  384. package/dist/core/workspace-context.d.ts +0 -71
  385. package/dist/hooks/bash-risk.d.ts +0 -57
  386. package/dist/hooks/bash-risk.js +0 -137
  387. package/dist/hooks/edit-verification.d.ts +0 -62
  388. package/dist/hooks/edit-verification.js +0 -256
  389. package/dist/hooks/gate-block-helper.d.ts +0 -44
  390. package/dist/hooks/gate-block-helper.js +0 -119
  391. package/dist/hooks/gate.d.ts +0 -24
  392. package/dist/hooks/gate.js +0 -173
  393. package/dist/hooks/gfi-gate.d.ts +0 -40
  394. package/dist/hooks/gfi-gate.js +0 -113
  395. package/dist/hooks/lifecycle.d.ts +0 -5
  396. package/dist/hooks/lifecycle.js +0 -284
  397. package/dist/hooks/llm.d.ts +0 -12
  398. package/dist/hooks/message-sanitize.d.ts +0 -3
  399. package/dist/hooks/message-sanitize.js +0 -37
  400. package/dist/hooks/pain.d.ts +0 -5
  401. package/dist/hooks/pain.js +0 -301
  402. package/dist/hooks/progressive-trust-gate.d.ts +0 -51
  403. package/dist/hooks/progressive-trust-gate.js +0 -89
  404. package/dist/hooks/prompt.d.ts +0 -47
  405. package/dist/hooks/prompt.js +0 -884
  406. package/dist/hooks/subagent.d.ts +0 -10
  407. package/dist/hooks/subagent.js +0 -387
  408. package/dist/hooks/thinking-checkpoint.d.ts +0 -37
  409. package/dist/hooks/thinking-checkpoint.js +0 -51
  410. package/dist/hooks/trajectory-collector.d.ts +0 -32
  411. package/dist/hooks/trajectory-collector.js +0 -256
  412. package/dist/http/principles-console-route.d.ts +0 -9
  413. package/dist/http/principles-console-route.js +0 -567
  414. package/dist/i18n/commands.d.ts +0 -26
  415. package/dist/i18n/commands.js +0 -116
  416. package/dist/index.d.ts +0 -7
  417. package/dist/index.js +0 -581
  418. package/dist/service/central-database.d.ts +0 -104
  419. package/dist/service/central-database.js +0 -649
  420. package/dist/service/control-ui-query-service.d.ts +0 -221
  421. package/dist/service/control-ui-query-service.js +0 -543
  422. package/dist/service/empathy-observer-manager.d.ts +0 -52
  423. package/dist/service/empathy-observer-manager.js +0 -229
  424. package/dist/service/evolution-query-service.d.ts +0 -155
  425. package/dist/service/evolution-query-service.js +0 -258
  426. package/dist/service/evolution-worker.d.ts +0 -101
  427. package/dist/service/evolution-worker.js +0 -974
  428. package/dist/service/nocturnal-runtime.d.ts +0 -183
  429. package/dist/service/nocturnal-service.d.ts +0 -163
  430. package/dist/service/nocturnal-service.js +0 -787
  431. package/dist/service/nocturnal-target-selector.d.ts +0 -145
  432. package/dist/service/nocturnal-target-selector.js +0 -315
  433. package/dist/service/phase3-input-filter.d.ts +0 -73
  434. package/dist/service/phase3-input-filter.js +0 -172
  435. package/dist/service/runtime-summary-service.d.ts +0 -122
  436. package/dist/service/runtime-summary-service.js +0 -485
  437. package/dist/service/trajectory-service.d.ts +0 -2
  438. package/dist/service/trajectory-service.js +0 -15
  439. package/dist/tools/critique-prompt.d.ts +0 -14
  440. package/dist/tools/deep-reflect.d.ts +0 -39
  441. package/dist/tools/deep-reflect.js +0 -350
  442. package/dist/tools/model-index.d.ts +0 -9
  443. package/dist/types/event-types.d.ts +0 -306
  444. package/dist/types/event-types.js +0 -106
  445. package/dist/types/hygiene-types.d.ts +0 -20
  446. package/dist/types/hygiene-types.js +0 -12
  447. package/dist/types/runtime-summary.d.ts +0 -47
  448. package/dist/types/runtime-summary.js +0 -1
  449. package/dist/types.d.ts +0 -50
  450. package/dist/types.js +0 -22
  451. package/dist/utils/file-lock.d.ts +0 -71
  452. package/dist/utils/file-lock.js +0 -309
  453. package/dist/utils/glob-match.d.ts +0 -28
  454. package/dist/utils/hashing.d.ts +0 -9
  455. package/dist/utils/io.d.ts +0 -6
  456. package/dist/utils/io.js +0 -106
  457. package/dist/utils/nlp.d.ts +0 -9
  458. package/dist/utils/plugin-logger.d.ts +0 -39
  459. package/dist/utils/subagent-probe.d.ts +0 -34
  460. package/dist/utils/subagent-probe.js +0 -81
@@ -0,0 +1,953 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import {
3
+ runTrinity,
4
+ runTrinityAsync,
5
+ validateDraftArtifact,
6
+ draftToArtifact,
7
+ DEFAULT_TRINITY_CONFIG,
8
+ type TrinityConfig,
9
+ type DreamerOutput,
10
+ type PhilosopherOutput,
11
+ type TrinityDraftArtifact,
12
+ type TrinityRuntimeAdapter,
13
+ } from '../../src/core/nocturnal-trinity.js';
14
+ import {
15
+ validateDreamerOutput,
16
+ validatePhilosopherOutput,
17
+ validateTrinityDraft,
18
+ } from '../../src/core/nocturnal-arbiter.js';
19
+
20
+ // ---------------------------------------------------------------------------
21
+ // Test Fixtures
22
+ // ---------------------------------------------------------------------------
23
+
24
+ function makeSnapshot(overrides: Partial<{
25
+ failureCount: number;
26
+ totalPainEvents: number;
27
+ totalGateBlocks: number;
28
+ }> = {}): {
29
+ sessionId: string;
30
+ stats: { failureCount: number; totalPainEvents: number; totalGateBlocks: number; totalAssistantTurns: number; totalToolCalls: number };
31
+ } {
32
+ return {
33
+ sessionId: 'session-test-123',
34
+ stats: {
35
+ failureCount: overrides.failureCount ?? 0,
36
+ totalPainEvents: overrides.totalPainEvents ?? 0,
37
+ totalGateBlocks: overrides.totalGateBlocks ?? 0,
38
+ totalAssistantTurns: 5,
39
+ totalToolCalls: 10,
40
+ },
41
+ };
42
+ }
43
+
44
+ // ---------------------------------------------------------------------------
45
+ // Tests: validateDreamerOutput
46
+ // ---------------------------------------------------------------------------
47
+
48
+ describe('validateDreamerOutput', () => {
49
+ it('passes a valid Dreamer output with candidates', () => {
50
+ const output = {
51
+ valid: true,
52
+ candidates: [
53
+ {
54
+ candidateIndex: 0,
55
+ badDecision: 'Did something wrong',
56
+ betterDecision: 'Do it right',
57
+ rationale: 'Because the principle says so',
58
+ confidence: 0.9,
59
+ },
60
+ ],
61
+ generatedAt: '2026-03-27T12:00:00.000Z',
62
+ };
63
+ const result = validateDreamerOutput(output);
64
+ expect(result.valid).toBe(true);
65
+ expect(result.failures).toHaveLength(0);
66
+ });
67
+
68
+ it('passes a valid Dreamer output with multiple candidates', () => {
69
+ const output = {
70
+ valid: true,
71
+ candidates: [
72
+ {
73
+ candidateIndex: 0,
74
+ badDecision: 'Did something wrong',
75
+ betterDecision: 'Do it right',
76
+ rationale: 'Because the principle says so',
77
+ confidence: 0.9,
78
+ },
79
+ {
80
+ candidateIndex: 1,
81
+ badDecision: 'Did another wrong thing',
82
+ betterDecision: 'Do it differently',
83
+ rationale: 'Alternative approach is better',
84
+ confidence: 0.8,
85
+ },
86
+ ],
87
+ generatedAt: '2026-03-27T12:00:00.000Z',
88
+ };
89
+ const result = validateDreamerOutput(output);
90
+ expect(result.valid).toBe(true);
91
+ expect(result.failures).toHaveLength(0);
92
+ });
93
+
94
+ it('rejects Dreamer output marked invalid', () => {
95
+ const output = {
96
+ valid: false,
97
+ candidates: [],
98
+ reason: 'No signal found',
99
+ generatedAt: '2026-03-27T12:00:00.000Z',
100
+ };
101
+ const result = validateDreamerOutput(output);
102
+ expect(result.valid).toBe(false);
103
+ expect(result.failures.some(f => f.includes('marked invalid'))).toBe(true);
104
+ });
105
+
106
+ it('rejects Dreamer output marked invalid without reason', () => {
107
+ const output = {
108
+ valid: false,
109
+ candidates: [],
110
+ generatedAt: '2026-03-27T12:00:00.000Z',
111
+ };
112
+ const result = validateDreamerOutput(output);
113
+ expect(result.valid).toBe(false);
114
+ });
115
+
116
+ it('rejects Dreamer output without candidates array', () => {
117
+ const output = {
118
+ valid: true,
119
+ generatedAt: '2026-03-27T12:00:00.000Z',
120
+ };
121
+ const result = validateDreamerOutput(output);
122
+ expect(result.valid).toBe(false);
123
+ expect(result.failures.some(f => f.includes('candidates array'))).toBe(true);
124
+ });
125
+
126
+ it('rejects Dreamer candidate missing required fields', () => {
127
+ const output = {
128
+ valid: true,
129
+ candidates: [
130
+ {
131
+ candidateIndex: 0,
132
+ badDecision: 'Has badDecision but missing betterDecision',
133
+ // missing: betterDecision, rationale, confidence
134
+ },
135
+ ],
136
+ generatedAt: '2026-03-27T12:00:00.000Z',
137
+ };
138
+ const result = validateDreamerOutput(output);
139
+ expect(result.valid).toBe(false);
140
+ expect(result.failures.some(f => f.includes('betterDecision'))).toBe(true);
141
+ expect(result.failures.some(f => f.includes('rationale'))).toBe(true);
142
+ expect(result.failures.some(f => f.includes('confidence'))).toBe(true);
143
+ });
144
+
145
+ it('rejects Dreamer candidate with invalid confidence (out of range)', () => {
146
+ const output = {
147
+ valid: true,
148
+ candidates: [
149
+ {
150
+ candidateIndex: 0,
151
+ badDecision: 'Wrong',
152
+ betterDecision: 'Right',
153
+ rationale: 'Because',
154
+ confidence: 1.5, // out of range
155
+ },
156
+ ],
157
+ generatedAt: '2026-03-27T12:00:00.000Z',
158
+ };
159
+ const result = validateDreamerOutput(output);
160
+ expect(result.valid).toBe(false);
161
+ expect(result.failures.some(f => f.includes('confidence'))).toBe(true);
162
+ });
163
+
164
+ it('rejects Dreamer candidate with duplicate candidateIndex', () => {
165
+ const output = {
166
+ valid: true,
167
+ candidates: [
168
+ {
169
+ candidateIndex: 0,
170
+ badDecision: 'Wrong 1',
171
+ betterDecision: 'Right 1',
172
+ rationale: 'Because 1',
173
+ confidence: 0.9,
174
+ },
175
+ {
176
+ candidateIndex: 0, // duplicate
177
+ badDecision: 'Wrong 2',
178
+ betterDecision: 'Right 2',
179
+ rationale: 'Because 2',
180
+ confidence: 0.8,
181
+ },
182
+ ],
183
+ generatedAt: '2026-03-27T12:00:00.000Z',
184
+ };
185
+ const result = validateDreamerOutput(output);
186
+ expect(result.valid).toBe(false);
187
+ expect(result.failures.some(f => f.includes('duplicate'))).toBe(true);
188
+ });
189
+
190
+ it('rejects Dreamer candidate with identical badDecision and betterDecision', () => {
191
+ const output = {
192
+ valid: true,
193
+ candidates: [
194
+ {
195
+ candidateIndex: 0,
196
+ badDecision: 'Do the same thing',
197
+ betterDecision: 'Do the same thing', // identical
198
+ rationale: 'Because it is correct',
199
+ confidence: 0.9,
200
+ },
201
+ ],
202
+ generatedAt: '2026-03-27T12:00:00.000Z',
203
+ };
204
+ const result = validateDreamerOutput(output);
205
+ expect(result.valid).toBe(false);
206
+ expect(result.failures.some(f => f.includes('identical'))).toBe(true);
207
+ });
208
+
209
+ it('rejects Dreamer output missing generatedAt', () => {
210
+ const output = {
211
+ valid: true,
212
+ candidates: [
213
+ {
214
+ candidateIndex: 0,
215
+ badDecision: 'Wrong',
216
+ betterDecision: 'Right',
217
+ rationale: 'Because',
218
+ confidence: 0.9,
219
+ },
220
+ ],
221
+ // missing generatedAt
222
+ };
223
+ const result = validateDreamerOutput(output);
224
+ expect(result.valid).toBe(false);
225
+ expect(result.failures.some(f => f.includes('generatedAt'))).toBe(true);
226
+ });
227
+
228
+ it('rejects non-object input', () => {
229
+ const result = validateDreamerOutput(null);
230
+ expect(result.valid).toBe(false);
231
+ });
232
+
233
+ it('rejects string input', () => {
234
+ const result = validateDreamerOutput('not an object');
235
+ expect(result.valid).toBe(false);
236
+ });
237
+ });
238
+
239
+ // ---------------------------------------------------------------------------
240
+ // Tests: validatePhilosopherOutput
241
+ // ---------------------------------------------------------------------------
242
+
243
+ describe('validatePhilosopherOutput', () => {
244
+ it('passes a valid Philosopher output', () => {
245
+ const output = {
246
+ valid: true,
247
+ judgments: [
248
+ {
249
+ candidateIndex: 0,
250
+ critique: 'Strong alignment',
251
+ principleAligned: true,
252
+ score: 0.92,
253
+ rank: 1,
254
+ },
255
+ ],
256
+ overallAssessment: 'Good candidate set',
257
+ generatedAt: '2026-03-27T12:00:00.000Z',
258
+ };
259
+ const result = validatePhilosopherOutput(output);
260
+ expect(result.valid).toBe(true);
261
+ expect(result.failures).toHaveLength(0);
262
+ });
263
+
264
+ it('rejects Philosopher output marked invalid', () => {
265
+ const output = {
266
+ valid: false,
267
+ judgments: [],
268
+ reason: 'No candidates to judge',
269
+ generatedAt: '2026-03-27T12:00:00.000Z',
270
+ };
271
+ const result = validatePhilosopherOutput(output);
272
+ expect(result.valid).toBe(false);
273
+ });
274
+
275
+ it('rejects Philosopher output without judgments array', () => {
276
+ const output = {
277
+ valid: true,
278
+ overallAssessment: 'Good',
279
+ generatedAt: '2026-03-27T12:00:00.000Z',
280
+ };
281
+ const result = validatePhilosopherOutput(output);
282
+ expect(result.valid).toBe(false);
283
+ expect(result.failures.some(f => f.includes('judgments array'))).toBe(true);
284
+ });
285
+
286
+ it('rejects Philosopher judgment missing required fields', () => {
287
+ const output = {
288
+ valid: true,
289
+ judgments: [
290
+ {
291
+ candidateIndex: 0,
292
+ // missing: critique, principleAligned, score, rank
293
+ },
294
+ ],
295
+ overallAssessment: 'Good',
296
+ generatedAt: '2026-03-27T12:00:00.000Z',
297
+ };
298
+ const result = validatePhilosopherOutput(output);
299
+ expect(result.valid).toBe(false);
300
+ });
301
+
302
+ it('rejects Philosopher judgment with invalid score (out of range)', () => {
303
+ const output = {
304
+ valid: true,
305
+ judgments: [
306
+ {
307
+ candidateIndex: 0,
308
+ critique: 'Good',
309
+ principleAligned: true,
310
+ score: 1.5, // out of range
311
+ rank: 1,
312
+ },
313
+ ],
314
+ overallAssessment: 'Good',
315
+ generatedAt: '2026-03-27T12:00:00.000Z',
316
+ };
317
+ const result = validatePhilosopherOutput(output);
318
+ expect(result.valid).toBe(false);
319
+ expect(result.failures.some(f => f.includes('score'))).toBe(true);
320
+ });
321
+
322
+ it('rejects Philosopher judgment with invalid rank (must be >= 1)', () => {
323
+ const output = {
324
+ valid: true,
325
+ judgments: [
326
+ {
327
+ candidateIndex: 0,
328
+ critique: 'Good',
329
+ principleAligned: true,
330
+ score: 0.9,
331
+ rank: 0, // invalid
332
+ },
333
+ ],
334
+ overallAssessment: 'Good',
335
+ generatedAt: '2026-03-27T12:00:00.000Z',
336
+ };
337
+ const result = validatePhilosopherOutput(output);
338
+ expect(result.valid).toBe(false);
339
+ expect(result.failures.some(f => f.includes('rank'))).toBe(true);
340
+ });
341
+
342
+ it('rejects Philosopher output with non-sequential ranks', () => {
343
+ const output = {
344
+ valid: true,
345
+ judgments: [
346
+ {
347
+ candidateIndex: 0,
348
+ critique: 'Good',
349
+ principleAligned: true,
350
+ score: 0.9,
351
+ rank: 1,
352
+ },
353
+ {
354
+ candidateIndex: 1,
355
+ critique: 'Also good',
356
+ principleAligned: true,
357
+ score: 0.8,
358
+ rank: 3, // should be 2
359
+ },
360
+ ],
361
+ overallAssessment: 'Good',
362
+ generatedAt: '2026-03-27T12:00:00.000Z',
363
+ };
364
+ const result = validatePhilosopherOutput(output);
365
+ expect(result.valid).toBe(false);
366
+ expect(result.failures.some(f => f.includes('sequential ranks'))).toBe(true);
367
+ });
368
+
369
+ it('rejects Philosopher output missing overallAssessment', () => {
370
+ const output = {
371
+ valid: true,
372
+ judgments: [
373
+ {
374
+ candidateIndex: 0,
375
+ critique: 'Good',
376
+ principleAligned: true,
377
+ score: 0.9,
378
+ rank: 1,
379
+ },
380
+ ],
381
+ // missing overallAssessment
382
+ generatedAt: '2026-03-27T12:00:00.000Z',
383
+ };
384
+ const result = validatePhilosopherOutput(output);
385
+ expect(result.valid).toBe(false);
386
+ expect(result.failures.some(f => f.includes('overallAssessment'))).toBe(true);
387
+ });
388
+ });
389
+
390
+ // ---------------------------------------------------------------------------
391
+ // Tests: validateTrinityDraft
392
+ // ---------------------------------------------------------------------------
393
+
394
+ describe('validateTrinityDraft', () => {
395
+ function makeValidDraft(overrides: Record<string, unknown> = {}): Record<string, unknown> {
396
+ return {
397
+ selectedCandidateIndex: 0,
398
+ badDecision: 'Did something wrong',
399
+ betterDecision: 'Do it right',
400
+ rationale: 'Because the principle says so and this is the right approach',
401
+ sessionId: 'session-test-123',
402
+ principleId: 'T-01',
403
+ sourceSnapshotRef: 'snapshot-test-001',
404
+ telemetry: {
405
+ chainMode: 'trinity',
406
+ dreamerPassed: true,
407
+ philosopherPassed: true,
408
+ scribePassed: true,
409
+ candidateCount: 3,
410
+ selectedCandidateIndex: 0,
411
+ stageFailures: [],
412
+ },
413
+ ...overrides,
414
+ };
415
+ }
416
+
417
+ it('passes a valid Trinity draft artifact', () => {
418
+ const draft = makeValidDraft();
419
+ const result = validateTrinityDraft(draft);
420
+ expect(result.valid).toBe(true);
421
+ expect(result.failures).toHaveLength(0);
422
+ });
423
+
424
+ it('rejects draft with missing badDecision', () => {
425
+ const draft = makeValidDraft();
426
+ delete draft.badDecision;
427
+ const result = validateTrinityDraft(draft);
428
+ expect(result.valid).toBe(false);
429
+ expect(result.failures.some(f => f.includes('badDecision'))).toBe(true);
430
+ });
431
+
432
+ it('rejects draft with empty badDecision', () => {
433
+ const draft = makeValidDraft({ badDecision: ' ' });
434
+ const result = validateTrinityDraft(draft);
435
+ expect(result.valid).toBe(false);
436
+ expect(result.failures.some(f => f.includes('badDecision'))).toBe(true);
437
+ });
438
+
439
+ it('rejects draft with short rationale (< 20 chars)', () => {
440
+ const draft = makeValidDraft({ rationale: 'Too short' });
441
+ const result = validateTrinityDraft(draft);
442
+ expect(result.valid).toBe(false);
443
+ expect(result.failures.some(f => f.includes('rationale'))).toBe(true);
444
+ });
445
+
446
+ it('rejects draft with identical badDecision and betterDecision', () => {
447
+ const draft = makeValidDraft({
448
+ badDecision: 'Same thing',
449
+ betterDecision: 'Same thing',
450
+ });
451
+ const result = validateTrinityDraft(draft);
452
+ expect(result.valid).toBe(false);
453
+ expect(result.failures.some(f => f.includes('identical'))).toBe(true);
454
+ });
455
+
456
+ it('rejects draft with invalid telemetry', () => {
457
+ const draft = makeValidDraft({ telemetry: null });
458
+ const result = validateTrinityDraft(draft);
459
+ expect(result.valid).toBe(false);
460
+ expect(result.failures.some(f => f.includes('telemetry'))).toBe(true);
461
+ });
462
+
463
+ it('rejects draft with invalid chainMode in telemetry', () => {
464
+ const draft = makeValidDraft({
465
+ telemetry: {
466
+ chainMode: 'invalid-mode', // must be 'trinity' or 'single-reflector'
467
+ dreamerPassed: true,
468
+ philosopherPassed: true,
469
+ scribePassed: true,
470
+ candidateCount: 3,
471
+ selectedCandidateIndex: 0,
472
+ stageFailures: [],
473
+ },
474
+ });
475
+ const result = validateTrinityDraft(draft);
476
+ expect(result.valid).toBe(false);
477
+ expect(result.failures.some(f => f.includes('chainMode'))).toBe(true);
478
+ });
479
+ });
480
+
481
+ // ---------------------------------------------------------------------------
482
+ // Tests: runTrinity — successful path
483
+ // ---------------------------------------------------------------------------
484
+
485
+ describe('runTrinity', () => {
486
+ it('produces a successful Trinity result with valid snapshot (failure signal)', () => {
487
+ const snapshot = makeSnapshot({ failureCount: 2 });
488
+ const config: TrinityConfig = {
489
+ useTrinity: true,
490
+ maxCandidates: 3,
491
+ useStubs: true, // Use stub implementations
492
+ };
493
+
494
+ const result = runTrinity({ snapshot, principleId: 'T-08', config });
495
+
496
+ expect(result.success).toBe(true);
497
+ expect(result.artifact).toBeDefined();
498
+ expect(result.telemetry.chainMode).toBe('trinity');
499
+ expect(result.telemetry.dreamerPassed).toBe(true);
500
+ expect(result.telemetry.philosopherPassed).toBe(true);
501
+ expect(result.telemetry.scribePassed).toBe(true);
502
+ expect(result.telemetry.candidateCount).toBeGreaterThan(0);
503
+ expect(result.telemetry.selectedCandidateIndex).toBeGreaterThanOrEqual(0);
504
+ expect(result.failures).toHaveLength(0);
505
+ expect(result.fallbackOccurred).toBe(false);
506
+ });
507
+
508
+ it('produces a successful Trinity result with pain signal', () => {
509
+ const snapshot = makeSnapshot({ totalPainEvents: 3 });
510
+ const config: TrinityConfig = {
511
+ useTrinity: true,
512
+ maxCandidates: 3,
513
+ useStubs: true,
514
+ };
515
+
516
+ const result = runTrinity({ snapshot, principleId: 'T-08', config });
517
+
518
+ expect(result.success).toBe(true);
519
+ expect(result.artifact).toBeDefined();
520
+ });
521
+
522
+ it('produces a successful Trinity result with gate block signal', () => {
523
+ const snapshot = makeSnapshot({ totalGateBlocks: 1 });
524
+ const config: TrinityConfig = {
525
+ useTrinity: true,
526
+ maxCandidates: 3,
527
+ useStubs: true,
528
+ };
529
+
530
+ const result = runTrinity({ snapshot, principleId: 'T-03', config });
531
+
532
+ expect(result.success).toBe(true);
533
+ expect(result.artifact).toBeDefined();
534
+ });
535
+
536
+ it('respects maxCandidates config', () => {
537
+ const snapshot = makeSnapshot({ failureCount: 5 });
538
+ const config: TrinityConfig = {
539
+ useTrinity: true,
540
+ maxCandidates: 2,
541
+ useStubs: true,
542
+ };
543
+
544
+ const result = runTrinity({ snapshot, principleId: 'T-08', config });
545
+
546
+ expect(result.success).toBe(true);
547
+ expect(result.telemetry.candidateCount).toBeLessThanOrEqual(2);
548
+ });
549
+ });
550
+
551
+ // ---------------------------------------------------------------------------
552
+ // Tests: runTrinity — failure paths
553
+ // ---------------------------------------------------------------------------
554
+
555
+ describe('runTrinity — failure paths', () => {
556
+ it('fails when snapshot has no signal and generates no candidates', () => {
557
+ // Snapshot with all zero stats - stub will fail to generate candidates
558
+ const snapshot = makeSnapshot({
559
+ failureCount: 0,
560
+ totalPainEvents: 0,
561
+ totalGateBlocks: 0,
562
+ });
563
+ const config: TrinityConfig = {
564
+ useTrinity: true,
565
+ maxCandidates: 3,
566
+ useStubs: true,
567
+ };
568
+
569
+ const result = runTrinity({ snapshot, principleId: 'T-08', config });
570
+
571
+ expect(result.success).toBe(false);
572
+ expect(result.failures.length).toBeGreaterThan(0);
573
+ expect(result.failures[0].stage).toBe('dreamer');
574
+ expect(result.telemetry.dreamerPassed).toBe(false);
575
+ });
576
+ });
577
+
578
+ // ---------------------------------------------------------------------------
579
+ // Tests: validateDraftArtifact
580
+ // ---------------------------------------------------------------------------
581
+
582
+ describe('validateDraftArtifact', () => {
583
+ function makeValidArtifact(): TrinityDraftArtifact {
584
+ return {
585
+ selectedCandidateIndex: 0,
586
+ badDecision: 'Did something wrong',
587
+ betterDecision: 'Do it right',
588
+ rationale: 'Because the principle says so and this is the correct approach',
589
+ sessionId: 'session-test-123',
590
+ principleId: 'T-01',
591
+ sourceSnapshotRef: 'snapshot-test-001',
592
+ telemetry: {
593
+ chainMode: 'trinity',
594
+ dreamerPassed: true,
595
+ philosopherPassed: true,
596
+ scribePassed: true,
597
+ candidateCount: 3,
598
+ selectedCandidateIndex: 0,
599
+ stageFailures: [],
600
+ },
601
+ };
602
+ }
603
+
604
+ it('passes a valid TrinityDraftArtifact', () => {
605
+ const artifact = makeValidArtifact();
606
+ const result = validateDraftArtifact(artifact);
607
+ expect(result.valid).toBe(true);
608
+ expect(result.failures).toHaveLength(0);
609
+ });
610
+
611
+ it('rejects artifact with missing badDecision', () => {
612
+ const artifact = makeValidArtifact();
613
+ delete (artifact as Record<string, unknown>).badDecision;
614
+ const result = validateDraftArtifact(artifact);
615
+ expect(result.valid).toBe(false);
616
+ });
617
+
618
+ it('rejects artifact with empty betterDecision', () => {
619
+ const artifact = makeValidArtifact();
620
+ artifact.betterDecision = ' ';
621
+ const result = validateDraftArtifact(artifact);
622
+ expect(result.valid).toBe(false);
623
+ });
624
+
625
+ it('rejects artifact with short rationale', () => {
626
+ const artifact = makeValidArtifact();
627
+ artifact.rationale = 'Too short';
628
+ const result = validateDraftArtifact(artifact);
629
+ expect(result.valid).toBe(false);
630
+ });
631
+
632
+ it('rejects artifact with identical badDecision and betterDecision', () => {
633
+ const artifact = makeValidArtifact();
634
+ artifact.badDecision = 'Same';
635
+ artifact.betterDecision = 'Same';
636
+ const result = validateDraftArtifact(artifact);
637
+ expect(result.valid).toBe(false);
638
+ expect(result.failures.some(f => f.includes('identical'))).toBe(true);
639
+ });
640
+ });
641
+
642
+ // ---------------------------------------------------------------------------
643
+ // Tests: draftToArtifact
644
+ // ---------------------------------------------------------------------------
645
+
646
+ describe('draftToArtifact', () => {
647
+ it('converts TrinityDraftArtifact to NocturnalArtifact-compatible structure', () => {
648
+ const draft: TrinityDraftArtifact = {
649
+ selectedCandidateIndex: 1,
650
+ badDecision: 'Did something wrong',
651
+ betterDecision: 'Do it right',
652
+ rationale: 'Because the principle says so',
653
+ sessionId: 'session-test-123',
654
+ principleId: 'T-01',
655
+ sourceSnapshotRef: 'snapshot-test-001',
656
+ telemetry: {
657
+ chainMode: 'trinity',
658
+ dreamerPassed: true,
659
+ philosopherPassed: true,
660
+ scribePassed: true,
661
+ candidateCount: 3,
662
+ selectedCandidateIndex: 1,
663
+ stageFailures: [],
664
+ },
665
+ };
666
+
667
+ const artifact = draftToArtifact(draft);
668
+
669
+ expect(artifact.artifactId).toBeDefined(); // Generated UUID
670
+ expect(artifact.sessionId).toBe('session-test-123');
671
+ expect(artifact.principleId).toBe('T-01');
672
+ expect(artifact.badDecision).toBe('Did something wrong');
673
+ expect(artifact.betterDecision).toBe('Do it right');
674
+ expect(artifact.rationale).toBe('Because the principle says so');
675
+ expect(artifact.sourceSnapshotRef).toBe('snapshot-test-001');
676
+ expect(artifact.createdAt).toBeDefined(); // Current timestamp
677
+ });
678
+ });
679
+
680
+ // ---------------------------------------------------------------------------
681
+ // Tests: DEFAULT_TRINITY_CONFIG
682
+ // ---------------------------------------------------------------------------
683
+
684
+ describe('DEFAULT_TRINITY_CONFIG', () => {
685
+ it('has sensible defaults', () => {
686
+ expect(DEFAULT_TRINITY_CONFIG.useTrinity).toBe(true);
687
+ expect(DEFAULT_TRINITY_CONFIG.maxCandidates).toBe(3);
688
+ expect(DEFAULT_TRINITY_CONFIG.useStubs).toBe(false); // real subagent execution is now the default
689
+ });
690
+ });
691
+
692
+ // ---------------------------------------------------------------------------
693
+ // Tests: runTrinity — useStubs=false without adapter (sync failure)
694
+ // ---------------------------------------------------------------------------
695
+
696
+ describe('runTrinity — useStubs=false without adapter', () => {
697
+ it('fails with clear error when useStubs=false but no runtimeAdapter provided', () => {
698
+ const snapshot = makeSnapshot({ failureCount: 2 });
699
+ const config: TrinityConfig = {
700
+ useTrinity: true,
701
+ maxCandidates: 3,
702
+ useStubs: false, // No adapter provided!
703
+ };
704
+
705
+ const result = runTrinity({ snapshot, principleId: 'T-08', config });
706
+
707
+ expect(result.success).toBe(false);
708
+ expect(result.failures.length).toBeGreaterThan(0);
709
+ expect(result.failures[0].stage).toBe('dreamer');
710
+ expect(result.failures[0].reason).toContain('runtimeAdapter');
711
+ expect(result.telemetry.usedStubs).toBe(false);
712
+ expect(result.telemetry.dreamerPassed).toBe(false);
713
+ });
714
+ });
715
+
716
+ // ---------------------------------------------------------------------------
717
+ // Tests: runTrinityAsync — with mock runtime adapter
718
+ // ---------------------------------------------------------------------------
719
+
720
+ describe('runTrinityAsync — with mock runtime adapter', () => {
721
+ function makeMockAdapter(overrides: Partial<{
722
+ dreamerOutput: DreamerOutput;
723
+ philosopherOutput: PhilosopherOutput;
724
+ scribeArtifact: TrinityDraftArtifact | null;
725
+ closeCalled: boolean;
726
+ }> = {}): TrinityRuntimeAdapter & { closeCalled: boolean } {
727
+ const defaultDreamerOutput: DreamerOutput = {
728
+ valid: true,
729
+ candidates: [
730
+ {
731
+ candidateIndex: 0,
732
+ badDecision: 'Did something wrong',
733
+ betterDecision: 'Do it right',
734
+ rationale: 'Because the principle says so',
735
+ confidence: 0.9,
736
+ },
737
+ ],
738
+ generatedAt: new Date().toISOString(),
739
+ };
740
+
741
+ const defaultPhilosopherOutput: PhilosopherOutput = {
742
+ valid: true,
743
+ judgments: [
744
+ {
745
+ candidateIndex: 0,
746
+ critique: 'Good alignment',
747
+ principleAligned: true,
748
+ score: 0.92,
749
+ rank: 1,
750
+ },
751
+ ],
752
+ overallAssessment: 'Good candidate',
753
+ generatedAt: new Date().toISOString(),
754
+ };
755
+
756
+ const defaultScribeArtifact: TrinityDraftArtifact = {
757
+ selectedCandidateIndex: 0,
758
+ badDecision: 'Did something wrong',
759
+ betterDecision: 'Do it right',
760
+ rationale: 'Because the principle says so and this is the right approach',
761
+ sessionId: 'session-test-123',
762
+ principleId: 'T-01',
763
+ sourceSnapshotRef: 'snapshot-test-001',
764
+ telemetry: {
765
+ chainMode: 'trinity',
766
+ usedStubs: false,
767
+ dreamerPassed: true,
768
+ philosopherPassed: true,
769
+ scribePassed: true,
770
+ candidateCount: 1,
771
+ selectedCandidateIndex: 0,
772
+ stageFailures: [],
773
+ },
774
+ };
775
+
776
+ return {
777
+ closeCalled: overrides.closeCalled ?? false,
778
+ invokeDreamer: vi.fn().mockResolvedValue(overrides.dreamerOutput ?? defaultDreamerOutput),
779
+ invokePhilosopher: vi.fn().mockResolvedValue(overrides.philosopherOutput ?? defaultPhilosopherOutput),
780
+ invokeScribe: vi.fn().mockResolvedValue(
781
+ overrides.scribeArtifact === null ? null : (overrides.scribeArtifact ?? defaultScribeArtifact)
782
+ ),
783
+ close: vi.fn().mockResolvedValue(undefined),
784
+ } as unknown as TrinityRuntimeAdapter & { closeCalled: boolean; invokeDreamer: ReturnType<typeof vi.fn>; invokePhilosopher: ReturnType<typeof vi.fn>; invokeScribe: ReturnType<typeof vi.fn> };
785
+ }
786
+
787
+ it('uses runtime adapter when useStubs=false with adapter provided', async () => {
788
+ const snapshot = makeSnapshot({ failureCount: 2 });
789
+ const adapter = makeMockAdapter();
790
+ const config: TrinityConfig = {
791
+ useTrinity: true,
792
+ maxCandidates: 3,
793
+ useStubs: false,
794
+ runtimeAdapter: adapter,
795
+ };
796
+
797
+ const result = await runTrinityAsync({ snapshot, principleId: 'T-08', config });
798
+
799
+ expect(result.success).toBe(true);
800
+ expect(adapter.invokeDreamer).toHaveBeenCalledWith(snapshot, 'T-08', 3);
801
+ expect(adapter.invokePhilosopher).toHaveBeenCalled();
802
+ expect(adapter.invokeScribe).toHaveBeenCalled();
803
+ expect(result.telemetry.usedStubs).toBe(false);
804
+ expect(result.telemetry.dreamerPassed).toBe(true);
805
+ expect(result.telemetry.philosopherPassed).toBe(true);
806
+ expect(result.telemetry.scribePassed).toBe(true);
807
+ });
808
+
809
+ it('fails closed when Dreamer stage returns invalid output', async () => {
810
+ const snapshot = makeSnapshot({ failureCount: 2 });
811
+ const adapter = makeMockAdapter({
812
+ dreamerOutput: { valid: false, candidates: [], reason: 'No signal found', generatedAt: new Date().toISOString() },
813
+ });
814
+ const config: TrinityConfig = {
815
+ useTrinity: true,
816
+ maxCandidates: 3,
817
+ useStubs: false,
818
+ runtimeAdapter: adapter,
819
+ };
820
+
821
+ const result = await runTrinityAsync({ snapshot, principleId: 'T-08', config });
822
+
823
+ expect(result.success).toBe(false);
824
+ expect(result.failures.length).toBeGreaterThan(0);
825
+ expect(result.failures[0].stage).toBe('dreamer');
826
+ expect(result.telemetry.dreamerPassed).toBe(false);
827
+ expect(result.telemetry.philosopherPassed).toBe(false);
828
+ expect(result.telemetry.scribePassed).toBe(false);
829
+ });
830
+
831
+ it('fails closed when Philosopher stage returns invalid output', async () => {
832
+ const snapshot = makeSnapshot({ failureCount: 2 });
833
+ const adapter = makeMockAdapter({
834
+ philosopherOutput: { valid: false, judgments: [], overallAssessment: '', reason: 'No candidates', generatedAt: new Date().toISOString() },
835
+ });
836
+ const config: TrinityConfig = {
837
+ useTrinity: true,
838
+ maxCandidates: 3,
839
+ useStubs: false,
840
+ runtimeAdapter: adapter,
841
+ };
842
+
843
+ const result = await runTrinityAsync({ snapshot, principleId: 'T-08', config });
844
+
845
+ expect(result.success).toBe(false);
846
+ expect(result.failures.some(f => f.stage === 'dreamer')).toBe(false); // Dreamer passed
847
+ expect(result.failures.some(f => f.stage === 'philosopher')).toBe(true);
848
+ expect(result.telemetry.dreamerPassed).toBe(true);
849
+ expect(result.telemetry.philosopherPassed).toBe(false);
850
+ });
851
+
852
+ it('fails closed when Scribe stage returns null', async () => {
853
+ const snapshot = makeSnapshot({ failureCount: 2 });
854
+ const adapter = makeMockAdapter({ scribeArtifact: null });
855
+ const config: TrinityConfig = {
856
+ useTrinity: true,
857
+ maxCandidates: 3,
858
+ useStubs: false,
859
+ runtimeAdapter: adapter,
860
+ };
861
+
862
+ const result = await runTrinityAsync({ snapshot, principleId: 'T-08', config });
863
+
864
+ expect(result.success).toBe(false);
865
+ expect(result.failures.some(f => f.stage === 'scribe')).toBe(true);
866
+ expect(result.telemetry.dreamerPassed).toBe(true);
867
+ expect(result.telemetry.philosopherPassed).toBe(true);
868
+ expect(result.telemetry.scribePassed).toBe(false);
869
+ });
870
+
871
+ it('calls adapter.close() after successful execution', async () => {
872
+ const snapshot = makeSnapshot({ failureCount: 2 });
873
+ const adapter = makeMockAdapter();
874
+ const config: TrinityConfig = {
875
+ useTrinity: true,
876
+ maxCandidates: 3,
877
+ useStubs: false,
878
+ runtimeAdapter: adapter,
879
+ };
880
+
881
+ await runTrinityAsync({ snapshot, principleId: 'T-08', config });
882
+
883
+ expect(adapter.close).toHaveBeenCalled();
884
+ });
885
+
886
+ it('calls adapter.close() even when execution fails', async () => {
887
+ const snapshot = makeSnapshot({ failureCount: 2 });
888
+ const adapter = makeMockAdapter({
889
+ dreamerOutput: { valid: false, candidates: [], reason: 'No signal', generatedAt: new Date().toISOString() },
890
+ });
891
+ const config: TrinityConfig = {
892
+ useTrinity: true,
893
+ maxCandidates: 3,
894
+ useStubs: false,
895
+ runtimeAdapter: adapter,
896
+ };
897
+
898
+ await runTrinityAsync({ snapshot, principleId: 'T-08', config });
899
+
900
+ expect(adapter.close).toHaveBeenCalled();
901
+ });
902
+
903
+ it('produces artifact compatible with draftToArtifact', async () => {
904
+ const snapshot = makeSnapshot({ failureCount: 2 });
905
+ const adapter = makeMockAdapter();
906
+ const config: TrinityConfig = {
907
+ useTrinity: true,
908
+ maxCandidates: 3,
909
+ useStubs: false,
910
+ runtimeAdapter: adapter,
911
+ };
912
+
913
+ const result = await runTrinityAsync({ snapshot, principleId: 'T-08', config });
914
+
915
+ expect(result.success).toBe(true);
916
+ expect(result.artifact).toBeDefined();
917
+ const artifact = draftToArtifact(result.artifact!);
918
+ expect(artifact.artifactId).toBeDefined();
919
+ expect(artifact.sessionId).toBe('session-test-123');
920
+ expect(artifact.principleId).toBe('T-01');
921
+ expect(artifact.badDecision).toBeDefined();
922
+ expect(artifact.betterDecision).toBeDefined();
923
+ });
924
+ });
925
+
926
+ // ---------------------------------------------------------------------------
927
+ // Tests: runTrinityAsync — useStubs=true still uses stubs
928
+ // ---------------------------------------------------------------------------
929
+
930
+ describe('runTrinityAsync — useStubs=true uses synchronous stubs', () => {
931
+ it('still uses stub implementations when useStubs=true even with adapter', async () => {
932
+ const snapshot = makeSnapshot({ failureCount: 2 });
933
+ const adapter = {
934
+ invokeDreamer: vi.fn().mockResolvedValue({ valid: true, candidates: [], generatedAt: new Date().toISOString() }),
935
+ invokePhilosopher: vi.fn().mockResolvedValue({ valid: true, judgments: [], overallAssessment: '', generatedAt: new Date().toISOString() }),
936
+ invokeScribe: vi.fn().mockResolvedValue(null),
937
+ };
938
+ const config: TrinityConfig = {
939
+ useTrinity: true,
940
+ maxCandidates: 3,
941
+ useStubs: true, // Explicitly use stubs
942
+ runtimeAdapter: adapter as unknown as TrinityRuntimeAdapter,
943
+ };
944
+
945
+ // With stubs, adapter is ignored - stub produces success with failureCount signal
946
+ const result = await runTrinityAsync({ snapshot, principleId: 'T-08', config });
947
+
948
+ expect(result.success).toBe(true); // Stub succeeds because snapshot has failureCount
949
+ expect(adapter.invokeDreamer).not.toHaveBeenCalled(); // Adapter NOT called
950
+ expect(adapter.invokePhilosopher).not.toHaveBeenCalled();
951
+ expect(adapter.invokeScribe).not.toHaveBeenCalled();
952
+ });
953
+ });