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,494 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import {
3
+ validateArtifact,
4
+ parseAndValidateArtifact,
5
+ type ArbiterResult,
6
+ } from '../../src/core/nocturnal-arbiter.js';
7
+
8
+ describe('Nocturnal Arbiter', () => {
9
+ // -------------------------------------------------------------------------
10
+ // Valid artifact factory
11
+ // -------------------------------------------------------------------------
12
+
13
+ function makeValidArtifact(overrides: Record<string, unknown> = {}): Record<string, unknown> {
14
+ return {
15
+ artifactId: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
16
+ sessionId: 'session-abc123',
17
+ principleId: 'T-08',
18
+ sourceSnapshotRef: 'snapshot-2026-03-27-001',
19
+ badDecision: 'After bash command failed, immediately retried without diagnosing the root cause',
20
+ betterDecision: 'Check the error message and verify preconditions before retrying a failed bash command',
21
+ rationale: 'Treating each failure as a signal to diagnose rather than blindly retry prevents repeated failures',
22
+ createdAt: '2026-03-27T12:00:00.000Z',
23
+ ...overrides,
24
+ };
25
+ }
26
+
27
+ // -------------------------------------------------------------------------
28
+ // Tests: validateArtifact — valid inputs
29
+ // -------------------------------------------------------------------------
30
+
31
+ describe('validateArtifact', () => {
32
+ it('passes a valid artifact with all required fields', () => {
33
+ const artifact = makeValidArtifact();
34
+ const result = validateArtifact(artifact);
35
+ expect(result.passed).toBe(true);
36
+ expect(result.artifact).toBeDefined();
37
+ expect(result.failures).toHaveLength(0);
38
+ });
39
+
40
+ it('passes with optional sourceSnapshotRef present', () => {
41
+ const artifact = makeValidArtifact({ sourceSnapshotRef: 'snapshot-custom-001' });
42
+ const result = validateArtifact(artifact);
43
+ expect(result.passed).toBe(true);
44
+ });
45
+
46
+ it('passes when principleId and sessionId cross-validate against expected values', () => {
47
+ const artifact = makeValidArtifact({ principleId: 'T-08', sessionId: 'session-abc123' });
48
+ const result = validateArtifact(artifact, {
49
+ expectedPrincipleId: 'T-08',
50
+ expectedSessionId: 'session-abc123',
51
+ });
52
+ expect(result.passed).toBe(true);
53
+ });
54
+
55
+ it('passes a minimal but complete artifact', () => {
56
+ // Only required fields, no optional
57
+ const artifact = {
58
+ artifactId: '11111111-2222-4333-aaaa-555555555555',
59
+ sessionId: 'session-minimal',
60
+ principleId: 'T-01',
61
+ badDecision: 'Edited a file without reading it first',
62
+ betterDecision: 'Read the file before editing to understand its current state',
63
+ rationale: 'Surveying the existing territory before making changes prevents conflicts',
64
+ createdAt: '2026-03-27T12:00:00.000Z',
65
+ };
66
+ const result = validateArtifact(artifact);
67
+ expect(result.passed).toBe(true);
68
+ });
69
+ });
70
+
71
+ // -------------------------------------------------------------------------
72
+ // Tests: validateArtifact — invalid JSON structure
73
+ // -------------------------------------------------------------------------
74
+
75
+ describe('validateArtifact — invalid JSON structure', () => {
76
+ it('rejects null', () => {
77
+ const result = validateArtifact(null);
78
+ expect(result.passed).toBe(false);
79
+ expect(result.failures[0].reason).toContain('must be a JSON object');
80
+ });
81
+
82
+ it('rejects undefined', () => {
83
+ const result = validateArtifact(undefined);
84
+ expect(result.passed).toBe(false);
85
+ expect(result.failures[0].reason).toContain('must be a JSON object');
86
+ });
87
+
88
+ it('rejects an array', () => {
89
+ const result = validateArtifact([{ artifactId: 'xxx' }]);
90
+ expect(result.passed).toBe(false);
91
+ expect(result.failures[0].reason).toContain('must be a JSON object');
92
+ });
93
+
94
+ it('rejects a string', () => {
95
+ const result = validateArtifact('not an object');
96
+ expect(result.passed).toBe(false);
97
+ expect(result.failures[0].reason).toContain('must be a JSON object');
98
+ });
99
+
100
+ it('rejects a number', () => {
101
+ const result = validateArtifact(42);
102
+ expect(result.passed).toBe(false);
103
+ expect(result.failures[0].reason).toContain('must be a JSON object');
104
+ });
105
+ });
106
+
107
+ // -------------------------------------------------------------------------
108
+ // Tests: validateArtifact — invalid flag from reflector
109
+ // -------------------------------------------------------------------------
110
+
111
+ describe('validateArtifact — invalid flag', () => {
112
+ it('rejects artifact with invalid: true', () => {
113
+ const artifact = makeValidArtifact({ invalid: true, reason: 'no clear violation found' });
114
+ const result = validateArtifact(artifact);
115
+ expect(result.passed).toBe(false);
116
+ expect(result.failures[0].reason).toContain('invalid');
117
+ });
118
+
119
+ it('rejects artifact with invalid: "true" (string)', () => {
120
+ const artifact = makeValidArtifact({ invalid: 'true', reason: 'no violation' });
121
+ const result = validateArtifact(artifact);
122
+ expect(result.passed).toBe(false);
123
+ expect(result.failures[0].reason).toContain('invalid');
124
+ });
125
+ });
126
+
127
+ // -------------------------------------------------------------------------
128
+ // Tests: validateArtifact — missing required fields
129
+ // -------------------------------------------------------------------------
130
+
131
+ describe('validateArtifact — missing required fields', () => {
132
+ it('rejects missing artifactId', () => {
133
+ const artifact = makeValidArtifact();
134
+ delete artifact.artifactId;
135
+ const result = validateArtifact(artifact);
136
+ expect(result.passed).toBe(false);
137
+ expect(result.failures.some(f => f.field === 'artifactId')).toBe(true);
138
+ });
139
+
140
+ it('rejects empty artifactId', () => {
141
+ const artifact = makeValidArtifact({ artifactId: '' });
142
+ const result = validateArtifact(artifact);
143
+ expect(result.passed).toBe(false);
144
+ expect(result.failures.some(f => f.field === 'artifactId')).toBe(true);
145
+ });
146
+
147
+ it('rejects whitespace-only artifactId', () => {
148
+ const artifact = makeValidArtifact({ artifactId: ' ' });
149
+ const result = validateArtifact(artifact);
150
+ expect(result.passed).toBe(false);
151
+ expect(result.failures.some(f => f.field === 'artifactId')).toBe(true);
152
+ });
153
+
154
+ it('rejects missing sessionId', () => {
155
+ const artifact = makeValidArtifact();
156
+ delete artifact.sessionId;
157
+ const result = validateArtifact(artifact);
158
+ expect(result.passed).toBe(false);
159
+ expect(result.failures.some(f => f.field === 'sessionId')).toBe(true);
160
+ });
161
+
162
+ it('rejects missing principleId', () => {
163
+ const artifact = makeValidArtifact();
164
+ delete artifact.principleId;
165
+ const result = validateArtifact(artifact);
166
+ expect(result.passed).toBe(false);
167
+ expect(result.failures.some(f => f.field === 'principleId')).toBe(true);
168
+ });
169
+
170
+ it('rejects missing badDecision', () => {
171
+ const artifact = makeValidArtifact();
172
+ delete artifact.badDecision;
173
+ const result = validateArtifact(artifact);
174
+ expect(result.passed).toBe(false);
175
+ expect(result.failures.some(f => f.field === 'badDecision')).toBe(true);
176
+ });
177
+
178
+ it('rejects missing betterDecision', () => {
179
+ const artifact = makeValidArtifact();
180
+ delete artifact.betterDecision;
181
+ const result = validateArtifact(artifact);
182
+ expect(result.passed).toBe(false);
183
+ expect(result.failures.some(f => f.field === 'betterDecision')).toBe(true);
184
+ });
185
+
186
+ it('rejects missing rationale', () => {
187
+ const artifact = makeValidArtifact();
188
+ delete artifact.rationale;
189
+ const result = validateArtifact(artifact);
190
+ expect(result.passed).toBe(false);
191
+ expect(result.failures.some(f => f.field === 'rationale')).toBe(true);
192
+ });
193
+
194
+ it('rejects missing createdAt', () => {
195
+ const artifact = makeValidArtifact();
196
+ delete artifact.createdAt;
197
+ const result = validateArtifact(artifact);
198
+ expect(result.passed).toBe(false);
199
+ expect(result.failures.some(f => f.field === 'createdAt')).toBe(true);
200
+ });
201
+
202
+ it('rejects empty createdAt', () => {
203
+ const artifact = makeValidArtifact({ createdAt: '' });
204
+ const result = validateArtifact(artifact);
205
+ expect(result.passed).toBe(false);
206
+ expect(result.failures.some(f => f.field === 'createdAt')).toBe(true);
207
+ });
208
+ });
209
+
210
+ // -------------------------------------------------------------------------
211
+ // Tests: validateArtifact — invalid field formats
212
+ // -------------------------------------------------------------------------
213
+
214
+ describe('validateArtifact — invalid field formats', () => {
215
+ it('rejects artifactId that is not a valid UUID', () => {
216
+ const artifact = makeValidArtifact({ artifactId: 'not-a-uuid' });
217
+ const result = validateArtifact(artifact);
218
+ expect(result.passed).toBe(false);
219
+ expect(result.failures.some(f => f.field === 'artifactId')).toBe(true);
220
+ });
221
+
222
+ it('rejects createdAt that is not ISO 8601', () => {
223
+ const artifact = makeValidArtifact({ createdAt: '2026-03-27' });
224
+ const result = validateArtifact(artifact);
225
+ expect(result.passed).toBe(false);
226
+ expect(result.failures.some(f => f.field === 'createdAt')).toBe(true);
227
+ });
228
+
229
+ it('rejects createdAt with time but no seconds', () => {
230
+ const artifact = makeValidArtifact({ createdAt: '2026-03-27T12:00Z' });
231
+ const result = validateArtifact(artifact);
232
+ expect(result.passed).toBe(false);
233
+ expect(result.failures.some(f => f.field === 'createdAt')).toBe(true);
234
+ });
235
+
236
+ it('rejects createdAt with milliseconds (valid ISO but our format is different)', () => {
237
+ // Our regex requires .SSS after T
238
+ const artifact = makeValidArtifact({ createdAt: '2026-03-27T12:00:00Z' });
239
+ const result = validateArtifact(artifact);
240
+ // Actually this should pass — our regex allows optional .SSS
241
+ // Let's test the other direction
242
+ const artifact2 = makeValidArtifact({ createdAt: '2026-03-27T12:00:00.123Z' });
243
+ const result2 = validateArtifact(artifact2);
244
+ expect(result2.passed).toBe(true);
245
+ });
246
+ });
247
+
248
+ // -------------------------------------------------------------------------
249
+ // Tests: validateArtifact — cross-validation
250
+ // -------------------------------------------------------------------------
251
+
252
+ describe('validateArtifact — cross-validation', () => {
253
+ it('rejects principleId mismatch', () => {
254
+ const artifact = makeValidArtifact({ principleId: 'T-08' });
255
+ const result = validateArtifact(artifact, { expectedPrincipleId: 'T-01' });
256
+ expect(result.passed).toBe(false);
257
+ expect(result.failures.some(f => f.reason.includes('principleId mismatch'))).toBe(true);
258
+ });
259
+
260
+ it('rejects sessionId mismatch', () => {
261
+ const artifact = makeValidArtifact({ sessionId: 'session-abc' });
262
+ const result = validateArtifact(artifact, { expectedSessionId: 'session-xyz' });
263
+ expect(result.passed).toBe(false);
264
+ expect(result.failures.some(f => f.reason.includes('sessionId mismatch'))).toBe(true);
265
+ });
266
+
267
+ it('passes when principleId matches expected', () => {
268
+ const artifact = makeValidArtifact({ principleId: 'T-08' });
269
+ const result = validateArtifact(artifact, { expectedPrincipleId: 'T-08' });
270
+ expect(result.passed).toBe(true);
271
+ });
272
+
273
+ it('passes when sessionId matches expected', () => {
274
+ const artifact = makeValidArtifact({ sessionId: 'session-abc' });
275
+ const result = validateArtifact(artifact, { expectedSessionId: 'session-abc' });
276
+ expect(result.passed).toBe(true);
277
+ });
278
+
279
+ it('passes when no expected values provided', () => {
280
+ const artifact = makeValidArtifact({ principleId: 'T-99', sessionId: 'session-xyz' });
281
+ const result = validateArtifact(artifact);
282
+ expect(result.passed).toBe(true);
283
+ });
284
+ });
285
+
286
+ // -------------------------------------------------------------------------
287
+ // Tests: validateArtifact — placeholder detection
288
+ // -------------------------------------------------------------------------
289
+
290
+ describe('validateArtifact — placeholder detection', () => {
291
+ const placeholderTests = [
292
+ { value: '<placeholder>', expected: true },
293
+ { value: '<uuid>', expected: true },
294
+ { value: '<session-id>', expected: true },
295
+ { value: '<principle-id>', expected: true },
296
+ { value: 'undefined', expected: true },
297
+ { value: 'null', expected: true },
298
+ { value: 'N/A', expected: true },
299
+ { value: 'TODO', expected: true },
300
+ { value: 'FIXME', expected: true },
301
+ { value: 'valid decision text', expected: false },
302
+ { value: 'Read the file before editing', expected: false },
303
+ ];
304
+
305
+ placeholderTests.forEach(({ value, expected }) => {
306
+ it(`badDecision: '${value}' → ${expected ? 'rejected' : 'accepted'}`, () => {
307
+ const artifact = makeValidArtifact({ badDecision: value });
308
+ const result = validateArtifact(artifact);
309
+ if (expected) {
310
+ expect(result.passed).toBe(false);
311
+ expect(result.failures.some(f => f.reason.includes('placeholder'))).toBe(true);
312
+ } else {
313
+ expect(result.passed).toBe(true);
314
+ }
315
+ });
316
+ });
317
+
318
+ it('rejects betterDecision containing placeholder', () => {
319
+ const artifact = makeValidArtifact({ betterDecision: '<action>' });
320
+ const result = validateArtifact(artifact);
321
+ expect(result.passed).toBe(false);
322
+ expect(result.failures.some(f => f.field === 'betterDecision')).toBe(true);
323
+ });
324
+
325
+ it('rejects rationale containing placeholder', () => {
326
+ const artifact = makeValidArtifact({ rationale: 'TODO: add rationale' });
327
+ const result = validateArtifact(artifact);
328
+ expect(result.passed).toBe(false);
329
+ expect(result.failures.some(f => f.field === 'rationale')).toBe(true);
330
+ });
331
+ });
332
+
333
+ // -------------------------------------------------------------------------
334
+ // Tests: validateArtifact — raw content detection
335
+ // -------------------------------------------------------------------------
336
+
337
+ describe('validateArtifact — raw content detection', () => {
338
+ it('rejects badDecision containing function definition', () => {
339
+ const artifact = makeValidArtifact({ badDecision: 'function handleError() { return 1; }' });
340
+ const result = validateArtifact(artifact);
341
+ expect(result.passed).toBe(false);
342
+ expect(result.failures.some(f => f.reason.includes('raw/private content'))).toBe(true);
343
+ });
344
+
345
+ it('rejects badDecision containing import statement', () => {
346
+ const artifact = makeValidArtifact({ badDecision: 'import { foo } from "./bar"; doSomething();' });
347
+ const result = validateArtifact(artifact);
348
+ expect(result.passed).toBe(false);
349
+ expect(result.failures.some(f => f.reason.includes('raw/private content'))).toBe(true);
350
+ });
351
+
352
+ it('rejects betterDecision containing credential pattern', () => {
353
+ const artifact = makeValidArtifact({ betterDecision: 'Check the api_key before proceeding' });
354
+ const result = validateArtifact(artifact);
355
+ expect(result.passed).toBe(false);
356
+ expect(result.failures.some(f => f.reason.includes('raw/private content'))).toBe(true);
357
+ });
358
+
359
+ it('accepts text without raw content patterns', () => {
360
+ const artifact = makeValidArtifact({
361
+ badDecision: 'Proceeded with editing without reading the file first',
362
+ betterDecision: 'Read the file to understand its current structure before making edits',
363
+ });
364
+ const result = validateArtifact(artifact);
365
+ expect(result.passed).toBe(true);
366
+ });
367
+ });
368
+
369
+ // -------------------------------------------------------------------------
370
+ // Tests: validateArtifact — semantic rules
371
+ // -------------------------------------------------------------------------
372
+
373
+ describe('validateArtifact — semantic rules', () => {
374
+ it('rejects identical badDecision and betterDecision', () => {
375
+ const artifact = makeValidArtifact({
376
+ badDecision: 'Read the file first',
377
+ betterDecision: 'Read the file first',
378
+ });
379
+ const result = validateArtifact(artifact);
380
+ expect(result.passed).toBe(false);
381
+ expect(result.failures.some(f => f.reason.includes('identical'))).toBe(true);
382
+ });
383
+
384
+ it('accepts different badDecision and betterDecision', () => {
385
+ const artifact = makeValidArtifact({
386
+ badDecision: 'Edited without reading',
387
+ betterDecision: 'Read the file before editing',
388
+ });
389
+ const result = validateArtifact(artifact);
390
+ expect(result.passed).toBe(true);
391
+ });
392
+
393
+ it('rejects rationale that is too short', () => {
394
+ const artifact = makeValidArtifact({ rationale: 'Because.' });
395
+ const result = validateArtifact(artifact);
396
+ expect(result.passed).toBe(false);
397
+ expect(result.failures.some(f => f.reason.includes('too short'))).toBe(true);
398
+ });
399
+
400
+ it('accepts rationale at minimum length (20 chars)', () => {
401
+ const artifact = makeValidArtifact({ rationale: '12345678901234567890' }); // exactly 20
402
+ const result = validateArtifact(artifact);
403
+ expect(result.passed).toBe(true);
404
+ });
405
+
406
+ it('rejects sourceSnapshotRef that is present but empty', () => {
407
+ const artifact = makeValidArtifact({ sourceSnapshotRef: '' });
408
+ const result = validateArtifact(artifact);
409
+ expect(result.passed).toBe(false);
410
+ expect(result.failures.some(f => f.field === 'sourceSnapshotRef')).toBe(true);
411
+ });
412
+ });
413
+
414
+ // -------------------------------------------------------------------------
415
+ // Tests: parseAndValidateArtifact
416
+ // -------------------------------------------------------------------------
417
+
418
+ describe('parseAndValidateArtifact', () => {
419
+ it('parses a valid JSON string and validates successfully', () => {
420
+ const json = JSON.stringify(makeValidArtifact());
421
+ const result = parseAndValidateArtifact(json);
422
+ expect(result.passed).toBe(true);
423
+ expect(result.artifact).toBeDefined();
424
+ expect(result.artifact?.principleId).toBe('T-08');
425
+ });
426
+
427
+ it('fails on invalid JSON', () => {
428
+ const result = parseAndValidateArtifact('not valid json {');
429
+ expect(result.passed).toBe(false);
430
+ expect(result.failures[0].reason).toContain('Failed to parse JSON');
431
+ });
432
+
433
+ it('fails on empty string', () => {
434
+ const result = parseAndValidateArtifact('');
435
+ expect(result.passed).toBe(false);
436
+ expect(result.failures[0].reason).toContain('Failed to parse JSON');
437
+ });
438
+
439
+ it('passes cross-validation from options', () => {
440
+ const artifact = makeValidArtifact({ principleId: 'T-01', sessionId: 'session-xyz' });
441
+ const json = JSON.stringify(artifact);
442
+ const result = parseAndValidateArtifact(json, {
443
+ expectedPrincipleId: 'T-01',
444
+ expectedSessionId: 'session-xyz',
445
+ });
446
+ expect(result.passed).toBe(true);
447
+ });
448
+
449
+ it('fails cross-validation from options on mismatch', () => {
450
+ const artifact = makeValidArtifact({ principleId: 'T-01' });
451
+ const json = JSON.stringify(artifact);
452
+ const result = parseAndValidateArtifact(json, { expectedPrincipleId: 'T-99' });
453
+ expect(result.passed).toBe(false);
454
+ });
455
+ });
456
+
457
+ // -------------------------------------------------------------------------
458
+ // Tests: constructed artifact output shape
459
+ // -------------------------------------------------------------------------
460
+
461
+ describe('constructed artifact shape', () => {
462
+ it('returns correct NocturnalArtifact shape when passed', () => {
463
+ const artifact = makeValidArtifact();
464
+ const result = validateArtifact(artifact);
465
+ expect(result.passed).toBe(true);
466
+ expect(result.artifact).toEqual({
467
+ artifactId: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
468
+ sessionId: 'session-abc123',
469
+ principleId: 'T-08',
470
+ sourceSnapshotRef: 'snapshot-2026-03-27-001',
471
+ badDecision: 'After bash command failed, immediately retried without diagnosing the root cause',
472
+ betterDecision: 'Check the error message and verify preconditions before retrying a failed bash command',
473
+ rationale: 'Treating each failure as a signal to diagnose rather than blindly retry prevents repeated failures',
474
+ createdAt: '2026-03-27T12:00:00.000Z',
475
+ });
476
+ });
477
+
478
+ it('converts all fields to strings even if passed as numbers', () => {
479
+ // artifactId is normally string, but test robustness
480
+ const artifact = makeValidArtifact() as Record<string, unknown>;
481
+ const result = validateArtifact(artifact);
482
+ expect(result.passed).toBe(true);
483
+ expect(typeof result.artifact?.artifactId).toBe('string');
484
+ });
485
+
486
+ it('uses empty string for missing sourceSnapshotRef', () => {
487
+ const artifact = makeValidArtifact();
488
+ delete artifact.sourceSnapshotRef;
489
+ const result = validateArtifact(artifact);
490
+ expect(result.passed).toBe(true);
491
+ expect(result.artifact?.sourceSnapshotRef).toBe('');
492
+ });
493
+ });
494
+ });