create-principles-disciple 1.144.27 → 1.145.1

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 (347) hide show
  1. package/LICENSE +73 -21
  2. package/NOTICE +2 -0
  3. package/README.md +1 -1
  4. package/_release/product-identity.json +2 -2
  5. package/codex-adapter/dist/index.d.ts +2 -2
  6. package/codex-adapter/dist/index.js +1 -1
  7. package/codex-adapter/dist/ingestion/admission.js +16 -0
  8. package/codex-adapter/dist/ingestion/codex-version.d.ts +10 -4
  9. package/codex-adapter/dist/ingestion/codex-version.js +10 -4
  10. package/codex-adapter/dist/ingestion/explicit-owner-statement.d.ts +48 -0
  11. package/codex-adapter/dist/ingestion/explicit-owner-statement.js +123 -0
  12. package/codex-adapter/dist/ingestion/ingestion.js +50 -1
  13. package/codex-adapter/dist/pd-hook.js +66 -3
  14. package/codex-adapter/dist/worker/workspace-worker.d.ts +14 -0
  15. package/codex-adapter/dist/worker/workspace-worker.js +194 -11
  16. package/codex-adapter/package.json +6 -4
  17. package/console/dist/server/models/ApprovalsConsoleModel.d.ts +2 -2
  18. package/console/dist/server/models/ApprovalsConsoleModel.js +1 -1
  19. package/console/dist/server/models/ApprovalsGroupedConsoleModel.js +1 -1
  20. package/console/dist/server/models/PrinciplesConsoleModel.d.ts +26 -1
  21. package/console/dist/server/models/PrinciplesConsoleModel.js +18 -1
  22. package/console/dist/server/routes/activations.js +4 -4
  23. package/console/dist/server/routes/approvals.js +2 -2
  24. package/console/dist/server/routes/failed-tasks.d.ts +9 -0
  25. package/console/dist/server/routes/failed-tasks.js +116 -7
  26. package/console/dist/server/routes/principles.js +73 -1
  27. package/console/dist/shared/prompt-injection-contract.d.ts +1 -1
  28. package/console/dist/ui/api.d.ts +5 -5
  29. package/console/dist/ui/i18n/en.json +13 -2
  30. package/console/dist/ui/i18n/zh-CN.json +13 -2
  31. package/console/dist/ui/pages/activation/ActivationPage.js +2 -1
  32. package/console/dist/ui/pages/failed-tasks/FailedTasksPage.js +42 -4
  33. package/console/dist/ui/pages/focus/FocusPage.js +3 -3
  34. package/console/dist/ui/pages/principles/PrinciplesPage.js +7 -2
  35. package/console/dist/ui/utils/validators.d.ts +6 -2
  36. package/console/dist/ui/utils/validators.js +19 -4
  37. package/console/dist/web/assets/app.css +26 -0
  38. package/console/dist/web/assets/app.js +24 -24
  39. package/console/package.json +6 -5
  40. package/core/dist/host/host-installer.d.ts +3 -1
  41. package/core/dist/host/host-installer.d.ts.map +1 -1
  42. package/core/dist/host/host-installer.js.map +1 -1
  43. package/core/dist/runtime-v2/admission-gate.d.ts +8 -0
  44. package/core/dist/runtime-v2/admission-gate.d.ts.map +1 -1
  45. package/core/dist/runtime-v2/admission-gate.js +3 -2
  46. package/core/dist/runtime-v2/admission-gate.js.map +1 -1
  47. package/core/dist/runtime-v2/config/pd-config-defaults.d.ts.map +1 -1
  48. package/core/dist/runtime-v2/config/pd-config-defaults.js +16 -1
  49. package/core/dist/runtime-v2/config/pd-config-defaults.js.map +1 -1
  50. package/core/dist/runtime-v2/context-payload.d.ts +43 -3
  51. package/core/dist/runtime-v2/context-payload.d.ts.map +1 -1
  52. package/core/dist/runtime-v2/context-payload.js +15 -1
  53. package/core/dist/runtime-v2/context-payload.js.map +1 -1
  54. package/core/dist/runtime-v2/evidence-guards.d.ts +6 -0
  55. package/core/dist/runtime-v2/evidence-guards.d.ts.map +1 -1
  56. package/core/dist/runtime-v2/evidence-guards.js +11 -0
  57. package/core/dist/runtime-v2/evidence-guards.js.map +1 -1
  58. package/core/dist/runtime-v2/evolution/evolution-types.d.ts +4 -4
  59. package/core/dist/runtime-v2/evolution/evolution-types.d.ts.map +1 -1
  60. package/core/dist/runtime-v2/evolution/evolution-types.js +1 -1
  61. package/core/dist/runtime-v2/evolution/evolution-types.js.map +1 -1
  62. package/core/dist/runtime-v2/feature-flags/feature-flag-contract.d.ts.map +1 -1
  63. package/core/dist/runtime-v2/feature-flags/feature-flag-contract.js +6 -0
  64. package/core/dist/runtime-v2/feature-flags/feature-flag-contract.js.map +1 -1
  65. package/core/dist/runtime-v2/feature-flags/feature-flag-lifecycle.d.ts.map +1 -1
  66. package/core/dist/runtime-v2/feature-flags/feature-flag-lifecycle.js +8 -0
  67. package/core/dist/runtime-v2/feature-flags/feature-flag-lifecycle.js.map +1 -1
  68. package/core/dist/runtime-v2/index.d.ts +5 -2
  69. package/core/dist/runtime-v2/index.d.ts.map +1 -1
  70. package/core/dist/runtime-v2/index.js +3 -1
  71. package/core/dist/runtime-v2/index.js.map +1 -1
  72. package/core/dist/runtime-v2/internalization/evaluator-output.d.ts +11 -0
  73. package/core/dist/runtime-v2/internalization/evaluator-output.d.ts.map +1 -1
  74. package/core/dist/runtime-v2/internalization/evaluator-output.js +10 -0
  75. package/core/dist/runtime-v2/internalization/evaluator-output.js.map +1 -1
  76. package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.d.ts +3 -3
  77. package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.d.ts.map +1 -1
  78. package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.js +49 -5
  79. package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.js.map +1 -1
  80. package/core/dist/runtime-v2/internalization/evaluator-runner.d.ts.map +1 -1
  81. package/core/dist/runtime-v2/internalization/evaluator-runner.js +13 -16
  82. package/core/dist/runtime-v2/internalization/evaluator-runner.js.map +1 -1
  83. package/core/dist/runtime-v2/internalization/owner-decision-review.d.ts.map +1 -1
  84. package/core/dist/runtime-v2/internalization/owner-decision-review.js +5 -1
  85. package/core/dist/runtime-v2/internalization/owner-decision-review.js.map +1 -1
  86. package/core/dist/runtime-v2/internalization/owner-review.d.ts +2 -2
  87. package/core/dist/runtime-v2/internalization/owner-review.js +2 -2
  88. package/core/dist/runtime-v2/internalization/scribe-output.d.ts +12 -0
  89. package/core/dist/runtime-v2/internalization/scribe-output.d.ts.map +1 -1
  90. package/core/dist/runtime-v2/internalization/scribe-output.js +20 -0
  91. package/core/dist/runtime-v2/internalization/scribe-output.js.map +1 -1
  92. package/core/dist/runtime-v2/internalization/scribe-prompt-builder.d.ts +32 -1
  93. package/core/dist/runtime-v2/internalization/scribe-prompt-builder.d.ts.map +1 -1
  94. package/core/dist/runtime-v2/internalization/scribe-prompt-builder.js +38 -1
  95. package/core/dist/runtime-v2/internalization/scribe-prompt-builder.js.map +1 -1
  96. package/core/dist/runtime-v2/internalization-queue-read-model.d.ts +6 -0
  97. package/core/dist/runtime-v2/internalization-queue-read-model.d.ts.map +1 -1
  98. package/core/dist/runtime-v2/internalization-queue-read-model.js +8 -1
  99. package/core/dist/runtime-v2/internalization-queue-read-model.js.map +1 -1
  100. package/core/dist/runtime-v2/intervention-evidence-normalizer.d.ts.map +1 -1
  101. package/core/dist/runtime-v2/intervention-evidence-normalizer.js +10 -3
  102. package/core/dist/runtime-v2/intervention-evidence-normalizer.js.map +1 -1
  103. package/core/dist/runtime-v2/pain-ingress.d.ts +1 -1
  104. package/core/dist/runtime-v2/pain-ingress.d.ts.map +1 -1
  105. package/core/dist/runtime-v2/pain-signal-bridge.d.ts +68 -2
  106. package/core/dist/runtime-v2/pain-signal-bridge.d.ts.map +1 -1
  107. package/core/dist/runtime-v2/pain-signal-bridge.js +541 -18
  108. package/core/dist/runtime-v2/pain-signal-bridge.js.map +1 -1
  109. package/core/dist/runtime-v2/pain-signal-runtime-factory.d.ts.map +1 -1
  110. package/core/dist/runtime-v2/pain-signal-runtime-factory.js +4 -0
  111. package/core/dist/runtime-v2/pain-signal-runtime-factory.js.map +1 -1
  112. package/core/dist/runtime-v2/product-telemetry/snapshot-contract.d.ts +2 -2
  113. package/core/dist/runtime-v2/product-telemetry/snapshot-contract.d.ts.map +1 -1
  114. package/core/dist/runtime-v2/product-telemetry/snapshot-contract.js +2 -2
  115. package/core/dist/runtime-v2/product-telemetry/snapshot-contract.js.map +1 -1
  116. package/core/dist/runtime-v2/store/candidate/admission-document.d.ts +90 -0
  117. package/core/dist/runtime-v2/store/candidate/admission-document.d.ts.map +1 -0
  118. package/core/dist/runtime-v2/store/candidate/admission-document.js +142 -0
  119. package/core/dist/runtime-v2/store/candidate/admission-document.js.map +1 -0
  120. package/core/dist/runtime-v2/store/candidate/candidate-store.d.ts +53 -0
  121. package/core/dist/runtime-v2/store/candidate/candidate-store.d.ts.map +1 -1
  122. package/core/dist/runtime-v2/store/candidate/memory-candidate-store.d.ts +35 -0
  123. package/core/dist/runtime-v2/store/candidate/memory-candidate-store.d.ts.map +1 -1
  124. package/core/dist/runtime-v2/store/candidate/memory-candidate-store.js +48 -0
  125. package/core/dist/runtime-v2/store/candidate/memory-candidate-store.js.map +1 -1
  126. package/core/dist/runtime-v2/store/candidate/sqlite-candidate-store.d.ts +44 -0
  127. package/core/dist/runtime-v2/store/candidate/sqlite-candidate-store.d.ts.map +1 -1
  128. package/core/dist/runtime-v2/store/candidate/sqlite-candidate-store.js +136 -0
  129. package/core/dist/runtime-v2/store/candidate/sqlite-candidate-store.js.map +1 -1
  130. package/core/dist/runtime-v2/store/context/sqlite-context-assembler.js.map +1 -1
  131. package/core/dist/runtime-v2/store/intervention-evidence-schema.js +1 -1
  132. package/core/dist/runtime-v2/store/runtime-state-manager.d.ts +65 -0
  133. package/core/dist/runtime-v2/store/runtime-state-manager.d.ts.map +1 -1
  134. package/core/dist/runtime-v2/store/runtime-state-manager.js +46 -0
  135. package/core/dist/runtime-v2/store/runtime-state-manager.js.map +1 -1
  136. package/core/dist/runtime-v2/store/sqlite-connection.d.ts.map +1 -1
  137. package/core/dist/runtime-v2/store/sqlite-connection.js +42 -0
  138. package/core/dist/runtime-v2/store/sqlite-connection.js.map +1 -1
  139. package/core/dist/runtime-v2/store/sqlite-intervention-evidence-store.d.ts +1 -1
  140. package/core/dist/runtime-v2/store/sqlite-intervention-evidence-store.d.ts.map +1 -1
  141. package/core/dist/runtime-v2/store/sqlite-intervention-evidence-store.js.map +1 -1
  142. package/core/dist/runtime-v2/store/task/sqlite-family-reopen.d.ts +19 -0
  143. package/core/dist/runtime-v2/store/task/sqlite-family-reopen.d.ts.map +1 -0
  144. package/core/dist/runtime-v2/store/task/sqlite-family-reopen.js +36 -0
  145. package/core/dist/runtime-v2/store/task/sqlite-family-reopen.js.map +1 -0
  146. package/core/dist/runtime-v2/store/task/sqlite-task-store.d.ts +23 -0
  147. package/core/dist/runtime-v2/store/task/sqlite-task-store.d.ts.map +1 -1
  148. package/core/dist/runtime-v2/store/task/sqlite-task-store.js +50 -0
  149. package/core/dist/runtime-v2/store/task/sqlite-task-store.js.map +1 -1
  150. package/core/dist/runtime-v2/task-status.d.ts +3 -1
  151. package/core/dist/runtime-v2/task-status.d.ts.map +1 -1
  152. package/core/dist/runtime-v2/task-status.js +1 -1
  153. package/core/dist/runtime-v2/task-status.js.map +1 -1
  154. package/core/dist/runtime-v2/trajectory-schema.d.ts.map +1 -1
  155. package/core/dist/runtime-v2/trajectory-schema.js +11 -0
  156. package/core/dist/runtime-v2/trajectory-schema.js.map +1 -1
  157. package/core/dist/runtime-v2/types/event-types.d.ts +2 -2
  158. package/core/dist/runtime-v2/types/event-types.d.ts.map +1 -1
  159. package/core/dist/runtime-v2/types/event-types.js +1 -1
  160. package/core/dist/runtime-v2/types/event-types.js.map +1 -1
  161. package/core/dist/runtime-v2/types/evidence-chain-contract.d.ts +1 -1
  162. package/core/dist/runtime-v2/types/evidence-chain-contract.d.ts.map +1 -1
  163. package/core/dist/runtime-v2/types/evidence-chain-contract.js +2 -2
  164. package/core/dist/runtime-v2/types/evidence-chain-contract.js.map +1 -1
  165. package/core/dist/runtime-v2/types/intervention-evidence-contract.d.ts +3 -3
  166. package/core/dist/runtime-v2/types/intervention-evidence-contract.d.ts.map +1 -1
  167. package/core/dist/runtime-v2/types/intervention-evidence-contract.js +1 -0
  168. package/core/dist/runtime-v2/types/intervention-evidence-contract.js.map +1 -1
  169. package/core/dist/telemetry-event.d.ts +2 -2
  170. package/core/dist/telemetry-event.d.ts.map +1 -1
  171. package/core/dist/telemetry-event.js +4 -0
  172. package/core/dist/telemetry-event.js.map +1 -1
  173. package/core/dist/trajectory-types.d.ts +2 -2
  174. package/core/dist/trajectory-types.d.ts.map +1 -1
  175. package/core/package.json +7 -5
  176. package/dist/index.d.ts.map +1 -1
  177. package/dist/index.js +84 -8
  178. package/dist/index.js.map +1 -1
  179. package/dist/installer.d.ts +42 -11
  180. package/dist/installer.d.ts.map +1 -1
  181. package/dist/installer.js +140 -36
  182. package/dist/installer.js.map +1 -1
  183. package/dist/installers/index.d.ts +18 -7
  184. package/dist/installers/index.d.ts.map +1 -1
  185. package/dist/installers/index.js +13 -4
  186. package/dist/installers/index.js.map +1 -1
  187. package/dist/installers/workbuddy-host-installer.d.ts +246 -0
  188. package/dist/installers/workbuddy-host-installer.d.ts.map +1 -0
  189. package/dist/installers/workbuddy-host-installer.js +1419 -0
  190. package/dist/installers/workbuddy-host-installer.js.map +1 -0
  191. package/dist/mvp-config.d.ts +24 -0
  192. package/dist/mvp-config.d.ts.map +1 -1
  193. package/dist/mvp-config.js +36 -0
  194. package/dist/mvp-config.js.map +1 -1
  195. package/dist/prompts.d.ts +13 -0
  196. package/dist/prompts.d.ts.map +1 -1
  197. package/dist/prompts.js +45 -8
  198. package/dist/prompts.js.map +1 -1
  199. package/dist/uninstaller.d.ts +1 -1
  200. package/dist/uninstaller.d.ts.map +1 -1
  201. package/dist/uninstaller.js +29 -4
  202. package/dist/uninstaller.js.map +1 -1
  203. package/host-runtime/dist/governance-signal-admission.d.ts +63 -0
  204. package/host-runtime/dist/governance-signal-admission.js +322 -53
  205. package/host-runtime/dist/host-liveness-contract.js +7 -1
  206. package/host-runtime/dist/index.d.ts +1 -0
  207. package/host-runtime/dist/index.js +5 -1
  208. package/host-runtime/dist/internalization-consumer-cycle.d.ts +12 -0
  209. package/host-runtime/dist/internalization-consumer-cycle.js +43 -1
  210. package/host-runtime/dist/intervention-evidence-ingress.d.ts +1 -1
  211. package/host-runtime/dist/product-telemetry/service.js +1 -1
  212. package/host-runtime/dist/production-pain-evidence.d.ts +59 -1
  213. package/host-runtime/dist/production-pain-evidence.js +200 -17
  214. package/host-runtime/dist/prompt-activation-deliverability.js +5 -4
  215. package/host-runtime/dist/prompt-injection-event-evidence.d.ts +1 -1
  216. package/host-runtime/dist/prompt-injection-event-evidence.js +5 -1
  217. package/host-runtime/dist/prompt-injection-projection.d.ts +14 -5
  218. package/host-runtime/dist/prompt-injection-projection.js +30 -16
  219. package/host-runtime/dist/workbuddy-worker-status.d.ts +24 -0
  220. package/host-runtime/dist/workbuddy-worker-status.js +52 -0
  221. package/host-runtime/package.json +7 -5
  222. package/install-layout/dist/index.d.ts +18 -1
  223. package/install-layout/dist/index.js +15 -2
  224. package/install-layout/package.json +5 -3
  225. package/package.json +9 -7
  226. package/pd-cli/dist/commands/candidate.d.ts.map +1 -1
  227. package/pd-cli/dist/commands/candidate.js +44 -4
  228. package/pd-cli/dist/commands/candidate.js.map +1 -1
  229. package/pd-cli/dist/commands/codex-worker.d.ts.map +1 -1
  230. package/pd-cli/dist/commands/codex-worker.js +30 -1
  231. package/pd-cli/dist/commands/codex-worker.js.map +1 -1
  232. package/pd-cli/dist/commands/config-doctor.d.ts.map +1 -1
  233. package/pd-cli/dist/commands/config-doctor.js +19 -4
  234. package/pd-cli/dist/commands/config-doctor.js.map +1 -1
  235. package/pd-cli/dist/commands/diagnose.d.ts.map +1 -1
  236. package/pd-cli/dist/commands/diagnose.js +4 -1
  237. package/pd-cli/dist/commands/diagnose.js.map +1 -1
  238. package/pd-cli/dist/commands/health-codex-install.d.ts +85 -0
  239. package/pd-cli/dist/commands/health-codex-install.d.ts.map +1 -0
  240. package/pd-cli/dist/commands/health-codex-install.js +207 -0
  241. package/pd-cli/dist/commands/health-codex-install.js.map +1 -0
  242. package/pd-cli/dist/commands/health-codex.d.ts.map +1 -1
  243. package/pd-cli/dist/commands/health-codex.js +78 -0
  244. package/pd-cli/dist/commands/health-codex.js.map +1 -1
  245. package/pd-cli/dist/commands/pain-list.d.ts +3 -2
  246. package/pd-cli/dist/commands/pain-list.d.ts.map +1 -1
  247. package/pd-cli/dist/commands/pain-list.js +10 -10
  248. package/pd-cli/dist/commands/pain-list.js.map +1 -1
  249. package/pd-cli/dist/commands/pain-record.d.ts +2 -0
  250. package/pd-cli/dist/commands/pain-record.d.ts.map +1 -1
  251. package/pd-cli/dist/commands/pain-record.js +36 -0
  252. package/pd-cli/dist/commands/pain-record.js.map +1 -1
  253. package/pd-cli/dist/commands/pain-retry.d.ts +6 -0
  254. package/pd-cli/dist/commands/pain-retry.d.ts.map +1 -1
  255. package/pd-cli/dist/commands/pain-retry.js +152 -5
  256. package/pd-cli/dist/commands/pain-retry.js.map +1 -1
  257. package/pd-cli/dist/commands/runtime-activation.d.ts +1 -1
  258. package/pd-cli/dist/commands/runtime-activation.d.ts.map +1 -1
  259. package/pd-cli/dist/commands/runtime-activation.js +3 -3
  260. package/pd-cli/dist/commands/runtime-activation.js.map +1 -1
  261. package/pd-cli/dist/commands/workbuddy-worker.d.ts +19 -0
  262. package/pd-cli/dist/commands/workbuddy-worker.d.ts.map +1 -0
  263. package/pd-cli/dist/commands/workbuddy-worker.js +168 -0
  264. package/pd-cli/dist/commands/workbuddy-worker.js.map +1 -0
  265. package/pd-cli/dist/index.js +29 -1
  266. package/pd-cli/dist/index.js.map +1 -1
  267. package/pd-cli/dist/resolve-workspace.d.ts +14 -2
  268. package/pd-cli/dist/resolve-workspace.d.ts.map +1 -1
  269. package/pd-cli/dist/resolve-workspace.js +88 -21
  270. package/pd-cli/dist/resolve-workspace.js.map +1 -1
  271. package/pd-cli/dist/services/admission-write-gate.d.ts +24 -0
  272. package/pd-cli/dist/services/admission-write-gate.d.ts.map +1 -0
  273. package/pd-cli/dist/services/admission-write-gate.js +41 -0
  274. package/pd-cli/dist/services/admission-write-gate.js.map +1 -0
  275. package/pd-cli/dist/services/pd-config-loader.d.ts +39 -7
  276. package/pd-cli/dist/services/pd-config-loader.d.ts.map +1 -1
  277. package/pd-cli/dist/services/pd-config-loader.js +111 -18
  278. package/pd-cli/dist/services/pd-config-loader.js.map +1 -1
  279. package/pd-cli/dist/services/rulehost-pipeline-runner.d.ts +10 -0
  280. package/pd-cli/dist/services/rulehost-pipeline-runner.d.ts.map +1 -1
  281. package/pd-cli/dist/services/rulehost-pipeline-runner.js +59 -4
  282. package/pd-cli/dist/services/rulehost-pipeline-runner.js.map +1 -1
  283. package/pd-cli/package.json +8 -5
  284. package/plugin/dist/bundle.js +564 -509
  285. package/plugin/dist/governance-audit.js +1 -1
  286. package/plugin/dist/openclaw.plugin.json +1 -1
  287. package/plugin/dist/rulehost-evidence.js +1 -1
  288. package/plugin/openclaw.plugin.json +1 -1
  289. package/plugin/package.json +7 -5
  290. package/release-manager/dist/index.d.ts.map +1 -1
  291. package/release-manager/dist/index.js +84 -8
  292. package/release-manager/dist/index.js.map +1 -1
  293. package/release-manager/dist/installer.d.ts +42 -11
  294. package/release-manager/dist/installer.d.ts.map +1 -1
  295. package/release-manager/dist/installer.js +140 -36
  296. package/release-manager/dist/installer.js.map +1 -1
  297. package/release-manager/dist/installers/index.d.ts +18 -7
  298. package/release-manager/dist/installers/index.d.ts.map +1 -1
  299. package/release-manager/dist/installers/index.js +13 -4
  300. package/release-manager/dist/installers/index.js.map +1 -1
  301. package/release-manager/dist/installers/workbuddy-host-installer.d.ts +246 -0
  302. package/release-manager/dist/installers/workbuddy-host-installer.d.ts.map +1 -0
  303. package/release-manager/dist/installers/workbuddy-host-installer.js +1419 -0
  304. package/release-manager/dist/installers/workbuddy-host-installer.js.map +1 -0
  305. package/release-manager/dist/mvp-config.d.ts +24 -0
  306. package/release-manager/dist/mvp-config.d.ts.map +1 -1
  307. package/release-manager/dist/mvp-config.js +36 -0
  308. package/release-manager/dist/mvp-config.js.map +1 -1
  309. package/release-manager/dist/prompts.d.ts +13 -0
  310. package/release-manager/dist/prompts.d.ts.map +1 -1
  311. package/release-manager/dist/prompts.js +45 -8
  312. package/release-manager/dist/prompts.js.map +1 -1
  313. package/release-manager/dist/uninstaller.d.ts +1 -1
  314. package/release-manager/dist/uninstaller.d.ts.map +1 -1
  315. package/release-manager/dist/uninstaller.js +29 -4
  316. package/release-manager/dist/uninstaller.js.map +1 -1
  317. package/release-manager/package.json +8 -6
  318. package/workbuddy-adapter/dist/codec/index.d.ts +2 -0
  319. package/workbuddy-adapter/dist/codec/index.js +2 -0
  320. package/workbuddy-adapter/dist/codec/input-decoder.d.ts +30 -0
  321. package/workbuddy-adapter/dist/codec/input-decoder.js +232 -0
  322. package/workbuddy-adapter/dist/codec/output-encoder.d.ts +62 -0
  323. package/workbuddy-adapter/dist/codec/output-encoder.js +75 -0
  324. package/workbuddy-adapter/dist/host-adapter.d.ts +33 -0
  325. package/workbuddy-adapter/dist/host-adapter.js +23 -0
  326. package/workbuddy-adapter/dist/index.d.ts +16 -0
  327. package/workbuddy-adapter/dist/index.js +16 -0
  328. package/workbuddy-adapter/dist/pd-hook.d.ts +17 -0
  329. package/workbuddy-adapter/dist/pd-hook.js +505 -0
  330. package/workbuddy-adapter/dist/tool-semantics.d.ts +22 -0
  331. package/workbuddy-adapter/dist/tool-semantics.js +33 -0
  332. package/workbuddy-adapter/dist/workbuddy-evidence-recorder.d.ts +50 -0
  333. package/workbuddy-adapter/dist/workbuddy-evidence-recorder.js +238 -0
  334. package/workbuddy-adapter/dist/worker/workspace-worker.d.ts +41 -0
  335. package/workbuddy-adapter/dist/worker/workspace-worker.js +219 -0
  336. package/workbuddy-adapter/dist/workspace-resolution.d.ts +15 -0
  337. package/workbuddy-adapter/dist/workspace-resolution.js +53 -0
  338. package/workbuddy-adapter/package.json +64 -0
  339. package/workbuddy-adapter/plugin/.codebuddy-plugin/plugin.json +6 -0
  340. package/workbuddy-adapter/plugin/hooks/hooks.json +62 -0
  341. package/workbuddy-adapter/plugin/scripts/wrapper.cjs +79 -0
  342. package/console/dist/ui/utils/navigation.d.ts +0 -9
  343. package/console/dist/ui/utils/navigation.js +0 -7
  344. package/core/dist/runtime-v2/internalization/dimension-coverage-policy.d.ts +0 -54
  345. package/core/dist/runtime-v2/internalization/dimension-coverage-policy.d.ts.map +0 -1
  346. package/core/dist/runtime-v2/internalization/dimension-coverage-policy.js +0 -70
  347. package/core/dist/runtime-v2/internalization/dimension-coverage-policy.js.map +0 -1
@@ -9,6 +9,10 @@
9
9
  * 3. lease + run one Diagnostician task, then ONE bounded downstream
10
10
  * consumer cycle through the SHARED host-neutral executor
11
11
  * (internalization_auto_consumer = workspace execution authority)
12
+ * 4. SPEC-CODEX-PAIN-CLOSURE-001 CR-05: continue the post-processing of
13
+ * succeeded diagnoses (pending candidates, intake/seed orphans) WITHOUT
14
+ * re-running the diagnosis LLM; CR-08: persist cycle liveness facts
15
+ * (readiness is computed elsewhere and never implies liveness).
12
16
  *
13
17
  * Every step reuses existing authority: catch-up reuses the Slice A/B
14
18
  * ingestion + admission seams, reconciliation reuses
@@ -29,6 +33,29 @@ import { catchUpCodexIngestion } from '../ingestion/catch-up.js';
29
33
  import { CODEX_TOOL_SEMANTIC_MAPPINGS, CODEX_TOOL_SEMANTICS } from '../tool-semantics.js';
30
34
  const WORKER_OWNER = 'companion-worker';
31
35
  const DEFAULT_DIAG_CANDIDATE_LIMIT = 5;
36
+ /** CR-05 §9.1: bounded per-cycle post-processing continuation budget. */
37
+ const DEFAULT_POST_PROCESSING_LIMIT = 2;
38
+ /** CR-08 §9.3: liveness facts are worker-owned runtime state (not governance truth). */
39
+ function isPlainRecord(value) {
40
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
41
+ }
42
+ const LIVENESS_FILE = 'codex-worker-liveness.json';
43
+ const LIVENESS_STAGE_DEADLINE_MS = {
44
+ catch_up: 5 * 60_000,
45
+ reconcile: 5 * 60_000,
46
+ diagnosis_execution: 15 * 60_000,
47
+ post_processing: 5 * 60_000,
48
+ downstream_cycle: 10 * 60_000,
49
+ };
50
+ export function readCodexWorkerLiveness(workspaceDir) {
51
+ try {
52
+ const parsed = JSON.parse(fs.readFileSync(path.join(path.resolve(workspaceDir), '.state', LIVENESS_FILE), 'utf8'));
53
+ return isPlainRecord(parsed) ? parsed : null;
54
+ }
55
+ catch {
56
+ return null;
57
+ }
58
+ }
32
59
  function workerLogger(logger) {
33
60
  return logger ?? {
34
61
  info: () => undefined,
@@ -44,6 +71,46 @@ function directoryExists(dir) {
44
71
  return false;
45
72
  }
46
73
  }
74
+ function writeLiveness(workspaceDir, patch) {
75
+ try {
76
+ const root = path.resolve(workspaceDir);
77
+ const stateDir = path.join(root, '.state');
78
+ const file = path.join(stateDir, LIVENESS_FILE);
79
+ // The subpath is a constant, so containment always holds — the explicit
80
+ // check keeps the write boundary auditable (defense in depth).
81
+ if (!file.startsWith(root + path.sep))
82
+ return;
83
+ if (!directoryExists(stateDir))
84
+ fs.mkdirSync(stateDir, { recursive: true });
85
+ let current = {};
86
+ try {
87
+ const parsed = JSON.parse(fs.readFileSync(file, 'utf8'));
88
+ if (isPlainRecord(parsed))
89
+ current = parsed;
90
+ }
91
+ catch {
92
+ current = {};
93
+ }
94
+ const merged = { ...current, ...patch, updatedAt: new Date().toISOString() };
95
+ // CodeQL (insecure temporary file): the fixed predictable `.tmp` name is a
96
+ // symlink-swap surface in a shared state dir. Write+rename through an
97
+ // EXCLUSIVE random temp file in the same directory (O_EXCL | 0600) — the
98
+ // rename stays atomic within the directory.
99
+ const tmp = path.join(stateDir, `${LIVENESS_FILE}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 10)}.tmp`);
100
+ const fd = fs.openSync(tmp, fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_EXCL, 0o600);
101
+ try {
102
+ fs.writeFileSync(fd, JSON.stringify(merged));
103
+ }
104
+ finally {
105
+ fs.closeSync(fd);
106
+ }
107
+ fs.renameSync(tmp, file);
108
+ }
109
+ catch {
110
+ // liveness bookkeeping must never break the cycle (rc-9: the cycle report
111
+ // itself remains the observable degradation channel)
112
+ }
113
+ }
47
114
  /**
48
115
  * Oldest pending diagnostician first; retry_wait candidates are filtered to
49
116
  * those whose backoff window has ELAPSED, so an old-but-still-waiting task
@@ -51,18 +118,42 @@ function directoryExists(dir) {
51
118
  * bounded (`limit`), and executePendingDiagnosis re-enforces the backoff
52
119
  * window on the chosen task — no double bookkeeping.
53
120
  */
54
- async function pickDiagnosticianCandidate(stateManager, limit) {
121
+ const DIAGNOSTICIAN_FAIR_SHARE_SCOPE = 'codex-worker-diagnostician-fair-share';
122
+ /**
123
+ * SPEC §9.1 fair-share pick (review P1): pending and due-retry are both
124
+ * continuously-eligible classes; neither may starve the other while new
125
+ * pending signals keep arriving. A persistent round-robin cursor (stored in
126
+ * the existing reconciliation_cursor authority, so a worker restart does NOT
127
+ * reset it to pending-priority) alternates service between the two classes —
128
+ * each eligible class is served at least every other cycle, well inside the
129
+ * 3-full-cycle bound. Single-eligible work is always served.
130
+ */
131
+ async function pickDiagnosticianCandidate(stateManager, limit, nowIso) {
55
132
  const pending = await stateManager.listTasks({ taskKind: 'diagnostician', status: 'pending', orderBy: 'updated_at_asc', limit });
56
- const [first] = pending;
57
- if (first !== undefined)
58
- return first;
133
+ const [pendingTask] = pending;
134
+ let retryTask = null;
59
135
  const retrying = await stateManager.listTasks({ taskKind: 'diagnostician', status: 'retry_wait', orderBy: 'updated_at_asc', limit });
60
136
  for (const candidate of retrying) {
61
137
  if (isRetryWaitBackoffElapsed(candidate.status, candidate.leaseExpiresAt)) {
62
- return candidate;
138
+ retryTask = candidate;
139
+ break;
63
140
  }
64
141
  }
65
- return null;
142
+ if (pendingTask === undefined && retryTask === null)
143
+ return null;
144
+ if (retryTask === null) {
145
+ await stateManager.setDiagnosticianServiceCursor(DIAGNOSTICIAN_FAIR_SHARE_SCOPE, { lastUpdatedAt: nowIso, lastTaskId: 'pending' });
146
+ return pendingTask ?? null;
147
+ }
148
+ if (pendingTask === undefined) {
149
+ await stateManager.setDiagnosticianServiceCursor(DIAGNOSTICIAN_FAIR_SHARE_SCOPE, { lastUpdatedAt: nowIso, lastTaskId: 'retry_wait' });
150
+ return retryTask;
151
+ }
152
+ const cursor = await stateManager.getDiagnosticianServiceCursor(DIAGNOSTICIAN_FAIR_SHARE_SCOPE);
153
+ const serveRetryFirst = cursor?.lastTaskId !== 'retry_wait';
154
+ const servedClass = serveRetryFirst ? 'retry_wait' : 'pending';
155
+ await stateManager.setDiagnosticianServiceCursor(DIAGNOSTICIAN_FAIR_SHARE_SCOPE, { lastUpdatedAt: nowIso, lastTaskId: servedClass });
156
+ return serveRetryFirst ? retryTask : pendingTask;
66
157
  }
67
158
  /**
68
159
  * Run ONE bounded worker cycle for a canonical workspace. Never throws:
@@ -73,6 +164,7 @@ export async function runCodexWorkspaceWorkerCycle(options) {
73
164
  const logger = workerLogger(options.logger);
74
165
  const emitEvent = options.emitEvent ?? ((_event, _payload) => undefined);
75
166
  const base = { workspaceDir };
167
+ const cycleStartedAt = new Date();
76
168
  // Step 1 — Workspace eligibility.
77
169
  if (!directoryExists(workspaceDir)) {
78
170
  return { ...base, mode: 'degraded', reason: 'workspace_missing', nextAction: 'The workspace directory no longer exists; remove it from the install manifest or restore it. No PD state was mutated.' };
@@ -86,6 +178,13 @@ export async function runCodexWorkspaceWorkerCycle(options) {
86
178
  if (flags['host.codex']?.enabled !== true) {
87
179
  return { ...base, mode: 'paused', reason: 'host.codex_disabled', nextAction: 'Set features.host.codex.enabled=true in the Workspace .pd/config.yaml to enable Codex PD behavior.' };
88
180
  }
181
+ // CR-08: liveness bookkeeping starts only after the workspace proved
182
+ // eligible — writing into a missing workspace directory would fabricate it.
183
+ writeLiveness(workspaceDir, {
184
+ lastCycleStartedAt: cycleStartedAt.toISOString(),
185
+ runningStage: 'catch_up',
186
+ stageDeadline: new Date(cycleStartedAt.getTime() + LIVENESS_STAGE_DEADLINE_MS.catch_up).toISOString(),
187
+ });
89
188
  const consumerFlag = loadFeatureFlagFromConfig(workspaceDir, 'internalization_auto_consumer', { info: (m) => logger.info(m), warn: (m) => logger.warn(m) });
90
189
  // PRI-638 P1-C: the Diagnostician capability gate is resolved from the same
91
190
  // canonical authority the runtime factory uses. An Owner-disabled agent is
@@ -128,7 +227,7 @@ export async function runCodexWorkspaceWorkerCycle(options) {
128
227
  mode: 'paused',
129
228
  reason: 'internalization_auto_consumer_disabled',
130
229
  nextAction: 'Automatic internalization execution is paused for this Workspace; manual commands remain available: pd diagnose, pd runtime internalization run-once.',
131
- report: { catchUp, reconcile, diagnostician: null, downstream: null },
230
+ report: { catchUp, reconcile, postProcessing: null, diagnostician: null, downstream: null },
132
231
  };
133
232
  }
134
233
  else {
@@ -144,7 +243,7 @@ export async function runCodexWorkspaceWorkerCycle(options) {
144
243
  if (sweep.recovered > 0 || sweep.errors.length > 0) {
145
244
  emitEvent('CODEX_WORKER_RECOVERY_SWEEP', JSON.stringify({ recovered: sweep.recovered, failed: sweep.errors.length }));
146
245
  }
147
- const candidate = await pickDiagnosticianCandidate(handle.stateManager, options.diagnosticianCandidateLimit ?? DEFAULT_DIAG_CANDIDATE_LIMIT);
246
+ const candidate = await pickDiagnosticianCandidate(handle.stateManager, options.diagnosticianCandidateLimit ?? DEFAULT_DIAG_CANDIDATE_LIMIT, new Date().toISOString());
148
247
  if (candidate !== null) {
149
248
  const stateDir = path.join(workspaceDir, '.state');
150
249
  const bridge = await createPainSignalBridge({
@@ -196,7 +295,82 @@ export async function runCodexWorkspaceWorkerCycle(options) {
196
295
  mode: 'degraded',
197
296
  reason: `diagnostician_execution_failed:${detail}`,
198
297
  nextAction: 'Inspect the Workspace runtime profile and provider configuration; the task keeps its pending/retry state and no evidence was mutated.',
199
- report: { catchUp, reconcile, diagnostician: null, downstream: null },
298
+ report: { catchUp, reconcile, postProcessing: null, diagnostician: null, downstream: null },
299
+ };
300
+ }
301
+ }
302
+ // Step 6b — SPEC-CODEX-PAIN-CLOSURE-001 CR-05 (SPEC §9.1): continue the
303
+ // post-processing of SUCCEEDED diagnoses (candidates stuck before
304
+ // admission/intake/seed — the original incident shape) without re-running
305
+ // the diagnosis LLM. Runs in the SAME consumer-flag authority as fresh
306
+ // diagnoses; every class (pending / due-retry / post-processing) is served
307
+ // every cycle it has eligible work, so no class can starve another
308
+ // (SPEC §9.1: each eligible class served within 3 full cycles).
309
+ let postProcessing = null;
310
+ writeLiveness(workspaceDir, { runningStage: 'post_processing', stageDeadline: new Date(Date.now() + LIVENESS_STAGE_DEADLINE_MS.post_processing).toISOString() });
311
+ if (consumerFlag.enabled && !diagnosticianDisabled) {
312
+ try {
313
+ const handle = await createRuntimeStateHandle({ workspaceDir, readonly: false });
314
+ try {
315
+ const { eligible: backlog, corrupt } = await handle.stateManager.listDiagnosisPostProcessingBacklog({ limit: DEFAULT_POST_PROCESSING_LIMIT });
316
+ const { eligible: backlogAll, corrupt: corruptAll } = await handle.stateManager.listDiagnosisPostProcessingBacklog({ limit: 200 });
317
+ const oldestCreatedAt = backlogAll[0]?.createdAt;
318
+ const blocked = [];
319
+ // SPEC review round-4 P1: corrupt admission_json rows are surfaced as a
320
+ // candidate-level queryable blocker — never deleted, never eligible for
321
+ // writes, never blocking healthy work (the scan filters via json_valid).
322
+ for (const item of corruptAll) {
323
+ blocked.push({ candidateId: item.candidateId, reason: 'admission_json_corrupt: repair or re-run the diagnosis (pd candidate show reports the unreadable disposition)' });
324
+ }
325
+ const resumedTaskIds = new Set();
326
+ if (backlog.length > 0) {
327
+ const stateDir = path.join(workspaceDir, '.state');
328
+ const bridge = await createPainSignalBridge({
329
+ workspaceDir,
330
+ stateDir,
331
+ ledgerAdapter: new PrincipleTreeLedgerAdapter({ stateDir }),
332
+ owner: WORKER_OWNER,
333
+ effectiveConfig: config.effective,
334
+ getEnvVar: (name) => process.env[name],
335
+ });
336
+ for (const item of backlog) {
337
+ const resumed = await bridge.resumeSucceededDiagnosisPostProcessing({ taskId: item.taskId });
338
+ if (resumed.status === 'resumed') {
339
+ resumedTaskIds.add(item.taskId);
340
+ }
341
+ else if (resumed.status === 'blocked') {
342
+ blocked.push({ candidateId: item.candidateId, reason: resumed.reason ?? 'blocked' });
343
+ }
344
+ for (const skip of resumed.skipped ?? [])
345
+ blocked.push(skip);
346
+ }
347
+ }
348
+ postProcessing = {
349
+ resumedTaskIds: [...resumedTaskIds],
350
+ processedCount: backlog.length,
351
+ remainingCount: Math.max(0, backlogAll.length - backlog.length),
352
+ oldestAgeMs: oldestCreatedAt !== undefined ? Math.max(0, Date.now() - Date.parse(oldestCreatedAt)) : null,
353
+ blocked,
354
+ };
355
+ if (corrupt.length > 0) {
356
+ emitEvent('CODEX_WORKER_ADMISSION_CORRUPT', JSON.stringify({ candidateIds: corruptAll.map((c) => c.candidateId) }));
357
+ }
358
+ if (backlog.length > 0) {
359
+ emitEvent('CODEX_WORKER_POST_PROCESSING', JSON.stringify(postProcessing));
360
+ }
361
+ }
362
+ finally {
363
+ await handle.close().catch(() => undefined);
364
+ }
365
+ }
366
+ catch (error) {
367
+ const detail = error instanceof Error ? error.message.slice(0, 200) : String(error).slice(0, 200);
368
+ return {
369
+ ...base,
370
+ mode: 'degraded',
371
+ reason: `post_processing_continuation_failed:${detail}`,
372
+ nextAction: 'Inspect the workspace state.db principle_candidates/tasks; the worker retries automatically on the next cycle.',
373
+ report: { catchUp, reconcile, postProcessing: null, diagnostician, downstream: null },
200
374
  };
201
375
  }
202
376
  }
@@ -223,9 +397,10 @@ export async function runCodexWorkspaceWorkerCycle(options) {
223
397
  mode: 'degraded',
224
398
  reason: `host_tool_declaration_persist_failed:${declared.reason}`,
225
399
  nextAction: 'Repair Codex host declaration persistence (.pd/host-tool-semantics/codex.json) before running the downstream internalization cycle; the worker retries automatically on the next cycle.',
226
- report: { catchUp, reconcile, diagnostician, downstream: null },
400
+ report: { catchUp, reconcile, postProcessing: null, diagnostician, downstream: null },
227
401
  };
228
402
  }
403
+ writeLiveness(workspaceDir, { runningStage: 'downstream_cycle', stageDeadline: new Date(Date.now() + LIVENESS_STAGE_DEADLINE_MS.downstream_cycle).toISOString() });
229
404
  const downstream = await runInternalizationConsumerCycle(workspaceDir, {
230
405
  owner: WORKER_OWNER,
231
406
  logLabel: 'CodexWorker',
@@ -249,11 +424,19 @@ export async function runCodexWorkspaceWorkerCycle(options) {
249
424
  : diagnostician?.status === 'degraded' ? `diagnostician_degraded:${diagnostician.message ?? 'unknown'}`
250
425
  : downstream.skipReason === 'runtime_config_error' || downstream.skipReason === 'cycle_error' || downstream.skipReason === 'config_malformed' ? `downstream:${downstream.skipReason}`
251
426
  : undefined;
427
+ writeLiveness(workspaceDir, {
428
+ runningStage: null,
429
+ stageDeadline: null,
430
+ lastCycleCompletedAt: new Date().toISOString(),
431
+ lastCycleDurationMs: Date.now() - cycleStartedAt.getTime(),
432
+ lastCycleMode: degradedReason !== undefined ? 'degraded' : 'ready',
433
+ ...(degradedReason !== undefined ? { lastDegradedReason: degradedReason } : {}),
434
+ });
252
435
  return {
253
436
  ...base,
254
437
  mode: degradedReason !== undefined ? 'degraded' : 'ready',
255
438
  ...(degradedReason !== undefined ? { reason: degradedReason, nextAction: 'Inspect the Workspace .pd/config.yaml runtime profile and the per-step report above; the worker retries automatically on the next cycle.' } : {}),
256
- report: { catchUp, reconcile, diagnostician, downstream },
439
+ report: { catchUp, reconcile, postProcessing, diagnostician, downstream },
257
440
  };
258
441
  }
259
442
  // Slice D (PRI-625): the mode authority moved to host-runtime so the CLI and
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@principles/codex-adapter",
3
- "version": "0.5.2",
3
+ "version": "0.5.5",
4
4
  "description": "Codex CLI host adapter for Principles Disciple — implements HostAdapter interface for OpenAI Codex CLI's stdin/stdout JSON hook model (ADR-0020).",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -22,7 +22,9 @@
22
22
  },
23
23
  "files": [
24
24
  "dist",
25
- "README.md"
25
+ "README.md",
26
+ "LICENSE",
27
+ "NOTICE"
26
28
  ],
27
29
  "scripts": {
28
30
  "build": "tsc",
@@ -38,10 +40,10 @@
38
40
  "devDependencies": {
39
41
  "@types/node": "^26.4.1",
40
42
  "typescript": "^7.0.2",
41
- "vite": "^8.3.0",
43
+ "vite": "^8.3.4",
42
44
  "vitest": "^5.0.0"
43
45
  },
44
- "license": "MIT",
46
+ "license": "Apache-2.0",
45
47
  "repository": {
46
48
  "type": "git",
47
49
  "url": "git+https://github.com/csuzngjh/principles.git",
@@ -78,12 +78,12 @@ export type ApproveWithActivationResult = {
78
78
  capacity: {
79
79
  category: 'single_item_exceeds_budget' | 'route_unconfirmed' | 'content_unconfirmed';
80
80
  route?: 'legacy_trim' | 'shared_render';
81
- hostKind?: 'openclaw' | 'codex';
81
+ hostKind?: 'openclaw' | 'codex' | 'workbuddy';
82
82
  budget?: number;
83
83
  costChars?: number;
84
84
  overByChars?: number;
85
85
  perHost?: {
86
- hostKind: 'openclaw' | 'codex';
86
+ hostKind: 'openclaw' | 'codex' | 'workbuddy';
87
87
  fits: boolean;
88
88
  costChars: number;
89
89
  }[];
@@ -306,7 +306,7 @@ export class ApprovalsConsoleModel {
306
306
  return `injection_budget_check_failed: could not recompute the prompt injection projection (${message}); the activation is committed but its injection status is unverified. nextAction=check .pd/state.db readability and re-run pd runtime activation list`;
307
307
  }
308
308
  if (resolution.status === 'unconfirmed') {
309
- return `injection_capacity_unconfirmed: the activation is committed but this workspace's effective injection route cannot be confirmed (${resolution.decision.unconfirmedReason ?? 'unknown reason'}); per-host forecasts: ${resolution.perHost.map((entry) => `${entry.hostKind}=${entry.projection.route} (${entry.projection.usedChars}c${entry.projection.truncated ? ', truncated' : ''})`).join('; ')}. nextAction=${resolution.decision.nextAction ?? 'pass an explicit target host (openclaw|codex)'}`;
309
+ return `injection_capacity_unconfirmed: the activation is committed but this workspace's effective injection route cannot be confirmed (${resolution.decision.unconfirmedReason ?? 'unknown reason'}); per-host forecasts: ${resolution.perHost.map((entry) => `${entry.hostKind}=${entry.projection.route} (${entry.projection.usedChars}c${entry.projection.truncated ? ', truncated' : ''})`).join('; ')}. nextAction=${resolution.decision.nextAction ?? 'pass an explicit target host (openclaw|codex|workbuddy)'}`;
310
310
  }
311
311
  const { projection } = resolution;
312
312
  if (projection.injectedActivationIds.includes(activationId)) {
@@ -363,7 +363,7 @@ export class ApprovalsGroupedConsoleModel {
363
363
  truncated: reference?.truncated ?? false,
364
364
  capacityStatus: 'unconfirmed',
365
365
  unconfirmedReason: resolution.decision.unconfirmedReason ?? 'effective injection route cannot be confirmed',
366
- nextAction: resolution.decision.nextAction ?? 'pass an explicit target host (openclaw|codex)',
366
+ nextAction: resolution.decision.nextAction ?? 'pass an explicit target host (openclaw|codex|workbuddy)',
367
367
  perHost: resolution.perHost.map((entry) => ({
368
368
  hostKind: entry.hostKind,
369
369
  route: entry.projection.route,
@@ -32,6 +32,15 @@ export interface PrincipleListItem {
32
32
  conflictsWithCount: number;
33
33
  createdAt: string;
34
34
  updatedAt: string;
35
+ /**
36
+ * PRI-956 (F15): the ledger `status` is an ASSET lifecycle ("ingested &
37
+ * not archived") and has no deactivated value — a deactivated principle
38
+ * stays `active` here forever. When the runtime authority (activations
39
+ * table) says every prompt activation for this principle is deactivated,
40
+ * the route annotates it so the UI can disclose "runtime revoked" instead
41
+ * of implying "in effect" from the asset status alone.
42
+ */
43
+ runtimeDeactivated?: boolean;
35
44
  /** Detected language of the principle text ('en' | 'zh' | 'unknown'). PRI-332 */
36
45
  detectedLanguage: 'en' | 'zh' | 'unknown';
37
46
  /** Structured readability warning code — front-end renders via i18n. PRI-332 P1-5 */
@@ -72,6 +81,13 @@ export interface PrinciplesListOutput {
72
81
  categories?: Record<string, number>;
73
82
  /** If the approval cross-check was unavailable, this explains why (ERR-002) */
74
83
  approvalCrossCheckUnavailable?: string;
84
+ /**
85
+ * PRI-956 (F15, D3): if the activations-table query that determines
86
+ * CURRENT runtime revocation was unavailable, this explains why — the UI
87
+ * must express "runtime status cannot be confirmed" instead of defaulting
88
+ * to either active or revoked.
89
+ */
90
+ revocationCheckUnavailable?: string;
75
91
  }
76
92
  export type PrincipleFilter = 'all' | 'actionable';
77
93
  export interface PrincipleDetailOutput {
@@ -86,7 +102,16 @@ export declare class PrinciplesConsoleModel {
86
102
  constructor(workspaceDir: string);
87
103
  private getLedgerPath;
88
104
  private loadLedger;
89
- listPrinciples(filter?: PrincipleFilter, decidedPrincipleIds?: Set<string>, pendingApprovalPrincipleIds?: Set<string>): Promise<PrinciplesListOutput>;
105
+ /**
106
+ * PRI-956 (F15): decision-id sets from the route's cross-checks. All
107
+ * optional; `deactivated` comes from the activations table (runtime
108
+ * authority) and only annotates disclosure — never re-buckets.
109
+ */
110
+ listPrinciples(filter?: PrincipleFilter, decisionIds?: {
111
+ decided?: Set<string>;
112
+ pendingApproval?: Set<string>;
113
+ deactivated?: Set<string>;
114
+ }): Promise<PrinciplesListOutput>;
90
115
  getPrincipleDetail(principleId: string): Promise<PrincipleDetailOutput | null>;
91
116
  archivePrinciple(principleId: string): Promise<boolean>;
92
117
  unarchivePrinciple(principleId: string): Promise<boolean>;
@@ -148,7 +148,15 @@ export class PrinciplesConsoleModel {
148
148
  this.cacheTimestamp = now;
149
149
  return ledger;
150
150
  }
151
- async listPrinciples(filter, decidedPrincipleIds, pendingApprovalPrincipleIds) {
151
+ /**
152
+ * PRI-956 (F15): decision-id sets from the route's cross-checks. All
153
+ * optional; `deactivated` comes from the activations table (runtime
154
+ * authority) and only annotates disclosure — never re-buckets.
155
+ */
156
+ async listPrinciples(filter, decisionIds) {
157
+ const decidedPrincipleIds = decisionIds?.decided;
158
+ const pendingApprovalPrincipleIds = decisionIds?.pendingApproval;
159
+ const deactivatedPrincipleIds = decisionIds?.deactivated;
152
160
  const ledger = this.loadLedger();
153
161
  const principles = Object.values(ledger.tree.principles);
154
162
  const items = [];
@@ -185,6 +193,15 @@ export class PrinciplesConsoleModel {
185
193
  const classified = classifyPrinciples(items, decidedPrincipleIds, pendingApprovalPrincipleIds);
186
194
  const workspaceClassified = classified.filter((c) => c.category !== 'builtin');
187
195
  const workspaceItems = workspaceClassified.map((c) => c.principle);
196
+ // PRI-956 (F15): disclose runtime revocation — the ledger status above is
197
+ // asset lifecycle and never reflects deactivation; the runtime authority
198
+ // (activations table) is the only source for "currently revoked".
199
+ if (deactivatedPrincipleIds) {
200
+ for (const p of workspaceItems) {
201
+ if (deactivatedPrincipleIds.has(p.id))
202
+ p.runtimeDeactivated = true;
203
+ }
204
+ }
188
205
  // PRI-641: summary and categories are computed over the same workspace
189
206
  // corpus the caller sees, so `principles[]` and `summary` can never
190
207
  // disagree (no "list 3, total 13" projection).
@@ -99,8 +99,8 @@ export async function handleActivationsRoute(req, res, workspaceDir, subPath, au
99
99
  let targetHost;
100
100
  const rawHost = url.searchParams.get('host');
101
101
  if (rawHost !== null) {
102
- if (rawHost !== 'openclaw' && rawHost !== 'codex') {
103
- sendError(res, 400, 'validation_error', 'host must be one of: openclaw, codex');
102
+ if (rawHost !== 'openclaw' && rawHost !== 'codex' && rawHost !== 'workbuddy') {
103
+ sendError(res, 400, 'validation_error', 'host must be one of: openclaw, codex, workbuddy');
104
104
  return;
105
105
  }
106
106
  targetHost = rawHost;
@@ -242,8 +242,8 @@ export async function handleActivationsRoute(req, res, workspaceDir, subPath, au
242
242
  }
243
243
  let targetHost;
244
244
  if (Object.hasOwn(body, 'host')) {
245
- if (body.host !== 'openclaw' && body.host !== 'codex') {
246
- sendError(res, 400, 'validation_error', 'host must be one of: openclaw, codex');
245
+ if (body.host !== 'openclaw' && body.host !== 'codex' && body.host !== 'workbuddy') {
246
+ sendError(res, 400, 'validation_error', 'host must be one of: openclaw, codex, workbuddy');
247
247
  return;
248
248
  }
249
249
  targetHost = body.host;
@@ -133,8 +133,8 @@ export async function handleApprovalsRoute(req, res, workspaceDir, subPath) {
133
133
  // when the workspace's applicable hosts would take different routes.
134
134
  let targetHost;
135
135
  if (Object.hasOwn(parsed, 'host')) {
136
- if (parsed.host !== 'openclaw' && parsed.host !== 'codex') {
137
- sendBadRequest(res, 'host must be one of: openclaw, codex');
136
+ if (parsed.host !== 'openclaw' && parsed.host !== 'codex' && parsed.host !== 'workbuddy') {
137
+ sendBadRequest(res, 'host must be one of: openclaw, codex, workbuddy');
138
138
  return;
139
139
  }
140
140
  targetHost = parsed.host;
@@ -1,6 +1,15 @@
1
1
  import type { IncomingMessage, ServerResponse } from 'node:http';
2
2
  import { SqliteTaskStore } from '@principles/core/runtime-v2';
3
3
  export declare function disposeFailedTasksModels(): void;
4
+ export interface PipelineHealth {
5
+ status: 'healthy' | 'backlogged' | 'stalled' | 'disabled' | 'unknown';
6
+ pendingCount: number | null;
7
+ oldestPendingTaskKind: string | null;
8
+ oldestPendingAgeMinutes: number | null;
9
+ lastProgressAt: string | null;
10
+ note: string;
11
+ nextAction?: string;
12
+ }
4
13
  export type FailedTasksContext = {
5
14
  workspaceDir: string;
6
15
  subPath: string;
@@ -155,6 +155,104 @@ function parseRecoverBody(body) {
155
155
  const force = raw.force === true;
156
156
  return { ok: true, reason, force };
157
157
  }
158
+ // ── PRI-955 (F11): derived learning-queue health projection ─────────────────
159
+ //
160
+ // "No failed tasks" is NOT "the pipeline is healthy" — the dogfood run
161
+ // (2026-10-13) had a 13-deep pending backlog with downstream stages starving
162
+ // for 35+ minutes while this page reported the pipeline as healthy. The
163
+ // health projection below is DERIVED on read from the same state.db facts the
164
+ // failed-task list already uses (no second truth, no new persistence):
165
+ // - pendingCount: pending runner-kind tasks (the learning queue)
166
+ // - oldestPendingAgeMinutes: queue age of the oldest pending task
167
+ // - lastProgressAt: newest succeeded transition (updated_at of the newest
168
+ // succeeded task) — evidence that the pipeline actually advanced recently
169
+ // Status rules (documented, never over-claimed):
170
+ // stalled — work is waiting AND nothing progressed inside the window
171
+ // backlogged — work is waiting and the pipeline still progresses
172
+ // healthy — nothing waiting AND a succeeded transition inside the window
173
+ // unknown — no usable evidence (fresh workspace, unreadable counters)
174
+ const PIPELINE_STALL_WINDOW_MS = 30 * 60 * 1000;
175
+ const PIPELINE_FRESH_WINDOW_MS = 24 * 60 * 60 * 1000;
176
+ function ageMinutes(iso, nowMs) {
177
+ if (typeof iso !== 'string' || iso.length === 0)
178
+ return null;
179
+ const ms = Date.parse(iso);
180
+ if (!Number.isFinite(ms))
181
+ return null;
182
+ return Math.max(0, Math.floor((nowMs - ms) / 60000));
183
+ }
184
+ async function derivePipelineHealth(store, consumerEnabled) {
185
+ const nowMs = Date.now();
186
+ try {
187
+ // C2/C4 (PRI-955 review): exact, untruncated aggregates over the
188
+ // authoritative tasks table, scoped to learning-pipeline runner kinds —
189
+ // a global MAX(succeeded) would let unrelated task kinds fake freshness,
190
+ // and a LIMIT-200 list length would misreport the queue size.
191
+ const agg = await store.aggregatePipelineHealth();
192
+ const { pendingCount } = agg;
193
+ const oldestPendingAgeMinutes = ageMinutes(agg.oldestPendingCreatedAt, nowMs);
194
+ const lastProgressAgeMinutes = ageMinutes(agg.lastSucceededAt, nowMs);
195
+ if (!consumerEnabled) {
196
+ return {
197
+ status: 'disabled', pendingCount, oldestPendingTaskKind: agg.oldestPendingTaskKind, oldestPendingAgeMinutes, lastProgressAt: agg.lastSucceededAt,
198
+ note: `内化自动消费者已禁用(internalization_auto_consumer=false)——学习管道不会推进。当前待处理 ${pendingCount} 个。`,
199
+ nextAction: '在 .pd/config.yaml 设置 features.internalization_auto_consumer.enabled=true,或改用 pd runtime internalization run-once 手动推进。',
200
+ };
201
+ }
202
+ if (pendingCount > 0) {
203
+ const stalled = (oldestPendingAgeMinutes ?? 0) > PIPELINE_STALL_WINDOW_MS / 60000
204
+ && (lastProgressAgeMinutes === null || lastProgressAgeMinutes > PIPELINE_STALL_WINDOW_MS / 60000);
205
+ if (stalled) {
206
+ return {
207
+ status: 'stalled', pendingCount, oldestPendingTaskKind: agg.oldestPendingTaskKind, oldestPendingAgeMinutes, lastProgressAt: agg.lastSucceededAt,
208
+ note: `学习队列有 ${pendingCount} 个待处理任务,最老等待 ${oldestPendingAgeMinutes ?? '?'} 分钟,且窗口内无任何推进。`,
209
+ nextAction: '检查内化消费者是否在运行(OpenClaw 自动消费 / pd codex worker),并查看任务队列明细。',
210
+ };
211
+ }
212
+ return {
213
+ status: 'backlogged', pendingCount, oldestPendingTaskKind: agg.oldestPendingTaskKind, oldestPendingAgeMinutes, lastProgressAt: agg.lastSucceededAt,
214
+ note: `学习队列有 ${pendingCount} 个待处理任务(最老等待 ${oldestPendingAgeMinutes ?? '?'} 分钟);最近推进 ${agg.lastSucceededAt ?? '未知'}。`,
215
+ nextAction: '积压会随消费者周期逐步消化;若长时间不降,检查消费者运行状态与模型通道。',
216
+ };
217
+ }
218
+ // PRI-955 review P1: healthy requires NO unresolved states either —
219
+ // pendingCount=0 + recent success is NOT healthy if failed, NHR, or
220
+ // retry_wait tasks are still present.
221
+ if (agg.failedCount > 0 || agg.needsHumanReviewCount > 0 || agg.retryWaitCount > 0) {
222
+ const parts = [];
223
+ if (agg.failedCount > 0)
224
+ parts.push(`${agg.failedCount} 个失败`);
225
+ if (agg.needsHumanReviewCount > 0)
226
+ parts.push(`${agg.needsHumanReviewCount} 个需人工审查`);
227
+ if (agg.retryWaitCount > 0)
228
+ parts.push(`${agg.retryWaitCount} 个等待重试`);
229
+ return {
230
+ status: 'backlogged', pendingCount: 0, oldestPendingTaskKind: null, oldestPendingAgeMinutes: null, lastProgressAt: agg.lastSucceededAt,
231
+ note: `学习队列无积压,但存在未解决任务:${parts.join('、')}。`,
232
+ nextAction: '在失败任务列表查看详情,或在治理焦点处理需人工审查的任务。',
233
+ };
234
+ }
235
+ if (lastProgressAgeMinutes !== null && lastProgressAgeMinutes <= PIPELINE_FRESH_WINDOW_MS / 60000) {
236
+ return {
237
+ status: 'healthy', pendingCount: 0, oldestPendingTaskKind: null, oldestPendingAgeMinutes: null, lastProgressAt: agg.lastSucceededAt,
238
+ note: `学习队列无积压;最近推进 ${agg.lastSucceededAt}。`,
239
+ };
240
+ }
241
+ return {
242
+ status: 'unknown', pendingCount: 0, oldestPendingTaskKind: null, oldestPendingAgeMinutes: null, lastProgressAt: agg.lastSucceededAt,
243
+ note: '当前没有待处理任务,但窗口内没有可确认的推进证据——无法判定管道健康。',
244
+ nextAction: '产生一次真实任务(或运行一次内化消费者)后回到本页复查。',
245
+ };
246
+ }
247
+ catch {
248
+ // rc-9: 降级必须可观察——无法读取队列事实时报告 unknown,而非默认健康。
249
+ return {
250
+ status: 'unknown', pendingCount: null, oldestPendingTaskKind: null, oldestPendingAgeMinutes: null, lastProgressAt: null,
251
+ note: '学习队列状态不可读(计数失败)——无法判定管道健康。',
252
+ nextAction: '检查 .pd/state.db 可读性,或用 pd health 查看工作区诊断。',
253
+ };
254
+ }
255
+ }
158
256
  /**
159
257
  * Dispatch one recovery attempt through the shared core services.
160
258
  *
@@ -341,7 +439,8 @@ export async function handleFailedTasksRoute(req, res, ctx) {
341
439
  sendSuccess(res, {
342
440
  tasks: [],
343
441
  total: 0,
344
- nextAction: 'No failed tasks. PD pipeline is healthy. (state.db not yet initialized — no tasks have been created.)',
442
+ pipeline: { status: 'unknown', pendingCount: null, oldestPendingTaskKind: null, oldestPendingAgeMinutes: null, lastProgressAt: null, note: 'state.db 尚未初始化——工作区还没有任何任务,管道健康状态未知。', nextAction: '运行时开始创建任务后回到本页复查。' },
443
+ nextAction: 'No failed tasks. (state.db not yet initialized — pipeline health is UNKNOWN until the runtime creates tasks.)',
345
444
  });
346
445
  return;
347
446
  }
@@ -363,19 +462,29 @@ export async function handleFailedTasksRoute(req, res, ctx) {
363
462
  ? { ownerDecisionRequired: decisionRequiredIds.has(task.taskId ?? '') }
364
463
  : {}),
365
464
  }));
366
- // Only report "PD pipeline is healthy" when total === 0. When
367
- // tasks.length === 0 but total > 0, the caller has paginated past the
368
- // last page — returning "healthy" here would mask the failures that
369
- // exist on earlier pages.
465
+ // PRI-955 (F11): "no failed tasks" ≠ "pipeline healthy" — derive the
466
+ // learning-queue health projection from the same state.db facts and let
467
+ // the UI show it next to the empty claim.
468
+ const consumerFlag = ctx.featureFlags && Object.hasOwn(ctx.featureFlags, 'internalization_auto_consumer')
469
+ ? ctx.featureFlags.internalization_auto_consumer
470
+ : undefined;
471
+ const consumerEnabled = consumerFlag ? consumerFlag.enabled === true : true; // 无该 flag 时无法断言禁用——不因缺失而宣称 disabled
472
+ const pipeline = await derivePipelineHealth(store, consumerEnabled);
473
+ // Only claim anything like "healthy" from the pipeline projection, never
474
+ // from the failed-task count alone. When tasks.length === 0 but
475
+ // total > 0, the caller has paginated past the last page.
370
476
  if (total === 0) {
371
477
  sendSuccess(res, {
372
478
  tasks: enriched,
373
479
  total,
374
- nextAction: 'No failed tasks. PD pipeline is healthy.',
480
+ pipeline,
481
+ nextAction: pipeline.status === 'healthy'
482
+ ? `No failed tasks in the queried range. ${pipeline.note}`
483
+ : `No failed tasks in the queried range. Pipeline status: ${pipeline.status} — ${pipeline.note}`,
375
484
  });
376
485
  }
377
486
  else {
378
- sendSuccess(res, { tasks: enriched, total });
487
+ sendSuccess(res, { tasks: enriched, total, pipeline });
379
488
  }
380
489
  }
381
490
  catch (err) {