principles-disciple 1.8.1 → 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 (470) 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/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 -129
  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 -101
  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 -13
  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 -52
  403. package/dist/hooks/progressive-trust-gate.js +0 -134
  404. package/dist/hooks/prompt.d.ts +0 -49
  405. package/dist/hooks/prompt.js +0 -905
  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 -681
  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 -88
  423. package/dist/service/empathy-observer-manager.js +0 -414
  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 -975
  428. package/dist/service/health-query-service.d.ts +0 -170
  429. package/dist/service/health-query-service.js +0 -662
  430. package/dist/service/nocturnal-runtime.d.ts +0 -183
  431. package/dist/service/nocturnal-service.d.ts +0 -163
  432. package/dist/service/nocturnal-service.js +0 -787
  433. package/dist/service/nocturnal-target-selector.d.ts +0 -145
  434. package/dist/service/nocturnal-target-selector.js +0 -315
  435. package/dist/service/phase3-input-filter.d.ts +0 -73
  436. package/dist/service/phase3-input-filter.js +0 -172
  437. package/dist/service/runtime-summary-service.d.ts +0 -122
  438. package/dist/service/runtime-summary-service.js +0 -485
  439. package/dist/service/subagent-workflow/empathy-observer-workflow-manager.d.ts +0 -48
  440. package/dist/service/subagent-workflow/index.d.ts +0 -4
  441. package/dist/service/subagent-workflow/index.js +0 -3
  442. package/dist/service/subagent-workflow/runtime-direct-driver.d.ts +0 -77
  443. package/dist/service/subagent-workflow/runtime-direct-driver.js +0 -75
  444. package/dist/service/subagent-workflow/types.js +0 -11
  445. package/dist/service/subagent-workflow/workflow-store.d.ts +0 -26
  446. package/dist/service/subagent-workflow/workflow-store.js +0 -165
  447. package/dist/service/trajectory-service.d.ts +0 -2
  448. package/dist/service/trajectory-service.js +0 -15
  449. package/dist/tools/critique-prompt.d.ts +0 -14
  450. package/dist/tools/deep-reflect.d.ts +0 -39
  451. package/dist/tools/deep-reflect.js +0 -350
  452. package/dist/tools/model-index.d.ts +0 -9
  453. package/dist/types/event-types.d.ts +0 -306
  454. package/dist/types/event-types.js +0 -106
  455. package/dist/types/hygiene-types.d.ts +0 -20
  456. package/dist/types/hygiene-types.js +0 -12
  457. package/dist/types/runtime-summary.d.ts +0 -47
  458. package/dist/types/runtime-summary.js +0 -1
  459. package/dist/types.d.ts +0 -50
  460. package/dist/types.js +0 -22
  461. package/dist/utils/file-lock.d.ts +0 -71
  462. package/dist/utils/file-lock.js +0 -309
  463. package/dist/utils/glob-match.d.ts +0 -28
  464. package/dist/utils/hashing.d.ts +0 -9
  465. package/dist/utils/io.d.ts +0 -6
  466. package/dist/utils/io.js +0 -106
  467. package/dist/utils/nlp.d.ts +0 -9
  468. package/dist/utils/plugin-logger.d.ts +0 -39
  469. package/dist/utils/subagent-probe.d.ts +0 -34
  470. 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
+ });