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,1302 @@
1
+ # CMS localization webhook API v2
2
+
3
+ `WebsiteLocalizationAPI` is the provider-neutral WSGI ingress exposed by the
4
+ composed website-localization runtime. It accepts a signed current CMS change,
5
+ cancellation, or published-content tombstone, durably enqueues one job per
6
+ required locale, and returns
7
+ content-free capabilities, queue progress, or end-to-end lifecycle status. It
8
+ never calls a model, approves a translation, prepares a publication, or returns
9
+ source or target prose.
10
+
11
+ ## Host setup
12
+
13
+ Use `runtime.cms_api`, or construct `WebsiteLocalizationAPI` with the exact CMS
14
+ bridge, event verifier, clock, and queue attempt limit. Mount the callable behind
15
+ a production WSGI server and TLS terminator. HTTPS is required by default. Only
16
+ a trusted proxy may derive `wsgi.url_scheme`; never trust an arbitrary forwarded
17
+ header. The host owns keys, credential-to-site provisioning, network access,
18
+ rate limits, timeouts, database backup, and request-log redaction.
19
+
20
+ This tenant-facing API deliberately does not expose service-wide health. An
21
+ operator can enable the separately authenticated `runtime.health_http` reader
22
+ described in
23
+ [`WEBSITE_LOCALIZATION_HEALTH_HTTP.md`](WEBSITE_LOCALIZATION_HEALTH_HTTP.md).
24
+
25
+ `require_https=False` is only for an authenticated loopback or test transport.
26
+ The application rejects query strings, transfer encoding, ambiguous JSON,
27
+ invalid UTF-8, a UTF-8 BOM, non-finite numbers, unknown fields, missing or
28
+ truncated lengths, and bodies over 4,000,000 bytes. The front server remains
29
+ responsible for unambiguous HTTP framing and request-smuggling protection.
30
+
31
+ ## Source-side reference client
32
+
33
+ `CMSLocalizationHTTPClient` in
34
+ `integrations/website_localization_cms_client.py` implements the sending side
35
+ for all six routes. Configure one origin containing only an HTTPS scheme and
36
+ authority, a callback that supplies host-owned authentication headers, and an
37
+ authority whose `sign(bytes)` method signs the exact canonical request bytes.
38
+ Credentials in the URL, base paths, query strings, fragments, redirects, and
39
+ authentication headers that collide with protocol headers are rejected.
40
+
41
+ Each method performs one HTTP attempt. The client does not retry: the CMS host
42
+ must decide whether and when to repeat an immutable event, cancellation,
43
+ tombstone, or short-lived read request. `submit_change`, `cancel`, and
44
+ `request_tombstone` accept complete versioned objects; `status`, `lifecycle`,
45
+ and `capabilities` create fresh signed read requests from the configured clock
46
+ and request-ID source. Caller mappings are copied through canonical
47
+ native-Unicode JSON before signing and are never mutated.
48
+
49
+ Successful responses require canonical bounded JSON, an allowed status code,
50
+ the exact response schema, and the originating request, event, and site
51
+ bindings. Capability responses additionally recompute both advertised hashes
52
+ and verify the exact ordered operation names, methods, paths, request schemas,
53
+ response schemas, and enabled booleans. Rehashing a substituted endpoint is
54
+ therefore insufficient. Stable server failures retain their content-free error
55
+ code and derive retryability from HTTP status; redirects and invalid bindings
56
+ fail closed.
57
+
58
+ ### Durable change dispatch
59
+
60
+ `DurableCMSChangeDispatcher` in
61
+ `integrations/website_localization_cms_dispatch.py` is the optional persistent
62
+ outbox in front of `CMSLocalizationHTTPClient.submit_change`. The host supplies
63
+ and secures a SQLite connection, enqueues the complete versioned change before
64
+ returning from its content-change handler, and runs `run_once` from a supervised
65
+ worker. Different worker processes must use different connections to the same
66
+ database.
67
+
68
+ The outbox binds `event_id` to canonical native-Unicode payload bytes, their
69
+ SHA-256, and a fixed attempt ceiling. An exact enqueue is idempotent; changing
70
+ the event, payload, or attempt policy under the same ID is a collision. Claiming
71
+ is transactional, carries an expiring worker token, and permits one HTTPS call.
72
+ The lease must outlive the configured client timeout. Retryable client failures
73
+ use capped exponential backoff; permanent failures and the attempt ceiling are
74
+ terminal. A lease lost after remote acceptance replays the same event after
75
+ expiry, relying on the server's existing exact event idempotency instead of
76
+ inventing a second identity.
77
+
78
+ `status` and `health` are content-free. They return event and payload hashes,
79
+ attempt state, stable error codes, remote acknowledgement metadata, counts, and
80
+ expired-lease indicators, never source text, credentials, signatures, or raw
81
+ transport errors. Schema, payload, hash, lease, acknowledgement, and state
82
+ inconsistencies block rather than being repaired optimistically.
83
+
84
+ ### Durable lifecycle monitoring
85
+
86
+ `DurableCMSLifecycleMonitor` in
87
+ `integrations/website_localization_cms_lifecycle_monitor.py` closes the
88
+ source-side loop after a successful change dispatch. Registration binds the
89
+ original canonical change hash to the returned event, site, plan, website
90
+ version, source sequence, and exact job count in a separate canonical binding
91
+ hash. A changed generation or a
92
+ dispatch that has not succeeded cannot be registered under the same event.
93
+
94
+ Each leased attempt invokes `CMSLocalizationHTTPClient.lifecycle` without a
95
+ caller-supplied request ID. The secure client therefore creates and signs a
96
+ fresh, purpose-bound request for every poll; an expired read request is never
97
+ stored for replay. Verified nonterminal states are polled again at the fixed
98
+ monitor interval. Retryable transport or service failures use capped
99
+ exponential backoff and a fixed consecutive-failure ceiling, while a successful
100
+ poll resets that failure streak. Permanent errors, malformed or
101
+ generation-mismatched responses, exhausted failures, and corrupted state stop
102
+ the event fail-closed.
103
+
104
+ Transactional expiring leases coordinate separate process connections and
105
+ recover a poll abandoned by a crashed worker. The monitor stores no source or
106
+ target text, credentials, signatures, or response prose. Its durable snapshot
107
+ contains only identities, locale names, counts, stable error codes, delivery
108
+ state, and the canonical lifecycle-response hash. `status` distinguishes
109
+ ongoing observation from verified terminal states; `health` remains blocked
110
+ for local monitor failure, expired ownership, or terminal localization,
111
+ publication, or deletion failure. A change already acknowledged as cancelled
112
+ or superseded becomes terminal without an unnecessary lifecycle request.
113
+
114
+ ### Durable cancellation and tombstone dispatch
115
+
116
+ `DurableCMSRemovalDispatcher` in
117
+ `integrations/website_localization_cms_removal_dispatch.py` is the persistent
118
+ source-side outbox for removal. Enqueue the complete cancellation when content
119
+ must stop before publication, or the complete tombstone only when an
120
+ acknowledged publication must be deleted. The dispatcher preserves these as
121
+ distinct operations and invokes `CMSLocalizationHTTPClient.cancel` or
122
+ `request_tombstone` accordingly; it never guesses which removal phase applies.
123
+
124
+ The durable identity is `(operation, cancellation_id)` or
125
+ `(operation, tombstone_id)`, bound to the event, complete canonical request
126
+ bytes, SHA-256, and fixed attempt ceiling. Exact replay is idempotent; changed
127
+ website version, event, source ID, sequence, payload, or retry policy under that
128
+ identity is rejected. Transactional leases coordinate separate worker
129
+ connections. A lost lease after remote acceptance repeats the same immutable
130
+ request, so the service's cancellation or tombstone ledger converges without a
131
+ second logical deletion.
132
+
133
+ One `run_once` performs exactly one secure client operation. Retryable failures
134
+ enter capped exponential backoff, permanent failures and exhausted attempts
135
+ become terminal, and malformed acknowledgements fail closed. Content-free
136
+ status and health retain only operation/request/event IDs, hashes, attempt
137
+ state, stable errors, remote delivery identity and status, counts, and lease
138
+ times. They retain no website text, credentials, signatures, or private error
139
+ detail. Database, payload, claim, or response inconsistency blocks before a
140
+ network call.
141
+
142
+ ### Coordinated source-CMS service
143
+
144
+ `CMSLocalizationSourceService` in
145
+ `integrations/website_localization_cms_source_service.py` composes the change
146
+ outbox, removal outbox, and lifecycle monitor into the complete source-side
147
+ worker. The host supplies three distinct durable SQLite connections, one
148
+ configured `CMSLocalizationHTTPClient`, stable worker identities, and a clock.
149
+ `enqueue_change` and `enqueue_removal` persist an immutable request before the
150
+ host acknowledges its own content change. `run_once` advances at most one
151
+ network operation; `run_forever` adds bounded active, idle, and blocked sleeps
152
+ without taking ownership of process signals or database connections.
153
+
154
+ The order is deliberate: due cancellations and tombstones run first, then a
155
+ locally missing lifecycle registration is repaired, then one new change is
156
+ sent, and only then is one lifecycle read performed. After a successful change
157
+ response, the service registers its exact event, plan, job count, website
158
+ generation, and canonical payload hash immediately. If the process exits after
159
+ remote acceptance or after the separate registration commit, the next tick
160
+ reconciles the two durable stores idempotently before any further change or
161
+ lifecycle network call. It never invents a new event identity or treats an
162
+ unregistered acknowledgement as completed monitoring.
163
+
164
+ The three underlying token-bound leases remain the concurrency authority, so
165
+ multiple supervised processes may use separate connections to the same three
166
+ database files. The service validates all dependencies, lease/timeout ordering,
167
+ worker IDs, connections, and delays before creating schemas. A conflicting
168
+ dispatch-to-lifecycle binding blocks the whole tick before network access.
169
+ `health` verifies all three stores plus every successful dispatch registration
170
+ and returns only counts, stable codes, and a pending-registration count. An
171
+ acknowledged change awaiting that local handoff is explicitly `degraded`; a
172
+ store failure or conflicting binding is `blocked`. Tick and health payloads
173
+ never contain website text, provider responses,
174
+ credentials, signatures, or exception messages.
175
+
176
+ Premortem: an acknowledgement can be lost between two local commits, a removal
177
+ can be starved by ordinary changes, a retry can duplicate logical work, or an
178
+ exception can leak customer prose. Registration reconciliation, removal-first
179
+ scheduling, the existing immutable IDs and leases, one external operation per
180
+ tick, and stable error reduction close those paths. Regression tests cover both
181
+ crash boundaries, restart recovery without resend, operation priority,
182
+ binding tampering, private exception redaction, health, loop delays, and
183
+ pre-schema configuration rejection.
184
+
185
+ ### Durable source-CMS runtime
186
+
187
+ `open_durable_cms_source` in
188
+ `integrations/website_localization_cms_source_runtime.py` is the production
189
+ composition root for the coordinated source service. The host supplies three
190
+ canonical absolute SQLite paths, one already configured provider-neutral CMS
191
+ HTTP client, and stable worker IDs. The runtime validates the entire service
192
+ configuration in memory before it creates any persistent schema, opens and
193
+ owns three separate connections, and exposes the same `enqueue_change`,
194
+ `enqueue_removal`, `run_once`, `run_forever`, and `health` lifecycle through a
195
+ single process-bound object. It is a context manager and repeated `close` is
196
+ safe; all later work blocks after close.
197
+
198
+ Each filesystem database must be a regular owner-owned file with exactly one
199
+ link and mode `0600` beneath a private, non-aliased directory chain. URI paths,
200
+ relative paths, path reuse, symlinks, hard links, permissive files or parents,
201
+ and path identity replacement fail closed. The runtime rechecks all three
202
+ identities before and after every state transition, serializes access from
203
+ threads inside one process, and checks process ownership before acquiring its
204
+ lock. A pre-fork copy therefore cannot inherit SQLite connections or a locked
205
+ thread state. Multi-process supervisors instead construct one runtime after
206
+ each fork; independent connections then coordinate through the existing
207
+ transactional leases.
208
+
209
+ The runtime deliberately does not read environment variables, credentials, or
210
+ configuration files and does not own process signals. Authentication and the
211
+ request-signing authority stay inside the supplied `CMSLocalizationHTTPClient`;
212
+ their values, database paths, website content, and private exception messages
213
+ never enter the runtime representation or stable failure codes. A deployment
214
+ may therefore choose its own secret manager and supervisor without weakening
215
+ the provider-neutral contract.
216
+
217
+ ### Durable terminal notification callback
218
+
219
+ Polling remains sufficient, but a deployment can pass `terminal_notifier` and
220
+ `notification_worker_id` to `open_durable_cms_source` or
221
+ `open_hosted_cms_source`. Both values are required together. After the durable
222
+ lifecycle monitor verifies a terminal state, the service first stores one
223
+ `blun.cms-source-terminal-notification.v1` object in the same protected
224
+ lifecycle database. It contains only:
225
+
226
+ - `notification_id`, `event_id`, `site_id`, and `plan_id`;
227
+ - `website_version`, `source_sequence`, and `job_count`;
228
+ - `change_sha256` and `lifecycle_binding_sha256`;
229
+ - `terminal_status` and `lifecycle_sha256`.
230
+
231
+ `lifecycle_sha256` is present for a signed lifecycle response and is `null`
232
+ only when the original dispatch acknowledgement already made `cancelled` or
233
+ `superseded` terminal. Source text, target text, locale prose, credentials,
234
+ provider responses, and private errors are never copied into this callback.
235
+
236
+ The host callback must atomically and idempotently record the notification and
237
+ return exactly:
238
+
239
+ ```json
240
+ {
241
+ "schema": "blun.cms-source-terminal-notification-ack.v1",
242
+ "notification_id": "terminal-<sha256>",
243
+ "event_id": "cms-event-184",
244
+ "site_id": "public-site",
245
+ "status": "accepted",
246
+ "notification_sha256": "<sha256>"
247
+ }
248
+ ```
249
+
250
+ Returning normally with any other object is a terminal protocol failure. A
251
+ callback may raise `TerminalNotificationFailure(code, retryable=True)` only
252
+ for a content-free transient condition; retries use the same immutable
253
+ notification identity, an expiring lease, bounded exponential backoff, and
254
+ the configured `max_notification_attempts`. Unknown exceptions are reduced to
255
+ `terminal_notification.callback_failure` and fail closed. Notification health
256
+ is included in aggregate service health and therefore in hosted readiness.
257
+ Disabling the optional notifier preserves the existing authenticated status
258
+ polling contract and does not create notification state.
259
+
260
+ For a remote backend, pass an
261
+ `HTTPTerminalNotifierAdapter` from
262
+ `integrations/website_localization_cms_terminal_notification_http.py` as the
263
+ `terminal_notifier`. It sends the canonical notification object itself as the
264
+ request body to one configured HTTPS URL. Loopback HTTP is available only by
265
+ explicit test/development opt-in. Redirects are never followed, and the adapter
266
+ performs exactly one transport attempt; the durable notification outbox alone
267
+ decides whether and when to retry.
268
+
269
+ Each request reserves these exact transport headers:
270
+
271
+ - `Content-Type: application/json; charset=utf-8`;
272
+ - `Accept: application/json`;
273
+ - `Idempotency-Key: <notification_id>`;
274
+ - `X-Localization-Terminal-Notification-Id: <notification_id>`;
275
+ - `X-Localization-Terminal-Notification-Sha256: <body_sha256>`.
276
+
277
+ The host-supplied authentication callback receives a fresh copy of this
278
+ content-free request before any network access:
279
+
280
+ ```json
281
+ {
282
+ "schema": "blun.cms-source-terminal-notification-http-auth.v1",
283
+ "method": "POST",
284
+ "origin": "https://cms.example.test",
285
+ "path": "/v1/localization/terminal-notifications",
286
+ "notification_id": "terminal-<sha256>",
287
+ "event_id": "cms-event-184",
288
+ "site_id": "public-site",
289
+ "body_sha256": "<sha256 of exact canonical request bytes>"
290
+ }
291
+ ```
292
+
293
+ It may return deployment-specific authentication or signature headers, but it
294
+ cannot replace reserved framing, idempotency, or binding headers. Empty,
295
+ duplicated, injected, oversized, or reserved authentication headers block
296
+ before transport. The receiver returns the exact acknowledgement documented
297
+ above with HTTP `200` and JSON UTF-8 content type. A `3xx` response is a
298
+ permanent redirect failure; `408`, `425`, `429`, and `5xx` statuses plus network
299
+ failures are retryable by the outbox. Other statuses and malformed or
300
+ cross-bound successful responses are permanent protocol failures. Response
301
+ bodies and private exceptions are never copied into durable error state.
302
+
303
+ #### Reference terminal-notification receiver
304
+
305
+ `integrations/website_localization_cms_terminal_notification_receiver.py`
306
+ provides the matching provider-neutral WSGI endpoint. Construct one
307
+ `DurableCMSTerminalNotificationInbox` around a worker-owned SQLite connection,
308
+ then mount `CMSTerminalNotificationReceiverApplication` at the configured path.
309
+ For a multithreaded WSGI worker, open that connection with
310
+ `check_same_thread=False`; the inbox serializes access. Do not construct it before
311
+ forking: its process binding deliberately blocks inherited connections.
312
+
313
+ The receiver accepts only canonical UTF-8 JSON sent over HTTPS with the exact
314
+ content type, content length, idempotency key, notification ID, and notification
315
+ SHA-256 headers documented above. Before storage, it calls the host verifier as
316
+ `authenticate(authentication_context, normalized_headers)`. The first argument
317
+ is exactly the content-free object supplied to the sender's authentication-header
318
+ provider. The verifier must return:
319
+
320
+ ```json
321
+ {
322
+ "schema": "blun.cms-source-terminal-notification-principal.v1",
323
+ "principal_id": "website-cms",
324
+ "credential_id": "cms-key",
325
+ "credential_version": "v1",
326
+ "scope": "terminal-notification:write",
327
+ "site_id": "public-site"
328
+ }
329
+ ```
330
+
331
+ The returned `site_id` must equal the notification site. The receiver stores the
332
+ exact body and its hash in one SQLite transaction before returning the existing
333
+ `blun.cms-source-terminal-notification-ack.v1` acknowledgement. An exact replay
334
+ returns that same acknowledgement and preserves the original `received_at`.
335
+ Changed bytes under an existing event, notification, or payload identity return
336
+ HTTP `409`; malformed or wrongly bound requests are permanent `4xx` failures,
337
+ while unavailable authentication, damaged storage, and unexpected internal
338
+ failures return retryable `503`. Every error body is content free.
339
+
340
+ For deployment, prefer
341
+ `integrations/website_localization_cms_terminal_notification_receiver_runtime.py`.
342
+ Its `open_durable_terminal_notification_receiver` factory takes a canonical
343
+ absolute database path, the host verifier, exact HTTPS origin, and optional path
344
+ and clock. It validates the full HTTP boundary before opening SQLite, creates a
345
+ missing file exclusively with mode `0600`, and rejects unsafe parents, symlinks,
346
+ hard links, special files, permissive modes, and path replacement. Construct one
347
+ runtime after each WSGI worker forks; inherited runtimes report
348
+ `foreign-process` and block without entering a possibly inherited lock.
349
+
350
+ The runtime itself is the WSGI application. It serializes request execution with
351
+ close, rechecks the pinned database identity before every operation, and owns the
352
+ connection until `close()` or context-manager exit. `status(event_id)` returns
353
+ only the verified content-free receipt binding. `health()` checks SQLite
354
+ integrity and every stored notification and processing row, then returns only
355
+ content-free status, runtime state, counts, due work, expired leases, and
356
+ terminal failures. Closed, exchanged, damaged, or foreign-process runtimes
357
+ return content-free HTTP `503` and require a new worker runtime; they never
358
+ attempt repair or invent acknowledgement state.
359
+
360
+ #### Durable CMS-side processing
361
+
362
+ Schema V2 of the receiver inbox creates one processing row in the same
363
+ transaction as every newly received notification. The HTTP acknowledgement is
364
+ therefore impossible unless both the immutable receipt and its discoverable
365
+ host work item have committed. When a V1 database opens, the receiver validates
366
+ the exact old schema and every stored notification, creates the processing
367
+ ledger, backfills one pending row per receipt, and advances the schema version
368
+ inside one transaction. Any altered input rolls the migration back.
369
+
370
+ Call `runtime.process_next(callback, worker_id)` to advance at most one due
371
+ notification. The callback receives a fresh mapping of the exact content-free
372
+ terminal notification and must return:
373
+
374
+ ```json
375
+ {"event_id":"cms-event-184","notification_id":"terminal-…","notification_sha256":"…","schema":"blun.cms-terminal-notification-processing-ack.v1","site_id":"public-site","status":"processed"}
376
+ ```
377
+
378
+ The claim binds the worker ID, random lease token, attempt number, deadline,
379
+ notification identity, event, site, terminal status, and exact payload hash.
380
+ Completion revalidates those bindings and rejects expired or replaced claims.
381
+ A host may raise `TerminalNotificationProcessingFailure` with one stable error
382
+ code and an explicit retry decision. Retryable failures wait using the
383
+ configured bounded exponential delay; permanent failures and exhausted attempts
384
+ remain durably failed and make health `blocked`. Unexpected exceptions are
385
+ reduced to `processing_callback_failure`; their messages are never stored.
386
+
387
+ The runtime serializes processing with request handling and shutdown. A crash
388
+ leaves the lease durable; the next worker recovers it after expiry and a stale
389
+ owner cannot complete it. Consequently, the host callback must apply its own
390
+ state change idempotently under `notification_id`: a crash after the host commit
391
+ but before the processing completion commit deliberately repeats the same exact
392
+ notification. Runtime health now includes content-free processing counts, due
393
+ work, expired leases, and terminal failures.
394
+
395
+ For a single-process WSGI deployment, use
396
+ `open_hosted_durable_terminal_notification_receiver`. It preflights the handler,
397
+ worker ID, lease, and all loop delays before creating the database, then starts
398
+ one process-owned, non-daemon worker. The runtime accepts HTTP notifications
399
+ only while that managed worker is running and the inbox remains healthy.
400
+ `worker_readiness()` returns the content-free
401
+ `blun.cms-terminal-receiver-readiness.v1` object; supervisors must remove the
402
+ instance from service whenever its status is `not_ready`.
403
+
404
+ Idle, active, and blocked outcomes use separate interruptible delays. A private
405
+ loop exception becomes `notification_receiver.worker_blocked`, makes readiness
406
+ fail, and prevents the same in-memory runtime from restarting. `close()` first
407
+ signals and joins the worker and closes SQLite only after the callback returns.
408
+ If the configured join timeout expires, close fails while durable state remains
409
+ open; the supervisor must resolve or terminate the stuck callback before trying
410
+ again. Construct the hosted runtime after every process fork.
411
+
412
+ #### Terminal-receiver status, health, readiness, and capabilities
413
+
414
+ The durable runtime also serves four authenticated, content-free control routes
415
+ on the same exact HTTPS origin:
416
+
417
+ | Method | Path | Required scope | Purpose |
418
+ | --- | --- | --- | --- |
419
+ | `POST` | `/v1/localization/terminal-notifications/status` | `terminal-notification-status:read` | Read one site's durable processing state |
420
+ | `GET` | `/v1/localization/terminal-notifications/health` | `terminal-notification-health:read` | Inspect aggregate runtime, worker, and durable inbox health |
421
+ | `GET` | `/v1/localization/terminal-notifications/readiness` | `terminal-notification-readiness:read` | Check the managed worker and verified inbox |
422
+ | `GET` | `/v1/localization/terminal-notifications/capabilities` | `terminal-notification-capabilities:read` | Discover the exact active receiver contract |
423
+
424
+ Status accepts only canonical UTF-8 JSON and requires
425
+ `X-Localization-Terminal-Status-SHA256` to equal the exact body hash:
426
+
427
+ ```json
428
+ {"event_id":"cms-event-184","schema":"blun.cms-terminal-receiver-status-request.v1","site_id":"public-site"}
429
+ ```
430
+
431
+ Authentication receives the method, origin, path, event, site, and exact body
432
+ hash. The returned principal must use
433
+ `blun.cms-source-terminal-notification-principal.v1`, the dedicated status
434
+ scope, and the same `site_id`. A valid response uses
435
+ `blun.cms-terminal-receiver-status-response.v1` and contains only immutable
436
+ notification bindings plus processing status, attempt limits, lease timing,
437
+ stable error code, and completion time. It never includes the stored payload or
438
+ website text. A missing event and another site's event return the same
439
+ content-free `404` response.
440
+
441
+ Readiness is strictly `GET`, body-free, query-free, and separately scoped. Its
442
+ `blun.cms-terminal-receiver-readiness.v1` response contains only `status`,
443
+ `worker_state`, `inbox_status`, and `error_code`. HTTP `200` requires a running
444
+ managed worker and verified `ok` inbox; all other states return `503`. None
445
+ of these control routes claims a lease, invokes the host handler, changes retry
446
+ state, or performs a model call.
447
+
448
+ Health is also strictly `GET`, body-free, query-free, and separately scoped.
449
+ Its `blun.cms-terminal-receiver-health.v1` response combines `runtime_state`,
450
+ `worker_state`, verified `inbox_status`, received and per-state processing
451
+ counts, due work, expired leases, terminal failures, and one stable error code.
452
+ HTTP `200` requires an open runtime, a running or deliberately unmanaged worker,
453
+ and an `ok` inbox. A stopped or failed managed worker, failed processing record,
454
+ expired lease, unreadable health state, or damaged inbox returns content-free
455
+ `503`. Unavailable counts are `null`; the receiver never invents a healthy
456
+ snapshot from partial evidence. Authentication completes before SQLite health
457
+ inspection, and the read does not claim, retry, complete, or otherwise mutate
458
+ processing state.
459
+
460
+ Discovery is also strictly `GET`, body-free, query-free, and separately scoped.
461
+ Its `blun.cms-terminal-receiver-capabilities-response.v1` envelope contains a
462
+ `blun.cms-terminal-receiver-capabilities.v1` contract and canonical SHA-256.
463
+ The digest covers all active operations, including the runtime's configured
464
+ notification intake path, plus methods, scopes, request and response schemas,
465
+ required fields, success statuses, transport limits, processing states, and
466
+ terminal outcomes. It contains no site, endpoint origin, notification,
467
+ credential, website text, provider response, or private error detail. The
468
+ contract includes the health operation's exact method, path, distinct scope,
469
+ schema, fields, and success status.
470
+
471
+ The capability request authenticates the exact empty-body hash before the
472
+ contract is built. It never reads the inbox, checks worker readiness, claims a
473
+ lease, or calls a handler. A custom intake path that collides with any control
474
+ route is rejected before SQLite is created. Missing or altered notification
475
+ schema fields, reused scopes, a request body, content type, query, wrong method,
476
+ or private authenticator failure returns a content-free fail-closed response.
477
+
478
+ #### Contract-pinned operator client
479
+
480
+ `integrations/website_localization_cms_terminal_receiver_client.py` provides a
481
+ provider-neutral HTTPS client for the complete receiver contract. Construct
482
+ `HTTPTerminalReceiverClient` with the receiver's exact origin, an
483
+ `expected_capabilities_sha256` obtained through trusted deployment
484
+ configuration, and a callback that supplies authentication headers for the
485
+ provided canonical request context. Do not learn and trust the digest from the
486
+ same untrusted connection that it is intended to authenticate.
487
+
488
+ `capabilities()` verifies the response envelope, the canonical digest, every
489
+ schema and limit, all five operation definitions, distinct scopes, and the
490
+ configured notification path. `health()`, `readiness()`, and `status()` first
491
+ repeat that live contract verification; a contract change therefore blocks the
492
+ operational read until the deployment deliberately updates its pin. Every HTTP
493
+ request is separately authenticated and uses exactly one bounded transport
494
+ attempt with no redirects.
495
+
496
+ `notify()` validates the complete immutable terminal notification before any
497
+ network call, repeats the pinned discovery, and sends the canonical bytes to
498
+ the notification path taken from that verified live contract. The client is
499
+ callable, so the same instance can be passed directly as `terminal_notifier` to
500
+ `open_durable_cms_source` or `open_hosted_cms_source`; no separately configured
501
+ write adapter or path is required. Its authentication context binds the exact
502
+ method, origin, verified path, body SHA-256, notification ID, event, and site.
503
+ The client owns no retry loop: `TerminalReceiverClientBlocked` advertises
504
+ `cms_notification_failure` plus stable retryability, allowing only the durable
505
+ source notification outbox to schedule another attempt. A malformed payload or
506
+ capability mismatch blocks before the write request, while an acknowledgement
507
+ must exactly match its notification, event, site, acceptance state, and payload
508
+ hash.
509
+
510
+ Health and readiness accept both their documented `200` and `503` states, then
511
+ validate exact fields and cross-field invariants before returning the
512
+ content-free snapshot. Status binds the canonical request body and its SHA-256
513
+ to the event and site, then rejects a response for any other tenant or identity.
514
+ All three operational responses also contain `capabilities_sha256`, derived
515
+ from the receiver's live configured contract. The client requires this value to
516
+ equal its trusted pin after discovery, preventing an endpoint switch or stale
517
+ response between the two requests from passing as current evidence.
518
+ Malformed JSON, duplicate keys, unexpected fields, inconsistent counts,
519
+ rehashed semantic contract drift, redirects, and private transport failures
520
+ raise `TerminalReceiverClientBlocked` with only a stable code and retryability.
521
+ The three read methods never claim work, mutate receiver state, process a
522
+ notification, or contain website text. `notify()` performs only the documented
523
+ durable intake operation and returns its content-free acknowledgement.
524
+
525
+ For a single-process WSGI deployment, `open_hosted_cms_source` adds the owned
526
+ worker lifecycle. It starts one non-daemon background worker before returning,
527
+ uses interruptible state-specific waits, and joins that worker before closing
528
+ any database. `start_worker` and `stop_worker` remain available to supervisors
529
+ that need an explicit startup boundary. Worker exceptions become a stable
530
+ content-free failure state; the same in-memory runtime cannot restart after
531
+ such a failure and must be replaced from its durable databases.
532
+
533
+ ### Authenticated source-CMS ingress
534
+
535
+ `integrations/website_localization_cms_source_http.py` provides the optional
536
+ WSGI boundary exposed as `runtime.http` when `open_durable_cms_source` receives
537
+ an `http_authenticator`. Mount it behind a production WSGI server and a trusted
538
+ TLS terminator. Construct the runtime after the worker process forks, and run
539
+ `runtime.run_forever` in the supervised background worker that owns the same
540
+ runtime. No HTTP route advances a lease or performs a network call.
541
+
542
+ The exact routes are:
543
+
544
+ | Method | Path | Required scope | Purpose |
545
+ | --- | --- | --- | --- |
546
+ | `POST` | `/v1/localization/source/changes` | `source-change:write` | Persist one complete signed CMS change |
547
+ | `POST` | `/v1/localization/source/removals` | `source-removal:write` | Persist one cancellation or tombstone |
548
+ | `POST` | `/v1/localization/source/status` | `source-status:read` | Read one site-bound event lifecycle |
549
+ | `GET` | `/v1/localization/source/health` | `source-health:read` | Read aggregate content-free health |
550
+ | `GET` | `/v1/localization/source/readiness` | `source-readiness:read` | Verify that the managed worker and durable service can accept work |
551
+ | `GET` | `/v1/localization/source/capabilities` | `source-capabilities:read` | Discover the exact active HTTP contract |
552
+
553
+ The capabilities route accepts no body or query. Its
554
+ `blun.cms-source-capabilities-response.v1` response contains one
555
+ `blun.cms-source-runtime-capabilities.v1` contract with the exact active
556
+ methods, paths, scopes, principal schemas, request and response schemas,
557
+ required top-level fields, success statuses, retry limits, and transport
558
+ bounds. The nested `sha256` is calculated over the canonical capability object
559
+ before that digest field is added. Clients can pin it during deployment and
560
+ reject unexpected contract drift without receiving website content or reading
561
+ the three runtime databases. The route has its own
562
+ `source-capabilities:read` credential, performs no state write, lease, repair,
563
+ or network call, and returns fail-closed if its route metadata is incomplete or
564
+ internally inconsistent.
565
+
566
+ The body-free readiness route uses
567
+ `blun.cms-source-readiness-response.v2`. It returns HTTP `200` only while the
568
+ managed worker is running and durable service health is `ok` or `degraded`;
569
+ startup, shutdown, a worker exception, closed state, or blocked durable health
570
+ returns HTTP `503`. Its separate `source-readiness:read` credential receives no
571
+ website content. After a runtime enters managed mode, change and removal routes
572
+ also require this worker state before persisting new work. Existing manually
573
+ driven runtimes retain their explicit `run_once` contract.
574
+
575
+ Change requests use
576
+ `blun.cms-source-change-enqueue-request.v1`; removal requests use
577
+ `blun.cms-source-removal-enqueue-request.v1`. Both contain the exact downstream
578
+ payload plus an explicit `max_attempts` from 1 through 20. Successful V2
579
+ responses return HTTP `202` with the canonical request identity, payload
580
+ SHA-256, durable state, attempt count, retry ceiling, and exact capability
581
+ digest. Replaying identical bytes converges on the same durable item. Reusing
582
+ an identity with different content or policy returns HTTP `409` and does not
583
+ alter stored work.
584
+
585
+ The status request is exact, query-free JSON and uses
586
+ `blun.cms-source-status-request.v1`:
587
+
588
+ ```json
589
+ {
590
+ "schema": "blun.cms-source-status-request.v1",
591
+ "event_id": "cms-event-184",
592
+ "site_id": "public-site"
593
+ }
594
+ ```
595
+
596
+ Its `blun.cms-source-status-response.v4` response contains one nested
597
+ `blun.cms-source-service-status.v3` snapshot. It binds the stored
598
+ `website_version`, `source_sequence`, canonical change hash, dispatch state and
599
+ attempts, remote plan and job count, local lifecycle state, remote lifecycle
600
+ status, lifecycle hash, required and approved locales, blocked locale reason
601
+ codes, and queue counts. It never contains source text, target text, delivery
602
+ payloads, credentials, signatures, provider bodies, or private exceptions.
603
+ The read performs no reconciliation, lease, retry, network call, or state
604
+ write. A caller can therefore distinguish queued work, a pending local
605
+ registration, active localization, approval, publication, cancellation, and a
606
+ terminal failure without accidentally advancing the worker.
607
+
608
+ Version 2 additionally exposes only the terminal notification state, its
609
+ content-free identity and hash, bounded attempt counters, and a public error
610
+ code. When the terminal notifier exposes the pinned receiver `status` method,
611
+ the snapshot also reports the independently durable processing observation,
612
+ poll failures, receiver attempts, and stable local and receiver error codes.
613
+ An intake acknowledgement is never presented as completed CMS processing.
614
+
615
+ The corresponding `blun.cms-source-health-response.v4` and nested
616
+ `blun.cms-source-service-health.v3` report notification and processing-observer
617
+ backlog plus component
618
+ health without revealing website content or callback responses.
619
+
620
+ Before parsing JSON or touching SQLite, the application calls the host-supplied
621
+ authenticator with this content-free request:
622
+
623
+ ```json
624
+ {
625
+ "schema": "blun.cms-source-runtime-http-auth-request.v1",
626
+ "method": "POST",
627
+ "path": "/v1/localization/source/changes",
628
+ "headers": [["authorization", "<host credential>"]],
629
+ "body_sha256": "<sha256 of exact request bytes>"
630
+ }
631
+ ```
632
+
633
+ For change, removal, health, and capabilities routes, the authenticator returns
634
+ exactly
635
+ `blun.cms-source-runtime-principal.v1` with `principal_id`, `credential_id`,
636
+ `credential_version`, and the one route-specific `scope`. Status uses the
637
+ separate `blun.cms-source-status-principal.v1` schema and additionally binds
638
+ one exact `site_id`. The request site must match that principal before SQLite
639
+ is queried; another site and an unknown event both return the same HTTP `404`
640
+ error. The runtime does not interpret credentials and never stores them.
641
+ Operators should give status, health, and write routes distinct credentials.
642
+ The WSGI server remains responsible for rejecting ambiguous wire-level HTTP
643
+ before constructing the WSGI environment.
644
+
645
+ Requests require an exact query-free HTTPS route, fixed `Content-Length`, UTF-8
646
+ JSON, and no transfer encoding. Health and capabilities accept no body or
647
+ content type. Every response sets `Cache-Control: no-store`,
648
+ `X-Content-Type-Options: nosniff`, and `Referrer-Policy: no-referrer`. Errors use
649
+ `blun.cms-source-runtime-http-error.v1` and never echo a body, header, path,
650
+ credential, exception, source string, or target string.
651
+
652
+ #### Contract-pinned website client
653
+
654
+ `integrations/website_localization_cms_source_client.py` is the provider-neutral
655
+ client for this complete ingress. Construct `CMSLocalizationSourceHTTPClient`
656
+ with one exact HTTPS origin, an `expected_capabilities_sha256` supplied through
657
+ trusted deployment configuration, and a callback that provides authentication
658
+ headers for the canonical request context. The callback receives the method,
659
+ origin, verified path and scope, body SHA-256, and the applicable event, site,
660
+ or request identity; it cannot replace framing, idempotency, or binding headers.
661
+
662
+ `submit_change()` and `submit_removal()` validate the complete V2 CMS change or
663
+ V1 cancellation/tombstone locally before discovery and before any write. Each
664
+ method fetches the capability object, requires its canonical digest and every
665
+ operation definition to match the installed contract and trusted pin, takes
666
+ the write path from that fresh result, and performs exactly one request. The
667
+ immutable event, cancellation, or tombstone identity is the idempotency key.
668
+ An accepted response must return the same operation, request and event IDs,
669
+ canonical inner-payload hash, retry ceiling, and capability digest.
670
+
671
+ `status()`, `health()`, and `readiness()` repeat discovery independently and
672
+ then validate every returned field and cross-field invariant. Status is bound
673
+ to the requested event and site. Health and readiness accept their documented
674
+ `200` and `503` states only when the HTTP status agrees with the nested state.
675
+ All five operational response schemas carry `capabilities_sha256`; a process
676
+ swap or stale response between discovery and the operation therefore blocks.
677
+
678
+ The adapter owns no retry loop. `CMSSourceClientBlocked` contains only a stable
679
+ content-free code and retryability decision, so the website host may apply its
680
+ own bounded retry policy. Network failures, `408`, `425`, `429`, and server
681
+ errors are retryable; redirects, contract drift, malformed JSON, unexpected
682
+ fields, cross-tenant evidence, payload mismatches, and invalid requests are
683
+ not. The client never returns website text through a status or health method
684
+ and never treats a failed or ambiguous response as accepted work.
685
+
686
+ #### Durable website-source delivery
687
+
688
+ `integrations/website_localization_cms_source_delivery.py` supplies the durable
689
+ retry policy for website and CMS hosts. Construct
690
+ `DurableCMSSourceDeliveryOutbox` with a caller-owned SQLite connection and one
691
+ already configured `CMSLocalizationSourceHTTPClient`. Enqueue the complete
692
+ change with `enqueue_change()` or a cancellation/tombstone with
693
+ `enqueue_removal()` before allowing the originating transaction to be treated
694
+ as handed off. Each stored item binds its canonical payload SHA-256, immutable
695
+ request and event IDs, site, source-service retry ceiling, delivery retry
696
+ ceiling, and the client's trusted capability digest.
697
+
698
+ `run_once()` claims at most one due item under an unpredictable token-bound
699
+ lease and performs exactly one client operation. Removal work is selected
700
+ before change work. A retryable client failure schedules durable exponential
701
+ backoff up to the delivery ceiling; a permanent failure or exhausted ceiling
702
+ becomes terminal. The independent `source_max_attempts` value is sent to the
703
+ remote source service and is never multiplied into the local delivery limit.
704
+
705
+ If the website process exits after remote acceptance but before the local
706
+ success commit, lease expiry makes the same canonical payload and idempotency
707
+ identity eligible again. The source service decides that replay idempotently.
708
+ A stale worker cannot complete or fail a lease after another worker has taken
709
+ it over. Choose `lease_seconds` greater than the configured client timeout,
710
+ run one tick at a time from a supervised worker, and keep the SQLite database
711
+ on durable private storage.
712
+
713
+ `status()` returns one content-free binding and attempt snapshot. `health()`
714
+ reports aggregate queue states, due work, expired leases, terminal failures,
715
+ and active rows pinned to another capability digest. SQLite schema or row
716
+ tampering blocks before client access. Contract drift does not silently move
717
+ queued writes to a newly advertised endpoint: the deployment must explicitly
718
+ resolve or migrate those immutable records first.
719
+
720
+ #### Hosted website-source delivery runtime
721
+
722
+ `integrations/website_localization_cms_source_delivery_runtime.py` is the
723
+ production composition root for that outbox. Call
724
+ `open_durable_cms_source_delivery()` with one absolute private database path,
725
+ one pinned source client, and a stable worker ID. It validates the client,
726
+ worker ID, timeout, lease, and backoff policy against an in-memory outbox before
727
+ creating the file. The resulting runtime owns its SQLite connection and must be
728
+ constructed after a prefork server creates the final worker process.
729
+
730
+ The database file is created mode `0600` without following links. Its complete
731
+ parent chain, owner, type, link count, permissions, device, and inode are
732
+ checked before and after every operation. A missing, replaced, linked, or
733
+ permission-weakened file blocks before client access. One reentrant runtime
734
+ lock serializes callers inside a process, while SQLite transactions and durable
735
+ leases let separately constructed processes share the same database safely.
736
+ An inherited runtime rejects the foreign process before attempting its lock.
737
+
738
+ `open_hosted_cms_source_delivery()` additionally starts one process-owned,
739
+ non-daemon background worker. It uses interruptible active, idle, and blocked
740
+ waits, claims one network attempt per tick, and gives removal events the same
741
+ priority defined by the outbox. Once managed, `enqueue_change()` and
742
+ `enqueue_removal()` accept new work only while that worker is alive and has no
743
+ recorded failure. Manual runtimes remain available for an external supervisor
744
+ that calls `run_once()` itself.
745
+
746
+ `worker_readiness()` returns only worker state, outbox health state, and a
747
+ stable error code. It is ready only when the managed worker is alive and the
748
+ outbox reports `ok`. `stop_worker()` signals before waiting, so an idle worker
749
+ wakes immediately. If a source-client call exceeds the configured join bound,
750
+ shutdown returns `source_delivery_runtime.worker_stop_timeout` and deliberately
751
+ keeps the database connection open; close it only after the worker finishes.
752
+ Worker exceptions are reduced to `source_delivery_runtime.worker_blocked`, and
753
+ the failed runtime cannot accept more managed source events.
754
+
755
+ Premortem: an invalid deployment could create state before discovering a bad
756
+ worker or timeout; two paths could alias one database; a pre-fork service could
757
+ reuse a vanished parent's lock; or a permission change could redirect the next
758
+ operation. In-memory configuration preflight, distinct canonical paths and
759
+ identities, process ownership checks before locking, per-call path guards, and
760
+ durable multi-process leases close those paths. Regression tests cover restart
761
+ persistence, owner-only creation, unsafe paths, changed permissions, close and
762
+ fork behavior, 24 concurrent thread ticks, and two independently constructed
763
+ workers converging on one change dispatch.
764
+
765
+ ## Create or resume localization work
766
+
767
+ ```http
768
+ POST /v2/localization/changes HTTP/1.1
769
+ Content-Type: application/json; charset=utf-8
770
+ Content-Length: <exact UTF-8 byte count>
771
+ X-Localization-Signature-Algorithm: <configured algorithm>
772
+ X-Localization-Key-Id: <site credential identifier>
773
+ X-Localization-Signature: <signature>
774
+ ```
775
+
776
+ The body is one complete `blun.cms-content-change.v2` event. Its required
777
+ `source_sequence` is a positive, monotonically increasing generation for the
778
+ exact `(site_id, localization.source_id)` pair. The nested `localization`
779
+ mapping is the planner contract documented in `WEBSITE_LOCALIZATION.md` and
780
+ binds the source revision and text, source locale, content type, glossary and
781
+ policy versions, provider/model/software identity, and optional target locales.
782
+ Omitting `target_locales` selects every supported EU official-language locale
783
+ except an EU source language.
784
+
785
+ Sign the UTF-8 bytes of canonical JSON: sorted object keys, no insignificant
786
+ spaces, native Unicode unescaped, NFC text, and no `NaN` or infinity. Whitespace
787
+ in the transmitted JSON may differ because the service parses strictly and
788
+ reconstructs the canonical payload before verification.
789
+
790
+ New current work returns `202 Accepted`. An exact replay returns `200 OK` and
791
+ `inserted_jobs: 0`, allowing safe recovery after an uncertain response. Reusing
792
+ an event ID or source sequence for different content returns `409 Conflict`.
793
+ A delayed event below an already accepted source generation is recorded as
794
+ `superseded` and returns `200 OK`; it can never become publication work.
795
+
796
+ If the process stops after persisting the signed event but before the separate
797
+ queue transaction, the service supervisor revalidates the stored signature and
798
+ automatically resumes exactly one such event per tick. It uses the same queue
799
+ attempt limit configured for this API. The deterministic plan and job IDs make
800
+ that recovery idempotent; an accepted cancellation remains terminal and is
801
+ never resumed. A missing or invalid signature, changed stored bytes, or queue
802
+ failure blocks before a model call and stays visible through health as
803
+ `cms.event.awaiting_queue_resume` until a valid recovery succeeds.
804
+
805
+ ```json
806
+ {"event_id":"cms-event-184","inserted_jobs":23,"job_count":23,"plan_id":"blun-l10n-plan-…","schema":"blun.website-localization-api.v2","status":"enqueued"}
807
+ ```
808
+
809
+ The response contains identifiers and counts only. Acceptance is not quality
810
+ approval or publication readiness.
811
+
812
+ ## Cancel unpublished localization work
813
+
814
+ A CMS can withdraw one exact accepted event without submitting replacement
815
+ text:
816
+
817
+ ```http
818
+ POST /v2/localization/cancellations HTTP/1.1
819
+ Content-Type: application/json; charset=utf-8
820
+ Content-Length: <exact UTF-8 byte count>
821
+ X-Localization-Signature-Algorithm: <configured algorithm>
822
+ X-Localization-Key-Id: <same site credential that created the event>
823
+ X-Localization-Signature: <signature>
824
+ ```
825
+
826
+ ```json
827
+ {"cancellation_id":"cms-cancellation-184","event_id":"cms-event-184","schema":"blun.cms-content-cancellation.v1","site_id":"public-site","source_id":"homepage.hero","source_sequence":184,"website_version":"website-2026-08-29.1"}
828
+ ```
829
+
830
+ Sign the complete canonical cancellation object. The service requires every
831
+ event, site, source, signed source sequence, website-version, and credential
832
+ binding to match the stored event. The first accepted cancellation returns
833
+ `202 Accepted`; exact replay returns `200 OK`. Reusing either the cancellation
834
+ ID or event binding for different bytes returns `409 Conflict`.
835
+
836
+ Acceptance permanently removes that event from production scheduling, blocks
837
+ new approvals and publication preparation, and terminally closes any pending or
838
+ retrying outbox entry with `event_cancelled`. The cancellation ledger is
839
+ immutable and reverified on reads and health checks. It does not delete shared
840
+ queue artifacts or translation memory because another current plan may validly
841
+ reference the same deterministic job.
842
+
843
+ The same operation also closes the crash gap after the signed event has been
844
+ stored but before its locale plan reaches the queue. Such an `accepted` event
845
+ can be cancelled without first replaying it. Status and lifecycle then report
846
+ every required locale as `cancelled` even though no queue row exists. An exact
847
+ change-event replay returns `cancelled` and cannot recreate work. If the
848
+ cancellation arrives while the atomic queue insertion is running, the bridge
849
+ rechecks the immutable ledger before promoting the event to `enqueued`; any
850
+ already-created shared queue artifacts remain ineligible through that event.
851
+
852
+ A confirmed publication cannot be cancelled through this endpoint. A currently
853
+ leased delivery also returns `409 Conflict`: after an external request starts,
854
+ the service cannot truthfully retract bytes that the CMS may already have
855
+ accepted. The caller must wait for the lease outcome; an accepted publication
856
+ remains immutable, while a failed or retryable delivery can then be cancelled.
857
+ Removing content that was already published uses the separately signed
858
+ tombstone operation below.
859
+
860
+ ## Delete an acknowledged publication
861
+
862
+ ```http
863
+ POST /v2/localization/tombstones HTTP/1.1
864
+ Content-Type: application/json; charset=utf-8
865
+ Content-Length: <exact UTF-8 byte count>
866
+ X-Localization-Signature-Algorithm: <configured algorithm>
867
+ X-Localization-Key-Id: <same site credential that created the event>
868
+ X-Localization-Signature: <signature>
869
+ ```
870
+
871
+ ```json
872
+ {"event_id":"cms-event-184","schema":"blun.cms-content-tombstone.v1","site_id":"public-site","source_id":"homepage.hero","source_sequence":184,"tombstone_id":"cms-tombstone-184","website_version":"website-2026-08-29.1"}
873
+ ```
874
+
875
+ The service accepts this request only after the exact publication has a valid,
876
+ signed `succeeded` acknowledgement. It binds the immutable tombstone to the
877
+ original tenant key, event, site, source generation, website version,
878
+ publication delivery ID, publication-payload hash, plan, and complete sorted
879
+ locale set. It never copies target text into the tombstone payload.
880
+
881
+ The first request returns `202 Accepted`; an exact replay returns `200 OK` and
882
+ the same deterministic delivery ID. Unknown, unpublished, cancelled,
883
+ differently bound, or colliding requests fail closed. A durable lease-based
884
+ outbox sends `blun.cms-localization-tombstone.v1`; the CMS must return the exact
885
+ signed `blun.cms-localization-tombstone-ack.v1` acknowledgement with status
886
+ `deleted`. Network and retryable acknowledgement failures use the bounded
887
+ outbox retry policy. Lifecycle and health report `deleting`,
888
+ `deletion_failed`, or `deleted`; accepting or retrying a tombstone does not call
889
+ a model or remove the immutable publication audit record.
890
+
891
+ ## Discover the active localization contract
892
+
893
+ A CMS can discover the exact runtime contract before creating work. This avoids
894
+ copying a locale list or quality-profile version into an integration where it
895
+ can silently become stale. The read uses its own signed purpose and does not
896
+ require an existing event:
897
+
898
+ ```http
899
+ POST /v2/localization/capabilities HTTP/1.1
900
+ Content-Type: application/json; charset=utf-8
901
+ Content-Length: <exact UTF-8 byte count>
902
+ X-Localization-Signature-Algorithm: <configured algorithm>
903
+ X-Localization-Key-Id: <credential identifier>
904
+ X-Localization-Signature: <signature>
905
+ ```
906
+
907
+ ```json
908
+ {"request_id":"capabilities-9","requested_at":1788955200,"schema":"blun.cms-localization-capabilities-request.v1"}
909
+ ```
910
+
911
+ The request signature and five-minute freshness window prevent an old or
912
+ different API request from being replayed for discovery. The response contains
913
+ the active change, plan, job, publication, and commercial-profile versions;
914
+ the ordered quality phases; accepted content types; the official EU language
915
+ source; the default target-selection rule; and all 24 exact BCP-47 locale
916
+ profiles. Every locale entry includes its EU code, language code, native name,
917
+ script, direction, quality-profile version, and quality-profile SHA-256 digest.
918
+ It does not expose the full profile instructions, credentials, customer text,
919
+ provider data, or mutable service state.
920
+
921
+ The nested `publication_http` object is the separately hashed, machine-readable
922
+ contract for the built-in outbound CMS adapter. It declares publication,
923
+ tombstone, and content-free health payload, request, acknowledgement, and
924
+ response schemas; the nested release-evidence schema; exact
925
+ success values; accepted JSON content types; at-least-once delivery; and the
926
+ three headers that bind every attempt to its delivery ID and payload hash. It
927
+ also declares the three health headers that bind a fresh probe ID and this
928
+ contract's digest. It does not advertise an active endpoint or credential, or
929
+ claim that a custom host publisher uses this adapter. A receiver can therefore
930
+ implement and test the
931
+ supported callback protocol without copying prose from the integration guide.
932
+ The transport and discovery manifest use the same constants, so schema or
933
+ header drift changes the digest or blocks discovery rather than producing a
934
+ partial contract.
935
+
936
+ CMS implementations can use
937
+ `integrations/website_localization_cms_receiver.py` as the fail-closed
938
+ publication, tombstone, and health reference receiver. For publication, the host
939
+ supplies the expected source generation, complete required-locale set, content
940
+ type, and commercial profile. For deletion, it supplies the exact acknowledged
941
+ publication delivery and payload hash as well as the complete locale set. The
942
+ receiver validates those bindings, release evidence where applicable, hashes,
943
+ headers, expiry times, and the publisher signature before invoking the host's
944
+ atomic, idempotent commit or delete callback. It returns a signed `accepted` or
945
+ `deleted` acknowledgement only after the callback confirms the exact delivery
946
+ ID and payload hash. The host continues to own authentication, key provisioning,
947
+ durable transactions, and target-text log redaction.
948
+
949
+ For the content-free health challenge, the host supplies an authentication
950
+ callback, the exact active `publication_http.sha256`, a health callback, and the
951
+ same acknowledgement authority used by the publisher adapter. The receiver
952
+ strictly parses and binds the probe before authentication, authenticates before
953
+ comparing deployment state or calling health logic, and accepts only an exact
954
+ `healthy` receipt for the probe ID and contract digest. It signs that same
955
+ binding only after the host confirms health. Authentication outages, wrong
956
+ contracts, malformed transport, private health errors, false receipts, and
957
+ signing failures cannot produce a healthy acknowledgement.
958
+
959
+ `CMSReceiverApplication` exposes these three receiver operations through one
960
+ provider-neutral WSGI callable. Its default mount is
961
+ `/v1/localization/callback`, matching the built-in publisher's one configured
962
+ endpoint for publication, tombstone, and health requests. The application
963
+ requires an exact query-free HTTPS `POST`, canonical UTF-8 JSON, explicit
964
+ bounded `Content-Length`, non-chunked framing, and host authentication. It then
965
+ dispatches only the three published outer schemas. Publication and tombstone
966
+ expectation resolvers see a request only after its payload, binding headers,
967
+ hashes, and publisher signature have passed verification; host writes retain
968
+ the later exact-receipt requirement. WSGI errors use the content-free
969
+ `blun.cms-localization-receiver-error.v1` envelope with only a stable code and
970
+ retry decision. A TLS terminator must set `wsgi.url_scheme` from trusted proxy
971
+ configuration, and the host must keep authorization headers and verified target
972
+ text out of access logs.
973
+
974
+ `DurableCMSReceiverStore` in
975
+ `integrations/website_localization_cms_receiver_store.py` supplies a complete
976
+ SQLite reference implementation for the five stateful host callbacks. It uses
977
+ one dedicated host-owned connection. The CMS registers each monotonic current
978
+ source before delivery and explicitly pre-registers a tombstone against the
979
+ exact active publication before deletion. Commit and delete recheck those
980
+ bindings inside `BEGIN IMMEDIATE`, so the earlier resolver lookup cannot race a
981
+ source change. Replays are bound to immutable delivery and payload hashes, a
982
+ replacement preserves the last-known-good bundle until its complete locale set
983
+ commits. The successful replacement transaction then securely deletes the
984
+ superseded payload and locale rows while retaining only content-free replay
985
+ evidence; any cleanup failure rolls the switch back to the previous active
986
+ bundle. Explicit deletion follows the same content-minimizing rule. The store's
987
+ health callback validates schema, SQLite integrity, canonical payloads, locale rows, active pointers, and
988
+ tombstone state before confirming the probe. Source and tombstone expectations
989
+ carry separate canonical hashes, so a syntactically valid field substitution
990
+ also blocks. `read_active_bundle` is for trusted
991
+ CMS rendering code only. It requires the complete trusted publication
992
+ expectation and returns text only when the active signed payload matches its
993
+ event, website version, plan, source revision, sequence and hash, exact locale
994
+ set, content type, and commercial profile. Site/source-only lookup is rejected,
995
+ so a retained last-known-good bundle cannot be attached to a newer generation.
996
+ The method contains target text and is not part of any public status response.
997
+ A WSGI worker process owns its store connection; request
998
+ threads may share it only through the composed runtime's serialized boundary.
999
+ Multiple worker processes may open distinct
1000
+ connections to the same file and rely on the transactional writer lock.
1001
+
1002
+ `open_durable_cms_receiver` in
1003
+ `integrations/website_localization_cms_receiver_runtime.py` is the reference
1004
+ composition root. It validates the complete receiver configuration before it
1005
+ opens SQLite, requires distinct publication and acknowledgement authority
1006
+ objects, rejects SQLite URI configuration, and returns one owner for the store,
1007
+ trusted registration and rendering methods, connection lifetime, and WSGI
1008
+ callable. A failed preflight creates no database; a later initialization failure
1009
+ closes the connection and never returns a partial application. Instantiate it
1010
+ once after each WSGI worker starts and call `close` during that worker's orderly
1011
+ shutdown. Inside one worker, a shared reentrant lock serializes all eight store
1012
+ paths: both expectation resolvers, commit, delete, health, trusted registration,
1013
+ and rendering. The composition root opens SQLite for cross-thread access only
1014
+ behind that lock, so a multithreaded worker can share the WSGI callable without
1015
+ racing transactions or reads. This does not make a pre-fork connection safe.
1016
+ The runtime binds itself to its creator process and rejects inherited callbacks
1017
+ and trusted host operations before lock acquisition; each worker process must
1018
+ construct and own its own runtime after forking.
1019
+
1020
+ For a durable store, pass a canonical absolute POSIX database path. Its direct
1021
+ parent must be a real service-owned directory. Every ancestor must be root- or
1022
+ service-owned and non-shared-writable, apart from a root-owned sticky temporary
1023
+ directory. The runtime atomically creates a missing database as `0600` and
1024
+ accepts an existing one only when it is a service-owned, single-link regular
1025
+ file with
1026
+ that exact mode. Directory and file identity, permissions, ownership, and link
1027
+ count are checked again before every callback or trusted host operation;
1028
+ symlinks, hard links, path replacement, and permission drift fail closed.
1029
+ `:memory:` is retained solely for ephemeral test composition.
1030
+
1031
+ The separate `api_contract` object lists all six tenant operations with their
1032
+ exact path, `POST` method, request schema, response schema, and current
1033
+ `enabled` state. A standalone host without approval and publication authorities
1034
+ still advertises lifecycle and tombstone schemas but marks those operations
1035
+ disabled, so a CMS can fail closed before submitting work. The object uses
1036
+ `blun.website-localization-http-capabilities.v1` and carries its own SHA-256
1037
+ over every other canonical field. Paths and schemas come from the same runtime
1038
+ constants used for routing; they are not copied into a second configuration.
1039
+
1040
+ The nested `blun.website-localization-capabilities.v3` object carries a
1041
+ `sha256` value over all its other canonical fields. Consumers can pin that
1042
+ digest for a deployment and deliberately reconfigure when it changes. The
1043
+ runtime rebuilds and validates the complete registry on every read; duplicate,
1044
+ missing, noncanonical, or profile-mismatched entries return a fail-closed `503`
1045
+ without a partial locale list.
1046
+
1047
+ Within it, `commercial_profile` is a separately hashed
1048
+ `translate-native.commercial-capabilities.v2` object. Its nested and separately
1049
+ hashed `review_summary_contract` defines the exact content-free result schema,
1050
+ field set, verified/review-required state invariant, ten allowed ordered
1051
+ review dimensions, complete-evidence hash semantics, and excluded sensitive
1052
+ content. A CMS or independent-review adapter can validate targeted commercial
1053
+ escalation without receiving project prices, brands, source/target text, spans,
1054
+ or reviewer prose. Any registry or digest drift blocks the whole discovery
1055
+ response rather than advertising a partial contract.
1056
+
1057
+ ```json
1058
+ {
1059
+ "capabilities": {
1060
+ "change_schema": "blun.cms-content-change.v2",
1061
+ "cancellation_schema": "blun.cms-content-cancellation.v1",
1062
+ "commercial_profile": {
1063
+ "profile": "translate-native.commercial.v2",
1064
+ "review_summary_contract": {
1065
+ "content_policy": {"project_brands": false, "project_prices": false, "reviewer_prose": false, "source_spans": false, "source_text": false, "target_spans": false, "target_text": false},
1066
+ "evidence_sha256": {"algorithm": "sha-256", "canonicalization": "utf-8-json-sort-keys-no-insignificant-whitespace", "covers": "complete-commercial-review-evidence"},
1067
+ "profile": "translate-native.commercial.v2",
1068
+ "required_fields": ["schema", "profile", "status", "review_required_dimensions", "evidence_sha256"],
1069
+ "result_schema": "translate-native.commercial-review-summary.v1",
1070
+ "review_required_dimensions": {"allowed": ["amount_currency", "discount_basis", "qualifiers", "tax_status", "billing_interval", "commitment", "renewal", "cancellation", "conditions", "offer_assignment"], "order": ["amount_currency", "discount_basis", "qualifiers", "tax_status", "billing_interval", "commitment", "renewal", "cancellation", "conditions", "offer_assignment"], "unique": true},
1071
+ "schema": "translate-native.commercial-review-summary-capabilities.v1",
1072
+ "sha256": "<sha256>",
1073
+ "statuses": {"review_required": {"requires_independent_review": true, "review_required_dimensions": "one-or-more"}, "verified": {"review_required_dimensions": "empty"}}
1074
+ },
1075
+ "schema": "translate-native.commercial-capabilities.v2",
1076
+ "sha256": "<sha256>"
1077
+ },
1078
+ "content_types": ["commercial", "cta", "documentation", "headline", "legal", "marketing", "seo", "ui"],
1079
+ "default_target_policy": "all-eu-official-locales-except-source-language",
1080
+ "eu_language_source": "https://european-union.europa.eu/principles-countries-history/languages_en",
1081
+ "job_schema": "blun.website-localization-job.v2",
1082
+ "locales": [{
1083
+ "direction": "ltr",
1084
+ "eu_code": "MT",
1085
+ "language": "mt",
1086
+ "locale": "mt-MT",
1087
+ "native_name": "Malti",
1088
+ "quality_profile_sha256": "<sha256>",
1089
+ "quality_profile_version": "eu-mt-MT-2026-09-1",
1090
+ "script": "Latn"
1091
+ }],
1092
+ "plan_schema": "blun.website-localization-plan.v2",
1093
+ "publication_http": {
1094
+ "binding_headers": [
1095
+ {"binding": "delivery_id", "name": "Idempotency-Key"},
1096
+ {"binding": "delivery_id", "name": "X-Localization-Delivery-Id"},
1097
+ {"binding": "payload_sha256", "name": "X-Localization-Payload-Sha256"}
1098
+ ],
1099
+ "delivery_semantics": "at-least-once",
1100
+ "health_binding_headers": [
1101
+ {"binding": "probe_id", "name": "Idempotency-Key"},
1102
+ {"binding": "probe_id", "name": "X-Localization-Probe-Id"},
1103
+ {"binding": "contract_sha256", "name": "X-Localization-Contract-Sha256"}
1104
+ ],
1105
+ "method": "POST",
1106
+ "operations": [{
1107
+ "acknowledgement_schema": "blun.cms-localization-publication-ack.v1",
1108
+ "acknowledgement_status": "accepted",
1109
+ "name": "publication",
1110
+ "payload_schema": "blun.cms-localization-publication.v3",
1111
+ "request_schema": "blun.cms-localization-publication-http.v1",
1112
+ "response_schema": "blun.cms-localization-publication-http-ack.v1"
1113
+ }],
1114
+ "request_content_type": "application/json; charset=utf-8",
1115
+ "release_evidence_schema": "blun.website-localization-release-evidence.v1",
1116
+ "response_content_types": ["application/json", "application/json; charset=utf-8"],
1117
+ "schema": "blun.cms-localization-publication-http-capabilities.v2",
1118
+ "sha256": "<sha256>"
1119
+ },
1120
+ "publication_schema": "blun.cms-localization-publication.v3",
1121
+ "quality_passes": ["target_native", "source_fidelity"],
1122
+ "schema": "blun.website-localization-capabilities.v3",
1123
+ "sha256": "<sha256>"
1124
+ },
1125
+ "api_contract": {
1126
+ "api_schema": "blun.website-localization-api.v2",
1127
+ "error_schema": "blun.website-localization-api.v2",
1128
+ "operations": [{
1129
+ "enabled": true,
1130
+ "method": "POST",
1131
+ "name": "lifecycle",
1132
+ "path": "/v2/localization/lifecycle",
1133
+ "request_schema": "blun.cms-localization-lifecycle-request.v1",
1134
+ "response_schema": "blun.cms-localization-lifecycle.v3"
1135
+ }],
1136
+ "schema": "blun.website-localization-http-capabilities.v1",
1137
+ "sha256": "<sha256>"
1138
+ },
1139
+ "request_id": "capabilities-9",
1140
+ "schema": "blun.website-localization-api.v2",
1141
+ "status": "CAPABILITIES"
1142
+ }
1143
+ ```
1144
+
1145
+ The abbreviated example shows one locale, one inbound operation, one outbound
1146
+ adapter operation, and only the commercial profile fields relevant to summary
1147
+ discovery. A successful real response always contains the complete commercial
1148
+ profile, all 24 locales, all six inbound operations, and all three outbound
1149
+ operations, and otherwise blocks.
1150
+
1151
+ ## Read per-locale progress
1152
+
1153
+ Status is a signed, short-lived POST rather than an unauthenticated identifier
1154
+ in a URL:
1155
+
1156
+ ```http
1157
+ POST /v2/localization/status HTTP/1.1
1158
+ Content-Type: application/json; charset=utf-8
1159
+ Content-Length: <exact UTF-8 byte count>
1160
+ X-Localization-Signature-Algorithm: <configured algorithm>
1161
+ X-Localization-Key-Id: <same credential that created the event>
1162
+ X-Localization-Signature: <signature>
1163
+ ```
1164
+
1165
+ ```json
1166
+ {"event_id":"cms-event-184","request_id":"status-7","requested_at":1788955200,"schema":"blun.cms-localization-status-request.v2","site_id":"public-site"}
1167
+ ```
1168
+
1169
+ Sign the canonical status object. `requested_at` must be within five minutes of
1170
+ the service clock. The signed `site_id` and signature header's `key_id` must
1171
+ match the stored event scope exactly, even when the verifier recognizes several
1172
+ valid credentials. This prevents one valid tenant credential from probing
1173
+ another tenant's event. A rotated deployment must retain the original verifier
1174
+ identity for unfinished events or explicitly migrate them outside this module.
1175
+
1176
+ The read-only response echoes `request_id` and reports the bound site, website
1177
+ version, source sequence, plan and job counts, plus one item per target locale.
1178
+ Each item contains job/locale identity, state, attempts, retry timing, lease
1179
+ expiry, stable error code, optional private-detail hash, and optional result
1180
+ hash. The top-level `cancelled` flag records an accepted withdrawal.
1181
+ `lease_expired: true` makes recoverable crashes visible. A succeeded queue
1182
+ row is reloaded and hash-checked before it can appear successful.
1183
+
1184
+ If the signed event is durable but the process stopped before queue insertion,
1185
+ `queue_recovery_pending` is `true`. Every required locale then reports
1186
+ `awaiting_queue_resume` with the stable content-free reason
1187
+ `cms.event.awaiting_queue_resume`; all real queue counts remain zero. The read
1188
+ does not create jobs or invoke a provider. The supervised service separately
1189
+ revalidates and resumes that exact event. Once queue insertion succeeds, the
1190
+ flag becomes `false` and ordinary queue state replaces the synthetic status.
1191
+
1192
+ Status is not readiness. Valid independent evidence, signed per-locale
1193
+ approvals, and the all-required-locales release gate remain authoritative.
1194
+ Superseded events, wrong site or credential scope, missing work, altered queue
1195
+ identity, or a corrupt result return a complete fail-closed error rather than a
1196
+ partial optimistic status.
1197
+
1198
+ ## Read verified end-to-end lifecycle
1199
+
1200
+ The tenant can separately ask whether the same event is cancelled, still
1201
+ translating, awaiting signed approvals, ready, publishing, blocked, failed, or
1202
+ confirmed as published. This operation has its own schema so a valid
1203
+ queue-progress request cannot be replayed for a broader lifecycle read:
1204
+
1205
+ ```http
1206
+ POST /v2/localization/lifecycle HTTP/1.1
1207
+ Content-Type: application/json; charset=utf-8
1208
+ Content-Length: <exact UTF-8 byte count>
1209
+ X-Localization-Signature-Algorithm: <configured algorithm>
1210
+ X-Localization-Key-Id: <same credential that created the event>
1211
+ X-Localization-Signature: <signature>
1212
+ ```
1213
+
1214
+ ```json
1215
+ {"event_id":"cms-event-184","request_id":"lifecycle-8","requested_at":1788955200,"schema":"blun.cms-localization-lifecycle-request.v1","site_id":"public-site"}
1216
+ ```
1217
+
1218
+ The signature, five-minute freshness window, site scope, and original key scope
1219
+ are identical to the progress route. The distinct schema binds the signature to
1220
+ this purpose. The composed runtime always supplies the approval and publication
1221
+ verification authorities. A standalone `WebsiteLocalizationAPI` must supply
1222
+ both authorities together; otherwise this route returns a fail-closed `503`.
1223
+
1224
+ A successful response uses
1225
+ `blun.cms-localization-lifecycle.v3`. It contains the event, site, website
1226
+ version, plan and source-sequence identifiers; aggregate queue counts; required,
1227
+ approved and blocked locales; and an optional content-free delivery summary.
1228
+ The lifecycle `status` is exactly one of:
1229
+
1230
+ - `cancelled`: the exact unpublished event has an accepted signed cancellation;
1231
+ - `queue_recovery`: the event is durably accepted but its per-locale queue rows
1232
+ still await the supervised, idempotent recovery pass;
1233
+ - `processing`: at least one required locale still has queue work;
1234
+ - `localization_failed`: at least one required locale failed terminally;
1235
+ - `awaiting_approval`: all locale results exist, but signed release evidence is
1236
+ missing;
1237
+ - `ready`: every required locale has a current verified approval, but no signed
1238
+ CMS delivery exists yet;
1239
+ - `publishing`: a valid signed delivery is pending, leased, or waiting for retry;
1240
+ - `publication_blocked`: the prepared delivery can no longer be published, for
1241
+ example because an approval expired before acknowledgement;
1242
+ - `publication_failed`: bounded delivery attempts ended terminally;
1243
+ - `published`: the CMS returned the exact signed acknowledgement for the
1244
+ delivery.
1245
+
1246
+ The read path revalidates every successful queue result and signed approval,
1247
+ then checks the complete stored publication envelope, signature, event, plan,
1248
+ source revision and locale set. It does not prepare, claim, renew, retry, sign,
1249
+ repair, or publish anything. A published acknowledgement stays published after
1250
+ its former approval validity window ends; expiration before acknowledgement is
1251
+ reported as `publication_blocked`.
1252
+
1253
+ ```json
1254
+ {"approved_locales":["fi-FI"],"blocked_locales":[],"delivery":{"attempts":0,"delivery_id":"blun-cms-delivery-…","last_error_code":null,"last_error_detail_hash":null,"lease_expired":false,"lease_expires_at":null,"max_attempts":5,"next_attempt_at":1788955201.0,"status":"pending"},"event_id":"cms-event-184","plan_id":"blun-l10n-plan-…","queue_counts":{"failed":0,"leased":0,"pending":0,"retry_wait":0,"succeeded":1},"request_id":"lifecycle-8","required_locales":["fi-FI"],"schema":"blun.cms-localization-lifecycle.v3","site_id":"public-site","source_sequence":42,"status":"publishing","website_version":"release-42"}
1255
+ ```
1256
+
1257
+ ## Failure contract
1258
+
1259
+ Every failure has `Cache-Control: no-store` and contains no customer or provider
1260
+ prose:
1261
+
1262
+ ```json
1263
+ {"error":"cms.status.scope_rejected","schema":"blun.website-localization-api.v2","status":"BLOCK"}
1264
+ ```
1265
+
1266
+ `401` covers invalid, expired, or wrong-scope signed requests; `409` covers
1267
+ identity, source-sequence, supersession, cancellation, in-flight publication,
1268
+ and legacy-ingress conflicts;
1269
+ `413` and `415` cover body size and media type; `503` covers inconsistent or
1270
+ unavailable durable state, including missing lifecycle authorities and invalid
1271
+ release or delivery evidence, or an inconsistent capability registry. Other
1272
+ invalid input returns `400`, and unexpected failures reduce to `api.internal`
1273
+ with `500`. Clients may retry a transport failure or the exact signed change;
1274
+ they must never modify a request under the same event identity.
1275
+
1276
+ Premortem: schema-v1 ingress could bypass source ordering, a valid credential
1277
+ could enumerate another site's event, a timeout could duplicate locale work, a
1278
+ corrupt queue could look complete, or a status response could leak customer
1279
+ text. The v2-only endpoint, exact event/sequence idempotency, signed site and
1280
+ original-key scoping, durable planner identities, full result revalidation, and
1281
+ content-free responses keep those paths fail-closed.
1282
+
1283
+ Lifecycle premortem: a tenant could probe another site's release, queue success
1284
+ could be mistaken for approval, an expired signature could remain apparently
1285
+ ready, a corrupt outbox row could be skipped, or a status read could mutate a
1286
+ lease. Purpose-bound signed requests, original-credential scope, verified
1287
+ release readiness, complete signed-delivery revalidation, explicit blocked
1288
+ states, and read-only regression checks keep those paths fail-closed.
1289
+
1290
+ Capability premortem: an integration could pin a stale locale list, a valid
1291
+ signature could be replayed across purposes, a changed profile could retain an
1292
+ old digest, a duplicate language could displace another EU language, or a
1293
+ partial response could look authoritative. A separate fresh signed request,
1294
+ canonical whole-object digest, exact 24-language registry validation, unique
1295
+ locale/language/EU-code checks, and all-or-nothing response close those paths.
1296
+
1297
+ Cancellation premortem: a signed request could target another source revision,
1298
+ another accepted key could withdraw a tenant's event, a crash could leave a
1299
+ pending outbox active, or the endpoint could claim to retract a request already
1300
+ in flight. Exact immutable bindings, original-key scope, one transactional
1301
+ ledger/outbox transition, service eligibility filtering, and explicit leased
1302
+ and published conflicts keep those paths fail-closed.