blun-king-cli 9.1.587 → 9.1.588

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 (436) hide show
  1. package/CHANGELOG.md +11 -185
  2. package/LIESMICH.txt +51 -13
  3. package/README.md +44 -47
  4. package/agent-spine-plugin/.codex-plugin/plugin.json +16 -4
  5. package/agent-spine-plugin/CHANGELOG.md +37 -5
  6. package/agent-spine-plugin/README.md +3 -3
  7. package/agent-spine-plugin/blun.plugin.json +45 -10
  8. package/agent-spine-plugin/docs/artifact-evaluation.md +93 -0
  9. package/agent-spine-plugin/docs/host-integration.md +42 -27
  10. package/agent-spine-plugin/docs/preflight-recall.md +4 -2
  11. package/agent-spine-plugin/docs/session-timeline.md +97 -236
  12. package/agent-spine-plugin/docs/world-model.md +25 -0
  13. package/agent-spine-plugin/hooks/codex.json +1 -1
  14. package/agent-spine-plugin/hooks/hooks.json +1 -1
  15. package/agent-spine-plugin/package.json +1 -3
  16. package/agent-spine-plugin/scripts/check-hosts.js +3 -3
  17. package/agent-spine-plugin/scripts/release-check.js +10 -5
  18. package/agent-spine-plugin/scripts/run-checks.js +4 -1
  19. package/agent-spine-plugin/scripts/run-tests-hermetic.js +32 -6
  20. package/agent-spine-plugin/src/cli-learning.js +15 -0
  21. package/agent-spine-plugin/src/cli.js +2 -0
  22. package/agent-spine-plugin/src/hook.js +32 -32
  23. package/agent-spine-plugin/src/lib/action-lesson-recall.js +73 -8
  24. package/agent-spine-plugin/src/lib/briefing.js +146 -36
  25. package/agent-spine-plugin/src/lib/channel-continuity.js +19 -0
  26. package/agent-spine-plugin/src/lib/delivery-agent-usage.js +14 -7
  27. package/agent-spine-plugin/src/lib/gateway-group-response.js +128 -0
  28. package/agent-spine-plugin/src/lib/gateway-runs.js +24 -15
  29. package/agent-spine-plugin/src/lib/hook-briefing-use.js +13 -3
  30. package/agent-spine-plugin/src/lib/hook-context.js +16 -3
  31. package/agent-spine-plugin/src/lib/hook-output.js +129 -5
  32. package/agent-spine-plugin/src/lib/hook-timeline.js +5 -3
  33. package/agent-spine-plugin/src/lib/indexed-memory.js +2 -2
  34. package/agent-spine-plugin/src/lib/learning-artifact-evaluator.js +114 -0
  35. package/agent-spine-plugin/src/lib/learning-context.js +11 -4
  36. package/agent-spine-plugin/src/lib/learning-measurements.js +2 -2
  37. package/agent-spine-plugin/src/lib/mcp-runtime.js +89 -3
  38. package/agent-spine-plugin/src/lib/mcp-source-context.js +12 -2
  39. package/agent-spine-plugin/src/lib/mcp-timeline-tools.js +91 -8
  40. package/agent-spine-plugin/src/lib/mcp-world-tools.js +2 -2
  41. package/agent-spine-plugin/src/lib/owned-file-lock.js +20 -1
  42. package/agent-spine-plugin/src/lib/persona-runtime.js +2 -2
  43. package/agent-spine-plugin/src/lib/preflight-delivery-id.js +27 -0
  44. package/agent-spine-plugin/src/lib/preflight.js +4 -4
  45. package/agent-spine-plugin/src/lib/session-timeline-codex.js +15 -0
  46. package/agent-spine-plugin/src/lib/session-timeline-contract.js +12 -4
  47. package/agent-spine-plugin/src/lib/session-timeline-event-extract.js +36 -7
  48. package/agent-spine-plugin/src/lib/session-timeline-host-origin.js +13 -10
  49. package/agent-spine-plugin/src/lib/session-timeline-invocation.js +1 -1
  50. package/agent-spine-plugin/src/lib/session-timeline-king.js +14 -0
  51. package/agent-spine-plugin/src/lib/session-timeline-prior.js +18 -12
  52. package/agent-spine-plugin/src/lib/session-timeline-provider.js +5 -0
  53. package/agent-spine-plugin/src/lib/session-timeline-query.js +2 -0
  54. package/agent-spine-plugin/src/lib/session-timeline-results.js +35 -10
  55. package/agent-spine-plugin/src/lib/session-timeline-source-open.js +30 -0
  56. package/agent-spine-plugin/src/lib/session-timeline.js +122 -75
  57. package/agent-spine-plugin/src/lib/source-roots.js +3 -2
  58. package/agent-spine-plugin/src/lib/task-knowledge-context.js +22 -1
  59. package/agent-spine-plugin/src/lib/timeline-continuation-update.js +100 -0
  60. package/agent-spine-plugin/src/lib/timeline-tool-guard.js +30 -7
  61. package/agent-spine-plugin/src/lib/timeline-user-feedback.js +217 -0
  62. package/agent-spine-plugin/src/lib/timeline-world-capture.js +233 -0
  63. package/agent-spine-plugin/src/lib/world-knowledge.js +59 -2
  64. package/agent-spine-plugin/src/lib/world-model.js +64 -9
  65. package/agent-spine-plugin/src/worker.js +13 -1
  66. package/bin/blun.js +43 -28
  67. package/bin/core-bootstrap.js +5 -4
  68. package/bin/king.js +43 -28
  69. package/bin/launcher-mode.js +1 -10
  70. package/bin/launcher-runtime.js +128 -295
  71. package/bin/managed-node.js +0 -0
  72. package/bin/managed-plugin-selection.cjs +0 -1
  73. package/bin/native-module-repair.js +0 -0
  74. package/bin/node-runtime.js +0 -0
  75. package/bin/node-version.js +0 -0
  76. package/bin/plugin-bootstrap.js +56 -120
  77. package/bin/private-paths.js +11 -34
  78. package/bin/standard-tools-bootstrap.js +34 -114
  79. package/bin/turn-thinking-policy.cjs +3 -11
  80. package/bin/update-copy.js +200 -0
  81. package/bin/update-lease.js +0 -0
  82. package/bin/update-notice.js +136 -289
  83. package/bin/verify-agent-behavior.cjs +122 -0
  84. package/bin/verify-agent-components.cjs +104 -0
  85. package/bin/verify-bundled-agent-sources.cjs +57 -0
  86. package/blun.mjs +143076 -135288
  87. package/bundled-agent-sources.json +701 -0
  88. package/package.json +12 -15
  89. package/standard-skills/translate-native/README.md +1293 -0
  90. package/standard-skills/translate-native/SKILL.md +172 -22
  91. package/standard-skills/translate-native/VERSION +1 -1
  92. package/standard-skills/translate-native/agents/openai.yaml +18 -0
  93. package/standard-skills/translate-native/assets/icon.svg +8 -0
  94. package/standard-skills/translate-native/docs/BLUN_CODE_INTEGRATION.md +76 -0
  95. package/standard-skills/translate-native/docs/PREMORTEM.md +489 -0
  96. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION.md +2035 -0
  97. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_API.md +1302 -0
  98. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_EVIDENCE_HTTP.md +136 -0
  99. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_HEALTH_HTTP.md +130 -0
  100. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_HTTP_PROVIDER.md +175 -0
  101. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_RECEIPT_VERIFIER_HTTP.md +86 -0
  102. package/standard-skills/translate-native/integrations/AGENT_RULES.md +32 -0
  103. package/standard-skills/translate-native/integrations/adapters/blun-code-language-guard.js +514 -0
  104. package/standard-skills/translate-native/integrations/adapters/node-language-guard.js +230 -0
  105. package/standard-skills/translate-native/integrations/audit_log.py +327 -0
  106. package/standard-skills/translate-native/integrations/claude_language_hook.js +1536 -0
  107. package/standard-skills/translate-native/integrations/commercial_localization_profile.py +42 -0
  108. package/standard-skills/translate-native/integrations/delivery-policy.example.json +28 -0
  109. package/standard-skills/translate-native/integrations/enforced_delivery.py +543 -0
  110. package/standard-skills/translate-native/integrations/guard_service.py +435 -0
  111. package/standard-skills/translate-native/integrations/language_gateway.py +67 -0
  112. package/standard-skills/translate-native/integrations/mcp_auth_headers.py +198 -0
  113. package/standard-skills/translate-native/integrations/mcp_http_gateway.py +429 -0
  114. package/standard-skills/translate-native/integrations/non_language_html_entities.js +1485 -0
  115. package/standard-skills/translate-native/integrations/pre_output_guard.py +65 -0
  116. package/standard-skills/translate-native/integrations/task_router.py +101 -0
  117. package/standard-skills/translate-native/integrations/website_localization.py +401 -0
  118. package/standard-skills/translate-native/integrations/website_localization_api.py +581 -0
  119. package/standard-skills/translate-native/integrations/website_localization_benchmark.py +1885 -0
  120. package/standard-skills/translate-native/integrations/website_localization_benchmark_campaign.py +1772 -0
  121. package/standard-skills/translate-native/integrations/website_localization_benchmark_candidate.py +506 -0
  122. package/standard-skills/translate-native/integrations/website_localization_benchmark_http.py +400 -0
  123. package/standard-skills/translate-native/integrations/website_localization_benchmark_review_store.py +781 -0
  124. package/standard-skills/translate-native/integrations/website_localization_benchmark_reviewer_http.py +500 -0
  125. package/standard-skills/translate-native/integrations/website_localization_benchmark_runtime.py +1107 -0
  126. package/standard-skills/translate-native/integrations/website_localization_benchmark_suite.py +463 -0
  127. package/standard-skills/translate-native/integrations/website_localization_cms.py +2835 -0
  128. package/standard-skills/translate-native/integrations/website_localization_cms_client.py +875 -0
  129. package/standard-skills/translate-native/integrations/website_localization_cms_dispatch.py +805 -0
  130. package/standard-skills/translate-native/integrations/website_localization_cms_http.py +588 -0
  131. package/standard-skills/translate-native/integrations/website_localization_cms_lifecycle_monitor.py +991 -0
  132. package/standard-skills/translate-native/integrations/website_localization_cms_receiver.py +1441 -0
  133. package/standard-skills/translate-native/integrations/website_localization_cms_receiver_runtime.py +414 -0
  134. package/standard-skills/translate-native/integrations/website_localization_cms_receiver_store.py +1073 -0
  135. package/standard-skills/translate-native/integrations/website_localization_cms_removal_dispatch.py +865 -0
  136. package/standard-skills/translate-native/integrations/website_localization_cms_source_client.py +583 -0
  137. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery.py +964 -0
  138. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_runtime.py +665 -0
  139. package/standard-skills/translate-native/integrations/website_localization_cms_source_http.py +1153 -0
  140. package/standard-skills/translate-native/integrations/website_localization_cms_source_runtime.py +675 -0
  141. package/standard-skills/translate-native/integrations/website_localization_cms_source_service.py +1125 -0
  142. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification.py +674 -0
  143. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_http.py +444 -0
  144. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_receiver.py +1469 -0
  145. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_receiver_runtime.py +1142 -0
  146. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_processing_monitor.py +634 -0
  147. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_receiver_client.py +804 -0
  148. package/standard-skills/translate-native/integrations/website_localization_deepl_baseline.py +922 -0
  149. package/standard-skills/translate-native/integrations/website_localization_evidence_http.py +482 -0
  150. package/standard-skills/translate-native/integrations/website_localization_health.py +1541 -0
  151. package/standard-skills/translate-native/integrations/website_localization_health_http.py +372 -0
  152. package/standard-skills/translate-native/integrations/website_localization_http_provider.py +297 -0
  153. package/standard-skills/translate-native/integrations/website_localization_native_reference_http.py +479 -0
  154. package/standard-skills/translate-native/integrations/website_localization_native_reference_intake.py +363 -0
  155. package/standard-skills/translate-native/integrations/website_localization_native_reference_queue.py +1449 -0
  156. package/standard-skills/translate-native/integrations/website_localization_native_reference_store.py +420 -0
  157. package/standard-skills/translate-native/integrations/website_localization_quality_profiles.py +235 -0
  158. package/standard-skills/translate-native/integrations/website_localization_queue.py +671 -0
  159. package/standard-skills/translate-native/integrations/website_localization_receipt_verifier_http.py +516 -0
  160. package/standard-skills/translate-native/integrations/website_localization_release.py +928 -0
  161. package/standard-skills/translate-native/integrations/website_localization_release_coordinator.py +1008 -0
  162. package/standard-skills/translate-native/integrations/website_localization_runner.py +276 -0
  163. package/standard-skills/translate-native/integrations/website_localization_runtime.py +862 -0
  164. package/standard-skills/translate-native/integrations/website_localization_service.py +350 -0
  165. package/standard-skills/translate-native/integrations/website_localization_supervisor.py +511 -0
  166. package/standard-skills/translate-native/integrations/website_localization_worker.py +663 -0
  167. package/standard-skills/translate-native/provenance.json +3 -4
  168. package/standard-skills/translate-native/references/commercial-localization.md +177 -0
  169. package/standard-skills/translate-native/scripts/blun_language_guard.py +7 -1
  170. package/standard-skills/translate-native/scripts/check_commercial_review.py +80 -0
  171. package/standard-skills/translate-native/scripts/commercial_localization_profile.py +333 -0
  172. package/standard-tools/language-guard/LICENSE +21 -0
  173. package/standard-tools/language-guard/VERSION +1 -0
  174. package/standard-tools/language-guard/blun_language_guard.py +7 -1
  175. package/standard-tools/language-guard/check_commercial_review.py +80 -0
  176. package/standard-tools/language-guard/commercial_localization_profile.py +333 -0
  177. package/standard-tools/language-guard/language_gateway.py +62 -0
  178. package/standard-tools/language-guard/pre_output_guard.py +64 -0
  179. package/standard-tools/language-guard/provenance.json +4 -11
  180. package/standard-tools/manifest.json +34 -11
  181. package/telegram-plugin/commands/access.md +2 -10
  182. package/telegram-plugin/dist/bridge.mjs +64041 -687
  183. package/telegram-plugin/dist/mcp-server.mjs +72810 -9027
  184. package/telegram-plugin/dist/noise.mjs +28 -63511
  185. package/agent-spine-plugin/CONTRIBUTING.md +0 -52
  186. package/agent-spine-plugin/SECURITY.md +0 -47
  187. package/agent-spine-plugin/docs/assignment-continuation.md +0 -48
  188. package/agent-spine-plugin/docs/releasing.md +0 -85
  189. package/agent-spine-plugin/docs/structured-completion.md +0 -67
  190. package/bin/abort-listener-policy.cjs +0 -43
  191. package/bin/active-steer-priority-policy.cjs +0 -24
  192. package/bin/agent-api-http-adapter.mjs +0 -446
  193. package/bin/agent-api-private-http-server.mjs +0 -288
  194. package/bin/agent-api-runtime.mjs +0 -252
  195. package/bin/agent-api-service-environment.mjs +0 -236
  196. package/bin/agent-api-service-host.mjs +0 -209
  197. package/bin/agent-api-service-process.mjs +0 -171
  198. package/bin/agent-api-session-registry.mjs +0 -428
  199. package/bin/agent-api-tool-broker.cjs +0 -248
  200. package/bin/agent-api-turn-controller.mjs +0 -461
  201. package/bin/agent-api-usage-journal.cjs +0 -259
  202. package/bin/agent-resume-snapshot.cjs +0 -241
  203. package/bin/agentspine-king-goal-inbox.mjs +0 -111
  204. package/bin/agentspine-king-goal-intake.mjs +0 -106
  205. package/bin/approval-rejection-stop.cjs +0 -15
  206. package/bin/assistant-message-offload-policy.cjs +0 -284
  207. package/bin/baseline-skill-performance-policy.cjs +0 -39
  208. package/bin/bash-search-scope-policy.cjs +0 -49
  209. package/bin/codebase-search-runtime.cjs +0 -23
  210. package/bin/cognitive-action-checkpoint.cjs +0 -1104
  211. package/bin/cognitive-attention-delivery.cjs +0 -76
  212. package/bin/cognitive-attention-policy.cjs +0 -143
  213. package/bin/cognitive-attention-runtime.cjs +0 -91
  214. package/bin/cognitive-context-projection.cjs +0 -73
  215. package/bin/cognitive-cross-portal-acceptance.cjs +0 -443
  216. package/bin/cognitive-effective-view.cjs +0 -77
  217. package/bin/cognitive-focus-projection.cjs +0 -206
  218. package/bin/cognitive-focus-scope.cjs +0 -37
  219. package/bin/cognitive-goal-autostart-policy.cjs +0 -72
  220. package/bin/cognitive-goal-time-trigger-controller.cjs +0 -146
  221. package/bin/cognitive-memory-adapter.cjs +0 -282
  222. package/bin/cognitive-memory-command.cjs +0 -293
  223. package/bin/cognitive-memory-provider.cjs +0 -92
  224. package/bin/cognitive-salience-policy.cjs +0 -159
  225. package/bin/cognitive-state-store.cjs +0 -508
  226. package/bin/cognitive-turn-lifecycle.cjs +0 -624
  227. package/bin/cognitive-work-focus.cjs +0 -180
  228. package/bin/compaction-history-archive.cjs +0 -166
  229. package/bin/compaction-history-startup.cjs +0 -50
  230. package/bin/compaction-model-policy.cjs +0 -31
  231. package/bin/compaction-stage-policy.cjs +0 -21
  232. package/bin/compaction-transaction-policy.cjs +0 -122
  233. package/bin/config-write-dedup-policy.cjs +0 -27
  234. package/bin/context-budget-ledger.cjs +0 -31
  235. package/bin/context-doctor-policy.cjs +0 -70
  236. package/bin/context-insight-policy.cjs +0 -36
  237. package/bin/context-performance-policy.cjs +0 -19
  238. package/bin/context-pressure-policy.cjs +0 -20
  239. package/bin/cron-run-output.cjs +0 -45
  240. package/bin/cron-run-store.cjs +0 -145
  241. package/bin/curiosity-scout-policy.cjs +0 -49
  242. package/bin/default-model-output-budget-policy.cjs +0 -28
  243. package/bin/durable-task-resume-policy.cjs +0 -130
  244. package/bin/durable-task-resume-runtime.cjs +0 -117
  245. package/bin/durable-task-resume-store.cjs +0 -88
  246. package/bin/editable-tool-approval-policy.cjs +0 -540
  247. package/bin/editable-tool-approval-runtime.cjs +0 -99
  248. package/bin/effective-system-prompt-cache-policy.cjs +0 -33
  249. package/bin/error-memory-performance-policy.cjs +0 -113
  250. package/bin/file-observation-policy.cjs +0 -133
  251. package/bin/foreground-output-capture-policy.cjs +0 -41
  252. package/bin/generated-source-health.cjs +0 -142
  253. package/bin/glob-pattern-policy.cjs +0 -13
  254. package/bin/goal-completion-evidence-policy.cjs +0 -120
  255. package/bin/grep-output-limit-policy.cjs +0 -39
  256. package/bin/historical-media-projection-policy.cjs +0 -48
  257. package/bin/history-offload-pressure-policy.cjs +0 -33
  258. package/bin/html-to-research-markdown.cjs +0 -147
  259. package/bin/identity-context-policy.cjs +0 -764
  260. package/bin/identity-journal-policy.cjs +0 -107
  261. package/bin/input-draft-persistence.cjs +0 -77
  262. package/bin/king-tui-function-contract.json +0 -33
  263. package/bin/launcher-restart-policy.cjs +0 -150
  264. package/bin/live-response-repetition-guard.cjs +0 -196
  265. package/bin/llm-config-log-dedup-policy.cjs +0 -76
  266. package/bin/loop-event-record-policy.cjs +0 -174
  267. package/bin/managed-context-startup-policy.cjs +0 -27
  268. package/bin/media-activity-layout-policy.cjs +0 -34
  269. package/bin/media-auto-retrieval-policy.cjs +0 -90
  270. package/bin/media-result-policy.cjs +0 -59
  271. package/bin/micro-compaction-policy.cjs +0 -145
  272. package/bin/mistake-relevance-policy.cjs +0 -319
  273. package/bin/model-retry-progress-policy.cjs +0 -46
  274. package/bin/native-large-file-io.cjs +0 -42
  275. package/bin/native-runtime-cache.cjs +0 -76
  276. package/bin/natural-presence-policy.cjs +0 -28
  277. package/bin/noninteractive-shell-env-policy.cjs +0 -19
  278. package/bin/observer-hooks.cjs +0 -14
  279. package/bin/outbound-claim-provenance.cjs +0 -150
  280. package/bin/oversized-context-offload-policy.cjs +0 -86
  281. package/bin/pending-media-policy.cjs +0 -182
  282. package/bin/pending-token-estimate-policy.cjs +0 -41
  283. package/bin/personal-memory-consent-policy.cjs +0 -72
  284. package/bin/personal-memory-performance-policy.cjs +0 -12
  285. package/bin/personality-choice-policy.cjs +0 -101
  286. package/bin/personality-memory-adapter.cjs +0 -379
  287. package/bin/personality-mode.cjs +0 -46
  288. package/bin/personality-setup-policy.cjs +0 -197
  289. package/bin/proactive-compaction-policy.cjs +0 -25
  290. package/bin/profile-identity-resolution.cjs +0 -136
  291. package/bin/profile-runtime.cjs +0 -318
  292. package/bin/profile-tool-exclusion-policy.cjs +0 -37
  293. package/bin/programmatic-context-isolation.cjs +0 -25
  294. package/bin/programmatic-tool-runtime.mjs +0 -627
  295. package/bin/provider-idle-timeout-policy.cjs +0 -14
  296. package/bin/provider-model-refresh-deadline.cjs +0 -53
  297. package/bin/provider-model-refresh-policy.cjs +0 -107
  298. package/bin/rate-limit-recovery-policy.cjs +0 -47
  299. package/bin/read-batch-policy.cjs +0 -32
  300. package/bin/read-continuation-policy.cjs +0 -59
  301. package/bin/recurring-cron-history-policy.cjs +0 -124
  302. package/bin/relationship-continuity-policy.cjs +0 -143
  303. package/bin/relationship-curiosity-policy.cjs +0 -107
  304. package/bin/relationship-learning-policy.cjs +0 -168
  305. package/bin/release-artifact-freeze-policy.cjs +0 -30
  306. package/bin/reload-plugin-bootstrap.cjs +0 -18
  307. package/bin/reload-queue-policy.cjs +0 -38
  308. package/bin/repeated-assistant-response-policy.cjs +0 -232
  309. package/bin/repeated-injection-projection.cjs +0 -107
  310. package/bin/repeated-user-message-projection.cjs +0 -8
  311. package/bin/research-page-result.cjs +0 -74
  312. package/bin/retry-checkpoint-policy.cjs +0 -13
  313. package/bin/runtime-exit-ledger.cjs +0 -144
  314. package/bin/scoped-cron-run-policy.cjs +0 -358
  315. package/bin/session-checkpoint-policy.cjs +0 -25
  316. package/bin/session-compaction-policy.cjs +0 -84
  317. package/bin/session-replay-policy.cjs +0 -20
  318. package/bin/session-replay-window-policy.cjs +0 -40
  319. package/bin/session-resume-checkpoint.cjs +0 -254
  320. package/bin/session-scrollback-archive.cjs +0 -229
  321. package/bin/skill-activation-performance-policy.cjs +0 -69
  322. package/bin/skill-listing-performance-policy.cjs +0 -92
  323. package/bin/soul-organization-policy.cjs +0 -78
  324. package/bin/soul-preservation-policy.cjs +0 -20
  325. package/bin/startup-preferences.cjs +0 -131
  326. package/bin/streaming-flush-performance-policy.cjs +0 -28
  327. package/bin/structured-agent-swarm-output.cjs +0 -325
  328. package/bin/structured-subagent-output.cjs +0 -252
  329. package/bin/subagent-context-fork-policy.cjs +0 -155
  330. package/bin/subagent-max-tokens-handoff-policy.cjs +0 -69
  331. package/bin/subagent-parent-responsiveness.cjs +0 -19
  332. package/bin/subagent-skill-policy.cjs +0 -206
  333. package/bin/subagent-timeout-policy.cjs +0 -182
  334. package/bin/subagent-tool-policy.cjs +0 -60
  335. package/bin/subagent-usage-rollup-policy.cjs +0 -29
  336. package/bin/system-prompt-context-policy.cjs +0 -124
  337. package/bin/system-prompt-token-cache-policy.cjs +0 -60
  338. package/bin/telegram-addressed-focus.cjs +0 -55
  339. package/bin/telegram-addressed-priority.cjs +0 -12
  340. package/bin/telegram-approval-relay.cjs +0 -290
  341. package/bin/telegram-bot-priority.cjs +0 -17
  342. package/bin/telegram-console-status-policy.cjs +0 -174
  343. package/bin/telegram-context-projection-policy.cjs +0 -141
  344. package/bin/telegram-delivery-lifecycle.cjs +0 -125
  345. package/bin/telegram-direct-focus-policy.cjs +0 -273
  346. package/bin/telegram-mcp-compatibility.cjs +0 -49
  347. package/bin/telegram-media-delivery-policy.cjs +0 -42
  348. package/bin/telegram-private-conversation-policy.cjs +0 -185
  349. package/bin/telegram-queue-handoff-policy.cjs +0 -73
  350. package/bin/telegram-remote-status-policy.cjs +0 -120
  351. package/bin/telegram-session-queue-runtime.mjs +0 -306
  352. package/bin/telegram-text-chunk-policy.cjs +0 -63
  353. package/bin/telegram-truncated-reply-policy.cjs +0 -37
  354. package/bin/telegram-urgent-policy.cjs +0 -45
  355. package/bin/telemetry-spool-policy.cjs +0 -57
  356. package/bin/thinking-activity-status-policy.cjs +0 -132
  357. package/bin/thinking-only-guard.cjs +0 -80
  358. package/bin/todo-list-turn-policy.cjs +0 -131
  359. package/bin/tool-call-loop-policy.cjs +0 -51
  360. package/bin/tool-file-persistence.cjs +0 -141
  361. package/bin/tool-result-offload-policy.cjs +0 -359
  362. package/bin/tool-result-offload-telemetry.cjs +0 -12
  363. package/bin/tool-schema-token-cache-policy.cjs +0 -41
  364. package/bin/tool-stream-preview-policy.cjs +0 -9
  365. package/bin/tui-functional-contract.cjs +0 -55
  366. package/bin/turn-tool-performance-policy.cjs +0 -486
  367. package/bin/usage-cache-efficiency-policy.cjs +0 -26
  368. package/bin/user-home-path-policy.cjs +0 -13
  369. package/bin/user-message-offload-policy.cjs +0 -103
  370. package/bin/user-prompt-hook-origin-policy.cjs +0 -34
  371. package/bin/user-tool-record-policy.cjs +0 -7
  372. package/bin/validated-learning-insight-policy.cjs +0 -58
  373. package/bin/validated-learning-outcome-trace.cjs +0 -107
  374. package/bin/validated-learning-performance-policy.cjs +0 -53
  375. package/bin/validated-learning-signal.cjs +0 -463
  376. package/bin/windows-bash-dialect-policy.cjs +0 -25
  377. package/bin/windows-node-crash-dump.cjs +0 -110
  378. package/bin/write-continuation-policy.cjs +0 -69
  379. package/codebase-index/README.md +0 -82
  380. package/codebase-index/codebase_index.py +0 -470
  381. package/standard-skills/agent-browser/SKILL.md +0 -19
  382. package/standard-skills/agent-browser/references/runtime.md +0 -8
  383. package/standard-skills/blun-session-inspector/SKILL.md +0 -41
  384. package/standard-skills/blun-session-inspector/scripts/inspect-session.cjs +0 -437
  385. package/standard-skills/design-taste-frontend/SKILL.md +0 -1206
  386. package/standard-skills/full-output-enforcement/SKILL.md +0 -49
  387. package/standard-skills/high-end-visual-design/SKILL.md +0 -98
  388. package/standard-skills/image-to-code/SKILL.md +0 -1228
  389. package/standard-skills/industrial-brutalist-ui/SKILL.md +0 -92
  390. package/standard-skills/minimalist-ui/SKILL.md +0 -85
  391. package/standard-skills/motion-design-taste/SKILL.md +0 -74
  392. package/standard-skills/playwright-testing/SKILL.md +0 -19
  393. package/standard-skills/playwright-testing/references/runtime.md +0 -7
  394. package/standard-skills/premortem/SKILL.md +0 -148
  395. package/standard-skills/redesign-existing-projects/SKILL.md +0 -178
  396. package/standard-skills/research-evidence/SKILL.md +0 -39
  397. package/standard-skills/research-evidence/references/evidence-format.md +0 -104
  398. package/standard-skills/research-evidence/scripts/evidence-collection.cjs +0 -260
  399. package/standard-skills/research-evidence/scripts/score-report.cjs +0 -130
  400. package/standard-skills/screenshot-lesen/SKILL.md +0 -52
  401. package/standard-skills/stitch-design-taste/DESIGN.md +0 -121
  402. package/standard-skills/stitch-design-taste/SKILL.md +0 -184
  403. package/standard-skills/telegram-channel/SKILL.md +0 -18
  404. package/standard-skills/telegram-channel/references/runtime.md +0 -7
  405. package/standard-skills/venture-flywheel/SKILL.md +0 -32
  406. package/standard-skills/venture-flywheel/identity/project-identity.cjs +0 -146
  407. package/standard-skills/venture-flywheel/policy/capability-engine.cjs +0 -114
  408. package/standard-skills/venture-flywheel/policy/repository-trust.cjs +0 -229
  409. package/standard-skills/venture-flywheel/references/BEISPIELE-phase0.md +0 -146
  410. package/standard-skills/venture-flywheel/references/CAPABILITY-MAP.md +0 -34
  411. package/standard-skills/venture-flywheel/references/SPEC-phase0-identity-trust.md +0 -77
  412. package/standard-skills/venture-flywheel/references/SPEC-phase0-state-events.md +0 -93
  413. package/standard-skills/venture-flywheel/schemas/capability-decision.schema.json +0 -13
  414. package/standard-skills/venture-flywheel/schemas/execution-event.schema.json +0 -44
  415. package/standard-skills/venture-flywheel/schemas/project-identity.schema.json +0 -32
  416. package/standard-skills/venture-flywheel/schemas/repository-trust.schema.json +0 -57
  417. package/standard-skills/venture-flywheel/schemas/run-transition.schema.json +0 -59
  418. package/standard-skills/venture-flywheel/state/execution-event.cjs +0 -191
  419. package/standard-skills/venture-flywheel/state/task-state-machine.cjs +0 -190
  420. package/standard-skills/web-lesen/SKILL.md +0 -73
  421. package/standard-skills/web-lesen/scripts/crawl_public.py +0 -379
  422. package/standard-skills/windows-mcp/SKILL.md +0 -19
  423. package/standard-skills/windows-mcp/references/runtime.md +0 -9
  424. package/telegram-plugin/DELIVERY.md +0 -36
  425. package/telegram-plugin/bin/telegram-approval-relay.cjs +0 -290
  426. package/telegram-plugin/bin/telegram-console-status-policy.cjs +0 -175
  427. package/telegram-plugin/bin/telegram-delivery-lifecycle.cjs +0 -125
  428. package/telegram-plugin/bin/telegram-direct-reply-policy.cjs +0 -48
  429. package/telegram-plugin/bin/telegram-launcher-status-queue.cjs +0 -122
  430. package/telegram-plugin/bin/telegram-private-conversation-policy.cjs +0 -186
  431. package/telegram-plugin/bin/telegram-remote-status-policy.cjs +0 -121
  432. package/telegram-plugin/bin/telegram-reply-parts.cjs +0 -149
  433. package/telegram-plugin/bin/telegram-text-chunk-policy.cjs +0 -63
  434. package/telegram-plugin/bin/telegram-typing-keepalive.cjs +0 -89
  435. package/telegram-plugin/compat/mcp-server-fa511cd1.mjs +0 -73825
  436. /package/{bin → scripts}/fix-node-pty-perms.js +0 -0
@@ -0,0 +1,1073 @@
1
+ #!/usr/bin/env python3
2
+ """Durable SQLite host callbacks for the fail-closed CMS receiver.
3
+
4
+ The trusted host registers its current source and tombstone expectations. The
5
+ receiver invokes this store only after transport and signature verification.
6
+ Every callback rechecks the current binding inside one SQLite write transaction
7
+ so a concurrent source change cannot publish or delete the wrong generation.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import hashlib
13
+ import json
14
+ import re
15
+ import sqlite3
16
+ import time
17
+ import unicodedata
18
+ from contextlib import contextmanager
19
+ from typing import Any, Callable, Iterator, Mapping
20
+
21
+
22
+ SCHEMA_VERSION = 1
23
+ MAX_JSON_BYTES = 4_000_000
24
+ TOKEN = re.compile(r"^[A-Za-z0-9_.:-]{1,256}$")
25
+ SHA256 = re.compile(r"^[0-9a-f]{64}$")
26
+ CONTENT_TYPES = {
27
+ "headline", "cta", "marketing", "ui", "documentation", "seo", "legal",
28
+ "commercial",
29
+ }
30
+ PUBLICATION_EXPECTATION_FIELDS = (
31
+ "event_id", "site_id", "website_version", "plan_id", "source_id",
32
+ "source_revision", "source_sequence", "source_sha256", "required_locales",
33
+ "content_type", "commercial_profile",
34
+ )
35
+ TOMBSTONE_EXPECTATION_FIELDS = (
36
+ "tombstone_id", "event_id", "site_id", "website_version", "plan_id",
37
+ "source_id", "source_sequence", "publication_delivery_id",
38
+ "publication_payload_sha256", "locales",
39
+ )
40
+
41
+
42
+ class CMSReceiverStoreBlocked(RuntimeError):
43
+ """A private durable-host failure; the HTTP receiver redacts its detail."""
44
+
45
+
46
+ def _canonical_json(value: Any) -> str:
47
+ try:
48
+ encoded = json.dumps(
49
+ value,
50
+ ensure_ascii=False,
51
+ allow_nan=False,
52
+ sort_keys=True,
53
+ separators=(",", ":"),
54
+ )
55
+ except (TypeError, ValueError, RecursionError) as error:
56
+ raise CMSReceiverStoreBlocked("store JSON is invalid") from error
57
+ raw = encoded.encode("utf-8")
58
+ if not raw or len(raw) > MAX_JSON_BYTES:
59
+ raise CMSReceiverStoreBlocked("store JSON is outside the supported size")
60
+ return encoded
61
+
62
+
63
+ def _decode_json(value: Any, *, field: str) -> Any:
64
+ if not isinstance(value, str) or len(value.encode("utf-8")) > MAX_JSON_BYTES:
65
+ raise CMSReceiverStoreBlocked(f"{field} is invalid")
66
+ try:
67
+ decoded = json.loads(value)
68
+ except (json.JSONDecodeError, RecursionError) as error:
69
+ raise CMSReceiverStoreBlocked(f"{field} is invalid") from error
70
+ if _canonical_json(decoded) != value:
71
+ raise CMSReceiverStoreBlocked(f"{field} is not canonical")
72
+ return decoded
73
+
74
+
75
+ def _mapping(value: Any, fields: tuple[str, ...], *, name: str) -> dict[str, Any]:
76
+ if isinstance(value, Mapping):
77
+ result = dict(value)
78
+ if set(result) != set(fields):
79
+ raise CMSReceiverStoreBlocked(f"{name} fields are invalid")
80
+ return result
81
+ missing = object()
82
+ result = {field: getattr(value, field, missing) for field in fields}
83
+ if any(item is missing for item in result.values()):
84
+ raise CMSReceiverStoreBlocked(f"{name} is invalid")
85
+ return result
86
+
87
+
88
+ def _token(value: Any, *, field: str) -> str:
89
+ if (
90
+ not isinstance(value, str)
91
+ or TOKEN.fullmatch(value) is None
92
+ or not unicodedata.is_normalized("NFC", value)
93
+ ):
94
+ raise CMSReceiverStoreBlocked(f"{field} is invalid")
95
+ return value
96
+
97
+
98
+ def _sha256(value: Any, *, field: str) -> str:
99
+ if not isinstance(value, str) or SHA256.fullmatch(value) is None:
100
+ raise CMSReceiverStoreBlocked(f"{field} is invalid")
101
+ return value
102
+
103
+
104
+ def _sequence(value: Any) -> int:
105
+ if isinstance(value, bool) or not isinstance(value, int) or value <= 0:
106
+ raise CMSReceiverStoreBlocked("source_sequence is invalid")
107
+ return value
108
+
109
+
110
+ def _locales(value: Any, *, field: str) -> tuple[str, ...]:
111
+ if isinstance(value, list):
112
+ value = tuple(value)
113
+ if (
114
+ not isinstance(value, tuple)
115
+ or not value
116
+ or value != tuple(sorted(set(value)))
117
+ or any(_token(locale, field=field) != locale for locale in value)
118
+ ):
119
+ raise CMSReceiverStoreBlocked(f"{field} is invalid")
120
+ return value
121
+
122
+
123
+ def _timestamp(value: Any) -> float:
124
+ if isinstance(value, bool) or not isinstance(value, (int, float)):
125
+ raise CMSReceiverStoreBlocked("clock returned an invalid timestamp")
126
+ result = float(value)
127
+ if result < 0 or result != result or result in {float("inf"), float("-inf")}:
128
+ raise CMSReceiverStoreBlocked("clock returned an invalid timestamp")
129
+ return result
130
+
131
+
132
+ def _binding_sha256(value: Mapping[str, Any]) -> str:
133
+ return hashlib.sha256(_canonical_json(value).encode("utf-8")).hexdigest()
134
+
135
+
136
+ def _publication_expectation(value: Any) -> dict[str, Any]:
137
+ result = _mapping(
138
+ value, PUBLICATION_EXPECTATION_FIELDS, name="publication expectation",
139
+ )
140
+ for field in (
141
+ "event_id", "site_id", "website_version", "plan_id", "source_id",
142
+ "source_revision",
143
+ ):
144
+ result[field] = _token(result[field], field=field)
145
+ result["source_sequence"] = _sequence(result["source_sequence"])
146
+ result["source_sha256"] = _sha256(
147
+ result["source_sha256"], field="source_sha256",
148
+ )
149
+ result["required_locales"] = _locales(
150
+ result["required_locales"], field="required_locales",
151
+ )
152
+ result["content_type"] = _token(result["content_type"], field="content_type")
153
+ if result["content_type"] not in CONTENT_TYPES:
154
+ raise CMSReceiverStoreBlocked("content_type is invalid")
155
+ commercial = result["commercial_profile"]
156
+ if commercial is not None:
157
+ commercial = _token(commercial, field="commercial_profile")
158
+ if (result["content_type"] == "commercial") != (commercial is not None):
159
+ raise CMSReceiverStoreBlocked("commercial profile scope is invalid")
160
+ result["commercial_profile"] = commercial
161
+ return result
162
+
163
+
164
+ def _tombstone_expectation(value: Any) -> dict[str, Any]:
165
+ result = _mapping(
166
+ value, TOMBSTONE_EXPECTATION_FIELDS, name="tombstone expectation",
167
+ )
168
+ for field in (
169
+ "tombstone_id", "event_id", "site_id", "website_version", "plan_id",
170
+ "source_id", "publication_delivery_id",
171
+ ):
172
+ result[field] = _token(result[field], field=field)
173
+ result["source_sequence"] = _sequence(result["source_sequence"])
174
+ result["publication_payload_sha256"] = _sha256(
175
+ result["publication_payload_sha256"],
176
+ field="publication_payload_sha256",
177
+ )
178
+ result["locales"] = _locales(result["locales"], field="locales")
179
+ return result
180
+
181
+
182
+ def _verified(value: Any, *, kind: str) -> tuple[str, str, dict[str, Any], str]:
183
+ delivery_id = _token(getattr(value, "delivery_id", None), field="delivery_id")
184
+ payload_sha256 = _sha256(
185
+ getattr(value, "payload_sha256", None), field="payload_sha256",
186
+ )
187
+ payload = getattr(value, "payload", None)
188
+ if not isinstance(payload, dict):
189
+ raise CMSReceiverStoreBlocked(f"verified {kind} payload is invalid")
190
+ payload_json = _canonical_json(payload)
191
+ if hashlib.sha256(payload_json.encode("utf-8")).hexdigest() != payload_sha256:
192
+ raise CMSReceiverStoreBlocked(f"verified {kind} hash is invalid")
193
+ if payload.get("delivery_id") != delivery_id:
194
+ raise CMSReceiverStoreBlocked(f"verified {kind} delivery is invalid")
195
+ return delivery_id, payload_sha256, payload, payload_json
196
+
197
+
198
+ @contextmanager
199
+ def _transaction(connection: sqlite3.Connection) -> Iterator[None]:
200
+ if connection.in_transaction:
201
+ raise CMSReceiverStoreBlocked("store cannot join an external transaction")
202
+ try:
203
+ connection.execute("BEGIN IMMEDIATE")
204
+ yield
205
+ except Exception:
206
+ connection.rollback()
207
+ raise
208
+ else:
209
+ connection.commit()
210
+
211
+
212
+ class DurableCMSReceiverStore:
213
+ """Dedicated crash-safe store implementing every receiver host callback."""
214
+
215
+ def __init__(
216
+ self,
217
+ connection: sqlite3.Connection,
218
+ *,
219
+ clock: Callable[[], float | int] = time.time,
220
+ ):
221
+ if not isinstance(connection, sqlite3.Connection) or connection.in_transaction:
222
+ raise CMSReceiverStoreBlocked("connection must be an idle SQLite connection")
223
+ if not callable(clock):
224
+ raise CMSReceiverStoreBlocked("clock must be callable")
225
+ self.connection = connection
226
+ self.clock = clock
227
+ self.connection.row_factory = sqlite3.Row
228
+ self.connection.execute("PRAGMA foreign_keys = ON")
229
+ self.connection.execute("PRAGMA busy_timeout = 5000")
230
+ self.connection.execute("PRAGMA secure_delete = ON")
231
+ if int(self.connection.execute("PRAGMA secure_delete").fetchone()[0]) != 1:
232
+ raise CMSReceiverStoreBlocked("secure deletion is unavailable")
233
+ version = int(self.connection.execute("PRAGMA user_version").fetchone()[0])
234
+ if version not in {0, SCHEMA_VERSION}:
235
+ raise CMSReceiverStoreBlocked("unsupported CMS receiver store schema")
236
+ if version == 0:
237
+ self._create_schema()
238
+ self._verify_schema()
239
+
240
+ def _create_schema(self) -> None:
241
+ with _transaction(self.connection):
242
+ version = int(
243
+ self.connection.execute("PRAGMA user_version").fetchone()[0]
244
+ )
245
+ if version == SCHEMA_VERSION:
246
+ return
247
+ if version != 0:
248
+ raise CMSReceiverStoreBlocked("CMS receiver store schema changed")
249
+ self.connection.execute("""
250
+ CREATE TABLE cms_receiver_sources (
251
+ site_id TEXT NOT NULL,
252
+ source_id TEXT NOT NULL,
253
+ event_id TEXT NOT NULL UNIQUE,
254
+ website_version TEXT NOT NULL,
255
+ plan_id TEXT NOT NULL,
256
+ source_revision TEXT NOT NULL,
257
+ source_sequence INTEGER NOT NULL CHECK (source_sequence > 0),
258
+ source_sha256 TEXT NOT NULL,
259
+ required_locales_json TEXT NOT NULL,
260
+ content_type TEXT NOT NULL,
261
+ commercial_profile TEXT,
262
+ expectation_sha256 TEXT NOT NULL,
263
+ active_delivery_id TEXT,
264
+ created_at REAL NOT NULL,
265
+ updated_at REAL NOT NULL,
266
+ PRIMARY KEY (site_id, source_id)
267
+ )
268
+ """)
269
+ self.connection.execute("""
270
+ CREATE TABLE cms_receiver_publications (
271
+ delivery_id TEXT PRIMARY KEY,
272
+ payload_sha256 TEXT NOT NULL UNIQUE,
273
+ site_id TEXT NOT NULL,
274
+ source_id TEXT NOT NULL,
275
+ source_sequence INTEGER NOT NULL CHECK (source_sequence > 0),
276
+ source_revision TEXT NOT NULL,
277
+ payload_json TEXT,
278
+ status TEXT NOT NULL CHECK (
279
+ status IN ('active', 'superseded', 'deleted')
280
+ ),
281
+ tombstone_delivery_id TEXT,
282
+ tombstone_payload_sha256 TEXT,
283
+ created_at REAL NOT NULL,
284
+ updated_at REAL NOT NULL,
285
+ UNIQUE (site_id, source_id, source_sequence)
286
+ )
287
+ """)
288
+ self.connection.execute("""
289
+ CREATE TABLE cms_receiver_localizations (
290
+ delivery_id TEXT NOT NULL,
291
+ locale TEXT NOT NULL,
292
+ target_text TEXT NOT NULL,
293
+ target_sha256 TEXT NOT NULL,
294
+ approval_id TEXT NOT NULL,
295
+ approval_expires_at REAL NOT NULL,
296
+ release_evidence_json TEXT NOT NULL,
297
+ PRIMARY KEY (delivery_id, locale),
298
+ FOREIGN KEY (delivery_id)
299
+ REFERENCES cms_receiver_publications (delivery_id)
300
+ ON DELETE CASCADE
301
+ )
302
+ """)
303
+ self.connection.execute("""
304
+ CREATE TABLE cms_receiver_tombstones (
305
+ tombstone_id TEXT PRIMARY KEY,
306
+ event_id TEXT NOT NULL,
307
+ site_id TEXT NOT NULL,
308
+ website_version TEXT NOT NULL,
309
+ plan_id TEXT NOT NULL,
310
+ source_id TEXT NOT NULL,
311
+ source_sequence INTEGER NOT NULL CHECK (source_sequence > 0),
312
+ publication_delivery_id TEXT NOT NULL UNIQUE,
313
+ publication_payload_sha256 TEXT NOT NULL,
314
+ locales_json TEXT NOT NULL,
315
+ expectation_sha256 TEXT NOT NULL,
316
+ status TEXT NOT NULL CHECK (status IN ('pending', 'deleted')),
317
+ delivery_id TEXT UNIQUE,
318
+ payload_sha256 TEXT UNIQUE,
319
+ created_at REAL NOT NULL,
320
+ updated_at REAL NOT NULL
321
+ )
322
+ """)
323
+ self.connection.execute(
324
+ "CREATE INDEX cms_receiver_publications_source "
325
+ "ON cms_receiver_publications (site_id, source_id, status)"
326
+ )
327
+ self.connection.execute(f"PRAGMA user_version = {SCHEMA_VERSION}")
328
+
329
+ def _verify_schema(self) -> None:
330
+ if int(self.connection.execute("PRAGMA secure_delete").fetchone()[0]) != 1:
331
+ raise CMSReceiverStoreBlocked("secure deletion was disabled")
332
+ expected = {
333
+ "cms_receiver_sources": (
334
+ "site_id", "source_id", "event_id", "website_version", "plan_id",
335
+ "source_revision", "source_sequence", "source_sha256",
336
+ "required_locales_json", "content_type", "commercial_profile",
337
+ "expectation_sha256", "active_delivery_id", "created_at",
338
+ "updated_at",
339
+ ),
340
+ "cms_receiver_publications": (
341
+ "delivery_id", "payload_sha256", "site_id", "source_id",
342
+ "source_sequence", "source_revision", "payload_json", "status",
343
+ "tombstone_delivery_id", "tombstone_payload_sha256", "created_at",
344
+ "updated_at",
345
+ ),
346
+ "cms_receiver_localizations": (
347
+ "delivery_id", "locale", "target_text", "target_sha256",
348
+ "approval_id", "approval_expires_at", "release_evidence_json",
349
+ ),
350
+ "cms_receiver_tombstones": (
351
+ "tombstone_id", "event_id", "site_id", "website_version",
352
+ "plan_id", "source_id", "source_sequence",
353
+ "publication_delivery_id", "publication_payload_sha256",
354
+ "locales_json", "expectation_sha256", "status", "delivery_id",
355
+ "payload_sha256", "created_at", "updated_at",
356
+ ),
357
+ }
358
+ version = int(self.connection.execute("PRAGMA user_version").fetchone()[0])
359
+ if version != SCHEMA_VERSION:
360
+ raise CMSReceiverStoreBlocked("CMS receiver store schema version is invalid")
361
+ for table, columns in expected.items():
362
+ actual = tuple(
363
+ row[1] for row in self.connection.execute(
364
+ f"PRAGMA table_info({table})"
365
+ ).fetchall()
366
+ )
367
+ if actual != columns:
368
+ raise CMSReceiverStoreBlocked(f"CMS receiver store table {table} drifted")
369
+
370
+ @staticmethod
371
+ def _source_from_row(row: sqlite3.Row) -> dict[str, Any]:
372
+ value = {
373
+ "event_id": row["event_id"],
374
+ "site_id": row["site_id"],
375
+ "website_version": row["website_version"],
376
+ "plan_id": row["plan_id"],
377
+ "source_id": row["source_id"],
378
+ "source_revision": row["source_revision"],
379
+ "source_sequence": row["source_sequence"],
380
+ "source_sha256": row["source_sha256"],
381
+ "required_locales": tuple(_decode_json(
382
+ row["required_locales_json"], field="required_locales_json",
383
+ )),
384
+ "content_type": row["content_type"],
385
+ "commercial_profile": row["commercial_profile"],
386
+ }
387
+ result = _publication_expectation(value)
388
+ if _binding_sha256(result) != row["expectation_sha256"]:
389
+ raise CMSReceiverStoreBlocked("source expectation hash is invalid")
390
+ return result
391
+
392
+ @staticmethod
393
+ def _tombstone_from_row(row: sqlite3.Row) -> dict[str, Any]:
394
+ value = {
395
+ "tombstone_id": row["tombstone_id"],
396
+ "event_id": row["event_id"],
397
+ "site_id": row["site_id"],
398
+ "website_version": row["website_version"],
399
+ "plan_id": row["plan_id"],
400
+ "source_id": row["source_id"],
401
+ "source_sequence": row["source_sequence"],
402
+ "publication_delivery_id": row["publication_delivery_id"],
403
+ "publication_payload_sha256": row["publication_payload_sha256"],
404
+ "locales": tuple(_decode_json(row["locales_json"], field="locales_json")),
405
+ }
406
+ result = _tombstone_expectation(value)
407
+ if _binding_sha256(result) != row["expectation_sha256"]:
408
+ raise CMSReceiverStoreBlocked("tombstone expectation hash is invalid")
409
+ return result
410
+
411
+ def _validate_publication_row(
412
+ self, row: sqlite3.Row,
413
+ ) -> dict[str, Any] | None:
414
+ delivery_id = _token(row["delivery_id"], field="delivery_id")
415
+ payload_sha256 = _sha256(row["payload_sha256"], field="payload_sha256")
416
+ _token(row["site_id"], field="site_id")
417
+ _token(row["source_id"], field="source_id")
418
+ _sequence(row["source_sequence"])
419
+ _token(row["source_revision"], field="source_revision")
420
+ localizations = self.connection.execute("""
421
+ SELECT locale, target_text, target_sha256, approval_id,
422
+ approval_expires_at, release_evidence_json
423
+ FROM cms_receiver_localizations
424
+ WHERE delivery_id = ? ORDER BY locale
425
+ """, (delivery_id,)).fetchall()
426
+ if row["status"] in {"deleted", "superseded"}:
427
+ if (
428
+ row["payload_json"] is not None
429
+ or localizations
430
+ ):
431
+ raise CMSReceiverStoreBlocked(
432
+ "inactive publication retained localized content"
433
+ )
434
+ if row["status"] == "superseded":
435
+ if (
436
+ row["tombstone_delivery_id"] is not None
437
+ or row["tombstone_payload_sha256"] is not None
438
+ ):
439
+ raise CMSReceiverStoreBlocked(
440
+ "superseded publication is invalid"
441
+ )
442
+ return None
443
+ if (
444
+ _token(
445
+ row["tombstone_delivery_id"], field="tombstone_delivery_id",
446
+ ) != row["tombstone_delivery_id"]
447
+ or _sha256(
448
+ row["tombstone_payload_sha256"],
449
+ field="tombstone_payload_sha256",
450
+ ) != row["tombstone_payload_sha256"]
451
+ ):
452
+ raise CMSReceiverStoreBlocked("deleted publication is invalid")
453
+ return None
454
+ if (
455
+ row["status"] != "active"
456
+ or row["tombstone_delivery_id"] is not None
457
+ or row["tombstone_payload_sha256"] is not None
458
+ ):
459
+ raise CMSReceiverStoreBlocked("publication state is invalid")
460
+ payload = _decode_json(row["payload_json"], field="payload_json")
461
+ if (
462
+ not isinstance(payload, dict)
463
+ or payload.get("delivery_id") != delivery_id
464
+ or payload.get("site_id") != row["site_id"]
465
+ or payload.get("source_id") != row["source_id"]
466
+ or payload.get("source_sequence") != row["source_sequence"]
467
+ or payload.get("source_revision") != row["source_revision"]
468
+ or hashlib.sha256(_canonical_json(payload).encode("utf-8")).hexdigest()
469
+ != payload_sha256
470
+ ):
471
+ raise CMSReceiverStoreBlocked("stored publication binding is invalid")
472
+ expected = payload.get("localizations")
473
+ if not isinstance(expected, list) or not all(
474
+ isinstance(item, dict) for item in expected
475
+ ):
476
+ raise CMSReceiverStoreBlocked("stored locale bundle is incomplete")
477
+ locales = [item.get("locale") for item in expected]
478
+ if any(not isinstance(locale, str) for locale in locales):
479
+ raise CMSReceiverStoreBlocked("stored locale bundle is incomplete")
480
+ if len(localizations) != len(expected) or locales != sorted(set(locales)):
481
+ raise CMSReceiverStoreBlocked("stored locale bundle is incomplete")
482
+ for stored, item in zip(localizations, expected):
483
+ if not isinstance(item, dict):
484
+ raise CMSReceiverStoreBlocked("stored locale is invalid")
485
+ target_text = stored["target_text"]
486
+ evidence = _decode_json(
487
+ stored["release_evidence_json"], field="release_evidence_json",
488
+ )
489
+ if (
490
+ stored["locale"] != item.get("locale")
491
+ or target_text != item.get("target_text")
492
+ or stored["target_sha256"] != item.get("target_sha256")
493
+ or stored["approval_id"] != item.get("approval_id")
494
+ or stored["approval_expires_at"] != item.get("approval_expires_at")
495
+ or evidence != item.get("release_evidence")
496
+ or not isinstance(target_text, str)
497
+ or hashlib.sha256(target_text.encode("utf-8")).hexdigest()
498
+ != stored["target_sha256"]
499
+ ):
500
+ raise CMSReceiverStoreBlocked("stored locale binding is invalid")
501
+ return payload
502
+
503
+ @staticmethod
504
+ def _assert_publication_binding(
505
+ payload: Mapping[str, Any], expectation: Mapping[str, Any],
506
+ ) -> None:
507
+ for field in (
508
+ "event_id", "site_id", "website_version", "plan_id", "source_id",
509
+ "source_revision", "source_sequence", "source_sha256",
510
+ ):
511
+ if payload.get(field) != expectation[field]:
512
+ raise CMSReceiverStoreBlocked("publication is no longer current")
513
+ localizations = payload.get("localizations")
514
+ if not isinstance(localizations, list):
515
+ raise CMSReceiverStoreBlocked("publication localizations are invalid")
516
+ locales = tuple(item.get("locale") for item in localizations if isinstance(item, dict))
517
+ if locales != expectation["required_locales"] or len(locales) != len(localizations):
518
+ raise CMSReceiverStoreBlocked("publication locale set is no longer current")
519
+ for item in localizations:
520
+ evidence = item.get("release_evidence")
521
+ if (
522
+ not isinstance(evidence, dict)
523
+ or evidence.get("content_type") != expectation["content_type"]
524
+ or evidence.get("commercial_profile")
525
+ != expectation["commercial_profile"]
526
+ ):
527
+ raise CMSReceiverStoreBlocked("publication scope is no longer current")
528
+
529
+ def register_source(self, expectation: Any) -> Mapping[str, Any]:
530
+ """Register or monotonically advance one trusted current source."""
531
+
532
+ value = _publication_expectation(expectation)
533
+ now = _timestamp(self.clock())
534
+ locales_json = _canonical_json(list(value["required_locales"]))
535
+ expectation_sha256 = _binding_sha256(value)
536
+ with _transaction(self.connection):
537
+ self._verify_schema()
538
+ row = self.connection.execute(
539
+ "SELECT * FROM cms_receiver_sources WHERE site_id = ? AND source_id = ?",
540
+ (value["site_id"], value["source_id"]),
541
+ ).fetchone()
542
+ if row is not None:
543
+ current = self._source_from_row(row)
544
+ if value["source_sequence"] < current["source_sequence"]:
545
+ raise CMSReceiverStoreBlocked("source sequence moved backwards")
546
+ if value["source_sequence"] == current["source_sequence"]:
547
+ if value != current:
548
+ raise CMSReceiverStoreBlocked("source sequence was reused")
549
+ return {
550
+ "site_id": value["site_id"],
551
+ "source_id": value["source_id"],
552
+ "source_sequence": value["source_sequence"],
553
+ "status": "current",
554
+ }
555
+ if row["active_delivery_id"] is not None and self.connection.execute(
556
+ "SELECT 1 FROM cms_receiver_tombstones "
557
+ "WHERE publication_delivery_id = ? AND status = 'pending'",
558
+ (row["active_delivery_id"],),
559
+ ).fetchone() is not None:
560
+ raise CMSReceiverStoreBlocked(
561
+ "source cannot advance during a pending tombstone"
562
+ )
563
+ try:
564
+ self.connection.execute("""
565
+ UPDATE cms_receiver_sources
566
+ SET event_id = ?, website_version = ?, plan_id = ?,
567
+ source_revision = ?, source_sequence = ?, source_sha256 = ?,
568
+ required_locales_json = ?, content_type = ?,
569
+ commercial_profile = ?, expectation_sha256 = ?,
570
+ updated_at = ?
571
+ WHERE site_id = ? AND source_id = ?
572
+ """, (
573
+ value["event_id"], value["website_version"], value["plan_id"],
574
+ value["source_revision"], value["source_sequence"],
575
+ value["source_sha256"], locales_json, value["content_type"],
576
+ value["commercial_profile"], expectation_sha256, now,
577
+ value["site_id"], value["source_id"],
578
+ ))
579
+ except sqlite3.IntegrityError as error:
580
+ raise CMSReceiverStoreBlocked("source identity collided") from error
581
+ else:
582
+ try:
583
+ self.connection.execute("""
584
+ INSERT INTO cms_receiver_sources (
585
+ site_id, source_id, event_id, website_version, plan_id,
586
+ source_revision, source_sequence, source_sha256,
587
+ required_locales_json, content_type, commercial_profile,
588
+ expectation_sha256, active_delivery_id, created_at,
589
+ updated_at
590
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?)
591
+ """, (
592
+ value["site_id"], value["source_id"], value["event_id"],
593
+ value["website_version"], value["plan_id"],
594
+ value["source_revision"], value["source_sequence"],
595
+ value["source_sha256"], locales_json, value["content_type"],
596
+ value["commercial_profile"], expectation_sha256, now, now,
597
+ ))
598
+ except sqlite3.IntegrityError as error:
599
+ raise CMSReceiverStoreBlocked("source identity collided") from error
600
+ return {
601
+ "site_id": value["site_id"],
602
+ "source_id": value["source_id"],
603
+ "source_sequence": value["source_sequence"],
604
+ "status": "current",
605
+ }
606
+
607
+ def resolve_publication_expectation(self, publication: Any) -> Mapping[str, Any]:
608
+ _, _, payload, _ = _verified(publication, kind="publication")
609
+ site_id = _token(payload.get("site_id"), field="site_id")
610
+ source_id = _token(payload.get("source_id"), field="source_id")
611
+ self._verify_schema()
612
+ row = self.connection.execute(
613
+ "SELECT * FROM cms_receiver_sources WHERE site_id = ? AND source_id = ?",
614
+ (site_id, source_id),
615
+ ).fetchone()
616
+ if row is None:
617
+ raise CMSReceiverStoreBlocked("source expectation is missing")
618
+ return self._source_from_row(row)
619
+
620
+ def commit(self, publication: Any) -> Mapping[str, Any]:
621
+ """Install a complete bundle, then atomically scrub its predecessor."""
622
+
623
+ delivery_id, payload_sha256, payload, payload_json = _verified(
624
+ publication, kind="publication",
625
+ )
626
+ now = _timestamp(self.clock())
627
+ with _transaction(self.connection):
628
+ self._verify_schema()
629
+ row = self.connection.execute(
630
+ "SELECT * FROM cms_receiver_sources WHERE site_id = ? AND source_id = ?",
631
+ (payload.get("site_id"), payload.get("source_id")),
632
+ ).fetchone()
633
+ if row is None:
634
+ raise CMSReceiverStoreBlocked("source expectation is missing")
635
+ current = self._source_from_row(row)
636
+ self._assert_publication_binding(payload, current)
637
+ existing = self.connection.execute(
638
+ "SELECT * FROM cms_receiver_publications WHERE delivery_id = ?",
639
+ (delivery_id,),
640
+ ).fetchone()
641
+ if existing is not None:
642
+ if (
643
+ existing["payload_sha256"] == payload_sha256
644
+ and existing["status"] == "active"
645
+ and row["active_delivery_id"] == delivery_id
646
+ and self._validate_publication_row(existing) == payload
647
+ ):
648
+ return {
649
+ "delivery_id": delivery_id,
650
+ "payload_sha256": payload_sha256,
651
+ "status": "committed",
652
+ }
653
+ raise CMSReceiverStoreBlocked("publication delivery collided")
654
+ if self.connection.execute(
655
+ "SELECT 1 FROM cms_receiver_publications "
656
+ "WHERE site_id = ? AND source_id = ? AND source_sequence = ?",
657
+ (current["site_id"], current["source_id"], current["source_sequence"]),
658
+ ).fetchone() is not None:
659
+ raise CMSReceiverStoreBlocked("publication generation collided")
660
+ old_delivery_id = row["active_delivery_id"]
661
+ if old_delivery_id is not None:
662
+ changed = self.connection.execute("""
663
+ UPDATE cms_receiver_publications
664
+ SET status = 'superseded', updated_at = ?
665
+ WHERE delivery_id = ? AND status = 'active'
666
+ """, (now, old_delivery_id)).rowcount
667
+ if changed != 1:
668
+ raise CMSReceiverStoreBlocked("active publication state is invalid")
669
+ try:
670
+ self.connection.execute("""
671
+ INSERT INTO cms_receiver_publications (
672
+ delivery_id, payload_sha256, site_id, source_id,
673
+ source_sequence, source_revision, payload_json, status,
674
+ tombstone_delivery_id, tombstone_payload_sha256,
675
+ created_at, updated_at
676
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, 'active', NULL, NULL, ?, ?)
677
+ """, (
678
+ delivery_id, payload_sha256, current["site_id"],
679
+ current["source_id"], current["source_sequence"],
680
+ current["source_revision"], payload_json, now, now,
681
+ ))
682
+ for item in payload["localizations"]:
683
+ self.connection.execute("""
684
+ INSERT INTO cms_receiver_localizations (
685
+ delivery_id, locale, target_text, target_sha256,
686
+ approval_id, approval_expires_at, release_evidence_json
687
+ ) VALUES (?, ?, ?, ?, ?, ?, ?)
688
+ """, (
689
+ delivery_id, item["locale"], item["target_text"],
690
+ item["target_sha256"], item["approval_id"],
691
+ item["approval_expires_at"],
692
+ _canonical_json(item["release_evidence"]),
693
+ ))
694
+ except (KeyError, TypeError, sqlite3.IntegrityError) as error:
695
+ raise CMSReceiverStoreBlocked("publication bundle is invalid") from error
696
+ changed = self.connection.execute("""
697
+ UPDATE cms_receiver_sources
698
+ SET active_delivery_id = ?, updated_at = ?
699
+ WHERE site_id = ? AND source_id = ? AND source_sequence = ?
700
+ AND source_revision = ? AND source_sha256 = ?
701
+ """, (
702
+ delivery_id, now, current["site_id"], current["source_id"],
703
+ current["source_sequence"], current["source_revision"],
704
+ current["source_sha256"],
705
+ )).rowcount
706
+ if changed != 1:
707
+ raise CMSReceiverStoreBlocked("source changed during publication")
708
+ if old_delivery_id is not None:
709
+ try:
710
+ self.connection.execute(
711
+ "DELETE FROM cms_receiver_localizations WHERE delivery_id = ?",
712
+ (old_delivery_id,),
713
+ )
714
+ changed = self.connection.execute("""
715
+ UPDATE cms_receiver_publications
716
+ SET payload_json = NULL, updated_at = ?
717
+ WHERE delivery_id = ? AND status = 'superseded'
718
+ """, (now, old_delivery_id)).rowcount
719
+ except sqlite3.Error as error:
720
+ raise CMSReceiverStoreBlocked(
721
+ "superseded publication cleanup failed"
722
+ ) from error
723
+ if changed != 1:
724
+ raise CMSReceiverStoreBlocked(
725
+ "superseded publication cleanup was not atomic"
726
+ )
727
+ return {
728
+ "delivery_id": delivery_id,
729
+ "payload_sha256": payload_sha256,
730
+ "status": "committed",
731
+ }
732
+
733
+ def read_active_bundle(self, expectation: Any) -> Mapping[str, Any] | None:
734
+ """Read only the active bundle for one exact trusted source binding."""
735
+
736
+ expected = _publication_expectation(expectation)
737
+ site_id = expected["site_id"]
738
+ source_id = expected["source_id"]
739
+ self._verify_schema()
740
+ source = self.connection.execute(
741
+ "SELECT * FROM cms_receiver_sources "
742
+ "WHERE site_id = ? AND source_id = ?",
743
+ (site_id, source_id),
744
+ ).fetchone()
745
+ if source is None:
746
+ return None
747
+ self._source_from_row(source)
748
+ if source["active_delivery_id"] is None:
749
+ return None
750
+ publication = self.connection.execute(
751
+ "SELECT * FROM cms_receiver_publications WHERE delivery_id = ?",
752
+ (source["active_delivery_id"],),
753
+ ).fetchone()
754
+ if (
755
+ publication is None
756
+ or publication["status"] != "active"
757
+ or publication["site_id"] != site_id
758
+ or publication["source_id"] != source_id
759
+ ):
760
+ raise CMSReceiverStoreBlocked("active publication is invalid")
761
+ payload = self._validate_publication_row(publication)
762
+ if payload is None:
763
+ raise CMSReceiverStoreBlocked("active publication was deleted")
764
+ self._assert_publication_binding(payload, expected)
765
+ return payload
766
+
767
+ def register_tombstone(self, expectation: Any) -> Mapping[str, Any]:
768
+ """Pre-authorize one exact deletion of the currently active publication."""
769
+
770
+ value = _tombstone_expectation(expectation)
771
+ now = _timestamp(self.clock())
772
+ locales_json = _canonical_json(list(value["locales"]))
773
+ expectation_sha256 = _binding_sha256(value)
774
+ with _transaction(self.connection):
775
+ self._verify_schema()
776
+ existing = self.connection.execute(
777
+ "SELECT * FROM cms_receiver_tombstones WHERE tombstone_id = ?",
778
+ (value["tombstone_id"],),
779
+ ).fetchone()
780
+ if existing is not None:
781
+ if self._tombstone_from_row(existing) != value:
782
+ raise CMSReceiverStoreBlocked("tombstone identity collided")
783
+ return {
784
+ "tombstone_id": value["tombstone_id"],
785
+ "publication_delivery_id": value["publication_delivery_id"],
786
+ "status": existing["status"],
787
+ }
788
+ publication = self.connection.execute(
789
+ "SELECT * FROM cms_receiver_publications WHERE delivery_id = ?",
790
+ (value["publication_delivery_id"],),
791
+ ).fetchone()
792
+ source = self.connection.execute(
793
+ "SELECT active_delivery_id FROM cms_receiver_sources "
794
+ "WHERE site_id = ? AND source_id = ?",
795
+ (value["site_id"], value["source_id"]),
796
+ ).fetchone()
797
+ if (
798
+ publication is None
799
+ or publication["status"] != "active"
800
+ or publication["payload_sha256"]
801
+ != value["publication_payload_sha256"]
802
+ or publication["site_id"] != value["site_id"]
803
+ or publication["source_id"] != value["source_id"]
804
+ or publication["source_sequence"] != value["source_sequence"]
805
+ or source is None
806
+ or source["active_delivery_id"] != value["publication_delivery_id"]
807
+ ):
808
+ raise CMSReceiverStoreBlocked("tombstone publication is not active")
809
+ payload = self._validate_publication_row(publication)
810
+ if (
811
+ not isinstance(payload, dict)
812
+ or payload.get("event_id") != value["event_id"]
813
+ or payload.get("website_version") != value["website_version"]
814
+ or payload.get("plan_id") != value["plan_id"]
815
+ or tuple(item.get("locale") for item in payload.get("localizations", []))
816
+ != value["locales"]
817
+ ):
818
+ raise CMSReceiverStoreBlocked("tombstone publication binding is invalid")
819
+ try:
820
+ self.connection.execute("""
821
+ INSERT INTO cms_receiver_tombstones (
822
+ tombstone_id, event_id, site_id, website_version, plan_id,
823
+ source_id, source_sequence, publication_delivery_id,
824
+ publication_payload_sha256, locales_json,
825
+ expectation_sha256, status, delivery_id, payload_sha256,
826
+ created_at, updated_at
827
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'pending', NULL, NULL, ?, ?)
828
+ """, (
829
+ value["tombstone_id"], value["event_id"], value["site_id"],
830
+ value["website_version"], value["plan_id"], value["source_id"],
831
+ value["source_sequence"], value["publication_delivery_id"],
832
+ value["publication_payload_sha256"], locales_json,
833
+ expectation_sha256, now, now,
834
+ ))
835
+ except sqlite3.IntegrityError as error:
836
+ raise CMSReceiverStoreBlocked("tombstone publication collided") from error
837
+ return {
838
+ "tombstone_id": value["tombstone_id"],
839
+ "publication_delivery_id": value["publication_delivery_id"],
840
+ "status": "pending",
841
+ }
842
+
843
+ def resolve_tombstone_expectation(self, tombstone: Any) -> Mapping[str, Any]:
844
+ _, _, payload, _ = _verified(tombstone, kind="tombstone")
845
+ tombstone_id = _token(payload.get("tombstone_id"), field="tombstone_id")
846
+ self._verify_schema()
847
+ row = self.connection.execute(
848
+ "SELECT * FROM cms_receiver_tombstones WHERE tombstone_id = ?",
849
+ (tombstone_id,),
850
+ ).fetchone()
851
+ if row is None:
852
+ raise CMSReceiverStoreBlocked("tombstone expectation is missing")
853
+ return self._tombstone_from_row(row)
854
+
855
+ def delete(self, tombstone: Any) -> Mapping[str, Any]:
856
+ """Atomically remove one exact active bundle and retain content-free evidence."""
857
+
858
+ delivery_id, payload_sha256, payload, _ = _verified(
859
+ tombstone, kind="tombstone",
860
+ )
861
+ now = _timestamp(self.clock())
862
+ with _transaction(self.connection):
863
+ self._verify_schema()
864
+ row = self.connection.execute(
865
+ "SELECT * FROM cms_receiver_tombstones WHERE tombstone_id = ?",
866
+ (payload.get("tombstone_id"),),
867
+ ).fetchone()
868
+ if row is None:
869
+ raise CMSReceiverStoreBlocked("tombstone expectation is missing")
870
+ expected = self._tombstone_from_row(row)
871
+ for field, value in expected.items():
872
+ actual = tuple(payload.get(field, ())) if field == "locales" else payload.get(field)
873
+ if actual != value:
874
+ raise CMSReceiverStoreBlocked("tombstone is no longer current")
875
+ if row["status"] == "deleted":
876
+ if row["delivery_id"] == delivery_id and row["payload_sha256"] == payload_sha256:
877
+ publication = self.connection.execute(
878
+ "SELECT * FROM cms_receiver_publications WHERE delivery_id = ?",
879
+ (expected["publication_delivery_id"],),
880
+ ).fetchone()
881
+ source = self.connection.execute(
882
+ "SELECT active_delivery_id FROM cms_receiver_sources "
883
+ "WHERE site_id = ? AND source_id = ?",
884
+ (expected["site_id"], expected["source_id"]),
885
+ ).fetchone()
886
+ if (
887
+ publication is None
888
+ or self._validate_publication_row(publication) is not None
889
+ or publication["status"] != "deleted"
890
+ or publication["payload_sha256"]
891
+ != expected["publication_payload_sha256"]
892
+ or publication["tombstone_delivery_id"] != delivery_id
893
+ or publication["tombstone_payload_sha256"] != payload_sha256
894
+ or source is None
895
+ or source["active_delivery_id"]
896
+ == expected["publication_delivery_id"]
897
+ ):
898
+ raise CMSReceiverStoreBlocked(
899
+ "deleted publication evidence is invalid"
900
+ )
901
+ return {
902
+ "delivery_id": delivery_id,
903
+ "payload_sha256": payload_sha256,
904
+ "status": "deleted",
905
+ }
906
+ raise CMSReceiverStoreBlocked("tombstone delivery collided")
907
+ publication = self.connection.execute(
908
+ "SELECT * FROM cms_receiver_publications WHERE delivery_id = ?",
909
+ (expected["publication_delivery_id"],),
910
+ ).fetchone()
911
+ source = self.connection.execute(
912
+ "SELECT active_delivery_id FROM cms_receiver_sources "
913
+ "WHERE site_id = ? AND source_id = ?",
914
+ (expected["site_id"], expected["source_id"]),
915
+ ).fetchone()
916
+ if (
917
+ publication is None
918
+ or publication["status"] != "active"
919
+ or publication["payload_sha256"]
920
+ != expected["publication_payload_sha256"]
921
+ or source is None
922
+ or source["active_delivery_id"]
923
+ != expected["publication_delivery_id"]
924
+ ):
925
+ raise CMSReceiverStoreBlocked("tombstone publication changed")
926
+ if self._validate_publication_row(publication) is None:
927
+ raise CMSReceiverStoreBlocked("tombstone publication was deleted")
928
+ self.connection.execute(
929
+ "DELETE FROM cms_receiver_localizations WHERE delivery_id = ?",
930
+ (expected["publication_delivery_id"],),
931
+ )
932
+ changed = self.connection.execute("""
933
+ UPDATE cms_receiver_publications
934
+ SET payload_json = NULL, status = 'deleted',
935
+ tombstone_delivery_id = ?, tombstone_payload_sha256 = ?,
936
+ updated_at = ?
937
+ WHERE delivery_id = ? AND status = 'active'
938
+ """, (
939
+ delivery_id, payload_sha256, now,
940
+ expected["publication_delivery_id"],
941
+ )).rowcount
942
+ if changed != 1:
943
+ raise CMSReceiverStoreBlocked("publication deletion was not atomic")
944
+ changed = self.connection.execute("""
945
+ UPDATE cms_receiver_sources
946
+ SET active_delivery_id = NULL, updated_at = ?
947
+ WHERE site_id = ? AND source_id = ? AND active_delivery_id = ?
948
+ """, (
949
+ now, expected["site_id"], expected["source_id"],
950
+ expected["publication_delivery_id"],
951
+ )).rowcount
952
+ if changed != 1:
953
+ raise CMSReceiverStoreBlocked("source deletion was not atomic")
954
+ changed = self.connection.execute("""
955
+ UPDATE cms_receiver_tombstones
956
+ SET status = 'deleted', delivery_id = ?, payload_sha256 = ?,
957
+ updated_at = ?
958
+ WHERE tombstone_id = ? AND status = 'pending'
959
+ """, (
960
+ delivery_id, payload_sha256, now, expected["tombstone_id"],
961
+ )).rowcount
962
+ if changed != 1:
963
+ raise CMSReceiverStoreBlocked("tombstone deletion was not atomic")
964
+ return {
965
+ "delivery_id": delivery_id,
966
+ "payload_sha256": payload_sha256,
967
+ "status": "deleted",
968
+ }
969
+
970
+ def check(self, probe: Any) -> Mapping[str, Any]:
971
+ """Return the exact content-free health receipt after durable checks."""
972
+
973
+ probe_id = _token(getattr(probe, "probe_id", None), field="probe_id")
974
+ contract_sha256 = _sha256(
975
+ getattr(probe, "contract_sha256", None), field="contract_sha256",
976
+ )
977
+ self._verify_schema()
978
+ if self.connection.in_transaction:
979
+ raise CMSReceiverStoreBlocked("health cannot join an external transaction")
980
+ quick = self.connection.execute("PRAGMA quick_check").fetchall()
981
+ if [tuple(row) for row in quick] != [("ok",)]:
982
+ raise CMSReceiverStoreBlocked("SQLite integrity check failed")
983
+ foreign_keys = self.connection.execute("PRAGMA foreign_key_check").fetchall()
984
+ if foreign_keys:
985
+ raise CMSReceiverStoreBlocked("SQLite foreign-key check failed")
986
+ sources = self.connection.execute(
987
+ "SELECT * FROM cms_receiver_sources ORDER BY site_id, source_id"
988
+ ).fetchall()
989
+ for source in sources:
990
+ self._source_from_row(source)
991
+ invalid_active = self.connection.execute("""
992
+ SELECT 1
993
+ FROM cms_receiver_sources AS source
994
+ LEFT JOIN cms_receiver_publications AS publication
995
+ ON publication.delivery_id = source.active_delivery_id
996
+ WHERE source.active_delivery_id IS NOT NULL AND (
997
+ publication.delivery_id IS NULL
998
+ OR publication.status != 'active'
999
+ OR publication.site_id != source.site_id
1000
+ OR publication.source_id != source.source_id
1001
+ )
1002
+ LIMIT 1
1003
+ """).fetchone()
1004
+ orphan_active = self.connection.execute("""
1005
+ SELECT 1
1006
+ FROM cms_receiver_publications AS publication
1007
+ LEFT JOIN cms_receiver_sources AS source
1008
+ ON source.site_id = publication.site_id
1009
+ AND source.source_id = publication.source_id
1010
+ AND source.active_delivery_id = publication.delivery_id
1011
+ WHERE publication.status = 'active' AND source.site_id IS NULL
1012
+ LIMIT 1
1013
+ """).fetchone()
1014
+ if invalid_active is not None or orphan_active is not None:
1015
+ raise CMSReceiverStoreBlocked("durable receiver state is inconsistent")
1016
+ publications = self.connection.execute(
1017
+ "SELECT * FROM cms_receiver_publications ORDER BY delivery_id"
1018
+ ).fetchall()
1019
+ for publication in publications:
1020
+ self._validate_publication_row(publication)
1021
+ tombstones = self.connection.execute(
1022
+ "SELECT * FROM cms_receiver_tombstones ORDER BY tombstone_id"
1023
+ ).fetchall()
1024
+ for tombstone in tombstones:
1025
+ value = self._tombstone_from_row(tombstone)
1026
+ if tombstone["status"] == "pending":
1027
+ publication = self.connection.execute("""
1028
+ SELECT publication.status, publication.payload_sha256,
1029
+ source.active_delivery_id
1030
+ FROM cms_receiver_publications AS publication
1031
+ LEFT JOIN cms_receiver_sources AS source
1032
+ ON source.site_id = publication.site_id
1033
+ AND source.source_id = publication.source_id
1034
+ WHERE publication.delivery_id = ?
1035
+ """, (value["publication_delivery_id"],)).fetchone()
1036
+ if (
1037
+ tombstone["delivery_id"] is not None
1038
+ or tombstone["payload_sha256"] is not None
1039
+ or publication is None
1040
+ or publication["status"] != "active"
1041
+ or publication["payload_sha256"]
1042
+ != value["publication_payload_sha256"]
1043
+ or publication["active_delivery_id"]
1044
+ != value["publication_delivery_id"]
1045
+ ):
1046
+ raise CMSReceiverStoreBlocked("pending tombstone is invalid")
1047
+ elif tombstone["status"] == "deleted":
1048
+ delivery_id = _token(tombstone["delivery_id"], field="delivery_id")
1049
+ payload_sha256 = _sha256(
1050
+ tombstone["payload_sha256"], field="payload_sha256",
1051
+ )
1052
+ publication = self.connection.execute(
1053
+ "SELECT status, payload_sha256, tombstone_delivery_id, "
1054
+ "tombstone_payload_sha256 "
1055
+ "FROM cms_receiver_publications WHERE delivery_id = ?",
1056
+ (value["publication_delivery_id"],),
1057
+ ).fetchone()
1058
+ if (
1059
+ publication is None
1060
+ or publication["status"] != "deleted"
1061
+ or publication["payload_sha256"]
1062
+ != value["publication_payload_sha256"]
1063
+ or publication["tombstone_delivery_id"] != delivery_id
1064
+ or publication["tombstone_payload_sha256"] != payload_sha256
1065
+ ):
1066
+ raise CMSReceiverStoreBlocked("deleted tombstone is invalid")
1067
+ else:
1068
+ raise CMSReceiverStoreBlocked("tombstone state is invalid")
1069
+ return {
1070
+ "probe_id": probe_id,
1071
+ "contract_sha256": contract_sha256,
1072
+ "status": "healthy",
1073
+ }