diff --git a/scripts/ci/zdr_policy.py b/scripts/ci/zdr_policy.py index eb327c8cad..ca1ec3bc48 100644 --- a/scripts/ci/zdr_policy.py +++ b/scripts/ci/zdr_policy.py @@ -21,6 +21,17 @@ consulted and frozen into this module's ``PROVIDER_ZDR_SCOPE`` attestation table as-of the date recorded on each entry. +Each frozen entry carries both the date it was verified (``as_of``) and the +date that verification stops counting as current (``valid_until``). A provider +data policy is a living document: NVIDIA can repeal a training carve-out and +OpenRouter can change what its feed covers, and neither event edits this file. +Without an explicit expiry a frozen citation ages silently — it keeps reading +as authoritative long after nobody has re-read the source. ``valid_until`` makes +that staleness observable (:func:`expired_provider_names`) and, for callers that +opt in by passing ``today``, decisive: an expired attestation cannot grant ZDR, +which is the same conservative stance this module already takes toward a policy +it cannot ascertain. + This module is stdlib-only so the whole policy can run and be tested offline; the runtime ZDR feed is merged in by ``scripts/ci/contextual_orchestrator_review_policy.py``. @@ -29,6 +40,7 @@ from __future__ import annotations import dataclasses +import datetime import re from typing import Mapping @@ -44,6 +56,9 @@ class ProviderZdrScope: given scope is attested by an authoritative, dated source. source: URL or document that grounds the attestation. as_of: ISO date the attestation was last verified. + valid_until: ISO date this verification stops counting as current, + normally ``as_of`` plus :data:`ATTESTATION_REVIEW_WINDOW_DAYS`. + Required on every entry so no citation can age silently. note: One-sentence scope note; never fabricated policy language. openrouter_endpoints_feed: When True, the authoritative OpenRouter ``/api/v1/endpoints/zdr`` feed decides per-model ZDR membership for @@ -54,16 +69,28 @@ class ProviderZdrScope: zero_data_retention: bool source: str as_of: str + valid_until: str note: str openrouter_endpoints_feed: bool = False +ATTESTATION_REVIEW_WINDOW_DAYS = 90 +"""Days a provider data-policy verification counts as current. + +Ninety days is a quarterly re-read cadence for a published terms-of-service or +data-policy document. It is deliberately longer than the monthly window used for +measured cost evidence: these citations track legal text that changes on the +provider's schedule, not a price that drifts continuously. +""" + + PROVIDER_ZDR_SCOPE: Mapping[str, ProviderZdrScope] = { "openrouter": ProviderZdrScope( provider_name="openrouter", zero_data_retention=True, source="https://openrouter.ai/docs/guides/features/zdr", as_of="2026-08-27", + valid_until="2026-11-25", note="OpenRouter itself retains no prompts unless prompt logging is " "explicitly opted into; the /api/v1/endpoints/zdr feed is the " "authoritative per-endpoint membership source.", @@ -88,6 +115,7 @@ class ProviderZdrScope: source="https://assets.ngc.nvidia.com/products/api-catalog/legal/" "NVIDIA%20API%20Trial%20Terms%20of%20Service.pdf", as_of="2026-08-30", + valid_until="2026-11-28", note="NVIDIA API Trial Terms of Service Section 3.3(iv) states User " "Content and Generated Content are used to improve NVIDIA products " "and services, including AI models -- affirmatively not ZDR.", @@ -98,6 +126,7 @@ class ProviderZdrScope: source="https://assets.ngc.nvidia.com/products/api-catalog/legal/" "NVIDIA%20API%20Trial%20Terms%20of%20Service.pdf", as_of="2026-08-30", + valid_until="2026-11-28", note="Secondary NVIDIA NIM key is the same integrate.api.nvidia.com " "trial API and shares the nvidia_nim entry's Section 3.3(iv) " "training-use scope verbatim.", @@ -107,6 +136,7 @@ class ProviderZdrScope: zero_data_retention=False, source="https://openrouter.ai/docs/guides/privacy/provider-logging", as_of="2026-08-27", + valid_until="2026-11-25", note="Default OpenAI API scope is not zero-retention (abuse-monitoring " "retention windows apply unless a contractual ZDR program is in place).", ), @@ -115,6 +145,7 @@ class ProviderZdrScope: zero_data_retention=False, source="https://openrouter.ai/docs/guides/features/zdr", as_of="2026-08-27", + valid_until="2026-11-25", note="Bytez retention policy is not attested; conservative default is " "retained/trains per OpenRouter's stance on unascertained policies.", ), @@ -173,6 +204,40 @@ def known_provider_names() -> tuple[str, ...]: return tuple(sorted(PROVIDER_ZDR_SCOPE)) +def attestation_is_current(provider_name: str, today: datetime.date) -> bool: + """Report whether a provider's attestation is still inside its review window. + + Args: + provider_name: Orchestrator provider identifier; must be present in + ``PROVIDER_ZDR_SCOPE``. + today: Date to evaluate the window against. Passed in rather than read + from the clock so callers and tests stay deterministic. + + Returns: + True while ``today`` is on or before the entry's ``valid_until`` date. + + Raises: + KeyError: If the provider is not in the policy table. + """ + scope = provider_zdr_scope(provider_name) + return today <= datetime.date.fromisoformat(scope.valid_until) + + +def expired_provider_names(today: datetime.date) -> tuple[str, ...]: + """Return the providers whose data-policy citation needs re-reading. + + Args: + today: Date to evaluate every entry's review window against. + + Returns: + Sorted provider identifiers whose ``valid_until`` has passed. Empty when + every citation is current. + """ + return tuple( + name for name in known_provider_names() if not attestation_is_current(name, today) + ) + + def route_key(provider_name: str, model: str) -> str: """Return the ``provider/model`` key used for exact ZDR membership. @@ -192,6 +257,7 @@ def is_zdr_model( *, model: str | None = None, zdr_endpoints: frozenset[str] = frozenset(), + today: datetime.date | None = None, ) -> bool: """Decide whether one discovered model route is ZDR-compliant. @@ -204,12 +270,20 @@ def is_zdr_model( from the OpenRouter ``/api/v1/endpoints/zdr`` feed. When the provider uses the feed, an empty set is not a fallback to \"all OpenRouter is ZDR\". + today: Optional date that enables expiry enforcement. When given, an + attestation past its ``valid_until`` grants nothing, because a + citation nobody has re-read is exactly the unascertained policy + this module treats conservatively. Omitted, the decision rests on + the table alone and expiry is only reportable via + :func:`expired_provider_names`. Returns: - True only for an attested zero-retention scope or an exact feed - membership match. + True only for an attested, still-current zero-retention scope or an + exact feed membership match. """ scope = provider_zdr_scope(provider_name) + if today is not None and not attestation_is_current(provider_name, today): + return False if scope.openrouter_endpoints_feed: if not zdr_endpoints or not model: return False diff --git a/tests/test_zdr_policy.py b/tests/test_zdr_policy.py index 90c7fe4197..6a87736690 100644 --- a/tests/test_zdr_policy.py +++ b/tests/test_zdr_policy.py @@ -2,6 +2,8 @@ from __future__ import annotations +import datetime + import pytest from scripts.ci import zdr_policy @@ -116,4 +118,97 @@ def test_is_zdr_model_feed_only_applies_to_the_openrouter_scope() -> None: ) def test_is_free_route(value: object, expected: bool) -> None: """Only explicitly truthy free markers count; strings are case-folded.""" - assert zdr_policy.is_free_route(value) is expected \ No newline at end of file + assert zdr_policy.is_free_route(value) is expected + +def test_every_attestation_carries_a_valid_until_after_its_as_of() -> None: + """No citation may age silently: expiry is required and must follow as_of.""" + for name in zdr_policy.known_provider_names(): + scope = zdr_policy.provider_zdr_scope(name) + as_of = datetime.date.fromisoformat(scope.as_of) + valid_until = datetime.date.fromisoformat(scope.valid_until) + assert valid_until > as_of, name + assert valid_until == as_of + datetime.timedelta( + days=zdr_policy.ATTESTATION_REVIEW_WINDOW_DAYS + ), name + + +def test_attestation_is_current_on_and_after_the_expiry_boundary() -> None: + """The window is inclusive of valid_until and closed the day after.""" + scope = zdr_policy.provider_zdr_scope("openrouter") + valid_until = datetime.date.fromisoformat(scope.valid_until) + assert zdr_policy.attestation_is_current("openrouter", valid_until) is True + assert ( + zdr_policy.attestation_is_current( + "openrouter", valid_until - datetime.timedelta(days=1) + ) + is True + ) + assert ( + zdr_policy.attestation_is_current( + "openrouter", valid_until + datetime.timedelta(days=1) + ) + is False + ) + + +def test_attestation_is_current_rejects_unknown_provider() -> None: + """Staleness cannot be asked about a provider outside the policy table.""" + with pytest.raises(KeyError): + zdr_policy.attestation_is_current("made_up_provider", datetime.date(2026, 9, 5)) + + +def test_expired_provider_names_is_empty_while_every_citation_is_current() -> None: + """A date inside every window reports nothing to re-read.""" + assert zdr_policy.expired_provider_names(datetime.date(2026, 9, 5)) == () + + +def test_expired_provider_names_reports_only_the_lapsed_entries() -> None: + """Entries expire independently and are reported sorted.""" + assert zdr_policy.expired_provider_names(datetime.date(2026, 11, 26)) == ( + "bytez", + "openai", + "openrouter", + ) + assert zdr_policy.expired_provider_names(datetime.date(2999, 12, 31)) == ( + zdr_policy.known_provider_names() + ) + + +def test_is_zdr_model_ignores_expiry_when_no_date_is_supplied() -> None: + """Omitting today leaves the existing table-only decision untouched.""" + feed = frozenset({"openrouter/deepseek/deepseek-r1:free"}) + assert ( + zdr_policy.is_zdr_model( + "openrouter", model="deepseek/deepseek-r1:free", zdr_endpoints=feed + ) + is True + ) + + +def test_is_zdr_model_fails_closed_on_an_expired_attestation() -> None: + """An unre-read citation grants nothing, feed membership notwithstanding.""" + feed = frozenset({"openrouter/deepseek/deepseek-r1:free"}) + assert ( + zdr_policy.is_zdr_model( + "openrouter", + model="deepseek/deepseek-r1:free", + zdr_endpoints=feed, + today=datetime.date(2026, 9, 5), + ) + is True + ) + assert ( + zdr_policy.is_zdr_model( + "openrouter", + model="deepseek/deepseek-r1:free", + zdr_endpoints=feed, + today=datetime.date(2026, 11, 26), + ) + is False + ) + + +def test_is_zdr_model_expiry_cannot_promote_a_non_zdr_provider() -> None: + """Expiry only ever removes a grant; a not-ZDR entry stays not-ZDR.""" + for today in (datetime.date(2026, 9, 5), datetime.date(2999, 12, 31)): + assert zdr_policy.is_zdr_model("nvidia_nim", model="any/model", today=today) is False