create-principles-disciple 1.121.7 → 1.121.9

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 (461) hide show
  1. package/README.md +13 -1
  2. package/console/dist/server/config/pd-config-store.d.ts +1 -3
  3. package/console/dist/server/config/pd-config-store.js +4 -4
  4. package/console/dist/server/feedback/channels.d.ts +2 -8
  5. package/console/dist/server/feedback/channels.js +0 -11
  6. package/console/dist/server/index.js +34 -5
  7. package/console/dist/server/models/ActivationsConsoleModel.d.ts +28 -1
  8. package/console/dist/server/models/ActivationsConsoleModel.js +72 -30
  9. package/console/dist/server/models/GovernanceExperienceCollector.d.ts +28 -0
  10. package/console/dist/server/models/GovernanceExperienceCollector.js +305 -0
  11. package/console/dist/server/models/GovernanceProjectionCollector.d.ts +60 -2
  12. package/console/dist/server/models/GovernanceProjectionCollector.js +323 -285
  13. package/console/dist/server/models/PrincipleClassifier.d.ts +1 -1
  14. package/console/dist/server/models/PrincipleClassifier.js +7 -4
  15. package/console/dist/server/models/PrinciplesConsoleModel.d.ts +2 -1
  16. package/console/dist/server/models/ReceiptsConsoleModel.d.ts +5 -0
  17. package/console/dist/server/models/ReceiptsConsoleModel.js +140 -13
  18. package/console/dist/server/routes/activations.js +1 -1
  19. package/console/dist/server/routes/failed-tasks.js +28 -11
  20. package/console/dist/server/routes/governance.d.ts +26 -0
  21. package/console/dist/server/routes/governance.js +51 -0
  22. package/console/dist/server/routes/owner-identity.d.ts +31 -0
  23. package/console/dist/server/routes/owner-identity.js +76 -0
  24. package/console/dist/server/routes/update-history.d.ts +5 -0
  25. package/console/dist/server/routes/update-history.js +50 -4
  26. package/console/dist/server/routes/update.js +186 -32
  27. package/console/dist/shared/feedback-channel-ids.d.ts +13 -0
  28. package/console/dist/shared/feedback-channel-ids.js +12 -0
  29. package/console/dist/shared/feedback-contract.d.ts +46 -0
  30. package/console/dist/shared/feedback-contract.js +45 -0
  31. package/console/dist/ui/App.js +1 -1
  32. package/console/dist/ui/api.d.ts +22 -3
  33. package/console/dist/ui/api.js +63 -3
  34. package/console/dist/ui/components/receipts/ReceiptCoverageDisclosure.d.ts +6 -0
  35. package/console/dist/ui/components/receipts/ReceiptCoverageDisclosure.js +66 -0
  36. package/console/dist/ui/i18n/en.json +198 -2
  37. package/console/dist/ui/i18n/zh-CN.json +196 -0
  38. package/console/dist/ui/pages/activation/ActivationPage.js +14 -2
  39. package/console/dist/ui/pages/failed-tasks/FailedTasksPage.js +25 -5
  40. package/console/dist/ui/pages/focus/FocusPage.d.ts +28 -1
  41. package/console/dist/ui/pages/focus/FocusPage.js +128 -15
  42. package/console/dist/ui/pages/principles/PrincipleDetailPage.d.ts +30 -0
  43. package/console/dist/ui/pages/principles/PrincipleDetailPage.js +49 -6
  44. package/console/dist/ui/pages/settings/SettingsPage.js +67 -2
  45. package/console/dist/ui/pages/settings/UpdatePage.js +7 -7
  46. package/console/dist/ui/utils/enum-labels.js +0 -1
  47. package/console/dist/ui/utils/validators.d.ts +53 -21
  48. package/console/dist/ui/utils/validators.js +248 -21
  49. package/console/dist/web/assets/app.css +6 -0
  50. package/console/dist/web/assets/app.js +2721 -1664
  51. package/console/package.json +4 -2
  52. package/core/dist/host/host-adapter.d.ts +1 -1
  53. package/core/dist/host/host-adapter.d.ts.map +1 -1
  54. package/core/dist/host/host-adapter.js +1 -0
  55. package/core/dist/host/host-adapter.js.map +1 -1
  56. package/core/dist/runtime-v2/__tests__/architecture-regression.test.js +12 -2
  57. package/core/dist/runtime-v2/__tests__/architecture-regression.test.js.map +1 -1
  58. package/core/dist/runtime-v2/__tests__/attack-e2e-pipeline-smoke.test.js +1 -1
  59. package/core/dist/runtime-v2/__tests__/attack-e2e-pipeline-smoke.test.js.map +1 -1
  60. package/core/dist/runtime-v2/__tests__/governance-experience.test.d.ts +2 -0
  61. package/core/dist/runtime-v2/__tests__/governance-experience.test.d.ts.map +1 -0
  62. package/core/dist/runtime-v2/__tests__/governance-experience.test.js +300 -0
  63. package/core/dist/runtime-v2/__tests__/governance-experience.test.js.map +1 -0
  64. package/core/dist/runtime-v2/__tests__/owner-identity.test.d.ts +2 -0
  65. package/core/dist/runtime-v2/__tests__/owner-identity.test.d.ts.map +1 -0
  66. package/core/dist/runtime-v2/__tests__/owner-identity.test.js +147 -0
  67. package/core/dist/runtime-v2/__tests__/owner-identity.test.js.map +1 -0
  68. package/core/dist/runtime-v2/__tests__/product-telemetry-contract.test.d.ts +18 -0
  69. package/core/dist/runtime-v2/__tests__/product-telemetry-contract.test.d.ts.map +1 -0
  70. package/core/dist/runtime-v2/__tests__/product-telemetry-contract.test.js +303 -0
  71. package/core/dist/runtime-v2/__tests__/product-telemetry-contract.test.js.map +1 -0
  72. package/core/dist/runtime-v2/__tests__/recovery-actions-log.test.js +40 -0
  73. package/core/dist/runtime-v2/__tests__/recovery-actions-log.test.js.map +1 -1
  74. package/core/dist/runtime-v2/__tests__/status-contract.test.d.ts +2 -0
  75. package/core/dist/runtime-v2/__tests__/status-contract.test.d.ts.map +1 -0
  76. package/core/dist/runtime-v2/__tests__/status-contract.test.js +107 -0
  77. package/core/dist/runtime-v2/__tests__/status-contract.test.js.map +1 -0
  78. package/core/dist/runtime-v2/activation/__tests__/promotion-evidence-snapshot.test.d.ts +2 -0
  79. package/core/dist/runtime-v2/activation/__tests__/promotion-evidence-snapshot.test.d.ts.map +1 -0
  80. package/core/dist/runtime-v2/activation/__tests__/promotion-evidence-snapshot.test.js +104 -0
  81. package/core/dist/runtime-v2/activation/__tests__/promotion-evidence-snapshot.test.js.map +1 -0
  82. package/core/dist/runtime-v2/activation/__tests__/promotion-readiness-evaluator.test.js +19 -0
  83. package/core/dist/runtime-v2/activation/__tests__/promotion-readiness-evaluator.test.js.map +1 -1
  84. package/core/dist/runtime-v2/activation/__tests__/rulecode-owner-decision-service.test.js +20 -0
  85. package/core/dist/runtime-v2/activation/__tests__/rulecode-owner-decision-service.test.js.map +1 -1
  86. package/core/dist/runtime-v2/activation/__tests__/sqlite-activation-safety-store.test.js +40 -0
  87. package/core/dist/runtime-v2/activation/__tests__/sqlite-activation-safety-store.test.js.map +1 -1
  88. package/core/dist/runtime-v2/activation/index.d.ts +2 -0
  89. package/core/dist/runtime-v2/activation/index.d.ts.map +1 -1
  90. package/core/dist/runtime-v2/activation/index.js +1 -0
  91. package/core/dist/runtime-v2/activation/index.js.map +1 -1
  92. package/core/dist/runtime-v2/activation/promotion-evidence-snapshot.d.ts +30 -0
  93. package/core/dist/runtime-v2/activation/promotion-evidence-snapshot.d.ts.map +1 -0
  94. package/core/dist/runtime-v2/activation/promotion-evidence-snapshot.js +87 -0
  95. package/core/dist/runtime-v2/activation/promotion-evidence-snapshot.js.map +1 -0
  96. package/core/dist/runtime-v2/activation/promotion-readiness-evaluator.d.ts.map +1 -1
  97. package/core/dist/runtime-v2/activation/promotion-readiness-evaluator.js +3 -0
  98. package/core/dist/runtime-v2/activation/promotion-readiness-evaluator.js.map +1 -1
  99. package/core/dist/runtime-v2/activation/promotion-readiness-reader.d.ts +1 -1
  100. package/core/dist/runtime-v2/activation/promotion-readiness-reader.d.ts.map +1 -1
  101. package/core/dist/runtime-v2/activation/promotion-readiness-reader.js +2 -2
  102. package/core/dist/runtime-v2/activation/promotion-readiness-reader.js.map +1 -1
  103. package/core/dist/runtime-v2/activation/rulecode-owner-decision-service.d.ts.map +1 -1
  104. package/core/dist/runtime-v2/activation/rulecode-owner-decision-service.js +18 -9
  105. package/core/dist/runtime-v2/activation/rulecode-owner-decision-service.js.map +1 -1
  106. package/core/dist/runtime-v2/adapter/__tests__/chaos-json-repair.test.js +1 -1
  107. package/core/dist/runtime-v2/adapter/__tests__/chaos-json-repair.test.js.map +1 -1
  108. package/core/dist/runtime-v2/adapter/__tests__/json-extractor.test.js +91 -1
  109. package/core/dist/runtime-v2/adapter/__tests__/json-extractor.test.js.map +1 -1
  110. package/core/dist/runtime-v2/adapter/__tests__/pi-ai-runtime-adapter.test.js +59 -12
  111. package/core/dist/runtime-v2/adapter/__tests__/pi-ai-runtime-adapter.test.js.map +1 -1
  112. package/core/dist/runtime-v2/adapter/__tests__/runtime-config-contract.test.js +10 -9
  113. package/core/dist/runtime-v2/adapter/__tests__/runtime-config-contract.test.js.map +1 -1
  114. package/core/dist/runtime-v2/adapter/__tests__/structured-output-repair.test.js +42 -1
  115. package/core/dist/runtime-v2/adapter/__tests__/structured-output-repair.test.js.map +1 -1
  116. package/core/dist/runtime-v2/adapter/json-extractor.d.ts +25 -0
  117. package/core/dist/runtime-v2/adapter/json-extractor.d.ts.map +1 -1
  118. package/core/dist/runtime-v2/adapter/json-extractor.js +126 -0
  119. package/core/dist/runtime-v2/adapter/json-extractor.js.map +1 -1
  120. package/core/dist/runtime-v2/adapter/l2-agent-loop-adapter.d.ts +9 -4
  121. package/core/dist/runtime-v2/adapter/l2-agent-loop-adapter.d.ts.map +1 -1
  122. package/core/dist/runtime-v2/adapter/l2-agent-loop-adapter.js +9 -4
  123. package/core/dist/runtime-v2/adapter/l2-agent-loop-adapter.js.map +1 -1
  124. package/core/dist/runtime-v2/adapter/output-repair-contract.d.ts +5 -1
  125. package/core/dist/runtime-v2/adapter/output-repair-contract.d.ts.map +1 -1
  126. package/core/dist/runtime-v2/adapter/output-repair-contract.js +4 -1
  127. package/core/dist/runtime-v2/adapter/output-repair-contract.js.map +1 -1
  128. package/core/dist/runtime-v2/adapter/pi-ai-catalog.d.ts +7 -0
  129. package/core/dist/runtime-v2/adapter/pi-ai-catalog.d.ts.map +1 -0
  130. package/core/dist/runtime-v2/adapter/pi-ai-catalog.js +19 -0
  131. package/core/dist/runtime-v2/adapter/pi-ai-catalog.js.map +1 -0
  132. package/core/dist/runtime-v2/adapter/pi-ai-runtime-adapter.d.ts +13 -15
  133. package/core/dist/runtime-v2/adapter/pi-ai-runtime-adapter.d.ts.map +1 -1
  134. package/core/dist/runtime-v2/adapter/pi-ai-runtime-adapter.js +104 -37
  135. package/core/dist/runtime-v2/adapter/pi-ai-runtime-adapter.js.map +1 -1
  136. package/core/dist/runtime-v2/adapter/structured-output-repair.d.ts +16 -1
  137. package/core/dist/runtime-v2/adapter/structured-output-repair.d.ts.map +1 -1
  138. package/core/dist/runtime-v2/adapter/structured-output-repair.js +38 -6
  139. package/core/dist/runtime-v2/adapter/structured-output-repair.js.map +1 -1
  140. package/core/dist/runtime-v2/adapter/tools/diagnostician-tool.d.ts +1 -1
  141. package/core/dist/runtime-v2/adapter/tools/diagnostician-tool.d.ts.map +1 -1
  142. package/core/dist/runtime-v2/config/__tests__/pd-config-agent-binding.test.js +8 -9
  143. package/core/dist/runtime-v2/config/__tests__/pd-config-agent-binding.test.js.map +1 -1
  144. package/core/dist/runtime-v2/config/__tests__/pd-config-contract.test.js +27 -3
  145. package/core/dist/runtime-v2/config/__tests__/pd-config-contract.test.js.map +1 -1
  146. package/core/dist/runtime-v2/config/__tests__/pd-config-principles.test.d.ts +11 -0
  147. package/core/dist/runtime-v2/config/__tests__/pd-config-principles.test.d.ts.map +1 -0
  148. package/core/dist/runtime-v2/config/__tests__/pd-config-principles.test.js +93 -0
  149. package/core/dist/runtime-v2/config/__tests__/pd-config-principles.test.js.map +1 -0
  150. package/core/dist/runtime-v2/config/__tests__/pd-config-workspace.test.js +41 -0
  151. package/core/dist/runtime-v2/config/__tests__/pd-config-workspace.test.js.map +1 -1
  152. package/core/dist/runtime-v2/config/index.d.ts +2 -2
  153. package/core/dist/runtime-v2/config/index.d.ts.map +1 -1
  154. package/core/dist/runtime-v2/config/index.js +2 -0
  155. package/core/dist/runtime-v2/config/index.js.map +1 -1
  156. package/core/dist/runtime-v2/config/pd-config-agent-binding.d.ts.map +1 -1
  157. package/core/dist/runtime-v2/config/pd-config-agent-binding.js +4 -13
  158. package/core/dist/runtime-v2/config/pd-config-agent-binding.js.map +1 -1
  159. package/core/dist/runtime-v2/config/pd-config-effective.d.ts.map +1 -1
  160. package/core/dist/runtime-v2/config/pd-config-effective.js +14 -7
  161. package/core/dist/runtime-v2/config/pd-config-effective.js.map +1 -1
  162. package/core/dist/runtime-v2/config/pd-config-feature-flags.d.ts.map +1 -1
  163. package/core/dist/runtime-v2/config/pd-config-feature-flags.js +5 -12
  164. package/core/dist/runtime-v2/config/pd-config-feature-flags.js.map +1 -1
  165. package/core/dist/runtime-v2/config/pd-config-types.d.ts +8 -0
  166. package/core/dist/runtime-v2/config/pd-config-types.d.ts.map +1 -1
  167. package/core/dist/runtime-v2/config/pd-config-types.js +2 -0
  168. package/core/dist/runtime-v2/config/pd-config-types.js.map +1 -1
  169. package/core/dist/runtime-v2/config/pd-config-validate.d.ts.map +1 -1
  170. package/core/dist/runtime-v2/config/pd-config-validate.js +48 -3
  171. package/core/dist/runtime-v2/config/pd-config-validate.js.map +1 -1
  172. package/core/dist/runtime-v2/core-principles/__tests__/core-axiom-block.test.js +36 -8
  173. package/core/dist/runtime-v2/core-principles/__tests__/core-axiom-block.test.js.map +1 -1
  174. package/core/dist/runtime-v2/core-principles/__tests__/core-principle-registry.test.js +36 -12
  175. package/core/dist/runtime-v2/core-principles/__tests__/core-principle-registry.test.js.map +1 -1
  176. package/core/dist/runtime-v2/core-principles/core-axiom-block.d.ts +22 -8
  177. package/core/dist/runtime-v2/core-principles/core-axiom-block.d.ts.map +1 -1
  178. package/core/dist/runtime-v2/core-principles/core-axiom-block.js +28 -13
  179. package/core/dist/runtime-v2/core-principles/core-axiom-block.js.map +1 -1
  180. package/core/dist/runtime-v2/core-principles/core-principle-registry.d.ts +29 -4
  181. package/core/dist/runtime-v2/core-principles/core-principle-registry.d.ts.map +1 -1
  182. package/core/dist/runtime-v2/core-principles/core-principle-registry.js +68 -33
  183. package/core/dist/runtime-v2/core-principles/core-principle-registry.js.map +1 -1
  184. package/core/dist/runtime-v2/core-principles/index.d.ts +3 -3
  185. package/core/dist/runtime-v2/core-principles/index.d.ts.map +1 -1
  186. package/core/dist/runtime-v2/core-principles/index.js +1 -1
  187. package/core/dist/runtime-v2/core-principles/index.js.map +1 -1
  188. package/core/dist/runtime-v2/evolution/evolution-types.d.ts +2 -1
  189. package/core/dist/runtime-v2/evolution/evolution-types.d.ts.map +1 -1
  190. package/core/dist/runtime-v2/evolution/evolution-types.js.map +1 -1
  191. package/core/dist/runtime-v2/feature-flags/__tests__/anonymous-product-telemetry-flag.test.d.ts +2 -0
  192. package/core/dist/runtime-v2/feature-flags/__tests__/anonymous-product-telemetry-flag.test.d.ts.map +1 -0
  193. package/core/dist/runtime-v2/feature-flags/__tests__/anonymous-product-telemetry-flag.test.js +48 -0
  194. package/core/dist/runtime-v2/feature-flags/__tests__/anonymous-product-telemetry-flag.test.js.map +1 -0
  195. package/core/dist/runtime-v2/feature-flags/__tests__/codex-conversation-ingestion-flag.test.d.ts +2 -0
  196. package/core/dist/runtime-v2/feature-flags/__tests__/codex-conversation-ingestion-flag.test.d.ts.map +1 -0
  197. package/core/dist/runtime-v2/feature-flags/__tests__/codex-conversation-ingestion-flag.test.js +46 -0
  198. package/core/dist/runtime-v2/feature-flags/__tests__/codex-conversation-ingestion-flag.test.js.map +1 -0
  199. package/core/dist/runtime-v2/feature-flags/__tests__/feature-flag-contract.test.js +86 -31
  200. package/core/dist/runtime-v2/feature-flags/__tests__/feature-flag-contract.test.js.map +1 -1
  201. package/core/dist/runtime-v2/feature-flags/__tests__/feature-flag-lifecycle.test.d.ts +2 -0
  202. package/core/dist/runtime-v2/feature-flags/__tests__/feature-flag-lifecycle.test.d.ts.map +1 -0
  203. package/core/dist/runtime-v2/feature-flags/__tests__/feature-flag-lifecycle.test.js +83 -0
  204. package/core/dist/runtime-v2/feature-flags/__tests__/feature-flag-lifecycle.test.js.map +1 -0
  205. package/core/dist/runtime-v2/feature-flags/feature-flag-contract.d.ts +19 -1
  206. package/core/dist/runtime-v2/feature-flags/feature-flag-contract.d.ts.map +1 -1
  207. package/core/dist/runtime-v2/feature-flags/feature-flag-contract.js +128 -10
  208. package/core/dist/runtime-v2/feature-flags/feature-flag-contract.js.map +1 -1
  209. package/core/dist/runtime-v2/feature-flags/feature-flag-lifecycle.d.ts +37 -0
  210. package/core/dist/runtime-v2/feature-flags/feature-flag-lifecycle.d.ts.map +1 -0
  211. package/core/dist/runtime-v2/feature-flags/feature-flag-lifecycle.js +304 -0
  212. package/core/dist/runtime-v2/feature-flags/feature-flag-lifecycle.js.map +1 -0
  213. package/core/dist/runtime-v2/feature-flags/index.d.ts +4 -2
  214. package/core/dist/runtime-v2/feature-flags/index.d.ts.map +1 -1
  215. package/core/dist/runtime-v2/feature-flags/index.js +3 -1
  216. package/core/dist/runtime-v2/feature-flags/index.js.map +1 -1
  217. package/core/dist/runtime-v2/governance-experience-contract.d.ts +468 -0
  218. package/core/dist/runtime-v2/governance-experience-contract.d.ts.map +1 -0
  219. package/core/dist/runtime-v2/governance-experience-contract.js +208 -0
  220. package/core/dist/runtime-v2/governance-experience-contract.js.map +1 -0
  221. package/core/dist/runtime-v2/governance-experience.d.ts +11 -0
  222. package/core/dist/runtime-v2/governance-experience.d.ts.map +1 -0
  223. package/core/dist/runtime-v2/governance-experience.js +276 -0
  224. package/core/dist/runtime-v2/governance-experience.js.map +1 -0
  225. package/core/dist/runtime-v2/index.d.ts +20 -8
  226. package/core/dist/runtime-v2/index.d.ts.map +1 -1
  227. package/core/dist/runtime-v2/index.js +20 -6
  228. package/core/dist/runtime-v2/index.js.map +1 -1
  229. package/core/dist/runtime-v2/internalization/__tests__/artificer-output-retry-flag.test.js +29 -10
  230. package/core/dist/runtime-v2/internalization/__tests__/artificer-output-retry-flag.test.js.map +1 -1
  231. package/core/dist/runtime-v2/owner-identity.d.ts +69 -0
  232. package/core/dist/runtime-v2/owner-identity.d.ts.map +1 -0
  233. package/core/dist/runtime-v2/owner-identity.js +179 -0
  234. package/core/dist/runtime-v2/owner-identity.js.map +1 -0
  235. package/core/dist/runtime-v2/pain-signal-runtime-factory.d.ts.map +1 -1
  236. package/core/dist/runtime-v2/pain-signal-runtime-factory.js +2 -3
  237. package/core/dist/runtime-v2/pain-signal-runtime-factory.js.map +1 -1
  238. package/core/dist/runtime-v2/product-telemetry/daily-identity.d.ts +70 -0
  239. package/core/dist/runtime-v2/product-telemetry/daily-identity.d.ts.map +1 -0
  240. package/core/dist/runtime-v2/product-telemetry/daily-identity.js +89 -0
  241. package/core/dist/runtime-v2/product-telemetry/daily-identity.js.map +1 -0
  242. package/core/dist/runtime-v2/product-telemetry/snapshot-contract.d.ts +110 -0
  243. package/core/dist/runtime-v2/product-telemetry/snapshot-contract.d.ts.map +1 -0
  244. package/core/dist/runtime-v2/product-telemetry/snapshot-contract.js +250 -0
  245. package/core/dist/runtime-v2/product-telemetry/snapshot-contract.js.map +1 -0
  246. package/core/dist/runtime-v2/pruning-read-model.d.ts +2 -1
  247. package/core/dist/runtime-v2/pruning-read-model.d.ts.map +1 -1
  248. package/core/dist/runtime-v2/pruning-read-model.js.map +1 -1
  249. package/core/dist/runtime-v2/receipt-coverage.d.ts +63 -0
  250. package/core/dist/runtime-v2/receipt-coverage.d.ts.map +1 -0
  251. package/core/dist/runtime-v2/receipt-coverage.js +19 -0
  252. package/core/dist/runtime-v2/receipt-coverage.js.map +1 -0
  253. package/core/dist/runtime-v2/recovery-actions-log.d.ts +7 -0
  254. package/core/dist/runtime-v2/recovery-actions-log.d.ts.map +1 -1
  255. package/core/dist/runtime-v2/recovery-actions-log.js +6 -0
  256. package/core/dist/runtime-v2/recovery-actions-log.js.map +1 -1
  257. package/core/dist/runtime-v2/runtime-protocol.js +1 -1
  258. package/core/dist/runtime-v2/runtime-protocol.js.map +1 -1
  259. package/core/dist/runtime-v2/store/task/__tests__/sqlite-task-store.failed-tasks.test.js +17 -1
  260. package/core/dist/runtime-v2/store/task/__tests__/sqlite-task-store.failed-tasks.test.js.map +1 -1
  261. package/core/dist/runtime-v2/store/task/sqlite-task-store.d.ts.map +1 -1
  262. package/core/dist/runtime-v2/store/task/sqlite-task-store.js +10 -0
  263. package/core/dist/runtime-v2/store/task/sqlite-task-store.js.map +1 -1
  264. package/core/dist/runtime-v2/store/task/task-types.d.ts +2 -0
  265. package/core/dist/runtime-v2/store/task/task-types.d.ts.map +1 -1
  266. package/core/dist/runtime-v2/task-status.d.ts +9 -5
  267. package/core/dist/runtime-v2/task-status.d.ts.map +1 -1
  268. package/core/dist/runtime-v2/task-status.js +15 -7
  269. package/core/dist/runtime-v2/task-status.js.map +1 -1
  270. package/core/dist/runtime-v2/types/event-types.d.ts +4 -0
  271. package/core/dist/runtime-v2/types/event-types.d.ts.map +1 -1
  272. package/core/dist/runtime-v2/types/event-types.js.map +1 -1
  273. package/core/dist/runtime-v2/types/index.d.ts +1 -1
  274. package/core/dist/runtime-v2/types/index.d.ts.map +1 -1
  275. package/core/dist/runtime-v2/types/index.js +1 -1
  276. package/core/dist/runtime-v2/types/index.js.map +1 -1
  277. package/core/dist/runtime-v2/types/principle-enums.d.ts +3 -2
  278. package/core/dist/runtime-v2/types/principle-enums.d.ts.map +1 -1
  279. package/core/dist/runtime-v2/types/principle-enums.js +11 -5
  280. package/core/dist/runtime-v2/types/principle-enums.js.map +1 -1
  281. package/core/dist/runtime-v2/types/principle-schema.d.ts +1 -1
  282. package/core/dist/runtime-v2/types/principle-tree-store.d.ts +1 -1
  283. package/core/dist/runtime-v2/utils/cli-process-runner.d.ts.map +1 -1
  284. package/core/dist/runtime-v2/utils/cli-process-runner.js +3 -2
  285. package/core/dist/runtime-v2/utils/cli-process-runner.js.map +1 -1
  286. package/core/package.json +4 -5
  287. package/dist/i18n.d.ts.map +1 -1
  288. package/dist/i18n.js +4 -2
  289. package/dist/i18n.js.map +1 -1
  290. package/dist/index.d.ts +2 -0
  291. package/dist/index.d.ts.map +1 -1
  292. package/dist/index.js +47 -11
  293. package/dist/index.js.map +1 -1
  294. package/dist/installer.d.ts +31 -5
  295. package/dist/installer.d.ts.map +1 -1
  296. package/dist/installer.js +472 -161
  297. package/dist/installer.js.map +1 -1
  298. package/dist/installers/codex-host-installer.d.ts.map +1 -1
  299. package/dist/installers/codex-host-installer.js +4 -0
  300. package/dist/installers/codex-host-installer.js.map +1 -1
  301. package/dist/mvp-config.d.ts +10 -0
  302. package/dist/mvp-config.d.ts.map +1 -1
  303. package/dist/mvp-config.js +30 -3
  304. package/dist/mvp-config.js.map +1 -1
  305. package/dist/uninstaller.d.ts +9 -1
  306. package/dist/uninstaller.d.ts.map +1 -1
  307. package/dist/uninstaller.js +103 -23
  308. package/dist/uninstaller.js.map +1 -1
  309. package/dist/update/atomic-file.d.ts +35 -0
  310. package/dist/update/atomic-file.d.ts.map +1 -0
  311. package/dist/update/atomic-file.js +120 -0
  312. package/dist/update/atomic-file.js.map +1 -0
  313. package/dist/update/bootstrap-protocol.d.ts +55 -0
  314. package/dist/update/bootstrap-protocol.d.ts.map +1 -0
  315. package/dist/update/bootstrap-protocol.js +132 -0
  316. package/dist/update/bootstrap-protocol.js.map +1 -0
  317. package/dist/update/channel-metadata.d.ts +28 -0
  318. package/dist/update/channel-metadata.d.ts.map +1 -0
  319. package/dist/update/channel-metadata.js +90 -0
  320. package/dist/update/channel-metadata.js.map +1 -0
  321. package/dist/update/channel-promotion.d.ts +39 -0
  322. package/dist/update/channel-promotion.d.ts.map +1 -0
  323. package/dist/update/channel-promotion.js +55 -0
  324. package/dist/update/channel-promotion.js.map +1 -0
  325. package/dist/update/data-compatibility.d.ts +35 -0
  326. package/dist/update/data-compatibility.d.ts.map +1 -0
  327. package/dist/update/data-compatibility.js +45 -0
  328. package/dist/update/data-compatibility.js.map +1 -0
  329. package/dist/update/install-layout.d.ts +59 -0
  330. package/dist/update/install-layout.d.ts.map +1 -0
  331. package/dist/update/install-layout.js +127 -0
  332. package/dist/update/install-layout.js.map +1 -0
  333. package/dist/update/legacy-migration.d.ts +56 -0
  334. package/dist/update/legacy-migration.d.ts.map +1 -0
  335. package/dist/update/legacy-migration.js +254 -0
  336. package/dist/update/legacy-migration.js.map +1 -0
  337. package/dist/update/product-identity.d.ts +50 -0
  338. package/dist/update/product-identity.d.ts.map +1 -0
  339. package/dist/update/product-identity.js +63 -0
  340. package/dist/update/product-identity.js.map +1 -0
  341. package/dist/update/release-asset-manifest.d.ts +31 -0
  342. package/dist/update/release-asset-manifest.d.ts.map +1 -0
  343. package/dist/update/release-asset-manifest.js +161 -0
  344. package/dist/update/release-asset-manifest.js.map +1 -0
  345. package/dist/update/release-identity.d.ts +33 -0
  346. package/dist/update/release-identity.d.ts.map +1 -0
  347. package/dist/update/release-identity.js +108 -0
  348. package/dist/update/release-identity.js.map +1 -0
  349. package/dist/update/release-manager.d.ts +96 -0
  350. package/dist/update/release-manager.d.ts.map +1 -0
  351. package/dist/update/release-manager.js +253 -0
  352. package/dist/update/release-manager.js.map +1 -0
  353. package/dist/update/release-metadata.d.ts +57 -0
  354. package/dist/update/release-metadata.d.ts.map +1 -0
  355. package/dist/update/release-metadata.js +200 -0
  356. package/dist/update/release-metadata.js.map +1 -0
  357. package/dist/update/release-policy.d.ts +51 -0
  358. package/dist/update/release-policy.d.ts.map +1 -0
  359. package/dist/update/release-policy.js +103 -0
  360. package/dist/update/release-policy.js.map +1 -0
  361. package/dist/update/rollback-policy.d.ts +56 -0
  362. package/dist/update/rollback-policy.d.ts.map +1 -0
  363. package/dist/update/rollback-policy.js +61 -0
  364. package/dist/update/rollback-policy.js.map +1 -0
  365. package/dist/update/transaction-journal.d.ts +121 -0
  366. package/dist/update/transaction-journal.d.ts.map +1 -0
  367. package/dist/update/transaction-journal.js +333 -0
  368. package/dist/update/transaction-journal.js.map +1 -0
  369. package/dist/update/trust-metadata.d.ts +55 -0
  370. package/dist/update/trust-metadata.d.ts.map +1 -0
  371. package/dist/update/trust-metadata.js +170 -0
  372. package/dist/update/trust-metadata.js.map +1 -0
  373. package/dist/update/update-history.d.ts +46 -0
  374. package/dist/update/update-history.d.ts.map +1 -0
  375. package/dist/update/update-history.js +123 -0
  376. package/dist/update/update-history.js.map +1 -0
  377. package/dist/utils/env.d.ts +7 -0
  378. package/dist/utils/env.d.ts.map +1 -1
  379. package/dist/utils/env.js +134 -44
  380. package/dist/utils/env.js.map +1 -1
  381. package/host-runtime/dist/governance-observation-store.d.ts +172 -0
  382. package/host-runtime/dist/governance-observation-store.js +649 -0
  383. package/host-runtime/dist/index.d.ts +6 -0
  384. package/host-runtime/dist/index.js +8 -0
  385. package/host-runtime/dist/product-telemetry/consent-store.d.ts +107 -0
  386. package/host-runtime/dist/product-telemetry/consent-store.js +329 -0
  387. package/host-runtime/dist/product-telemetry/eligibility.d.ts +53 -0
  388. package/host-runtime/dist/product-telemetry/eligibility.js +69 -0
  389. package/host-runtime/dist/product-telemetry/exporter.d.ts +46 -0
  390. package/host-runtime/dist/product-telemetry/exporter.js +71 -0
  391. package/host-runtime/dist/product-telemetry/milestone-readers.d.ts +61 -0
  392. package/host-runtime/dist/product-telemetry/milestone-readers.js +234 -0
  393. package/host-runtime/dist/product-telemetry/service.d.ts +142 -0
  394. package/host-runtime/dist/product-telemetry/service.js +532 -0
  395. package/host-runtime/dist/product-telemetry/workspace-scope.d.ts +38 -0
  396. package/host-runtime/dist/product-telemetry/workspace-scope.js +64 -0
  397. package/host-runtime/package.json +7 -2
  398. package/install-layout/dist/index.d.ts +48 -0
  399. package/install-layout/dist/index.js +81 -0
  400. package/install-layout/dist/index.test.d.ts +1 -0
  401. package/install-layout/dist/index.test.js +34 -0
  402. package/install-layout/package.json +33 -0
  403. package/package.json +16 -6
  404. package/pd-cli/dist/commands/__tests__/telemetry-flag-wiring.test.d.ts +9 -0
  405. package/pd-cli/dist/commands/__tests__/telemetry-flag-wiring.test.d.ts.map +1 -0
  406. package/pd-cli/dist/commands/__tests__/telemetry-flag-wiring.test.js +76 -0
  407. package/pd-cli/dist/commands/__tests__/telemetry-flag-wiring.test.js.map +1 -0
  408. package/pd-cli/dist/commands/__tests__/version.test.d.ts +2 -0
  409. package/pd-cli/dist/commands/__tests__/version.test.d.ts.map +1 -0
  410. package/pd-cli/dist/commands/__tests__/version.test.js +134 -0
  411. package/pd-cli/dist/commands/__tests__/version.test.js.map +1 -0
  412. package/pd-cli/dist/commands/console.d.ts.map +1 -1
  413. package/pd-cli/dist/commands/console.js +29 -10
  414. package/pd-cli/dist/commands/console.js.map +1 -1
  415. package/pd-cli/dist/commands/pain-record.d.ts.map +1 -1
  416. package/pd-cli/dist/commands/pain-record.js +6 -15
  417. package/pd-cli/dist/commands/pain-record.js.map +1 -1
  418. package/pd-cli/dist/commands/runtime-activation.d.ts +29 -0
  419. package/pd-cli/dist/commands/runtime-activation.d.ts.map +1 -1
  420. package/pd-cli/dist/commands/runtime-activation.js +70 -31
  421. package/pd-cli/dist/commands/runtime-activation.js.map +1 -1
  422. package/pd-cli/dist/commands/runtime-internalization-enqueue-successors.d.ts.map +1 -1
  423. package/pd-cli/dist/commands/runtime-internalization-enqueue-successors.js +2 -2
  424. package/pd-cli/dist/commands/runtime-internalization-enqueue-successors.js.map +1 -1
  425. package/pd-cli/dist/commands/telemetry.d.ts +82 -0
  426. package/pd-cli/dist/commands/telemetry.d.ts.map +1 -0
  427. package/pd-cli/dist/commands/telemetry.js +268 -0
  428. package/pd-cli/dist/commands/telemetry.js.map +1 -0
  429. package/pd-cli/dist/commands/version.d.ts +11 -0
  430. package/pd-cli/dist/commands/version.d.ts.map +1 -0
  431. package/pd-cli/dist/commands/version.js +48 -0
  432. package/pd-cli/dist/commands/version.js.map +1 -0
  433. package/pd-cli/dist/index.js +35 -2
  434. package/pd-cli/dist/index.js.map +1 -1
  435. package/pd-cli/dist/services/version-report.d.ts +44 -0
  436. package/pd-cli/dist/services/version-report.d.ts.map +1 -0
  437. package/pd-cli/dist/services/version-report.js +152 -0
  438. package/pd-cli/dist/services/version-report.js.map +1 -0
  439. package/pd-cli/package.json +3 -2
  440. package/plugin/dist/bundle.js +870 -932
  441. package/plugin/dist/core/init.d.ts +2 -1
  442. package/plugin/dist/core/principle-application-ledger.d.ts +10 -0
  443. package/plugin/dist/core/product-telemetry-trigger.d.ts +14 -0
  444. package/plugin/dist/core/thinking-os-parser.d.ts +5 -2
  445. package/plugin/dist/governance-audit.js +386 -0
  446. package/plugin/dist/hooks/gate-block-helper.d.ts +25 -1
  447. package/plugin/dist/hooks/gate.d.ts +22 -0
  448. package/plugin/dist/openclaw.plugin.json +1 -1
  449. package/plugin/dist/rulehost-evidence.js +832 -0
  450. package/plugin/dist/templates/langs/en/principles/THINKING_OS.md +53 -45
  451. package/plugin/dist/templates/langs/en/skills/pd-mentor/SKILL.md +1 -1
  452. package/plugin/dist/templates/langs/zh/principles/THINKING_OS.md +62 -46
  453. package/plugin/dist/templates/langs/zh/skills/pd-mentor/SKILL.md +1 -1
  454. package/plugin/dist/templates/workspace/.principles/THINKING_OS.md +66 -38
  455. package/plugin/openclaw.plugin.json +1 -1
  456. package/plugin/package.json +1 -1
  457. package/plugin/templates/langs/en/principles/THINKING_OS.md +53 -45
  458. package/plugin/templates/langs/en/skills/pd-mentor/SKILL.md +1 -1
  459. package/plugin/templates/langs/zh/principles/THINKING_OS.md +62 -46
  460. package/plugin/templates/langs/zh/skills/pd-mentor/SKILL.md +1 -1
  461. package/plugin/templates/workspace/.principles/THINKING_OS.md +66 -38
@@ -0,0 +1,649 @@
1
+ /**
2
+ * Governance Observation Store — Codex Governance Closure Slice A (PRI-622).
3
+ *
4
+ * Host-neutral storage seam for bounded Governance Observations, per Codex
5
+ * Governance Closure SPEC rev 2 §6/§7/§10/§11 and the Slice A Implementation
6
+ * SPEC (2026-08-29). The Codex adapter decodes host transcripts and calls
7
+ * {@link ingestGovernanceObservations}; this module owns everything below the
8
+ * seam: sanitization at the persistence boundary, logical/physical identity
9
+ * upsert and convergence, the transcript checkpoint, bounded retention
10
+ * (latest ≤32 visible turns per rollout AND ≤7 days, whichever expires
11
+ * sooner), compaction/rollback tombstones, and the promotion substrate
12
+ * (≤12 preceding turns + trigger + next completed assistant turn with a
13
+ * durable pending tail) that Slice B will drive after pain admission.
14
+ *
15
+ * Slice A creates NO pain identity: promotion accepts a caller-provided
16
+ * pain reference only (SPEC §18).
17
+ *
18
+ * Storage authority: the existing Workspace trajectory store
19
+ * `{workspace}/.state/trajectory.db`. The baseline schema is owned by
20
+ * openclaw-plugin's `applyTrajectorySchema` (mirrored in core's
21
+ * `ensureTrajectorySchema` for the pain tables). The `governance_*` tables
22
+ * below are written and read ONLY by this module, so their DDL lives here —
23
+ * idempotent, versioned by `governance_observation_schema_version`, and
24
+ * additive to any existing trajectory.db (unknown tables are untouched by
25
+ * the other owners). No raw transcript bytes, no second pain authority.
26
+ */
27
+ import fs from 'node:fs';
28
+ import path from 'node:path';
29
+ import Database from 'better-sqlite3';
30
+ import { sanitizeString, sanitizeValue } from '@principles/core/runtime-v2';
31
+ // ─── Retention policy constants (Owner-approved, SPEC rev 2 §11) ────────────
32
+ export const GOVERNANCE_RETENTION_MAX_TURNS = 32;
33
+ export const GOVERNANCE_RETENTION_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
34
+ export const GOVERNANCE_PROMOTION_PRECEDING_TURNS = 12;
35
+ export const GOVERNANCE_PENDING_TAIL_STALE_MS = 7 * 24 * 60 * 60 * 1000;
36
+ const GOVERNANCE_OBSERVATION_SCHEMA_VERSION = 2;
37
+ const MAX_TEXT_BOUND = 200; // matches MAX_EVIDENCE_VALUE_CHARS; guards stored identity fields
38
+ const MAX_JSON_COLUMN = 8_000;
39
+ function isRecord(value) {
40
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
41
+ }
42
+ function own(value, key) {
43
+ return Object.hasOwn(value, key) ? Object.getOwnPropertyDescriptor(value, key)?.value : undefined;
44
+ }
45
+ function boundedString(value, max) {
46
+ if (value === null || value === undefined)
47
+ return null;
48
+ return value.length > max ? value.slice(0, max) : value;
49
+ }
50
+ function safeJson(value) {
51
+ try {
52
+ const text = JSON.stringify(value);
53
+ if (typeof text !== 'string')
54
+ return null;
55
+ if (text.length <= MAX_JSON_COLUMN)
56
+ return text;
57
+ // Truncating a serialized JSON document mid-value yields INVALID JSON.
58
+ // Keep the column bound while staying parseable: store an ESCAPED
59
+ // fragment inside a small wrapper object, shrinking until the wrapper
60
+ // itself fits (escaping can expand the fragment, so verify each round).
61
+ let keep = text.length;
62
+ while (keep > 0) {
63
+ keep = Math.max(0, keep - Math.max(200, Math.floor(keep / 4)));
64
+ const wrapped = JSON.stringify({ truncated: true, preview: text.slice(0, keep) });
65
+ if (wrapped.length <= MAX_JSON_COLUMN)
66
+ return wrapped;
67
+ }
68
+ return '{"truncated":true,"preview":""}';
69
+ }
70
+ catch {
71
+ return null;
72
+ }
73
+ }
74
+ const CREATE_STATEMENTS = [
75
+ `CREATE TABLE IF NOT EXISTS governance_observation_schema_version (version INTEGER NOT NULL)`,
76
+ `CREATE TABLE IF NOT EXISTS governance_rollouts (
77
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
78
+ host_kind TEXT NOT NULL,
79
+ rollout_identity TEXT NOT NULL,
80
+ root_session_id TEXT NOT NULL,
81
+ parent_rollout_id TEXT,
82
+ agent_identity TEXT,
83
+ agent_depth INTEGER,
84
+ created_at TEXT NOT NULL,
85
+ updated_at TEXT NOT NULL,
86
+ UNIQUE(host_kind, rollout_identity)
87
+ )`,
88
+ `CREATE TABLE IF NOT EXISTS governance_observations (
89
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
90
+ rollout_row_id INTEGER NOT NULL,
91
+ host_kind TEXT NOT NULL,
92
+ rollout_identity TEXT NOT NULL,
93
+ root_session_id TEXT NOT NULL,
94
+ host_turn_id TEXT NOT NULL,
95
+ kind TEXT NOT NULL,
96
+ logical_key TEXT NOT NULL,
97
+ transcript_record_key TEXT,
98
+ source_order INTEGER,
99
+ source_ordinal INTEGER,
100
+ assistant_item_id TEXT,
101
+ phase TEXT,
102
+ tool_use_id TEXT,
103
+ transcript_tool_call_id TEXT,
104
+ visible_text TEXT,
105
+ sanitized_tool_facts_json TEXT,
106
+ source TEXT NOT NULL,
107
+ completeness TEXT NOT NULL,
108
+ retention_class TEXT NOT NULL DEFAULT 'operational',
109
+ observed_at TEXT NOT NULL,
110
+ promoted_at TEXT,
111
+ promotion_ref TEXT,
112
+ expired_at TEXT,
113
+ UNIQUE(logical_key)
114
+ )`,
115
+ `CREATE INDEX IF NOT EXISTS idx_governance_observations_source_order
116
+ ON governance_observations(rollout_row_id, source_order)`,
117
+ `CREATE UNIQUE INDEX IF NOT EXISTS idx_governance_observations_physical
118
+ ON governance_observations(transcript_record_key) WHERE transcript_record_key IS NOT NULL`,
119
+ `CREATE INDEX IF NOT EXISTS idx_governance_observations_rollout_turn
120
+ ON governance_observations(rollout_row_id, host_turn_id)`,
121
+ `CREATE INDEX IF NOT EXISTS idx_governance_observations_retention
122
+ ON governance_observations(retention_class, observed_at)`,
123
+ `CREATE TABLE IF NOT EXISTS governance_transcript_checkpoints (
124
+ host_kind TEXT NOT NULL,
125
+ rollout_identity TEXT NOT NULL,
126
+ byte_offset INTEGER NOT NULL,
127
+ last_ordinal INTEGER NOT NULL,
128
+ cli_version TEXT,
129
+ root_session_id TEXT NOT NULL,
130
+ incomplete_tail INTEGER NOT NULL DEFAULT 0,
131
+ last_degradation_reason TEXT,
132
+ last_degradation_ordinal INTEGER,
133
+ updated_at TEXT NOT NULL,
134
+ PRIMARY KEY (host_kind, rollout_identity)
135
+ )`,
136
+ `CREATE TABLE IF NOT EXISTS governance_pending_promotion_tails (
137
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
138
+ rollout_row_id INTEGER NOT NULL,
139
+ host_kind TEXT NOT NULL,
140
+ rollout_identity TEXT NOT NULL,
141
+ trigger_logical_key TEXT NOT NULL,
142
+ pain_ref TEXT NOT NULL,
143
+ state TEXT NOT NULL,
144
+ created_at TEXT NOT NULL,
145
+ completed_at TEXT,
146
+ stale_at TEXT,
147
+ UNIQUE(rollout_row_id, trigger_logical_key, pain_ref)
148
+ )`,
149
+ `CREATE INDEX IF NOT EXISTS idx_governance_pending_tails_state
150
+ ON governance_pending_promotion_tails(state, created_at)`,
151
+ ];
152
+ /** Column additions after v1 (dev databases created before the source-order fix). */
153
+ const V2_ADDED_COLUMNS = [
154
+ { table: 'governance_observations', name: 'source_order', type: 'INTEGER' },
155
+ { table: 'governance_observations', name: 'source_ordinal', type: 'INTEGER' },
156
+ ];
157
+ function ensureGovernanceObservationSchema(db) {
158
+ db.transaction(() => {
159
+ for (const statement of CREATE_STATEMENTS)
160
+ db.exec(statement);
161
+ for (const column of V2_ADDED_COLUMNS) {
162
+ try {
163
+ db.exec(`ALTER TABLE ${column.table} ADD COLUMN ${column.name} ${column.type}`);
164
+ }
165
+ catch (error) {
166
+ const message = error instanceof Error ? error.message : String(error);
167
+ if (!message.includes('duplicate column name'))
168
+ throw error;
169
+ }
170
+ }
171
+ const row = db.prepare('SELECT version FROM governance_observation_schema_version LIMIT 1').get();
172
+ const current = isRecord(row) ? own(row, 'version') : undefined;
173
+ if (typeof current !== 'number') {
174
+ db.prepare('INSERT INTO governance_observation_schema_version (version) VALUES (?)').run(GOVERNANCE_OBSERVATION_SCHEMA_VERSION);
175
+ }
176
+ else if (current < GOVERNANCE_OBSERVATION_SCHEMA_VERSION) {
177
+ db.prepare('UPDATE governance_observation_schema_version SET version = ?').run(GOVERNANCE_OBSERVATION_SCHEMA_VERSION);
178
+ }
179
+ })();
180
+ }
181
+ function openStore(workspaceDir, factory) {
182
+ const dbPath = path.join(workspaceDir, '.state', 'trajectory.db');
183
+ if (!fs.existsSync(dbPath)) {
184
+ return { ok: false, reason: 'trajectory_db_not_found', nextAction: 'initialize the selected PD workspace (pd runtime init) before enabling conversation ingestion' };
185
+ }
186
+ try {
187
+ const db = factory ? factory(dbPath) : new Database(dbPath);
188
+ db.pragma('busy_timeout = 5000');
189
+ db.pragma('journal_mode = WAL');
190
+ ensureGovernanceObservationSchema(db);
191
+ return { db, close: () => { try {
192
+ db.close();
193
+ }
194
+ catch { /* write result already determined */ } } };
195
+ }
196
+ catch (error) {
197
+ const detail = error instanceof Error ? error.message.slice(0, 200) : String(error).slice(0, 200);
198
+ return { ok: false, reason: `trajectory_database_unavailable:${detail}`, nextAction: 'inspect or repair the selected PD trajectory database' };
199
+ }
200
+ }
201
+ function rowField(row, key) {
202
+ return isRecord(row) ? own(row, key) : undefined;
203
+ }
204
+ function upsertRollout(db, input, now) {
205
+ const existing = db.prepare('SELECT id FROM governance_rollouts WHERE host_kind = ? AND rollout_identity = ?').get(input.hostKind, input.rolloutIdentity);
206
+ const existingId = rowField(existing, 'id');
207
+ if (typeof existingId === 'number') {
208
+ db.prepare('UPDATE governance_rollouts SET root_session_id = ?, parent_rollout_id = COALESCE(?, parent_rollout_id), agent_identity = COALESCE(?, agent_identity), agent_depth = COALESCE(?, agent_depth), updated_at = ? WHERE id = ?')
209
+ .run(input.rootSessionId, input.parentRolloutIdentity ?? null, input.agentIdentity ?? null, input.agentDepth ?? null, now, existingId);
210
+ return existingId;
211
+ }
212
+ const result = db.prepare('INSERT INTO governance_rollouts (host_kind, rollout_identity, root_session_id, parent_rollout_id, agent_identity, agent_depth, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)')
213
+ .run(input.hostKind, input.rolloutIdentity, input.rootSessionId, input.parentRolloutIdentity ?? null, boundedString(input.agentIdentity, MAX_TEXT_BOUND), input.agentDepth ?? null, now, now);
214
+ return Number(result.lastInsertRowid);
215
+ }
216
+ function applyObservation({ db, rolloutRowId, input, workspaceDir }) {
217
+ const existing = db.prepare('SELECT * FROM governance_observations WHERE logical_key = ?').get(input.logicalObservationKey);
218
+ if (!isRecord(existing)) {
219
+ // Physical replay guard: the same transcript record decoded twice (crash
220
+ // before checkpoint, or a re-invoked hook) must not contribute twice.
221
+ if (input.transcriptRecordKey !== undefined) {
222
+ const physical = db.prepare('SELECT id FROM governance_observations WHERE transcript_record_key = ?').get(input.transcriptRecordKey);
223
+ if (isRecord(physical))
224
+ return { disposition: 'duplicate' };
225
+ }
226
+ const sanitizedText = input.visibleText !== undefined ? sanitizeString(input.visibleText, workspaceDir) : null;
227
+ const facts = input.toolFacts !== undefined ? safeJson({ facts: sanitizeValue(input.toolFacts, 0, workspaceDir) }) : null;
228
+ db.prepare(`INSERT INTO governance_observations
229
+ (rollout_row_id, host_kind, rollout_identity, root_session_id, host_turn_id, kind, logical_key, transcript_record_key, source_order, source_ordinal, assistant_item_id, phase, tool_use_id, transcript_tool_call_id, visible_text, sanitized_tool_facts_json, source, completeness, retention_class, observed_at)
230
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'operational', ?)`)
231
+ .run(rolloutRowId, input.hostKind, input.rolloutIdentity, input.rootSessionId, input.hostTurnId, input.kind, input.logicalObservationKey, input.transcriptRecordKey ?? null, input.recordByteStart ?? null, input.recordOrdinal ?? null, boundedString(input.assistantItemId, MAX_TEXT_BOUND), boundedString(input.phase, MAX_TEXT_BOUND) ?? null, boundedString(input.toolUseId, MAX_TEXT_BOUND), boundedString(input.transcriptToolCallId, MAX_TEXT_BOUND), sanitizedText, facts, input.source, input.completeness, input.observedAt);
232
+ return { disposition: 'inserted' };
233
+ }
234
+ const existingId = rowField(existing, 'id');
235
+ const existingText = rowField(existing, 'visible_text');
236
+ // Source precedence (SPEC §10): user rows created live are enriched by the
237
+ // transcript with identity/ordering; tool rows created live are enriched or
238
+ // no-op'd by tool_use_id; assistant content comes only from the transcript.
239
+ // A user content mismatch for one logical key is a lineage conflict: keep
240
+ // the first committed content, mark partial, and stop checkpoint advance at
241
+ // the conflicting record — never overwrite silently.
242
+ if (input.kind === 'user_turn'
243
+ && input.source === 'transcript'
244
+ && input.visibleText !== undefined
245
+ && typeof existingText === 'string'
246
+ && sanitizeString(input.visibleText, workspaceDir) !== existingText) {
247
+ db.prepare("UPDATE governance_observations SET completeness = 'partial' WHERE id = ?").run(existingId);
248
+ return { disposition: 'conflict', stopByteStart: input.recordByteStart };
249
+ }
250
+ const updates = [];
251
+ const values = [];
252
+ const fill = (column, value) => {
253
+ const current = rowField(existing, column);
254
+ if ((current === null || current === undefined) && value !== null && value !== undefined) {
255
+ updates.push(`${column} = ?`);
256
+ values.push(value);
257
+ }
258
+ };
259
+ fill('transcript_record_key', input.transcriptRecordKey ?? null);
260
+ // Transcript replay enriches the live row with its real transcript position
261
+ // (PR #1455 review P1): all ordering logic reads source_order, so a live row
262
+ // must gain it the moment the transcript catch-up converges the same event.
263
+ fill('source_order', input.recordByteStart);
264
+ fill('source_ordinal', input.recordOrdinal);
265
+ fill('assistant_item_id', boundedString(input.assistantItemId, MAX_TEXT_BOUND));
266
+ fill('phase', boundedString(input.phase, MAX_TEXT_BOUND));
267
+ fill('tool_use_id', boundedString(input.toolUseId, MAX_TEXT_BOUND));
268
+ fill('transcript_tool_call_id', boundedString(input.transcriptToolCallId, MAX_TEXT_BOUND));
269
+ fill('visible_text', input.visibleText !== undefined ? sanitizeString(input.visibleText, workspaceDir) : null);
270
+ fill('sanitized_tool_facts_json', input.toolFacts !== undefined ? safeJson({ facts: sanitizeValue(input.toolFacts, 0, workspaceDir) }) : null);
271
+ const existingCompleteness = rowField(existing, 'completeness');
272
+ if (existingCompleteness === 'partial' && input.completeness === 'complete') {
273
+ updates.push('completeness = ?');
274
+ values.push('complete');
275
+ }
276
+ if (updates.length === 0)
277
+ return { disposition: 'duplicate' };
278
+ updates.push('source = ?');
279
+ values.push(input.source);
280
+ values.push(existingId);
281
+ db.prepare(`UPDATE governance_observations SET ${updates.join(', ')} WHERE id = ?`).run(...values);
282
+ return { disposition: 'enriched' };
283
+ }
284
+ function expireRows(db, ids, expiry) {
285
+ const { now, klass } = expiry;
286
+ if (ids.length === 0)
287
+ return;
288
+ const chunks = [];
289
+ for (let index = 0; index < ids.length; index += 200)
290
+ chunks.push(ids.slice(index, index + 200));
291
+ for (const chunk of chunks) {
292
+ const placeholders = chunk.map(() => '?').join(',');
293
+ db.prepare(`UPDATE governance_observations SET visible_text = NULL, sanitized_tool_facts_json = NULL, retention_class = ?, expired_at = ? WHERE id IN (${placeholders}) AND retention_class = 'operational'`)
294
+ .run(klass, now, ...chunk);
295
+ }
296
+ }
297
+ function listTurnsInSourceOrder(db, rolloutRowId) {
298
+ const rows = db.prepare(`SELECT host_turn_id, MIN(source_order) AS turn_order, MIN(id) AS arrival
299
+ FROM governance_observations
300
+ WHERE rollout_row_id = ? AND retention_class = 'operational'
301
+ GROUP BY host_turn_id
302
+ ORDER BY (MIN(source_order) IS NULL) ASC, MIN(source_order) ASC, MIN(id) ASC`).all(rolloutRowId);
303
+ return rows.map((row) => ({
304
+ hostTurnId: String(rowField(row, 'host_turn_id')),
305
+ turnOrder: typeof rowField(row, 'turn_order') === 'number' ? rowField(row, 'turn_order') : null,
306
+ }));
307
+ }
308
+ function turnObservationIds(db, rolloutRowId, hostTurnId) {
309
+ const rows = db.prepare("SELECT id FROM governance_observations WHERE rollout_row_id = ? AND host_turn_id = ? AND retention_class = 'operational'")
310
+ .all(rolloutRowId, hostTurnId);
311
+ return rows.map((row) => Number(rowField(row, 'id'))).filter((id) => Number.isInteger(id));
312
+ }
313
+ function pruneRetention(db, rolloutRowId, now) {
314
+ const nowIso = now.toISOString();
315
+ // Age bound: global, bounded single statements over the retention index.
316
+ db.prepare(`UPDATE governance_observations SET visible_text = NULL, sanitized_tool_facts_json = NULL, retention_class = 'expired', expired_at = ?
317
+ WHERE retention_class = 'operational' AND observed_at < ?`)
318
+ .run(nowIso, new Date(now.getTime() - GOVERNANCE_RETENTION_MAX_AGE_MS).toISOString());
319
+ // Turn bound: per ingested rollout, keep the latest N conversational turns
320
+ // in canonical source order (live-only turns count as newest).
321
+ if (rolloutRowId === null)
322
+ return;
323
+ const turns = listTurnsInSourceOrder(db, rolloutRowId);
324
+ if (turns.length <= GOVERNANCE_RETENTION_MAX_TURNS)
325
+ return;
326
+ const expiredIds = [];
327
+ for (const turn of turns.slice(0, turns.length - GOVERNANCE_RETENTION_MAX_TURNS)) {
328
+ expiredIds.push(...turnObservationIds(db, rolloutRowId, turn.hostTurnId));
329
+ }
330
+ expireRows(db, expiredIds, { now: nowIso, klass: 'expired' });
331
+ }
332
+ function completePendingTails(db, rolloutRowId, now) {
333
+ const pending = db.prepare("SELECT * FROM governance_pending_promotion_tails WHERE rollout_row_id = ? AND state = 'pending'").all(rolloutRowId);
334
+ let completed = 0;
335
+ for (const tail of pending) {
336
+ const triggerKey = rowField(tail, 'trigger_logical_key');
337
+ if (typeof triggerKey !== 'string')
338
+ continue;
339
+ const triggerRow = db.prepare('SELECT * FROM governance_observations WHERE logical_key = ?').get(triggerKey);
340
+ if (!isRecord(triggerRow))
341
+ continue;
342
+ const triggerOrder = rowField(triggerRow, 'source_order');
343
+ if (typeof triggerOrder !== 'number')
344
+ continue; // not yet positioned by the transcript replay
345
+ // The next completed assistant turn: the earliest final-answer assistant
346
+ // observation AFTER the trigger in transcript source order.
347
+ const next = db.prepare(`SELECT id FROM governance_observations
348
+ WHERE rollout_row_id = ? AND kind = 'assistant_turn' AND phase = 'final_answer' AND completeness = 'complete' AND source_order > ?
349
+ ORDER BY source_order LIMIT 1`).get(rolloutRowId, triggerOrder);
350
+ const nextId = rowField(next, 'id');
351
+ if (typeof nextId !== 'number')
352
+ continue;
353
+ db.prepare("UPDATE governance_observations SET retention_class = 'promoted', promoted_at = ?, promotion_ref = COALESCE(promotion_ref, ?) WHERE id = ? AND retention_class != 'promoted'")
354
+ .run(now, rowField(tail, 'pain_ref'), nextId);
355
+ db.prepare("UPDATE governance_pending_promotion_tails SET state = 'completed', completed_at = ? WHERE id = ?").run(now, rowField(tail, 'id'));
356
+ completed += 1;
357
+ }
358
+ return completed;
359
+ }
360
+ function expireStalePendingTails(db, now) {
361
+ db.prepare("UPDATE governance_pending_promotion_tails SET state = 'stale', stale_at = ? WHERE state = 'pending' AND created_at < ?")
362
+ .run(now.toISOString(), new Date(now.getTime() - GOVERNANCE_PENDING_TAIL_STALE_MS).toISOString());
363
+ }
364
+ export function ingestGovernanceObservations(input) {
365
+ const opened = openStore(input.workspaceDir, input.databaseFactory);
366
+ if (!('db' in opened))
367
+ return { ok: false, reason: opened.reason, nextAction: opened.nextAction, inserted: 0, enriched: 0, duplicates: 0, checkpointCommitted: false, warnings: [] };
368
+ const { db, close } = opened;
369
+ const now = (input.now ?? new Date()).toISOString();
370
+ const warnings = [];
371
+ try {
372
+ let inserted = 0;
373
+ let enriched = 0;
374
+ let duplicates = 0;
375
+ let conflictStop;
376
+ let conflictKey = '';
377
+ const result = db.transaction(() => {
378
+ let rolloutRowId = null;
379
+ if (input.rollout !== undefined) {
380
+ rolloutRowId = upsertRollout(db, input.rollout, now);
381
+ }
382
+ for (const observation of input.observations) {
383
+ rolloutRowId = upsertRollout(db, observation, now);
384
+ const outcome = applyObservation({ db, rolloutRowId, input: observation, workspaceDir: input.workspaceDir });
385
+ if (outcome.disposition === 'inserted')
386
+ inserted += 1;
387
+ else if (outcome.disposition === 'enriched')
388
+ enriched += 1;
389
+ else if (outcome.disposition === 'duplicate')
390
+ duplicates += 1;
391
+ else if (outcome.disposition === 'conflict') {
392
+ conflictStop = outcome.stopByteStart;
393
+ conflictKey = observation.logicalObservationKey;
394
+ return 'conflict';
395
+ }
396
+ }
397
+ // Durable checkpoint: advances only in the transaction that commits its
398
+ // decoded observations (SPEC §8) — including deltas that contained only
399
+ // skipped or unknown records. A conflict or malformed record stops the
400
+ // advance at the offending record so it is re-examined, never skipped.
401
+ if (input.checkpoint !== undefined) {
402
+ const degradations = input.degradations ?? [];
403
+ const [firstDegradation] = degradations;
404
+ const stopOffset = conflictStop;
405
+ const byteOffset = stopOffset !== undefined && stopOffset < input.checkpoint.byteOffset ? stopOffset : input.checkpoint.byteOffset;
406
+ const lastDegradation = conflictKey
407
+ ? { reason: 'logical_key_content_conflict', ordinal: null }
408
+ : firstDegradation
409
+ ? { reason: firstDegradation.reason, ordinal: firstDegradation.ordinal ?? null }
410
+ : null;
411
+ db.prepare(`INSERT INTO governance_transcript_checkpoints
412
+ (host_kind, rollout_identity, byte_offset, last_ordinal, cli_version, root_session_id, incomplete_tail, last_degradation_reason, last_degradation_ordinal, updated_at)
413
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
414
+ ON CONFLICT(host_kind, rollout_identity) DO UPDATE SET
415
+ byte_offset = excluded.byte_offset, last_ordinal = excluded.last_ordinal, cli_version = COALESCE(excluded.cli_version, cli_version),
416
+ root_session_id = excluded.root_session_id, incomplete_tail = excluded.incomplete_tail,
417
+ last_degradation_reason = excluded.last_degradation_reason, last_degradation_ordinal = excluded.last_degradation_ordinal, updated_at = excluded.updated_at`)
418
+ .run(input.checkpoint.hostKind, input.checkpoint.rolloutIdentity, byteOffset, input.checkpoint.lastOrdinal, input.checkpoint.cliVersion ?? null, input.checkpoint.rootSessionId, input.checkpoint.incompleteTail ? 1 : 0, lastDegradation?.reason ?? null, lastDegradation?.ordinal ?? null, now);
419
+ }
420
+ // Marker semantics: compaction replaces logical history (tombstone every
421
+ // unpromoted observation recorded before the marker); rollback truncates
422
+ // the last N logical turns. Physical records remain; bodies must not.
423
+ if (input.compactionTimestamp && rolloutRowId !== null) {
424
+ const stale = db.prepare(`SELECT id FROM governance_observations WHERE rollout_row_id = ? AND retention_class = 'operational' AND observed_at <= ?`)
425
+ .all(rolloutRowId, input.compactionTimestamp);
426
+ expireRows(db, stale.map((row) => Number(rowField(row, 'id'))).filter((id) => Number.isInteger(id)), { now, klass: 'rolled_back' });
427
+ warnings.push('compaction_marker_applied');
428
+ }
429
+ if (Array.isArray(input.rollbackTurns) && input.rollbackTurns.length > 0 && rolloutRowId !== null) {
430
+ let rolledBackTurns = 0;
431
+ for (const markerTurns of input.rollbackTurns) {
432
+ if (typeof markerTurns !== 'number' || markerTurns <= 0)
433
+ continue;
434
+ // The LAST N logical turns in canonical source order (live-only
435
+ // turns are the newest; replayed turns order by transcript bytes).
436
+ const turns = listTurnsInSourceOrder(db, rolloutRowId).slice(-markerTurns);
437
+ const ids = [];
438
+ for (const turn of turns) {
439
+ ids.push(...turnObservationIds(db, rolloutRowId, turn.hostTurnId));
440
+ }
441
+ expireRows(db, ids, { now, klass: 'rolled_back' });
442
+ rolledBackTurns += markerTurns;
443
+ }
444
+ warnings.push(`rollback_marker_applied:${rolledBackTurns}`);
445
+ }
446
+ if (rolloutRowId !== null)
447
+ completePendingTails(db, rolloutRowId, now);
448
+ expireStalePendingTails(db, new Date(now));
449
+ pruneRetention(db, rolloutRowId, new Date(now));
450
+ return 'committed';
451
+ })();
452
+ if (result === 'conflict') {
453
+ return {
454
+ ok: false, reason: 'logical_key_content_conflict', nextAction: 'the first committed content was preserved and the observation is marked partial; run the audited quarantine/recovery command once available (Slice D) or re-ingest from a fresh rollout',
455
+ inserted, enriched, duplicates, checkpointCommitted: false, warnings: [`conflict:${conflictKey}`],
456
+ };
457
+ }
458
+ return { ok: true, inserted, enriched, duplicates, checkpointCommitted: input.checkpoint !== undefined, warnings };
459
+ }
460
+ catch (error) {
461
+ const detail = error instanceof Error ? error.message.slice(0, 200) : String(error).slice(0, 200);
462
+ return { ok: false, reason: `governance_write_failed:${detail}`, nextAction: 'inspect the workspace trajectory database and retry; nothing was partially committed', inserted: 0, enriched: 0, duplicates: 0, checkpointCommitted: false, warnings: [] };
463
+ }
464
+ finally {
465
+ close();
466
+ }
467
+ }
468
+ export function readGovernanceCheckpoint(args) {
469
+ const { workspaceDir, hostKind, rolloutIdentity, databaseFactory } = args;
470
+ const opened = openStore(workspaceDir, databaseFactory);
471
+ if (!('db' in opened))
472
+ return opened;
473
+ const { db, close } = opened;
474
+ try {
475
+ const row = db.prepare('SELECT * FROM governance_transcript_checkpoints WHERE host_kind = ? AND rollout_identity = ?').get(hostKind, rolloutIdentity);
476
+ if (!isRecord(row))
477
+ return null;
478
+ return {
479
+ hostKind: String(rowField(row, 'host_kind')),
480
+ rolloutIdentity: String(rowField(row, 'rollout_identity')),
481
+ byteOffset: Number(rowField(row, 'byte_offset')),
482
+ lastOrdinal: Number(rowField(row, 'last_ordinal')),
483
+ cliVersion: typeof rowField(row, 'cli_version') === 'string' ? rowField(row, 'cli_version') : null,
484
+ rootSessionId: String(rowField(row, 'root_session_id')),
485
+ incompleteTail: rowField(row, 'incomplete_tail') === 1,
486
+ lastDegradationReason: typeof rowField(row, 'last_degradation_reason') === 'string' ? rowField(row, 'last_degradation_reason') : null,
487
+ lastDegradationOrdinal: typeof rowField(row, 'last_degradation_ordinal') === 'number' ? rowField(row, 'last_degradation_ordinal') : null,
488
+ updatedAt: String(rowField(row, 'updated_at')),
489
+ };
490
+ }
491
+ finally {
492
+ close();
493
+ }
494
+ }
495
+ export function listGovernanceObservations(input) {
496
+ const opened = openStore(input.workspaceDir, input.databaseFactory);
497
+ if (!('db' in opened))
498
+ return opened;
499
+ const { db, close } = opened;
500
+ try {
501
+ const conditions = [];
502
+ const values = [];
503
+ if (input.rolloutIdentity !== undefined) {
504
+ conditions.push('rollout_identity = ?');
505
+ values.push(input.rolloutIdentity);
506
+ }
507
+ if (input.retentionClass !== undefined) {
508
+ conditions.push('retention_class = ?');
509
+ values.push(input.retentionClass);
510
+ }
511
+ const where = conditions.length > 0 ? ` WHERE ${conditions.join(' AND ')}` : '';
512
+ const limit = Math.min(Math.max(input.limit ?? 500, 1), 2000);
513
+ const rows = db.prepare(`SELECT * FROM governance_observations${where} ORDER BY id LIMIT ?`).all(...values, limit);
514
+ const observations = rows.map((row) => ({
515
+ id: Number(rowField(row, 'id')),
516
+ rolloutIdentity: String(rowField(row, 'rollout_identity')),
517
+ rootSessionId: String(rowField(row, 'root_session_id')),
518
+ hostTurnId: String(rowField(row, 'host_turn_id')),
519
+ kind: rowField(row, 'kind'),
520
+ logicalKey: String(rowField(row, 'logical_key')),
521
+ transcriptRecordKey: typeof rowField(row, 'transcript_record_key') === 'string' ? rowField(row, 'transcript_record_key') : null,
522
+ sourceOrder: typeof rowField(row, 'source_order') === 'number' ? rowField(row, 'source_order') : null,
523
+ sourceOrdinal: typeof rowField(row, 'source_ordinal') === 'number' ? rowField(row, 'source_ordinal') : null,
524
+ assistantItemId: typeof rowField(row, 'assistant_item_id') === 'string' ? rowField(row, 'assistant_item_id') : null,
525
+ phase: typeof rowField(row, 'phase') === 'string' ? rowField(row, 'phase') : null,
526
+ toolUseId: typeof rowField(row, 'tool_use_id') === 'string' ? rowField(row, 'tool_use_id') : null,
527
+ transcriptToolCallId: typeof rowField(row, 'transcript_tool_call_id') === 'string' ? rowField(row, 'transcript_tool_call_id') : null,
528
+ visibleText: typeof rowField(row, 'visible_text') === 'string' ? rowField(row, 'visible_text') : null,
529
+ sanitizedToolFactsJson: typeof rowField(row, 'sanitized_tool_facts_json') === 'string' ? rowField(row, 'sanitized_tool_facts_json') : null,
530
+ source: rowField(row, 'source'),
531
+ completeness: rowField(row, 'completeness'),
532
+ retentionClass: rowField(row, 'retention_class'),
533
+ observedAt: String(rowField(row, 'observed_at')),
534
+ promotionRef: typeof rowField(row, 'promotion_ref') === 'string' ? rowField(row, 'promotion_ref') : null,
535
+ }));
536
+ return { ok: true, observations };
537
+ }
538
+ finally {
539
+ close();
540
+ }
541
+ }
542
+ class PromotionMissingError extends Error {
543
+ reason;
544
+ nextAction;
545
+ constructor(reason, nextAction) {
546
+ super(reason);
547
+ this.reason = reason;
548
+ this.nextAction = nextAction;
549
+ this.name = 'PromotionMissingError';
550
+ }
551
+ }
552
+ export function promoteGovernanceEvidence(input) {
553
+ const opened = openStore(input.workspaceDir, input.databaseFactory);
554
+ if (!('db' in opened))
555
+ return { ok: false, reason: opened.reason, nextAction: opened.nextAction };
556
+ const { db, close } = opened;
557
+ const now = (input.now ?? new Date()).toISOString();
558
+ const warnings = [];
559
+ try {
560
+ let promoted = 0;
561
+ let tailState = 'pending';
562
+ db.transaction(() => {
563
+ const rollout = db.prepare('SELECT id FROM governance_rollouts WHERE host_kind = ? AND rollout_identity = ?').get(input.hostKind, input.rolloutIdentity);
564
+ const rolloutRowId = rowField(rollout, 'id');
565
+ if (typeof rolloutRowId !== 'number')
566
+ throw new PromotionMissingError('rollout_not_found', 'ingest the rollout before promoting evidence around it');
567
+ // Idempotency: a completed or still-pending tail for the same trigger +
568
+ // pain reference is a no-op replay (crash, restart, duplicate delivery).
569
+ const existingTail = db.prepare('SELECT * FROM governance_pending_promotion_tails WHERE rollout_row_id = ? AND trigger_logical_key = ? AND pain_ref = ?')
570
+ .get(rolloutRowId, input.triggerLogicalKey, input.painRef);
571
+ if (isRecord(existingTail)) {
572
+ const state = rowField(existingTail, 'state');
573
+ if (state === 'completed') {
574
+ tailState = 'completed';
575
+ return;
576
+ }
577
+ if (state === 'pending') {
578
+ // Recovery invariant: the tail may already be satisfiable now.
579
+ const completedNow = completePendingTails(db, rolloutRowId, now);
580
+ if (completedNow > 0)
581
+ tailState = 'completed';
582
+ return;
583
+ }
584
+ // state === 'stale': a diagnosable terminal state — do not silently
585
+ // re-arm; the caller must start a new promotion with a new reference.
586
+ throw new PromotionMissingError('promotion_tail_stale', 'the pending promotion tail expired; create a new promotion with a new pain reference');
587
+ }
588
+ const triggerRow = db.prepare('SELECT * FROM governance_observations WHERE logical_key = ? AND rollout_row_id = ?').get(input.triggerLogicalKey, rolloutRowId);
589
+ if (!isRecord(triggerRow))
590
+ throw new PromotionMissingError('trigger_not_found', 'the triggering observation must be ingested before promotion');
591
+ const triggerId = rowField(triggerRow, 'id');
592
+ const triggerOrder = typeof rowField(triggerRow, 'source_order') === 'number' ? rowField(triggerRow, 'source_order') : null;
593
+ const promoteIds = (ids) => {
594
+ for (const id of ids) {
595
+ db.prepare("UPDATE governance_observations SET retention_class = 'promoted', promoted_at = ?, promotion_ref = COALESCE(promotion_ref, ?) WHERE id = ? AND retention_class != 'promoted'")
596
+ .run(now, input.painRef, id);
597
+ promoted += 1;
598
+ }
599
+ };
600
+ // ≤12 preceding visible turns: the turns immediately BEFORE the
601
+ // trigger's own turn in canonical source order (transcript byte order;
602
+ // a live trigger not yet replayed positions after everything observed
603
+ // so far, which is exactly its real position — nothing newer exists).
604
+ const turns = listTurnsInSourceOrder(db, rolloutRowId);
605
+ const triggerTurnIndex = turns.findIndex((turn) => {
606
+ const ids = db.prepare('SELECT id FROM governance_observations WHERE rollout_row_id = ? AND host_turn_id = ? AND logical_key = ?')
607
+ .all(rolloutRowId, turn.hostTurnId, input.triggerLogicalKey);
608
+ return ids.length > 0;
609
+ });
610
+ const precedingStart = triggerTurnIndex > 0 ? Math.max(0, triggerTurnIndex - GOVERNANCE_PROMOTION_PRECEDING_TURNS) : -1;
611
+ const preceding = triggerTurnIndex > 0 ? turns.slice(precedingStart, triggerTurnIndex) : [];
612
+ for (const turn of preceding) {
613
+ promoteIds(turnObservationIds(db, rolloutRowId, turn.hostTurnId));
614
+ }
615
+ promoteIds([Number(triggerId)]);
616
+ if (triggerOrder !== null) {
617
+ // Next completed assistant turn: the earliest final-answer assistant
618
+ // AFTER the trigger in transcript source order. When the trigger has
619
+ // no transcript position yet, the durable pending tail below resolves
620
+ // once the replay enriches it (completePendingTails re-checks).
621
+ const next = db.prepare(`SELECT id FROM governance_observations
622
+ WHERE rollout_row_id = ? AND kind = 'assistant_turn' AND phase = 'final_answer' AND completeness = 'complete' AND source_order > ?
623
+ ORDER BY source_order LIMIT 1`).get(rolloutRowId, triggerOrder);
624
+ const nextId = rowField(next, 'id');
625
+ if (typeof nextId === 'number') {
626
+ promoteIds([nextId]);
627
+ tailState = 'completed';
628
+ }
629
+ }
630
+ if (tailState !== 'completed') {
631
+ db.prepare(`INSERT INTO governance_pending_promotion_tails (rollout_row_id, host_kind, rollout_identity, trigger_logical_key, pain_ref, state, created_at)
632
+ VALUES (?, ?, ?, ?, ?, 'pending', ?)`)
633
+ .run(rolloutRowId, input.hostKind, input.rolloutIdentity, input.triggerLogicalKey, input.painRef, now);
634
+ tailState = 'pending';
635
+ warnings.push('pending_next_assistant_tail');
636
+ }
637
+ })();
638
+ return { ok: true, promoted, tailState, warnings };
639
+ }
640
+ catch (error) {
641
+ if (error instanceof PromotionMissingError)
642
+ return { ok: false, reason: error.reason, nextAction: error.nextAction };
643
+ const detail = error instanceof Error ? error.message.slice(0, 200) : String(error).slice(0, 200);
644
+ return { ok: false, reason: `governance_write_failed:${detail}`, nextAction: 'inspect the workspace trajectory database and retry; nothing was partially committed' };
645
+ }
646
+ finally {
647
+ close();
648
+ }
649
+ }
@@ -8,7 +8,13 @@ export * from './pd-config.js';
8
8
  export * from './production-rulehost-gate.js';
9
9
  export * from './rule-implementation-runtime.js';
10
10
  export * from './production-pain-evidence.js';
11
+ export * from './governance-observation-store.js';
11
12
  export * from './host-liveness-contract.js';
13
+ export * from './product-telemetry/consent-store.js';
14
+ export * from './product-telemetry/eligibility.js';
15
+ export * from './product-telemetry/exporter.js';
16
+ export * from './product-telemetry/milestone-readers.js';
17
+ export * from './product-telemetry/service.js';
12
18
  export declare const HOST_RUNTIME_ROUTES: readonly ['before_prompt_build', 'before_tool_call', 'after_tool_call'];
13
19
  export type HostRuntimeRoute = (typeof HOST_RUNTIME_ROUTES)[number];
14
20
  export type HostRuntimePort = (event: HostEvent) => HostEventResult | Promise<HostEventResult>;