blun-king-cli 9.1.597 → 9.1.600

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 (106) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.md +17 -1
  3. package/agent-spine-plugin/CHANGELOG.md +1 -1
  4. package/agent-spine-plugin/docs/host-integration.md +1 -15
  5. package/agent-spine-plugin/docs/preflight-recall.md +1 -1
  6. package/agent-spine-plugin/scripts/check-install.js +1 -6
  7. package/agent-spine-plugin/scripts/hermetic-process.js +8 -0
  8. package/agent-spine-plugin/scripts/release-check.js +2 -3
  9. package/agent-spine-plugin/scripts/run-tests-hermetic.js +8 -11
  10. package/agent-spine-plugin/src/hook.js +40 -40
  11. package/agent-spine-plugin/src/lib/briefing.js +6 -3
  12. package/agent-spine-plugin/src/lib/hook-context.js +63 -64
  13. package/agent-spine-plugin/src/lib/hook-output.js +115 -94
  14. package/agent-spine-plugin/src/lib/hook-timeline.js +20 -11
  15. package/agent-spine-plugin/src/lib/host-instruction-budget.js +8 -14
  16. package/agent-spine-plugin/src/lib/mcp-timeline-tools.js +0 -7
  17. package/agent-spine-plugin/src/lib/pre-answer-timeline-recall.js +104 -0
  18. package/agent-spine-plugin/src/lib/session-timeline-codex.js +0 -4
  19. package/agent-spine-plugin/src/lib/session-timeline-enrollment-source.js +0 -3
  20. package/agent-spine-plugin/src/lib/session-timeline-enrollment-storage.js +0 -4
  21. package/agent-spine-plugin/src/lib/session-timeline-enrollment-transport.js +0 -2
  22. package/agent-spine-plugin/src/lib/session-timeline-enrollment.js +0 -22
  23. package/agent-spine-plugin/src/lib/session-timeline-host-origin.js +0 -4
  24. package/agent-spine-plugin/src/lib/session-timeline-prior.js +42 -50
  25. package/agent-spine-plugin/src/lib/session-timeline-provider.js +0 -3
  26. package/agent-spine-plugin/src/lib/session-timeline-query.js +0 -1
  27. package/agent-spine-plugin/src/lib/session-timeline-sid-acl.js +0 -4
  28. package/agent-spine-plugin/src/lib/session-timeline-source.js +0 -3
  29. package/agent-spine-plugin/src/lib/session-timeline-transport.js +0 -8
  30. package/agent-spine-plugin/src/lib/session-timeline-windows-acl.js +0 -3
  31. package/agent-spine-plugin/src/lib/session-timeline.js +370 -408
  32. package/agent-spine-plugin/src/lib/source-roots.js +26 -16
  33. package/agent-spine-plugin/src/lib/timeline-user-feedback.js +0 -1
  34. package/bin/core-bootstrap.js +18 -10
  35. package/bin/launcher-mode.js +5 -0
  36. package/bin/launcher-runtime.js +27 -0
  37. package/bin/verify-bundled-agent-sources.cjs +28 -2
  38. package/blun.mjs +798 -143
  39. package/bundled-agent-sources.json +192 -155
  40. package/package.json +1 -1
  41. package/standard-skills/translate-native/README.md +1334 -1
  42. package/standard-skills/translate-native/SKILL.md +1 -1
  43. package/standard-skills/translate-native/VERSION +1 -1
  44. package/standard-skills/translate-native/docs/PREMORTEM.md +901 -0
  45. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION.md +208 -41
  46. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_API.md +1319 -77
  47. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_EVIDENCE_HTTP.md +66 -11
  48. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_HTTP_PROVIDER.md +19 -1
  49. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_RECEIPT_VERIFIER_HTTP.md +24 -6
  50. package/standard-skills/translate-native/integrations/commercial_localization_profile.py +23 -0
  51. package/standard-skills/translate-native/integrations/website_localization.py +234 -10
  52. package/standard-skills/translate-native/integrations/website_localization_benchmark.py +39 -3
  53. package/standard-skills/translate-native/integrations/website_localization_cms.py +512 -10
  54. package/standard-skills/translate-native/integrations/website_localization_cms_client.py +116 -0
  55. package/standard-skills/translate-native/integrations/website_localization_cms_receiver.py +101 -11
  56. package/standard-skills/translate-native/integrations/website_localization_cms_receiver_runtime.py +12 -1
  57. package/standard-skills/translate-native/integrations/website_localization_cms_receiver_store.py +186 -24
  58. package/standard-skills/translate-native/integrations/website_localization_cms_source_client.py +64 -1
  59. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery.py +528 -5
  60. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_auth.py +918 -0
  61. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_auth_runtime.py +609 -0
  62. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_client.py +885 -0
  63. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_http.py +1244 -0
  64. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_runtime.py +220 -0
  65. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_sidecar_adapter.py +331 -0
  66. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_capabilities_http.py +444 -0
  67. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_client.py +673 -0
  68. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_dispatch.py +1317 -0
  69. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_dispatch_client.py +826 -0
  70. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_dispatch_http.py +1152 -0
  71. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_dispatch_openapi.py +896 -0
  72. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_dispatch_runtime.py +621 -0
  73. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_http.py +1180 -0
  74. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_runtime.py +1479 -0
  75. package/standard-skills/translate-native/integrations/website_localization_cms_source_http.py +133 -17
  76. package/standard-skills/translate-native/integrations/website_localization_cms_source_runtime.py +319 -4
  77. package/standard-skills/translate-native/integrations/website_localization_cms_source_service.py +72 -2
  78. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_http.py +13 -0
  79. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_receiver.py +56 -13
  80. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_receiver_openapi.py +335 -0
  81. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_receiver_runtime.py +66 -1
  82. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_processing_monitor.py +73 -7
  83. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_receiver_client.py +93 -9
  84. package/standard-skills/translate-native/integrations/website_localization_evidence_http.py +221 -11
  85. package/standard-skills/translate-native/integrations/website_localization_health.py +6 -0
  86. package/standard-skills/translate-native/integrations/website_localization_http_provider.py +1 -1
  87. package/standard-skills/translate-native/integrations/website_localization_quality_profiles.py +350 -0
  88. package/standard-skills/translate-native/integrations/website_localization_queue.py +126 -56
  89. package/standard-skills/translate-native/integrations/website_localization_receipt_verifier_http.py +166 -13
  90. package/standard-skills/translate-native/integrations/website_localization_release.py +423 -17
  91. package/standard-skills/translate-native/integrations/website_localization_release_coordinator.py +168 -13
  92. package/standard-skills/translate-native/integrations/website_localization_runner.py +10 -0
  93. package/standard-skills/translate-native/integrations/website_localization_worker.py +213 -9
  94. package/standard-skills/translate-native/provenance.json +2 -2
  95. package/standard-skills/translate-native/references/commercial-localization.md +96 -17
  96. package/standard-skills/translate-native/scripts/blun_language_guard.py +3 -1
  97. package/standard-skills/translate-native/scripts/check_commercial_review.py +69 -9
  98. package/standard-skills/translate-native/scripts/commercial_localization_profile.py +829 -31
  99. package/standard-tools/language-guard/VERSION +1 -1
  100. package/standard-tools/language-guard/blun_language_guard.py +3 -1
  101. package/standard-tools/language-guard/check_commercial_review.py +69 -9
  102. package/standard-tools/language-guard/commercial_localization_profile.py +829 -31
  103. package/standard-tools/language-guard/provenance.json +2 -2
  104. package/standard-tools/manifest.json +1 -1
  105. package/worker-host.mjs +215 -82
  106. package/agent-spine-plugin/scripts/check-install-king.js +0 -37
@@ -19,13 +19,14 @@ from typing import Any, Callable, Mapping, Protocol
19
19
 
20
20
 
21
21
  SCHEMA = "blun.website-localization-release-coordinator.v1"
22
- EVIDENCE_REQUEST_SCHEMA = "blun.localization-quality-evidence-request.v5"
22
+ EVIDENCE_REQUEST_SCHEMA = "blun.localization-quality-evidence-request.v14"
23
23
  EVIDENCE_RESPONSE_SCHEMA = "blun.localization-quality-evidence-response.v2"
24
24
  INDEPENDENT_MODEL_REVIEW_SCHEMA = "blun.independent-model-review.v1"
25
25
  EVIDENCE_STATE_SCHEMA = "blun.localization-quality-evidence-state.v1"
26
26
  TOKEN = re.compile(r"^[A-Za-z0-9_.:-]{1,256}$")
27
27
  ERROR_CODE = re.compile(r"^[a-z][a-z0-9_.-]{0,127}$")
28
28
  SHA256 = re.compile(r"^[0-9a-f]{64}$")
29
+ REQUEST_ID = re.compile(r"^blun-l10n-evidence-[0-9a-f]{64}$")
29
30
  MAX_RECEIPT_LENGTH = 16_384
30
31
  MAX_ATTEMPTS = 20
31
32
  MAX_LEASE_SECONDS = 3_600
@@ -36,6 +37,18 @@ _EVIDENCE_COLUMNS = (
36
37
  "next_attempt_at", "lease_owner", "lease_token", "lease_expires_at",
37
38
  "last_error_code", "created_at", "updated_at",
38
39
  )
40
+ EVIDENCE_REQUEST_IDENTITY_FIELDS = (
41
+ "schema", "evidence_revision", "event_id", "plan_id", "job_id",
42
+ "result_sha256", "source_sha256", "target_sha256", "source_locale",
43
+ "target_locale", "content_type", "glossary_version", "policy_version",
44
+ "provider", "software_version", "review_confidence", "quality_profile",
45
+ "commercial_profile", "commercial_review", "commercial_review_routing",
46
+ "commercial_review_routing_contract_sha256",
47
+ "commercial_review_routing_contract",
48
+ "commercial_review_resolution_contract_sha256",
49
+ "commercial_review_resolution_contract", "human_review_required",
50
+ "independent_review_required",
51
+ )
39
52
 
40
53
 
41
54
  def _load_module(name: str, path: Path):
@@ -137,9 +150,14 @@ class QualityEvidenceRequest:
137
150
  source_text: str
138
151
  target_text: str
139
152
  review_confidence: dict[str, str]
140
- quality_profile: dict[str, str]
153
+ quality_profile: dict[str, Any]
141
154
  commercial_profile: str | None
142
155
  commercial_review: dict[str, Any] | None
156
+ commercial_review_routing: dict[str, Any] | None
157
+ commercial_review_routing_contract_sha256: str | None
158
+ commercial_review_routing_contract: dict[str, Any] | None
159
+ commercial_review_resolution_contract_sha256: str | None
160
+ commercial_review_resolution_contract: dict[str, Any] | None
143
161
  human_review_required: bool
144
162
  independent_review_required: bool
145
163
 
@@ -213,6 +231,107 @@ def _hash(value: str) -> str:
213
231
  return hashlib.sha256(value.encode("utf-8")).hexdigest()
214
232
 
215
233
 
234
+ def _request_id_for_payload(payload: dict[str, Any]) -> str:
235
+ identity = {
236
+ field: payload[field]
237
+ for field in EVIDENCE_REQUEST_IDENTITY_FIELDS
238
+ }
239
+ return "blun-l10n-evidence-" + _hash(_canonical_json(identity))
240
+
241
+
242
+ def _validated_request_payload(request: Any) -> dict[str, Any]:
243
+ if not isinstance(request, QualityEvidenceRequest):
244
+ raise LocalizationReleaseCoordinatorBlocked("evidence.request.invalid")
245
+ try:
246
+ payload = request.as_payload()
247
+ except Exception:
248
+ raise LocalizationReleaseCoordinatorBlocked("evidence.request.invalid") from None
249
+ if (
250
+ not isinstance(payload, dict)
251
+ or set(payload) != set(EVIDENCE_REQUEST_IDENTITY_FIELDS) | {
252
+ "request_id", "source_text", "target_text",
253
+ }
254
+ or payload.get("schema") != EVIDENCE_REQUEST_SCHEMA
255
+ or payload.get("request_id") != request.request_id
256
+ or not isinstance(request.request_id, str)
257
+ or REQUEST_ID.fullmatch(request.request_id) is None
258
+ or not isinstance(payload.get("source_text"), str)
259
+ or not isinstance(payload.get("target_text"), str)
260
+ or not isinstance(payload.get("source_sha256"), str)
261
+ or not isinstance(payload.get("target_sha256"), str)
262
+ or SHA256.fullmatch(payload["source_sha256"]) is None
263
+ or SHA256.fullmatch(payload["target_sha256"]) is None
264
+ or _hash(payload["source_text"]) != payload["source_sha256"]
265
+ or _hash(payload["target_text"]) != payload["target_sha256"]
266
+ ):
267
+ raise LocalizationReleaseCoordinatorBlocked("evidence.request.invalid")
268
+ if request.request_id != _request_id_for_payload(payload):
269
+ raise LocalizationReleaseCoordinatorBlocked(
270
+ "evidence.request_id.binding_mismatch",
271
+ )
272
+ commercial_review = payload["commercial_review"]
273
+ commercial_profile = payload["commercial_profile"]
274
+ try:
275
+ current_routing_contract = (
276
+ _CMS._COMMERCIAL.public_review_routing_contract(
277
+ commercial_profile,
278
+ )
279
+ if isinstance(commercial_profile, str)
280
+ and isinstance(commercial_review, dict)
281
+ else None
282
+ )
283
+ expected_routing_contract_sha256 = (
284
+ current_routing_contract["sha256"]
285
+ if current_routing_contract is not None
286
+ else None
287
+ )
288
+ except (KeyError, TypeError, ValueError):
289
+ raise LocalizationReleaseCoordinatorBlocked(
290
+ "evidence.request.invalid",
291
+ ) from None
292
+ expected_routing_contract = (
293
+ current_routing_contract
294
+ if isinstance(commercial_review, dict)
295
+ and commercial_review.get("status") == "review_required"
296
+ else None
297
+ )
298
+ if (
299
+ payload["commercial_review_routing_contract_sha256"]
300
+ != expected_routing_contract_sha256
301
+ or payload["commercial_review_routing_contract"]
302
+ != expected_routing_contract
303
+ or (
304
+ payload["commercial_review_routing"] is None
305
+ ) != (expected_routing_contract is None)
306
+ ):
307
+ raise LocalizationReleaseCoordinatorBlocked("evidence.request.invalid")
308
+ try:
309
+ expected_resolution_contract = (
310
+ _CMS._COMMERCIAL.public_review_resolution_contract(
311
+ commercial_profile,
312
+ )
313
+ if isinstance(commercial_review, dict)
314
+ and commercial_review.get("status") == "review_required"
315
+ else None
316
+ )
317
+ except (KeyError, TypeError, ValueError):
318
+ raise LocalizationReleaseCoordinatorBlocked(
319
+ "evidence.request.invalid",
320
+ ) from None
321
+ if (
322
+ payload["commercial_review_resolution_contract"]
323
+ != expected_resolution_contract
324
+ or payload["commercial_review_resolution_contract_sha256"]
325
+ != (
326
+ expected_resolution_contract["sha256"]
327
+ if expected_resolution_contract is not None
328
+ else None
329
+ )
330
+ ):
331
+ raise LocalizationReleaseCoordinatorBlocked("evidence.request.invalid")
332
+ return payload
333
+
334
+
216
335
  def _token(value: Any, code: str) -> str:
217
336
  if not isinstance(value, str) or TOKEN.fullmatch(value) is None:
218
337
  raise LocalizationReleaseCoordinatorBlocked(code)
@@ -309,15 +428,17 @@ class QualityEvidenceStateStore:
309
428
 
310
429
  @staticmethod
311
430
  def _binding(request: QualityEvidenceRequest) -> tuple[str, ...]:
312
- if not isinstance(request, QualityEvidenceRequest):
313
- raise LocalizationReleaseCoordinatorBlocked("evidence.request.invalid")
431
+ payload = _validated_request_payload(request)
314
432
  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"),
433
+ payload["request_id"],
434
+ _token(payload["event_id"], "coordinator.event_id.invalid"),
435
+ _token(payload["plan_id"], "evidence.plan_id.invalid"),
436
+ _token(payload["job_id"], "evidence.job_id.invalid"),
437
+ payload["result_sha256"],
438
+ _token(
439
+ payload["evidence_revision"],
440
+ "coordinator.evidence_revision.invalid",
441
+ ),
321
442
  )
322
443
  if not isinstance(values[4], str) or SHA256.fullmatch(values[4]) is None:
323
444
  raise LocalizationReleaseCoordinatorBlocked("evidence.result_sha256.invalid")
@@ -656,6 +777,25 @@ def _request(
656
777
  result_sha256: str,
657
778
  evidence_revision: str,
658
779
  ) -> QualityEvidenceRequest:
780
+ commercial_profile = job.as_payload().get("commercial_profile")
781
+ commercial_review = json.loads(_canonical_json(result["commercial_review"]))
782
+ commercial_review_routing = json.loads(_canonical_json(
783
+ result["commercial_review_routing"],
784
+ ))
785
+ routing_contract = (
786
+ _CMS._COMMERCIAL.public_review_routing_contract(commercial_profile)
787
+ if commercial_profile is not None
788
+ and commercial_review["status"] == "review_required"
789
+ else None
790
+ )
791
+ resolution_contract = (
792
+ _CMS._COMMERCIAL.public_review_resolution_contract(
793
+ commercial_profile,
794
+ )
795
+ if commercial_profile is not None
796
+ and commercial_review["status"] == "review_required"
797
+ else None
798
+ )
659
799
  binding = {
660
800
  "schema": EVIDENCE_REQUEST_SCHEMA,
661
801
  "evidence_revision": evidence_revision,
@@ -674,12 +814,24 @@ def _request(
674
814
  "software_version": result["software_version"],
675
815
  "review_confidence": json.loads(_canonical_json(result["review_confidence"])),
676
816
  "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"])),
817
+ "commercial_profile": commercial_profile,
818
+ "commercial_review": commercial_review,
819
+ "commercial_review_routing": commercial_review_routing,
820
+ "commercial_review_routing_contract_sha256": result[
821
+ "commercial_review_routing_contract_sha256"
822
+ ],
823
+ "commercial_review_routing_contract": routing_contract,
824
+ "commercial_review_resolution_contract_sha256": (
825
+ result["commercial_review_resolution_contract_sha256"]
826
+ if commercial_review is not None
827
+ and commercial_review["status"] == "review_required"
828
+ else None
829
+ ),
830
+ "commercial_review_resolution_contract": resolution_contract,
679
831
  "human_review_required": result["human_review_required"],
680
832
  "independent_review_required": result["independent_review_required"],
681
833
  }
682
- request_id = "blun-l10n-evidence-" + _hash(_canonical_json(binding))
834
+ request_id = _request_id_for_payload(binding)
683
835
  return QualityEvidenceRequest(
684
836
  **binding,
685
837
  request_id=request_id,
@@ -692,6 +844,7 @@ def _obtain_evidence(
692
844
  provider: Any,
693
845
  request: QualityEvidenceRequest,
694
846
  ) -> tuple[str, str | None, dict[str, Any] | None]:
847
+ _validated_request_payload(request)
695
848
  obtain = getattr(provider, "obtain", None)
696
849
  if not callable(obtain):
697
850
  raise LocalizationReleaseCoordinatorBlocked("evidence.provider.invalid")
@@ -949,6 +1102,8 @@ def run_next_release(
949
1102
  quality_receipt,
950
1103
  guarded_quality_verifier,
951
1104
  approval_authority,
1105
+ evidence_request_id=request.request_id,
1106
+ evidence_revision=request.evidence_revision,
952
1107
  now=approval_now,
953
1108
  ttl_seconds=approval_ttl_seconds,
954
1109
  human_review_receipt=human_receipt,
@@ -136,6 +136,15 @@ def _retry_delay(attempt: int, base: float, maximum: float) -> float:
136
136
  return min(maximum, base * (2 ** min(attempt - 1, 30)))
137
137
 
138
138
 
139
+ def _current_job_binding(payload: dict[str, Any]) -> bool:
140
+ """Return False only for an expected current-worker binding rejection."""
141
+ try:
142
+ _WORKER._validated_job(payload)
143
+ except _WORKER.LocalizationWorkerBlocked:
144
+ return False
145
+ return True
146
+
147
+
139
148
  def run_next_localization_job(
140
149
  queue: Any,
141
150
  worker_id: str,
@@ -177,6 +186,7 @@ def run_next_localization_job(
177
186
  claim_arguments = {
178
187
  "now": _now(clock),
179
188
  "lease_seconds": lease_seconds,
189
+ "binding_validator": _current_job_binding,
180
190
  }
181
191
  if eligible_plan_ids is not None:
182
192
  claim_arguments["eligible_plan_ids"] = eligible_plan_ids
@@ -20,10 +20,10 @@ from pathlib import Path
20
20
  from typing import Any, Callable, Mapping, Protocol
21
21
 
22
22
 
23
- WORKER_SCHEMA = "blun.website-localization-worker.v4"
23
+ WORKER_SCHEMA = "blun.website-localization-worker.v9"
24
24
  CANDIDATE_SCHEMA = "blun.website-localization-candidate.v1"
25
25
  REVIEW_SCHEMA = "blun.website-localization-review.v2"
26
- RESULT_SCHEMA = "blun.website-localization-result.v5"
26
+ RESULT_SCHEMA = "blun.website-localization-result.v10"
27
27
  MAX_TEXT_BYTES = 2_000_000
28
28
  MAX_FIELD_LENGTH = 2_000
29
29
  ERROR_CODE = re.compile(r"^[a-z][a-z0-9_.-]{0,127}$")
@@ -456,7 +456,7 @@ def _base_context(job: dict[str, Any], assets: LocalizationAssets) -> dict[str,
456
456
  }
457
457
  if any(quality_profile[name] != value for name, value in expected_quality.items()):
458
458
  raise LocalizationWorkerBlocked("quality_profile.binding_mismatch", retryable=False)
459
- return {
459
+ context = {
460
460
  "job_id": job["job_id"],
461
461
  "target": job["target"],
462
462
  "content_type": job["content_type"],
@@ -468,6 +468,122 @@ def _base_context(job: dict[str, Any], assets: LocalizationAssets) -> dict[str,
468
468
  "protected_terms": list(assets.protected_terms),
469
469
  "quality_profile": quality_profile,
470
470
  }
471
+ if job["content_type"] == "commercial":
472
+ commercial_quality = _PLANNER.commercial_quality_profile_for(
473
+ job["target"]["locale"]
474
+ )
475
+ if job.get("commercial_quality_profile") != commercial_quality:
476
+ raise LocalizationWorkerBlocked(
477
+ "commercial_quality_profile.binding_mismatch",
478
+ retryable=False,
479
+ )
480
+ context["commercial_quality_profile"] = commercial_quality
481
+ return context
482
+
483
+
484
+ def _commercial_review_evidence_contract(profile: str) -> dict[str, Any]:
485
+ """Resolve the exact public evidence contract before any provider access."""
486
+ try:
487
+ contract = _COMMERCIAL.public_review_evidence_contract(profile)
488
+ public_contract = _COMMERCIAL.public_profile(profile)[
489
+ "review_evidence_contract"
490
+ ]
491
+ if not isinstance(contract, dict) or not isinstance(
492
+ public_contract, dict
493
+ ):
494
+ raise TypeError
495
+ unsigned = dict(contract)
496
+ digest = unsigned.pop("sha256")
497
+ except (KeyError, TypeError, ValueError) as error:
498
+ raise LocalizationWorkerBlocked(
499
+ "commercial_review_evidence_contract.binding_mismatch",
500
+ retryable=False,
501
+ ) from error
502
+ if (
503
+ contract != public_contract
504
+ or contract.get("schema")
505
+ != _COMMERCIAL.REVIEW_EVIDENCE_CAPABILITIES_SCHEMA
506
+ or contract.get("result_schema") != profile
507
+ or contract.get("profile") != profile
508
+ or not isinstance(digest, str)
509
+ or len(digest) != 64
510
+ or digest != _hash_json(unsigned)
511
+ ):
512
+ raise LocalizationWorkerBlocked(
513
+ "commercial_review_evidence_contract.binding_mismatch",
514
+ retryable=False,
515
+ )
516
+ return json.loads(_canonical_json(contract))
517
+
518
+
519
+ def _commercial_review_routing_contract(profile: str) -> dict[str, Any]:
520
+ """Resolve the exact private-routing contract before provider access."""
521
+ try:
522
+ contract = _COMMERCIAL.public_review_routing_contract(profile)
523
+ public_contract = _COMMERCIAL.public_profile(profile)[
524
+ "review_routing_contract"
525
+ ]
526
+ if not isinstance(contract, dict) or not isinstance(
527
+ public_contract, dict
528
+ ):
529
+ raise TypeError
530
+ unsigned = dict(contract)
531
+ digest = unsigned.pop("sha256")
532
+ except (KeyError, TypeError, ValueError) as error:
533
+ raise LocalizationWorkerBlocked(
534
+ "commercial_review_routing_contract.binding_mismatch",
535
+ retryable=False,
536
+ ) from error
537
+ if (
538
+ contract != public_contract
539
+ or contract.get("schema")
540
+ != _COMMERCIAL.REVIEW_ROUTING_CAPABILITIES_SCHEMA
541
+ or contract.get("result_schema") != _COMMERCIAL.REVIEW_ROUTING_SCHEMA
542
+ or contract.get("profile") != profile
543
+ or not isinstance(digest, str)
544
+ or len(digest) != 64
545
+ or digest != _hash_json(unsigned)
546
+ ):
547
+ raise LocalizationWorkerBlocked(
548
+ "commercial_review_routing_contract.binding_mismatch",
549
+ retryable=False,
550
+ )
551
+ return json.loads(_canonical_json(contract))
552
+
553
+
554
+ def _commercial_review_resolution_contract(profile: str) -> dict[str, Any]:
555
+ """Resolve the exact public resolution contract before provider access."""
556
+ try:
557
+ contract = _COMMERCIAL.public_review_resolution_contract(profile)
558
+ public_contract = _COMMERCIAL.public_profile(profile)[
559
+ "review_resolution_contract"
560
+ ]
561
+ if not isinstance(contract, dict) or not isinstance(
562
+ public_contract, dict
563
+ ):
564
+ raise TypeError
565
+ unsigned = dict(contract)
566
+ digest = unsigned.pop("sha256")
567
+ except (KeyError, TypeError, ValueError) as error:
568
+ raise LocalizationWorkerBlocked(
569
+ "commercial_review_resolution_contract.binding_mismatch",
570
+ retryable=False,
571
+ ) from error
572
+ if (
573
+ contract != public_contract
574
+ or contract.get("schema")
575
+ != _COMMERCIAL.REVIEW_RESOLUTION_CAPABILITIES_SCHEMA
576
+ or contract.get("result_schema") != _COMMERCIAL.REVIEW_RESOLUTION_SCHEMA
577
+ or contract.get("profile") != profile
578
+ or not isinstance(digest, str)
579
+ or len(digest) != 64
580
+ or digest != _hash_json(unsigned)
581
+ ):
582
+ raise LocalizationWorkerBlocked(
583
+ "commercial_review_resolution_contract.binding_mismatch",
584
+ retryable=False,
585
+ )
586
+ return json.loads(_canonical_json(contract))
471
587
 
472
588
 
473
589
  def run_localization_job(
@@ -490,8 +606,44 @@ def run_localization_job(
490
606
  locale = job["target"]["locale"]
491
607
  base = _base_context(job, assets)
492
608
  commercial = job["content_type"] == "commercial"
609
+ commercial_evidence_contract = None
610
+ commercial_routing_contract = None
611
+ commercial_resolution_contract = None
493
612
  if commercial:
494
613
  base["commercial_profile"] = job["commercial_profile"]
614
+ commercial_evidence_contract = _commercial_review_evidence_contract(
615
+ job["commercial_profile"]
616
+ )
617
+ if (
618
+ job["commercial_review_evidence_contract_sha256"]
619
+ != commercial_evidence_contract["sha256"]
620
+ ):
621
+ raise LocalizationWorkerBlocked(
622
+ "commercial_review_evidence_contract.binding_mismatch",
623
+ retryable=False,
624
+ )
625
+ commercial_routing_contract = _commercial_review_routing_contract(
626
+ job["commercial_profile"]
627
+ )
628
+ if (
629
+ job["commercial_review_routing_contract_sha256"]
630
+ != commercial_routing_contract["sha256"]
631
+ ):
632
+ raise LocalizationWorkerBlocked(
633
+ "commercial_review_routing_contract.binding_mismatch",
634
+ retryable=False,
635
+ )
636
+ commercial_resolution_contract = _commercial_review_resolution_contract(
637
+ job["commercial_profile"]
638
+ )
639
+ if (
640
+ job["commercial_review_resolution_contract_sha256"]
641
+ != commercial_resolution_contract["sha256"]
642
+ ):
643
+ raise LocalizationWorkerBlocked(
644
+ "commercial_review_resolution_contract.binding_mismatch",
645
+ retryable=False,
646
+ )
495
647
  full_glossary = [asdict(term) for term in assets.glossary]
496
648
  target_terms = [
497
649
  {"target": term.target}
@@ -563,6 +715,15 @@ def run_localization_job(
563
715
  "source": job["source"],
564
716
  "candidate": candidate,
565
717
  "glossary": full_glossary,
718
+ **(
719
+ {
720
+ "commercial_review_evidence_contract": (
721
+ commercial_evidence_contract
722
+ ),
723
+ }
724
+ if commercial
725
+ else {}
726
+ ),
566
727
  "response_schema": {
567
728
  "schema": REVIEW_SCHEMA,
568
729
  "phase": "source_fidelity",
@@ -576,6 +737,7 @@ def run_localization_job(
576
737
  })
577
738
  fidelity_response, request_hash, response_hash = _invoke(provider, fidelity_request)
578
739
  commercial_summary = None
740
+ commercial_review_routing = None
579
741
  commercial_escalation_required = False
580
742
  if commercial:
581
743
  # Hash above binds the complete evidence, even though the ordinary
@@ -585,13 +747,36 @@ def run_localization_job(
585
747
  try:
586
748
  commercial_summary = _COMMERCIAL.validate_review(
587
749
  commercial_review, job["source"]["text"], candidate,
588
- job["commercial_profile"], allow_uncertain=True,
750
+ job["commercial_profile"],
751
+ target_locale=locale,
752
+ commercial_quality_profile_version=(
753
+ job["commercial_quality_profile"]["version"]
754
+ ),
755
+ commercial_quality_profile_sha256=(
756
+ job["commercial_quality_profile"]["sha256"]
757
+ ),
758
+ allow_uncertain=True,
589
759
  )
590
760
  except _COMMERCIAL.CommercialReviewBlocked as error:
591
761
  raise LocalizationWorkerBlocked(error.code, retryable=False) from None
592
762
  commercial_escalation_required = (
593
763
  commercial_summary["status"] == "review_required"
594
764
  )
765
+ if commercial_escalation_required:
766
+ try:
767
+ commercial_review_routing = (
768
+ _COMMERCIAL.review_routing_context(
769
+ commercial_review,
770
+ job["source"]["text"],
771
+ candidate,
772
+ commercial_summary,
773
+ job["commercial_profile"],
774
+ )
775
+ )
776
+ except _COMMERCIAL.CommercialReviewBlocked as error:
777
+ raise LocalizationWorkerBlocked(
778
+ error.code, retryable=False,
779
+ ) from None
595
780
  findings, fidelity_confidence = _review(
596
781
  fidelity_response,
597
782
  "source_fidelity",
@@ -625,6 +810,18 @@ def run_localization_job(
625
810
  )
626
811
  progress("integrity")
627
812
 
813
+ quality_result = {
814
+ "locale": locale,
815
+ "version": job["target"]["quality_profile_version"],
816
+ "sha256": job["target"]["quality_profile_sha256"],
817
+ }
818
+ if commercial:
819
+ quality_result["commercial"] = {
820
+ "profile": job["commercial_profile"],
821
+ "version": job["commercial_quality_profile"]["version"],
822
+ "sha256": job["commercial_quality_profile"]["sha256"],
823
+ }
824
+
628
825
  return {
629
826
  "schema": RESULT_SCHEMA,
630
827
  "worker_schema": WORKER_SCHEMA,
@@ -648,12 +845,19 @@ def run_localization_job(
648
845
  "target_native": native_confidence,
649
846
  "source_fidelity": fidelity_confidence,
650
847
  },
651
- "quality_profile": {
652
- "locale": locale,
653
- "version": job["target"]["quality_profile_version"],
654
- "sha256": job["target"]["quality_profile_sha256"],
655
- },
848
+ "quality_profile": quality_result,
656
849
  "commercial_review": commercial_summary,
850
+ "commercial_review_routing": commercial_review_routing,
851
+ "commercial_review_routing_contract_sha256": (
852
+ commercial_routing_contract["sha256"]
853
+ if commercial_routing_contract is not None
854
+ else None
855
+ ),
856
+ "commercial_review_resolution_contract_sha256": (
857
+ commercial_resolution_contract["sha256"]
858
+ if commercial_resolution_contract is not None
859
+ else None
860
+ ),
657
861
  "human_review_required": job["content_type"] == "legal",
658
862
  "independent_review_required": (
659
863
  job["content_type"] != "legal"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "translate-native",
3
- "version": "6.64.0",
4
- "sourceCommit": "d5d350ba61a5ea7fa4efb2244abcfc191452a33f",
3
+ "version": "6.155.0",
4
+ "sourceCommit": "21efc66da8f77992c8c63ac0f145a106c31e7eb2",
5
5
  "sourceRepository": "https://github.com/Maykbiletti/translate-native"
6
6
  }