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
@@ -9,14 +9,37 @@ from __future__ import annotations
9
9
 
10
10
  import hashlib
11
11
  import json
12
+ import re
12
13
  from typing import Any
13
14
 
14
15
 
15
- PUBLIC_PROFILE_SCHEMA = "translate-native.commercial-capabilities.v2"
16
+ PUBLIC_PROFILE_SCHEMA = "translate-native.commercial-capabilities.v15"
17
+ REVIEW_EVIDENCE_CAPABILITIES_SCHEMA = (
18
+ "translate-native.commercial-review-evidence-capabilities.v2"
19
+ )
16
20
  REVIEW_SUMMARY_CAPABILITIES_SCHEMA = (
17
- "translate-native.commercial-review-summary-capabilities.v1"
21
+ "translate-native.commercial-review-summary-capabilities.v7"
22
+ )
23
+ REVIEW_SUMMARY_SCHEMA = "translate-native.commercial-review-summary.v6"
24
+ REVIEW_ROUTING_CAPABILITIES_SCHEMA = (
25
+ "translate-native.commercial-review-routing-capabilities.v1"
26
+ )
27
+ REVIEW_ROUTING_SCHEMA = "translate-native.commercial-review-routing.v2"
28
+ EVIDENCE_BINDING_SCHEMA = "translate-native.commercial-review-evidence-binding.v5"
29
+ REVIEW_RESOLUTION_CAPABILITIES_SCHEMA = (
30
+ "translate-native.commercial-review-resolution-capabilities.v4"
31
+ )
32
+ REVIEW_RESOLUTION_SCHEMA = "translate-native.commercial-review-resolution.v4"
33
+ COMMERCIAL_LOCALE_PROFILE_SCHEMA = (
34
+ "translate-native.commercial-locale-quality-profile.v2"
18
35
  )
19
- REVIEW_SUMMARY_SCHEMA = "translate-native.commercial-review-summary.v1"
36
+ COMMERCIAL_RENDERING_REFERENCE_SCHEMA = (
37
+ "translate-native.commercial-rendering-reference.v1"
38
+ )
39
+ TARGET_LOCALE = re.compile(
40
+ r"^[a-z]{2,3}(?:-[A-Z][a-z]{3})?-[A-Z]{2}$"
41
+ )
42
+ PROFILE_VERSION = re.compile(r"^[A-Za-z0-9_.:-]{1,256}$")
20
43
 
21
44
  DIMENSIONS = {
22
45
  "amount_currency": "Amounts, currency identity, units and price-to-product association; no conversion or rounding.",
@@ -38,6 +61,44 @@ def _canonical_json(value: Any) -> bytes:
38
61
  ).encode("utf-8")
39
62
 
40
63
 
64
+ def _text_sha256(value: str) -> str:
65
+ return hashlib.sha256(value.encode("utf-8")).hexdigest()
66
+
67
+
68
+ def evidence_sha256(
69
+ value: Any,
70
+ source: str,
71
+ target: str,
72
+ profile: str,
73
+ *,
74
+ target_locale: str,
75
+ commercial_quality_profile_version: str,
76
+ commercial_quality_profile_sha256: str,
77
+ ) -> str:
78
+ """Bind evidence to exact texts, locale, and commercial quality generation."""
79
+ review_evidence_contract_sha256 = public_review_evidence_contract(
80
+ profile,
81
+ )["sha256"]
82
+ binding = {
83
+ "schema": EVIDENCE_BINDING_SCHEMA,
84
+ "profile": profile,
85
+ "review_evidence_contract_sha256": (
86
+ review_evidence_contract_sha256
87
+ ),
88
+ "target_locale": target_locale,
89
+ "commercial_quality_profile_version": (
90
+ commercial_quality_profile_version
91
+ ),
92
+ "commercial_quality_profile_sha256": (
93
+ commercial_quality_profile_sha256
94
+ ),
95
+ "source_sha256": _text_sha256(source),
96
+ "target_sha256": _text_sha256(target),
97
+ "evidence": value,
98
+ }
99
+ return hashlib.sha256(_canonical_json(binding)).hexdigest()
100
+
101
+
41
102
  def public_review_summary_contract(profile: str) -> dict[str, Any]:
42
103
  """Return the exact content-free summary contract external adapters consume."""
43
104
  body = {
@@ -46,12 +107,17 @@ def public_review_summary_contract(profile: str) -> dict[str, Any]:
46
107
  "profile": profile,
47
108
  "required_fields": [
48
109
  "schema", "profile", "status", "review_required_dimensions",
49
- "evidence_sha256",
110
+ "offer_count", "review_required_offers",
111
+ "review_evidence_contract_sha256", "evidence_sha256",
50
112
  ],
51
113
  "statuses": {
52
- "verified": {"review_required_dimensions": "empty"},
114
+ "verified": {
115
+ "review_required_dimensions": "empty",
116
+ "review_required_offers": "empty",
117
+ },
53
118
  "review_required": {
54
119
  "review_required_dimensions": "one-or-more",
120
+ "review_required_offers": "zero-or-more",
55
121
  "requires_independent_review": True,
56
122
  },
57
123
  },
@@ -60,10 +126,51 @@ def public_review_summary_contract(profile: str) -> dict[str, Any]:
60
126
  "order": list(DIMENSIONS),
61
127
  "unique": True,
62
128
  },
129
+ "review_required_offers": {
130
+ "item_required_fields": ["dimension", "offer_indexes"],
131
+ "dimension_order": list(DIMENSIONS),
132
+ "dimension_must_be_review_required": True,
133
+ "offer_indexes": {
134
+ "meaning": "zero-based-opaque-offer-registry-position",
135
+ "minimum": 0,
136
+ "maximum_exclusive": 1000,
137
+ "order": "ascending",
138
+ "unique": True,
139
+ },
140
+ "configured_offer_identifiers_published": False,
141
+ },
142
+ "offer_count": {
143
+ "meaning": "opaque-offer-registry-size",
144
+ "minimum": 0,
145
+ "maximum": 1000,
146
+ },
63
147
  "evidence_sha256": {
64
148
  "algorithm": "sha-256",
65
149
  "canonicalization": "utf-8-json-sort-keys-no-insignificant-whitespace",
66
- "covers": "complete-commercial-review-evidence",
150
+ "binding_schema": EVIDENCE_BINDING_SCHEMA,
151
+ "binding_fields": [
152
+ "schema", "profile", "review_evidence_contract_sha256",
153
+ "target_locale",
154
+ "commercial_quality_profile_version",
155
+ "commercial_quality_profile_sha256", "source_sha256",
156
+ "target_sha256", "evidence",
157
+ ],
158
+ "text_hashing": "exact-utf-8",
159
+ "covers": [
160
+ "commercial-profile",
161
+ "exact-review-evidence-contract",
162
+ "exact-target-locale",
163
+ "commercial-quality-profile-generation",
164
+ "exact-source-sha256",
165
+ "exact-target-sha256",
166
+ "offer-registry-and-proposition-assignment",
167
+ "complete-commercial-review-evidence",
168
+ ],
169
+ },
170
+ "review_evidence_contract_sha256": {
171
+ "algorithm": "sha-256",
172
+ "equals": public_review_evidence_contract(profile)["sha256"],
173
+ "purpose": "reject-stale-or-reinterpreted-private-evidence",
67
174
  },
68
175
  "content_policy": {
69
176
  "source_text": False,
@@ -81,13 +188,335 @@ def public_review_summary_contract(profile: str) -> dict[str, Any]:
81
188
  }
82
189
 
83
190
 
191
+ def public_review_evidence_contract(profile: str) -> dict[str, Any]:
192
+ """Return the exact public contract for private commercial evidence."""
193
+ body = {
194
+ "schema": REVIEW_EVIDENCE_CAPABILITIES_SCHEMA,
195
+ "result_schema": profile,
196
+ "profile": profile,
197
+ "required_fields": ["schema", "coverage", "offers", "checks"],
198
+ "coverage": {
199
+ "allowed": ["complete", "uncertain"],
200
+ "complete": "every-proposition-and-offer-association-reviewed",
201
+ "uncertain": "independent-review-required",
202
+ },
203
+ "offer_registry": {
204
+ "field": "offers",
205
+ "max_items": 1000,
206
+ "item_required_fields": [
207
+ "id", "source_spans", "target_spans",
208
+ ],
209
+ "identifier": {
210
+ "pattern": PROFILE_VERSION.pattern,
211
+ "unique": True,
212
+ },
213
+ "regions": {
214
+ "fields": ["source_spans", "target_spans"],
215
+ "span_format": (
216
+ "zero-based-unicode-code-points-exclusive-end"
217
+ ),
218
+ "non_empty_text": True,
219
+ "ordered": True,
220
+ "overlap": "forbidden-within-and-across-offers",
221
+ "discontiguous": True,
222
+ "at_least_one_side_non_empty": True,
223
+ },
224
+ },
225
+ "checks": {
226
+ "required_dimensions": list(DIMENSIONS),
227
+ "exact_dimension_set": True,
228
+ "max_items_per_dimension": 1000,
229
+ "statuses": [
230
+ "equivalent", "not_present", "changed", "uncertain",
231
+ ],
232
+ "status_items": {
233
+ "equivalent": "one-or-more-matched",
234
+ "not_present": "empty",
235
+ "changed": "one-or-more-specific",
236
+ "uncertain": "one-or-more-specific",
237
+ },
238
+ "offer_statuses": {
239
+ "field": "offer_statuses",
240
+ "item_required_fields": ["offer", "status"],
241
+ "coverage": "exactly-one-per-registered-offer",
242
+ "order": "offer-registry-order",
243
+ "statuses": [
244
+ "equivalent", "not_present", "changed", "uncertain",
245
+ ],
246
+ "global_status": (
247
+ "changed-then-uncertain-then-equivalent-then-not_present"
248
+ ),
249
+ "items_must_match_offer_status": True,
250
+ },
251
+ "item": {
252
+ "required_fields": [
253
+ "offer", "relation", "source_span", "target_span",
254
+ "explanation",
255
+ ],
256
+ "offer": "registered-offer-id",
257
+ "relations": ["matched", "source_only", "target_only"],
258
+ "matched_requires": "source-and-target-spans",
259
+ "source_only_requires": "source-span-and-null-target-span",
260
+ "target_only_requires": "null-source-span-and-target-span",
261
+ "span_containment": "inside-named-offer-region",
262
+ "duplicates": "forbidden-per-dimension",
263
+ "explanation": "non-empty-maximum-2000-code-points",
264
+ },
265
+ "offer_assignment": {
266
+ "equivalent": (
267
+ "exactly-one-matched-item-per-registered-offer"
268
+ ),
269
+ "other_equivalent_checks_require_equivalent_assignment": True,
270
+ },
271
+ },
272
+ "trust_boundary": {
273
+ "validates": "structure-offsets-and-verdict-consistency",
274
+ "semantic_truth": False,
275
+ "numeric_regex_semantic_proof": False,
276
+ "unresolved_route": (
277
+ "independent-model-or-qualified-native-domain-review"
278
+ ),
279
+ "publication_authority": False,
280
+ },
281
+ "content_policy": {
282
+ "source_text": False,
283
+ "target_text": False,
284
+ "source_spans": False,
285
+ "target_spans": False,
286
+ "reviewer_prose": False,
287
+ "project_prices": False,
288
+ "project_brands": False,
289
+ },
290
+ }
291
+ return {
292
+ **body,
293
+ "sha256": hashlib.sha256(_canonical_json(body)).hexdigest(),
294
+ }
295
+
296
+
297
+ def public_review_routing_contract(profile: str) -> dict[str, Any]:
298
+ """Return the exact public contract for private actionable offer routes."""
299
+ body = {
300
+ "schema": REVIEW_ROUTING_CAPABILITIES_SCHEMA,
301
+ "result_schema": REVIEW_ROUTING_SCHEMA,
302
+ "profile": profile,
303
+ "applies_when": {
304
+ "review_summary_status": "review_required",
305
+ "offer_count": "exact-review-summary-offer-count",
306
+ },
307
+ "required_fields": [
308
+ "schema", "profile", "contract_sha256", "offer_count",
309
+ "source_length", "target_length", "offers",
310
+ ],
311
+ "text_lengths": {
312
+ "fields": ["source_length", "target_length"],
313
+ "unit": "unicode-code-points",
314
+ "must_equal_complete_texts": True,
315
+ },
316
+ "offers": {
317
+ "coverage": "exactly-one-per-registered-offer",
318
+ "order": "offer-registry-order",
319
+ "item_required_fields": [
320
+ "offer_index", "source_spans", "target_spans",
321
+ ],
322
+ "offer_index": {
323
+ "meaning": "zero-based-opaque-offer-registry-position",
324
+ "minimum": 0,
325
+ "maximum_exclusive": 1000,
326
+ "order": "ascending",
327
+ "unique": True,
328
+ },
329
+ "regions": {
330
+ "fields": ["source_spans", "target_spans"],
331
+ "span_format": (
332
+ "zero-based-unicode-code-points-exclusive-end"
333
+ ),
334
+ "non_empty_text": True,
335
+ "ordered": True,
336
+ "overlap": "forbidden-within-and-across-offers",
337
+ "discontiguous": True,
338
+ "at_least_one_side_non_empty": True,
339
+ },
340
+ },
341
+ "trust_boundary": {
342
+ "validates": "shape-offsets-order-count-and-text-lengths",
343
+ "semantic_truth": False,
344
+ "route_values": "private-evidence-and-receipt-boundary-only",
345
+ "public_release_evidence": False,
346
+ "publication_authority": False,
347
+ },
348
+ "content_policy": {
349
+ "configured_offer_identifiers": False,
350
+ "source_text": False,
351
+ "target_text": False,
352
+ "reviewer_prose": False,
353
+ "project_prices": False,
354
+ "project_brands": False,
355
+ },
356
+ }
357
+ return {
358
+ **body,
359
+ "sha256": hashlib.sha256(_canonical_json(body)).hexdigest(),
360
+ }
361
+
362
+
363
+ def public_review_resolution_contract(profile: str) -> dict[str, Any]:
364
+ """Return the exact content-free contract for resolving uncertain checks."""
365
+ body = {
366
+ "schema": REVIEW_RESOLUTION_CAPABILITIES_SCHEMA,
367
+ "result_schema": REVIEW_RESOLUTION_SCHEMA,
368
+ "profile": profile,
369
+ "applies_when": {
370
+ "review_summary_status": "review_required",
371
+ "reviewed_dimensions": "exact-ordered-review-summary-dimensions",
372
+ "reviewed_offer_count": "exact-review-summary-offer-count",
373
+ "reviewed_offers": "exact-ordered-review-summary-offer-scope",
374
+ },
375
+ "required_fields": [
376
+ "schema", "profile", "contract_sha256", "status",
377
+ "reviewed_dimensions", "reviewed_offer_count", "reviewed_offers",
378
+ "method", "receipt_sha256",
379
+ "primary_provider", "provider",
380
+ ],
381
+ "status": "resolved",
382
+ "methods": {
383
+ "qualified_human": {
384
+ "primary_provider": "required",
385
+ "provider": "null",
386
+ "receipt": "verified-qualified-human-review",
387
+ },
388
+ "independent_model": {
389
+ "primary_provider": "required",
390
+ "provider": "required",
391
+ "provider_id_must_differ_from_primary_provider": True,
392
+ "receipt": "verified-independent-model-review",
393
+ },
394
+ },
395
+ "provider_identity": {
396
+ "fields": ["id", "model_id", "model_version"],
397
+ "primary_provider": "required",
398
+ "credentials_published": False,
399
+ },
400
+ "reviewed_dimensions": {
401
+ "allowed": list(DIMENSIONS),
402
+ "order": list(DIMENSIONS),
403
+ "unique": True,
404
+ "must_equal_review_summary": True,
405
+ },
406
+ "reviewed_offers": {
407
+ "must_equal_review_summary": True,
408
+ "configured_offer_identifiers_published": False,
409
+ },
410
+ "reviewed_offer_count": {
411
+ "must_equal_review_summary": True,
412
+ },
413
+ "receipt_sha256": {
414
+ "algorithm": "sha-256",
415
+ "covers": "exact-verified-review-receipt",
416
+ "raw_receipt_published": False,
417
+ },
418
+ "content_policy": {
419
+ "source_text": False,
420
+ "target_text": False,
421
+ "raw_receipt": False,
422
+ "reviewer_prose": False,
423
+ "qualified_human_identity": False,
424
+ "project_prices": False,
425
+ "project_brands": False,
426
+ },
427
+ }
428
+ return {
429
+ **body,
430
+ "sha256": hashlib.sha256(_canonical_json(body)).hexdigest(),
431
+ }
432
+
433
+
434
+ def _resolution_provider(value: Any) -> dict[str, str] | None:
435
+ if value is None:
436
+ return None
437
+ if not isinstance(value, dict) or set(value) != {
438
+ "id", "model_id", "model_version",
439
+ }:
440
+ raise CommercialReviewBlocked("review.commercial.resolution_invalid")
441
+ if any(
442
+ not isinstance(value.get(field), str)
443
+ or PROFILE_VERSION.fullmatch(value[field]) is None
444
+ for field in value
445
+ ):
446
+ raise CommercialReviewBlocked("review.commercial.resolution_invalid")
447
+ return json.loads(_canonical_json(value))
448
+
449
+
450
+ def validate_review_resolution(
451
+ value: Any,
452
+ summary: Any,
453
+ profile: str,
454
+ ) -> dict[str, Any]:
455
+ """Validate exact review-resolution routing without trusting reviewer prose."""
456
+ summary = validate_summary(summary, profile, review_required=True)
457
+ contract = public_review_resolution_contract(profile)
458
+ if (
459
+ not isinstance(value, dict)
460
+ or set(value) != {
461
+ "schema", "profile", "contract_sha256", "status",
462
+ "reviewed_dimensions", "reviewed_offer_count", "reviewed_offers",
463
+ "method", "receipt_sha256",
464
+ "primary_provider", "provider",
465
+ }
466
+ or value.get("schema") != REVIEW_RESOLUTION_SCHEMA
467
+ or value.get("profile") != profile
468
+ or value.get("contract_sha256") != contract["sha256"]
469
+ or value.get("status") != "resolved"
470
+ or value.get("reviewed_dimensions")
471
+ != summary["review_required_dimensions"]
472
+ or value.get("reviewed_offer_count") != summary["offer_count"]
473
+ or value.get("reviewed_offers")
474
+ != summary["review_required_offers"]
475
+ or value.get("method") not in {
476
+ "qualified_human", "independent_model",
477
+ }
478
+ or not isinstance(value.get("receipt_sha256"), str)
479
+ or len(value["receipt_sha256"]) != 64
480
+ or any(
481
+ character not in "0123456789abcdef"
482
+ for character in value["receipt_sha256"]
483
+ )
484
+ ):
485
+ raise CommercialReviewBlocked("review.commercial.resolution_invalid")
486
+ primary_provider = _resolution_provider(value.get("primary_provider"))
487
+ provider = _resolution_provider(value.get("provider"))
488
+ if primary_provider is None or (
489
+ value["method"] == "qualified_human" and provider is not None
490
+ ) or (
491
+ value["method"] == "independent_model"
492
+ and (
493
+ provider is None
494
+ or provider["id"] == primary_provider["id"]
495
+ )
496
+ ):
497
+ raise CommercialReviewBlocked("review.commercial.resolution_invalid")
498
+ return json.loads(_canonical_json({
499
+ **value,
500
+ "primary_provider": primary_provider,
501
+ "provider": provider,
502
+ }))
503
+
504
+
84
505
  def public_profile(profile: str) -> dict[str, Any]:
85
506
  """Return the public, brand-neutral contract implemented by this module."""
86
507
  body = {
87
508
  "schema": PUBLIC_PROFILE_SCHEMA,
88
509
  "profile": profile,
510
+ "review_evidence_schema": profile,
511
+ "review_evidence_contract": public_review_evidence_contract(profile),
89
512
  "review_summary_schema": REVIEW_SUMMARY_SCHEMA,
90
513
  "review_summary_contract": public_review_summary_contract(profile),
514
+ "review_routing_schema": REVIEW_ROUTING_SCHEMA,
515
+ "review_routing_contract": public_review_routing_contract(profile),
516
+ "review_resolution_schema": REVIEW_RESOLUTION_SCHEMA,
517
+ "review_resolution_contract": public_review_resolution_contract(
518
+ profile,
519
+ ),
91
520
  "applies_to": {
92
521
  "content_type": "commercial",
93
522
  "locales": "all-supported-target-locales",
@@ -124,11 +553,43 @@ def public_profile(profile: str) -> dict[str, Any]:
124
553
  "method": "semantic-provider-evidence",
125
554
  "deterministic_numeric_regex_is_sufficient": False,
126
555
  "evidence_granularity": "every-proposition-per-offer",
556
+ "offer_registry": {
557
+ "identifiers": "unique",
558
+ "source_and_target_regions": "ordered-non-overlapping",
559
+ "discontiguous_regions_allowed": True,
560
+ "every_proposition_contained_in_declared_offer": True,
561
+ "every_offer_has_exactly_one_assignment_item": True,
562
+ "every_dimension_has_exactly_one_status_per_offer": True,
563
+ },
127
564
  "directions": ["matched", "source_only", "target_only"],
128
565
  "ambiguous_values": "unresolved",
129
566
  "unresolved_route": "independent-model-or-qualified-native-domain-review",
130
567
  "automatic_publication_when_unresolved": False,
131
568
  },
569
+ "locale_quality_profile": {
570
+ "schema": COMMERCIAL_LOCALE_PROFILE_SCHEMA,
571
+ "required": True,
572
+ "binding_fields": [
573
+ "locale", "version", "commercial_profile",
574
+ "quality_profile_version", "quality_profile_sha256",
575
+ "rendering_reference", "sha256",
576
+ ],
577
+ "rendering_reference": {
578
+ "schema": COMMERCIAL_RENDERING_REFERENCE_SCHEMA,
579
+ "authority": "Unicode CLDR",
580
+ "version": "48",
581
+ "purpose": "target-locale-rendering-guidance",
582
+ "semantic_proof": False,
583
+ "unresolved_route": (
584
+ "independent-model-or-qualified-native-domain-review"
585
+ ),
586
+ },
587
+ "required_commercial_checks": list(DIMENSIONS),
588
+ "provider_phases": [
589
+ "transcreation", "target_native", "source_fidelity",
590
+ ],
591
+ "tamper_policy": "block-before-provider",
592
+ },
132
593
  "protected_terms": "project-configuration-only",
133
594
  }
134
595
  return {
@@ -153,8 +614,10 @@ all offers, headings, footnotes, links and conditions. Record every applicable p
153
614
  offer label, semantic explanation and relation: 'matched', 'source_only' for an omission, or 'target_only' for an
154
615
  addition. Give exact zero-based Python Unicode code-point spans with an exclusive end; the absent side of a one-sided
155
616
  item MUST be null. Repeated amounts must stay attached to their own offer; number multisets do not prove fidelity.
617
+ 'offer_statuses' MUST contain exactly one verdict for every registered offer, in registry order, for every dimension.
618
+ Derive the dimension status from those offer verdicts with precedence changed, uncertain, equivalent, not_present.
156
619
  'not_present' is valid only if a dimension is absent from BOTH texts. 'equivalent' requires nonempty matched items
157
- covering every applicable proposition. A dimension-level 'changed' or 'uncertain' verdict requires at least one
620
+ for that offer covering every applicable proposition. A per-offer 'changed' or 'uncertain' verdict requires at least one
158
621
  specific item; use coverage='uncertain' for unresolved overall completeness without inventing a span.
159
622
  Use 'changed' for a known defect and 'uncertain' for unresolved interpretation, coverage or insufficient language/domain
160
623
  evidence. Use coverage='uncertain' unless every proposition and offer association was checked. Never resolve numeric
@@ -173,9 +636,18 @@ def review_contract(schema: str) -> dict[str, Any]:
173
636
  return {
174
637
  "schema": schema,
175
638
  "coverage": "complete or uncertain",
639
+ "offers": [{
640
+ "id": "stable unique offer identifier",
641
+ "source_spans": [[0, 1]],
642
+ "target_spans": [[0, 1]],
643
+ }],
176
644
  "checks": {
177
645
  name: {
178
646
  "status": "equivalent, not_present, changed or uncertain",
647
+ "offer_statuses": [{
648
+ "offer": "stable offer label",
649
+ "status": "equivalent, not_present, changed or uncertain",
650
+ }],
179
651
  "items": [{
180
652
  "offer": "stable offer label",
181
653
  "relation": "matched, source_only, or target_only",
@@ -195,13 +667,16 @@ def validate_review(
195
667
  target: str,
196
668
  schema: str,
197
669
  *,
670
+ target_locale: str,
671
+ commercial_quality_profile_version: str,
672
+ commercial_quality_profile_sha256: str,
198
673
  allow_uncertain: bool = False,
199
674
  ) -> dict[str, Any]:
200
675
  """Validate evidence and return a content-free, hash-bound routing summary."""
201
676
  def invalid() -> None:
202
677
  raise CommercialReviewBlocked("review.commercial.invalid")
203
678
 
204
- def span(value: Any, text: str) -> None:
679
+ def span(value: Any, text: str) -> tuple[int, int]:
205
680
  if (
206
681
  not isinstance(value, list) or len(value) != 2
207
682
  or any(type(offset) is not int for offset in value)
@@ -209,55 +684,169 @@ def validate_review(
209
684
  or not text[value[0]:value[1]].strip()
210
685
  ):
211
686
  invalid()
687
+ return value[0], value[1]
688
+
689
+ def regions(value: Any, text: str) -> tuple[tuple[int, int], ...]:
690
+ if not isinstance(value, list) or len(value) > 1000:
691
+ invalid()
692
+ parsed = tuple(span(item, text) for item in value)
693
+ if list(parsed) != sorted(parsed) or any(
694
+ previous[1] > current[0]
695
+ for previous, current in zip(parsed, parsed[1:])
696
+ ):
697
+ invalid()
698
+ return parsed
212
699
 
213
- if not isinstance(value, dict) or set(value) != {"schema", "coverage", "checks"}:
700
+ if (
701
+ not isinstance(target_locale, str)
702
+ or TARGET_LOCALE.fullmatch(target_locale) is None
703
+ or not isinstance(commercial_quality_profile_version, str)
704
+ or PROFILE_VERSION.fullmatch(commercial_quality_profile_version) is None
705
+ or not isinstance(commercial_quality_profile_sha256, str)
706
+ or len(commercial_quality_profile_sha256) != 64
707
+ or any(
708
+ character not in "0123456789abcdef"
709
+ for character in commercial_quality_profile_sha256
710
+ )
711
+ ):
712
+ invalid()
713
+ if not isinstance(value, dict) or set(value) != {
714
+ "schema", "coverage", "offers", "checks",
715
+ }:
214
716
  invalid()
215
717
  if value["schema"] != schema or value["coverage"] not in ("complete", "uncertain"):
216
718
  invalid()
719
+ offers = value["offers"]
720
+ if not isinstance(offers, list) or len(offers) > 1000:
721
+ invalid()
722
+ offer_regions: dict[str, dict[str, tuple[tuple[int, int], ...]]] = {}
723
+ occupied = {"source_spans": [], "target_spans": []}
724
+ for offer in offers:
725
+ if not isinstance(offer, dict) or set(offer) != {
726
+ "id", "source_spans", "target_spans",
727
+ }:
728
+ invalid()
729
+ offer_id = offer["id"]
730
+ if (
731
+ not isinstance(offer_id, str)
732
+ or PROFILE_VERSION.fullmatch(offer_id) is None
733
+ or offer_id in offer_regions
734
+ ):
735
+ invalid()
736
+ parsed = {
737
+ "source_spans": regions(offer["source_spans"], source),
738
+ "target_spans": regions(offer["target_spans"], target),
739
+ }
740
+ if not parsed["source_spans"] and not parsed["target_spans"]:
741
+ invalid()
742
+ offer_regions[offer_id] = parsed
743
+ for side in occupied:
744
+ occupied[side].extend(
745
+ (start, end, offer_id) for start, end in parsed[side]
746
+ )
747
+ for side in occupied:
748
+ ordered = sorted(occupied[side])
749
+ if any(
750
+ previous[1] > current[0]
751
+ for previous, current in zip(ordered, ordered[1:])
752
+ ):
753
+ invalid()
217
754
  checks = value["checks"]
218
755
  if not isinstance(checks, dict) or set(checks) != set(DIMENSIONS):
219
756
  invalid()
220
757
  uncertain_dimensions: set[str] = set()
758
+ uncertain_offers: dict[str, set[int]] = {
759
+ name: set() for name in DIMENSIONS
760
+ }
221
761
  coverage_uncertain = value["coverage"] == "uncertain"
222
762
  changed = False
223
763
  evidenced = False
764
+ offer_ids = list(offer_regions)
765
+ status_priority = {
766
+ "not_present": 0,
767
+ "equivalent": 1,
768
+ "uncertain": 2,
769
+ "changed": 3,
770
+ }
224
771
  for name, check in checks.items():
225
- if not isinstance(check, dict) or set(check) != {"status", "items"}:
772
+ if not isinstance(check, dict) or set(check) != {
773
+ "status", "offer_statuses", "items",
774
+ }:
226
775
  invalid()
227
- status, items = check["status"], check["items"]
776
+ status = check["status"]
777
+ offer_statuses = check["offer_statuses"]
778
+ items = check["items"]
228
779
  if status not in ("equivalent", "not_present", "changed", "uncertain"):
229
780
  invalid()
230
- if not isinstance(items, list) or len(items) > 1000:
231
- invalid()
232
781
  if (
233
- (status == "not_present" and items)
234
- or (status in ("equivalent", "changed", "uncertain") and not items)
782
+ not isinstance(offer_statuses, list)
783
+ or len(offer_statuses) != len(offer_ids)
235
784
  ):
236
785
  invalid()
786
+ parsed_offer_statuses: dict[str, str] = {}
787
+ for index, offer_status in enumerate(offer_statuses):
788
+ if (
789
+ not isinstance(offer_status, dict)
790
+ or set(offer_status) != {"offer", "status"}
791
+ or offer_status.get("offer") != offer_ids[index]
792
+ or offer_status.get("status") not in status_priority
793
+ or offer_status["offer"] in parsed_offer_statuses
794
+ ):
795
+ invalid()
796
+ parsed_offer_statuses[offer_status["offer"]] = (
797
+ offer_status["status"]
798
+ )
799
+ derived_status = (
800
+ max(parsed_offer_statuses.values(), key=status_priority.__getitem__)
801
+ if parsed_offer_statuses else "not_present"
802
+ )
803
+ if status != derived_status:
804
+ invalid()
805
+ if not isinstance(items, list) or len(items) > 1000:
806
+ invalid()
237
807
  seen = set()
808
+ items_by_offer = {offer_id: [] for offer_id in offer_ids}
238
809
  for item in items:
239
810
  if not isinstance(item, dict) or set(item) != {
240
811
  "offer", "relation", "source_span", "target_span", "explanation",
241
812
  }:
242
813
  invalid()
243
- for field in ("offer", "explanation"):
244
- if not isinstance(item[field], str) or not item[field].strip() or len(item[field]) > 2000:
245
- invalid()
814
+ if (
815
+ not isinstance(item["offer"], str)
816
+ or PROFILE_VERSION.fullmatch(item["offer"]) is None
817
+ or item["offer"] not in offer_regions
818
+ or not isinstance(item["explanation"], str)
819
+ or not item["explanation"].strip()
820
+ or len(item["explanation"]) > 2000
821
+ ):
822
+ invalid()
246
823
  relation = item["relation"]
824
+ source_span = target_span = None
247
825
  if relation == "matched":
248
- span(item["source_span"], source)
249
- span(item["target_span"], target)
826
+ source_span = span(item["source_span"], source)
827
+ target_span = span(item["target_span"], target)
250
828
  elif relation == "source_only":
251
- span(item["source_span"], source)
829
+ source_span = span(item["source_span"], source)
252
830
  if item["target_span"] is not None:
253
831
  invalid()
254
832
  elif relation == "target_only":
255
833
  if item["source_span"] is not None:
256
834
  invalid()
257
- span(item["target_span"], target)
835
+ target_span = span(item["target_span"], target)
258
836
  else:
259
837
  invalid()
260
- if status == "equivalent" and relation != "matched":
838
+ declared = offer_regions[item["offer"]]
839
+ if source_span is not None and not any(
840
+ region[0] <= source_span[0]
841
+ and source_span[1] <= region[1]
842
+ for region in declared["source_spans"]
843
+ ):
844
+ invalid()
845
+ if target_span is not None and not any(
846
+ region[0] <= target_span[0]
847
+ and target_span[1] <= region[1]
848
+ for region in declared["target_spans"]
849
+ ):
261
850
  invalid()
262
851
  identity = (
263
852
  item["offer"], relation,
@@ -267,10 +856,33 @@ def validate_review(
267
856
  if identity in seen:
268
857
  invalid()
269
858
  seen.add(identity)
270
- if status == "uncertain":
859
+ items_by_offer[item["offer"]].append(item)
860
+ for offer_id, offer_status in parsed_offer_statuses.items():
861
+ offer_items = items_by_offer[offer_id]
862
+ if (
863
+ (offer_status == "not_present" and offer_items)
864
+ or (
865
+ offer_status in ("equivalent", "changed", "uncertain")
866
+ and not offer_items
867
+ )
868
+ or (
869
+ offer_status == "equivalent"
870
+ and any(item["relation"] != "matched" for item in offer_items)
871
+ )
872
+ ):
873
+ invalid()
874
+ if any(value == "uncertain" for value in parsed_offer_statuses.values()):
271
875
  uncertain_dimensions.add(name)
272
- changed |= status == "changed"
273
- evidenced |= status == "equivalent"
876
+ uncertain_offers[name].update(
877
+ index for index, offer_id in enumerate(offer_ids)
878
+ if parsed_offer_statuses[offer_id] == "uncertain"
879
+ )
880
+ changed |= any(
881
+ value == "changed" for value in parsed_offer_statuses.values()
882
+ )
883
+ evidenced |= any(
884
+ value == "equivalent" for value in parsed_offer_statuses.values()
885
+ )
274
886
  if changed:
275
887
  raise CommercialReviewBlocked("review.commercial.changed")
276
888
  # Every evidenced condition must resolve to an explicitly reviewed offer.
@@ -279,12 +891,22 @@ def validate_review(
279
891
  check["status"] == "equivalent" for name, check in checks.items() if name != "offer_assignment"
280
892
  ):
281
893
  uncertain_dimensions.add("offer_assignment")
894
+ if not uncertain_offers["offer_assignment"]:
895
+ uncertain_offers["offer_assignment"].update(
896
+ range(len(offer_ids))
897
+ )
282
898
  else:
283
- offers = {item["offer"] for item in assignment["items"]}
284
- if any(item["offer"] not in offers for check in checks.values() for item in check["items"]):
899
+ assigned = [item["offer"] for item in assignment["items"]]
900
+ if (
901
+ any(item["relation"] != "matched" for item in assignment["items"])
902
+ or len(assigned) != len(set(assigned))
903
+ or set(assigned) != set(offer_regions)
904
+ ):
285
905
  invalid()
286
906
  if coverage_uncertain or not evidenced:
287
907
  uncertain_dimensions.update(DIMENSIONS)
908
+ for name in DIMENSIONS:
909
+ uncertain_offers[name].update(range(len(offer_ids)))
288
910
  summary = {
289
911
  "schema": REVIEW_SUMMARY_SCHEMA,
290
912
  "profile": schema,
@@ -292,7 +914,30 @@ def validate_review(
292
914
  "review_required_dimensions": [
293
915
  name for name in DIMENSIONS if name in uncertain_dimensions
294
916
  ],
295
- "evidence_sha256": hashlib.sha256(_canonical_json(value)).hexdigest(),
917
+ "offer_count": len(offer_ids),
918
+ "review_required_offers": [
919
+ {
920
+ "dimension": name,
921
+ "offer_indexes": sorted(uncertain_offers[name]),
922
+ }
923
+ for name in DIMENSIONS if uncertain_offers[name]
924
+ ],
925
+ "review_evidence_contract_sha256": (
926
+ public_review_evidence_contract(schema)["sha256"]
927
+ ),
928
+ "evidence_sha256": evidence_sha256(
929
+ value,
930
+ source,
931
+ target,
932
+ schema,
933
+ target_locale=target_locale,
934
+ commercial_quality_profile_version=(
935
+ commercial_quality_profile_version
936
+ ),
937
+ commercial_quality_profile_sha256=(
938
+ commercial_quality_profile_sha256
939
+ ),
940
+ ),
296
941
  }
297
942
  if uncertain_dimensions and not allow_uncertain:
298
943
  raise CommercialReviewBlocked("review.commercial.independent_review_required")
@@ -310,24 +955,177 @@ def validate_summary(
310
955
  not isinstance(value, dict)
311
956
  or set(value) != {
312
957
  "schema", "profile", "status", "review_required_dimensions",
313
- "evidence_sha256",
958
+ "offer_count", "review_required_offers",
959
+ "review_evidence_contract_sha256", "evidence_sha256",
314
960
  }
315
961
  or value["schema"] != REVIEW_SUMMARY_SCHEMA
316
962
  or value["profile"] != profile
963
+ or value["review_evidence_contract_sha256"]
964
+ != public_review_evidence_contract(profile)["sha256"]
317
965
  or value["status"] not in {"verified", "review_required"}
318
966
  or not isinstance(value["evidence_sha256"], str)
319
967
  or len(value["evidence_sha256"]) != 64
320
968
  or any(character not in "0123456789abcdef" for character in value["evidence_sha256"])
321
969
  or not isinstance(value["review_required_dimensions"], list)
970
+ or type(value["offer_count"]) is not int
971
+ or not 0 <= value["offer_count"] <= 1000
972
+ or not isinstance(value["review_required_offers"], list)
322
973
  ):
323
974
  raise CommercialReviewBlocked("review.commercial.summary_invalid")
324
975
  dimensions = value["review_required_dimensions"]
976
+ offer_scope = value["review_required_offers"]
325
977
  if (
326
- len(dimensions) != len(set(dimensions))
978
+ len(offer_scope) > len(DIMENSIONS)
979
+ or len(dimensions) != len(set(dimensions))
327
980
  or any(name not in DIMENSIONS for name in dimensions)
328
981
  or dimensions != [name for name in DIMENSIONS if name in dimensions]
329
982
  or (value["status"] == "verified") != (not dimensions)
983
+ or (value["status"] == "verified" and offer_scope)
330
984
  or (value["status"] == "review_required" and not review_required)
331
985
  ):
332
986
  raise CommercialReviewBlocked("review.commercial.summary_invalid")
987
+ previous_dimension_index = -1
988
+ seen_scope_dimensions: set[str] = set()
989
+ for item in offer_scope:
990
+ if (
991
+ not isinstance(item, dict)
992
+ or set(item) != {"dimension", "offer_indexes"}
993
+ or item.get("dimension") not in dimensions
994
+ or item["dimension"] in seen_scope_dimensions
995
+ or not isinstance(item.get("offer_indexes"), list)
996
+ or not item["offer_indexes"]
997
+ or any(type(index) is not int or not 0 <= index < value["offer_count"]
998
+ for index in item["offer_indexes"])
999
+ or item["offer_indexes"] != sorted(set(item["offer_indexes"]))
1000
+ ):
1001
+ raise CommercialReviewBlocked("review.commercial.summary_invalid")
1002
+ dimension_index = list(DIMENSIONS).index(item["dimension"])
1003
+ if dimension_index <= previous_dimension_index:
1004
+ raise CommercialReviewBlocked("review.commercial.summary_invalid")
1005
+ previous_dimension_index = dimension_index
1006
+ seen_scope_dimensions.add(item["dimension"])
333
1007
  return json.loads(_canonical_json(value))
1008
+
1009
+
1010
+ def validate_review_routing_context(
1011
+ value: Any,
1012
+ source: str,
1013
+ target: str,
1014
+ summary: Any,
1015
+ profile: str,
1016
+ ) -> dict[str, Any]:
1017
+ """Validate private offer regions used only by an independent reviewer."""
1018
+ try:
1019
+ summary = validate_summary(summary, profile, review_required=True)
1020
+ except CommercialReviewBlocked:
1021
+ raise CommercialReviewBlocked("review.commercial.routing_invalid") from None
1022
+ if (
1023
+ summary["status"] != "review_required"
1024
+ or not isinstance(source, str)
1025
+ or not isinstance(target, str)
1026
+ or not isinstance(value, dict)
1027
+ or set(value) != {
1028
+ "schema", "profile", "contract_sha256", "offer_count",
1029
+ "source_length", "target_length", "offers",
1030
+ }
1031
+ or value.get("schema") != REVIEW_ROUTING_SCHEMA
1032
+ or value.get("profile") != profile
1033
+ or value.get("contract_sha256")
1034
+ != public_review_routing_contract(profile)["sha256"]
1035
+ or value.get("offer_count") != summary["offer_count"]
1036
+ or type(value.get("source_length")) is not int
1037
+ or value["source_length"] != len(source)
1038
+ or type(value.get("target_length")) is not int
1039
+ or value["target_length"] != len(target)
1040
+ or not isinstance(value.get("offers"), list)
1041
+ or len(value["offers"]) != value["offer_count"]
1042
+ ):
1043
+ raise CommercialReviewBlocked("review.commercial.routing_invalid")
1044
+
1045
+ occupied = {"source_spans": [], "target_spans": []}
1046
+ for expected_index, offer in enumerate(value["offers"]):
1047
+ if (
1048
+ not isinstance(offer, dict)
1049
+ or set(offer) != {
1050
+ "offer_index", "source_spans", "target_spans",
1051
+ }
1052
+ or offer.get("offer_index") != expected_index
1053
+ ):
1054
+ raise CommercialReviewBlocked("review.commercial.routing_invalid")
1055
+ for field, text in (
1056
+ ("source_spans", source), ("target_spans", target),
1057
+ ):
1058
+ spans = offer.get(field)
1059
+ if not isinstance(spans, list) or len(spans) > 1000:
1060
+ raise CommercialReviewBlocked(
1061
+ "review.commercial.routing_invalid",
1062
+ )
1063
+ parsed = []
1064
+ for span in spans:
1065
+ if (
1066
+ not isinstance(span, list)
1067
+ or len(span) != 2
1068
+ or any(type(offset) is not int for offset in span)
1069
+ or not 0 <= span[0] < span[1] <= len(text)
1070
+ or not text[span[0]:span[1]].strip()
1071
+ ):
1072
+ raise CommercialReviewBlocked(
1073
+ "review.commercial.routing_invalid",
1074
+ )
1075
+ parsed.append(tuple(span))
1076
+ if parsed != sorted(parsed) or any(
1077
+ previous[1] > current[0]
1078
+ for previous, current in zip(parsed, parsed[1:])
1079
+ ):
1080
+ raise CommercialReviewBlocked(
1081
+ "review.commercial.routing_invalid",
1082
+ )
1083
+ occupied[field].extend(
1084
+ (start, end, expected_index) for start, end in parsed
1085
+ )
1086
+ if not offer["source_spans"] and not offer["target_spans"]:
1087
+ raise CommercialReviewBlocked("review.commercial.routing_invalid")
1088
+ for spans in occupied.values():
1089
+ ordered = sorted(spans)
1090
+ if any(
1091
+ previous[1] > current[0]
1092
+ for previous, current in zip(ordered, ordered[1:])
1093
+ ):
1094
+ raise CommercialReviewBlocked("review.commercial.routing_invalid")
1095
+ return json.loads(_canonical_json(value))
1096
+
1097
+
1098
+ def review_routing_context(
1099
+ evidence: Any,
1100
+ source: str,
1101
+ target: str,
1102
+ summary: Any,
1103
+ profile: str,
1104
+ ) -> dict[str, Any]:
1105
+ """Remove offer IDs and prose while retaining actionable private regions."""
1106
+ if (
1107
+ not isinstance(evidence, dict)
1108
+ or evidence.get("schema") != profile
1109
+ or not isinstance(evidence.get("offers"), list)
1110
+ ):
1111
+ raise CommercialReviewBlocked("review.commercial.routing_invalid")
1112
+ context = {
1113
+ "schema": REVIEW_ROUTING_SCHEMA,
1114
+ "profile": profile,
1115
+ "contract_sha256": public_review_routing_contract(profile)["sha256"],
1116
+ "offer_count": len(evidence["offers"]),
1117
+ "source_length": len(source),
1118
+ "target_length": len(target),
1119
+ "offers": [
1120
+ {
1121
+ "offer_index": index,
1122
+ "source_spans": offer.get("source_spans"),
1123
+ "target_spans": offer.get("target_spans"),
1124
+ }
1125
+ if isinstance(offer, dict) else offer
1126
+ for index, offer in enumerate(evidence["offers"])
1127
+ ],
1128
+ }
1129
+ return validate_review_routing_context(
1130
+ context, source, target, summary, profile,
1131
+ )