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,919 @@
1
+ import { afterEach, describe, expect, it } from 'vitest';
2
+ import * as fs from 'fs';
3
+ import * as os from 'os';
4
+ import * as path from 'path';
5
+ import { EventLogService } from '../../src/core/event-log.js';
6
+ import { clearSession, trackFriction } from '../../src/core/session-tracker.js';
7
+ import { WorkspaceContext } from '../../src/core/workspace-context.js';
8
+ import { serializeKvLines } from '../../src/utils/io.js';
9
+ import { RuntimeSummaryService } from '../../src/service/runtime-summary-service.js';
10
+
11
+ const tempDirs: string[] = [];
12
+
13
+ function makeWorkspace(): string {
14
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'pd-runtime-summary-'));
15
+ tempDirs.push(dir);
16
+ fs.mkdirSync(path.join(dir, '.state', 'sessions'), { recursive: true });
17
+ fs.mkdirSync(path.join(dir, '.state', 'logs'), { recursive: true });
18
+ return dir;
19
+ }
20
+
21
+ function writeJson(filePath: string, value: unknown): void {
22
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
23
+ fs.writeFileSync(filePath, JSON.stringify(value, null, 2), 'utf8');
24
+ }
25
+
26
+ function writeSession(workspace: string, sessionId: string, payload: Record<string, unknown>): void {
27
+ writeJson(path.join(workspace, '.state', 'sessions', `${sessionId}.json`), {
28
+ sessionId,
29
+ ...payload,
30
+ });
31
+ }
32
+
33
+ function writeEvents(workspace: string, entries: unknown[]): void {
34
+ const filePath = path.join(workspace, '.state', 'logs', 'events.jsonl');
35
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
36
+ const content = entries.map((entry) => JSON.stringify(entry)).join('\n');
37
+ fs.writeFileSync(filePath, content ? `${content}\n` : '', 'utf8');
38
+ }
39
+
40
+ afterEach(() => {
41
+ for (const dir of tempDirs.splice(0)) {
42
+ fs.rmSync(dir, { recursive: true, force: true });
43
+ }
44
+ WorkspaceContext.clearCache();
45
+ clearSession('live-session');
46
+ });
47
+
48
+ describe('RuntimeSummaryService', () => {
49
+ it('builds an active workspace summary from canonical state files', () => {
50
+ const workspace = makeWorkspace();
51
+ writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
52
+ trust_score: 85,
53
+ success_streak: 50,
54
+ last_updated: '2026-03-20T10:00:00Z',
55
+ });
56
+ writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
57
+ { id: '1', status: 'pending', score: 50 },
58
+ { id: '2', status: 'completed', score: 10 },
59
+ ]);
60
+ writeJson(path.join(workspace, '.state', 'evolution_directive.json'), {
61
+ active: true,
62
+ task: 'fix something important',
63
+ timestamp: '2026-03-20T10:00:00Z',
64
+ });
65
+ writeJson(path.join(workspace, '.state', 'pain_candidates.json'), {
66
+ candidates: {
67
+ a: {},
68
+ b: {},
69
+ },
70
+ });
71
+ fs.writeFileSync(
72
+ path.join(workspace, '.state', '.pain_flag'),
73
+ serializeKvLines({
74
+ source: 'tool_failure',
75
+ score: '50',
76
+ time: '2026-03-20T10:00:00Z',
77
+ }),
78
+ 'utf8'
79
+ );
80
+ writeSession(workspace, 's1', {
81
+ currentGfi: 45,
82
+ dailyGfiPeak: 78,
83
+ lastActivityAt: 2,
84
+ });
85
+ writeSession(workspace, 's0', {
86
+ currentGfi: 20,
87
+ dailyGfiPeak: 30,
88
+ lastActivityAt: 1,
89
+ });
90
+ writeEvents(workspace, [
91
+ {
92
+ ts: '2026-03-20T10:00:01Z',
93
+ type: 'pain_signal',
94
+ category: 'detected',
95
+ sessionId: 's1',
96
+ data: { source: 'tool_failure', score: 50, reason: 'write failed' },
97
+ },
98
+ {
99
+ ts: '2026-03-20T10:00:02Z',
100
+ type: 'gate_bypass',
101
+ category: 'bypassed',
102
+ sessionId: 's1',
103
+ data: { toolName: 'write' },
104
+ },
105
+ ]);
106
+
107
+ const summary = RuntimeSummaryService.getSummary(workspace);
108
+
109
+ expect(summary.gfi.current).toBe(45);
110
+ expect(summary.gfi.peak).toBe(78);
111
+ expect(summary.evolution.queue.pending).toBe(1);
112
+ expect(summary.evolution.queue.completed).toBe(1);
113
+ expect(summary.evolution.directive.exists).toBe(false);
114
+ expect(summary.evolution.directive.active).toBeNull();
115
+ expect(summary.evolution.dataQuality).toBe('authoritative');
116
+ expect(summary.pain.activeFlag).toBe(true);
117
+ expect(summary.pain.activeFlagSource).toBe('tool_failure');
118
+ expect(summary.pain.candidates).toBe(2);
119
+ expect(summary.pain.lastSignal?.source).toBe('tool_failure');
120
+ expect(summary.gate.recentBypasses).toBe(1);
121
+ expect(summary.metadata.sessionId).toBe('s1');
122
+ expect(summary.metadata.selectedSessionReason).toBe('latest_active');
123
+ });
124
+
125
+ it('returns partial warnings when canonical files are missing', () => {
126
+ const workspace = makeWorkspace();
127
+ writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
128
+ trust_score: 100,
129
+ });
130
+
131
+ const summary = RuntimeSummaryService.getSummary(workspace);
132
+
133
+ expect(summary.gfi.current).toBeNull();
134
+ expect(summary.evolution.dataQuality).toBe('partial');
135
+ expect(summary.evolution.directive.exists).toBe(false);
136
+ expect(summary.pain.candidates).toBeNull();
137
+ expect(summary.metadata.warnings.join('\n')).toContain('No persisted session state was found');
138
+ expect(summary.metadata.warnings.join('\n')).toContain('partial');
139
+ });
140
+
141
+ it('derives compact phase3 readiness flags from queue inputs', () => {
142
+ const workspace = makeWorkspace();
143
+ writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
144
+ { id: 'task-1', status: 'in_progress' },
145
+ { id: 'task-1', status: 'completed', completed_at: '2026-03-20T10:01:00Z' },
146
+ { id: 'task-2', status: 'completed' },
147
+ ]);
148
+
149
+ const summary = RuntimeSummaryService.getSummary(workspace);
150
+
151
+ expect(summary.phase3.queueTruthReady).toBe(false);
152
+ expect(summary.phase3.phase3ShadowEligible).toBe(false);
153
+ expect(summary.phase3.evolutionRejectedReasons).toEqual(
154
+ expect.arrayContaining([
155
+ 'reused_task_id',
156
+ 'missing_started_at',
157
+ 'missing_completed_at',
158
+ ])
159
+ );
160
+ });
161
+
162
+ it('derives phase3 readiness from valid queue entries', () => {
163
+ const workspace = makeWorkspace();
164
+ writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
165
+ { id: 'task-1', status: 'pending' },
166
+ { id: 'task-2', status: 'in_progress', started_at: '2026-03-20T10:00:00Z' },
167
+ { id: 'task-3', status: 'completed', completed_at: '2026-03-20T10:01:00Z' },
168
+ ]);
169
+
170
+ const summary = RuntimeSummaryService.getSummary(workspace);
171
+
172
+ expect(summary.phase3.queueTruthReady).toBe(true);
173
+ expect(summary.phase3.phase3ShadowEligible).toBe(true);
174
+ });
175
+
176
+ it('prefers the explicit session when provided', () => {
177
+ const workspace = makeWorkspace();
178
+ writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
179
+ trust_score: 30,
180
+ last_updated: '2026-03-20T10:00:00Z',
181
+ });
182
+ writeSession(workspace, 's1', {
183
+ currentGfi: 10,
184
+ dailyGfiPeak: 12,
185
+ lastActivityAt: 10,
186
+ });
187
+ writeSession(workspace, 's2', {
188
+ currentGfi: 70,
189
+ dailyGfiPeak: 80,
190
+ lastActivityAt: 20,
191
+ });
192
+
193
+ const summary = RuntimeSummaryService.getSummary(workspace, { sessionId: 's1' });
194
+
195
+ expect(summary.metadata.sessionId).toBe('s1');
196
+ expect(summary.metadata.selectedSessionReason).toBe('explicit');
197
+ expect(summary.gfi.current).toBe(10);
198
+ expect(summary.gfi.peak).toBe(12);
199
+ });
200
+
201
+ it('prefers the session with the newest control activity timestamp over plain activity', () => {
202
+ const workspace = makeWorkspace();
203
+ writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
204
+ trust_score: 42,
205
+ last_updated: '2026-03-20T10:00:00Z',
206
+ });
207
+ writeSession(workspace, 's1', {
208
+ currentGfi: 11,
209
+ dailyGfiPeak: 20,
210
+ lastActivityAt: 50,
211
+ lastControlActivityAt: 200,
212
+ });
213
+ writeSession(workspace, 's2', {
214
+ currentGfi: 99,
215
+ dailyGfiPeak: 99,
216
+ lastActivityAt: 100,
217
+ lastControlActivityAt: 100,
218
+ });
219
+
220
+ const summary = RuntimeSummaryService.getSummary(workspace);
221
+
222
+ expect(summary.metadata.sessionId).toBe('s1');
223
+ expect(summary.gfi.current).toBe(11);
224
+ });
225
+
226
+ it('warns when a legacy directive is stale relative to an empty queue', () => {
227
+ const workspace = makeWorkspace();
228
+ writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
229
+ trust_score: 100,
230
+ });
231
+ writeJson(path.join(workspace, '.state', 'evolution_queue.json'), []);
232
+ writeJson(path.join(workspace, '.state', 'evolution_directive.json'), {
233
+ active: true,
234
+ task: 'old task',
235
+ timestamp: '2026-03-18T00:00:00Z',
236
+ });
237
+
238
+ const summary = RuntimeSummaryService.getSummary(workspace);
239
+
240
+ expect(summary.evolution.directive.exists).toBe(false);
241
+ expect(summary.evolution.directive.active).toBeNull();
242
+ expect(summary.evolution.dataQuality).toBe('authoritative');
243
+ expect(summary.evolution.directive.ageSeconds).toBeNull();
244
+ expect(summary.metadata.warnings.join('\n')).toContain('Legacy directive file disagrees with queue-derived evolution state');
245
+ });
246
+
247
+ it('derives directive view from queue-only in-progress work and treats it as authoritative without a directive file', () => {
248
+ const workspace = makeWorkspace();
249
+ writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
250
+ trust_score: 55,
251
+ last_updated: '2026-03-20T10:00:00Z',
252
+ });
253
+ writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
254
+ { id: 'task-1', status: 'in_progress', score: 60, task: 'Diagnose systemic pain [ID: task-1]' },
255
+ ]);
256
+
257
+ const summary = RuntimeSummaryService.getSummary(workspace);
258
+
259
+ expect(summary.evolution.queue.inProgress).toBe(1);
260
+ expect(summary.evolution.directive.exists).toBe(true);
261
+ expect(summary.evolution.directive.active).toBe(true);
262
+ expect(summary.evolution.directive.taskPreview).toContain('Diagnose systemic pain [ID: task-1]');
263
+ expect(summary.evolution.dataQuality).toBe('authoritative');
264
+ });
265
+
266
+ it('uses trigger_text_preview when an in-progress queue item is missing task text', () => {
267
+ const workspace = makeWorkspace();
268
+ writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
269
+ trust_score: 55,
270
+ last_updated: '2026-03-20T10:00:00Z',
271
+ });
272
+ writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
273
+ {
274
+ id: 'task-2',
275
+ status: 'in_progress',
276
+ score: 80,
277
+ source: 'tool_failure',
278
+ reason: 'write failed',
279
+ trigger_text_preview: 'Could not find the file to patch',
280
+ },
281
+ ]);
282
+
283
+ const summary = RuntimeSummaryService.getSummary(workspace);
284
+
285
+ expect(summary.evolution.directive.exists).toBe(true);
286
+ expect(summary.evolution.directive.active).toBe(true);
287
+ expect(summary.evolution.directive.taskPreview).toContain('Could not find the file to patch');
288
+ expect(summary.evolution.directive.taskPreview).toContain('Diagnose systemic pain [ID: task-2]');
289
+ });
290
+
291
+ it('skips a malformed top-ranked in-progress task and falls back to the next resolvable task', () => {
292
+ const workspace = makeWorkspace();
293
+ writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
294
+ trust_score: 55,
295
+ last_updated: '2026-03-20T10:00:00Z',
296
+ });
297
+ writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
298
+ {
299
+ status: 'in_progress',
300
+ score: 100,
301
+ task: 'undefined',
302
+ },
303
+ {
304
+ id: 'task-3',
305
+ status: 'in_progress',
306
+ score: 20,
307
+ task: 'Diagnose systemic pain [ID: task-3]',
308
+ },
309
+ ]);
310
+
311
+ const summary = RuntimeSummaryService.getSummary(workspace);
312
+
313
+ expect(summary.evolution.directive.exists).toBe(true);
314
+ expect(summary.evolution.directive.taskPreview).toContain('Diagnose systemic pain [ID: task-3]');
315
+ expect(summary.evolution.directive.taskPreview).not.toContain('undefined');
316
+ });
317
+
318
+ it('warns when a legacy directive disagrees with queue in-progress state', () => {
319
+ const workspace = makeWorkspace();
320
+ writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
321
+ trust_score: 55,
322
+ last_updated: '2026-03-20T10:00:00Z',
323
+ });
324
+ writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
325
+ { id: 'task-1', status: 'in_progress', score: 60, task: 'Diagnose systemic pain [ID: task-1]' },
326
+ ]);
327
+ writeJson(path.join(workspace, '.state', 'evolution_directive.json'), {
328
+ active: false,
329
+ task: 'legacy mismatch task',
330
+ timestamp: '2026-03-20T10:00:00Z',
331
+ });
332
+
333
+ const summary = RuntimeSummaryService.getSummary(workspace);
334
+
335
+ expect(summary.evolution.directive.exists).toBe(true);
336
+ expect(summary.evolution.directive.active).toBe(true);
337
+ expect(summary.metadata.warnings.join('\n')).toContain('Legacy directive file disagrees');
338
+ expect(summary.evolution.dataQuality).toBe('authoritative');
339
+ });
340
+
341
+ it('surfaces observer empathy events as authoritative gfi sources', () => {
342
+ const workspace = makeWorkspace();
343
+ writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
344
+ trust_score: 59,
345
+ last_updated: '2026-03-20T10:00:00Z',
346
+ });
347
+ writeSession(workspace, 's-observer', {
348
+ currentGfi: 25,
349
+ dailyGfiPeak: 25,
350
+ lastActivityAt: 5,
351
+ });
352
+ writeEvents(workspace, [
353
+ {
354
+ ts: '2026-03-20T10:00:03Z',
355
+ type: 'pain_signal',
356
+ category: 'detected',
357
+ sessionId: 's-observer',
358
+ data: {
359
+ source: 'user_empathy',
360
+ origin: 'system_infer',
361
+ severity: 'moderate',
362
+ confidence: 0.8,
363
+ score: 25,
364
+ reason: 'observer caught frustration',
365
+ },
366
+ },
367
+ ]);
368
+
369
+ const summary = RuntimeSummaryService.getSummary(workspace, { sessionId: 's-observer' });
370
+
371
+ expect(summary.gfi.sources).toEqual([
372
+ expect.objectContaining({
373
+ source: 'user_empathy',
374
+ origin: 'system_infer',
375
+ score: 25,
376
+ confidence: 0.8,
377
+ }),
378
+ ]);
379
+ expect(summary.pain.lastSignal?.source).toBe('user_empathy');
380
+ });
381
+
382
+ it('includes in-memory session state and buffered events before flush', () => {
383
+ const workspace = makeWorkspace();
384
+ writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
385
+ trust_score: 59,
386
+ last_updated: '2026-03-20T10:00:00Z',
387
+ });
388
+
389
+ trackFriction('live-session', 18, 'user_empathy_mild', workspace, { source: 'user_empathy' });
390
+ const eventLog = EventLogService.get(path.join(workspace, '.state'));
391
+ eventLog.recordPainSignal('live-session', {
392
+ score: 18,
393
+ source: 'user_empathy',
394
+ reason: 'buffered empathy event',
395
+ origin: 'assistant_self_report',
396
+ severity: 'mild',
397
+ confidence: 1,
398
+ detection_mode: 'structured',
399
+ deduped: false,
400
+ eventId: 'live-emp-1',
401
+ });
402
+
403
+ const summary = RuntimeSummaryService.getSummary(workspace, { sessionId: 'live-session' });
404
+
405
+ expect(summary.metadata.sessionId).toBe('live-session');
406
+ expect(summary.metadata.selectedSessionReason).toBe('explicit');
407
+ expect(summary.gfi.current).toBe(18);
408
+ expect(summary.pain.lastSignal?.reason).toBe('buffered empathy event');
409
+ expect(summary.gfi.sources).toEqual([
410
+ expect.objectContaining({
411
+ source: 'user_empathy',
412
+ score: 18,
413
+ origin: 'assistant_self_report',
414
+ }),
415
+ ]);
416
+ expect(summary.metadata.warnings.join('\n')).not.toContain('Live event buffer is unavailable');
417
+ });
418
+
419
+ it('deduplicates persisted and buffered copies of the same event by eventId', () => {
420
+ const workspace = makeWorkspace();
421
+ writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
422
+ trust_score: 59,
423
+ last_updated: '2026-03-20T10:00:00Z',
424
+ });
425
+ writeSession(workspace, 's-dedupe', {
426
+ currentGfi: 18,
427
+ dailyGfiPeak: 18,
428
+ lastActivityAt: 5,
429
+ lastControlActivityAt: 5,
430
+ });
431
+ writeEvents(workspace, [
432
+ {
433
+ ts: '2026-03-20T10:00:03Z',
434
+ type: 'pain_signal',
435
+ category: 'detected',
436
+ sessionId: 's-dedupe',
437
+ data: {
438
+ source: 'user_empathy',
439
+ origin: 'assistant_self_report',
440
+ severity: 'mild',
441
+ confidence: 1,
442
+ score: 18,
443
+ reason: 'same event',
444
+ eventId: 'dup-1',
445
+ },
446
+ },
447
+ ]);
448
+
449
+ const eventLog = EventLogService.get(path.join(workspace, '.state'));
450
+ eventLog.recordPainSignal('s-dedupe', {
451
+ source: 'user_empathy',
452
+ origin: 'assistant_self_report',
453
+ severity: 'mild',
454
+ confidence: 1,
455
+ score: 18,
456
+ reason: 'same event',
457
+ eventId: 'dup-1',
458
+ detection_mode: 'structured',
459
+ deduped: false,
460
+ });
461
+
462
+ const summary = RuntimeSummaryService.getSummary(workspace, { sessionId: 's-dedupe' });
463
+
464
+ expect(summary.gfi.sources).toHaveLength(1);
465
+ expect(summary.pain.lastSignal?.reason).toBe('same event');
466
+ });
467
+
468
+ it('warns when malformed event lines are skipped', () => {
469
+ const workspace = makeWorkspace();
470
+ writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
471
+ trust_score: 59,
472
+ last_updated: '2026-03-20T10:00:00Z',
473
+ });
474
+ fs.writeFileSync(
475
+ path.join(workspace, '.state', 'logs', 'events.jsonl'),
476
+ [
477
+ JSON.stringify({
478
+ ts: '2026-03-20T10:00:01Z',
479
+ type: 'pain_signal',
480
+ category: 'detected',
481
+ sessionId: 's1',
482
+ data: { source: 'tool_failure', score: 10, reason: 'write failed' },
483
+ }),
484
+ '{not-json}',
485
+ ].join('\n'),
486
+ 'utf8'
487
+ );
488
+
489
+ const summary = RuntimeSummaryService.getSummary(workspace);
490
+
491
+ expect(summary.metadata.warnings.join('\n')).toContain('Skipped 1 malformed event line');
492
+ });
493
+
494
+ // Task 8: Updated phase3 integration tests
495
+ describe('Phase 3 Input Quarantine Integration', () => {
496
+ it('reports legacy queue status as rejection reason in phase3 section', () => {
497
+ const workspace = makeWorkspace();
498
+ writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
499
+ trust_score: 85,
500
+ frozen: true,
501
+ last_updated: '2026-03-20T10:00:00Z',
502
+ });
503
+ writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
504
+ { id: '1afdd4bb', status: 'resolved', started_at: '2026-03-24T15:29:39.710Z', completed_at: '2026-03-24T15:29:39.710Z' }
505
+ ]);
506
+
507
+ const summary = RuntimeSummaryService.getSummary(workspace);
508
+
509
+ expect(summary.phase3.queueTruthReady).toBe(false);
510
+ expect(summary.phase3.evolutionRejectedReasons).toContain('legacy_queue_status');
511
+ });
512
+
513
+ it('reports null status as rejection reason in phase3 section', () => {
514
+ const workspace = makeWorkspace();
515
+ writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
516
+ trust_score: 85,
517
+ frozen: true,
518
+ last_updated: '2026-03-20T10:00:00Z',
519
+ });
520
+ writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
521
+ { id: '6a7c7c48', status: null, started_at: '2026-03-24T15:29:39.710Z' }
522
+ ]);
523
+
524
+ const summary = RuntimeSummaryService.getSummary(workspace);
525
+
526
+ expect(summary.phase3.queueTruthReady).toBe(false);
527
+ expect(summary.phase3.evolutionRejectedReasons).toContain('missing_status');
528
+ });
529
+
530
+ it('reports timeout-only outcomes as referenceOnly (not rejected)', () => {
531
+ const workspace = makeWorkspace();
532
+ writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
533
+ { id: 'e5da4f5c', status: 'completed', resolution: 'auto_completed_timeout', completed_at: '2026-03-24T15:29:39.710Z' }
534
+ ]);
535
+
536
+ const summary = RuntimeSummaryService.getSummary(workspace);
537
+
538
+ // Timeout-only outcomes are valid data (queue is ready)
539
+ // They go to referenceOnly, not rejected
540
+ expect(summary.phase3.queueTruthReady).toBe(true);
541
+ // No rejection reasons from timeout
542
+ expect(summary.phase3.evolutionRejectedReasons).not.toContain('timeout_only_outcome');
543
+ });
544
+
545
+ it('labels directive status as compatibility-only when directive file exists', () => {
546
+ const workspace = makeWorkspace();
547
+ writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
548
+ trust_score: 85,
549
+ frozen: true,
550
+ last_updated: '2026-03-20T10:00:00Z',
551
+ });
552
+ writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
553
+ { id: 'task-1', status: 'completed', completed_at: '2026-03-25T10:00:00.000Z' },
554
+ { id: 'task-2', status: 'in_progress', started_at: '2026-03-25T11:00:00.000Z' }
555
+ ]);
556
+ writeJson(path.join(workspace, '.state', 'evolution_directive.json'), {
557
+ active: true,
558
+ task: 'some task',
559
+ timestamp: '2026-03-20T10:00:00Z',
560
+ });
561
+
562
+ const summary = RuntimeSummaryService.getSummary(workspace);
563
+
564
+ expect(summary.phase3.directiveStatus).toBe('compatibility-only');
565
+ expect(summary.phase3.directiveIgnoredReason).toBe('queue is only truth source');
566
+ });
567
+
568
+ it('reports directive status as missing when directive file does not exist', () => {
569
+ const workspace = makeWorkspace();
570
+ writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
571
+ trust_score: 85,
572
+ frozen: true,
573
+ last_updated: '2026-03-20T10:00:00Z',
574
+ });
575
+ writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
576
+ { id: 'task-1', status: 'completed', completed_at: '2026-03-25T10:00:00.000Z' }
577
+ ]);
578
+ // No evolution_directive.json file
579
+
580
+ const summary = RuntimeSummaryService.getSummary(workspace);
581
+
582
+ expect(summary.phase3.directiveStatus).toBe('missing');
583
+ expect(summary.phase3.directiveIgnoredReason).toBe('queue is only truth source');
584
+ });
585
+
586
+ it('reports directive as compatibility-only even when stale', () => {
587
+ // Production scenario: directive stopped updating on 2026-03-22
588
+ // Should still be labeled as compatibility-only, not 'stale'
589
+ const workspace = makeWorkspace();
590
+ writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
591
+ trust_score: 85,
592
+ frozen: true,
593
+ last_updated: '2026-03-20T10:00:00Z',
594
+ });
595
+ writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
596
+ { id: 'task-1', status: 'completed', completed_at: '2026-03-25T10:00:00.000Z' }
597
+ ]);
598
+ writeJson(path.join(workspace, '.state', 'evolution_directive.json'), {
599
+ active: true,
600
+ task: 'old task',
601
+ timestamp: '2026-03-22T00:00:00Z', // Stale timestamp
602
+ });
603
+
604
+ const summary = RuntimeSummaryService.getSummary(workspace);
605
+
606
+ expect(summary.phase3.directiveStatus).toBe('compatibility-only');
607
+ expect(summary.phase3.directiveIgnoredReason).toBe('queue is only truth source');
608
+ });
609
+
610
+ it('labels directive as compatibility-only even when active flag is false', () => {
611
+ // Directive presence matters for labeling, not the active flag value
612
+ const workspace = makeWorkspace();
613
+ writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
614
+ trust_score: 85,
615
+ frozen: true,
616
+ last_updated: '2026-03-20T10:00:00Z',
617
+ });
618
+ writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
619
+ { id: 'task-1', status: 'completed', completed_at: '2026-03-25T10:00:00.000Z' }
620
+ ]);
621
+ writeJson(path.join(workspace, '.state', 'evolution_directive.json'), {
622
+ active: false, // inactive directive
623
+ task: 'inactive task',
624
+ timestamp: '2026-03-20T10:00:00Z',
625
+ });
626
+
627
+ const summary = RuntimeSummaryService.getSummary(workspace);
628
+
629
+ expect(summary.phase3.directiveStatus).toBe('compatibility-only');
630
+ expect(summary.phase3.directiveIgnoredReason).toBe('queue is only truth source');
631
+ });
632
+
633
+ it('labels directive as compatibility-only even with minimal/empty fields', () => {
634
+ // Directive file exists with minimal content should still be labeled
635
+ const workspace = makeWorkspace();
636
+ writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
637
+ trust_score: 85,
638
+ frozen: true,
639
+ last_updated: '2026-03-20T10:00:00Z',
640
+ });
641
+ writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
642
+ { id: 'task-1', status: 'completed', completed_at: '2026-03-25T10:00:00.000Z' }
643
+ ]);
644
+ writeJson(path.join(workspace, '.state', 'evolution_directive.json'), {
645
+ // Minimal directive with no task, timestamp, or active fields
646
+ });
647
+
648
+ const summary = RuntimeSummaryService.getSummary(workspace);
649
+
650
+ expect(summary.phase3.directiveStatus).toBe('compatibility-only');
651
+ expect(summary.phase3.directiveIgnoredReason).toBe('queue is only truth source');
652
+ });
653
+ });
654
+
655
+ // Task 8: TDD tests for Runtime vs Analytics Separation (RED phase)
656
+ describe('Runtime vs Analytics Separation', () => {
657
+ it('populates runtime truth section with queue and sessions', () => {
658
+ const workspace = makeWorkspace();
659
+ writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
660
+ { id: 'task-1', status: 'pending' },
661
+ { id: 'task-2', status: 'in_progress' },
662
+ { id: 'task-3', status: 'completed' },
663
+ ]);
664
+
665
+ const summary = RuntimeSummaryService.getSummary(workspace);
666
+
667
+ expect(summary.runtime.queueState.total).toBe(3);
668
+ expect(summary.runtime.queueState.pending).toBe(1);
669
+ expect(summary.runtime.queueState.inProgress).toBe(1);
670
+ expect(summary.runtime.queueState.completed).toBe(1);
671
+ expect(summary.runtime.activeSessions).toEqual(expect.any(Array));
672
+ });
673
+
674
+ it('populates analytics truth section with trajectory, daily stats, trends', () => {
675
+ const workspace = makeWorkspace();
676
+ writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
677
+ trust_score: 75,
678
+ last_updated: '2026-03-20T10:00:00Z',
679
+ });
680
+ writeJson(path.join(workspace, '.state', 'logs', 'daily-stats.json'), {
681
+ '2026-03-20': {
682
+ toolCalls: 120,
683
+ painSignals: 15,
684
+ evolutionTasks: 5,
685
+ },
686
+ });
687
+
688
+ const summary = RuntimeSummaryService.getSummary(workspace);
689
+
690
+ // These properties don't exist yet - tests will FAIL (RED phase)
691
+ expect(summary.analytics.trajectoryData).toBeDefined();
692
+ expect(summary.analytics.dailyStats.toolCalls).toBe(120);
693
+ expect(summary.analytics.dailyStats.painSignals).toBe(15);
694
+ expect(summary.analytics.trends).toBeDefined();
695
+ });
696
+
697
+ it('calculates Phase 3 readiness from runtime truth only', () => {
698
+ const workspace = makeWorkspace();
699
+ writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
700
+ { id: 'task-1', status: 'completed', completed_at: '2026-03-25T10:00:00.000Z' },
701
+ { id: 'task-2', status: 'pending' },
702
+ ]);
703
+
704
+ const summary = RuntimeSummaryService.getSummary(workspace);
705
+
706
+ // Runtime truth drives eligibility
707
+ expect(summary.phase3.queueTruthReady).toBe(true);
708
+ expect(summary.phase3.phase3ShadowEligible).toBe(true);
709
+
710
+ // Phase 3 eligibility source should be runtime truth
711
+ expect(summary.phase3.eligibilitySource).toBe('runtime_truth');
712
+ });
713
+
714
+ it('does not use analytics for Phase 3 eligibility', () => {
715
+ const workspace = makeWorkspace();
716
+ writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
717
+ trust_score: 85,
718
+ frozen: true,
719
+ last_updated: '2026-03-20T10:00:00Z',
720
+ });
721
+ // Invalid runtime queue - no valid entries
722
+ writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
723
+ { id: 'task-1', status: 'resolved' }, // legacy status - invalid
724
+ ]);
725
+
726
+ const summary = RuntimeSummaryService.getSummary(workspace);
727
+
728
+ // Runtime truth invalid → not eligible, regardless of analytics
729
+ expect(summary.phase3.queueTruthReady).toBe(false);
730
+ expect(summary.phase3.phase3ShadowEligible).toBe(false);
731
+
732
+ // Analytics data exists but is ignored for eligibility
733
+ expect(summary.analytics).toBeDefined();
734
+ });
735
+
736
+ it('separates queue into runtime truth section only', () => {
737
+ const workspace = makeWorkspace();
738
+ writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
739
+ trust_score: 75,
740
+ last_updated: '2026-03-20T10:00:00Z',
741
+ });
742
+ writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
743
+ { id: 'task-1', status: 'pending', score: 50 },
744
+ { id: 'task-2', status: 'in_progress', score: 60 },
745
+ { id: 'task-3', status: 'completed', score: 10 },
746
+ ]);
747
+
748
+ const summary = RuntimeSummaryService.getSummary(workspace);
749
+
750
+ // Queue data belongs to runtime truth
751
+ expect(summary.runtime.queueState.total).toBe(3);
752
+ expect(summary.runtime.queueState.pending).toBe(1);
753
+ expect(summary.runtime.queueState.inProgress).toBe(1);
754
+ expect(summary.runtime.queueState.completed).toBe(1);
755
+ expect(summary.runtime.queueState.lastUpdated).toBeDefined();
756
+
757
+ // Analytics section should NOT have queue data
758
+ expect(summary.analytics.trajectoryData).not.toHaveProperty('queue');
759
+ });
760
+
761
+ it('surfaces reference-only Phase 3 evidence in the runtime summary', () => {
762
+ const workspace = makeWorkspace();
763
+ writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
764
+ trust_score: 85,
765
+ frozen: true,
766
+ last_updated: '2026-03-20T10:00:00Z',
767
+ });
768
+ writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
769
+ { id: 'timeout-1', status: 'completed', resolution: 'auto_completed_timeout', completed_at: '2026-03-24T15:29:39.710Z' },
770
+ ]);
771
+
772
+ const summary = RuntimeSummaryService.getSummary(workspace);
773
+
774
+ expect(summary.phase3.queueTruthReady).toBe(true);
775
+ expect(summary.phase3.evolutionReferenceOnly).toBe(1);
776
+ expect(summary.phase3.evolutionReferenceOnlyReasons).toContain('timeout_only');
777
+ expect(summary.phase3.evolutionRejected).toBe(0);
778
+ expect(summary.phase3.phase3ShadowEligible).toBe(false);
779
+ });
780
+
781
+ it('includes lastUpdated timestamps in runtime truth section', () => {
782
+ const workspace = makeWorkspace();
783
+ writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
784
+ { id: 'task-1', status: 'pending' },
785
+ ]);
786
+
787
+ const summary = RuntimeSummaryService.getSummary(workspace);
788
+
789
+ // Runtime truth section includes timing metadata
790
+ expect(summary.runtime.queueState.lastUpdated).toBeDefined();
791
+ });
792
+
793
+ it('populates activeSessions from session tracker in runtime truth', () => {
794
+ const workspace = makeWorkspace();
795
+ writeJson(path.join(workspace, '.state', 'AGENT_SCORECARD.json'), {
796
+ trust_score: 75,
797
+ last_updated: '2026-03-20T10:00:00Z',
798
+ });
799
+ writeSession(workspace, 'session-1', {
800
+ currentGfi: 10,
801
+ lastActivityAt: 100,
802
+ });
803
+ writeSession(workspace, 'session-2', {
804
+ currentGfi: 20,
805
+ lastActivityAt: 200,
806
+ });
807
+
808
+ const summary = RuntimeSummaryService.getSummary(workspace);
809
+
810
+ // Active sessions belong to runtime truth
811
+ expect(summary.runtime.activeSessions).toEqual(expect.any(Array));
812
+ expect(summary.runtime.activeSessions).toContain('session-1');
813
+ expect(summary.runtime.activeSessions).toContain('session-2');
814
+
815
+ // Analytics section should NOT have sessions
816
+ expect(summary.analytics).not.toHaveProperty('sessions');
817
+ });
818
+ });
819
+
820
+ // ═══════════════════════════════════════════════════════════════════════════
821
+ // Task 3: Directive File Does Not Affect Phase 3 Eligibility
822
+ // ═══════════════════════════════════════════════════════════════════════════
823
+ describe('Directive File Is Compatibility-Only Display Artifact', () => {
824
+ /**
825
+ * PURPOSE: Prove that EVOLUTION_DIRECTIVE.json does NOT affect Phase 3 eligibility.
826
+ * Directive is a compatibility-only display artifact, not a truth source.
827
+ * Phase 3 eligibility depends ONLY on queue.
828
+ */
829
+
830
+ it('Phase 3 eligibility is same whether directive exists or not', () => {
831
+ const workspace = makeWorkspace();
832
+
833
+ // Valid queue for Phase 3
834
+ writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
835
+ { id: 'task-1', status: 'pending' },
836
+ ]);
837
+
838
+ // Get summary WITHOUT directive file
839
+ const summaryWithoutDirective = RuntimeSummaryService.getSummary(workspace);
840
+
841
+ // Now add directive file with conflicting content
842
+ writeJson(path.join(workspace, '.state', 'evolution_directive.json'), {
843
+ active: false, // Contradicts queue
844
+ task: 'completely different task',
845
+ taskId: 'different-id',
846
+ timestamp: '2026-03-15T10:00:00Z', // Old timestamp
847
+ });
848
+
849
+ const summaryWithDirective = RuntimeSummaryService.getSummary(workspace);
850
+
851
+ // Phase 3 eligibility should be IDENTICAL regardless of directive
852
+ expect(summaryWithDirective.phase3.phase3ShadowEligible).toBe(
853
+ summaryWithoutDirective.phase3.phase3ShadowEligible
854
+ );
855
+ expect(summaryWithDirective.phase3.queueTruthReady).toBe(
856
+ summaryWithoutDirective.phase3.queueTruthReady
857
+ );
858
+
859
+ // Both should be eligible because queue is valid
860
+ expect(summaryWithDirective.phase3.phase3ShadowEligible).toBe(true);
861
+ });
862
+
863
+ it('Phase 3 eligibility is false when queue invalid, regardless of directive', () => {
864
+ const workspace = makeWorkspace();
865
+
866
+ // Invalid queue (legacy status)
867
+ writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
868
+ { id: 'task-1', status: 'resolved' }, // Legacy status
869
+ ]);
870
+
871
+ // Add directive claiming everything is fine
872
+ writeJson(path.join(workspace, '.state', 'evolution_directive.json'), {
873
+ active: true,
874
+ task: 'valid active task',
875
+ taskId: 'task-1',
876
+ timestamp: new Date().toISOString(),
877
+ });
878
+
879
+ const summary = RuntimeSummaryService.getSummary(workspace);
880
+
881
+ // Phase 3 eligibility should be false despite directive claiming active
882
+ expect(summary.phase3.phase3ShadowEligible).toBe(false);
883
+ expect(summary.phase3.queueTruthReady).toBe(false);
884
+ });
885
+
886
+ it('directive summary shows queue-derived values, not file content', () => {
887
+ const workspace = makeWorkspace();
888
+
889
+ // Queue has in_progress task
890
+ writeJson(path.join(workspace, '.state', 'evolution_queue.json'), [
891
+ {
892
+ id: 'queue-task-123',
893
+ status: 'in_progress',
894
+ task: 'task from queue',
895
+ started_at: '2026-03-24T10:00:00Z',
896
+ },
897
+ ]);
898
+
899
+ // Directive has completely different content (stale/mismatch)
900
+ writeJson(path.join(workspace, '.state', 'evolution_directive.json'), {
901
+ active: true,
902
+ task: 'stale task from directive file',
903
+ taskId: 'directive-task-999',
904
+ timestamp: '2026-03-15T10:00:00Z',
905
+ });
906
+
907
+ const summary = RuntimeSummaryService.getSummary(workspace);
908
+
909
+ // Directive summary should show queue-derived values
910
+ // The directive taskPreview should come from queue, not directive file
911
+ expect(summary.evolution.directive.exists).toBe(true);
912
+ expect(summary.evolution.directive.active).toBe(true);
913
+
914
+ // The directiveStatus should indicate it's compatibility-only
915
+ expect(summary.phase3.directiveStatus).toBe('compatibility-only');
916
+ expect(summary.phase3.directiveIgnoredReason).toBe('queue is only truth source');
917
+ });
918
+ });
919
+ });