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,757 @@
1
+ /**
2
+ * Local Worker Routing Policy — Tests
3
+ * ====================================
4
+ *
5
+ * Tests for task classification and routing decision logic.
6
+ *
7
+ * Test organization:
8
+ * - Without deployment: classification-only tests (reader_eligible, editor_eligible, high_entropy, risk, ambiguous)
9
+ * - With deployment enabled: full route_local decision
10
+ * - With deployment disabled: stay_main with deployment_unavailable
11
+ * - Helper functions: canRouteToProfile, isAnyLocalRoutingEnabled, listEnabledProfiles
12
+ */
13
+
14
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
15
+ import * as fs from 'fs';
16
+ import * as path from 'path';
17
+ import * as os from 'os';
18
+ import {
19
+ classifyTask,
20
+ canRouteToProfile,
21
+ isAnyLocalRoutingEnabled,
22
+ listEnabledProfiles,
23
+ type RoutingInput,
24
+ type RoutingDecision,
25
+ } from '../../src/core/local-worker-routing.js';
26
+ import {
27
+ registerTrainingRun,
28
+ startTrainingRun,
29
+ completeTrainingRun,
30
+ registerCheckpoint,
31
+ attachEvalSummary,
32
+ markCheckpointDeployable,
33
+ } from '../../src/core/model-training-registry.js';
34
+ import {
35
+ advancePromotion,
36
+ DEFAULT_BASELINE_METRICS,
37
+ } from '../../src/core/promotion-gate.js';
38
+ import {
39
+ bindCheckpointToWorkerProfile,
40
+ enableRoutingForProfile,
41
+ disableRoutingForProfile,
42
+ } from '../../src/core/model-deployment-registry.js';
43
+
44
+ // ---------------------------------------------------------------------------
45
+ // Test Fixtures
46
+ // ---------------------------------------------------------------------------
47
+
48
+ function makeTmpDir(): string {
49
+ return fs.mkdtempSync(path.join(os.tmpdir(), 'pd-routing-test-'));
50
+ }
51
+
52
+ function rmdir(dir: string): void {
53
+ try {
54
+ if (fs.existsSync(dir)) {
55
+ fs.rmSync(dir, { recursive: true, force: true });
56
+ }
57
+ } catch {
58
+ // Ignore
59
+ }
60
+ }
61
+
62
+ /** Set up a fully deployable reader-family checkpoint and bind to local-reader */
63
+ function setupReaderDeployment(tmpDir: string, routingEnabled = false): string {
64
+ const run = registerTrainingRun(tmpDir, {
65
+ targetModelFamily: 'claude-reader-latest',
66
+ datasetFingerprint: 'sha256-rdr',
67
+ exportId: 'export-rdr',
68
+ sampleCount: 10,
69
+ configFingerprint: 'cfg-v1',
70
+ });
71
+ const ck = registerCheckpoint(tmpDir, {
72
+ trainRunId: run.trainRunId,
73
+ targetModelFamily: 'claude-reader-latest',
74
+ artifactPath: '/ck/reader.safetensors',
75
+ });
76
+ attachEvalSummary(tmpDir, ck.checkpointId, {
77
+ evalId: 'eval-rdr',
78
+ checkpointId: ck.checkpointId,
79
+ targetModelFamily: 'claude-reader-latest',
80
+ benchmarkId: 'bench',
81
+ mode: 'reduced_prompt',
82
+ baselineScore: 0.5,
83
+ candidateScore: 0.65,
84
+ delta: 0.15,
85
+ verdict: 'pass',
86
+ });
87
+ startTrainingRun(tmpDir, run.trainRunId);
88
+ completeTrainingRun(tmpDir, run.trainRunId);
89
+ markCheckpointDeployable(tmpDir, ck.checkpointId, true);
90
+ advancePromotion(tmpDir, {
91
+ checkpointId: ck.checkpointId,
92
+ targetProfile: 'local-reader',
93
+ baselineMetrics: DEFAULT_BASELINE_METRICS,
94
+ orchestratorReviewPassed: true,
95
+ reviewNote: 'Test approval',
96
+ });
97
+ bindCheckpointToWorkerProfile(tmpDir, 'local-reader', ck.checkpointId, 'reader deployment');
98
+ if (routingEnabled) {
99
+ enableRoutingForProfile(tmpDir, 'local-reader');
100
+ }
101
+ return ck.checkpointId;
102
+ }
103
+
104
+ /** Set up a fully deployable editor-family checkpoint and bind to local-editor */
105
+ function setupEditorDeployment(tmpDir: string, routingEnabled = false): string {
106
+ const run = registerTrainingRun(tmpDir, {
107
+ targetModelFamily: 'gpt-editor-v4',
108
+ datasetFingerprint: 'sha256-edt',
109
+ exportId: 'export-edt',
110
+ sampleCount: 10,
111
+ configFingerprint: 'cfg-v1',
112
+ });
113
+ const ck = registerCheckpoint(tmpDir, {
114
+ trainRunId: run.trainRunId,
115
+ targetModelFamily: 'gpt-editor-v4',
116
+ artifactPath: '/ck/editor.safetensors',
117
+ });
118
+ attachEvalSummary(tmpDir, ck.checkpointId, {
119
+ evalId: 'eval-edt',
120
+ checkpointId: ck.checkpointId,
121
+ targetModelFamily: 'gpt-editor-v4',
122
+ benchmarkId: 'bench',
123
+ mode: 'reduced_prompt',
124
+ baselineScore: 0.5,
125
+ candidateScore: 0.7,
126
+ delta: 0.2,
127
+ verdict: 'pass',
128
+ });
129
+ startTrainingRun(tmpDir, run.trainRunId);
130
+ completeTrainingRun(tmpDir, run.trainRunId);
131
+ markCheckpointDeployable(tmpDir, ck.checkpointId, true);
132
+ advancePromotion(tmpDir, {
133
+ checkpointId: ck.checkpointId,
134
+ targetProfile: 'local-editor',
135
+ baselineMetrics: DEFAULT_BASELINE_METRICS,
136
+ orchestratorReviewPassed: true,
137
+ reviewNote: 'Test approval',
138
+ });
139
+ bindCheckpointToWorkerProfile(tmpDir, 'local-editor', ck.checkpointId, 'editor deployment');
140
+ if (routingEnabled) {
141
+ enableRoutingForProfile(tmpDir, 'local-editor');
142
+ }
143
+ return ck.checkpointId;
144
+ }
145
+
146
+ describe('LocalWorkerRouting reader_eligible classification', () => {
147
+ let tmpDir: string;
148
+ beforeEach(() => { tmpDir = makeTmpDir(); });
149
+ afterEach(() => { rmdir(tmpDir); });
150
+
151
+ it('classifies "read_file" taskIntent as reader_eligible', () => {
152
+ const decision = classifyTask({ taskIntent: 'read_file', taskDescription: 'Read the config file' }, tmpDir);
153
+ expect(decision.classification).toBe('deployment_unavailable'); // eligible but no deployment
154
+ expect(decision.decision).toBe('stay_main'); // No deployment
155
+ expect(decision.deploymentCheck.performed).toBe(true);
156
+ expect(decision.deploymentCheck.routingEnabled).toBe(false);
157
+ });
158
+
159
+ it('classifies "grep" taskIntent as reader_eligible', () => {
160
+ const decision = classifyTask({ taskIntent: 'grep', taskDescription: 'Find all occurrences of foo in src/' }, tmpDir);
161
+ expect(decision.classification).toBe('deployment_unavailable');
162
+ });
163
+
164
+ it('classifies "summarize" taskIntent as reader_eligible', () => {
165
+ const decision = classifyTask({ taskIntent: 'summarize', taskDescription: 'Summarize the changelog' }, tmpDir);
166
+ expect(decision.classification).toBe('deployment_unavailable');
167
+ });
168
+
169
+ it('classifies "inspect" keyword in description as reader_eligible', () => {
170
+ const decision = classifyTask({ taskIntent: 'read', taskDescription: 'inspect the package.json for dependencies' }, tmpDir);
171
+ expect(decision.classification).toBe('deployment_unavailable');
172
+ });
173
+
174
+ it('classifies with only taskIntent (no description) as reader_eligible', () => {
175
+ const decision = classifyTask({ taskIntent: 'grep' }, tmpDir);
176
+ expect(decision.classification).toBe('deployment_unavailable');
177
+ });
178
+ });
179
+
180
+ // ---------------------------------------------------------------------------
181
+ // Tests: Editor-Eligible Classification (no deployment)
182
+ // ---------------------------------------------------------------------------
183
+
184
+ describe('LocalWorkerRouting editor_eligible classification', () => {
185
+ let tmpDir: string;
186
+ beforeEach(() => { tmpDir = makeTmpDir(); });
187
+ afterEach(() => { rmdir(tmpDir); });
188
+
189
+ it('classifies "edit" taskIntent as editor_eligible', () => {
190
+ const decision = classifyTask({ taskIntent: 'edit_file', taskDescription: 'Edit the config to add new key' }, tmpDir);
191
+ expect(decision.classification).toBe('deployment_unavailable'); // eligible but no deployment
192
+ expect(decision.decision).toBe('stay_main'); // No deployment
193
+ });
194
+
195
+ it('classifies "fix" taskIntent as editor_eligible', () => {
196
+ const decision = classifyTask({ taskIntent: 'fix', taskDescription: 'Fix the typo in README.md' }, tmpDir);
197
+ expect(decision.classification).toBe('deployment_unavailable');
198
+ });
199
+
200
+ it('classifies "replace" keyword in description as editor_eligible', () => {
201
+ const decision = classifyTask({ taskIntent: 'replace', taskDescription: 'Replace all old API calls with new ones' }, tmpDir);
202
+ expect(decision.classification).toBe('deployment_unavailable');
203
+ });
204
+
205
+ it('classifies "add" keyword as editor_eligible', () => {
206
+ const decision = classifyTask({ taskIntent: 'add', taskDescription: 'add logging to the function' }, tmpDir);
207
+ expect(decision.classification).toBe('deployment_unavailable');
208
+ });
209
+ });
210
+
211
+ // ---------------------------------------------------------------------------
212
+ // Tests: High-Entropy Rejection
213
+ // ---------------------------------------------------------------------------
214
+
215
+ describe('LocalWorkerRouting high_entropy_disallowed', () => {
216
+ let tmpDir: string;
217
+ beforeEach(() => { tmpDir = makeTmpDir(); });
218
+ afterEach(() => { rmdir(tmpDir); });
219
+
220
+ it('rejects "design" taskIntent as high_entropy', () => {
221
+ const decision = classifyTask({ taskIntent: 'design_system', taskDescription: 'Design the new architecture' }, tmpDir);
222
+ expect(decision.classification).toBe('high_entropy_disallowed');
223
+ expect(decision.decision).toBe('stay_main');
224
+ expect(decision.blockers.length).toBeGreaterThan(0);
225
+ });
226
+
227
+ it('rejects "plan" keyword as high_entropy', () => {
228
+ const decision = classifyTask({ taskIntent: 'plan', taskDescription: 'Plan the refactoring approach' }, tmpDir);
229
+ expect(decision.classification).toBe('high_entropy_disallowed');
230
+ });
231
+
232
+ it('rejects "architect" keyword as high_entropy', () => {
233
+ const decision = classifyTask({ taskIntent: 'architect', taskDescription: 'Architect the microservices layout' }, tmpDir);
234
+ expect(decision.classification).toBe('high_entropy_disallowed');
235
+ });
236
+
237
+ it('rejects "research" keyword as high_entropy', () => {
238
+ const decision = classifyTask({ taskIntent: 'research', taskDescription: 'Research the best approach for this problem' }, tmpDir);
239
+ expect(decision.classification).toBe('high_entropy_disallowed');
240
+ });
241
+
242
+ it('rejects "investigate" keyword as high_entropy', () => {
243
+ const decision = classifyTask({ taskIntent: 'investigate', taskDescription: 'Investigate the memory leak' }, tmpDir);
244
+ // Note: "investigate" is high entropy but "fix" is editor-eligible
245
+ expect(decision.classification).toBe('high_entropy_disallowed');
246
+ });
247
+
248
+ it('rejects complexity hint "multi_step" as high_entropy', () => {
249
+ const decision = classifyTask({
250
+ taskIntent: 'fix',
251
+ taskDescription: 'Fix the bug',
252
+ complexityHints: ['multi_step', 'cross_file'],
253
+ }, tmpDir);
254
+ expect(decision.classification).toBe('high_entropy_disallowed');
255
+ });
256
+
257
+ it('rejects "ambiguous" complexity hint as high_entropy', () => {
258
+ const decision = classifyTask({
259
+ taskIntent: 'fix',
260
+ taskDescription: 'Improve the code',
261
+ complexityHints: ['ambiguous'],
262
+ }, tmpDir);
263
+ expect(decision.classification).toBe('high_entropy_disallowed');
264
+ });
265
+
266
+ it('high_entropy blocks even with editor-eligible keywords', () => {
267
+ // "design" + "edit" → high_entropy wins
268
+ const decision = classifyTask({
269
+ taskIntent: 'edit',
270
+ taskDescription: 'design and edit the new module',
271
+ }, tmpDir);
272
+ expect(decision.classification).toBe('high_entropy_disallowed');
273
+ });
274
+
275
+ it('rejects large-scale multi-file editing (4+ files) as high_entropy', () => {
276
+ // Bounded scope: 1-3 files → editor_eligible
277
+ // Too broad: 4+ files → high_entropy_disallowed (requires main agent coordination)
278
+ const decision = classifyTask({
279
+ taskIntent: 'edit',
280
+ taskDescription: 'Fix the bug across multiple modules',
281
+ requestedFiles: [
282
+ 'src/auth/login.ts',
283
+ 'src/auth/session.ts',
284
+ 'src/auth/middleware.ts',
285
+ 'src/auth/guards.ts',
286
+ ],
287
+ }, tmpDir);
288
+ expect(decision.classification).toBe('high_entropy_disallowed');
289
+ expect(decision.blockers[0]).toContain('large-scale multi-file edit');
290
+ expect(decision.decision).toBe('stay_main');
291
+ });
292
+
293
+ it('allows bounded multi-file editing (1-3 files) as editor_eligible', () => {
294
+ const decision = classifyTask({
295
+ taskIntent: 'edit',
296
+ taskDescription: 'Fix the bug in auth files',
297
+ requestedFiles: [
298
+ 'src/auth/login.ts',
299
+ 'src/auth/session.ts',
300
+ ],
301
+ }, tmpDir);
302
+ // Raw classification is editor_eligible; no deployment → final is deployment_unavailable
303
+ expect(decision.classification).toBe('deployment_unavailable');
304
+ expect(decision.decision).toBe('stay_main'); // no deployment
305
+ });
306
+ });
307
+
308
+ // ---------------------------------------------------------------------------
309
+ // Tests: Risk Disallowed
310
+ // ---------------------------------------------------------------------------
311
+
312
+ describe('LocalWorkerRouting risk_disallowed', () => {
313
+ let tmpDir: string;
314
+ beforeEach(() => { tmpDir = makeTmpDir(); });
315
+ afterEach(() => { rmdir(tmpDir); });
316
+
317
+ it('rejects bash tool as risk', () => {
318
+ const decision = classifyTask({
319
+ taskIntent: 'run',
320
+ taskDescription: 'Execute a bash command',
321
+ requestedTools: ['bash'],
322
+ }, tmpDir);
323
+ expect(decision.classification).toBe('risk_disallowed');
324
+ expect(decision.decision).toBe('stay_main');
325
+ expect(decision.blockers).toContain('risk tool requested (bash/exec/sudo/DROP/DELETE)');
326
+ });
327
+
328
+ it('rejects rm/destroy tools as risk', () => {
329
+ const decision = classifyTask({
330
+ taskIntent: 'process',
331
+ requestedTools: ['rm', 'delete'],
332
+ riskSignals: ['destructive'],
333
+ }, tmpDir);
334
+ expect(decision.classification).toBe('risk_disallowed');
335
+ });
336
+
337
+ it('rejects production file as risk', () => {
338
+ const decision = classifyTask({
339
+ taskIntent: 'edit',
340
+ requestedFiles: ['production-config.yaml', '.env'],
341
+ }, tmpDir);
342
+ expect(decision.classification).toBe('risk_disallowed');
343
+ expect(decision.blockers).toContain('risk file pattern detected (production/secrets/.git/node_modules)');
344
+ });
345
+
346
+ it('rejects .git/config as risk file', () => {
347
+ const decision = classifyTask({
348
+ taskIntent: 'edit',
349
+ requestedFiles: ['.git/config'],
350
+ }, tmpDir);
351
+ expect(decision.classification).toBe('risk_disallowed');
352
+ });
353
+
354
+ it('rejects explicit riskSignals as risk', () => {
355
+ const decision = classifyTask({
356
+ taskIntent: 'edit',
357
+ riskSignals: ['destructive', 'irreversible'],
358
+ }, tmpDir);
359
+ expect(decision.classification).toBe('risk_disallowed');
360
+ expect(decision.blockers).toContain('risk tool requested (bash/exec/sudo/DROP/DELETE)');
361
+ });
362
+
363
+ it('risk blocks even reader-eligible tasks', () => {
364
+ // bash + read → risk wins
365
+ const decision = classifyTask({
366
+ taskIntent: 'grep',
367
+ taskDescription: 'Search for pattern in files',
368
+ requestedTools: ['bash'],
369
+ }, tmpDir);
370
+ expect(decision.classification).toBe('risk_disallowed');
371
+ });
372
+
373
+ it('rejects node_modules as risk file', () => {
374
+ const decision = classifyTask({
375
+ taskIntent: 'edit',
376
+ requestedFiles: ['node_modules/some/package.json'],
377
+ }, tmpDir);
378
+ expect(decision.classification).toBe('risk_disallowed');
379
+ });
380
+ });
381
+
382
+ // ---------------------------------------------------------------------------
383
+ // Tests: Ambiguous Scope
384
+ // ---------------------------------------------------------------------------
385
+
386
+ describe('LocalWorkerRouting ambiguous_scope', () => {
387
+ let tmpDir: string;
388
+ beforeEach(() => { tmpDir = makeTmpDir(); });
389
+ afterEach(() => { rmdir(tmpDir); });
390
+
391
+ it('rejects very short generic taskDescription as ambiguous', () => {
392
+ const decision = classifyTask({ taskIntent: 'process', taskDescription: 'fix it' }, tmpDir);
393
+ expect(decision.classification).toBe('ambiguous_scope');
394
+ });
395
+
396
+ it('rejects "todo" as ambiguous', () => {
397
+ const decision = classifyTask({ taskIntent: 'todo', taskDescription: 'todo' }, tmpDir);
398
+ expect(decision.classification).toBe('ambiguous_scope');
399
+ });
400
+
401
+ it('rejects "improve" as ambiguous', () => {
402
+ const decision = classifyTask({ taskIntent: 'improve', taskDescription: 'improve' }, tmpDir);
403
+ expect(decision.classification).toBe('ambiguous_scope');
404
+ });
405
+
406
+ it('rejects open-ended question words as ambiguous', () => {
407
+ const decision = classifyTask({
408
+ taskIntent: 'analyze',
409
+ taskDescription: 'Should we refactor this or rewrite it?',
410
+ }, tmpDir);
411
+ expect(decision.classification).toBe('ambiguous_scope');
412
+ expect(decision.blockers).toContain('open-ended question words detected');
413
+ });
414
+
415
+ it('rejects when no intent and no description', () => {
416
+ const decision = classifyTask({}, tmpDir);
417
+ expect(decision.classification).toBe('ambiguous_scope');
418
+ });
419
+
420
+ it('does NOT classify a detailed description as ambiguous', () => {
421
+ const decision = classifyTask({
422
+ taskIntent: 'fix',
423
+ taskDescription: 'Fix the null pointer exception thrown when parsing the config file in parseConfig()',
424
+ }, tmpDir);
425
+ // Task is editor_eligible (fix keyword in intent and description)
426
+ // No deployment exists, so final classification is deployment_unavailable
427
+ expect(decision.classification).toBe('deployment_unavailable');
428
+ expect(decision.decision).toBe('stay_main'); // no deployment exists
429
+ });
430
+
431
+ it('DEBUG: detailed fix description classification', () => {
432
+ const decision = classifyTask({
433
+ taskIntent: 'fix',
434
+ taskDescription: 'Fix the null pointer exception thrown when parsing the config file in parseConfig()',
435
+ }, tmpDir);
436
+ // Same as above — editor_eligible but no deployment → deployment_unavailable
437
+ expect(decision.classification).toBe('deployment_unavailable');
438
+ });
439
+ });
440
+
441
+ // ---------------------------------------------------------------------------
442
+ // Tests: Deployment Availability (no deployment at all)
443
+ // ---------------------------------------------------------------------------
444
+
445
+ describe('LocalWorkerRouting deployment_unavailable', () => {
446
+ let tmpDir: string;
447
+ beforeEach(() => { tmpDir = makeTmpDir(); });
448
+ afterEach(() => { rmdir(tmpDir); });
449
+
450
+ it('returns deployment_unavailable when no deployment exists', () => {
451
+ const decision = classifyTask({ taskIntent: 'read_file', taskDescription: 'read the config' }, tmpDir);
452
+ expect(decision.classification).toBe('deployment_unavailable');
453
+ expect(decision.decision).toBe('stay_main');
454
+ expect(decision.deploymentCheck.performed).toBe(true);
455
+ expect(decision.deploymentCheck.routingEnabled).toBe(false);
456
+ });
457
+ });
458
+
459
+ // ---------------------------------------------------------------------------
460
+ // Tests: Routing with Enabled Deployment
461
+ // ---------------------------------------------------------------------------
462
+
463
+ describe('LocalWorkerRouting with enabled deployment', () => {
464
+ let tmpDir: string;
465
+ beforeEach(() => { tmpDir = makeTmpDir(); });
466
+ afterEach(() => { rmdir(tmpDir); });
467
+
468
+ it('reader task routes to local-reader when deployment is enabled', () => {
469
+ setupReaderDeployment(tmpDir, true);
470
+
471
+ const decision = classifyTask({ taskIntent: 'read_file', taskDescription: 'read the config' }, tmpDir);
472
+
473
+ expect(decision.decision).toBe('route_local');
474
+ expect(decision.targetProfile).toBe('local-reader');
475
+ expect(decision.classification).toBe('reader_eligible');
476
+ expect(decision.deploymentCheck.routingEnabled).toBe(true);
477
+ expect(decision.blockers).toHaveLength(0);
478
+ });
479
+
480
+ it('editor task routes to local-editor when deployment is enabled', () => {
481
+ setupEditorDeployment(tmpDir, true);
482
+
483
+ const decision = classifyTask({ taskIntent: 'edit', taskDescription: 'edit the config' }, tmpDir);
484
+
485
+ expect(decision.decision).toBe('route_local');
486
+ expect(decision.targetProfile).toBe('local-editor');
487
+ expect(decision.classification).toBe('editor_eligible');
488
+ expect(decision.blockers).toHaveLength(0);
489
+ });
490
+
491
+ it('reader task still blocked as high_entropy even with enabled deployment', () => {
492
+ setupReaderDeployment(tmpDir, true);
493
+
494
+ const decision = classifyTask({
495
+ taskIntent: 'design',
496
+ taskDescription: 'Design the new system architecture',
497
+ }, tmpDir);
498
+
499
+ expect(decision.decision).toBe('stay_main');
500
+ expect(decision.classification).toBe('high_entropy_disallowed');
501
+ });
502
+
503
+ it('reader task blocked as risk even with enabled deployment', () => {
504
+ setupReaderDeployment(tmpDir, true);
505
+
506
+ const decision = classifyTask({
507
+ taskIntent: 'grep',
508
+ requestedTools: ['bash'],
509
+ }, tmpDir);
510
+
511
+ expect(decision.decision).toBe('stay_main');
512
+ expect(decision.classification).toBe('risk_disallowed');
513
+ });
514
+ });
515
+
516
+ // ---------------------------------------------------------------------------
517
+ // Tests: Routing with Disabled Deployment
518
+ // ---------------------------------------------------------------------------
519
+
520
+ describe('LocalWorkerRouting with disabled deployment (routing=false)', () => {
521
+ let tmpDir: string;
522
+ beforeEach(() => { tmpDir = makeTmpDir(); });
523
+ afterEach(() => { rmdir(tmpDir); });
524
+
525
+ it('reader-eligible task stays_main when routing is disabled', () => {
526
+ setupReaderDeployment(tmpDir, false); // routingEnabled = false
527
+
528
+ const decision = classifyTask({ taskIntent: 'read_file', taskDescription: 'read the config' }, tmpDir);
529
+
530
+ expect(decision.decision).toBe('stay_main');
531
+ expect(decision.classification).toBe('deployment_unavailable');
532
+ expect(decision.deploymentCheck.routingEnabled).toBe(false);
533
+ expect(decision.reason).toContain('routing is not enabled');
534
+ });
535
+
536
+ it('editor-eligible task stays_main when routing is disabled', () => {
537
+ setupEditorDeployment(tmpDir, false);
538
+
539
+ const decision = classifyTask({ taskIntent: 'edit', taskDescription: 'edit the file' }, tmpDir);
540
+
541
+ expect(decision.decision).toBe('stay_main');
542
+ expect(decision.classification).toBe('deployment_unavailable');
543
+ });
544
+
545
+ it('re-enabling routing allows route_local again', () => {
546
+ setupReaderDeployment(tmpDir, false);
547
+ enableRoutingForProfile(tmpDir, 'local-reader');
548
+
549
+ const decision = classifyTask({ taskIntent: 'read_file' }, tmpDir);
550
+ expect(decision.decision).toBe('route_local');
551
+ expect(decision.targetProfile).toBe('local-reader');
552
+ });
553
+
554
+ it('stays_main when active checkpoint has been revoked (no longer deployable)', () => {
555
+ // Set up deployment with routing enabled
556
+ const ckId = setupReaderDeployment(tmpDir, true);
557
+
558
+ // Verify it routes successfully first
559
+ const before = classifyTask({ taskIntent: 'read_file' }, tmpDir);
560
+ expect(before.decision).toBe('route_local');
561
+
562
+ // Revoke the checkpoint — it no longer passes evaluation
563
+ markCheckpointDeployable(tmpDir, ckId, false);
564
+
565
+ // Routing must now be blocked — governance: revoked checkpoints must not be used
566
+ const after = classifyTask({ taskIntent: 'read_file' }, tmpDir);
567
+ expect(after.decision).toBe('stay_main');
568
+ expect(after.classification).toBe('deployment_unavailable');
569
+ expect(after.deploymentCheck.checkpointDeployable).toBe(false);
570
+ expect(after.blockers.some((b: string) => b.includes('no longer deployable'))).toBe(true);
571
+ });
572
+ });
573
+
574
+ // ---------------------------------------------------------------------------
575
+ // Tests: canRouteToProfile helper
576
+ // ---------------------------------------------------------------------------
577
+
578
+ describe('LocalWorkerRouting canRouteToProfile', () => {
579
+ let tmpDir: string;
580
+ beforeEach(() => { tmpDir = makeTmpDir(); });
581
+ afterEach(() => { rmdir(tmpDir); });
582
+
583
+ it('returns true when profile has enabled deployment and task is eligible', () => {
584
+ setupReaderDeployment(tmpDir, true);
585
+
586
+ const result = canRouteToProfile({ taskIntent: 'read_file', taskDescription: 'read config' }, tmpDir, 'local-reader');
587
+ expect(result).toBe(true);
588
+ });
589
+
590
+ it('returns false when no deployment exists', () => {
591
+ const result = canRouteToProfile({ taskIntent: 'read_file' }, tmpDir, 'local-reader');
592
+ expect(result).toBe(false);
593
+ });
594
+
595
+ it('returns false when task is high-entropy', () => {
596
+ setupReaderDeployment(tmpDir, true);
597
+
598
+ const result = canRouteToProfile({ taskIntent: 'design', taskDescription: 'Design the system' }, tmpDir, 'local-reader');
599
+ expect(result).toBe(false);
600
+ });
601
+
602
+ it('returns false when routing is disabled', () => {
603
+ setupReaderDeployment(tmpDir, false);
604
+
605
+ const result = canRouteToProfile({ taskIntent: 'read_file' }, tmpDir, 'local-reader');
606
+ expect(result).toBe(false);
607
+ });
608
+
609
+ it('returns false for editor profile on reader task', () => {
610
+ setupEditorDeployment(tmpDir, true);
611
+
612
+ const result = canRouteToProfile({ taskIntent: 'read_file', taskDescription: 'read config' }, tmpDir, 'local-editor');
613
+ expect(result).toBe(false);
614
+ });
615
+ });
616
+
617
+ // ---------------------------------------------------------------------------
618
+ // Tests: isAnyLocalRoutingEnabled / listEnabledProfiles
619
+ // ---------------------------------------------------------------------------
620
+
621
+ describe('LocalWorkerRouting isAnyLocalRoutingEnabled / listEnabledProfiles', () => {
622
+ let tmpDir: string;
623
+ beforeEach(() => { tmpDir = makeTmpDir(); });
624
+ afterEach(() => { rmdir(tmpDir); });
625
+
626
+ it('returns false when no deployments exist', () => {
627
+ expect(isAnyLocalRoutingEnabled(tmpDir)).toBe(false);
628
+ expect(listEnabledProfiles(tmpDir)).toEqual([]);
629
+ });
630
+
631
+ it('returns false when deployments exist but routing is disabled', () => {
632
+ setupReaderDeployment(tmpDir, false);
633
+ setupEditorDeployment(tmpDir, false);
634
+
635
+ expect(isAnyLocalRoutingEnabled(tmpDir)).toBe(false);
636
+ expect(listEnabledProfiles(tmpDir)).toEqual([]);
637
+ });
638
+
639
+ it('returns true and lists profile when routing is enabled', () => {
640
+ setupReaderDeployment(tmpDir, true);
641
+
642
+ expect(isAnyLocalRoutingEnabled(tmpDir)).toBe(true);
643
+ expect(listEnabledProfiles(tmpDir)).toEqual(['local-reader']);
644
+ });
645
+
646
+ it('lists multiple enabled profiles', () => {
647
+ setupReaderDeployment(tmpDir, true);
648
+ setupEditorDeployment(tmpDir, true);
649
+
650
+ const enabled = listEnabledProfiles(tmpDir);
651
+ expect(enabled).toContain('local-reader');
652
+ expect(enabled).toContain('local-editor');
653
+ expect(enabled).toHaveLength(2);
654
+ });
655
+
656
+ it('only lists profiles with routing enabled (not just bound)', () => {
657
+ setupReaderDeployment(tmpDir, true); // enabled
658
+ setupEditorDeployment(tmpDir, false); // bound but disabled
659
+
660
+ const enabled = listEnabledProfiles(tmpDir);
661
+ expect(enabled).toEqual(['local-reader']);
662
+ });
663
+ });
664
+
665
+ // ---------------------------------------------------------------------------
666
+ // Tests: targetProfile override
667
+ // ---------------------------------------------------------------------------
668
+
669
+ describe('LocalWorkerRouting targetProfile override', () => {
670
+ let tmpDir: string;
671
+ beforeEach(() => { tmpDir = makeTmpDir(); });
672
+ afterEach(() => { rmdir(tmpDir); });
673
+
674
+ it('uses targetProfile from input when specified', () => {
675
+ setupEditorDeployment(tmpDir, true); // Only editor is enabled
676
+
677
+ // Reader deployment doesn't exist but we explicitly target reader
678
+ const decision = classifyTask({
679
+ taskIntent: 'read_file',
680
+ taskDescription: 'read the config',
681
+ targetProfile: 'local-reader',
682
+ }, tmpDir);
683
+
684
+ // Reader deployment doesn't exist → deployment_unavailable
685
+ expect(decision.decision).toBe('stay_main');
686
+ expect(decision.classification).toBe('deployment_unavailable');
687
+ });
688
+
689
+ it('rejects editor task targeting local-reader (profile-task mismatch)', () => {
690
+ // Both deployments enabled
691
+ setupReaderDeployment(tmpDir, true);
692
+ setupEditorDeployment(tmpDir, true);
693
+
694
+ // Editor-eligible task but explicitly targeting local-reader
695
+ const decision = classifyTask({
696
+ taskIntent: 'edit',
697
+ taskDescription: 'Edit the file',
698
+ targetProfile: 'local-reader',
699
+ }, tmpDir);
700
+
701
+ // MUST reject: editor task cannot route to reader profile
702
+ expect(decision.decision).toBe('stay_main');
703
+ expect(decision.classification).toBe('profile_mismatch');
704
+ expect(decision.blockers).toContainEqual(expect.stringContaining('profile mismatch'));
705
+ });
706
+
707
+ it('accepts editor task targeting local-editor (correct profile)', () => {
708
+ setupEditorDeployment(tmpDir, true);
709
+
710
+ const decision = classifyTask({
711
+ taskIntent: 'edit',
712
+ taskDescription: 'Edit the file',
713
+ targetProfile: 'local-editor',
714
+ }, tmpDir);
715
+
716
+ expect(decision.decision).toBe('route_local');
717
+ expect(decision.targetProfile).toBe('local-editor');
718
+ });
719
+ });
720
+
721
+ // ---------------------------------------------------------------------------
722
+ // Tests: Decision Explainability
723
+ // ---------------------------------------------------------------------------
724
+
725
+ describe('LocalWorkerRouting explainability', () => {
726
+ let tmpDir: string;
727
+ beforeEach(() => { tmpDir = makeTmpDir(); });
728
+ afterEach(() => { rmdir(tmpDir); });
729
+
730
+ it('always provides a reason string', () => {
731
+ setupReaderDeployment(tmpDir, true);
732
+
733
+ const decision = classifyTask({ taskIntent: 'read_file' }, tmpDir);
734
+ expect(typeof decision.reason).toBe('string');
735
+ expect(decision.reason.length).toBeGreaterThan(0);
736
+ });
737
+
738
+ it('blockers is empty when route_local', () => {
739
+ setupReaderDeployment(tmpDir, true);
740
+
741
+ const decision = classifyTask({ taskIntent: 'read_file' }, tmpDir);
742
+ expect(decision.blockers).toEqual([]);
743
+ });
744
+
745
+ it('blockers is non-empty when stay_main', () => {
746
+ const decision = classifyTask({ taskIntent: 'design', taskDescription: 'Design the system' }, tmpDir);
747
+ expect(decision.blockers.length).toBeGreaterThan(0);
748
+ });
749
+
750
+ it('provides deployment check details', () => {
751
+ const decision = classifyTask({ taskIntent: 'read_file' }, tmpDir);
752
+ expect(decision.deploymentCheck).toBeDefined();
753
+ expect('performed' in decision.deploymentCheck).toBe(true);
754
+ expect('profileAvailable' in decision.deploymentCheck).toBe(true);
755
+ expect('routingEnabled' in decision.deploymentCheck).toBe(true);
756
+ });
757
+ });