create-principles-disciple 1.144.27 → 1.145.0

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 (338) hide show
  1. package/_release/product-identity.json +2 -2
  2. package/codex-adapter/dist/index.d.ts +2 -2
  3. package/codex-adapter/dist/index.js +1 -1
  4. package/codex-adapter/dist/ingestion/admission.js +16 -0
  5. package/codex-adapter/dist/ingestion/codex-version.d.ts +10 -4
  6. package/codex-adapter/dist/ingestion/codex-version.js +10 -4
  7. package/codex-adapter/dist/ingestion/explicit-owner-statement.d.ts +48 -0
  8. package/codex-adapter/dist/ingestion/explicit-owner-statement.js +123 -0
  9. package/codex-adapter/dist/ingestion/ingestion.js +50 -1
  10. package/codex-adapter/dist/pd-hook.js +66 -3
  11. package/codex-adapter/dist/worker/workspace-worker.d.ts +14 -0
  12. package/codex-adapter/dist/worker/workspace-worker.js +194 -11
  13. package/codex-adapter/package.json +2 -2
  14. package/console/dist/server/models/ApprovalsConsoleModel.d.ts +2 -2
  15. package/console/dist/server/models/ApprovalsConsoleModel.js +1 -1
  16. package/console/dist/server/models/ApprovalsGroupedConsoleModel.js +1 -1
  17. package/console/dist/server/models/PrinciplesConsoleModel.d.ts +26 -1
  18. package/console/dist/server/models/PrinciplesConsoleModel.js +18 -1
  19. package/console/dist/server/routes/activations.js +4 -4
  20. package/console/dist/server/routes/approvals.js +2 -2
  21. package/console/dist/server/routes/failed-tasks.d.ts +9 -0
  22. package/console/dist/server/routes/failed-tasks.js +116 -7
  23. package/console/dist/server/routes/principles.js +73 -1
  24. package/console/dist/shared/prompt-injection-contract.d.ts +1 -1
  25. package/console/dist/ui/api.d.ts +5 -5
  26. package/console/dist/ui/i18n/en.json +13 -2
  27. package/console/dist/ui/i18n/zh-CN.json +13 -2
  28. package/console/dist/ui/pages/activation/ActivationPage.js +2 -1
  29. package/console/dist/ui/pages/failed-tasks/FailedTasksPage.js +42 -4
  30. package/console/dist/ui/pages/focus/FocusPage.js +3 -3
  31. package/console/dist/ui/pages/principles/PrinciplesPage.js +7 -2
  32. package/console/dist/ui/utils/validators.d.ts +6 -2
  33. package/console/dist/ui/utils/validators.js +19 -4
  34. package/console/dist/web/assets/app.css +26 -0
  35. package/console/dist/web/assets/app.js +24 -24
  36. package/console/package.json +4 -4
  37. package/core/dist/host/host-installer.d.ts +3 -1
  38. package/core/dist/host/host-installer.d.ts.map +1 -1
  39. package/core/dist/host/host-installer.js.map +1 -1
  40. package/core/dist/runtime-v2/admission-gate.d.ts +8 -0
  41. package/core/dist/runtime-v2/admission-gate.d.ts.map +1 -1
  42. package/core/dist/runtime-v2/admission-gate.js +3 -2
  43. package/core/dist/runtime-v2/admission-gate.js.map +1 -1
  44. package/core/dist/runtime-v2/config/pd-config-defaults.d.ts.map +1 -1
  45. package/core/dist/runtime-v2/config/pd-config-defaults.js +16 -1
  46. package/core/dist/runtime-v2/config/pd-config-defaults.js.map +1 -1
  47. package/core/dist/runtime-v2/context-payload.d.ts +43 -3
  48. package/core/dist/runtime-v2/context-payload.d.ts.map +1 -1
  49. package/core/dist/runtime-v2/context-payload.js +15 -1
  50. package/core/dist/runtime-v2/context-payload.js.map +1 -1
  51. package/core/dist/runtime-v2/evidence-guards.d.ts +6 -0
  52. package/core/dist/runtime-v2/evidence-guards.d.ts.map +1 -1
  53. package/core/dist/runtime-v2/evidence-guards.js +11 -0
  54. package/core/dist/runtime-v2/evidence-guards.js.map +1 -1
  55. package/core/dist/runtime-v2/evolution/evolution-types.d.ts +4 -4
  56. package/core/dist/runtime-v2/evolution/evolution-types.d.ts.map +1 -1
  57. package/core/dist/runtime-v2/evolution/evolution-types.js +1 -1
  58. package/core/dist/runtime-v2/evolution/evolution-types.js.map +1 -1
  59. package/core/dist/runtime-v2/feature-flags/feature-flag-contract.d.ts.map +1 -1
  60. package/core/dist/runtime-v2/feature-flags/feature-flag-contract.js +6 -0
  61. package/core/dist/runtime-v2/feature-flags/feature-flag-contract.js.map +1 -1
  62. package/core/dist/runtime-v2/feature-flags/feature-flag-lifecycle.d.ts.map +1 -1
  63. package/core/dist/runtime-v2/feature-flags/feature-flag-lifecycle.js +8 -0
  64. package/core/dist/runtime-v2/feature-flags/feature-flag-lifecycle.js.map +1 -1
  65. package/core/dist/runtime-v2/index.d.ts +5 -2
  66. package/core/dist/runtime-v2/index.d.ts.map +1 -1
  67. package/core/dist/runtime-v2/index.js +3 -1
  68. package/core/dist/runtime-v2/index.js.map +1 -1
  69. package/core/dist/runtime-v2/internalization/evaluator-output.d.ts +11 -0
  70. package/core/dist/runtime-v2/internalization/evaluator-output.d.ts.map +1 -1
  71. package/core/dist/runtime-v2/internalization/evaluator-output.js +10 -0
  72. package/core/dist/runtime-v2/internalization/evaluator-output.js.map +1 -1
  73. package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.d.ts +3 -3
  74. package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.d.ts.map +1 -1
  75. package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.js +49 -5
  76. package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.js.map +1 -1
  77. package/core/dist/runtime-v2/internalization/evaluator-runner.d.ts.map +1 -1
  78. package/core/dist/runtime-v2/internalization/evaluator-runner.js +13 -16
  79. package/core/dist/runtime-v2/internalization/evaluator-runner.js.map +1 -1
  80. package/core/dist/runtime-v2/internalization/owner-decision-review.d.ts.map +1 -1
  81. package/core/dist/runtime-v2/internalization/owner-decision-review.js +5 -1
  82. package/core/dist/runtime-v2/internalization/owner-decision-review.js.map +1 -1
  83. package/core/dist/runtime-v2/internalization/owner-review.d.ts +2 -2
  84. package/core/dist/runtime-v2/internalization/owner-review.js +2 -2
  85. package/core/dist/runtime-v2/internalization/scribe-output.d.ts +12 -0
  86. package/core/dist/runtime-v2/internalization/scribe-output.d.ts.map +1 -1
  87. package/core/dist/runtime-v2/internalization/scribe-output.js +20 -0
  88. package/core/dist/runtime-v2/internalization/scribe-output.js.map +1 -1
  89. package/core/dist/runtime-v2/internalization/scribe-prompt-builder.d.ts +32 -1
  90. package/core/dist/runtime-v2/internalization/scribe-prompt-builder.d.ts.map +1 -1
  91. package/core/dist/runtime-v2/internalization/scribe-prompt-builder.js +38 -1
  92. package/core/dist/runtime-v2/internalization/scribe-prompt-builder.js.map +1 -1
  93. package/core/dist/runtime-v2/internalization-queue-read-model.d.ts +6 -0
  94. package/core/dist/runtime-v2/internalization-queue-read-model.d.ts.map +1 -1
  95. package/core/dist/runtime-v2/internalization-queue-read-model.js +8 -1
  96. package/core/dist/runtime-v2/internalization-queue-read-model.js.map +1 -1
  97. package/core/dist/runtime-v2/intervention-evidence-normalizer.d.ts.map +1 -1
  98. package/core/dist/runtime-v2/intervention-evidence-normalizer.js +10 -3
  99. package/core/dist/runtime-v2/intervention-evidence-normalizer.js.map +1 -1
  100. package/core/dist/runtime-v2/pain-ingress.d.ts +1 -1
  101. package/core/dist/runtime-v2/pain-ingress.d.ts.map +1 -1
  102. package/core/dist/runtime-v2/pain-signal-bridge.d.ts +68 -2
  103. package/core/dist/runtime-v2/pain-signal-bridge.d.ts.map +1 -1
  104. package/core/dist/runtime-v2/pain-signal-bridge.js +541 -18
  105. package/core/dist/runtime-v2/pain-signal-bridge.js.map +1 -1
  106. package/core/dist/runtime-v2/pain-signal-runtime-factory.d.ts.map +1 -1
  107. package/core/dist/runtime-v2/pain-signal-runtime-factory.js +4 -0
  108. package/core/dist/runtime-v2/pain-signal-runtime-factory.js.map +1 -1
  109. package/core/dist/runtime-v2/product-telemetry/snapshot-contract.d.ts +2 -2
  110. package/core/dist/runtime-v2/product-telemetry/snapshot-contract.d.ts.map +1 -1
  111. package/core/dist/runtime-v2/product-telemetry/snapshot-contract.js +2 -2
  112. package/core/dist/runtime-v2/product-telemetry/snapshot-contract.js.map +1 -1
  113. package/core/dist/runtime-v2/store/candidate/admission-document.d.ts +90 -0
  114. package/core/dist/runtime-v2/store/candidate/admission-document.d.ts.map +1 -0
  115. package/core/dist/runtime-v2/store/candidate/admission-document.js +142 -0
  116. package/core/dist/runtime-v2/store/candidate/admission-document.js.map +1 -0
  117. package/core/dist/runtime-v2/store/candidate/candidate-store.d.ts +53 -0
  118. package/core/dist/runtime-v2/store/candidate/candidate-store.d.ts.map +1 -1
  119. package/core/dist/runtime-v2/store/candidate/memory-candidate-store.d.ts +35 -0
  120. package/core/dist/runtime-v2/store/candidate/memory-candidate-store.d.ts.map +1 -1
  121. package/core/dist/runtime-v2/store/candidate/memory-candidate-store.js +48 -0
  122. package/core/dist/runtime-v2/store/candidate/memory-candidate-store.js.map +1 -1
  123. package/core/dist/runtime-v2/store/candidate/sqlite-candidate-store.d.ts +44 -0
  124. package/core/dist/runtime-v2/store/candidate/sqlite-candidate-store.d.ts.map +1 -1
  125. package/core/dist/runtime-v2/store/candidate/sqlite-candidate-store.js +136 -0
  126. package/core/dist/runtime-v2/store/candidate/sqlite-candidate-store.js.map +1 -1
  127. package/core/dist/runtime-v2/store/context/sqlite-context-assembler.js.map +1 -1
  128. package/core/dist/runtime-v2/store/intervention-evidence-schema.js +1 -1
  129. package/core/dist/runtime-v2/store/runtime-state-manager.d.ts +65 -0
  130. package/core/dist/runtime-v2/store/runtime-state-manager.d.ts.map +1 -1
  131. package/core/dist/runtime-v2/store/runtime-state-manager.js +46 -0
  132. package/core/dist/runtime-v2/store/runtime-state-manager.js.map +1 -1
  133. package/core/dist/runtime-v2/store/sqlite-connection.d.ts.map +1 -1
  134. package/core/dist/runtime-v2/store/sqlite-connection.js +42 -0
  135. package/core/dist/runtime-v2/store/sqlite-connection.js.map +1 -1
  136. package/core/dist/runtime-v2/store/sqlite-intervention-evidence-store.d.ts +1 -1
  137. package/core/dist/runtime-v2/store/sqlite-intervention-evidence-store.d.ts.map +1 -1
  138. package/core/dist/runtime-v2/store/sqlite-intervention-evidence-store.js.map +1 -1
  139. package/core/dist/runtime-v2/store/task/sqlite-family-reopen.d.ts +19 -0
  140. package/core/dist/runtime-v2/store/task/sqlite-family-reopen.d.ts.map +1 -0
  141. package/core/dist/runtime-v2/store/task/sqlite-family-reopen.js +36 -0
  142. package/core/dist/runtime-v2/store/task/sqlite-family-reopen.js.map +1 -0
  143. package/core/dist/runtime-v2/store/task/sqlite-task-store.d.ts +23 -0
  144. package/core/dist/runtime-v2/store/task/sqlite-task-store.d.ts.map +1 -1
  145. package/core/dist/runtime-v2/store/task/sqlite-task-store.js +50 -0
  146. package/core/dist/runtime-v2/store/task/sqlite-task-store.js.map +1 -1
  147. package/core/dist/runtime-v2/task-status.d.ts +3 -1
  148. package/core/dist/runtime-v2/task-status.d.ts.map +1 -1
  149. package/core/dist/runtime-v2/task-status.js +1 -1
  150. package/core/dist/runtime-v2/task-status.js.map +1 -1
  151. package/core/dist/runtime-v2/trajectory-schema.d.ts.map +1 -1
  152. package/core/dist/runtime-v2/trajectory-schema.js +11 -0
  153. package/core/dist/runtime-v2/trajectory-schema.js.map +1 -1
  154. package/core/dist/runtime-v2/types/event-types.d.ts +2 -2
  155. package/core/dist/runtime-v2/types/event-types.d.ts.map +1 -1
  156. package/core/dist/runtime-v2/types/event-types.js +1 -1
  157. package/core/dist/runtime-v2/types/event-types.js.map +1 -1
  158. package/core/dist/runtime-v2/types/evidence-chain-contract.d.ts +1 -1
  159. package/core/dist/runtime-v2/types/evidence-chain-contract.d.ts.map +1 -1
  160. package/core/dist/runtime-v2/types/evidence-chain-contract.js +2 -2
  161. package/core/dist/runtime-v2/types/evidence-chain-contract.js.map +1 -1
  162. package/core/dist/runtime-v2/types/intervention-evidence-contract.d.ts +3 -3
  163. package/core/dist/runtime-v2/types/intervention-evidence-contract.d.ts.map +1 -1
  164. package/core/dist/runtime-v2/types/intervention-evidence-contract.js +1 -0
  165. package/core/dist/runtime-v2/types/intervention-evidence-contract.js.map +1 -1
  166. package/core/dist/telemetry-event.d.ts +2 -2
  167. package/core/dist/telemetry-event.d.ts.map +1 -1
  168. package/core/dist/telemetry-event.js +4 -0
  169. package/core/dist/telemetry-event.js.map +1 -1
  170. package/core/dist/trajectory-types.d.ts +2 -2
  171. package/core/dist/trajectory-types.d.ts.map +1 -1
  172. package/core/package.json +3 -3
  173. package/dist/index.d.ts.map +1 -1
  174. package/dist/index.js +84 -8
  175. package/dist/index.js.map +1 -1
  176. package/dist/installer.d.ts +42 -11
  177. package/dist/installer.d.ts.map +1 -1
  178. package/dist/installer.js +140 -36
  179. package/dist/installer.js.map +1 -1
  180. package/dist/installers/index.d.ts +18 -7
  181. package/dist/installers/index.d.ts.map +1 -1
  182. package/dist/installers/index.js +13 -4
  183. package/dist/installers/index.js.map +1 -1
  184. package/dist/installers/workbuddy-host-installer.d.ts +246 -0
  185. package/dist/installers/workbuddy-host-installer.d.ts.map +1 -0
  186. package/dist/installers/workbuddy-host-installer.js +1419 -0
  187. package/dist/installers/workbuddy-host-installer.js.map +1 -0
  188. package/dist/mvp-config.d.ts +24 -0
  189. package/dist/mvp-config.d.ts.map +1 -1
  190. package/dist/mvp-config.js +36 -0
  191. package/dist/mvp-config.js.map +1 -1
  192. package/dist/prompts.d.ts +13 -0
  193. package/dist/prompts.d.ts.map +1 -1
  194. package/dist/prompts.js +45 -8
  195. package/dist/prompts.js.map +1 -1
  196. package/dist/uninstaller.d.ts +1 -1
  197. package/dist/uninstaller.d.ts.map +1 -1
  198. package/dist/uninstaller.js +29 -4
  199. package/dist/uninstaller.js.map +1 -1
  200. package/host-runtime/dist/governance-signal-admission.d.ts +63 -0
  201. package/host-runtime/dist/governance-signal-admission.js +322 -53
  202. package/host-runtime/dist/host-liveness-contract.js +7 -1
  203. package/host-runtime/dist/index.d.ts +1 -0
  204. package/host-runtime/dist/index.js +5 -1
  205. package/host-runtime/dist/internalization-consumer-cycle.d.ts +12 -0
  206. package/host-runtime/dist/internalization-consumer-cycle.js +43 -1
  207. package/host-runtime/dist/intervention-evidence-ingress.d.ts +1 -1
  208. package/host-runtime/dist/product-telemetry/service.js +1 -1
  209. package/host-runtime/dist/production-pain-evidence.d.ts +59 -1
  210. package/host-runtime/dist/production-pain-evidence.js +200 -17
  211. package/host-runtime/dist/prompt-activation-deliverability.js +5 -4
  212. package/host-runtime/dist/prompt-injection-event-evidence.d.ts +1 -1
  213. package/host-runtime/dist/prompt-injection-event-evidence.js +5 -1
  214. package/host-runtime/dist/prompt-injection-projection.d.ts +14 -5
  215. package/host-runtime/dist/prompt-injection-projection.js +30 -16
  216. package/host-runtime/dist/workbuddy-worker-status.d.ts +24 -0
  217. package/host-runtime/dist/workbuddy-worker-status.js +52 -0
  218. package/host-runtime/package.json +3 -3
  219. package/install-layout/dist/index.d.ts +18 -1
  220. package/install-layout/dist/index.js +15 -2
  221. package/install-layout/package.json +1 -1
  222. package/package.json +6 -5
  223. package/pd-cli/dist/commands/candidate.d.ts.map +1 -1
  224. package/pd-cli/dist/commands/candidate.js +44 -4
  225. package/pd-cli/dist/commands/candidate.js.map +1 -1
  226. package/pd-cli/dist/commands/codex-worker.d.ts.map +1 -1
  227. package/pd-cli/dist/commands/codex-worker.js +30 -1
  228. package/pd-cli/dist/commands/codex-worker.js.map +1 -1
  229. package/pd-cli/dist/commands/config-doctor.d.ts.map +1 -1
  230. package/pd-cli/dist/commands/config-doctor.js +19 -4
  231. package/pd-cli/dist/commands/config-doctor.js.map +1 -1
  232. package/pd-cli/dist/commands/diagnose.d.ts.map +1 -1
  233. package/pd-cli/dist/commands/diagnose.js +4 -1
  234. package/pd-cli/dist/commands/diagnose.js.map +1 -1
  235. package/pd-cli/dist/commands/health-codex-install.d.ts +85 -0
  236. package/pd-cli/dist/commands/health-codex-install.d.ts.map +1 -0
  237. package/pd-cli/dist/commands/health-codex-install.js +207 -0
  238. package/pd-cli/dist/commands/health-codex-install.js.map +1 -0
  239. package/pd-cli/dist/commands/health-codex.d.ts.map +1 -1
  240. package/pd-cli/dist/commands/health-codex.js +78 -0
  241. package/pd-cli/dist/commands/health-codex.js.map +1 -1
  242. package/pd-cli/dist/commands/pain-list.d.ts +3 -2
  243. package/pd-cli/dist/commands/pain-list.d.ts.map +1 -1
  244. package/pd-cli/dist/commands/pain-list.js +10 -10
  245. package/pd-cli/dist/commands/pain-list.js.map +1 -1
  246. package/pd-cli/dist/commands/pain-record.d.ts +2 -0
  247. package/pd-cli/dist/commands/pain-record.d.ts.map +1 -1
  248. package/pd-cli/dist/commands/pain-record.js +36 -0
  249. package/pd-cli/dist/commands/pain-record.js.map +1 -1
  250. package/pd-cli/dist/commands/pain-retry.d.ts +6 -0
  251. package/pd-cli/dist/commands/pain-retry.d.ts.map +1 -1
  252. package/pd-cli/dist/commands/pain-retry.js +152 -5
  253. package/pd-cli/dist/commands/pain-retry.js.map +1 -1
  254. package/pd-cli/dist/commands/runtime-activation.d.ts +1 -1
  255. package/pd-cli/dist/commands/runtime-activation.d.ts.map +1 -1
  256. package/pd-cli/dist/commands/runtime-activation.js +3 -3
  257. package/pd-cli/dist/commands/runtime-activation.js.map +1 -1
  258. package/pd-cli/dist/commands/workbuddy-worker.d.ts +19 -0
  259. package/pd-cli/dist/commands/workbuddy-worker.d.ts.map +1 -0
  260. package/pd-cli/dist/commands/workbuddy-worker.js +168 -0
  261. package/pd-cli/dist/commands/workbuddy-worker.js.map +1 -0
  262. package/pd-cli/dist/index.js +29 -1
  263. package/pd-cli/dist/index.js.map +1 -1
  264. package/pd-cli/dist/resolve-workspace.d.ts +14 -2
  265. package/pd-cli/dist/resolve-workspace.d.ts.map +1 -1
  266. package/pd-cli/dist/resolve-workspace.js +88 -21
  267. package/pd-cli/dist/resolve-workspace.js.map +1 -1
  268. package/pd-cli/dist/services/admission-write-gate.d.ts +24 -0
  269. package/pd-cli/dist/services/admission-write-gate.d.ts.map +1 -0
  270. package/pd-cli/dist/services/admission-write-gate.js +41 -0
  271. package/pd-cli/dist/services/admission-write-gate.js.map +1 -0
  272. package/pd-cli/dist/services/pd-config-loader.d.ts +39 -7
  273. package/pd-cli/dist/services/pd-config-loader.d.ts.map +1 -1
  274. package/pd-cli/dist/services/pd-config-loader.js +111 -18
  275. package/pd-cli/dist/services/pd-config-loader.js.map +1 -1
  276. package/pd-cli/dist/services/rulehost-pipeline-runner.d.ts +10 -0
  277. package/pd-cli/dist/services/rulehost-pipeline-runner.d.ts.map +1 -1
  278. package/pd-cli/dist/services/rulehost-pipeline-runner.js +59 -4
  279. package/pd-cli/dist/services/rulehost-pipeline-runner.js.map +1 -1
  280. package/pd-cli/package.json +4 -3
  281. package/plugin/dist/bundle.js +564 -509
  282. package/plugin/dist/governance-audit.js +1 -1
  283. package/plugin/dist/openclaw.plugin.json +1 -1
  284. package/plugin/dist/rulehost-evidence.js +1 -1
  285. package/plugin/openclaw.plugin.json +1 -1
  286. package/plugin/package.json +3 -3
  287. package/release-manager/dist/index.d.ts.map +1 -1
  288. package/release-manager/dist/index.js +84 -8
  289. package/release-manager/dist/index.js.map +1 -1
  290. package/release-manager/dist/installer.d.ts +42 -11
  291. package/release-manager/dist/installer.d.ts.map +1 -1
  292. package/release-manager/dist/installer.js +140 -36
  293. package/release-manager/dist/installer.js.map +1 -1
  294. package/release-manager/dist/installers/index.d.ts +18 -7
  295. package/release-manager/dist/installers/index.d.ts.map +1 -1
  296. package/release-manager/dist/installers/index.js +13 -4
  297. package/release-manager/dist/installers/index.js.map +1 -1
  298. package/release-manager/dist/installers/workbuddy-host-installer.d.ts +246 -0
  299. package/release-manager/dist/installers/workbuddy-host-installer.d.ts.map +1 -0
  300. package/release-manager/dist/installers/workbuddy-host-installer.js +1419 -0
  301. package/release-manager/dist/installers/workbuddy-host-installer.js.map +1 -0
  302. package/release-manager/dist/mvp-config.d.ts +24 -0
  303. package/release-manager/dist/mvp-config.d.ts.map +1 -1
  304. package/release-manager/dist/mvp-config.js +36 -0
  305. package/release-manager/dist/mvp-config.js.map +1 -1
  306. package/release-manager/dist/prompts.d.ts +13 -0
  307. package/release-manager/dist/prompts.d.ts.map +1 -1
  308. package/release-manager/dist/prompts.js +45 -8
  309. package/release-manager/dist/prompts.js.map +1 -1
  310. package/release-manager/dist/uninstaller.d.ts +1 -1
  311. package/release-manager/dist/uninstaller.d.ts.map +1 -1
  312. package/release-manager/dist/uninstaller.js +29 -4
  313. package/release-manager/dist/uninstaller.js.map +1 -1
  314. package/release-manager/package.json +5 -4
  315. package/workbuddy-adapter/dist/codec/index.d.ts +2 -0
  316. package/workbuddy-adapter/dist/codec/index.js +2 -0
  317. package/workbuddy-adapter/dist/codec/input-decoder.d.ts +30 -0
  318. package/workbuddy-adapter/dist/codec/input-decoder.js +232 -0
  319. package/workbuddy-adapter/dist/codec/output-encoder.d.ts +62 -0
  320. package/workbuddy-adapter/dist/codec/output-encoder.js +75 -0
  321. package/workbuddy-adapter/dist/host-adapter.d.ts +33 -0
  322. package/workbuddy-adapter/dist/host-adapter.js +23 -0
  323. package/workbuddy-adapter/dist/index.d.ts +16 -0
  324. package/workbuddy-adapter/dist/index.js +16 -0
  325. package/workbuddy-adapter/dist/pd-hook.d.ts +17 -0
  326. package/workbuddy-adapter/dist/pd-hook.js +505 -0
  327. package/workbuddy-adapter/dist/tool-semantics.d.ts +22 -0
  328. package/workbuddy-adapter/dist/tool-semantics.js +33 -0
  329. package/workbuddy-adapter/dist/workbuddy-evidence-recorder.d.ts +50 -0
  330. package/workbuddy-adapter/dist/workbuddy-evidence-recorder.js +238 -0
  331. package/workbuddy-adapter/dist/worker/workspace-worker.d.ts +41 -0
  332. package/workbuddy-adapter/dist/worker/workspace-worker.js +219 -0
  333. package/workbuddy-adapter/dist/workspace-resolution.d.ts +15 -0
  334. package/workbuddy-adapter/dist/workspace-resolution.js +53 -0
  335. package/workbuddy-adapter/package.json +62 -0
  336. package/workbuddy-adapter/plugin/.codebuddy-plugin/plugin.json +6 -0
  337. package/workbuddy-adapter/plugin/hooks/hooks.json +62 -0
  338. package/workbuddy-adapter/plugin/scripts/wrapper.cjs +79 -0
@@ -40,6 +40,8 @@ import { promoteGovernanceEvidence, ensureGovernanceSchema } from './governance-
40
40
  import { loadPdConfigForPlugin } from './pd-config.js';
41
41
  // ─── Shared detector constants (one truth for both hosts) ───────────────────
42
42
  export const GOVERNANCE_STRONG_PAIN_SCORE = 70;
43
+ /** CR-07: rate-limit bucket key for explicit Owner statements (no keyword rule version). */
44
+ const EXPLICIT_OWNER_STATEMENT_RULE_VERSION = 9001;
43
45
  export const GOVERNANCE_STRONG_RATE_LIMIT_PER_HOUR = 5;
44
46
  export const GOVERNANCE_RATE_LIMIT_WINDOW_MS = 60 * 60 * 1000;
45
47
  /** Sync-only detection config: ambiguous candidates are NOT signals here (§12). */
@@ -292,6 +294,23 @@ function ensureGovernanceAdmissionSchema(db) {
292
294
  db.transaction(() => {
293
295
  for (const statement of ADMISSION_CREATE_STATEMENTS)
294
296
  db.exec(statement);
297
+ // SPEC-CODEX-PAIN-CLOSURE-001 CR-01: this store also writes tool_calls
298
+ // (admitToolFailure); make sure the host tool-use-id join column exists on
299
+ // workspaces whose base schema predates it (idempotent additive ALTER —
300
+ // only when the table itself exists: governance-only databases without a
301
+ // tool_calls table are a valid pre-existing state, not an error).
302
+ const hasToolCalls = db.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'tool_calls'").get() !== undefined;
303
+ if (hasToolCalls) {
304
+ try {
305
+ db.exec('ALTER TABLE tool_calls ADD COLUMN tool_use_id TEXT');
306
+ db.exec('CREATE INDEX IF NOT EXISTS idx_tool_calls_session_tool_use_id ON tool_calls(session_id, tool_use_id)');
307
+ }
308
+ catch (err) {
309
+ const message = err instanceof Error ? err.message : String(err);
310
+ if (!message.includes('duplicate column name'))
311
+ throw err;
312
+ }
313
+ }
295
314
  })();
296
315
  }
297
316
  function openAdmissionStore(workspaceDir, factory) {
@@ -299,8 +318,9 @@ function openAdmissionStore(workspaceDir, factory) {
299
318
  if (!fs.existsSync(dbPath)) {
300
319
  return { ok: false, reason: 'trajectory_db_not_found', nextAction: 'initialize the selected PD workspace (pd runtime init) before enabling conversation ingestion' };
301
320
  }
321
+ let db;
302
322
  try {
303
- const db = factory ? factory(dbPath) : new Database(dbPath);
323
+ db = factory ? factory(dbPath) : new Database(dbPath);
304
324
  db.pragma('busy_timeout = 5000');
305
325
  db.pragma('journal_mode = WAL');
306
326
  ensureGovernanceAdmissionSchema(db);
@@ -308,11 +328,19 @@ function openAdmissionStore(workspaceDir, factory) {
308
328
  // tails): one open makes the whole governance schema ready.
309
329
  ensureGovernanceSchema(db);
310
330
  return { db, close: () => { try {
311
- db.close();
331
+ db?.close();
312
332
  }
313
333
  catch { /* write result already determined */ } } };
314
334
  }
315
335
  catch (error) {
336
+ // PRI-624 discipline: a half-opened handle (e.g. a corrupt/non-SQLite
337
+ // file that passes the lazy open but fails the first pragma) must be
338
+ // closed here — the worker retries every cycle, so a leaked handle would
339
+ // pin the workspace trajectory.db (and its temp dir) forever.
340
+ try {
341
+ db?.close();
342
+ }
343
+ catch { /* best-effort cleanup of an unusable handle */ }
316
344
  const detail = error instanceof Error ? error.message.slice(0, 200) : String(error).slice(0, 200);
317
345
  return { ok: false, reason: `trajectory_database_unavailable:${detail}`, nextAction: 'inspect or repair the selected PD trajectory database' };
318
346
  }
@@ -356,9 +384,11 @@ function tryConsumeRateLimit({ db, rootSessionId, ruleVersion, now }) {
356
384
  return true;
357
385
  }
358
386
  function insertCorrectionPain({ db, candidate, workspaceDir, painId, detection, nowIso }) {
359
- const reason = detection.matchedTerms.length > 0
360
- ? `User correction detected: ${detection.matchedTerms.join(', ')}`
361
- : 'User correction detected';
387
+ const reason = candidate.explicitOwnerStatement === true
388
+ ? 'Codex explicit owner statement (PD记录痛苦 prefix)'
389
+ : detection !== undefined && detection.matchedTerms.length > 0
390
+ ? `User correction detected: ${detection.matchedTerms.join(', ')}`
391
+ : 'User correction detected';
362
392
  // P1-1 privacy (review round 2): the FULL raw text must pass through the
363
393
  // sanitizer BEFORE any truncation — sanitizeString's own contract is
364
394
  // redact → path-replace → bound. Slicing first would split a token that
@@ -372,12 +402,32 @@ function insertCorrectionPain({ db, candidate, workspaceDir, painId, detection,
372
402
  .run(candidate.rootSessionId, 'user_correction', GOVERNANCE_STRONG_PAIN_SCORE, reason.slice(0, MAX_REASON_BOUND), 'severe', 'system_infer', null, excerpt, painId, null, candidate.hostKind, nowIso);
373
403
  return reason;
374
404
  }
375
- function taskSubmissionPayload(candidate, workspaceDir, reason) {
405
+ /**
406
+ * ONE builder for the tool_failure task payload shape — the Codex inline
407
+ * admission path and the WorkBuddy bare-pain sweep must produce the SAME
408
+ * shape, differing only in the attributed host and the pain facts.
409
+ */
410
+ function toolFailureTaskPayload(fields) {
376
411
  // P1-1 privacy (review round 2): sanitize the FULL text before any
377
412
  // truncation — a token crossing the 200-char boundary must be redacted, not
378
413
  // split into a fragment that survives verbatim. sanitizeString bounds itself.
379
- const payload = candidate.kind === 'user_correction'
380
- ? {
414
+ const payload = {
415
+ painType: 'tool_failure',
416
+ source: fields.toolName,
417
+ reason: fields.reason || `tool=${fields.toolName}`,
418
+ score: fields.score,
419
+ sessionId: fields.sessionId,
420
+ hostKind: fields.hostKind,
421
+ evidence: [{ sourceRef: `governance_observation:${fields.logicalObservationKey}`, note: sanitizeString(fields.reason || fields.toolName, fields.workspaceDir) }],
422
+ };
423
+ return JSON.stringify(payload);
424
+ }
425
+ function taskSubmissionPayload(candidate, workspaceDir, reason) {
426
+ if (candidate.kind === 'user_correction') {
427
+ // P1-1 privacy (review round 2): sanitize the FULL text before any
428
+ // truncation — a token crossing the 200-char boundary must be redacted, not
429
+ // split into a fragment that survives verbatim. sanitizeString bounds itself.
430
+ const payload = {
381
431
  painType: 'user_frustration',
382
432
  source: 'user_correction',
383
433
  reason: reason || 'User correction detected',
@@ -385,39 +435,56 @@ function taskSubmissionPayload(candidate, workspaceDir, reason) {
385
435
  sessionId: candidate.rootSessionId,
386
436
  hostKind: 'codex',
387
437
  evidence: [{ sourceRef: `governance_observation:${candidate.logicalObservationKey}`, note: sanitizeString(candidate.text, workspaceDir) }],
388
- }
389
- : {
390
- painType: 'tool_failure',
391
- source: candidate.toolName,
392
- reason: reason || `tool=${candidate.toolName}`,
393
- score: 70,
394
- sessionId: candidate.rootSessionId,
395
- hostKind: 'codex',
396
- evidence: [{ sourceRef: `governance_observation:${candidate.logicalObservationKey}`, note: sanitizeString(reason || candidate.toolName, workspaceDir) }],
438
+ // SPEC-CODEX-PAIN-CLOSURE-001 CR-02 (merged from main): an explicit
439
+ // Owner statement rides as classified correction evidence — never
440
+ // summarized by an AI, never inside evidence[] pseudo-trace entries.
441
+ ...(candidate.explicitOwnerStatement === true
442
+ ? { correctionEvidence: { text: sanitizeString(candidate.text, workspaceDir), classification: 'owner_statement', channel: 'codex_explicit_prefix' } }
443
+ : {}),
397
444
  };
398
- return JSON.stringify(payload);
445
+ return JSON.stringify(payload);
446
+ }
447
+ return toolFailureTaskPayload({
448
+ hostKind: candidate.hostKind,
449
+ logicalObservationKey: candidate.logicalObservationKey,
450
+ sessionId: candidate.rootSessionId,
451
+ toolName: candidate.toolName,
452
+ reason,
453
+ score: 70,
454
+ workspaceDir,
455
+ });
399
456
  }
400
- function insertAdmissionMarker({ db, candidate, workspaceDir, canonicalPainId, ruleVersion, reason, nowIso }) {
401
- const payload = taskSubmissionPayload(candidate, workspaceDir, reason);
457
+ function insertAdmissionMarker({ db, identity, canonicalPainId, ruleVersion, reason, payloadJson, nowIso }) {
402
458
  db.prepare(`INSERT INTO governance_signal_admissions
403
459
  (host_kind, logical_observation_key, rollout_identity, root_session_id, signal_kind, decision, canonical_pain_id, diagnostician_task_id, rule_version, reason, task_payload_json, created_at, updated_at)
404
460
  VALUES (?, ?, ?, ?, ?, 'admitted', ?, NULL, ?, ?, ?, ?, ?)`)
405
- .run(candidate.hostKind, candidate.logicalObservationKey, candidate.rolloutIdentity, candidate.rootSessionId, candidate.kind, canonicalPainId, ruleVersion, reason.slice(0, MAX_REASON_BOUND) || null, payload, nowIso, nowIso);
461
+ .run(identity.hostKind, identity.logicalObservationKey, identity.rolloutIdentity, identity.rootSessionId, identity.signalKind, canonicalPainId, ruleVersion, reason.slice(0, MAX_REASON_BOUND) || null, payloadJson, nowIso, nowIso);
406
462
  }
407
463
  function admitCorrection({ db, candidate, workspaceDir, now, nowIso, keywordStore, onDegradation }) {
464
+ // SPEC-CODEX-PAIN-CLOSURE-001 CR-07: an EXPLICIT Owner statement
465
+ // (`PD记录痛苦:<text>` in the current Codex message) does not pass through
466
+ // keyword detection at all — the explicit request IS the signal. Ordinary
467
+ // conversation keeps the STRONG+high-precision gate unchanged.
468
+ const isExplicit = candidate.explicitOwnerStatement === true;
408
469
  // Detection runs OUTSIDE the write transaction (pure scan of the shared
409
- // keyword store); its outcome feeds the transaction below.
410
- const detection = evaluateCorrectionSignal({
411
- workspaceDir,
412
- text: candidate.text,
413
- sessionId: candidate.rootSessionId,
414
- detectedAt: nowIso,
415
- ...(keywordStore ? { store: keywordStore } : {}),
416
- ...(onDegradation ? { onDegradation } : {}),
417
- });
418
- const isStrongHighPrecision = detection.output.isSignal
419
- && detection.output.strength === 'STRONG'
420
- && detection.output.matchedPrecision === 'high';
470
+ // keyword store); its outcome feeds the transaction below. An EXPLICIT
471
+ // statement skips detection entirely — the Owner's explicit request is the
472
+ // signal (CR-07), so there is no detection result and no rule version.
473
+ const detection = isExplicit
474
+ ? undefined
475
+ : evaluateCorrectionSignal({
476
+ workspaceDir,
477
+ text: candidate.text,
478
+ sessionId: candidate.rootSessionId,
479
+ detectedAt: nowIso,
480
+ ...(keywordStore ? { store: keywordStore } : {}),
481
+ ...(onDegradation ? { onDegradation } : {}),
482
+ });
483
+ const isStrongHighPrecision = isExplicit
484
+ || (detection !== undefined
485
+ && detection.output.isSignal
486
+ && detection.output.strength === 'STRONG'
487
+ && detection.output.matchedPrecision === 'high');
421
488
  if (!isStrongHighPrecision) {
422
489
  // Ordinary conversation / weak negation / ambiguous candidates: no pain,
423
490
  // no task, no quota, and no marker (SPEC §18 scenario 6 — silence).
@@ -445,12 +512,25 @@ function admitCorrection({ db, candidate, workspaceDir, now, nowIso, keywordStor
445
512
  db.prepare('UPDATE pain_events SET host_kind = ? WHERE canonical_pain_id = ? AND host_kind IS NULL').run(candidate.hostKind, painId);
446
513
  }
447
514
  else {
448
- if (!tryConsumeRateLimit({ db, rootSessionId: candidate.rootSessionId, ruleVersion: detection.ruleVersion, now })) {
515
+ // CR-07: explicit submissions carry their own rate-limit bucket
516
+ // (detection never ran — no keyword rule version exists for them).
517
+ const rateLimitRuleVersion = isExplicit ? EXPLICIT_OWNER_STATEMENT_RULE_VERSION : (detection?.ruleVersion ?? EXPLICIT_OWNER_STATEMENT_RULE_VERSION);
518
+ if (!tryConsumeRateLimit({ db, rootSessionId: candidate.rootSessionId, ruleVersion: rateLimitRuleVersion, now })) {
449
519
  throw new RateLimitedSignal();
450
520
  }
451
- reason = insertCorrectionPain({ db, candidate, workspaceDir, painId, detection: detection.output, nowIso });
521
+ reason = insertCorrectionPain({ db, candidate, workspaceDir, painId, detection: detection?.output, nowIso });
452
522
  }
453
- insertAdmissionMarker({ db, candidate, workspaceDir, canonicalPainId: painId, ruleVersion: detection.ruleVersion, reason, nowIso });
523
+ insertAdmissionMarker({
524
+ db,
525
+ identity: { hostKind: candidate.hostKind, logicalObservationKey: candidate.logicalObservationKey, rolloutIdentity: candidate.rolloutIdentity, rootSessionId: candidate.rootSessionId, signalKind: candidate.kind },
526
+ canonicalPainId: painId,
527
+ // Merged from main (CR-07): explicit owner statements never ran keyword
528
+ // detection, so `detection` is optional — no ruleVersion exists for them.
529
+ ruleVersion: detection?.ruleVersion ?? null,
530
+ reason,
531
+ payloadJson: taskSubmissionPayload(candidate, workspaceDir, reason),
532
+ nowIso,
533
+ });
454
534
  })();
455
535
  }
456
536
  catch (error) {
@@ -459,7 +539,7 @@ function admitCorrection({ db, candidate, workspaceDir, now, nowIso, keywordStor
459
539
  }
460
540
  throw error;
461
541
  }
462
- return { disposition: 'admitted', kind: 'user_correction', logicalObservationKey: candidate.logicalObservationKey, canonicalPainId: painId, duplicate, ruleVersion: detection.ruleVersion };
542
+ return { disposition: 'admitted', kind: 'user_correction', logicalObservationKey: candidate.logicalObservationKey, canonicalPainId: painId, duplicate, ruleVersion: detection?.ruleVersion ?? null };
463
543
  }
464
544
  function admitToolFailure({ db, candidate, workspaceDir, nowIso }) {
465
545
  const identity = deriveProductionToolPainIdentity({
@@ -468,6 +548,9 @@ function admitToolFailure({ db, candidate, workspaceDir, nowIso }) {
468
548
  turnId: candidate.hostTurnId,
469
549
  toolName: candidate.toolName,
470
550
  source: candidate.source,
551
+ // I1 identity parity: same call id the live handler derived from (the
552
+ // candidate builder forwards the host `tool_use_id`).
553
+ ...(candidate.toolCallId !== undefined ? { toolCallId: candidate.toolCallId } : {}),
471
554
  ...(candidate.toolInput !== undefined ? { toolInput: candidate.toolInput } : {}),
472
555
  ...(candidate.toolOutput !== undefined ? { toolOutput: candidate.toolOutput } : {}),
473
556
  });
@@ -475,7 +558,7 @@ function admitToolFailure({ db, candidate, workspaceDir, nowIso }) {
475
558
  // Admission gate — identical semantics to the live production handler on a
476
559
  // fresh process (ADR-0020 accepts cold in-memory cooldowns for Codex;
477
560
  // canonical idempotency is the guard).
478
- const sourceObservation = buildToolFailureObservation({ toolName: candidate.toolName, error: outcome.error, exitCode: outcome.exitCode });
561
+ const sourceObservation = buildToolFailureObservation({ toolName: candidate.toolName, error: outcome.error, exitCode: outcome.exitCode ?? undefined });
479
562
  const sourceKind = resolveSourceKind({
480
563
  observedAt: nowIso,
481
564
  workspaceId: workspaceDir,
@@ -483,7 +566,7 @@ function admitToolFailure({ db, candidate, workspaceDir, nowIso }) {
483
566
  toolName: candidate.toolName,
484
567
  failureSource: sourceObservation.failureSource,
485
568
  toolNotFound: sourceObservation.toolNotFound,
486
- nonZeroExit: outcome.exitCode !== 0,
569
+ nonZeroExit: outcome.exitCode !== null && outcome.exitCode !== 0,
487
570
  });
488
571
  // Risk evaluation mirrors the production handler: it reads the RAW params
489
572
  // (sanitized params replace paths with placeholders and would lose the
@@ -491,7 +574,7 @@ function admitToolFailure({ db, candidate, workspaceDir, nowIso }) {
491
574
  const rawFile = isRecord(outcome.params) ? (own(outcome.params, 'file_path') ?? own(outcome.params, 'path')) : undefined;
492
575
  const relativePath = String(rawFile ?? 'unknown').slice(0, 500);
493
576
  const isRisky = path.isAbsolute(relativePath) && !path.resolve(relativePath).startsWith(`${workspaceDir}${path.sep}`);
494
- const painScore = Math.min(100, (outcome.exitCode !== 0 ? 70 : 0) + (isRisky ? 20 : 0));
577
+ const painScore = Math.min(100, (outcome.failure ? 70 : 0) + (isRisky ? 20 : 0));
495
578
  const triage = evaluateTriage({ sourceKind, score: painScore, isRisky });
496
579
  const trigger = evaluateTriggerController({ triageResult: triage, isOwnerManual: false, isCooldownActive: false, isValid: true, score: painScore, sessionId: candidate.rootSessionId });
497
580
  const gateAdmitted = outcome.failure && PRODUCTION_WRITE_TOOLS.has(candidate.toolName) && trigger.shouldCreateDiagnosticTask;
@@ -503,18 +586,24 @@ function admitToolFailure({ db, candidate, workspaceDir, nowIso }) {
503
586
  let duplicateWithoutPain = false;
504
587
  let reason = '';
505
588
  db.transaction(() => {
506
- // Same duplicate probe as the production handler: an identical tool_calls
507
- // row means this exact call was already recorded.
508
- if (db.prepare('SELECT 1 FROM tool_calls WHERE session_id = ? AND tool_name = ? AND params_json = ? AND outcome = ? AND exit_code IS ? AND error_message IS ? AND result_preview IS ?')
509
- .get(candidate.rootSessionId, candidate.toolName, paramsJson, outcome.failure ? 'failure' : 'success', outcome.exitCode, outcome.error ?? null, resultPreview) !== undefined) {
589
+ // Same duplicate probes as the production handler: the host tool-call id
590
+ // (CR-01) is the stable join key; the content-shape probe stays the
591
+ // fallback for hosts/candidates without ids.
592
+ if (candidate.toolUseId !== undefined
593
+ && db.prepare('SELECT 1 FROM tool_calls WHERE session_id = ? AND tool_use_id = ?').get(candidate.rootSessionId, candidate.toolUseId) !== undefined) {
594
+ duplicate = true;
595
+ duplicateWithoutPain = db.prepare('SELECT 1 FROM pain_events WHERE canonical_pain_id = ?').get(painId) === undefined;
596
+ }
597
+ else if (db.prepare('SELECT 1 FROM tool_calls WHERE session_id = ? AND tool_name = ? AND params_json = ? AND outcome = ? AND exit_code IS ? AND error_message IS ? AND result_preview IS ?')
598
+ .get(candidate.rootSessionId, candidate.toolName, paramsJson, outcome.outcome, outcome.exitCode, outcome.error ?? null, resultPreview) !== undefined) {
510
599
  duplicate = true;
511
600
  duplicateWithoutPain = db.prepare('SELECT 1 FROM pain_events WHERE canonical_pain_id = ?').get(painId) === undefined;
512
601
  }
513
602
  else {
514
603
  db.prepare(`INSERT INTO sessions (session_id, started_at, updated_at) VALUES (?, ?, ?) ON CONFLICT(session_id) DO UPDATE SET updated_at = excluded.updated_at`)
515
604
  .run(candidate.rootSessionId, nowIso, nowIso);
516
- db.prepare(`INSERT INTO tool_calls (session_id, tool_name, outcome, duration_ms, exit_code, error_type, error_message, gfi_before, gfi_after, params_json, result_preview, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
517
- .run(candidate.rootSessionId, candidate.toolName, outcome.failure ? 'failure' : 'success', outcome.durationMs ?? null, outcome.exitCode, outcome.error ? outcome.error.split(/[\s:]/, 1)[0] : null, outcome.error ?? null, null, null, paramsJson, resultPreview, nowIso);
605
+ db.prepare(`INSERT INTO tool_calls (session_id, tool_name, outcome, duration_ms, exit_code, error_type, error_message, gfi_before, gfi_after, params_json, result_preview, created_at, tool_use_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
606
+ .run(candidate.rootSessionId, candidate.toolName, outcome.outcome, outcome.durationMs ?? null, outcome.exitCode, outcome.error ? outcome.error.split(/[\s:]/, 1)[0] : null, outcome.error ?? null, null, null, paramsJson, resultPreview, nowIso, candidate.toolUseId ?? null);
518
607
  reason = `tool=${candidate.toolName}; error=${outcome.error ?? `exit=${outcome.exitCode}`}; path=${relativePath}`;
519
608
  db.prepare(`INSERT INTO pain_events (session_id, source, score, reason, severity, origin, confidence, text, canonical_pain_id, runtime_task_id, host_kind, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
520
609
  .run(candidate.rootSessionId, sourceObservation.failureSource ?? 'tool_failure', painScore, reason, painScore >= 70 ? 'severe' : painScore >= 40 ? 'moderate' : 'mild', 'system_infer', null, null, painId, null, candidate.hostKind, nowIso);
@@ -523,7 +612,15 @@ function admitToolFailure({ db, candidate, workspaceDir, nowIso }) {
523
612
  // pain row; the IS NULL guard keeps any durable attribution.
524
613
  db.prepare('UPDATE pain_events SET host_kind = ? WHERE canonical_pain_id = ? AND host_kind IS NULL').run(candidate.hostKind, painId);
525
614
  if (!duplicateWithoutPain) {
526
- insertAdmissionMarker({ db, candidate, workspaceDir, canonicalPainId: painId, ruleVersion: null, reason: sanitizeString(reason, workspaceDir), nowIso });
615
+ insertAdmissionMarker({
616
+ db,
617
+ identity: { hostKind: candidate.hostKind, logicalObservationKey: candidate.logicalObservationKey, rolloutIdentity: candidate.rolloutIdentity, rootSessionId: candidate.rootSessionId, signalKind: candidate.kind },
618
+ canonicalPainId: painId,
619
+ ruleVersion: null,
620
+ reason: sanitizeString(reason, workspaceDir),
621
+ payloadJson: taskSubmissionPayload(candidate, workspaceDir, reason),
622
+ nowIso,
623
+ });
527
624
  }
528
625
  // A duplicate call row without an admitted pain mirrors the production
529
626
  // handler (no pain insert on duplicates) and records no marker, so a later
@@ -601,10 +698,25 @@ function isGovernanceTaskPayload(value) {
601
698
  }
602
699
  if (typeof own(value, 'score') !== 'number')
603
700
  return false;
604
- if (own(value, 'hostKind') !== 'codex')
701
+ // The admitting host is part of the frozen payload contract; the Diagnostician
702
+ // attribution must be one of the governed hosts (never free text).
703
+ const hostKind = own(value, 'hostKind');
704
+ if (hostKind !== 'openclaw' && hostKind !== 'codex' && hostKind !== 'workbuddy')
705
+ return false;
706
+ // CR-02 (merged from main): a validated explicit Owner statement is a
707
+ // first-class input basis — the evidence array may legitimately be empty for
708
+ // such submissions (the statement itself satisfies the empty-input-evidence
709
+ // admission condition).
710
+ const correctionEvidence = own(value, 'correctionEvidence');
711
+ const hasOwnerStatement = isRecord(correctionEvidence)
712
+ && own(correctionEvidence, 'classification') === 'owner_statement'
713
+ && own(correctionEvidence, 'channel') === 'codex_explicit_prefix'
714
+ && typeof own(correctionEvidence, 'text') === 'string'
715
+ && own(correctionEvidence, 'text').length > 0;
716
+ if (correctionEvidence !== undefined && !hasOwnerStatement)
605
717
  return false;
606
718
  const evidence = own(value, 'evidence');
607
- if (!Array.isArray(evidence) || evidence.length === 0 || evidence.length > 8)
719
+ if (!Array.isArray(evidence) || (evidence.length === 0 && !hasOwnerStatement) || evidence.length > 8)
608
720
  return false;
609
721
  for (const entry of evidence) {
610
722
  if (!isRecord(entry))
@@ -693,8 +805,16 @@ export async function ensureGovernanceDiagnosticianTask(input) {
693
805
  score: payloadParsed.score,
694
806
  sessionId: payloadParsed.sessionId,
695
807
  provenance: 'host_context_bound',
696
- hostKind: 'codex',
808
+ // The admitting host comes from the marker's own payload — the ONE
809
+ // durable attribution fact (existing codex markers keep 'codex',
810
+ // workbuddy markers attribute 'workbuddy').
811
+ hostKind: payloadParsed.hostKind,
697
812
  evidence: [...payloadParsed.evidence],
813
+ // CR-02: replay the explicit Owner statement verbatim (classified,
814
+ // channel-disclosed) so re-created tasks keep the same input basis.
815
+ ...(payloadParsed.correctionEvidence !== undefined
816
+ ? { correctionEvidence: { ...payloadParsed.correctionEvidence, sessionId: payloadParsed.sessionId } }
817
+ : {}),
698
818
  recordObservability: true,
699
819
  });
700
820
  // recordPain (async) leaves a cached bridge holding open SQLite
@@ -791,6 +911,129 @@ export async function ensureGovernanceContinuation(input) {
791
911
  close();
792
912
  }
793
913
  }
914
+ /**
915
+ * Admit bare host pains — failure rows the shared production pain handler
916
+ * already persisted (host_kind set, canonical_pain_id set, runtime_task_id
917
+ * NULL) but that no inline admission exists for. The WorkBuddy hook is the
918
+ * only evidence producer on that host (SPEC workbuddy-host-adapter §1: no
919
+ * transcript reads), so these rows would otherwise stay stranded as
920
+ * evidence-only forever: reconcile only continues ADMITTED markers, and no
921
+ * WorkBuddy admission step existed (review R2/P1).
922
+ *
923
+ * Policy: only failures scoring at/above the Codex tool_failure admission
924
+ * floor (GOVERNANCE_STRONG_PAIN_SCORE = 70) are queued for diagnosis — that
925
+ * floor is the existing signal-quality boundary for write-tool failures, and
926
+ * lower-scored failures stay evidence-only in pain_events/tool_calls rather
927
+ * than diluting the diagnostician queue. Idempotency reuses the ONE existing
928
+ * authorities: the unique admission marker (canonical_pain_id probe +
929
+ * UNIQUE logical key) and the deterministic task id, so repeated sweeps and
930
+ * crash restarts never mint a second task. Task creation reuses
931
+ * `ensureGovernanceDiagnosticianTask` unchanged (including its Case-B link
932
+ * repair). Evidence promotion is deliberately NOT attempted here: the
933
+ * promotion substrate (governance_rollouts/observations) is fed by transcript
934
+ * ingestion, which the WorkBuddy MVP excludes.
935
+ */
936
+ export async function admitBareHostPains(input) {
937
+ const opened = openAdmissionStore(input.workspaceDir, input.databaseFactory);
938
+ if (!('db' in opened)) {
939
+ return { ok: false, reason: opened.reason, nextAction: opened.nextAction, admitted: 0, ensured: 0, skippedLowScore: 0, skippedInvalid: 0, degradations: [opened.reason] };
940
+ }
941
+ const { db, close } = opened;
942
+ if (!hasProductionPainSchema(db)) {
943
+ close();
944
+ return { ok: false, reason: 'trajectory_schema_invalid', nextAction: 'run the supported PD workspace migration before enabling the worker', admitted: 0, ensured: 0, skippedLowScore: 0, skippedInvalid: 0, degradations: ['trajectory_schema_invalid'] };
945
+ }
946
+ const nowIso = (input.now ?? new Date()).toISOString();
947
+ const workspaceDir = path.resolve(input.workspaceDir);
948
+ const limit = Math.min(Math.max(input.limit ?? 5, 1), 50);
949
+ // Bare pains of this host that no admission marker covers yet. A marker is
950
+ // the durable "already handled" fact; runtime_task_id additionally excludes
951
+ // pains that reached a task through another path.
952
+ const barePainPredicate = `
953
+ FROM pain_events p
954
+ WHERE p.host_kind = ?
955
+ AND p.runtime_task_id IS NULL
956
+ AND p.canonical_pain_id IS NOT NULL
957
+ AND NOT EXISTS (SELECT 1 FROM governance_signal_admissions a WHERE a.canonical_pain_id = p.canonical_pain_id)`;
958
+ try {
959
+ const lowScoreRow = db.prepare(`SELECT COUNT(*) AS n ${barePainPredicate} AND p.score < ?`).get(input.hostKind, GOVERNANCE_STRONG_PAIN_SCORE);
960
+ const skippedLowScore = typeof rowField(lowScoreRow, 'n') === 'number' ? rowField(lowScoreRow, 'n') : 0;
961
+ const rows = db.prepare(`SELECT p.canonical_pain_id, p.session_id, p.source, p.score, p.reason ${barePainPredicate} AND p.score >= ? ORDER BY p.id LIMIT ?`)
962
+ .all(input.hostKind, GOVERNANCE_STRONG_PAIN_SCORE, limit);
963
+ let admitted = 0;
964
+ let ensured = 0;
965
+ let skippedInvalid = 0;
966
+ const degradations = [];
967
+ const freshMarkers = [];
968
+ for (const row of rows) {
969
+ const painId = rowField(row, 'canonical_pain_id');
970
+ const sessionId = rowField(row, 'session_id');
971
+ const score = rowField(row, 'score');
972
+ if (typeof painId !== 'string' || painId.length === 0 || typeof sessionId !== 'string' || sessionId.length === 0
973
+ || typeof score !== 'number' || !Number.isFinite(score)) {
974
+ skippedInvalid += 1;
975
+ continue;
976
+ }
977
+ const logicalObservationKey = `${input.hostKind}|${sessionId}|${painId}`;
978
+ const reason = typeof rowField(row, 'reason') === 'string' ? rowField(row, 'reason') : '';
979
+ const source = typeof rowField(row, 'source') === 'string' ? rowField(row, 'source') : 'tool_failure';
980
+ // The pain row carries the tool name only inside its reason prefix
981
+ // (`tool=<name>; ...` — the production writer's format); the row's
982
+ // source column is the failure class, used as the fallback.
983
+ const toolName = /^tool=([^;]{1,120});/.exec(reason)?.[1] ?? source;
984
+ const inserted = db.transaction(() => {
985
+ // Re-probe inside the transaction: a concurrent sweep or live admission
986
+ // may have marked this pain between the scan and this write.
987
+ if (db.prepare('SELECT 1 FROM governance_signal_admissions WHERE canonical_pain_id = ?').get(painId) !== undefined)
988
+ return false;
989
+ insertAdmissionMarker({
990
+ db,
991
+ identity: { hostKind: input.hostKind, logicalObservationKey, rolloutIdentity: sessionId, rootSessionId: sessionId, signalKind: 'tool_failure' },
992
+ canonicalPainId: painId,
993
+ ruleVersion: null,
994
+ reason,
995
+ payloadJson: toolFailureTaskPayload({ hostKind: input.hostKind, logicalObservationKey, sessionId, toolName, reason, score, workspaceDir }),
996
+ nowIso,
997
+ });
998
+ return true;
999
+ })();
1000
+ if (inserted) {
1001
+ admitted += 1;
1002
+ freshMarkers.push({ logicalObservationKey, canonicalPainId: painId });
1003
+ }
1004
+ }
1005
+ // Reuse the crash-safe ensure semantics unchanged: deterministic task id,
1006
+ // task-store PK idempotency, Case-B link repair.
1007
+ for (const marker of freshMarkers) {
1008
+ const continuation = await ensureGovernanceDiagnosticianTask({
1009
+ workspaceDir,
1010
+ logicalObservationKey: marker.logicalObservationKey,
1011
+ canonicalPainId: marker.canonicalPainId,
1012
+ ...(input.databaseFactory ? { databaseFactory: input.databaseFactory } : {}),
1013
+ });
1014
+ if (continuation.ok) {
1015
+ if (continuation.created)
1016
+ ensured += 1;
1017
+ }
1018
+ else {
1019
+ degradations.push(`${marker.logicalObservationKey}:${continuation.reason}`);
1020
+ }
1021
+ }
1022
+ return {
1023
+ ok: degradations.length === 0,
1024
+ ...(degradations.length > 0 ? { reason: 'bare_pain_admission_degradations', nextAction: 'inspect the per-item degradations; the admitted pains and markers remain durable and reconcile retries task creation' } : {}),
1025
+ admitted, ensured, skippedLowScore, skippedInvalid,
1026
+ degradations: degradations.slice(0, 10),
1027
+ };
1028
+ }
1029
+ catch (error) {
1030
+ const detail = error instanceof Error ? error.message.slice(0, 200) : String(error).slice(0, 200);
1031
+ return { ok: false, reason: `bare_pain_admission_failed:${detail}`, nextAction: 'inspect the workspace trajectory database and retry; committed markers remain durable', admitted: 0, ensured: 0, skippedLowScore: 0, skippedInvalid: 0, degradations: [`bare_pain_admission_failed:${detail}`] };
1032
+ }
1033
+ finally {
1034
+ close();
1035
+ }
1036
+ }
794
1037
  /**
795
1038
  * Narrow idempotent reconciliation between the trajectory admission markers
796
1039
  * and the Runtime V2 task store (separate SQLite databases — no cross-store
@@ -826,6 +1069,14 @@ export async function reconcileGovernanceContinuation(input) {
826
1069
  // Case A/B: task link missing (crash before task, or before the link write)
827
1070
  // Case C: promotion never started (no observation carries this painRef)
828
1071
  //
1072
+ // Case C/D (promotion recovery) is scoped to the hosts whose evidence
1073
+ // substrate it exists for: the promotion rows come from transcript
1074
+ // ingestion (governance_rollouts/observations), which only Codex feeds.
1075
+ // A WorkBuddy marker is COMPLETE with task+link — its hook is the only
1076
+ // evidence producer and the MVP excludes transcript reads, so demanding a
1077
+ // promotion row would degrade reconciliation with rollout_not_found
1078
+ // forever (review R2/P1).
1079
+ //
829
1080
  // A pending tail (promotion started, waiting for the next assistant turn)
830
1081
  // is NOT a recovery need — it's a forward-looking state resolved by future
831
1082
  // evidence. The live already_admitted redelivery path (ensureContinuation
@@ -833,13 +1084,14 @@ export async function reconcileGovernanceContinuation(input) {
833
1084
  // not retry pending tails, so pending-tail markers do not block later
834
1085
  // markers. Stale tails are NOT auto-recoverable (substrate refuses to
835
1086
  // re-arm them): they are counted separately below and reported.
836
- const markers = db.prepare(`SELECT a.logical_observation_key, a.canonical_pain_id
1087
+ const markers = db.prepare(`SELECT a.logical_observation_key, a.canonical_pain_id, a.host_kind
837
1088
  FROM governance_signal_admissions a
838
1089
  WHERE a.decision = 'admitted'
839
1090
  AND (
840
1091
  a.diagnostician_task_id IS NULL
841
- OR NOT EXISTS (SELECT 1 FROM governance_observations o
842
- WHERE o.promotion_ref = a.canonical_pain_id)
1092
+ OR (a.host_kind = 'codex'
1093
+ AND NOT EXISTS (SELECT 1 FROM governance_observations o
1094
+ WHERE o.promotion_ref = a.canonical_pain_id))
843
1095
  )
844
1096
  ORDER BY a.id LIMIT ?`).all(limit);
845
1097
  for (const row of markers) {
@@ -847,6 +1099,23 @@ export async function reconcileGovernanceContinuation(input) {
847
1099
  const painId = rowField(row, 'canonical_pain_id');
848
1100
  if (typeof key !== 'string' || typeof painId !== 'string')
849
1101
  continue;
1102
+ // WorkBuddy (and any future no-transcript host) markers continue through
1103
+ // the task-ensure seam ONLY: there is no promotion substrate to recover,
1104
+ // and routing them through ensureGovernanceContinuation would report a
1105
+ // permanent rollout_not_found degradation despite a healthy task link.
1106
+ if (rowField(row, 'host_kind') !== 'codex') {
1107
+ const ensured = await ensureGovernanceDiagnosticianTask({ workspaceDir, logicalObservationKey: key, canonicalPainId: painId });
1108
+ if (ensured.ok) {
1109
+ if (ensured.created)
1110
+ tasksEnsured += 1;
1111
+ if (ensured.linkRepaired)
1112
+ linksRepaired += 1;
1113
+ }
1114
+ else {
1115
+ degradations.push(`${key}:${ensured.reason}`);
1116
+ }
1117
+ continue;
1118
+ }
850
1119
  const cont = await ensureGovernanceContinuation({ workspaceDir, logicalObservationKey: key, canonicalPainId: painId });
851
1120
  if (cont.ok) {
852
1121
  // Only count REAL actions: a task actually created, a link actually
@@ -96,7 +96,13 @@ export function readWorkspaceHostKindFacts(workspaceDir) {
96
96
  nextAction: provenance.nextAction,
97
97
  };
98
98
  }
99
- const evidenceKinds = provenance.kinds.filter((kind) => kind === 'openclaw' || kind === 'codex');
99
+ // Known-host filter — must stay in lockstep with PROMPT_INJECTION_TARGET_HOSTS
100
+ // (prompt-injection-projection.ts, the roster SSoT). Importing that roster here
101
+ // would create a module cycle (the projection reads host facts from THIS
102
+ // module), so the set is restated with this sync note. Review R1: a two-host
103
+ // filter silently dropped WorkBuddy pain evidence, so a WorkBuddy-only
104
+ // workspace could never confirm its shared route from persisted facts.
105
+ const evidenceKinds = provenance.kinds.filter((kind) => kind === 'openclaw' || kind === 'codex' || kind === 'workbuddy');
100
106
  const effectiveKinds = [...new Set([...declaredKinds, ...evidenceKinds])].sort();
101
107
  return { ok: true, declaredKinds, evidenceKinds, effectiveKinds };
102
108
  }
@@ -31,6 +31,7 @@ export * from './product-telemetry/service.js';
31
31
  export * from './codex-disclosure.js';
32
32
  export * from './codex-ingestion-consent.js';
33
33
  export * from './codex-worker-status.js';
34
+ export * from './workbuddy-worker-status.js';
34
35
  export * from './codex-legacy-registration.js';
35
36
  export * from './codex-transcript-locate.js';
36
37
  export * from './intervention-evidence-ingress.js';
@@ -53,6 +53,10 @@ export * from './codex-ingestion-consent.js';
53
53
  // PRI-625 Slice D: ONE §15 worker-mode authority, moved from codex-adapter so
54
54
  // the CLI, the Console, and the worker share the same semantics.
55
55
  export * from './codex-worker-status.js';
56
+ // WorkBuddy third host (SPEC workbuddy-host-adapter §5): the §15 worker-mode
57
+ // authority for the WorkBuddy governance workspace — same one-semantics
58
+ // placement as the Codex evaluation above, no ingestion step to gate.
59
+ export * from './workbuddy-worker-status.js';
56
60
  // PRI-625 Slice D: ONE legacy-registration predicate (installer refusal,
57
61
  // health dualRegistration, and future setup flows all read the same fact).
58
62
  export * from './codex-legacy-registration.js';
@@ -226,7 +230,7 @@ export function createProductionHostRuntime(options = {}) {
226
230
  skippedWarnings: prompt.warnings,
227
231
  injectedCharCount: prompt.additionalContext.length,
228
232
  budget: RUNTIME_V2_PRINCIPLE_BUDGET,
229
- ...(options.hostKind === 'openclaw' || options.hostKind === 'codex' ? { hostKind: options.hostKind } : {}),
233
+ ...(options.hostKind === 'openclaw' || options.hostKind === 'codex' || options.hostKind === 'workbuddy' ? { hostKind: options.hostKind } : {}),
230
234
  eventId: randomUUID(),
231
235
  ...(prompt.truncated !== undefined ? { v2Truncated: prompt.truncated } : {}),
232
236
  ...(event.context.turnId !== undefined ? { runId: event.context.turnId } : {}),
@@ -71,6 +71,18 @@ export interface InternalizationConsumerCycleOutcome {
71
71
  readonly taskKind?: string;
72
72
  readonly runStatus?: 'succeeded' | 'failed' | 'retried';
73
73
  }
74
+ /**
75
+ * PRI-954 (F10) oldest-ready-stage-first ordering. Returns the configured
76
+ * runner kinds reordered to start at the stage that owns the globally oldest
77
+ * ready task (by `createdAt`); later stages keep their relative priority
78
+ * order, so dependency order inside one stage scan is untouched. Tasks
79
+ * without a readable createdAt sort last for ordering purposes (their stage
80
+ * keeps the default position) — unknown age never jumps the queue.
81
+ */
82
+ export declare function orderKindsByOldestReadyStage<T extends string>(runnerKinds: readonly T[], readyTasks: readonly {
83
+ taskKind: T;
84
+ createdAt?: string;
85
+ }[]): T[];
74
86
  /**
75
87
  * Run ONE bounded consumer cycle for a workspace. At most one downstream task
76
88
  * is leased and executed per cycle (DEFAULT_CONSUMER_MAX_TASKS_PER_CYCLE=1);