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,1008 @@
1
+ #!/usr/bin/env python3
2
+ """Idempotent bridge from completed locale results to signed CMS delivery."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import hashlib
7
+ import importlib.util
8
+ import json
9
+ import re
10
+ import secrets
11
+ import sqlite3
12
+ import sys
13
+ import time
14
+ import unicodedata
15
+ from contextlib import contextmanager
16
+ from dataclasses import asdict, dataclass
17
+ from pathlib import Path
18
+ from typing import Any, Callable, Mapping, Protocol
19
+
20
+
21
+ SCHEMA = "blun.website-localization-release-coordinator.v1"
22
+ EVIDENCE_REQUEST_SCHEMA = "blun.localization-quality-evidence-request.v5"
23
+ EVIDENCE_RESPONSE_SCHEMA = "blun.localization-quality-evidence-response.v2"
24
+ INDEPENDENT_MODEL_REVIEW_SCHEMA = "blun.independent-model-review.v1"
25
+ EVIDENCE_STATE_SCHEMA = "blun.localization-quality-evidence-state.v1"
26
+ TOKEN = re.compile(r"^[A-Za-z0-9_.:-]{1,256}$")
27
+ ERROR_CODE = re.compile(r"^[a-z][a-z0-9_.-]{0,127}$")
28
+ SHA256 = re.compile(r"^[0-9a-f]{64}$")
29
+ MAX_RECEIPT_LENGTH = 16_384
30
+ MAX_ATTEMPTS = 20
31
+ MAX_LEASE_SECONDS = 3_600
32
+ EVIDENCE_STATES = {"pending", "leased", "retry_wait", "succeeded", "failed"}
33
+ _EVIDENCE_COLUMNS = (
34
+ "request_id", "event_id", "plan_id", "job_id", "result_sha256",
35
+ "evidence_revision", "status", "attempts", "max_attempts",
36
+ "next_attempt_at", "lease_owner", "lease_token", "lease_expires_at",
37
+ "last_error_code", "created_at", "updated_at",
38
+ )
39
+
40
+
41
+ def _load_module(name: str, path: Path):
42
+ spec = importlib.util.spec_from_file_location(name, path)
43
+ if spec is None or spec.loader is None:
44
+ raise RuntimeError(f"cannot load release coordinator dependency: {path.name}")
45
+ module = importlib.util.module_from_spec(spec)
46
+ sys.modules[spec.name] = module
47
+ spec.loader.exec_module(module)
48
+ return module
49
+
50
+
51
+ _ROOT = Path(__file__).resolve().parents[1]
52
+ _CMS = _load_module(
53
+ "blun_website_localization_release_coordinator_cms",
54
+ _ROOT / "integrations" / "website_localization_cms.py",
55
+ )
56
+
57
+
58
+ class LocalizationReleaseCoordinatorBlocked(RuntimeError):
59
+ """Stable failure that never contains source, target, receipt, or provider prose."""
60
+
61
+ def __init__(self, code: str, *, retryable: bool = False):
62
+ if not isinstance(code, str) or ERROR_CODE.fullmatch(code) is None:
63
+ raise ValueError("coordinator failure code is invalid")
64
+ if not isinstance(retryable, bool):
65
+ raise ValueError("coordinator retryability must be boolean")
66
+ super().__init__(code)
67
+ self.code = code
68
+ self.retryable = retryable
69
+
70
+
71
+ class QualityEvidenceUnavailable(RuntimeError):
72
+ """Adapter-declared, content-free evidence-provider failure."""
73
+
74
+ localization_evidence_failure = True
75
+
76
+ def __init__(self, code: str, *, retryable: bool):
77
+ if not isinstance(code, str) or ERROR_CODE.fullmatch(code) is None:
78
+ raise ValueError("evidence failure code is invalid")
79
+ if not isinstance(retryable, bool):
80
+ raise ValueError("evidence retryability must be boolean")
81
+ super().__init__(code)
82
+ self.code = code
83
+ self.retryable = retryable
84
+
85
+
86
+ class _ReceiptVerificationGuardUnavailable(RuntimeError):
87
+ localization_receipt_verification_failure = True
88
+
89
+ def __init__(self):
90
+ super().__init__("operation_guard")
91
+ self.code = "operation_guard"
92
+ self.retryable = True
93
+
94
+
95
+ class _GuardedReceiptVerifier:
96
+ def __init__(self, verifier: Any, guard: Callable[[float], Any], lease_seconds: float):
97
+ self.verifier = verifier
98
+ self.guard = guard
99
+ self.lease_seconds = lease_seconds
100
+
101
+ def verify(self, **values):
102
+ try:
103
+ self.guard(self.lease_seconds)
104
+ except Exception:
105
+ raise _ReceiptVerificationGuardUnavailable() from None
106
+ return self.verifier.verify(**values)
107
+
108
+
109
+ def _guarded_verifier(
110
+ verifier: Any | None,
111
+ operation_guard: Callable[[float], Any] | None,
112
+ lease_seconds: float,
113
+ ) -> Any | None:
114
+ if verifier is None or operation_guard is None:
115
+ return verifier
116
+ return _GuardedReceiptVerifier(verifier, operation_guard, lease_seconds)
117
+
118
+
119
+ @dataclass(frozen=True)
120
+ class QualityEvidenceRequest:
121
+ schema: str
122
+ request_id: str
123
+ evidence_revision: str
124
+ event_id: str
125
+ plan_id: str
126
+ job_id: str
127
+ result_sha256: str
128
+ source_sha256: str
129
+ target_sha256: str
130
+ source_locale: str
131
+ target_locale: str
132
+ content_type: str
133
+ glossary_version: str
134
+ policy_version: str
135
+ provider: dict[str, Any]
136
+ software_version: str
137
+ source_text: str
138
+ target_text: str
139
+ review_confidence: dict[str, str]
140
+ quality_profile: dict[str, str]
141
+ commercial_profile: str | None
142
+ commercial_review: dict[str, Any] | None
143
+ human_review_required: bool
144
+ independent_review_required: bool
145
+
146
+ def as_payload(self) -> dict[str, Any]:
147
+ return json.loads(_canonical_json(asdict(self)))
148
+
149
+
150
+ class QualityEvidenceProvider(Protocol):
151
+ def obtain(self, request: QualityEvidenceRequest) -> Mapping[str, Any]: ...
152
+
153
+
154
+ @dataclass(frozen=True)
155
+ class QualityEvidenceClaim:
156
+ request_id: str
157
+ lease_owner: str
158
+ lease_token: str
159
+ lease_expires_at: float
160
+ attempt: int
161
+ max_attempts: int
162
+
163
+
164
+ @dataclass(frozen=True)
165
+ class QualityEvidenceStatus:
166
+ schema: str
167
+ request_id: str
168
+ event_id: str
169
+ plan_id: str
170
+ job_id: str
171
+ result_sha256: str
172
+ evidence_revision: str
173
+ status: str
174
+ attempts: int
175
+ max_attempts: int
176
+ next_attempt_at: float
177
+ lease_expires_at: float | None
178
+ last_error_code: str | None
179
+
180
+ def as_payload(self) -> dict[str, Any]:
181
+ return asdict(self)
182
+
183
+
184
+ @dataclass(frozen=True)
185
+ class ReleaseCoordinatorOutcome:
186
+ schema: str
187
+ status: str
188
+ event_id: str
189
+ plan_id: str
190
+ job_id: str | None = None
191
+ target_locale: str | None = None
192
+ approval_id: str | None = None
193
+ delivery_id: str | None = None
194
+
195
+ def as_payload(self) -> dict[str, Any]:
196
+ return asdict(self)
197
+
198
+
199
+ def _canonical_json(value: Any) -> str:
200
+ try:
201
+ return json.dumps(
202
+ value,
203
+ ensure_ascii=False,
204
+ allow_nan=False,
205
+ sort_keys=True,
206
+ separators=(",", ":"),
207
+ )
208
+ except (TypeError, ValueError):
209
+ raise LocalizationReleaseCoordinatorBlocked("coordinator.json.invalid") from None
210
+
211
+
212
+ def _hash(value: str) -> str:
213
+ return hashlib.sha256(value.encode("utf-8")).hexdigest()
214
+
215
+
216
+ def _token(value: Any, code: str) -> str:
217
+ if not isinstance(value, str) or TOKEN.fullmatch(value) is None:
218
+ raise LocalizationReleaseCoordinatorBlocked(code)
219
+ return value
220
+
221
+
222
+ def _timestamp(value: Any) -> float:
223
+ if isinstance(value, bool) or not isinstance(value, (int, float)):
224
+ raise LocalizationReleaseCoordinatorBlocked("coordinator.time.invalid")
225
+ value = float(value)
226
+ if value < 0 or value != value or value in {float("inf"), float("-inf")}:
227
+ raise LocalizationReleaseCoordinatorBlocked("coordinator.time.invalid")
228
+ return value
229
+
230
+
231
+ def _positive_duration(value: Any, code: str, maximum: float) -> float:
232
+ value = _timestamp(value)
233
+ if value <= 0 or value > maximum:
234
+ raise LocalizationReleaseCoordinatorBlocked(code)
235
+ return value
236
+
237
+
238
+ def _attempt_limit(value: Any) -> int:
239
+ if isinstance(value, bool) or not isinstance(value, int) or not 1 <= value <= MAX_ATTEMPTS:
240
+ raise LocalizationReleaseCoordinatorBlocked("evidence.max_attempts.invalid")
241
+ return value
242
+
243
+
244
+ def _stored_integer(value: Any, code: str, *, minimum: int, maximum: int) -> int:
245
+ if isinstance(value, bool) or not isinstance(value, int) or not minimum <= value <= maximum:
246
+ raise LocalizationReleaseCoordinatorBlocked(code)
247
+ return value
248
+
249
+
250
+ @contextmanager
251
+ def _transaction(connection: sqlite3.Connection):
252
+ if connection.in_transaction:
253
+ raise LocalizationReleaseCoordinatorBlocked("evidence.state.transaction_active")
254
+ try:
255
+ connection.execute("BEGIN IMMEDIATE")
256
+ yield
257
+ connection.commit()
258
+ except Exception:
259
+ connection.rollback()
260
+ raise
261
+
262
+
263
+ class QualityEvidenceStateStore:
264
+ """Durable, content-free leases and bounded attempts for quality evidence."""
265
+
266
+ def __init__(self, connection: sqlite3.Connection):
267
+ if not isinstance(connection, sqlite3.Connection):
268
+ raise LocalizationReleaseCoordinatorBlocked("evidence.state.connection_invalid")
269
+ self.connection = connection
270
+ self.connection.row_factory = sqlite3.Row
271
+ with _transaction(self.connection):
272
+ self.connection.execute("""
273
+ CREATE TABLE IF NOT EXISTS localization_quality_evidence_state (
274
+ request_id TEXT PRIMARY KEY,
275
+ event_id TEXT NOT NULL,
276
+ plan_id TEXT NOT NULL,
277
+ job_id TEXT NOT NULL,
278
+ result_sha256 TEXT NOT NULL,
279
+ evidence_revision TEXT NOT NULL,
280
+ status TEXT NOT NULL CHECK (
281
+ status IN ('pending', 'leased', 'retry_wait', 'succeeded', 'failed')
282
+ ),
283
+ attempts INTEGER NOT NULL CHECK (attempts >= 0),
284
+ max_attempts INTEGER NOT NULL CHECK (max_attempts >= 1),
285
+ next_attempt_at REAL NOT NULL,
286
+ lease_owner TEXT,
287
+ lease_token TEXT,
288
+ lease_expires_at REAL,
289
+ last_error_code TEXT,
290
+ created_at REAL NOT NULL,
291
+ updated_at REAL NOT NULL,
292
+ UNIQUE (event_id, plan_id, job_id, result_sha256, evidence_revision)
293
+ )
294
+ """)
295
+ self.connection.execute("""
296
+ CREATE INDEX IF NOT EXISTS localization_quality_evidence_due
297
+ ON localization_quality_evidence_state (
298
+ event_id, status, next_attempt_at, created_at, request_id
299
+ )
300
+ """)
301
+ columns = tuple(
302
+ row["name"]
303
+ for row in self.connection.execute(
304
+ "PRAGMA table_info(localization_quality_evidence_state)"
305
+ )
306
+ )
307
+ if columns != _EVIDENCE_COLUMNS:
308
+ raise LocalizationReleaseCoordinatorBlocked("evidence.state.schema_altered")
309
+
310
+ @staticmethod
311
+ def _binding(request: QualityEvidenceRequest) -> tuple[str, ...]:
312
+ if not isinstance(request, QualityEvidenceRequest):
313
+ raise LocalizationReleaseCoordinatorBlocked("evidence.request.invalid")
314
+ values = (
315
+ _token(request.request_id, "evidence.request_id.invalid"),
316
+ _token(request.event_id, "coordinator.event_id.invalid"),
317
+ _token(request.plan_id, "evidence.plan_id.invalid"),
318
+ _token(request.job_id, "evidence.job_id.invalid"),
319
+ request.result_sha256,
320
+ _token(request.evidence_revision, "coordinator.evidence_revision.invalid"),
321
+ )
322
+ if not isinstance(values[4], str) or SHA256.fullmatch(values[4]) is None:
323
+ raise LocalizationReleaseCoordinatorBlocked("evidence.result_sha256.invalid")
324
+ return values
325
+
326
+ @staticmethod
327
+ def _claim_values(claim: Any) -> QualityEvidenceClaim:
328
+ if not isinstance(claim, QualityEvidenceClaim):
329
+ raise LocalizationReleaseCoordinatorBlocked("evidence.claim.invalid")
330
+ _token(claim.request_id, "evidence.request_id.invalid")
331
+ _token(claim.lease_owner, "evidence.worker_id.invalid")
332
+ _token(claim.lease_token, "evidence.lease_token.invalid")
333
+ return claim
334
+
335
+ @staticmethod
336
+ def _status_from_row(row: sqlite3.Row) -> QualityEvidenceStatus:
337
+ request_id = _token(row["request_id"], "evidence.state.invalid")
338
+ event_id = _token(row["event_id"], "evidence.state.invalid")
339
+ plan_id = _token(row["plan_id"], "evidence.state.invalid")
340
+ job_id = _token(row["job_id"], "evidence.state.invalid")
341
+ result_sha256 = row["result_sha256"]
342
+ if not isinstance(result_sha256, str) or SHA256.fullmatch(result_sha256) is None:
343
+ raise LocalizationReleaseCoordinatorBlocked("evidence.state.invalid")
344
+ evidence_revision = _token(row["evidence_revision"], "evidence.state.invalid")
345
+ status = row["status"]
346
+ if status not in EVIDENCE_STATES:
347
+ raise LocalizationReleaseCoordinatorBlocked("evidence.state.invalid")
348
+ max_attempts = _stored_integer(
349
+ row["max_attempts"], "evidence.state.invalid", minimum=1, maximum=MAX_ATTEMPTS,
350
+ )
351
+ attempts = _stored_integer(
352
+ row["attempts"], "evidence.state.invalid", minimum=0, maximum=max_attempts,
353
+ )
354
+ next_attempt_at = _timestamp(row["next_attempt_at"])
355
+ lease_values = (row["lease_owner"], row["lease_token"], row["lease_expires_at"])
356
+ if status == "leased":
357
+ lease_owner = _token(lease_values[0], "evidence.state.invalid")
358
+ lease_token = _token(lease_values[1], "evidence.state.invalid")
359
+ lease_expires_at = _timestamp(lease_values[2])
360
+ if lease_expires_at <= next_attempt_at:
361
+ raise LocalizationReleaseCoordinatorBlocked("evidence.state.invalid")
362
+ del lease_owner, lease_token
363
+ elif lease_values != (None, None, None):
364
+ raise LocalizationReleaseCoordinatorBlocked("evidence.state.invalid")
365
+ else:
366
+ lease_expires_at = None
367
+ error_code = row["last_error_code"]
368
+ if error_code is not None and (
369
+ not isinstance(error_code, str) or ERROR_CODE.fullmatch(error_code) is None
370
+ ):
371
+ raise LocalizationReleaseCoordinatorBlocked("evidence.state.invalid")
372
+ if status in {"pending", "leased", "succeeded"} and error_code is not None:
373
+ raise LocalizationReleaseCoordinatorBlocked("evidence.state.invalid")
374
+ _timestamp(row["created_at"])
375
+ _timestamp(row["updated_at"])
376
+ return QualityEvidenceStatus(
377
+ EVIDENCE_STATE_SCHEMA,
378
+ request_id,
379
+ event_id,
380
+ plan_id,
381
+ job_id,
382
+ result_sha256,
383
+ evidence_revision,
384
+ status,
385
+ attempts,
386
+ max_attempts,
387
+ next_attempt_at,
388
+ lease_expires_at,
389
+ error_code,
390
+ )
391
+
392
+ def claim(
393
+ self,
394
+ request: QualityEvidenceRequest,
395
+ *,
396
+ worker_id: Any,
397
+ now: float | int,
398
+ lease_seconds: float | int,
399
+ max_attempts: int,
400
+ ) -> QualityEvidenceClaim | None:
401
+ binding = self._binding(request)
402
+ worker_id = _token(worker_id, "evidence.worker_id.invalid")
403
+ now = _timestamp(now)
404
+ lease_seconds = _positive_duration(
405
+ lease_seconds, "evidence.lease.invalid", MAX_LEASE_SECONDS,
406
+ )
407
+ max_attempts = _attempt_limit(max_attempts)
408
+ blocked_code = None
409
+ claimed = None
410
+ with _transaction(self.connection):
411
+ row = self.connection.execute(
412
+ "SELECT * FROM localization_quality_evidence_state WHERE request_id = ?",
413
+ (request.request_id,),
414
+ ).fetchone()
415
+ if row is None:
416
+ self.connection.execute("""
417
+ INSERT INTO localization_quality_evidence_state (
418
+ request_id, event_id, plan_id, job_id, result_sha256,
419
+ evidence_revision, status, attempts, max_attempts,
420
+ next_attempt_at, created_at, updated_at
421
+ ) VALUES (?, ?, ?, ?, ?, ?, 'pending', 0, ?, ?, ?, ?)
422
+ """, (*binding, max_attempts, now, now, now))
423
+ row = self.connection.execute(
424
+ "SELECT * FROM localization_quality_evidence_state WHERE request_id = ?",
425
+ (request.request_id,),
426
+ ).fetchone()
427
+ self._status_from_row(row)
428
+ actual = tuple(row[name] for name in _EVIDENCE_COLUMNS[:6])
429
+ if actual != binding:
430
+ raise LocalizationReleaseCoordinatorBlocked("evidence.state.binding_mismatch")
431
+ if row["max_attempts"] != max_attempts:
432
+ raise LocalizationReleaseCoordinatorBlocked("evidence.state.attempt_policy_changed")
433
+ status = row["status"]
434
+ if status not in EVIDENCE_STATES:
435
+ raise LocalizationReleaseCoordinatorBlocked("evidence.state.invalid")
436
+ if status == "leased" and _timestamp(row["lease_expires_at"]) <= now:
437
+ terminal = row["attempts"] >= row["max_attempts"]
438
+ status = "failed" if terminal else "retry_wait"
439
+ self.connection.execute("""
440
+ UPDATE localization_quality_evidence_state
441
+ SET status = ?, next_attempt_at = ?, lease_owner = NULL,
442
+ lease_token = NULL, lease_expires_at = NULL,
443
+ last_error_code = 'lease_expired', updated_at = ?
444
+ WHERE request_id = ? AND status = 'leased'
445
+ """, (status, now, now, request.request_id))
446
+ if status == "succeeded":
447
+ blocked_code = "evidence.revision.already_consumed"
448
+ elif status == "failed":
449
+ blocked_code = "evidence.attempts_exhausted"
450
+ else:
451
+ row = self.connection.execute(
452
+ "SELECT * FROM localization_quality_evidence_state WHERE request_id = ?",
453
+ (request.request_id,),
454
+ ).fetchone()
455
+ if row["status"] != "leased" and _timestamp(row["next_attempt_at"]) <= now:
456
+ lease_token = secrets.token_urlsafe(32)
457
+ expires_at = now + lease_seconds
458
+ updated = self.connection.execute("""
459
+ UPDATE localization_quality_evidence_state
460
+ SET status = 'leased', attempts = attempts + 1,
461
+ lease_owner = ?, lease_token = ?, lease_expires_at = ?,
462
+ last_error_code = NULL, updated_at = ?
463
+ WHERE request_id = ? AND status IN ('pending', 'retry_wait')
464
+ AND next_attempt_at <= ? AND attempts < max_attempts
465
+ """, (
466
+ worker_id, lease_token, expires_at, now, request.request_id, now,
467
+ ))
468
+ if updated.rowcount != 1:
469
+ raise LocalizationReleaseCoordinatorBlocked(
470
+ "evidence.claim.lost", retryable=True,
471
+ )
472
+ claimed = QualityEvidenceClaim(
473
+ request.request_id,
474
+ worker_id,
475
+ lease_token,
476
+ expires_at,
477
+ row["attempts"] + 1,
478
+ row["max_attempts"],
479
+ )
480
+ if blocked_code is not None:
481
+ raise LocalizationReleaseCoordinatorBlocked(blocked_code)
482
+ return claimed
483
+
484
+ def _live_row(self, claim: Any, now: float) -> sqlite3.Row:
485
+ claim = self._claim_values(claim)
486
+ row = self.connection.execute(
487
+ "SELECT * FROM localization_quality_evidence_state WHERE request_id = ?",
488
+ (claim.request_id,),
489
+ ).fetchone()
490
+ if (
491
+ row is None or row["status"] != "leased"
492
+ or row["lease_owner"] != claim.lease_owner
493
+ or row["lease_token"] != claim.lease_token
494
+ ):
495
+ raise LocalizationReleaseCoordinatorBlocked("evidence.lease_lost")
496
+ if _timestamp(row["lease_expires_at"]) <= now:
497
+ raise LocalizationReleaseCoordinatorBlocked("evidence.lease_expired")
498
+ return row
499
+
500
+ def succeed(self, claim: QualityEvidenceClaim, *, now: float | int) -> None:
501
+ now = _timestamp(now)
502
+ with _transaction(self.connection):
503
+ row = self.connection.execute(
504
+ "SELECT * FROM localization_quality_evidence_state WHERE request_id = ?",
505
+ (self._claim_values(claim).request_id,),
506
+ ).fetchone()
507
+ if row is not None and row["status"] == "succeeded":
508
+ self._status_from_row(row)
509
+ return
510
+ self._live_row(claim, now)
511
+ updated = self.connection.execute("""
512
+ UPDATE localization_quality_evidence_state
513
+ SET status = 'succeeded', next_attempt_at = ?, lease_owner = NULL,
514
+ lease_token = NULL, lease_expires_at = NULL,
515
+ last_error_code = NULL, updated_at = ?
516
+ WHERE request_id = ? AND status = 'leased'
517
+ AND lease_owner = ? AND lease_token = ?
518
+ """, (
519
+ now, now, claim.request_id, claim.lease_owner, claim.lease_token,
520
+ ))
521
+ if updated.rowcount != 1:
522
+ raise LocalizationReleaseCoordinatorBlocked("evidence.finish_lost")
523
+
524
+ def fail(
525
+ self,
526
+ claim: QualityEvidenceClaim,
527
+ error: LocalizationReleaseCoordinatorBlocked,
528
+ *,
529
+ now: float | int,
530
+ ) -> None:
531
+ if not isinstance(error, LocalizationReleaseCoordinatorBlocked):
532
+ raise LocalizationReleaseCoordinatorBlocked("evidence.failure.invalid")
533
+ now = _timestamp(now)
534
+ with _transaction(self.connection):
535
+ current = self.connection.execute(
536
+ "SELECT * FROM localization_quality_evidence_state WHERE request_id = ?",
537
+ (self._claim_values(claim).request_id,),
538
+ ).fetchone()
539
+ if current is not None and current["status"] == "succeeded":
540
+ self._status_from_row(current)
541
+ return
542
+ row = self._live_row(claim, now)
543
+ terminal = not error.retryable or row["attempts"] >= row["max_attempts"]
544
+ status = "failed" if terminal else "retry_wait"
545
+ next_attempt = now if terminal else now + min(
546
+ 3_600.0, 5.0 * (2 ** (row["attempts"] - 1)),
547
+ )
548
+ updated = self.connection.execute("""
549
+ UPDATE localization_quality_evidence_state
550
+ SET status = ?, next_attempt_at = ?, lease_owner = NULL,
551
+ lease_token = NULL, lease_expires_at = NULL,
552
+ last_error_code = ?, updated_at = ?
553
+ WHERE request_id = ? AND status = 'leased'
554
+ AND lease_owner = ? AND lease_token = ?
555
+ """, (
556
+ status, next_attempt, error.code, now,
557
+ claim.request_id, claim.lease_owner, claim.lease_token,
558
+ ))
559
+ if updated.rowcount != 1:
560
+ raise LocalizationReleaseCoordinatorBlocked("evidence.finish_lost")
561
+
562
+ def mark_approved(
563
+ self,
564
+ event_id: Any,
565
+ plan_id: Any,
566
+ job_id: Any,
567
+ result_sha256: Any,
568
+ *,
569
+ now: float | int,
570
+ ) -> None:
571
+ event_id = _token(event_id, "coordinator.event_id.invalid")
572
+ plan_id = _token(plan_id, "evidence.plan_id.invalid")
573
+ job_id = _token(job_id, "evidence.job_id.invalid")
574
+ if not isinstance(result_sha256, str) or SHA256.fullmatch(result_sha256) is None:
575
+ raise LocalizationReleaseCoordinatorBlocked("evidence.result_sha256.invalid")
576
+ now = _timestamp(now)
577
+ with _transaction(self.connection):
578
+ rows = self.connection.execute("""
579
+ SELECT * FROM localization_quality_evidence_state
580
+ WHERE event_id = ? AND plan_id = ? AND job_id = ?
581
+ """, (event_id, plan_id, job_id)).fetchall()
582
+ for row in rows:
583
+ self._status_from_row(row)
584
+ if row["result_sha256"] != result_sha256:
585
+ raise LocalizationReleaseCoordinatorBlocked(
586
+ "evidence.state.binding_mismatch"
587
+ )
588
+ self.connection.execute("""
589
+ UPDATE localization_quality_evidence_state
590
+ SET status = 'succeeded', next_attempt_at = ?, lease_owner = NULL,
591
+ lease_token = NULL, lease_expires_at = NULL,
592
+ last_error_code = NULL, updated_at = ?
593
+ WHERE event_id = ? AND plan_id = ? AND job_id = ?
594
+ AND result_sha256 = ?
595
+ AND status != 'succeeded'
596
+ """, (now, now, event_id, plan_id, job_id, result_sha256))
597
+
598
+ def statuses(self, event_id: Any) -> tuple[QualityEvidenceStatus, ...]:
599
+ event_id = _token(event_id, "coordinator.event_id.invalid")
600
+ rows = self.connection.execute("""
601
+ SELECT * FROM localization_quality_evidence_state
602
+ WHERE event_id = ? ORDER BY created_at, request_id
603
+ """, (event_id,)).fetchall()
604
+ return tuple(self._status_from_row(row) for row in rows)
605
+
606
+
607
+ def _receipt(value: Any, code: str) -> str:
608
+ if (
609
+ not isinstance(value, str) or not value or value != value.strip()
610
+ or len(value) > MAX_RECEIPT_LENGTH or "\x00" in value
611
+ or not unicodedata.is_normalized("NFC", value)
612
+ ):
613
+ raise LocalizationReleaseCoordinatorBlocked(code)
614
+ return value
615
+
616
+
617
+ def _external_code(error: Exception, fallback: str) -> str:
618
+ code = getattr(error, "code", None)
619
+ if isinstance(code, str) and ERROR_CODE.fullmatch(code) is not None:
620
+ combined = code if code.startswith(fallback + ".") else fallback + "." + code
621
+ if len(combined) <= 128:
622
+ return combined
623
+ return fallback + ".failed"
624
+
625
+
626
+ def _load_event(bridge: Any, event_id: str, event_verifier: Any):
627
+ loader = getattr(bridge, "_load_event", None)
628
+ if not callable(loader):
629
+ raise LocalizationReleaseCoordinatorBlocked("coordinator.bridge.invalid")
630
+ try:
631
+ event, plan = loader(event_id, event_verifier)
632
+ except Exception as error:
633
+ raise LocalizationReleaseCoordinatorBlocked(_external_code(error, "cms")) from None
634
+ if not isinstance(event, dict) or not isinstance(getattr(plan, "jobs", None), tuple):
635
+ raise LocalizationReleaseCoordinatorBlocked("coordinator.bridge.invalid")
636
+ return event, plan
637
+
638
+
639
+ def _validated_result(store: Any, plan: Any, job: Any) -> tuple[dict[str, Any], str]:
640
+ validated_result = getattr(store, "validated_result", None)
641
+ if not callable(validated_result):
642
+ raise LocalizationReleaseCoordinatorBlocked("coordinator.release_store.invalid")
643
+ try:
644
+ result = validated_result(plan, job.job_id)
645
+ except Exception as error:
646
+ raise LocalizationReleaseCoordinatorBlocked(_external_code(error, "result")) from None
647
+ result_json = _canonical_json(result)
648
+ return result, _hash(result_json)
649
+
650
+
651
+ def _request(
652
+ event: dict[str, Any],
653
+ plan: Any,
654
+ job: Any,
655
+ result: dict[str, Any],
656
+ result_sha256: str,
657
+ evidence_revision: str,
658
+ ) -> QualityEvidenceRequest:
659
+ binding = {
660
+ "schema": EVIDENCE_REQUEST_SCHEMA,
661
+ "evidence_revision": evidence_revision,
662
+ "event_id": event["event_id"],
663
+ "plan_id": plan.plan_id,
664
+ "job_id": job.job_id,
665
+ "result_sha256": result_sha256,
666
+ "source_sha256": result["source_sha256"],
667
+ "target_sha256": result["target_sha256"],
668
+ "source_locale": result["source_locale"],
669
+ "target_locale": result["target_locale"],
670
+ "content_type": result["content_type"],
671
+ "glossary_version": result["glossary_version"],
672
+ "policy_version": result["policy_version"],
673
+ "provider": json.loads(_canonical_json(result["provider"])),
674
+ "software_version": result["software_version"],
675
+ "review_confidence": json.loads(_canonical_json(result["review_confidence"])),
676
+ "quality_profile": json.loads(_canonical_json(result["quality_profile"])),
677
+ "commercial_profile": job.as_payload().get("commercial_profile"),
678
+ "commercial_review": json.loads(_canonical_json(result["commercial_review"])),
679
+ "human_review_required": result["human_review_required"],
680
+ "independent_review_required": result["independent_review_required"],
681
+ }
682
+ request_id = "blun-l10n-evidence-" + _hash(_canonical_json(binding))
683
+ return QualityEvidenceRequest(
684
+ **binding,
685
+ request_id=request_id,
686
+ source_text=job.as_payload()["source"]["text"],
687
+ target_text=result["candidate"],
688
+ )
689
+
690
+
691
+ def _obtain_evidence(
692
+ provider: Any,
693
+ request: QualityEvidenceRequest,
694
+ ) -> tuple[str, str | None, dict[str, Any] | None]:
695
+ obtain = getattr(provider, "obtain", None)
696
+ if not callable(obtain):
697
+ raise LocalizationReleaseCoordinatorBlocked("evidence.provider.invalid")
698
+ request_hash = _hash(_canonical_json(request.as_payload()))
699
+ try:
700
+ response = obtain(request)
701
+ except Exception as error:
702
+ if getattr(type(error), "localization_evidence_failure", None) is True:
703
+ retryable = getattr(error, "retryable", None)
704
+ if isinstance(retryable, bool):
705
+ raise LocalizationReleaseCoordinatorBlocked(
706
+ _external_code(error, "evidence"), retryable=retryable,
707
+ ) from None
708
+ raise LocalizationReleaseCoordinatorBlocked("evidence.unavailable", retryable=True) from None
709
+ if _hash(_canonical_json(request.as_payload())) != request_hash:
710
+ raise LocalizationReleaseCoordinatorBlocked("evidence.request_mutated")
711
+ expected = {
712
+ "schema", "request_id", "result_sha256", "quality_receipt",
713
+ "human_review_receipt", "independent_model_review",
714
+ }
715
+ try:
716
+ if not isinstance(response, Mapping) or set(response) != expected:
717
+ raise ValueError
718
+ response = dict(response)
719
+ except Exception:
720
+ raise LocalizationReleaseCoordinatorBlocked("evidence.response.invalid") from None
721
+ if (
722
+ response["schema"] != EVIDENCE_RESPONSE_SCHEMA
723
+ or response["request_id"] != request.request_id
724
+ or response["result_sha256"] != request.result_sha256
725
+ ):
726
+ raise LocalizationReleaseCoordinatorBlocked("evidence.response.binding_mismatch")
727
+ quality_receipt = _receipt(response["quality_receipt"], "evidence.quality_receipt.invalid")
728
+ human_receipt = response["human_review_receipt"]
729
+ independent = response["independent_model_review"]
730
+ if not (request.human_review_required or request.independent_review_required):
731
+ if human_receipt is not None or independent is not None:
732
+ raise LocalizationReleaseCoordinatorBlocked("evidence.review_escalation.unexpected")
733
+ return quality_receipt, None, None
734
+ if request.human_review_required:
735
+ if independent is not None:
736
+ raise LocalizationReleaseCoordinatorBlocked("evidence.independent_model_review.legal_forbidden")
737
+ human_receipt = _receipt(human_receipt, "evidence.human_receipt.required")
738
+ return quality_receipt, human_receipt, None
739
+ if (human_receipt is None) == (independent is None):
740
+ raise LocalizationReleaseCoordinatorBlocked("evidence.review_escalation.required")
741
+ if human_receipt is not None:
742
+ return quality_receipt, _receipt(
743
+ human_receipt, "evidence.human_receipt.required",
744
+ ), None
745
+ if not isinstance(independent, Mapping) or set(independent) != {"schema", "provider", "receipt"}:
746
+ raise LocalizationReleaseCoordinatorBlocked("evidence.independent_model_review.invalid")
747
+ independent = dict(independent)
748
+ if independent["schema"] != INDEPENDENT_MODEL_REVIEW_SCHEMA:
749
+ raise LocalizationReleaseCoordinatorBlocked("evidence.independent_model_review.invalid")
750
+ reviewer = independent["provider"]
751
+ if not isinstance(reviewer, Mapping) or set(reviewer) != {"id", "model_id", "model_version"}:
752
+ raise LocalizationReleaseCoordinatorBlocked("evidence.independent_model_review.provider.invalid")
753
+ reviewer = {
754
+ name: _token(reviewer[name], "evidence.independent_model_review.provider.invalid")
755
+ for name in ("id", "model_id", "model_version")
756
+ }
757
+ if reviewer["id"] == request.provider["id"]:
758
+ raise LocalizationReleaseCoordinatorBlocked("evidence.independent_model_review.not_independent")
759
+ return quality_receipt, None, {
760
+ "schema": INDEPENDENT_MODEL_REVIEW_SCHEMA,
761
+ "provider": reviewer,
762
+ "receipt": _receipt(
763
+ independent["receipt"], "evidence.independent_model_review.receipt.required",
764
+ ),
765
+ }
766
+
767
+
768
+ def _outcome(status: str, event: dict[str, Any], plan: Any, **values) -> ReleaseCoordinatorOutcome:
769
+ return ReleaseCoordinatorOutcome(
770
+ schema=SCHEMA,
771
+ status=status,
772
+ event_id=event["event_id"],
773
+ plan_id=plan.plan_id,
774
+ **values,
775
+ )
776
+
777
+
778
+ def _existing_delivery(bridge: Any, event_id: str, authority: Any, now: float):
779
+ connection = getattr(bridge, "connection", None)
780
+ request_from_row = getattr(bridge, "_request_from_row", None)
781
+ if connection is None or not callable(request_from_row):
782
+ raise LocalizationReleaseCoordinatorBlocked("coordinator.bridge.invalid")
783
+ try:
784
+ row = connection.execute(
785
+ "SELECT * FROM cms_publication_deliveries WHERE event_id = ?",
786
+ (event_id,),
787
+ ).fetchone()
788
+ if row is None:
789
+ return None
790
+ status = row["status"]
791
+ if status not in {"pending", "leased", "retry_wait", "succeeded", "failed"}:
792
+ raise LocalizationReleaseCoordinatorBlocked("cms.delivery.invalid")
793
+ # A completed delivery is immutable history. A delivery that has not
794
+ # succeeded must still hold current approvals before being called ready.
795
+ request = request_from_row(row, authority, 0.0 if status == "succeeded" else now)
796
+ except Exception as error:
797
+ if isinstance(error, LocalizationReleaseCoordinatorBlocked):
798
+ raise
799
+ raise LocalizationReleaseCoordinatorBlocked(_external_code(error, "cms")) from None
800
+ return request, status
801
+
802
+
803
+ def run_next_release(
804
+ bridge: Any,
805
+ event_id: str,
806
+ event_verifier: Any,
807
+ evidence_provider: QualityEvidenceProvider,
808
+ quality_verifier: Any,
809
+ approval_authority: Any,
810
+ publication_authority: Any,
811
+ *,
812
+ evidence_state: QualityEvidenceStateStore,
813
+ evidence_revision: str,
814
+ evidence_worker_id: str,
815
+ evidence_lease_seconds: float | int = 300,
816
+ evidence_max_attempts: int = 5,
817
+ now: float | int | None = None,
818
+ approval_ttl_seconds: float | int = 2_592_000,
819
+ delivery_max_attempts: int = 5,
820
+ human_review_verifier: Any | None = None,
821
+ independent_model_review_verifier: Any | None = None,
822
+ operation_guard: Callable[[float], Any] | None = None,
823
+ clock: Callable[[], float] = time.time,
824
+ ) -> ReleaseCoordinatorOutcome:
825
+ """Approve at most one completed locale and prepare only a complete bundle."""
826
+
827
+ event_id = _token(event_id, "coordinator.event_id.invalid")
828
+ evidence_revision = _token(evidence_revision, "coordinator.evidence_revision.invalid")
829
+ if not isinstance(evidence_state, QualityEvidenceStateStore):
830
+ raise LocalizationReleaseCoordinatorBlocked("evidence.state.invalid")
831
+ if operation_guard is not None and not callable(operation_guard):
832
+ raise LocalizationReleaseCoordinatorBlocked("evidence.operation_guard.invalid")
833
+ fixed_now = now is not None
834
+ now = _timestamp(clock() if now is None else now)
835
+
836
+ def current_time() -> float:
837
+ return now if fixed_now else _timestamp(clock())
838
+
839
+ event, plan = _load_event(bridge, event_id, event_verifier)
840
+ queue = getattr(bridge, "queue", None)
841
+ store = getattr(bridge, "release_store", None)
842
+ if queue is None or store is None:
843
+ raise LocalizationReleaseCoordinatorBlocked("coordinator.bridge.invalid")
844
+ existing_delivery = _existing_delivery(bridge, event_id, publication_authority, now)
845
+ if existing_delivery is not None:
846
+ delivery, delivery_status = existing_delivery
847
+ if delivery_status == "failed":
848
+ raise LocalizationReleaseCoordinatorBlocked("cms.delivery.failed")
849
+ return _outcome(
850
+ "delivered" if delivery_status == "succeeded" else "delivery_ready",
851
+ event,
852
+ plan,
853
+ delivery_id=delivery.delivery_id,
854
+ )
855
+
856
+ selected = None
857
+ for job in sorted(plan.jobs, key=lambda item: item.as_payload()["target"]["locale"]):
858
+ try:
859
+ status = queue.status(job.job_id)
860
+ except Exception as error:
861
+ raise LocalizationReleaseCoordinatorBlocked(_external_code(error, "queue")) from None
862
+ if status.status != "succeeded":
863
+ continue
864
+ try:
865
+ store.lookup(plan, job.job_id, approval_authority, now=now)
866
+ except Exception as error:
867
+ code = getattr(error, "code", None)
868
+ if code not in {"approval.missing", "approval.expired"}:
869
+ raise LocalizationReleaseCoordinatorBlocked(_external_code(error, "approval")) from None
870
+ if selected is None:
871
+ selected = job
872
+ else:
873
+ _, approved_result_sha256 = _validated_result(store, plan, job)
874
+ evidence_state.mark_approved(
875
+ event["event_id"],
876
+ plan.plan_id,
877
+ job.job_id,
878
+ approved_result_sha256,
879
+ now=now,
880
+ )
881
+
882
+ if selected is None:
883
+ try:
884
+ readiness = store.readiness(plan, approval_authority, now=now)
885
+ except Exception as error:
886
+ raise LocalizationReleaseCoordinatorBlocked(_external_code(error, "approval")) from None
887
+ if not readiness.ready:
888
+ return _outcome("waiting", event, plan)
889
+ try:
890
+ delivery = bridge.prepare_delivery(
891
+ event_id,
892
+ event_verifier,
893
+ approval_authority,
894
+ publication_authority,
895
+ now=now,
896
+ max_attempts=delivery_max_attempts,
897
+ )
898
+ except Exception as error:
899
+ raise LocalizationReleaseCoordinatorBlocked(_external_code(error, "cms")) from None
900
+ return _outcome("delivery_ready", event, plan, delivery_id=delivery.delivery_id)
901
+
902
+ result, result_sha256 = _validated_result(store, plan, selected)
903
+ request = _request(event, plan, selected, result, result_sha256, evidence_revision)
904
+ claim = evidence_state.claim(
905
+ request,
906
+ worker_id=evidence_worker_id,
907
+ now=now,
908
+ lease_seconds=evidence_lease_seconds,
909
+ max_attempts=evidence_max_attempts,
910
+ )
911
+ if claim is None:
912
+ return _outcome(
913
+ "waiting",
914
+ event,
915
+ plan,
916
+ job_id=selected.job_id,
917
+ target_locale=request.target_locale,
918
+ )
919
+ if operation_guard is not None:
920
+ try:
921
+ operation_guard(float(evidence_lease_seconds))
922
+ except Exception:
923
+ raise LocalizationReleaseCoordinatorBlocked(
924
+ "evidence.operation_guard.failed",
925
+ ) from None
926
+ try:
927
+ quality_receipt, human_receipt, independent_review = _obtain_evidence(
928
+ evidence_provider, request,
929
+ )
930
+ except LocalizationReleaseCoordinatorBlocked as error:
931
+ evidence_state.fail(claim, error, now=current_time())
932
+ raise
933
+ approval_now = current_time()
934
+ guarded_quality_verifier = _guarded_verifier(
935
+ quality_verifier, operation_guard, float(evidence_lease_seconds),
936
+ )
937
+ guarded_human_verifier = _guarded_verifier(
938
+ human_review_verifier, operation_guard, float(evidence_lease_seconds),
939
+ )
940
+ guarded_independent_verifier = _guarded_verifier(
941
+ independent_model_review_verifier,
942
+ operation_guard,
943
+ float(evidence_lease_seconds),
944
+ )
945
+ try:
946
+ approved = store.approve(
947
+ plan,
948
+ selected.job_id,
949
+ quality_receipt,
950
+ guarded_quality_verifier,
951
+ approval_authority,
952
+ now=approval_now,
953
+ ttl_seconds=approval_ttl_seconds,
954
+ human_review_receipt=human_receipt,
955
+ human_review_verifier=(
956
+ guarded_human_verifier if human_receipt is not None else None
957
+ ),
958
+ independent_model_review=independent_review,
959
+ independent_model_review_verifier=(
960
+ guarded_independent_verifier if independent_review is not None else None
961
+ ),
962
+ )
963
+ except Exception as error:
964
+ code = _external_code(error, "release")
965
+ blocked = LocalizationReleaseCoordinatorBlocked(
966
+ code,
967
+ retryable=(
968
+ code == "release.approval.signing.failed"
969
+ or getattr(error, "retryable", False) is True
970
+ ),
971
+ )
972
+ evidence_state.fail(claim, blocked, now=current_time())
973
+ raise blocked from None
974
+ evidence_state.succeed(claim, now=current_time())
975
+
976
+ try:
977
+ readiness = store.readiness(plan, approval_authority, now=approval_now)
978
+ except Exception as error:
979
+ raise LocalizationReleaseCoordinatorBlocked(_external_code(error, "approval")) from None
980
+ if not readiness.ready:
981
+ return _outcome(
982
+ "approved",
983
+ event,
984
+ plan,
985
+ job_id=selected.job_id,
986
+ target_locale=request.target_locale,
987
+ approval_id=approved.approval_id,
988
+ )
989
+ try:
990
+ delivery = bridge.prepare_delivery(
991
+ event_id,
992
+ event_verifier,
993
+ approval_authority,
994
+ publication_authority,
995
+ now=current_time(),
996
+ max_attempts=delivery_max_attempts,
997
+ )
998
+ except Exception as error:
999
+ raise LocalizationReleaseCoordinatorBlocked(_external_code(error, "cms")) from None
1000
+ return _outcome(
1001
+ "delivery_ready",
1002
+ event,
1003
+ plan,
1004
+ job_id=selected.job_id,
1005
+ target_locale=request.target_locale,
1006
+ approval_id=approved.approval_id,
1007
+ delivery_id=delivery.delivery_id,
1008
+ )