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,668 @@
1
+ /**
2
+ * Nocturnal Dataset — Sample Lineage Store and Review State Registry
3
+ * =================================================================
4
+ *
5
+ * PURPOSE: Establish each approved nocturnal sample as a first-class auditable
6
+ * data asset with fingerprint, lineage, review state, and model family binding.
7
+ *
8
+ * ARCHITECTURE:
9
+ * - Registry file: {stateDir}/.state/nocturnal/dataset-registry.json
10
+ * - One JSON array of NocturnalDatasetRecord
11
+ * - Each record is immutable except for reviewStatus and reviewReason
12
+ * - sampleFingerprint is the primary key (deterministic: SHA-256 of artifactId+principleId+sessionId)
13
+ *
14
+ * RELATIONSHIP TO NOCTURNAL ARTIFACTS:
15
+ * - Artifacts live in: .state/nocturnal/samples/{artifactId}.json
16
+ * - Dataset records reference artifacts via artifactId and artifactPath
17
+ * - Artifacts are NOT modified by dataset operations
18
+ *
19
+ * DESIGN CONSTRAINTS:
20
+ * - No training run registry (Phase 4)
21
+ * - No checkpoint registry (Phase 4)
22
+ * - No worker routing changes
23
+ * - No JSONL export (that's Task 3.2)
24
+ * - Lineage is append-only for approved records
25
+ * - reviewStatus transitions are the only state mutations allowed
26
+ */
27
+
28
+ import * as fs from 'fs';
29
+ import * as path from 'path';
30
+ import * as crypto from 'crypto';
31
+ import { NocturnalPathResolver, resolveNocturnalDir } from './nocturnal-paths.js';
32
+ import type { NocturnalArtifact } from './nocturnal-arbiter.js';
33
+ import { withLock } from '../utils/file-lock.js';
34
+
35
+ // ---------------------------------------------------------------------------
36
+ // Types
37
+ // ---------------------------------------------------------------------------
38
+
39
+ /**
40
+ * Review status for a nocturnal dataset sample.
41
+ * Follows the lifecycle: pending_review → approved_for_training | rejected | superseded
42
+ */
43
+ export type NocturnalReviewStatus =
44
+ | 'pending_review'
45
+ | 'approved_for_training'
46
+ | 'rejected'
47
+ | 'superseded';
48
+
49
+ /**
50
+ * A nocturnal dataset record — the immutable lineage entry for one sample.
51
+ *
52
+ * PRIMARY KEY: sampleFingerprint (deterministic SHA-256)
53
+ * MUTABLE FIELDS: reviewStatus, reviewReason only
54
+ * IMMUTABLE FIELDS: all others
55
+ */
56
+ export interface NocturnalDatasetRecord {
57
+ /**
58
+ * Deterministic fingerprint: SHA-256(artifactId + principleId + sessionId).
59
+ * Primary key for dataset operations.
60
+ */
61
+ sampleFingerprint: string;
62
+
63
+ /** Reference to the original artifact */
64
+ artifactId: string;
65
+
66
+ /** Source session */
67
+ sessionId: string;
68
+
69
+ /** Target principle that generated this sample */
70
+ principleId: string;
71
+
72
+ /** Reference to the trajectory snapshot used */
73
+ sourceSnapshotRef: string;
74
+
75
+ /**
76
+ * Current review state.
77
+ * Only transitions allowed: pending_review → approved_for_training | rejected | superseded
78
+ */
79
+ reviewStatus: NocturnalReviewStatus;
80
+
81
+ /**
82
+ * Human-provided reason for the review decision.
83
+ * Required for approved_for_training and rejected; optional for superseded.
84
+ */
85
+ reviewReason?: string;
86
+
87
+ /**
88
+ * Target model family this sample is bound to.
89
+ * REQUIRED for export-ready samples.
90
+ * NULL means "not yet assigned" (pending_review defaults to null).
91
+ */
92
+ targetModelFamily: string | null;
93
+
94
+ /**
95
+ * When this sample was first registered in the dataset.
96
+ */
97
+ createdAt: string;
98
+
99
+ /**
100
+ * Last time reviewStatus or reviewReason was updated.
101
+ */
102
+ updatedAt: string;
103
+
104
+ /**
105
+ * Absolute path to the artifact file.
106
+ */
107
+ artifactPath: string;
108
+ }
109
+
110
+ /**
111
+ * Filter options for listing dataset records.
112
+ */
113
+ export interface DatasetFilterOptions {
114
+ /**
115
+ * Filter by review status.
116
+ */
117
+ reviewStatus?: NocturnalReviewStatus | NocturnalReviewStatus[];
118
+
119
+ /**
120
+ * Filter by target model family.
121
+ * NULL means "any" (including null/unassigned).
122
+ */
123
+ targetModelFamily?: string | null;
124
+
125
+ /**
126
+ * Include only export-ready records.
127
+ * An export-ready record must have:
128
+ * - reviewStatus === 'approved_for_training'
129
+ * - targetModelFamily !== null
130
+ * - artifactPath points to an existing file
131
+ */
132
+ exportReadyOnly?: boolean;
133
+ }
134
+
135
+ /**
136
+ * Result of registering a sample.
137
+ */
138
+ export interface RegisterSampleResult {
139
+ /** The registered record */
140
+ record: NocturnalDatasetRecord;
141
+ /** Whether this was a new registration (true) or duplicate link (false) */
142
+ isNew: boolean;
143
+ /**
144
+ * If isNew === false, this points to the existing record.
145
+ */
146
+ existingRecord?: NocturnalDatasetRecord;
147
+ }
148
+
149
+ // ---------------------------------------------------------------------------
150
+ // Fingerprint Generation
151
+ // ---------------------------------------------------------------------------
152
+
153
+ /**
154
+ * Generate a deterministic sample fingerprint from an artifact.
155
+ *
156
+ * FINGERPRINT = SHA-256(artifactId || principleId || sessionId)
157
+ *
158
+ * The fingerprint is deterministic so the same sample always produces
159
+ * the same fingerprint, enabling duplicate detection.
160
+ */
161
+ export function generateSampleFingerprint(
162
+ artifactId: string,
163
+ principleId: string,
164
+ sessionId: string
165
+ ): string {
166
+ const input = `${artifactId}|${principleId}|${sessionId}`;
167
+ return crypto.createHash('sha256').update(input, 'utf8').digest('hex');
168
+ }
169
+
170
+ /**
171
+ * Generate a fingerprint from an existing NocturnalArtifact.
172
+ */
173
+ export function generateFingerprintFromArtifact(artifact: NocturnalArtifact): string {
174
+ return generateSampleFingerprint(
175
+ artifact.artifactId,
176
+ artifact.principleId,
177
+ artifact.sessionId
178
+ );
179
+ }
180
+
181
+ // ---------------------------------------------------------------------------
182
+ // Registry Path
183
+ // ---------------------------------------------------------------------------
184
+
185
+ /**
186
+ * Path to the dataset registry file.
187
+ */
188
+ function getRegistryPath(workspaceDir: string): string {
189
+ // Registry lives in .state/nocturnal/dataset-registry.json
190
+ const nocturnalRoot = resolveNocturnalDir(workspaceDir, 'ROOT');
191
+ return path.join(nocturnalRoot, 'dataset-registry.json');
192
+ }
193
+
194
+ /**
195
+ * Ensure the registry directory exists.
196
+ */
197
+ function ensureRegistryDir(workspaceDir: string): void {
198
+ const registryPath = getRegistryPath(workspaceDir);
199
+ const dir = path.dirname(registryPath);
200
+ if (!fs.existsSync(dir)) {
201
+ fs.mkdirSync(dir, { recursive: true });
202
+ }
203
+ }
204
+
205
+ /**
206
+ * Read the registry file. Returns empty array if missing.
207
+ */
208
+ function readRegistry(workspaceDir: string): NocturnalDatasetRecord[] {
209
+ const registryPath = getRegistryPath(workspaceDir);
210
+ if (!fs.existsSync(registryPath)) {
211
+ return [];
212
+ }
213
+ try {
214
+ const content = fs.readFileSync(registryPath, 'utf-8');
215
+ return JSON.parse(content) as NocturnalDatasetRecord[];
216
+ } catch (err) {
217
+ // Corrupted registry — fail-safe to empty array, but log the problem
218
+ console.warn(`[nocturnal-dataset] Registry corrupted at ${registryPath}, recovering with empty state: ${String(err)}`);
219
+ return [];
220
+ }
221
+ }
222
+
223
+ /**
224
+ * Write the registry file atomically (write-then-rename for atomicity).
225
+ * Caller must hold the registry lock (via withRegistryLock).
226
+ */
227
+ function writeRegistry(workspaceDir: string, records: NocturnalDatasetRecord[]): void {
228
+ ensureRegistryDir(workspaceDir);
229
+ const registryPath = getRegistryPath(workspaceDir);
230
+ const tmpPath = `${registryPath}.tmp`;
231
+ fs.writeFileSync(tmpPath, JSON.stringify(records, null, 2), 'utf-8');
232
+ fs.renameSync(tmpPath, registryPath);
233
+ }
234
+
235
+ // ---------------------------------------------------------------------------
236
+ // Core Operations
237
+ // ---------------------------------------------------------------------------
238
+
239
+ /**
240
+ * Execute a read-modify-write on the registry under an exclusive lock.
241
+ * This prevents concurrent writers from racing on the same file.
242
+ */
243
+ function withRegistryLock<T>(workspaceDir: string, fn: (records: NocturnalDatasetRecord[]) => T): T {
244
+ const registryPath = getRegistryPath(workspaceDir);
245
+ return withLock(registryPath, () => {
246
+ const records = readRegistry(workspaceDir);
247
+ return fn(records);
248
+ });
249
+ }
250
+
251
+ /**
252
+ * Register an approved nocturnal artifact in the dataset registry.
253
+ *
254
+ * DUPLICATE HANDLING:
255
+ * - If a record with the same sampleFingerprint already exists, returns
256
+ * existingRecord (isNew === false) instead of creating a duplicate.
257
+ * - The original artifact file is never modified.
258
+ *
259
+ * @param workspaceDir - Workspace directory
260
+ * @param artifact - The approved NocturnalArtifact
261
+ * @param artifactPath - Absolute path where the artifact file is stored
262
+ * @param targetModelFamily - Model family binding (required for export-ready)
263
+ * @returns RegisterSampleResult
264
+ */
265
+ export function registerSample(
266
+ workspaceDir: string,
267
+ artifact: NocturnalArtifact,
268
+ artifactPath: string,
269
+ targetModelFamily: string | null = null
270
+ ): RegisterSampleResult {
271
+ const fingerprint = generateFingerprintFromArtifact(artifact);
272
+ const now = new Date().toISOString();
273
+
274
+ return withRegistryLock(workspaceDir, (records) => {
275
+ const existing = records.find((r) => r.sampleFingerprint === fingerprint);
276
+ if (existing) {
277
+ return {
278
+ record: existing,
279
+ isNew: false,
280
+ existingRecord: existing,
281
+ };
282
+ }
283
+
284
+ const record: NocturnalDatasetRecord = {
285
+ sampleFingerprint: fingerprint,
286
+ artifactId: artifact.artifactId,
287
+ sessionId: artifact.sessionId,
288
+ principleId: artifact.principleId,
289
+ sourceSnapshotRef: artifact.sourceSnapshotRef,
290
+ reviewStatus: 'pending_review',
291
+ reviewReason: undefined,
292
+ targetModelFamily,
293
+ createdAt: now,
294
+ updatedAt: now,
295
+ artifactPath: path.normalize(artifactPath),
296
+ };
297
+
298
+ records.push(record);
299
+ writeRegistry(workspaceDir, records);
300
+
301
+ return { record, isNew: true };
302
+ });
303
+ }
304
+
305
+ /**
306
+ * Get a dataset record by fingerprint.
307
+ */
308
+ export function getDatasetRecord(
309
+ workspaceDir: string,
310
+ sampleFingerprint: string
311
+ ): NocturnalDatasetRecord | null {
312
+ const records = readRegistry(workspaceDir);
313
+ return records.find((r) => r.sampleFingerprint === sampleFingerprint) ?? null;
314
+ }
315
+
316
+ /**
317
+ * Get a dataset record by artifactId.
318
+ */
319
+ export function getDatasetRecordByArtifactId(
320
+ workspaceDir: string,
321
+ artifactId: string
322
+ ): NocturnalDatasetRecord | null {
323
+ const records = readRegistry(workspaceDir);
324
+ return records.find((r) => r.artifactId === artifactId) ?? null;
325
+ }
326
+
327
+ /**
328
+ * List dataset records with optional filtering.
329
+ *
330
+ * @param workspaceDir - Workspace directory
331
+ * @param filter - Optional filter criteria
332
+ * @returns Filtered records sorted by createdAt descending
333
+ */
334
+ export function listDatasetRecords(
335
+ workspaceDir: string,
336
+ filter?: DatasetFilterOptions
337
+ ): NocturnalDatasetRecord[] {
338
+ let records = readRegistry(workspaceDir);
339
+
340
+ if (!filter) {
341
+ return records.sort(
342
+ (a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
343
+ );
344
+ }
345
+
346
+ // Filter by reviewStatus
347
+ if (filter.reviewStatus !== undefined) {
348
+ const statuses = Array.isArray(filter.reviewStatus)
349
+ ? filter.reviewStatus
350
+ : [filter.reviewStatus];
351
+ records = records.filter((r) => statuses.includes(r.reviewStatus));
352
+ }
353
+
354
+ // Filter by targetModelFamily
355
+ if (filter.targetModelFamily !== undefined) {
356
+ if (filter.targetModelFamily === null) {
357
+ // Include only null/unassigned
358
+ records = records.filter((r) => r.targetModelFamily === null);
359
+ } else {
360
+ records = records.filter((r) => r.targetModelFamily === filter.targetModelFamily);
361
+ }
362
+ }
363
+
364
+ // Filter export-ready only
365
+ if (filter.exportReadyOnly === true) {
366
+ records = records.filter((r) => {
367
+ if (r.reviewStatus !== 'approved_for_training') return false;
368
+ if (r.targetModelFamily === null) return false;
369
+ // Verify artifact file exists
370
+ if (!fs.existsSync(r.artifactPath)) return false;
371
+ return true;
372
+ });
373
+ }
374
+
375
+ return records.sort(
376
+ (a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
377
+ );
378
+ }
379
+
380
+ /**
381
+ * Valid review status transitions.
382
+ *pending_review → approved_for_training | rejected | superseded
383
+ * approved_for_training → superseded (if a better sample replaces it)
384
+ * rejected → pending_review (if re-review is requested)
385
+ * superseded → (terminal state, no transitions)
386
+ */
387
+ const VALID_TRANSITIONS: Record<NocturnalReviewStatus, NocturnalReviewStatus[]> = {
388
+ pending_review: ['approved_for_training', 'rejected', 'superseded'],
389
+ approved_for_training: ['superseded'],
390
+ rejected: ['pending_review', 'superseded'],
391
+ superseded: [], // terminal
392
+ };
393
+
394
+ /**
395
+ * Update the review status of a dataset record.
396
+ *
397
+ * @param workspaceDir - Workspace directory
398
+ * @param sampleFingerprint - The fingerprint of the record to update
399
+ * @param newStatus - The new review status
400
+ * @param reason - Optional reason (required for approved/rejected per spec)
401
+ * @returns Updated record, or null if not found
402
+ * @throws Error if transition is invalid
403
+ */
404
+ export function updateReviewStatus(
405
+ workspaceDir: string,
406
+ sampleFingerprint: string,
407
+ newStatus: NocturnalReviewStatus,
408
+ reason?: string
409
+ ): NocturnalDatasetRecord {
410
+ return withRegistryLock(workspaceDir, (records) => {
411
+ const idx = records.findIndex((r) => r.sampleFingerprint === sampleFingerprint);
412
+
413
+ if (idx === -1) {
414
+ throw new Error(`Dataset record not found: ${sampleFingerprint}`);
415
+ }
416
+
417
+ const record = records[idx];
418
+
419
+ // Validate transition
420
+ const allowed = VALID_TRANSITIONS[record.reviewStatus];
421
+ if (!allowed.includes(newStatus)) {
422
+ throw new Error(
423
+ `Invalid review status transition: ${record.reviewStatus} → ${newStatus}. ` +
424
+ `Allowed transitions from ${record.reviewStatus}: ${allowed.join(', ') || 'none'}`
425
+ );
426
+ }
427
+
428
+ // Enforce reason requirement for approved/rejected
429
+ if (
430
+ (newStatus === 'approved_for_training' || newStatus === 'rejected') &&
431
+ !reason
432
+ ) {
433
+ throw new Error(
434
+ `reviewReason is required when transitioning to ${newStatus}`
435
+ );
436
+ }
437
+
438
+ // Apply update
439
+ records[idx] = {
440
+ ...record,
441
+ reviewStatus: newStatus,
442
+ reviewReason: reason ?? record.reviewReason,
443
+ updatedAt: new Date().toISOString(),
444
+ };
445
+
446
+ writeRegistry(workspaceDir, records);
447
+ return records[idx];
448
+ });
449
+ }
450
+
451
+ /**
452
+ * Update the target model family binding.
453
+ */
454
+ export function updateTargetModelFamily(
455
+ workspaceDir: string,
456
+ sampleFingerprint: string,
457
+ targetModelFamily: string | null
458
+ ): NocturnalDatasetRecord {
459
+ return withRegistryLock(workspaceDir, (records) => {
460
+ const idx = records.findIndex((r) => r.sampleFingerprint === sampleFingerprint);
461
+
462
+ if (idx === -1) {
463
+ throw new Error(`Dataset record not found: ${sampleFingerprint}`);
464
+ }
465
+
466
+ records[idx] = {
467
+ ...records[idx],
468
+ targetModelFamily,
469
+ updatedAt: new Date().toISOString(),
470
+ };
471
+
472
+ writeRegistry(workspaceDir, records);
473
+ return records[idx];
474
+ });
475
+ }
476
+
477
+ /**
478
+ * Check if a sample is export-ready.
479
+ *
480
+ * EXPORT-READY means:
481
+ * - reviewStatus === 'approved_for_training'
482
+ * - targetModelFamily !== null
483
+ * - artifact file exists
484
+ * - lineage fields are complete
485
+ */
486
+ export function isExportReady(
487
+ workspaceDir: string,
488
+ sampleFingerprint: string
489
+ ): boolean {
490
+ const record = getDatasetRecord(workspaceDir, sampleFingerprint);
491
+ if (!record) return false;
492
+ if (record.reviewStatus !== 'approved_for_training') return false;
493
+ if (record.targetModelFamily === null) return false;
494
+ if (!fs.existsSync(record.artifactPath)) return false;
495
+ return true;
496
+ }
497
+
498
+ /**
499
+ * List all export-ready records for a specific target model family.
500
+ */
501
+ export function listExportReadyRecords(
502
+ workspaceDir: string,
503
+ targetModelFamily?: string | null
504
+ ): NocturnalDatasetRecord[] {
505
+ return listDatasetRecords(workspaceDir, {
506
+ exportReadyOnly: true,
507
+ targetModelFamily: targetModelFamily ?? undefined,
508
+ });
509
+ }
510
+
511
+ /**
512
+ * Get the artifact path for a dataset record.
513
+ * Verifies the file exists before returning.
514
+ */
515
+ export function getArtifactPath(
516
+ workspaceDir: string,
517
+ sampleFingerprint: string
518
+ ): string | null {
519
+ const record = getDatasetRecord(workspaceDir, sampleFingerprint);
520
+ if (!record) return null;
521
+ if (!fs.existsSync(record.artifactPath)) return null;
522
+ return record.artifactPath;
523
+ }
524
+
525
+ /**
526
+ * Read the artifact file for a dataset record.
527
+ * @throws Error if record not found, artifact file missing, or unreadable
528
+ */
529
+ export function readDatasetArtifact(
530
+ workspaceDir: string,
531
+ sampleFingerprint: string
532
+ ): NocturnalArtifact {
533
+ const artifactPath = getArtifactPath(workspaceDir, sampleFingerprint);
534
+ if (!artifactPath) {
535
+ throw new Error(`Artifact file not found for sample ${sampleFingerprint}`);
536
+ }
537
+
538
+ const content = fs.readFileSync(artifactPath, 'utf-8');
539
+ const parsed = JSON.parse(content);
540
+ // Return only the NocturnalArtifact fields (not the extended sample record)
541
+ return {
542
+ artifactId: parsed.artifactId,
543
+ sessionId: parsed.sessionId,
544
+ principleId: parsed.principleId,
545
+ sourceSnapshotRef: parsed.sourceSnapshotRef,
546
+ badDecision: parsed.badDecision,
547
+ betterDecision: parsed.betterDecision,
548
+ rationale: parsed.rationale,
549
+ createdAt: parsed.createdAt,
550
+ } as NocturnalArtifact;
551
+ }
552
+
553
+ /**
554
+ * Count records by status for dashboard purposes.
555
+ */
556
+ export function getDatasetStats(
557
+ workspaceDir: string
558
+ ): {
559
+ total: number;
560
+ pendingReview: number;
561
+ approvedForTraining: number;
562
+ rejected: number;
563
+ superseded: number;
564
+ exportReadyByFamily: Record<string, number>;
565
+ } {
566
+ const records = readRegistry(workspaceDir);
567
+
568
+ const counts = {
569
+ total: records.length,
570
+ pendingReview: 0,
571
+ approvedForTraining: 0,
572
+ rejected: 0,
573
+ superseded: 0,
574
+ exportReadyByFamily: {} as Record<string, number>,
575
+ };
576
+
577
+ for (const record of records) {
578
+ switch (record.reviewStatus) {
579
+ case 'pending_review':
580
+ counts.pendingReview++;
581
+ break;
582
+ case 'approved_for_training':
583
+ counts.approvedForTraining++;
584
+ break;
585
+ case 'rejected':
586
+ counts.rejected++;
587
+ break;
588
+ case 'superseded':
589
+ counts.superseded++;
590
+ break;
591
+ }
592
+
593
+ // Count export-ready by family
594
+ if (
595
+ record.reviewStatus === 'approved_for_training' &&
596
+ record.targetModelFamily !== null &&
597
+ fs.existsSync(record.artifactPath)
598
+ ) {
599
+ const family = record.targetModelFamily;
600
+ counts.exportReadyByFamily[family] = (counts.exportReadyByFamily[family] || 0) + 1;
601
+ }
602
+ }
603
+
604
+ return counts;
605
+ }
606
+
607
+ // ---------------------------------------------------------------------------
608
+ // Auto-registration from persisted samples
609
+ // ---------------------------------------------------------------------------
610
+
611
+ /**
612
+ * Scan the samples directory and register any approved artifacts
613
+ * that are not yet in the dataset registry.
614
+ *
615
+ * This is used for:
616
+ * 1. Initial migration of Phase 2 artifacts to Phase 3 dataset
617
+ * 2. Recovering from registry corruption
618
+ *
619
+ * @param workspaceDir - Workspace directory
620
+ * @param targetModelFamily - Default target family for migrated samples
621
+ * @returns Number of newly registered samples
622
+ */
623
+ export function migrateSampleArtifacts(
624
+ workspaceDir: string,
625
+ targetModelFamily: string | null = null
626
+ ): number {
627
+ const samplePaths = NocturnalPathResolver.listSamples(workspaceDir);
628
+ let newCount = 0;
629
+
630
+ for (const samplePath of samplePaths) {
631
+ try {
632
+ const content = fs.readFileSync(samplePath, 'utf-8');
633
+ const sample = JSON.parse(content);
634
+
635
+ // Only process approved samples
636
+ if (sample.status !== 'approved') continue;
637
+ if (!sample.artifactId || !sample.sessionId || !sample.principleId) continue;
638
+
639
+ // Skip if already in registry
640
+ const fingerprint = generateSampleFingerprint(
641
+ sample.artifactId,
642
+ sample.principleId,
643
+ sample.sessionId
644
+ );
645
+ const existing = getDatasetRecord(workspaceDir, fingerprint);
646
+ if (existing) continue;
647
+
648
+ // Register the artifact
649
+ const artifact: NocturnalArtifact = {
650
+ artifactId: sample.artifactId,
651
+ sessionId: sample.sessionId,
652
+ principleId: sample.principleId,
653
+ sourceSnapshotRef: sample.sourceSnapshotRef || '',
654
+ badDecision: sample.badDecision || '',
655
+ betterDecision: sample.betterDecision || '',
656
+ rationale: sample.rationale || '',
657
+ createdAt: sample.createdAt || new Date().toISOString(),
658
+ };
659
+
660
+ registerSample(workspaceDir, artifact, samplePath, targetModelFamily);
661
+ newCount++;
662
+ } catch {
663
+ // Skip malformed files
664
+ }
665
+ }
666
+
667
+ return newCount;
668
+ }