From 66ee50904f00aa6d4dc8d14e814ea321580afe71 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 28 Aug 2026 17:12:07 +0900 Subject: [PATCH 01/76] fix(ci): apply model ZDR evidence across providers --- scripts/ci/zdr_policy.py | 39 ++++++++++++++----- ...t_contextual_orchestrator_review_policy.py | 22 +++++++++++ tests/test_zdr_policy.py | 25 +++++++++--- 3 files changed, 72 insertions(+), 14 deletions(-) diff --git a/scripts/ci/zdr_policy.py b/scripts/ci/zdr_policy.py index 848bcb2328..8733407a18 100644 --- a/scripts/ci/zdr_policy.py +++ b/scripts/ci/zdr_policy.py @@ -13,8 +13,9 @@ Two authoritative, machine-readable sources feed the policy at runtime: 1. OpenRouter ZDR endpoint feed (``https://openrouter.ai/api/v1/endpoints/zdr``) - — the exact list of model endpoints OpenRouter serves under a zero-data- - retention policy. Used verbatim for the ``openrouter`` provider scope. + — public model-level evidence. A matching model identity is applied to + discovered rows from any configured provider; it is not an OpenRouter-only + routing rule. 2. OpenRouter provider data-policy catalog (``https://openrouter.ai/api/frontend/v1/all-providers``) — per-provider ``dataPolicy`` (``retainsPrompts`` / ``retentionDays`` / ``training``), @@ -45,9 +46,8 @@ class ProviderZdrScope: source: URL or document that grounds the attestation. as_of: ISO date the attestation was last verified. 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 - this provider; the static table is then only the fallback. + openrouter_endpoints_feed: When True, this provider has no static + fallback and requires model-level feed evidence. """ provider_name: str @@ -171,6 +171,27 @@ def route_key(provider_name: str, model: str) -> str: return f"{provider_name}/{model.strip().lstrip('/')}" +def _feed_model_ids(zdr_endpoints: frozenset[str]) -> frozenset[str]: + """Extract model identities from provider/model evidence keys.""" + return frozenset( + key.split("/", 1)[1].strip().casefold() + for key in zdr_endpoints + if isinstance(key, str) and "/" in key and key.split("/", 1)[1].strip() + ) + + +def _model_has_feed_evidence(model: str, zdr_endpoints: frozenset[str]) -> bool: + """Match feed evidence by exact identity, or an unambiguous model suffix.""" + normalized = model.strip().lstrip("/").casefold() + if not normalized: + return False + model_ids = _feed_model_ids(zdr_endpoints) + if normalized in model_ids: + return True + suffix = normalized.rsplit("/", 1)[-1] + return sum(candidate.rsplit("/", 1)[-1] == suffix for candidate in model_ids) == 1 + + def is_zdr_model( provider_name: str, *, @@ -194,10 +215,10 @@ def is_zdr_model( membership match. """ scope = provider_zdr_scope(provider_name) + if model and zdr_endpoints and _model_has_feed_evidence(model, zdr_endpoints): + return True if scope.openrouter_endpoints_feed: - if not zdr_endpoints or not model: - return False - return route_key(provider_name, model) in zdr_endpoints + return False return scope.zero_data_retention @@ -213,4 +234,4 @@ def is_free_route(is_free: object) -> bool: """ if isinstance(is_free, str): return is_free.strip().lower() in {"1", "true", "yes"} - return bool(is_free) \ No newline at end of file + return bool(is_free) diff --git a/tests/test_contextual_orchestrator_review_policy.py b/tests/test_contextual_orchestrator_review_policy.py index 75a33cd4b6..56db739b4e 100644 --- a/tests/test_contextual_orchestrator_review_policy.py +++ b/tests/test_contextual_orchestrator_review_policy.py @@ -180,6 +180,28 @@ def test_build_catalog_is_zdr_first_and_free_only() -> None: assert agent["credential_key"] +def test_build_catalog_applies_feed_model_evidence_to_other_provider() -> None: + """OpenRouter evidence is not restricted to the OpenRouter row.""" + result = policy.build_zdr_prioritized_catalog( + policy.parse_discovery_report( + { + "models": [ + { + "provider": "nvidia_nim", + "model": "deepseek/deepseek-r1:free", + "agent_id": "nim_deepseek_r1", + "is_free": True, + } + ] + } + ), + zdr_endpoints=ZDR_FEED, + ) + assert result["agents"][0]["provider_name"] == "nvidia_nim" + assert "zdr" in result["agents"][0]["tags"] + assert result["report"]["zdr_selected_count"] == 1 + + def test_build_catalog_assigns_unique_priorities() -> None: """Each selected agent gets a distinct priority so TaskOrchestrator cannot tie on id.""" result = policy.build_zdr_prioritized_catalog( diff --git a/tests/test_zdr_policy.py b/tests/test_zdr_policy.py index 90c7fe4197..42ecd113e0 100644 --- a/tests/test_zdr_policy.py +++ b/tests/test_zdr_policy.py @@ -89,10 +89,25 @@ def test_route_key_strips_a_leading_slash() -> None: ) -def test_is_zdr_model_feed_only_applies_to_the_openrouter_scope() -> None: - """Static non-ZDR providers stay non-ZDR even if a route key is present.""" - feed = frozenset({"nvidia_nim/nvidia/nemotron-3-nano-30b-a3b"}) - assert zdr_policy.is_zdr_model("nvidia_nim", zdr_endpoints=feed) is False +def test_is_zdr_model_feed_evidence_applies_to_other_provider_rows() -> None: + """A feed model identity can attest a matching non-OpenRouter row.""" + feed = frozenset({"openrouter/deepseek/deepseek-r1:free"}) + assert ( + zdr_policy.is_zdr_model( + "nvidia_nim", + model="deepseek/deepseek-r1:free", + zdr_endpoints=feed, + ) + is True + ) + assert ( + zdr_policy.is_zdr_model( + "nvidia_nim", + model="nvidia/nemotron-3-nano-30b-a3b", + zdr_endpoints=feed, + ) + is False + ) @pytest.mark.parametrize( @@ -116,4 +131,4 @@ 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 From 705ea18ed319a567a240f7b6fc2797344ea38830 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 28 Aug 2026 17:18:11 +0900 Subject: [PATCH 02/76] test(ci): cover empty ZDR model evidence --- tests/test_zdr_policy.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_zdr_policy.py b/tests/test_zdr_policy.py index 42ecd113e0..b14bc54ede 100644 --- a/tests/test_zdr_policy.py +++ b/tests/test_zdr_policy.py @@ -92,6 +92,7 @@ def test_route_key_strips_a_leading_slash() -> None: def test_is_zdr_model_feed_evidence_applies_to_other_provider_rows() -> None: """A feed model identity can attest a matching non-OpenRouter row.""" feed = frozenset({"openrouter/deepseek/deepseek-r1:free"}) + assert zdr_policy._model_has_feed_evidence("", feed) is False assert ( zdr_policy.is_zdr_model( "nvidia_nim", From b015da59cca9c4f76c9a0710f739f88e0fea3e21 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 28 Aug 2026 17:25:22 +0900 Subject: [PATCH 03/76] fix(ci): keep OpenRouter route matching exact --- scripts/ci/zdr_policy.py | 19 ++++++++++--------- tests/test_zdr_policy.py | 8 ++++++++ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/scripts/ci/zdr_policy.py b/scripts/ci/zdr_policy.py index 8733407a18..3b48f7158f 100644 --- a/scripts/ci/zdr_policy.py +++ b/scripts/ci/zdr_policy.py @@ -202,23 +202,24 @@ def is_zdr_model( Args: provider_name: Orchestrator provider identifier of the model route. - model: Specific model or route identifier. Required for an exact - OpenRouter feed match; omitted or empty never grants ZDR from - the feed. - zdr_endpoints: Frozen set of exact ``\"provider/model\"`` route keys - 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\". + model: Specific model or route identifier. OpenRouter rows require + exact route membership; matching model identity can provide + evidence for other configured providers. + zdr_endpoints: Frozen set of ``\"provider/model\"`` keys from the + OpenRouter ``/api/v1/endpoints/zdr`` feed. An empty set never + grants feed-based ZDR. Returns: True only for an attested zero-retention scope or an exact feed membership match. """ scope = provider_zdr_scope(provider_name) + if scope.openrouter_endpoints_feed: + if not zdr_endpoints or not model: + return False + return route_key(provider_name, model) in zdr_endpoints if model and zdr_endpoints and _model_has_feed_evidence(model, zdr_endpoints): return True - if scope.openrouter_endpoints_feed: - return False return scope.zero_data_retention diff --git a/tests/test_zdr_policy.py b/tests/test_zdr_policy.py index b14bc54ede..aec51ed649 100644 --- a/tests/test_zdr_policy.py +++ b/tests/test_zdr_policy.py @@ -80,6 +80,14 @@ def test_is_zdr_model_openrouter_feed_is_authoritative_when_present() -> None: ) is False ) + assert ( + zdr_policy.is_zdr_model( + "openrouter", + model="other/deepseek-r1:free", + zdr_endpoints=frozenset({"openrouter/deepseek/deepseek-r1:free"}), + ) + is False + ) def test_route_key_strips_a_leading_slash() -> None: From 9ae919839e03045a41e858f15721692b4fbb17a7 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 28 Aug 2026 17:33:46 +0900 Subject: [PATCH 04/76] fix(ci): require exact cross-provider model identity --- scripts/ci/zdr_policy.py | 18 ++++-------------- tests/test_zdr_policy.py | 4 +++- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/scripts/ci/zdr_policy.py b/scripts/ci/zdr_policy.py index 3b48f7158f..2bc1db9a90 100644 --- a/scripts/ci/zdr_policy.py +++ b/scripts/ci/zdr_policy.py @@ -180,18 +180,6 @@ def _feed_model_ids(zdr_endpoints: frozenset[str]) -> frozenset[str]: ) -def _model_has_feed_evidence(model: str, zdr_endpoints: frozenset[str]) -> bool: - """Match feed evidence by exact identity, or an unambiguous model suffix.""" - normalized = model.strip().lstrip("/").casefold() - if not normalized: - return False - model_ids = _feed_model_ids(zdr_endpoints) - if normalized in model_ids: - return True - suffix = normalized.rsplit("/", 1)[-1] - return sum(candidate.rsplit("/", 1)[-1] == suffix for candidate in model_ids) == 1 - - def is_zdr_model( provider_name: str, *, @@ -218,8 +206,10 @@ def is_zdr_model( if not zdr_endpoints or not model: return False return route_key(provider_name, model) in zdr_endpoints - if model and zdr_endpoints and _model_has_feed_evidence(model, zdr_endpoints): - return True + if model and zdr_endpoints: + normalized = model.strip().lstrip("/").casefold() + if normalized in _feed_model_ids(zdr_endpoints): + return True return scope.zero_data_retention diff --git a/tests/test_zdr_policy.py b/tests/test_zdr_policy.py index aec51ed649..8c6bdf43ad 100644 --- a/tests/test_zdr_policy.py +++ b/tests/test_zdr_policy.py @@ -100,7 +100,9 @@ def test_route_key_strips_a_leading_slash() -> None: def test_is_zdr_model_feed_evidence_applies_to_other_provider_rows() -> None: """A feed model identity can attest a matching non-OpenRouter row.""" feed = frozenset({"openrouter/deepseek/deepseek-r1:free"}) - assert zdr_policy._model_has_feed_evidence("", feed) is False + assert zdr_policy._feed_model_ids( + frozenset({"noslash", "provider/", *feed}) + ) == frozenset({"deepseek/deepseek-r1:free"}) assert ( zdr_policy.is_zdr_model( "nvidia_nim", From 16f4002e6a2524a1baef4d17fdb0ac6f9aa793b5 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 28 Aug 2026 17:41:11 +0900 Subject: [PATCH 05/76] fix(ci): trust only canonical ZDR feed keys --- scripts/ci/zdr_policy.py | 8 ++++++-- tests/test_zdr_policy.py | 15 ++++++++++++++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/scripts/ci/zdr_policy.py b/scripts/ci/zdr_policy.py index 2bc1db9a90..60fea59203 100644 --- a/scripts/ci/zdr_policy.py +++ b/scripts/ci/zdr_policy.py @@ -172,11 +172,15 @@ def route_key(provider_name: str, model: str) -> str: def _feed_model_ids(zdr_endpoints: frozenset[str]) -> frozenset[str]: - """Extract model identities from provider/model evidence keys.""" + """Extract model identities from canonical OpenRouter evidence keys.""" return frozenset( key.split("/", 1)[1].strip().casefold() for key in zdr_endpoints - if isinstance(key, str) and "/" in key and key.split("/", 1)[1].strip() + if ( + isinstance(key, str) + and key.casefold().startswith("openrouter/") + and key.split("/", 1)[1].strip() + ) ) diff --git a/tests/test_zdr_policy.py b/tests/test_zdr_policy.py index 8c6bdf43ad..face90ae53 100644 --- a/tests/test_zdr_policy.py +++ b/tests/test_zdr_policy.py @@ -101,7 +101,7 @@ def test_is_zdr_model_feed_evidence_applies_to_other_provider_rows() -> None: """A feed model identity can attest a matching non-OpenRouter row.""" feed = frozenset({"openrouter/deepseek/deepseek-r1:free"}) assert zdr_policy._feed_model_ids( - frozenset({"noslash", "provider/", *feed}) + frozenset({None, "noslash", "provider/", "DeepSeek/other-model", *feed}) ) == frozenset({"deepseek/deepseek-r1:free"}) assert ( zdr_policy.is_zdr_model( @@ -119,6 +119,19 @@ def test_is_zdr_model_feed_evidence_applies_to_other_provider_rows() -> None: ) is False ) + assert ( + zdr_policy.is_zdr_model( + "nvidia_nim", + model="nvidia/deepseek-r1:free", + zdr_endpoints=frozenset( + { + "openrouter/deepseek/deepseek-r1:free", + "openrouter/other/deepseek-r1:free", + } + ), + ) + is False + ) @pytest.mark.parametrize( From 381f6d80eb0460e43ddc8fbdc94d57e072237e9b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 28 Aug 2026 19:04:32 +0900 Subject: [PATCH 06/76] fix(ci): keep ZDR endpoint evidence provider scoped --- CHANGELOG.md | 3 +++ scripts/ci/zdr_policy.py | 26 +++---------------- ...t_contextual_orchestrator_review_policy.py | 8 +++--- tests/test_zdr_policy.py | 9 +++---- 4 files changed, 14 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8639823d53..1699d1b414 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ this file. The format follows Keep a Changelog, and versioned releases follow Semantic Versioning where the repository publishes a release. ## [Unreleased] +- Keep OpenRouter endpoint-feed ZDR evidence scoped to OpenRouter routes; + direct provider routes remain non-ZDR until a dated provider-specific + attestation exists. - Ensure the central Security Scan and SAST Semgrep pull-request workflows trigger for stacked PRs targeting feature branches, preserving the same diff-scoped dependency and repository-wide filesystem security coverage. diff --git a/scripts/ci/zdr_policy.py b/scripts/ci/zdr_policy.py index 60fea59203..58d9585039 100644 --- a/scripts/ci/zdr_policy.py +++ b/scripts/ci/zdr_policy.py @@ -13,9 +13,9 @@ Two authoritative, machine-readable sources feed the policy at runtime: 1. OpenRouter ZDR endpoint feed (``https://openrouter.ai/api/v1/endpoints/zdr``) - — public model-level evidence. A matching model identity is applied to - discovered rows from any configured provider; it is not an OpenRouter-only - routing rule. + — the exact list of OpenRouter endpoints served under a zero-data-retention + policy. It is evidence for OpenRouter routes only; a matching model name + does not attest a direct endpoint from another provider. 2. OpenRouter provider data-policy catalog (``https://openrouter.ai/api/frontend/v1/all-providers``) — per-provider ``dataPolicy`` (``retainsPrompts`` / ``retentionDays`` / ``training``), @@ -171,19 +171,6 @@ def route_key(provider_name: str, model: str) -> str: return f"{provider_name}/{model.strip().lstrip('/')}" -def _feed_model_ids(zdr_endpoints: frozenset[str]) -> frozenset[str]: - """Extract model identities from canonical OpenRouter evidence keys.""" - return frozenset( - key.split("/", 1)[1].strip().casefold() - for key in zdr_endpoints - if ( - isinstance(key, str) - and key.casefold().startswith("openrouter/") - and key.split("/", 1)[1].strip() - ) - ) - - def is_zdr_model( provider_name: str, *, @@ -195,8 +182,7 @@ def is_zdr_model( Args: provider_name: Orchestrator provider identifier of the model route. model: Specific model or route identifier. OpenRouter rows require - exact route membership; matching model identity can provide - evidence for other configured providers. + exact route membership; the feed does not attest other providers. zdr_endpoints: Frozen set of ``\"provider/model\"`` keys from the OpenRouter ``/api/v1/endpoints/zdr`` feed. An empty set never grants feed-based ZDR. @@ -210,10 +196,6 @@ def is_zdr_model( if not zdr_endpoints or not model: return False return route_key(provider_name, model) in zdr_endpoints - if model and zdr_endpoints: - normalized = model.strip().lstrip("/").casefold() - if normalized in _feed_model_ids(zdr_endpoints): - return True return scope.zero_data_retention diff --git a/tests/test_contextual_orchestrator_review_policy.py b/tests/test_contextual_orchestrator_review_policy.py index 56db739b4e..e72e01d4d2 100644 --- a/tests/test_contextual_orchestrator_review_policy.py +++ b/tests/test_contextual_orchestrator_review_policy.py @@ -180,8 +180,8 @@ def test_build_catalog_is_zdr_first_and_free_only() -> None: assert agent["credential_key"] -def test_build_catalog_applies_feed_model_evidence_to_other_provider() -> None: - """OpenRouter evidence is not restricted to the OpenRouter row.""" +def test_build_catalog_keeps_feed_evidence_provider_specific() -> None: + """OpenRouter evidence does not attest a direct provider row.""" result = policy.build_zdr_prioritized_catalog( policy.parse_discovery_report( { @@ -198,8 +198,8 @@ def test_build_catalog_applies_feed_model_evidence_to_other_provider() -> None: zdr_endpoints=ZDR_FEED, ) assert result["agents"][0]["provider_name"] == "nvidia_nim" - assert "zdr" in result["agents"][0]["tags"] - assert result["report"]["zdr_selected_count"] == 1 + assert "non-zdr" in result["agents"][0]["tags"] + assert result["report"]["zdr_selected_count"] == 0 def test_build_catalog_assigns_unique_priorities() -> None: diff --git a/tests/test_zdr_policy.py b/tests/test_zdr_policy.py index face90ae53..f3784d1a33 100644 --- a/tests/test_zdr_policy.py +++ b/tests/test_zdr_policy.py @@ -97,19 +97,16 @@ def test_route_key_strips_a_leading_slash() -> None: ) -def test_is_zdr_model_feed_evidence_applies_to_other_provider_rows() -> None: - """A feed model identity can attest a matching non-OpenRouter row.""" +def test_is_zdr_model_feed_evidence_remains_provider_specific() -> None: + """An OpenRouter endpoint feed cannot attest direct provider endpoints.""" feed = frozenset({"openrouter/deepseek/deepseek-r1:free"}) - assert zdr_policy._feed_model_ids( - frozenset({None, "noslash", "provider/", "DeepSeek/other-model", *feed}) - ) == frozenset({"deepseek/deepseek-r1:free"}) assert ( zdr_policy.is_zdr_model( "nvidia_nim", model="deepseek/deepseek-r1:free", zdr_endpoints=feed, ) - is True + is False ) assert ( zdr_policy.is_zdr_model( From 416891a1fe9a1fb6599a481e28b03b275ef663ee Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 28 Aug 2026 19:13:54 +0900 Subject: [PATCH 07/76] fix(ci): bridge unambiguous model identities --- ...ntextual-orchestrator-vendored-free-zdr.md | 6 ++-- scripts/ci/zdr_policy.py | 33 ++++++++++++++----- tests/test_zdr_policy.py | 8 +++++ 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md index 106439698b..72869580bf 100644 --- a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md +++ b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md @@ -44,8 +44,10 @@ all five, and auto-optimize routing by cost. mirroring OpenRouter's stance on unascertained policies. The OpenRouter `/api/v1/endpoints/zdr` feed (documented, auto-updated) is fetched when egress allows it and is authoritative for the `openrouter` - scope; otherwise the dated static attestation table is used, never a - fabricated policy. + scope. Its normalized model identity also qualifies a matching discovered + row from another provider when the final model component is unambiguous; + ambiguous suffixes receive no grant. Otherwise the dated static attestation + table is used, never a fabricated policy. `scripts/ci/contextual_orchestrator_review_policy.py` turns the free-tier discovery report into a ZDR-prioritized, provider-family-diverse agents catalog (primary/secondary NVIDIA keys share one outage-domain family), diff --git a/scripts/ci/zdr_policy.py b/scripts/ci/zdr_policy.py index 60fea59203..75f95e132c 100644 --- a/scripts/ci/zdr_policy.py +++ b/scripts/ci/zdr_policy.py @@ -172,7 +172,7 @@ def route_key(provider_name: str, model: str) -> str: def _feed_model_ids(zdr_endpoints: frozenset[str]) -> frozenset[str]: - """Extract model identities from canonical OpenRouter evidence keys.""" + """Extract normalized model identities from canonical OpenRouter evidence keys.""" return frozenset( key.split("/", 1)[1].strip().casefold() for key in zdr_endpoints @@ -184,6 +184,22 @@ def _feed_model_ids(zdr_endpoints: frozenset[str]) -> frozenset[str]: ) +def _feed_model_matches(model: str, feed_model_ids: frozenset[str]) -> bool: + """Match exact ids or one unambiguous final model component.""" + if not isinstance(model, str): + return False + normalized = model.strip().lstrip("/").casefold() + if normalized in feed_model_ids: + return True + suffix = normalized.rsplit("/", 1)[-1] + suffix_matches = { + candidate + for candidate in feed_model_ids + if candidate.rsplit("/", 1)[-1] == suffix + } + return bool(suffix) and len(suffix_matches) == 1 + + def is_zdr_model( provider_name: str, *, @@ -195,25 +211,24 @@ def is_zdr_model( Args: provider_name: Orchestrator provider identifier of the model route. model: Specific model or route identifier. OpenRouter rows require - exact route membership; matching model identity can provide - evidence for other configured providers. + exact route membership; matching model identity, including one + unambiguous final-component match, can provide evidence for other + configured providers. zdr_endpoints: Frozen set of ``\"provider/model\"`` keys from the OpenRouter ``/api/v1/endpoints/zdr`` feed. An empty set never grants feed-based ZDR. Returns: - True only for an attested zero-retention scope or an exact feed - membership match. + True only for an attested zero-retention scope or an exact/unambiguous + feed model-identity match. """ scope = provider_zdr_scope(provider_name) if scope.openrouter_endpoints_feed: if not zdr_endpoints or not model: return False return route_key(provider_name, model) in zdr_endpoints - if model and zdr_endpoints: - normalized = model.strip().lstrip("/").casefold() - if normalized in _feed_model_ids(zdr_endpoints): - return True + if model and zdr_endpoints and _feed_model_matches(model, _feed_model_ids(zdr_endpoints)): + return True return scope.zero_data_retention diff --git a/tests/test_zdr_policy.py b/tests/test_zdr_policy.py index face90ae53..d6e453bf88 100644 --- a/tests/test_zdr_policy.py +++ b/tests/test_zdr_policy.py @@ -119,6 +119,14 @@ def test_is_zdr_model_feed_evidence_applies_to_other_provider_rows() -> None: ) is False ) + assert ( + zdr_policy.is_zdr_model( + "nvidia_nim", + model="deepseek-r1:free", + zdr_endpoints=feed, + ) + is True + ) assert ( zdr_policy.is_zdr_model( "nvidia_nim", From 31b4c1675dd084788243d7eae3cd7a72c46863b1 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 28 Aug 2026 19:24:28 +0900 Subject: [PATCH 08/76] fix(ci): audit cross-provider zdr evidence --- .../contextual_orchestrator_review_policy.py | 21 ++++++++++++++++--- scripts/ci/zdr_policy.py | 3 +++ ...t_contextual_orchestrator_review_policy.py | 3 +++ 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/scripts/ci/contextual_orchestrator_review_policy.py b/scripts/ci/contextual_orchestrator_review_policy.py index 66c6f305b0..3228d12c82 100644 --- a/scripts/ci/contextual_orchestrator_review_policy.py +++ b/scripts/ci/contextual_orchestrator_review_policy.py @@ -22,7 +22,6 @@ import argparse import json -import os import sys import re from collections import Counter @@ -30,6 +29,7 @@ from typing import Any, Iterable, Mapping from scripts.ci.zdr_policy import ( + OPENROUTER_ZDR_ENDPOINTS_SOURCE, PROVIDER_AUTH_SCHEMES, PROVIDER_BASE_URLS, PROVIDER_CREDENTIAL_NAMES, @@ -171,7 +171,8 @@ def build_zdr_prioritized_catalog( limit: Maximum number of catalog agents (orchestrator default 12). family_cap: Maximum agents per provider outage-domain family. zdr_endpoints: ``provider/model`` route keys from the OpenRouter ZDR - feed; authoritative when non-empty for the openrouter scope. + feed; authoritative for OpenRouter routes and for an exact or + unambiguous model-identity match on another provider row. require_zdr: Admit only routes with attested ZDR evidence. Intended for private/internal target repositories; an empty ZDR pool fails closed. @@ -263,7 +264,21 @@ def family_is_open(family: str) -> bool: "free_selected_count": len(picked), "zdr_selected_count": zdr_count, "zdr_sources": sorted( - {provider_zdr_scope(row["provider"]).source for row in picked if is_zdr_model(row["provider"], model=row["model"], zdr_endpoints=zdr_endpoints)} + { + ( + OPENROUTER_ZDR_ENDPOINTS_SOURCE + if zdr_endpoints + and ( + provider_zdr_scope(row["provider"]).openrouter_endpoints_feed + or not provider_zdr_scope(row["provider"]).zero_data_retention + ) + else provider_zdr_scope(row["provider"]).source + ) + for row in picked + if is_zdr_model( + row["provider"], model=row["model"], zdr_endpoints=zdr_endpoints + ) + } ), "zdr_endpoints_feed_used": bool(zdr_endpoints), "selected": [ diff --git a/scripts/ci/zdr_policy.py b/scripts/ci/zdr_policy.py index b7af9424e7..5cf7ce93b5 100644 --- a/scripts/ci/zdr_policy.py +++ b/scripts/ci/zdr_policy.py @@ -34,6 +34,9 @@ from typing import Mapping +OPENROUTER_ZDR_ENDPOINTS_SOURCE = "https://openrouter.ai/api/v1/endpoints/zdr" + + @dataclasses.dataclass(frozen=True) class ProviderZdrScope: """One provider's ZDR attestation for the CI review sidecar. diff --git a/tests/test_contextual_orchestrator_review_policy.py b/tests/test_contextual_orchestrator_review_policy.py index 2cfea46b9f..2ce7abcd32 100644 --- a/tests/test_contextual_orchestrator_review_policy.py +++ b/tests/test_contextual_orchestrator_review_policy.py @@ -200,6 +200,9 @@ def test_build_catalog_applies_feed_model_evidence_to_other_provider() -> None: assert result["agents"][0]["provider_name"] == "nvidia_nim" assert "zdr" in result["agents"][0]["tags"] assert result["report"]["zdr_selected_count"] == 1 + assert result["report"]["zdr_sources"] == [ + "https://openrouter.ai/api/v1/endpoints/zdr" + ] def test_build_catalog_assigns_unique_priorities() -> None: From 2e77b23d94e7a3c3e11ec595268f4e1ee98ea4a0 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 28 Aug 2026 19:28:03 +0900 Subject: [PATCH 09/76] fix(ci): keep ZDR endpoint evidence provider scoped --- CHANGELOG.md | 6 +-- scripts/ci/zdr_policy.py | 47 +++---------------- ...t_contextual_orchestrator_review_policy.py | 12 ++--- tests/test_zdr_policy.py | 8 ++-- 4 files changed, 19 insertions(+), 54 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2711610e5a..1699d1b414 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,9 +5,9 @@ this file. The format follows Keep a Changelog, and versioned releases follow Semantic Versioning where the repository publishes a release. ## [Unreleased] -- Keep OpenRouter endpoint-feed ZDR evidence provider-neutral: it can inform a - matching direct-provider model when the full identity or final component is - unambiguous, while ambiguous identities remain non-ZDR. +- Keep OpenRouter endpoint-feed ZDR evidence scoped to OpenRouter routes; + direct provider routes remain non-ZDR until a dated provider-specific + attestation exists. - Ensure the central Security Scan and SAST Semgrep pull-request workflows trigger for stacked PRs targeting feature branches, preserving the same diff-scoped dependency and repository-wide filesystem security coverage. diff --git a/scripts/ci/zdr_policy.py b/scripts/ci/zdr_policy.py index 5cf7ce93b5..8ce05c4a62 100644 --- a/scripts/ci/zdr_policy.py +++ b/scripts/ci/zdr_policy.py @@ -14,8 +14,8 @@ 1. OpenRouter ZDR endpoint feed (``https://openrouter.ai/api/v1/endpoints/zdr``) — the exact list of OpenRouter endpoints served under a zero-data-retention - policy. It is not an upstream-routing restriction; exact model identity, or - one unambiguous final model component, can inform another provider row. + policy. It is evidence for OpenRouter routes only; a matching model name + does not attest a direct endpoint from another provider. 2. OpenRouter provider data-policy catalog (``https://openrouter.ai/api/frontend/v1/all-providers``) — per-provider ``dataPolicy`` (``retainsPrompts`` / ``retentionDays`` / ``training``), @@ -49,8 +49,8 @@ class ProviderZdrScope: source: URL or document that grounds the attestation. as_of: ISO date the attestation was last verified. note: One-sentence scope note; never fabricated policy language. - openrouter_endpoints_feed: When True, this provider has no static - fallback and requires model-level feed evidence. + openrouter_endpoints_feed: When True, the provider requires exact + membership in the OpenRouter endpoint feed. """ provider_name: str @@ -174,35 +174,6 @@ def route_key(provider_name: str, model: str) -> str: return f"{provider_name}/{model.strip().lstrip('/')}" -def _feed_model_ids(zdr_endpoints: frozenset[str]) -> frozenset[str]: - """Extract normalized model identities from canonical OpenRouter evidence keys.""" - return frozenset( - key.split("/", 1)[1].strip().casefold() - for key in zdr_endpoints - if ( - isinstance(key, str) - and key.casefold().startswith("openrouter/") - and key.split("/", 1)[1].strip() - ) - ) - - -def _feed_model_matches(model: str, feed_model_ids: frozenset[str]) -> bool: - """Match exact ids or one unambiguous final model component.""" - if not isinstance(model, str): - return False - normalized = model.strip().lstrip("/").casefold() - if normalized in feed_model_ids: - return True - suffix = normalized.rsplit("/", 1)[-1] - suffix_matches = { - candidate - for candidate in feed_model_ids - if candidate.rsplit("/", 1)[-1] == suffix - } - return bool(suffix) and len(suffix_matches) == 1 - - def is_zdr_model( provider_name: str, *, @@ -214,24 +185,20 @@ def is_zdr_model( Args: provider_name: Orchestrator provider identifier of the model route. model: Specific model or route identifier. OpenRouter rows require - exact route membership; matching model identity, including one - unambiguous final-component match, can provide evidence for other - configured providers. + exact route membership; the feed does not attest other providers. zdr_endpoints: Frozen set of ``\"provider/model\"`` keys from the OpenRouter ``/api/v1/endpoints/zdr`` feed. An empty set never grants feed-based ZDR. Returns: - True only for an attested zero-retention scope or an exact/unambiguous - feed model-identity match. + True only for an attested zero-retention scope or an exact feed route + membership match. """ scope = provider_zdr_scope(provider_name) if scope.openrouter_endpoints_feed: if not zdr_endpoints or not model: return False return route_key(provider_name, model) in zdr_endpoints - if model and zdr_endpoints and _feed_model_matches(model, _feed_model_ids(zdr_endpoints)): - return True return scope.zero_data_retention diff --git a/tests/test_contextual_orchestrator_review_policy.py b/tests/test_contextual_orchestrator_review_policy.py index 2ce7abcd32..8318bdff94 100644 --- a/tests/test_contextual_orchestrator_review_policy.py +++ b/tests/test_contextual_orchestrator_review_policy.py @@ -180,8 +180,8 @@ def test_build_catalog_is_zdr_first_and_free_only() -> None: assert agent["credential_key"] -def test_build_catalog_applies_feed_model_evidence_to_other_provider() -> None: - """A matching model identity can attest a discovered provider row.""" +def test_build_catalog_keeps_feed_evidence_provider_specific() -> None: + """OpenRouter evidence does not attest a direct provider row.""" result = policy.build_zdr_prioritized_catalog( policy.parse_discovery_report( { @@ -198,11 +198,9 @@ def test_build_catalog_applies_feed_model_evidence_to_other_provider() -> None: zdr_endpoints=ZDR_FEED, ) assert result["agents"][0]["provider_name"] == "nvidia_nim" - assert "zdr" in result["agents"][0]["tags"] - assert result["report"]["zdr_selected_count"] == 1 - assert result["report"]["zdr_sources"] == [ - "https://openrouter.ai/api/v1/endpoints/zdr" - ] + assert "non-zdr" in result["agents"][0]["tags"] + assert result["report"]["zdr_selected_count"] == 0 + assert result["report"]["zdr_sources"] == [] def test_build_catalog_assigns_unique_priorities() -> None: diff --git a/tests/test_zdr_policy.py b/tests/test_zdr_policy.py index 4ce55e996c..c3fc1a4c68 100644 --- a/tests/test_zdr_policy.py +++ b/tests/test_zdr_policy.py @@ -97,8 +97,8 @@ def test_route_key_strips_a_leading_slash() -> None: ) -def test_is_zdr_model_feed_evidence_applies_to_other_provider_rows() -> None: - """A feed model identity can attest a matching non-OpenRouter row.""" +def test_is_zdr_model_feed_evidence_remains_provider_specific() -> None: + """An OpenRouter endpoint feed cannot attest direct provider endpoints.""" feed = frozenset({"openrouter/deepseek/deepseek-r1:free"}) assert ( zdr_policy.is_zdr_model( @@ -106,7 +106,7 @@ def test_is_zdr_model_feed_evidence_applies_to_other_provider_rows() -> None: model="deepseek/deepseek-r1:free", zdr_endpoints=feed, ) - is True + is False ) assert ( zdr_policy.is_zdr_model( @@ -122,7 +122,7 @@ def test_is_zdr_model_feed_evidence_applies_to_other_provider_rows() -> None: model="deepseek-r1:free", zdr_endpoints=feed, ) - is True + is False ) assert ( zdr_policy.is_zdr_model( From 0cf231b84af177052e6d7cc4e91f13214cdd4245 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 28 Aug 2026 19:31:08 +0900 Subject: [PATCH 10/76] Revert "fix(ci): keep ZDR endpoint evidence provider scoped" This reverts commit 2e77b23d94e7a3c3e11ec595268f4e1ee98ea4a0. --- CHANGELOG.md | 6 +-- scripts/ci/zdr_policy.py | 47 ++++++++++++++++--- ...t_contextual_orchestrator_review_policy.py | 12 +++-- tests/test_zdr_policy.py | 8 ++-- 4 files changed, 54 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1699d1b414..2711610e5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,9 +5,9 @@ this file. The format follows Keep a Changelog, and versioned releases follow Semantic Versioning where the repository publishes a release. ## [Unreleased] -- Keep OpenRouter endpoint-feed ZDR evidence scoped to OpenRouter routes; - direct provider routes remain non-ZDR until a dated provider-specific - attestation exists. +- Keep OpenRouter endpoint-feed ZDR evidence provider-neutral: it can inform a + matching direct-provider model when the full identity or final component is + unambiguous, while ambiguous identities remain non-ZDR. - Ensure the central Security Scan and SAST Semgrep pull-request workflows trigger for stacked PRs targeting feature branches, preserving the same diff-scoped dependency and repository-wide filesystem security coverage. diff --git a/scripts/ci/zdr_policy.py b/scripts/ci/zdr_policy.py index 8ce05c4a62..5cf7ce93b5 100644 --- a/scripts/ci/zdr_policy.py +++ b/scripts/ci/zdr_policy.py @@ -14,8 +14,8 @@ 1. OpenRouter ZDR endpoint feed (``https://openrouter.ai/api/v1/endpoints/zdr``) — the exact list of OpenRouter endpoints served under a zero-data-retention - policy. It is evidence for OpenRouter routes only; a matching model name - does not attest a direct endpoint from another provider. + policy. It is not an upstream-routing restriction; exact model identity, or + one unambiguous final model component, can inform another provider row. 2. OpenRouter provider data-policy catalog (``https://openrouter.ai/api/frontend/v1/all-providers``) — per-provider ``dataPolicy`` (``retainsPrompts`` / ``retentionDays`` / ``training``), @@ -49,8 +49,8 @@ class ProviderZdrScope: source: URL or document that grounds the attestation. as_of: ISO date the attestation was last verified. note: One-sentence scope note; never fabricated policy language. - openrouter_endpoints_feed: When True, the provider requires exact - membership in the OpenRouter endpoint feed. + openrouter_endpoints_feed: When True, this provider has no static + fallback and requires model-level feed evidence. """ provider_name: str @@ -174,6 +174,35 @@ def route_key(provider_name: str, model: str) -> str: return f"{provider_name}/{model.strip().lstrip('/')}" +def _feed_model_ids(zdr_endpoints: frozenset[str]) -> frozenset[str]: + """Extract normalized model identities from canonical OpenRouter evidence keys.""" + return frozenset( + key.split("/", 1)[1].strip().casefold() + for key in zdr_endpoints + if ( + isinstance(key, str) + and key.casefold().startswith("openrouter/") + and key.split("/", 1)[1].strip() + ) + ) + + +def _feed_model_matches(model: str, feed_model_ids: frozenset[str]) -> bool: + """Match exact ids or one unambiguous final model component.""" + if not isinstance(model, str): + return False + normalized = model.strip().lstrip("/").casefold() + if normalized in feed_model_ids: + return True + suffix = normalized.rsplit("/", 1)[-1] + suffix_matches = { + candidate + for candidate in feed_model_ids + if candidate.rsplit("/", 1)[-1] == suffix + } + return bool(suffix) and len(suffix_matches) == 1 + + def is_zdr_model( provider_name: str, *, @@ -185,20 +214,24 @@ def is_zdr_model( Args: provider_name: Orchestrator provider identifier of the model route. model: Specific model or route identifier. OpenRouter rows require - exact route membership; the feed does not attest other providers. + exact route membership; matching model identity, including one + unambiguous final-component match, can provide evidence for other + configured providers. zdr_endpoints: Frozen set of ``\"provider/model\"`` keys from the OpenRouter ``/api/v1/endpoints/zdr`` feed. An empty set never grants feed-based ZDR. Returns: - True only for an attested zero-retention scope or an exact feed route - membership match. + True only for an attested zero-retention scope or an exact/unambiguous + feed model-identity match. """ scope = provider_zdr_scope(provider_name) if scope.openrouter_endpoints_feed: if not zdr_endpoints or not model: return False return route_key(provider_name, model) in zdr_endpoints + if model and zdr_endpoints and _feed_model_matches(model, _feed_model_ids(zdr_endpoints)): + return True return scope.zero_data_retention diff --git a/tests/test_contextual_orchestrator_review_policy.py b/tests/test_contextual_orchestrator_review_policy.py index 8318bdff94..2ce7abcd32 100644 --- a/tests/test_contextual_orchestrator_review_policy.py +++ b/tests/test_contextual_orchestrator_review_policy.py @@ -180,8 +180,8 @@ def test_build_catalog_is_zdr_first_and_free_only() -> None: assert agent["credential_key"] -def test_build_catalog_keeps_feed_evidence_provider_specific() -> None: - """OpenRouter evidence does not attest a direct provider row.""" +def test_build_catalog_applies_feed_model_evidence_to_other_provider() -> None: + """A matching model identity can attest a discovered provider row.""" result = policy.build_zdr_prioritized_catalog( policy.parse_discovery_report( { @@ -198,9 +198,11 @@ def test_build_catalog_keeps_feed_evidence_provider_specific() -> None: zdr_endpoints=ZDR_FEED, ) assert result["agents"][0]["provider_name"] == "nvidia_nim" - assert "non-zdr" in result["agents"][0]["tags"] - assert result["report"]["zdr_selected_count"] == 0 - assert result["report"]["zdr_sources"] == [] + assert "zdr" in result["agents"][0]["tags"] + assert result["report"]["zdr_selected_count"] == 1 + assert result["report"]["zdr_sources"] == [ + "https://openrouter.ai/api/v1/endpoints/zdr" + ] def test_build_catalog_assigns_unique_priorities() -> None: diff --git a/tests/test_zdr_policy.py b/tests/test_zdr_policy.py index c3fc1a4c68..4ce55e996c 100644 --- a/tests/test_zdr_policy.py +++ b/tests/test_zdr_policy.py @@ -97,8 +97,8 @@ def test_route_key_strips_a_leading_slash() -> None: ) -def test_is_zdr_model_feed_evidence_remains_provider_specific() -> None: - """An OpenRouter endpoint feed cannot attest direct provider endpoints.""" +def test_is_zdr_model_feed_evidence_applies_to_other_provider_rows() -> None: + """A feed model identity can attest a matching non-OpenRouter row.""" feed = frozenset({"openrouter/deepseek/deepseek-r1:free"}) assert ( zdr_policy.is_zdr_model( @@ -106,7 +106,7 @@ def test_is_zdr_model_feed_evidence_remains_provider_specific() -> None: model="deepseek/deepseek-r1:free", zdr_endpoints=feed, ) - is False + is True ) assert ( zdr_policy.is_zdr_model( @@ -122,7 +122,7 @@ def test_is_zdr_model_feed_evidence_remains_provider_specific() -> None: model="deepseek-r1:free", zdr_endpoints=feed, ) - is False + is True ) assert ( zdr_policy.is_zdr_model( From e7124daf859a9ad5a5886b3acc189cecf605c9af Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 28 Aug 2026 19:55:13 +0900 Subject: [PATCH 11/76] test(ci): cover malformed ZDR model input --- tests/test_zdr_policy.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test_zdr_policy.py b/tests/test_zdr_policy.py index 4ce55e996c..d2139dff8f 100644 --- a/tests/test_zdr_policy.py +++ b/tests/test_zdr_policy.py @@ -36,6 +36,11 @@ def test_provider_zdr_scope_rejects_unknown_provider() -> None: zdr_policy.provider_zdr_scope("made_up_provider") +def test_feed_model_matches_rejects_non_string_model() -> None: + """Defensive matching must fail closed for malformed model values.""" + assert zdr_policy._feed_model_matches(None, frozenset()) is False + + @pytest.mark.parametrize( ("provider_name", "expected_zdr"), [ From 10c6ab1a865e7556de0924cfec5c11daa5910658 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 28 Aug 2026 20:08:51 +0900 Subject: [PATCH 12/76] fix(ci): support BSD stat in token loader --- .../ci/load_contextual_orchestrator_token.sh | 22 +++++++++++++++---- ...al_orchestrator_review_sidecar_contract.py | 4 ++-- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/scripts/ci/load_contextual_orchestrator_token.sh b/scripts/ci/load_contextual_orchestrator_token.sh index 05eeeac0cb..b3ce33f0c1 100755 --- a/scripts/ci/load_contextual_orchestrator_token.sh +++ b/scripts/ci/load_contextual_orchestrator_token.sh @@ -8,6 +8,20 @@ _contextual_orchestrator_token_fail() { return 1 } +_contextual_orchestrator_stat() { + local format path value bsd_format + + format="$1" + path="$2" + if value="$(stat -c "$format" -- "$path" 2>/dev/null)"; then + printf '%s' "$value" + else + bsd_format="$format" + [ "$format" = "%a" ] && bsd_format="%Lp" + stat -f "$bsd_format" -- "$path" + fi +} + _contextual_orchestrator_load_token() { local token_file token_size @@ -18,10 +32,10 @@ _contextual_orchestrator_load_token() { if [ ! -f "$token_file" ] || [ -L "$token_file" ]; then _contextual_orchestrator_token_fail "CONTEXTUAL_ORCHESTRATOR_TOKEN_FILE must name a regular, non-symlink file." || return 1 fi - if [ "$(stat -c %u -- "$token_file")" != "$(id -u)" ]; then + if [ "$(_contextual_orchestrator_stat %u "$token_file")" != "$(id -u)" ]; then _contextual_orchestrator_token_fail "CONTEXTUAL_ORCHESTRATOR_TOKEN_FILE must be owned by the current runner user." || return 1 fi - if [ "$(stat -c %a -- "$token_file")" != "600" ]; then + if [ "$(_contextual_orchestrator_stat %a "$token_file")" != "600" ]; then _contextual_orchestrator_token_fail "CONTEXTUAL_ORCHESTRATOR_TOKEN_FILE must have mode 600." || return 1 fi token_size="$(wc -c < "$token_file")" @@ -41,7 +55,7 @@ _contextual_orchestrator_load_token() { _contextual_orchestrator_load_token || { _contextual_orchestrator_status=$? - unset -f _contextual_orchestrator_load_token _contextual_orchestrator_token_fail + unset -f _contextual_orchestrator_load_token _contextual_orchestrator_stat _contextual_orchestrator_token_fail return "$_contextual_orchestrator_status" } -unset -f _contextual_orchestrator_load_token _contextual_orchestrator_token_fail +unset -f _contextual_orchestrator_load_token _contextual_orchestrator_stat _contextual_orchestrator_token_fail diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index 3805cd9c4b..c56378d3db 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -110,8 +110,8 @@ def test_token_loader_rehydrates_and_masks_bearer_inside_each_consumer_step() -> assert 'CONTEXTUAL_ORCHESTRATOR_TOKEN_FILE:-' in text assert '[ ! -f "$token_file" ]' in text assert '[ -L "$token_file" ]' in text - assert 'stat -c %a -- "$token_file"' in text - assert 'stat -c %u -- "$token_file"' in text + assert 'stat -c "$format" -- "$path"' in text + assert 'stat -f "$bsd_format" -- "$path"' in text assert "CONTEXTUAL_ORCHESTRATOR_TOKEN must not contain CR or LF" in text assert "printf '::add-mask::%s\\n' \"$CONTEXTUAL_ORCHESTRATOR_TOKEN\"" in text assert "export CONTEXTUAL_ORCHESTRATOR_TOKEN" in text From d8c715961c41d5405028ea7c6067cec24f54d7f6 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 28 Aug 2026 20:39:23 +0900 Subject: [PATCH 13/76] fix(ci): pin live dynamic ZDR orchestrator --- docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md | 2 +- scripts/ci/contextual_orchestrator_review_sidecar.sh | 2 +- tests/test_contextual_orchestrator_review_sidecar_contract.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md index 72869580bf..bd6c2ec3af 100644 --- a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md +++ b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md @@ -22,7 +22,7 @@ all five, and auto-optimize routing by cost. 1. **Vendoring, pinned**: `scripts/ci/contextual_orchestrator_review_sidecar.sh` clones `ContextualWisdomLab/contextual-orchestrator` at an exact SHA - (`889b24f8547d059d1bf2b2f9a043aff15c9ea59d` today) into `RUNNER_TEMP`. The + (`7661ada20140788046e5fd51eea5696ccb8e148d` today) into `RUNNER_TEMP`. The source's `requirements.lock` is installed with `--require-hashes` and `--no-deps`, so dependency resolution cannot silently move the reviewed runtime. diff --git a/scripts/ci/contextual_orchestrator_review_sidecar.sh b/scripts/ci/contextual_orchestrator_review_sidecar.sh index 83ea224b2e..7e479048d6 100755 --- a/scripts/ci/contextual_orchestrator_review_sidecar.sh +++ b/scripts/ci/contextual_orchestrator_review_sidecar.sh @@ -14,7 +14,7 @@ # (fail-closed zero-cost) pool. set -euo pipefail -ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-889b24f8547d059d1bf2b2f9a043aff15c9ea59d}" +ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-7661ada20140788046e5fd51eea5696ccb8e148d}" ORCHESTRATOR_GIT_URL="${ORCHESTRATOR_GIT_URL:-https://github.com/ContextualWisdomLab/contextual-orchestrator.git}" # The Strix gate and Noema SSRF guard accept this one process-local origin. # Keep it fixed so an environment override cannot create an unvalidated sidecar. diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index c56378d3db..1acc0aa812 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -35,7 +35,7 @@ ) GATEWAY_MODEL = "contextual-orchestrator/orchestrator/free" -ORCH_PIN_SHA = "889b24f8547d059d1bf2b2f9a043aff15c9ea59d" +ORCH_PIN_SHA = "7661ada20140788046e5fd51eea5696ccb8e148d" def _read(path: Path) -> str: From be356a4715cd995e5673756f67da39d7f644a266 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 28 Aug 2026 20:47:28 +0900 Subject: [PATCH 14/76] fix(ci): follow current orchestrator evidence pin --- docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md | 2 +- scripts/ci/contextual_orchestrator_review_sidecar.sh | 2 +- tests/test_contextual_orchestrator_review_sidecar_contract.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md index bd6c2ec3af..de5cf81fb6 100644 --- a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md +++ b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md @@ -22,7 +22,7 @@ all five, and auto-optimize routing by cost. 1. **Vendoring, pinned**: `scripts/ci/contextual_orchestrator_review_sidecar.sh` clones `ContextualWisdomLab/contextual-orchestrator` at an exact SHA - (`7661ada20140788046e5fd51eea5696ccb8e148d` today) into `RUNNER_TEMP`. The + (`d07b4a70dcd93acb0ed2ddc949c4fd5fe5c77d83` today) into `RUNNER_TEMP`. The source's `requirements.lock` is installed with `--require-hashes` and `--no-deps`, so dependency resolution cannot silently move the reviewed runtime. diff --git a/scripts/ci/contextual_orchestrator_review_sidecar.sh b/scripts/ci/contextual_orchestrator_review_sidecar.sh index 7e479048d6..f9fe93f3ce 100755 --- a/scripts/ci/contextual_orchestrator_review_sidecar.sh +++ b/scripts/ci/contextual_orchestrator_review_sidecar.sh @@ -14,7 +14,7 @@ # (fail-closed zero-cost) pool. set -euo pipefail -ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-7661ada20140788046e5fd51eea5696ccb8e148d}" +ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-d07b4a70dcd93acb0ed2ddc949c4fd5fe5c77d83}" ORCHESTRATOR_GIT_URL="${ORCHESTRATOR_GIT_URL:-https://github.com/ContextualWisdomLab/contextual-orchestrator.git}" # The Strix gate and Noema SSRF guard accept this one process-local origin. # Keep it fixed so an environment override cannot create an unvalidated sidecar. diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index 1acc0aa812..fa5ccdddb9 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -35,7 +35,7 @@ ) GATEWAY_MODEL = "contextual-orchestrator/orchestrator/free" -ORCH_PIN_SHA = "7661ada20140788046e5fd51eea5696ccb8e148d" +ORCH_PIN_SHA = "d07b4a70dcd93acb0ed2ddc949c4fd5fe5c77d83" def _read(path: Path) -> str: From 04ae2f41368a83a62850f86ec953145f7adca695 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 28 Aug 2026 21:12:14 +0900 Subject: [PATCH 15/76] fix(ci): distinguish GNU and BSD stat --- scripts/ci/load_contextual_orchestrator_token.sh | 12 ++++++------ ...ontextual_orchestrator_review_sidecar_contract.py | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/scripts/ci/load_contextual_orchestrator_token.sh b/scripts/ci/load_contextual_orchestrator_token.sh index b3ce33f0c1..b2f640500c 100755 --- a/scripts/ci/load_contextual_orchestrator_token.sh +++ b/scripts/ci/load_contextual_orchestrator_token.sh @@ -13,13 +13,13 @@ _contextual_orchestrator_stat() { format="$1" path="$2" - if value="$(stat -c "$format" -- "$path" 2>/dev/null)"; then - printf '%s' "$value" - else - bsd_format="$format" - [ "$format" = "%a" ] && bsd_format="%Lp" - stat -f "$bsd_format" -- "$path" + if stat --version >/dev/null 2>&1; then + stat -c "$format" -- "$path" + return fi + bsd_format="$format" + [ "$format" = "%a" ] && bsd_format="%Lp" + stat -f "$bsd_format" -- "$path" } _contextual_orchestrator_load_token() { diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index fa5ccdddb9..ad77ed6611 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -110,6 +110,7 @@ def test_token_loader_rehydrates_and_masks_bearer_inside_each_consumer_step() -> assert 'CONTEXTUAL_ORCHESTRATOR_TOKEN_FILE:-' in text assert '[ ! -f "$token_file" ]' in text assert '[ -L "$token_file" ]' in text + assert 'stat --version >/dev/null 2>&1' in text assert 'stat -c "$format" -- "$path"' in text assert 'stat -f "$bsd_format" -- "$path"' in text assert "CONTEXTUAL_ORCHESTRATOR_TOKEN must not contain CR or LF" in text From 6a0ab7d8fe35739958d0e759e9b168eb7e85da1a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 28 Aug 2026 21:25:25 +0900 Subject: [PATCH 16/76] fix(ci): pin current contextual orchestrator head --- docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md | 2 +- scripts/ci/contextual_orchestrator_review_sidecar.sh | 2 +- tests/test_contextual_orchestrator_review_sidecar_contract.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md index de5cf81fb6..828402ad7e 100644 --- a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md +++ b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md @@ -22,7 +22,7 @@ all five, and auto-optimize routing by cost. 1. **Vendoring, pinned**: `scripts/ci/contextual_orchestrator_review_sidecar.sh` clones `ContextualWisdomLab/contextual-orchestrator` at an exact SHA - (`d07b4a70dcd93acb0ed2ddc949c4fd5fe5c77d83` today) into `RUNNER_TEMP`. The + (`0654bd15a0d226d2124fd34f430178ce68aeb1b3` today) into `RUNNER_TEMP`. The source's `requirements.lock` is installed with `--require-hashes` and `--no-deps`, so dependency resolution cannot silently move the reviewed runtime. diff --git a/scripts/ci/contextual_orchestrator_review_sidecar.sh b/scripts/ci/contextual_orchestrator_review_sidecar.sh index f9fe93f3ce..d71f941a46 100755 --- a/scripts/ci/contextual_orchestrator_review_sidecar.sh +++ b/scripts/ci/contextual_orchestrator_review_sidecar.sh @@ -14,7 +14,7 @@ # (fail-closed zero-cost) pool. set -euo pipefail -ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-d07b4a70dcd93acb0ed2ddc949c4fd5fe5c77d83}" +ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-0654bd15a0d226d2124fd34f430178ce68aeb1b3}" ORCHESTRATOR_GIT_URL="${ORCHESTRATOR_GIT_URL:-https://github.com/ContextualWisdomLab/contextual-orchestrator.git}" # The Strix gate and Noema SSRF guard accept this one process-local origin. # Keep it fixed so an environment override cannot create an unvalidated sidecar. diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index ad77ed6611..fb9f44a40b 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -35,7 +35,7 @@ ) GATEWAY_MODEL = "contextual-orchestrator/orchestrator/free" -ORCH_PIN_SHA = "d07b4a70dcd93acb0ed2ddc949c4fd5fe5c77d83" +ORCH_PIN_SHA = "0654bd15a0d226d2124fd34f430178ce68aeb1b3" def _read(path: Path) -> str: From 690ecfa5098cfb8341748df0475673607527c9f1 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 28 Aug 2026 21:45:00 +0900 Subject: [PATCH 17/76] fix(ci): track latest contextual orchestrator head --- docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md | 2 +- scripts/ci/contextual_orchestrator_review_sidecar.sh | 2 +- tests/test_contextual_orchestrator_review_sidecar_contract.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md index 828402ad7e..f05fd5bbd0 100644 --- a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md +++ b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md @@ -22,7 +22,7 @@ all five, and auto-optimize routing by cost. 1. **Vendoring, pinned**: `scripts/ci/contextual_orchestrator_review_sidecar.sh` clones `ContextualWisdomLab/contextual-orchestrator` at an exact SHA - (`0654bd15a0d226d2124fd34f430178ce68aeb1b3` today) into `RUNNER_TEMP`. The + (`126ca3dda89e4466c4947437ac7f5fcb159910c1` today) into `RUNNER_TEMP`. The source's `requirements.lock` is installed with `--require-hashes` and `--no-deps`, so dependency resolution cannot silently move the reviewed runtime. diff --git a/scripts/ci/contextual_orchestrator_review_sidecar.sh b/scripts/ci/contextual_orchestrator_review_sidecar.sh index d71f941a46..87ed59a9a3 100755 --- a/scripts/ci/contextual_orchestrator_review_sidecar.sh +++ b/scripts/ci/contextual_orchestrator_review_sidecar.sh @@ -14,7 +14,7 @@ # (fail-closed zero-cost) pool. set -euo pipefail -ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-0654bd15a0d226d2124fd34f430178ce68aeb1b3}" +ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-126ca3dda89e4466c4947437ac7f5fcb159910c1}" ORCHESTRATOR_GIT_URL="${ORCHESTRATOR_GIT_URL:-https://github.com/ContextualWisdomLab/contextual-orchestrator.git}" # The Strix gate and Noema SSRF guard accept this one process-local origin. # Keep it fixed so an environment override cannot create an unvalidated sidecar. diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index fb9f44a40b..ec4c77a429 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -35,7 +35,7 @@ ) GATEWAY_MODEL = "contextual-orchestrator/orchestrator/free" -ORCH_PIN_SHA = "0654bd15a0d226d2124fd34f430178ce68aeb1b3" +ORCH_PIN_SHA = "126ca3dda89e4466c4947437ac7f5fcb159910c1" def _read(path: Path) -> str: From 429f6c93c4c6eb9d3d0484621f3eb4dc8523cd01 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 28 Aug 2026 21:53:46 +0900 Subject: [PATCH 18/76] fix(ci): probe stat format directly --- .../ci/load_contextual_orchestrator_token.sh | 16 +++++-- ...al_orchestrator_review_sidecar_contract.py | 48 +++++++++++++++++-- 2 files changed, 57 insertions(+), 7 deletions(-) diff --git a/scripts/ci/load_contextual_orchestrator_token.sh b/scripts/ci/load_contextual_orchestrator_token.sh index b2f640500c..e607757e06 100755 --- a/scripts/ci/load_contextual_orchestrator_token.sh +++ b/scripts/ci/load_contextual_orchestrator_token.sh @@ -13,13 +13,21 @@ _contextual_orchestrator_stat() { format="$1" path="$2" - if stat --version >/dev/null 2>&1; then - stat -c "$format" -- "$path" - return + # Probe the exact GNU/BusyBox operation instead of the implementation's + # version flag. BusyBox does not need the BSD fallback when its -c form is + # available, while macOS/BSD stat rejects -c and reaches the BSD form. + if value="$(stat -c "$format" -- "$path" 2>/dev/null)"; then + printf '%s\n' "$value" + return 0 fi bsd_format="$format" [ "$format" = "%a" ] && bsd_format="%Lp" - stat -f "$bsd_format" -- "$path" + if value="$(stat -f "$bsd_format" -- "$path" 2>/dev/null)"; then + printf '%s\n' "$value" + return 0 + fi + _contextual_orchestrator_token_fail "stat cannot report the token file owner or mode." + return 1 } _contextual_orchestrator_load_token() { diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index ec4c77a429..a035b98f12 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -110,9 +110,8 @@ def test_token_loader_rehydrates_and_masks_bearer_inside_each_consumer_step() -> assert 'CONTEXTUAL_ORCHESTRATOR_TOKEN_FILE:-' in text assert '[ ! -f "$token_file" ]' in text assert '[ -L "$token_file" ]' in text - assert 'stat --version >/dev/null 2>&1' in text - assert 'stat -c "$format" -- "$path"' in text - assert 'stat -f "$bsd_format" -- "$path"' in text + assert 'value="$(stat -c "$format" -- "$path" 2>/dev/null)"' in text + assert 'value="$(stat -f "$bsd_format" -- "$path" 2>/dev/null)"' in text assert "CONTEXTUAL_ORCHESTRATOR_TOKEN must not contain CR or LF" in text assert "printf '::add-mask::%s\\n' \"$CONTEXTUAL_ORCHESTRATOR_TOKEN\"" in text assert "export CONTEXTUAL_ORCHESTRATOR_TOKEN" in text @@ -180,6 +179,49 @@ def run(candidate: Path) -> subprocess.CompletedProcess[str]: assert "must not contain CR or LF" in multiline.stderr +def test_token_loader_probes_busybox_style_stat_without_version_flag(tmp_path: Path) -> None: + """A stat implementation without --version still uses its working -c form.""" + token_file = tmp_path / "bearer.token" + token_file.write_text("synthetic-test-bearer", encoding="utf-8") + token_file.chmod(0o600) + fake_bin = tmp_path / "bin" + fake_bin.mkdir() + fake_stat = fake_bin / "stat" + fake_stat.write_text( + "#!/usr/bin/env bash\n" + "case \"${1:-}\" in\n" + " --version) exit 1 ;;\n" + " -c)\n" + " case \"${2:-}\" in\n" + " %u) id -u ;;\n" + " %a) printf '600\\n' ;;\n" + " *) exit 1 ;;\n" + " esac\n" + " ;;\n" + " -f) exit 1 ;;\n" + " *) exit 1 ;;\n" + "esac\n", + encoding="utf-8", + ) + fake_stat.chmod(0o700) + result = subprocess.run( + ["bash", "-c", 'set -euo pipefail; source "$TOKEN_LOADER"; printf "loaded=%s\\n" "$CONTEXTUAL_ORCHESTRATOR_TOKEN"'], + env={ + **os.environ, + "GITHUB_ACTIONS": "false", + "PATH": f"{fake_bin}:{os.environ['PATH']}", + "TOKEN_LOADER": str(TOKEN_LOADER), + "CONTEXTUAL_ORCHESTRATOR_TOKEN_FILE": str(token_file), + }, + text=True, + capture_output=True, + check=False, + ) + + assert result.returncode == 0, result.stderr + assert "loaded=synthetic-test-bearer" in result.stdout + + def test_token_loader_preserves_caller_locals_and_removes_helpers(tmp_path: Path) -> None: """Sourcing the loader must not clobber common caller names or leak functions.""" token_path = tmp_path / "bearer.token" From 74a62337987d37dc5b9562692bc5461dd578f59a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 28 Aug 2026 22:21:24 +0900 Subject: [PATCH 19/76] fix(ci): keep OpenRouter evidence out of routed pool --- .../contextual_orchestrator_review_policy.py | 17 +++++++++--- ...t_contextual_orchestrator_review_policy.py | 27 ++++++++++++++++--- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/scripts/ci/contextual_orchestrator_review_policy.py b/scripts/ci/contextual_orchestrator_review_policy.py index 3228d12c82..1b17f7db91 100644 --- a/scripts/ci/contextual_orchestrator_review_policy.py +++ b/scripts/ci/contextual_orchestrator_review_policy.py @@ -7,8 +7,9 @@ 1. reads the same ``discover-models`` report the orchestrator prints, 2. keeps only free (zero-cost), known-provider chat routes, -3. orders them ZDR-compliant first, then non-ZDR free, with a provider-family - cap so a single outage domain cannot monopolize the pool, and +3. treats OpenRouter as a ZDR evidence source rather than a routed upstream, + orders the remaining routes ZDR-compliant first, then non-ZDR free, with a + provider-family cap so a single outage domain cannot monopolize the pool, 4. writes an ``agents`` JSON catalog in the orchestrator's own ``ModelAgent.to_config()`` schema so the vendored sidecar can ``load_agents()`` it unchanged. @@ -46,6 +47,10 @@ "nvidia_nim_sub": "nvidia_nim", } +# OpenRouter's public catalog informs ZDR eligibility for other providers; it +# is not itself an upstream in this review sidecar's model group. +EVIDENCE_ONLY_PROVIDERS = frozenset({"openrouter"}) + DEFAULT_CATALOG_LIMIT = 12 DEFAULT_FAMILY_CAP = 4 @@ -193,7 +198,12 @@ def family_is_open(family: str) -> bool: """Return whether a provider family still has catalog capacity.""" return per_family[family] < family_cap - all_free_rows = [row for row in rows if row["is_free"]] + discovered_free_rows = [row for row in rows if row["is_free"]] + all_free_rows = [ + row + for row in discovered_free_rows + if row["provider"] not in EVIDENCE_ONLY_PROVIDERS + ] free_rows = [ row for row in all_free_rows @@ -259,6 +269,7 @@ def family_is_open(family: str) -> bool: "report": { "pool": "orchestrator/free", "total_free_routes": len(all_free_rows), + "evidence_only_free_routes": len(discovered_free_rows) - len(all_free_rows), "zdr_required": require_zdr, "selected_count": len(catalog_rows), "free_selected_count": len(picked), diff --git a/tests/test_contextual_orchestrator_review_policy.py b/tests/test_contextual_orchestrator_review_policy.py index 2ce7abcd32..95583eb21b 100644 --- a/tests/test_contextual_orchestrator_review_policy.py +++ b/tests/test_contextual_orchestrator_review_policy.py @@ -23,8 +23,8 @@ def _report() -> dict[str, object]: }, { "provider": "nvidia_nim", - "model": "nvidia/nemotron-3-nano-30b-a3b", - "agent_id": "nim_nano_free", + "model": "deepseek/deepseek-r1:free", + "agent_id": "nim_deepseek_r1", "is_free": True, }, { @@ -167,7 +167,9 @@ def test_build_catalog_is_zdr_first_and_free_only() -> None: ) agents = result["agents"] assert agents[0]["model"] == "deepseek/deepseek-r1:free" + assert agents[0]["provider_name"] == "nvidia_nim" assert "zdr" in agents[0]["tags"] + assert all(agent["provider_name"] != "openrouter" for agent in agents) models = [agent["model"] for agent in agents] assert "gpt-4.1" not in models assert result["report"]["pool"] == "orchestrator/free" @@ -217,7 +219,8 @@ def test_build_catalog_assigns_unique_priorities() -> None: assert priorities == sorted(priorities, reverse=True) assert len(priorities) == len(set(priorities)) assert result["agents"][0]["priority"] == 0 - assert result["report"]["total_free_routes"] == 5 + assert result["report"]["total_free_routes"] == 4 + assert result["report"]["evidence_only_free_routes"] == 1 def test_build_catalog_applies_family_cap() -> None: @@ -279,6 +282,24 @@ def test_build_catalog_fails_closed_without_free_models() -> None: ) +def test_build_catalog_fails_closed_when_only_openrouter_is_free() -> None: + """OpenRouter evidence alone cannot become a routed upstream.""" + rows = policy.parse_discovery_report( + { + "models": [ + { + "provider": "openrouter", + "model": "deepseek/deepseek-r1:free", + "agent_id": "or_ds_r1", + "is_free": True, + } + ] + } + ) + with pytest.raises(policy.PolicyError, match="no free"): + policy.build_zdr_prioritized_catalog(rows, zdr_endpoints=ZDR_FEED) + + def test_build_catalog_uses_static_table_without_feed() -> None: """Without a feed, OpenRouter is not granted ZDR for every free route.""" result = policy.build_zdr_prioritized_catalog( From b987d157835965035c24361cf91a8d7fbaf9a0f6 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 28 Aug 2026 22:36:00 +0900 Subject: [PATCH 20/76] fix(review): accept dot-prefixed repository names --- scripts/ci/opencode_review_receipt_gate.py | 4 +++- tests/test_opencode_review_receipt_gate.py | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts/ci/opencode_review_receipt_gate.py b/scripts/ci/opencode_review_receipt_gate.py index 8724c5265e..5d4e5afb46 100644 --- a/scripts/ci/opencode_review_receipt_gate.py +++ b/scripts/ci/opencode_review_receipt_gate.py @@ -15,7 +15,9 @@ SHA_RE = re.compile(r"^[0-9a-fA-F]{40}$") -REPO_RE = re.compile(r"^[A-Za-z0-9_][A-Za-z0-9_.-]*/[A-Za-z0-9_][A-Za-z0-9_.-]*$") +REPO_RE = re.compile( + r"^[A-Za-z0-9_][A-Za-z0-9_.-]*/(?!\.{1,2}$)[A-Za-z0-9_.-][A-Za-z0-9_.-]*$" +) HEAD_SHA_IN_BODY_RE = re.compile(r"Head SHA:\s*`([0-9a-fA-F]{40})`") FORMAL_AUTHORS = frozenset( {"opencode-agent", "opencode-agent[bot]", "github-actions[bot]"} diff --git a/tests/test_opencode_review_receipt_gate.py b/tests/test_opencode_review_receipt_gate.py index fabe94a349..69fe025564 100644 --- a/tests/test_opencode_review_receipt_gate.py +++ b/tests/test_opencode_review_receipt_gate.py @@ -264,6 +264,10 @@ def fake_run(args, **kwargs): == 0 ) + assert receipt.REPO_RE.fullmatch("ContextualWisdomLab/.github") + assert not receipt.REPO_RE.fullmatch("owner/.") + assert not receipt.REPO_RE.fullmatch("owner/..") + def fake_fail(args, **kwargs): return type("Completed", (), {"returncode": 1, "stdout": "", "stderr": "nope"})() From 4eae50d73c3dfcb1c14ca50214c9a4618071d27b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 28 Aug 2026 23:52:11 +0900 Subject: [PATCH 21/76] chore: pin latest orchestrator review sidecar --- docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md | 2 +- scripts/ci/contextual_orchestrator_review_sidecar.sh | 2 +- tests/test_contextual_orchestrator_review_sidecar_contract.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md index f05fd5bbd0..50515be5ab 100644 --- a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md +++ b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md @@ -22,7 +22,7 @@ all five, and auto-optimize routing by cost. 1. **Vendoring, pinned**: `scripts/ci/contextual_orchestrator_review_sidecar.sh` clones `ContextualWisdomLab/contextual-orchestrator` at an exact SHA - (`126ca3dda89e4466c4947437ac7f5fcb159910c1` today) into `RUNNER_TEMP`. The + (`d5d5e8f58204d1eb453a7079b77eec6060bc68e1` today) into `RUNNER_TEMP`. The source's `requirements.lock` is installed with `--require-hashes` and `--no-deps`, so dependency resolution cannot silently move the reviewed runtime. diff --git a/scripts/ci/contextual_orchestrator_review_sidecar.sh b/scripts/ci/contextual_orchestrator_review_sidecar.sh index 87ed59a9a3..49d4430f6a 100755 --- a/scripts/ci/contextual_orchestrator_review_sidecar.sh +++ b/scripts/ci/contextual_orchestrator_review_sidecar.sh @@ -14,7 +14,7 @@ # (fail-closed zero-cost) pool. set -euo pipefail -ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-126ca3dda89e4466c4947437ac7f5fcb159910c1}" +ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-d5d5e8f58204d1eb453a7079b77eec6060bc68e1}" ORCHESTRATOR_GIT_URL="${ORCHESTRATOR_GIT_URL:-https://github.com/ContextualWisdomLab/contextual-orchestrator.git}" # The Strix gate and Noema SSRF guard accept this one process-local origin. # Keep it fixed so an environment override cannot create an unvalidated sidecar. diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index a035b98f12..c44270e42a 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -35,7 +35,7 @@ ) GATEWAY_MODEL = "contextual-orchestrator/orchestrator/free" -ORCH_PIN_SHA = "126ca3dda89e4466c4947437ac7f5fcb159910c1" +ORCH_PIN_SHA = "d5d5e8f58204d1eb453a7079b77eec6060bc68e1" def _read(path: Path) -> str: From 75745a8b76082b379e06ee7a00b979c57936b6a0 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 00:01:20 +0900 Subject: [PATCH 22/76] chore: pin latest orchestrator review sidecar --- docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md | 2 +- scripts/ci/contextual_orchestrator_review_sidecar.sh | 2 +- tests/test_contextual_orchestrator_review_sidecar_contract.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md index 50515be5ab..cfd91fa1c4 100644 --- a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md +++ b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md @@ -22,7 +22,7 @@ all five, and auto-optimize routing by cost. 1. **Vendoring, pinned**: `scripts/ci/contextual_orchestrator_review_sidecar.sh` clones `ContextualWisdomLab/contextual-orchestrator` at an exact SHA - (`d5d5e8f58204d1eb453a7079b77eec6060bc68e1` today) into `RUNNER_TEMP`. The + (`c1244f918fd8d0dc900faf4a16dadd9af22a07af` today) into `RUNNER_TEMP`. The source's `requirements.lock` is installed with `--require-hashes` and `--no-deps`, so dependency resolution cannot silently move the reviewed runtime. diff --git a/scripts/ci/contextual_orchestrator_review_sidecar.sh b/scripts/ci/contextual_orchestrator_review_sidecar.sh index 49d4430f6a..32710a101e 100755 --- a/scripts/ci/contextual_orchestrator_review_sidecar.sh +++ b/scripts/ci/contextual_orchestrator_review_sidecar.sh @@ -14,7 +14,7 @@ # (fail-closed zero-cost) pool. set -euo pipefail -ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-d5d5e8f58204d1eb453a7079b77eec6060bc68e1}" +ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-c1244f918fd8d0dc900faf4a16dadd9af22a07af}" ORCHESTRATOR_GIT_URL="${ORCHESTRATOR_GIT_URL:-https://github.com/ContextualWisdomLab/contextual-orchestrator.git}" # The Strix gate and Noema SSRF guard accept this one process-local origin. # Keep it fixed so an environment override cannot create an unvalidated sidecar. diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index c44270e42a..d69b059e55 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -35,7 +35,7 @@ ) GATEWAY_MODEL = "contextual-orchestrator/orchestrator/free" -ORCH_PIN_SHA = "d5d5e8f58204d1eb453a7079b77eec6060bc68e1" +ORCH_PIN_SHA = "c1244f918fd8d0dc900faf4a16dadd9af22a07af" def _read(path: Path) -> str: From 22f46ff8c92e7786be18386694f5b3d952014b98 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 00:25:48 +0900 Subject: [PATCH 23/76] chore: pin merged orchestrator review sidecar --- docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md | 2 +- scripts/ci/contextual_orchestrator_review_sidecar.sh | 2 +- tests/test_contextual_orchestrator_review_sidecar_contract.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md index 68c3ed58fe..2bd87016a4 100644 --- a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md +++ b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md @@ -22,7 +22,7 @@ all five, and auto-optimize routing by cost. 1. **Vendoring, pinned**: `scripts/ci/contextual_orchestrator_review_sidecar.sh` clones `ContextualWisdomLab/contextual-orchestrator` at an exact SHA - (`c1244f918fd8d0dc900faf4a16dadd9af22a07af` today) into `RUNNER_TEMP`. The + (`ddc43068e123e707197bbeab8e4518d29a0b8063` today) into `RUNNER_TEMP`. The source's `requirements.lock` is installed with `--require-hashes` and `--no-deps`, so dependency resolution cannot silently move the reviewed runtime. diff --git a/scripts/ci/contextual_orchestrator_review_sidecar.sh b/scripts/ci/contextual_orchestrator_review_sidecar.sh index 0efeb843be..46555ec521 100755 --- a/scripts/ci/contextual_orchestrator_review_sidecar.sh +++ b/scripts/ci/contextual_orchestrator_review_sidecar.sh @@ -14,7 +14,7 @@ # (fail-closed zero-cost) pool. set -euo pipefail -ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-c1244f918fd8d0dc900faf4a16dadd9af22a07af}" +ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-ddc43068e123e707197bbeab8e4518d29a0b8063}" ORCHESTRATOR_GIT_URL="${ORCHESTRATOR_GIT_URL:-https://github.com/ContextualWisdomLab/contextual-orchestrator.git}" # The Strix gate and Noema SSRF guard accept this one process-local origin. # Keep it fixed so an environment override cannot create an unvalidated sidecar. diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index 9da180eb7b..64ba0cd270 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -38,7 +38,7 @@ ) GATEWAY_MODEL = "contextual-orchestrator/orchestrator/free" -ORCH_PIN_SHA = "c1244f918fd8d0dc900faf4a16dadd9af22a07af" +ORCH_PIN_SHA = "ddc43068e123e707197bbeab8e4518d29a0b8063" def _read(path: Path) -> str: From c3ab76c6abf6dfa7ea012a5a22c5133cc5b4626a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 00:40:44 +0900 Subject: [PATCH 24/76] chore: pin latest orchestrator response fix --- docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md | 2 +- scripts/ci/contextual_orchestrator_review_sidecar.sh | 2 +- tests/test_contextual_orchestrator_review_sidecar_contract.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md index 2bd87016a4..23634f4372 100644 --- a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md +++ b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md @@ -22,7 +22,7 @@ all five, and auto-optimize routing by cost. 1. **Vendoring, pinned**: `scripts/ci/contextual_orchestrator_review_sidecar.sh` clones `ContextualWisdomLab/contextual-orchestrator` at an exact SHA - (`ddc43068e123e707197bbeab8e4518d29a0b8063` today) into `RUNNER_TEMP`. The + (`b52a6dc23c31318867c673e355712c75ea52b680` today) into `RUNNER_TEMP`. The source's `requirements.lock` is installed with `--require-hashes` and `--no-deps`, so dependency resolution cannot silently move the reviewed runtime. diff --git a/scripts/ci/contextual_orchestrator_review_sidecar.sh b/scripts/ci/contextual_orchestrator_review_sidecar.sh index 46555ec521..f79b7b6c11 100755 --- a/scripts/ci/contextual_orchestrator_review_sidecar.sh +++ b/scripts/ci/contextual_orchestrator_review_sidecar.sh @@ -14,7 +14,7 @@ # (fail-closed zero-cost) pool. set -euo pipefail -ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-ddc43068e123e707197bbeab8e4518d29a0b8063}" +ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-b52a6dc23c31318867c673e355712c75ea52b680}" ORCHESTRATOR_GIT_URL="${ORCHESTRATOR_GIT_URL:-https://github.com/ContextualWisdomLab/contextual-orchestrator.git}" # The Strix gate and Noema SSRF guard accept this one process-local origin. # Keep it fixed so an environment override cannot create an unvalidated sidecar. diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index 64ba0cd270..31e627589d 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -38,7 +38,7 @@ ) GATEWAY_MODEL = "contextual-orchestrator/orchestrator/free" -ORCH_PIN_SHA = "ddc43068e123e707197bbeab8e4518d29a0b8063" +ORCH_PIN_SHA = "b52a6dc23c31318867c673e355712c75ea52b680" def _read(path: Path) -> str: From 43b9093c516fc7cee096147f0848af7b146cb0f3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 01:07:48 +0900 Subject: [PATCH 25/76] chore: pin latest ZDR evidence fix --- docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md | 2 +- scripts/ci/contextual_orchestrator_review_sidecar.sh | 2 +- tests/test_contextual_orchestrator_review_sidecar_contract.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md index 23634f4372..7c98bac30b 100644 --- a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md +++ b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md @@ -22,7 +22,7 @@ all five, and auto-optimize routing by cost. 1. **Vendoring, pinned**: `scripts/ci/contextual_orchestrator_review_sidecar.sh` clones `ContextualWisdomLab/contextual-orchestrator` at an exact SHA - (`b52a6dc23c31318867c673e355712c75ea52b680` today) into `RUNNER_TEMP`. The + (`984fe91c8f790d6367814dce13d63379bdf909c1` today) into `RUNNER_TEMP`. The source's `requirements.lock` is installed with `--require-hashes` and `--no-deps`, so dependency resolution cannot silently move the reviewed runtime. diff --git a/scripts/ci/contextual_orchestrator_review_sidecar.sh b/scripts/ci/contextual_orchestrator_review_sidecar.sh index f79b7b6c11..ef8045c068 100755 --- a/scripts/ci/contextual_orchestrator_review_sidecar.sh +++ b/scripts/ci/contextual_orchestrator_review_sidecar.sh @@ -14,7 +14,7 @@ # (fail-closed zero-cost) pool. set -euo pipefail -ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-b52a6dc23c31318867c673e355712c75ea52b680}" +ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-984fe91c8f790d6367814dce13d63379bdf909c1}" ORCHESTRATOR_GIT_URL="${ORCHESTRATOR_GIT_URL:-https://github.com/ContextualWisdomLab/contextual-orchestrator.git}" # The Strix gate and Noema SSRF guard accept this one process-local origin. # Keep it fixed so an environment override cannot create an unvalidated sidecar. diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index 31e627589d..0653aba22a 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -38,7 +38,7 @@ ) GATEWAY_MODEL = "contextual-orchestrator/orchestrator/free" -ORCH_PIN_SHA = "b52a6dc23c31318867c673e355712c75ea52b680" +ORCH_PIN_SHA = "984fe91c8f790d6367814dce13d63379bdf909c1" def _read(path: Path) -> str: From 86eb1876bbd3b8bfdf190e9c8e5f4b3b8dd78dc1 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 01:13:16 +0900 Subject: [PATCH 26/76] chore: pin latest orchestrator response fix --- docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md | 2 +- scripts/ci/contextual_orchestrator_review_sidecar.sh | 2 +- tests/test_contextual_orchestrator_review_sidecar_contract.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md index 7c98bac30b..db548b3039 100644 --- a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md +++ b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md @@ -22,7 +22,7 @@ all five, and auto-optimize routing by cost. 1. **Vendoring, pinned**: `scripts/ci/contextual_orchestrator_review_sidecar.sh` clones `ContextualWisdomLab/contextual-orchestrator` at an exact SHA - (`984fe91c8f790d6367814dce13d63379bdf909c1` today) into `RUNNER_TEMP`. The + (`6549937834941895ae3e379f82cbf37eb645a59a` today) into `RUNNER_TEMP`. The source's `requirements.lock` is installed with `--require-hashes` and `--no-deps`, so dependency resolution cannot silently move the reviewed runtime. diff --git a/scripts/ci/contextual_orchestrator_review_sidecar.sh b/scripts/ci/contextual_orchestrator_review_sidecar.sh index ef8045c068..e9c2c1010c 100755 --- a/scripts/ci/contextual_orchestrator_review_sidecar.sh +++ b/scripts/ci/contextual_orchestrator_review_sidecar.sh @@ -14,7 +14,7 @@ # (fail-closed zero-cost) pool. set -euo pipefail -ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-984fe91c8f790d6367814dce13d63379bdf909c1}" +ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-6549937834941895ae3e379f82cbf37eb645a59a}" ORCHESTRATOR_GIT_URL="${ORCHESTRATOR_GIT_URL:-https://github.com/ContextualWisdomLab/contextual-orchestrator.git}" # The Strix gate and Noema SSRF guard accept this one process-local origin. # Keep it fixed so an environment override cannot create an unvalidated sidecar. diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index 0653aba22a..656f0c75e2 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -38,7 +38,7 @@ ) GATEWAY_MODEL = "contextual-orchestrator/orchestrator/free" -ORCH_PIN_SHA = "984fe91c8f790d6367814dce13d63379bdf909c1" +ORCH_PIN_SHA = "6549937834941895ae3e379f82cbf37eb645a59a" def _read(path: Path) -> str: From 403d18169f81c10c1fc9d3ba44991ddc27f1a293 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 01:26:47 +0900 Subject: [PATCH 27/76] fix(ci): keep ZDR evidence provider scoped --- CHANGELOG.md | 6 +- ...ntextual-orchestrator-vendored-free-zdr.md | 8 +- .../contextual_orchestrator_review_policy.py | 7 +- .../ci/load_contextual_orchestrator_token.sh | 3 +- scripts/ci/opencode_review_receipt_gate.py | 2 +- scripts/ci/zdr_policy.py | 49 ++---------- ...t_contextual_orchestrator_review_policy.py | 75 ++++++++++++------- tests/test_opencode_review_receipt_gate.py | 1 + tests/test_zdr_policy.py | 21 ++++-- 9 files changed, 85 insertions(+), 87 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 191af5b97f..00047ac926 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,9 +5,9 @@ this file. The format follows Keep a Changelog, and versioned releases follow Semantic Versioning where the repository publishes a release. ## [Unreleased] -- Keep OpenRouter endpoint-feed ZDR evidence provider-neutral: it can inform a - matching direct-provider model when the full identity or final component is - unambiguous, while ambiguous identities remain non-ZDR. +- Keep OpenRouter endpoint-feed ZDR evidence scoped to OpenRouter routes; + direct provider routes remain non-ZDR until a dated provider-specific + attestation exists. - Add a bounded hourly LineageWeave stacked-PR review-repair caller while preserving the existing review-agent, model-routing, and protected-merge boundaries. Product-gap development remains a separately gated coordinator diff --git a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md index db548b3039..86f5f6583e 100644 --- a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md +++ b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md @@ -44,10 +44,10 @@ all five, and auto-optimize routing by cost. mirroring OpenRouter's stance on unascertained policies. The OpenRouter `/api/v1/endpoints/zdr` feed (documented, auto-updated) is fetched when egress allows it and is authoritative for the `openrouter` - scope. Its normalized model identity also qualifies a matching discovered - row from another provider when the final model component is unambiguous; - ambiguous suffixes receive no grant. Otherwise the dated static attestation - table is used, never a fabricated policy. + scope. It never attests a matching model served directly by another + provider; those routes require their own dated provider-specific + attestation. Otherwise the dated static attestation table is used, never a + fabricated policy. `scripts/ci/contextual_orchestrator_review_policy.py` turns the free-tier discovery report into a ZDR-prioritized, provider-family-diverse agents catalog (primary/secondary NVIDIA keys share one outage-domain family), diff --git a/scripts/ci/contextual_orchestrator_review_policy.py b/scripts/ci/contextual_orchestrator_review_policy.py index 1b17f7db91..2f3cf80404 100644 --- a/scripts/ci/contextual_orchestrator_review_policy.py +++ b/scripts/ci/contextual_orchestrator_review_policy.py @@ -47,8 +47,8 @@ "nvidia_nim_sub": "nvidia_nim", } -# OpenRouter's public catalog informs ZDR eligibility for other providers; it -# is not itself an upstream in this review sidecar's model group. +# OpenRouter supplies provider-scoped ZDR evidence but is not itself an +# upstream in this review sidecar's model group. EVIDENCE_ONLY_PROVIDERS = frozenset({"openrouter"}) DEFAULT_CATALOG_LIMIT = 12 @@ -176,8 +176,7 @@ def build_zdr_prioritized_catalog( limit: Maximum number of catalog agents (orchestrator default 12). family_cap: Maximum agents per provider outage-domain family. zdr_endpoints: ``provider/model`` route keys from the OpenRouter ZDR - feed; authoritative for OpenRouter routes and for an exact or - unambiguous model-identity match on another provider row. + feed; authoritative only for OpenRouter routes. require_zdr: Admit only routes with attested ZDR evidence. Intended for private/internal target repositories; an empty ZDR pool fails closed. diff --git a/scripts/ci/load_contextual_orchestrator_token.sh b/scripts/ci/load_contextual_orchestrator_token.sh index af5809df2f..6aeced1594 100755 --- a/scripts/ci/load_contextual_orchestrator_token.sh +++ b/scripts/ci/load_contextual_orchestrator_token.sh @@ -9,7 +9,7 @@ _contextual_orchestrator_token_fail() { } _contextual_orchestrator_stat() { - local format="$1" target="$2" value bsd_format + local format="$1" target="$2" value # Probe the exact GNU/BusyBox operation instead of the implementation's # version flag. BusyBox does not need the BSD fallback when its -c form is @@ -18,7 +18,6 @@ _contextual_orchestrator_stat() { printf '%s\n' "$value" return 0 fi - bsd_format="$format" if [ "$format" = "%a" ]; then if value="$(stat -f '%Mp %Lp' "$target" 2>/dev/null)"; then printf '%s\n' "$value" diff --git a/scripts/ci/opencode_review_receipt_gate.py b/scripts/ci/opencode_review_receipt_gate.py index 5d4e5afb46..51bfa3c222 100644 --- a/scripts/ci/opencode_review_receipt_gate.py +++ b/scripts/ci/opencode_review_receipt_gate.py @@ -16,7 +16,7 @@ SHA_RE = re.compile(r"^[0-9a-fA-F]{40}$") REPO_RE = re.compile( - r"^[A-Za-z0-9_][A-Za-z0-9_.-]*/(?!\.{1,2}$)[A-Za-z0-9_.-][A-Za-z0-9_.-]*$" + r"^[A-Za-z0-9_][A-Za-z0-9_.-]*/(?!\.{1,2}$)[A-Za-z0-9_.][A-Za-z0-9_.-]*$" ) HEAD_SHA_IN_BODY_RE = re.compile(r"Head SHA:\s*`([0-9a-fA-F]{40})`") FORMAL_AUTHORS = frozenset( diff --git a/scripts/ci/zdr_policy.py b/scripts/ci/zdr_policy.py index 5cf7ce93b5..24912aa675 100644 --- a/scripts/ci/zdr_policy.py +++ b/scripts/ci/zdr_policy.py @@ -14,8 +14,8 @@ 1. OpenRouter ZDR endpoint feed (``https://openrouter.ai/api/v1/endpoints/zdr``) — the exact list of OpenRouter endpoints served under a zero-data-retention - policy. It is not an upstream-routing restriction; exact model identity, or - one unambiguous final model component, can inform another provider row. + policy. It is evidence for OpenRouter routes only; a matching model name + does not attest a direct endpoint from another provider. 2. OpenRouter provider data-policy catalog (``https://openrouter.ai/api/frontend/v1/all-providers``) — per-provider ``dataPolicy`` (``retainsPrompts`` / ``retentionDays`` / ``training``), @@ -49,8 +49,8 @@ class ProviderZdrScope: source: URL or document that grounds the attestation. as_of: ISO date the attestation was last verified. note: One-sentence scope note; never fabricated policy language. - openrouter_endpoints_feed: When True, this provider has no static - fallback and requires model-level feed evidence. + openrouter_endpoints_feed: When True, the provider requires exact + membership in the OpenRouter endpoint feed. """ provider_name: str @@ -174,35 +174,6 @@ def route_key(provider_name: str, model: str) -> str: return f"{provider_name}/{model.strip().lstrip('/')}" -def _feed_model_ids(zdr_endpoints: frozenset[str]) -> frozenset[str]: - """Extract normalized model identities from canonical OpenRouter evidence keys.""" - return frozenset( - key.split("/", 1)[1].strip().casefold() - for key in zdr_endpoints - if ( - isinstance(key, str) - and key.casefold().startswith("openrouter/") - and key.split("/", 1)[1].strip() - ) - ) - - -def _feed_model_matches(model: str, feed_model_ids: frozenset[str]) -> bool: - """Match exact ids or one unambiguous final model component.""" - if not isinstance(model, str): - return False - normalized = model.strip().lstrip("/").casefold() - if normalized in feed_model_ids: - return True - suffix = normalized.rsplit("/", 1)[-1] - suffix_matches = { - candidate - for candidate in feed_model_ids - if candidate.rsplit("/", 1)[-1] == suffix - } - return bool(suffix) and len(suffix_matches) == 1 - - def is_zdr_model( provider_name: str, *, @@ -214,24 +185,20 @@ def is_zdr_model( Args: provider_name: Orchestrator provider identifier of the model route. model: Specific model or route identifier. OpenRouter rows require - exact route membership; matching model identity, including one - unambiguous final-component match, can provide evidence for other - configured providers. + exact route membership; the feed does not attest other providers. zdr_endpoints: Frozen set of ``\"provider/model\"`` keys from the OpenRouter ``/api/v1/endpoints/zdr`` feed. An empty set never grants feed-based ZDR. Returns: - True only for an attested zero-retention scope or an exact/unambiguous - feed model-identity match. + True only for an attested zero-retention scope or an exact feed route + membership match. """ scope = provider_zdr_scope(provider_name) if scope.openrouter_endpoints_feed: - if not zdr_endpoints or not model: + if not zdr_endpoints or not isinstance(model, str) or not model: return False return route_key(provider_name, model) in zdr_endpoints - if model and zdr_endpoints and _feed_model_matches(model, _feed_model_ids(zdr_endpoints)): - return True return scope.zero_data_retention diff --git a/tests/test_contextual_orchestrator_review_policy.py b/tests/test_contextual_orchestrator_review_policy.py index 95583eb21b..dd5d58c2ed 100644 --- a/tests/test_contextual_orchestrator_review_policy.py +++ b/tests/test_contextual_orchestrator_review_policy.py @@ -3,6 +3,7 @@ from __future__ import annotations import json +from dataclasses import replace import pytest @@ -157,8 +158,8 @@ def test_parse_discovery_report_rejects_invalid_rows(report: dict[str, object]) policy.parse_discovery_report(report) -def test_build_catalog_is_zdr_first_and_free_only() -> None: - """ZDR-compliant routes outrank non-ZDR free routes; priced routes stay out.""" +def test_build_catalog_is_free_only_and_provider_scoped() -> None: + """Priced routes stay out and cross-provider feed evidence grants no ZDR.""" result = policy.build_zdr_prioritized_catalog( policy.parse_discovery_report(_report()), limit=12, @@ -168,12 +169,12 @@ def test_build_catalog_is_zdr_first_and_free_only() -> None: agents = result["agents"] assert agents[0]["model"] == "deepseek/deepseek-r1:free" assert agents[0]["provider_name"] == "nvidia_nim" - assert "zdr" in agents[0]["tags"] + assert "non-zdr" in agents[0]["tags"] assert all(agent["provider_name"] != "openrouter" for agent in agents) models = [agent["model"] for agent in agents] assert "gpt-4.1" not in models assert result["report"]["pool"] == "orchestrator/free" - assert result["report"]["zdr_selected_count"] == 1 + assert result["report"]["zdr_selected_count"] == 0 assert result["report"]["zdr_endpoints_feed_used"] is True assert result["report"]["selected_count"] == len(agents) for agent in agents: @@ -182,8 +183,8 @@ def test_build_catalog_is_zdr_first_and_free_only() -> None: assert agent["credential_key"] -def test_build_catalog_applies_feed_model_evidence_to_other_provider() -> None: - """A matching model identity can attest a discovered provider row.""" +def test_build_catalog_keeps_feed_evidence_provider_specific() -> None: + """OpenRouter evidence does not attest a direct provider row.""" result = policy.build_zdr_prioritized_catalog( policy.parse_discovery_report( { @@ -200,11 +201,40 @@ def test_build_catalog_applies_feed_model_evidence_to_other_provider() -> None: zdr_endpoints=ZDR_FEED, ) assert result["agents"][0]["provider_name"] == "nvidia_nim" - assert "zdr" in result["agents"][0]["tags"] + assert "non-zdr" in result["agents"][0]["tags"] + assert result["report"]["zdr_selected_count"] == 0 + assert result["report"]["zdr_sources"] == [] + + +def test_build_catalog_accepts_provider_specific_attestation(monkeypatch) -> None: + """A dated direct-provider attestation can authorize its own route.""" + monkeypatch.setitem( + zdr_policy.PROVIDER_ZDR_SCOPE, + "nvidia_nim", + replace( + zdr_policy.PROVIDER_ZDR_SCOPE["nvidia_nim"], + zero_data_retention=True, + source="https://provider.example/zdr", + as_of="2026-08-28", + ), + ) + result = policy.build_zdr_prioritized_catalog( + policy.parse_discovery_report( + { + "models": [ + { + "provider": "nvidia_nim", + "model": "deepseek/deepseek-r1:free", + "agent_id": "nim_deepseek_r1", + "is_free": True, + } + ] + } + ), + require_zdr=True, + ) assert result["report"]["zdr_selected_count"] == 1 - assert result["report"]["zdr_sources"] == [ - "https://openrouter.ai/api/v1/endpoints/zdr" - ] + assert result["agents"][0]["tags"][-1] == "zdr" def test_build_catalog_assigns_unique_priorities() -> None: @@ -432,21 +462,16 @@ def test_main_requires_discovery_report_arg() -> None: with pytest.raises(SystemExit): policy.main(["--out", "x.json", "--report", "y.json"]) -def test_private_catalog_admits_only_attested_zdr_routes() -> None: - """Private-target evidence never falls through to a non-ZDR free route.""" - result = policy.build_zdr_prioritized_catalog( - policy.parse_discovery_report(_report()), - limit=12, - family_cap=4, - zdr_endpoints=ZDR_FEED, - require_zdr=True, - ) - - assert result["agents"] - assert all("zdr" in agent["tags"] for agent in result["agents"]) - assert all("non-zdr" not in agent["tags"] for agent in result["agents"]) - assert result["report"]["zdr_required"] is True - assert result["report"]["selected_count"] == 1 +def test_private_catalog_rejects_cross_provider_zdr_evidence() -> None: + """An OpenRouter feed cannot authorize a direct route for a private target.""" + with pytest.raises(policy.PolicyError, match="ZDR"): + policy.build_zdr_prioritized_catalog( + policy.parse_discovery_report(_report()), + limit=12, + family_cap=4, + zdr_endpoints=ZDR_FEED, + require_zdr=True, + ) def test_private_catalog_fails_closed_without_attested_zdr_route() -> None: diff --git a/tests/test_opencode_review_receipt_gate.py b/tests/test_opencode_review_receipt_gate.py index 69fe025564..047bb70576 100644 --- a/tests/test_opencode_review_receipt_gate.py +++ b/tests/test_opencode_review_receipt_gate.py @@ -267,6 +267,7 @@ def fake_run(args, **kwargs): assert receipt.REPO_RE.fullmatch("ContextualWisdomLab/.github") assert not receipt.REPO_RE.fullmatch("owner/.") assert not receipt.REPO_RE.fullmatch("owner/..") + assert not receipt.REPO_RE.fullmatch("owner/-repo") def fake_fail(args, **kwargs): return type("Completed", (), {"returncode": 1, "stdout": "", "stderr": "nope"})() diff --git a/tests/test_zdr_policy.py b/tests/test_zdr_policy.py index d2139dff8f..cebb3d81a5 100644 --- a/tests/test_zdr_policy.py +++ b/tests/test_zdr_policy.py @@ -36,9 +36,16 @@ def test_provider_zdr_scope_rejects_unknown_provider() -> None: zdr_policy.provider_zdr_scope("made_up_provider") -def test_feed_model_matches_rejects_non_string_model() -> None: - """Defensive matching must fail closed for malformed model values.""" - assert zdr_policy._feed_model_matches(None, frozenset()) is False +def test_is_zdr_model_rejects_non_string_model() -> None: + """Defensive route evaluation must fail closed for malformed model values.""" + assert ( + zdr_policy.is_zdr_model( + "openrouter", + model=object(), # type: ignore[arg-type] + zdr_endpoints=frozenset({"openrouter/provider/model"}), + ) + is False + ) @pytest.mark.parametrize( @@ -102,8 +109,8 @@ def test_route_key_strips_a_leading_slash() -> None: ) -def test_is_zdr_model_feed_evidence_applies_to_other_provider_rows() -> None: - """A feed model identity can attest a matching non-OpenRouter row.""" +def test_is_zdr_model_feed_evidence_remains_provider_specific() -> None: + """An OpenRouter endpoint feed cannot attest direct provider endpoints.""" feed = frozenset({"openrouter/deepseek/deepseek-r1:free"}) assert ( zdr_policy.is_zdr_model( @@ -111,7 +118,7 @@ def test_is_zdr_model_feed_evidence_applies_to_other_provider_rows() -> None: model="deepseek/deepseek-r1:free", zdr_endpoints=feed, ) - is True + is False ) assert ( zdr_policy.is_zdr_model( @@ -127,7 +134,7 @@ def test_is_zdr_model_feed_evidence_applies_to_other_provider_rows() -> None: model="deepseek-r1:free", zdr_endpoints=feed, ) - is True + is False ) assert ( zdr_policy.is_zdr_model( From a0dcf3555b8c1286011c22cbd8c9619b7542078e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 01:35:26 +0900 Subject: [PATCH 28/76] fix(opencode): align repository identity validation --- scripts/ci/opencode_coverage_identity.py | 4 +++- tests/test_opencode_coverage_identity.py | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/scripts/ci/opencode_coverage_identity.py b/scripts/ci/opencode_coverage_identity.py index 0bf439e746..52db3c5bb3 100644 --- a/scripts/ci/opencode_coverage_identity.py +++ b/scripts/ci/opencode_coverage_identity.py @@ -19,7 +19,9 @@ CANONICAL_WORKFLOW_NAMES = frozenset({"Required OpenCode Review"}) DISPATCH_WORKFLOW_NAME = "OpenCode Review Dispatch" SHA_RE = re.compile(r"^[0-9a-fA-F]{40}$") -REPO_RE = re.compile(r"^[A-Za-z0-9_][A-Za-z0-9_.-]*/[A-Za-z0-9_.-]+$") +REPO_RE = re.compile( + r"^[A-Za-z0-9_][A-Za-z0-9_.-]*/(?!\.{1,2}$)[A-Za-z0-9_.][A-Za-z0-9_.-]*$" +) TERMINAL_RESULTS = frozenset( {"success", "failure", "cancelled", "skipped", "neutral", "timed_out", "action_required"} ) diff --git a/tests/test_opencode_coverage_identity.py b/tests/test_opencode_coverage_identity.py index c446901008..8ebec8aaa3 100644 --- a/tests/test_opencode_coverage_identity.py +++ b/tests/test_opencode_coverage_identity.py @@ -150,6 +150,14 @@ def unexpected_run(args, **kwargs): identity.fetch_check_runs("ContextualWisdomLab/kaefa", "not-a-sha") +def test_repository_identity_accepts_leading_dot_but_rejects_path_segments() -> None: + """Central dot repositories are valid while dot paths and options fail closed.""" + assert identity.REPO_RE.fullmatch("ContextualWisdomLab/.github") + assert not identity.REPO_RE.fullmatch("owner/.") + assert not identity.REPO_RE.fullmatch("owner/..") + assert not identity.REPO_RE.fullmatch("owner/-repo") + + def test_fetch_check_runs_retries_transient_github_read_failure(monkeypatch) -> None: """A transient 429 is retried before exact-head identity fails closed.""" From bdca63992aeb28112fbab66eec86c621f74cca6e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 01:35:44 +0900 Subject: [PATCH 29/76] docs(changelog): record repository identity boundary --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 00047ac926..834bcdf31c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ this file. The format follows Keep a Changelog, and versioned releases follow Semantic Versioning where the repository publishes a release. ## [Unreleased] +- Accept valid leading-dot repository names such as `ContextualWisdomLab/.github` + in both OpenCode receipt and coverage-identity gates while rejecting dot-path + segments and option-like names before any GitHub CLI call. - Keep OpenRouter endpoint-feed ZDR evidence scoped to OpenRouter routes; direct provider routes remain non-ZDR until a dated provider-specific attestation exists. From 5b231739758a555e850536682da380e81e9c67ec Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 01:36:30 +0900 Subject: [PATCH 30/76] docs(changelog): record portable token stat gate --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 834bcdf31c..740ac94ea2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ this file. The format follows Keep a Changelog, and versioned releases follow Semantic Versioning where the repository publishes a release. ## [Unreleased] +- Preserve the private contextual-orchestrator bearer-file owner and mode gate + across GNU, BusyBox, and BSD/macOS `stat` implementations without relaxing + the required current-user ownership or exact mode `600` contract. - Accept valid leading-dot repository names such as `ContextualWisdomLab/.github` in both OpenCode receipt and coverage-identity gates while rejecting dot-path segments and option-like names before any GitHub CLI call. From 8321a574ee0a01e9138cfe03c7bf80f44dd37fd5 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 01:37:54 +0900 Subject: [PATCH 31/76] refactor(ci): remove unreachable feed source branch --- scripts/ci/contextual_orchestrator_review_policy.py | 11 +---------- tests/test_contextual_orchestrator_review_policy.py | 1 + 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/scripts/ci/contextual_orchestrator_review_policy.py b/scripts/ci/contextual_orchestrator_review_policy.py index 2f3cf80404..94b8f4c2cb 100644 --- a/scripts/ci/contextual_orchestrator_review_policy.py +++ b/scripts/ci/contextual_orchestrator_review_policy.py @@ -30,7 +30,6 @@ from typing import Any, Iterable, Mapping from scripts.ci.zdr_policy import ( - OPENROUTER_ZDR_ENDPOINTS_SOURCE, PROVIDER_AUTH_SCHEMES, PROVIDER_BASE_URLS, PROVIDER_CREDENTIAL_NAMES, @@ -275,15 +274,7 @@ def family_is_open(family: str) -> bool: "zdr_selected_count": zdr_count, "zdr_sources": sorted( { - ( - OPENROUTER_ZDR_ENDPOINTS_SOURCE - if zdr_endpoints - and ( - provider_zdr_scope(row["provider"]).openrouter_endpoints_feed - or not provider_zdr_scope(row["provider"]).zero_data_retention - ) - else provider_zdr_scope(row["provider"]).source - ) + provider_zdr_scope(row["provider"]).source for row in picked if is_zdr_model( row["provider"], model=row["model"], zdr_endpoints=zdr_endpoints diff --git a/tests/test_contextual_orchestrator_review_policy.py b/tests/test_contextual_orchestrator_review_policy.py index dd5d58c2ed..1a5ddd021a 100644 --- a/tests/test_contextual_orchestrator_review_policy.py +++ b/tests/test_contextual_orchestrator_review_policy.py @@ -234,6 +234,7 @@ def test_build_catalog_accepts_provider_specific_attestation(monkeypatch) -> Non require_zdr=True, ) assert result["report"]["zdr_selected_count"] == 1 + assert result["report"]["zdr_sources"] == ["https://provider.example/zdr"] assert result["agents"][0]["tags"][-1] == "zdr" From 373a20074acb07c7d149dfee2e1de267492d37f4 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 01:45:22 +0900 Subject: [PATCH 32/76] chore: pin latest evidence-only routing fix --- docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md | 2 +- scripts/ci/contextual_orchestrator_review_sidecar.sh | 2 +- tests/test_contextual_orchestrator_review_sidecar_contract.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md index 86f5f6583e..fd3de25639 100644 --- a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md +++ b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md @@ -22,7 +22,7 @@ all five, and auto-optimize routing by cost. 1. **Vendoring, pinned**: `scripts/ci/contextual_orchestrator_review_sidecar.sh` clones `ContextualWisdomLab/contextual-orchestrator` at an exact SHA - (`6549937834941895ae3e379f82cbf37eb645a59a` today) into `RUNNER_TEMP`. The + (`588aef38be5e46dbf23a29b9b6767c5b1ebd38dc` today) into `RUNNER_TEMP`. The source's `requirements.lock` is installed with `--require-hashes` and `--no-deps`, so dependency resolution cannot silently move the reviewed runtime. diff --git a/scripts/ci/contextual_orchestrator_review_sidecar.sh b/scripts/ci/contextual_orchestrator_review_sidecar.sh index e9c2c1010c..ba0651f3d7 100755 --- a/scripts/ci/contextual_orchestrator_review_sidecar.sh +++ b/scripts/ci/contextual_orchestrator_review_sidecar.sh @@ -14,7 +14,7 @@ # (fail-closed zero-cost) pool. set -euo pipefail -ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-6549937834941895ae3e379f82cbf37eb645a59a}" +ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-588aef38be5e46dbf23a29b9b6767c5b1ebd38dc}" ORCHESTRATOR_GIT_URL="${ORCHESTRATOR_GIT_URL:-https://github.com/ContextualWisdomLab/contextual-orchestrator.git}" # The Strix gate and Noema SSRF guard accept this one process-local origin. # Keep it fixed so an environment override cannot create an unvalidated sidecar. diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index 656f0c75e2..05bb8627d5 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -38,7 +38,7 @@ ) GATEWAY_MODEL = "contextual-orchestrator/orchestrator/free" -ORCH_PIN_SHA = "6549937834941895ae3e379f82cbf37eb645a59a" +ORCH_PIN_SHA = "588aef38be5e46dbf23a29b9b6767c5b1ebd38dc" def _read(path: Path) -> str: From 841ce401476896aed5475dd0d0472f4d8444fcec Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 02:07:41 +0900 Subject: [PATCH 33/76] chore: pin latest model selection fix --- docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md | 2 +- scripts/ci/contextual_orchestrator_review_sidecar.sh | 2 +- tests/test_contextual_orchestrator_review_sidecar_contract.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md index fd3de25639..350f5a5224 100644 --- a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md +++ b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md @@ -22,7 +22,7 @@ all five, and auto-optimize routing by cost. 1. **Vendoring, pinned**: `scripts/ci/contextual_orchestrator_review_sidecar.sh` clones `ContextualWisdomLab/contextual-orchestrator` at an exact SHA - (`588aef38be5e46dbf23a29b9b6767c5b1ebd38dc` today) into `RUNNER_TEMP`. The + (`caab2ed1521663f8ba773a70a2a15fa55971b9e9` today) into `RUNNER_TEMP`. The source's `requirements.lock` is installed with `--require-hashes` and `--no-deps`, so dependency resolution cannot silently move the reviewed runtime. diff --git a/scripts/ci/contextual_orchestrator_review_sidecar.sh b/scripts/ci/contextual_orchestrator_review_sidecar.sh index ba0651f3d7..7986f64b6c 100755 --- a/scripts/ci/contextual_orchestrator_review_sidecar.sh +++ b/scripts/ci/contextual_orchestrator_review_sidecar.sh @@ -14,7 +14,7 @@ # (fail-closed zero-cost) pool. set -euo pipefail -ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-588aef38be5e46dbf23a29b9b6767c5b1ebd38dc}" +ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-caab2ed1521663f8ba773a70a2a15fa55971b9e9}" ORCHESTRATOR_GIT_URL="${ORCHESTRATOR_GIT_URL:-https://github.com/ContextualWisdomLab/contextual-orchestrator.git}" # The Strix gate and Noema SSRF guard accept this one process-local origin. # Keep it fixed so an environment override cannot create an unvalidated sidecar. diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index 05bb8627d5..b215478486 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -38,7 +38,7 @@ ) GATEWAY_MODEL = "contextual-orchestrator/orchestrator/free" -ORCH_PIN_SHA = "588aef38be5e46dbf23a29b9b6767c5b1ebd38dc" +ORCH_PIN_SHA = "caab2ed1521663f8ba773a70a2a15fa55971b9e9" def _read(path: Path) -> str: From e9579157b85443b937fd2b4979c952c3afb3cff6 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 02:24:58 +0900 Subject: [PATCH 34/76] chore: pin serving-agent guard --- docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md | 2 +- scripts/ci/contextual_orchestrator_review_sidecar.sh | 2 +- tests/test_contextual_orchestrator_review_sidecar_contract.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md index 350f5a5224..6cf351effa 100644 --- a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md +++ b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md @@ -22,7 +22,7 @@ all five, and auto-optimize routing by cost. 1. **Vendoring, pinned**: `scripts/ci/contextual_orchestrator_review_sidecar.sh` clones `ContextualWisdomLab/contextual-orchestrator` at an exact SHA - (`caab2ed1521663f8ba773a70a2a15fa55971b9e9` today) into `RUNNER_TEMP`. The + (`cbb9d6eec237a6bb42cc177586a893bc5f281e7d` today) into `RUNNER_TEMP`. The source's `requirements.lock` is installed with `--require-hashes` and `--no-deps`, so dependency resolution cannot silently move the reviewed runtime. diff --git a/scripts/ci/contextual_orchestrator_review_sidecar.sh b/scripts/ci/contextual_orchestrator_review_sidecar.sh index 7986f64b6c..2203b4ba91 100755 --- a/scripts/ci/contextual_orchestrator_review_sidecar.sh +++ b/scripts/ci/contextual_orchestrator_review_sidecar.sh @@ -14,7 +14,7 @@ # (fail-closed zero-cost) pool. set -euo pipefail -ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-caab2ed1521663f8ba773a70a2a15fa55971b9e9}" +ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-cbb9d6eec237a6bb42cc177586a893bc5f281e7d}" ORCHESTRATOR_GIT_URL="${ORCHESTRATOR_GIT_URL:-https://github.com/ContextualWisdomLab/contextual-orchestrator.git}" # The Strix gate and Noema SSRF guard accept this one process-local origin. # Keep it fixed so an environment override cannot create an unvalidated sidecar. diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index b215478486..ec2edf568d 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -38,7 +38,7 @@ ) GATEWAY_MODEL = "contextual-orchestrator/orchestrator/free" -ORCH_PIN_SHA = "caab2ed1521663f8ba773a70a2a15fa55971b9e9" +ORCH_PIN_SHA = "cbb9d6eec237a6bb42cc177586a893bc5f281e7d" def _read(path: Path) -> str: From c63b5f5fd905223871f7c3c2efd5bc70cf43933a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 03:29:02 +0900 Subject: [PATCH 35/76] fix: pin current contextual orchestrator sidecar --- docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md | 2 +- scripts/ci/contextual_orchestrator_review_sidecar.sh | 2 +- tests/test_contextual_orchestrator_review_sidecar_contract.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md index 6cf351effa..91d1e7992f 100644 --- a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md +++ b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md @@ -22,7 +22,7 @@ all five, and auto-optimize routing by cost. 1. **Vendoring, pinned**: `scripts/ci/contextual_orchestrator_review_sidecar.sh` clones `ContextualWisdomLab/contextual-orchestrator` at an exact SHA - (`cbb9d6eec237a6bb42cc177586a893bc5f281e7d` today) into `RUNNER_TEMP`. The + (`f5abd8759a457184f0046c7a13253c3d0b47049b` today) into `RUNNER_TEMP`. The source's `requirements.lock` is installed with `--require-hashes` and `--no-deps`, so dependency resolution cannot silently move the reviewed runtime. diff --git a/scripts/ci/contextual_orchestrator_review_sidecar.sh b/scripts/ci/contextual_orchestrator_review_sidecar.sh index 2203b4ba91..a72e228ced 100755 --- a/scripts/ci/contextual_orchestrator_review_sidecar.sh +++ b/scripts/ci/contextual_orchestrator_review_sidecar.sh @@ -14,7 +14,7 @@ # (fail-closed zero-cost) pool. set -euo pipefail -ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-cbb9d6eec237a6bb42cc177586a893bc5f281e7d}" +ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-f5abd8759a457184f0046c7a13253c3d0b47049b}" ORCHESTRATOR_GIT_URL="${ORCHESTRATOR_GIT_URL:-https://github.com/ContextualWisdomLab/contextual-orchestrator.git}" # The Strix gate and Noema SSRF guard accept this one process-local origin. # Keep it fixed so an environment override cannot create an unvalidated sidecar. diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index ec2edf568d..cd39dcb33b 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -38,7 +38,7 @@ ) GATEWAY_MODEL = "contextual-orchestrator/orchestrator/free" -ORCH_PIN_SHA = "cbb9d6eec237a6bb42cc177586a893bc5f281e7d" +ORCH_PIN_SHA = "f5abd8759a457184f0046c7a13253c3d0b47049b" def _read(path: Path) -> str: From ae7bb0d2a47890f6f2728497be801af89b987900 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 03:48:53 +0900 Subject: [PATCH 36/76] fix: pin current contextual orchestrator head --- docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md | 2 +- scripts/ci/contextual_orchestrator_review_sidecar.sh | 2 +- tests/test_contextual_orchestrator_review_sidecar_contract.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md index 91d1e7992f..168acc8f8e 100644 --- a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md +++ b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md @@ -22,7 +22,7 @@ all five, and auto-optimize routing by cost. 1. **Vendoring, pinned**: `scripts/ci/contextual_orchestrator_review_sidecar.sh` clones `ContextualWisdomLab/contextual-orchestrator` at an exact SHA - (`f5abd8759a457184f0046c7a13253c3d0b47049b` today) into `RUNNER_TEMP`. The + (`29d9493fcdbf11aaa3d43bc6c7e10857bb85ca73` today) into `RUNNER_TEMP`. The source's `requirements.lock` is installed with `--require-hashes` and `--no-deps`, so dependency resolution cannot silently move the reviewed runtime. diff --git a/scripts/ci/contextual_orchestrator_review_sidecar.sh b/scripts/ci/contextual_orchestrator_review_sidecar.sh index a72e228ced..33e1a751de 100755 --- a/scripts/ci/contextual_orchestrator_review_sidecar.sh +++ b/scripts/ci/contextual_orchestrator_review_sidecar.sh @@ -14,7 +14,7 @@ # (fail-closed zero-cost) pool. set -euo pipefail -ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-f5abd8759a457184f0046c7a13253c3d0b47049b}" +ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-29d9493fcdbf11aaa3d43bc6c7e10857bb85ca73}" ORCHESTRATOR_GIT_URL="${ORCHESTRATOR_GIT_URL:-https://github.com/ContextualWisdomLab/contextual-orchestrator.git}" # The Strix gate and Noema SSRF guard accept this one process-local origin. # Keep it fixed so an environment override cannot create an unvalidated sidecar. diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index cd39dcb33b..189645ba71 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -38,7 +38,7 @@ ) GATEWAY_MODEL = "contextual-orchestrator/orchestrator/free" -ORCH_PIN_SHA = "f5abd8759a457184f0046c7a13253c3d0b47049b" +ORCH_PIN_SHA = "29d9493fcdbf11aaa3d43bc6c7e10857bb85ca73" def _read(path: Path) -> str: From e7a6709b62a9cf16e69d397e40ed7dbb8ebde313 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 04:17:05 +0900 Subject: [PATCH 37/76] fix: apply ZDR model evidence across candidate providers --- ...ntextual-orchestrator-vendored-free-zdr.md | 10 +-- .../contextual_orchestrator_review_policy.py | 10 ++- scripts/ci/zdr_policy.py | 66 +++++++++++++++---- ...t_contextual_orchestrator_review_policy.py | 39 +++++------ tests/test_zdr_policy.py | 8 +-- 5 files changed, 91 insertions(+), 42 deletions(-) diff --git a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md index 168acc8f8e..e5cb9c6b23 100644 --- a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md +++ b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md @@ -43,11 +43,11 @@ all five, and auto-optimize routing by cost. attested from a machine-readable, dated source is treated as non-ZDR, mirroring OpenRouter's stance on unascertained policies. The OpenRouter `/api/v1/endpoints/zdr` feed (documented, auto-updated) is - fetched when egress allows it and is authoritative for the `openrouter` - scope. It never attests a matching model served directly by another - provider; those routes require their own dated provider-specific - attestation. Otherwise the dated static attestation table is used, never a - fabricated policy. + fetched when egress allows it. OpenRouter routes require exact feed + membership; for every other discovered provider, the feed's model identity + is used as selection evidence for a matching candidate row. Nonmatching or + ambiguous candidates remain non-ZDR. Otherwise the dated static attestation + table is used, never a fabricated policy. `scripts/ci/contextual_orchestrator_review_policy.py` turns the free-tier discovery report into a ZDR-prioritized, provider-family-diverse agents catalog (primary/secondary NVIDIA keys share one outage-domain family), diff --git a/scripts/ci/contextual_orchestrator_review_policy.py b/scripts/ci/contextual_orchestrator_review_policy.py index 94b8f4c2cb..50d5e38325 100644 --- a/scripts/ci/contextual_orchestrator_review_policy.py +++ b/scripts/ci/contextual_orchestrator_review_policy.py @@ -8,6 +8,7 @@ 1. reads the same ``discover-models`` report the orchestrator prints, 2. keeps only free (zero-cost), known-provider chat routes, 3. treats OpenRouter as a ZDR evidence source rather than a routed upstream, + applies matching model evidence to every caller-supplied provider row, orders the remaining routes ZDR-compliant first, then non-ZDR free, with a provider-family cap so a single outage domain cannot monopolize the pool, 4. writes an ``agents`` JSON catalog in the orchestrator's own @@ -35,7 +36,7 @@ PROVIDER_CREDENTIAL_NAMES, is_free_route, is_zdr_model, - provider_zdr_scope, + zdr_evidence_source, route_key, ) @@ -175,7 +176,8 @@ def build_zdr_prioritized_catalog( limit: Maximum number of catalog agents (orchestrator default 12). family_cap: Maximum agents per provider outage-domain family. zdr_endpoints: ``provider/model`` route keys from the OpenRouter ZDR - feed; authoritative only for OpenRouter routes. + feed; used as model-level evidence for matching caller-supplied + provider rows, while OpenRouter rows require exact membership. require_zdr: Admit only routes with attested ZDR evidence. Intended for private/internal target repositories; an empty ZDR pool fails closed. @@ -274,7 +276,9 @@ def family_is_open(family: str) -> bool: "zdr_selected_count": zdr_count, "zdr_sources": sorted( { - provider_zdr_scope(row["provider"]).source + zdr_evidence_source( + row["provider"], model=row["model"], zdr_endpoints=zdr_endpoints + ) for row in picked if is_zdr_model( row["provider"], model=row["model"], zdr_endpoints=zdr_endpoints diff --git a/scripts/ci/zdr_policy.py b/scripts/ci/zdr_policy.py index 24912aa675..fcda7516e2 100644 --- a/scripts/ci/zdr_policy.py +++ b/scripts/ci/zdr_policy.py @@ -13,9 +13,9 @@ Two authoritative, machine-readable sources feed the policy at runtime: 1. OpenRouter ZDR endpoint feed (``https://openrouter.ai/api/v1/endpoints/zdr``) - — the exact list of OpenRouter endpoints served under a zero-data-retention - policy. It is evidence for OpenRouter routes only; a matching model name - does not attest a direct endpoint from another provider. + — the machine-readable model evidence used to match discovered model + identities across the caller's candidate providers. It is not a routing + target; direct OpenRouter routes still require exact feed membership. 2. OpenRouter provider data-policy catalog (``https://openrouter.ai/api/frontend/v1/all-providers``) — per-provider ``dataPolicy`` (``retainsPrompts`` / ``retentionDays`` / ``training``), @@ -50,7 +50,8 @@ class ProviderZdrScope: as_of: ISO date the attestation was last verified. note: One-sentence scope note; never fabricated policy language. openrouter_endpoints_feed: When True, the provider requires exact - membership in the OpenRouter endpoint feed. + membership in the OpenRouter endpoint feed; other providers may + use the feed as model-level evidence for matching candidates. """ provider_name: str @@ -185,21 +186,64 @@ def is_zdr_model( Args: provider_name: Orchestrator provider identifier of the model route. model: Specific model or route identifier. OpenRouter rows require - exact route membership; the feed does not attest other providers. + exact route membership; other provider rows may match the same + model identity in the feed. zdr_endpoints: Frozen set of ``\"provider/model\"`` keys from the OpenRouter ``/api/v1/endpoints/zdr`` feed. An empty set never grants feed-based ZDR. Returns: - True only for an attested zero-retention scope or an exact feed route - membership match. + True only for an attested zero-retention scope, an exact OpenRouter + feed route, or an unambiguous matching model identity from that feed. """ + return zdr_evidence_source( + provider_name, model=model, zdr_endpoints=zdr_endpoints + ) is not None + + +def zdr_evidence_source( + provider_name: str, + *, + model: str | None = None, + zdr_endpoints: frozenset[str] = frozenset(), +) -> str | None: + """Return the source that attests one model, or ``None`` when unattested.""" scope = provider_zdr_scope(provider_name) + if not isinstance(model, str) or not model.strip(): + return ( + scope.source + if scope.zero_data_retention and not scope.openrouter_endpoints_feed + else None + ) + candidate = model.strip().lstrip("/").casefold() + feed = {str(endpoint).strip().casefold() for endpoint in zdr_endpoints if str(endpoint).strip()} if scope.openrouter_endpoints_feed: - if not zdr_endpoints or not isinstance(model, str) or not model: - return False - return route_key(provider_name, model) in zdr_endpoints - return scope.zero_data_retention + return ( + OPENROUTER_ZDR_ENDPOINTS_SOURCE + if feed and route_key(provider_name, model).casefold() in feed + else None + ) + elif feed: + matched = route_key(provider_name, model).casefold() in feed + feed_models = { + endpoint.split("/", 1)[1] if "/" in endpoint else endpoint + for endpoint in feed + } + if not matched and candidate in feed_models: + matched = True + if not matched: + suffix = candidate.rsplit("/", 1)[-1] + suffix_matches = [ + feed_model + for feed_model in feed_models + if feed_model.rsplit("/", 1)[-1] == suffix + ] + matched = bool(suffix) and len(suffix_matches) == 1 + else: + matched = False + if matched: + return OPENROUTER_ZDR_ENDPOINTS_SOURCE + return scope.source if scope.zero_data_retention else None def is_free_route(is_free: object) -> bool: diff --git a/tests/test_contextual_orchestrator_review_policy.py b/tests/test_contextual_orchestrator_review_policy.py index 1a5ddd021a..36ae47d660 100644 --- a/tests/test_contextual_orchestrator_review_policy.py +++ b/tests/test_contextual_orchestrator_review_policy.py @@ -158,8 +158,8 @@ def test_parse_discovery_report_rejects_invalid_rows(report: dict[str, object]) policy.parse_discovery_report(report) -def test_build_catalog_is_free_only_and_provider_scoped() -> None: - """Priced routes stay out and cross-provider feed evidence grants no ZDR.""" +def test_build_catalog_is_free_only_and_applies_model_evidence_across_providers() -> None: + """Priced routes stay out and matching feed evidence marks another provider.""" result = policy.build_zdr_prioritized_catalog( policy.parse_discovery_report(_report()), limit=12, @@ -169,12 +169,12 @@ def test_build_catalog_is_free_only_and_provider_scoped() -> None: agents = result["agents"] assert agents[0]["model"] == "deepseek/deepseek-r1:free" assert agents[0]["provider_name"] == "nvidia_nim" - assert "non-zdr" in agents[0]["tags"] + assert "zdr" in agents[0]["tags"] assert all(agent["provider_name"] != "openrouter" for agent in agents) models = [agent["model"] for agent in agents] assert "gpt-4.1" not in models assert result["report"]["pool"] == "orchestrator/free" - assert result["report"]["zdr_selected_count"] == 0 + assert result["report"]["zdr_selected_count"] == 1 assert result["report"]["zdr_endpoints_feed_used"] is True assert result["report"]["selected_count"] == len(agents) for agent in agents: @@ -183,8 +183,8 @@ def test_build_catalog_is_free_only_and_provider_scoped() -> None: assert agent["credential_key"] -def test_build_catalog_keeps_feed_evidence_provider_specific() -> None: - """OpenRouter evidence does not attest a direct provider row.""" +def test_build_catalog_applies_feed_evidence_to_a_direct_provider_row() -> None: + """OpenRouter model evidence can select a matching direct provider row.""" result = policy.build_zdr_prioritized_catalog( policy.parse_discovery_report( { @@ -201,9 +201,9 @@ def test_build_catalog_keeps_feed_evidence_provider_specific() -> None: zdr_endpoints=ZDR_FEED, ) assert result["agents"][0]["provider_name"] == "nvidia_nim" - assert "non-zdr" in result["agents"][0]["tags"] - assert result["report"]["zdr_selected_count"] == 0 - assert result["report"]["zdr_sources"] == [] + assert "zdr" in result["agents"][0]["tags"] + assert result["report"]["zdr_selected_count"] == 1 + assert result["report"]["zdr_sources"] == [zdr_policy.OPENROUTER_ZDR_ENDPOINTS_SOURCE] def test_build_catalog_accepts_provider_specific_attestation(monkeypatch) -> None: @@ -463,16 +463,17 @@ def test_main_requires_discovery_report_arg() -> None: with pytest.raises(SystemExit): policy.main(["--out", "x.json", "--report", "y.json"]) -def test_private_catalog_rejects_cross_provider_zdr_evidence() -> None: - """An OpenRouter feed cannot authorize a direct route for a private target.""" - with pytest.raises(policy.PolicyError, match="ZDR"): - policy.build_zdr_prioritized_catalog( - policy.parse_discovery_report(_report()), - limit=12, - family_cap=4, - zdr_endpoints=ZDR_FEED, - require_zdr=True, - ) +def test_private_catalog_accepts_cross_provider_model_evidence() -> None: + """A matching OpenRouter model feed entry authorizes the direct candidate.""" + result = policy.build_zdr_prioritized_catalog( + policy.parse_discovery_report(_report()), + limit=12, + family_cap=4, + zdr_endpoints=ZDR_FEED, + require_zdr=True, + ) + assert result["report"]["zdr_selected_count"] == 1 + assert result["agents"][0]["provider_name"] == "nvidia_nim" def test_private_catalog_fails_closed_without_attested_zdr_route() -> None: diff --git a/tests/test_zdr_policy.py b/tests/test_zdr_policy.py index cebb3d81a5..64ee647274 100644 --- a/tests/test_zdr_policy.py +++ b/tests/test_zdr_policy.py @@ -109,8 +109,8 @@ def test_route_key_strips_a_leading_slash() -> None: ) -def test_is_zdr_model_feed_evidence_remains_provider_specific() -> None: - """An OpenRouter endpoint feed cannot attest direct provider endpoints.""" +def test_is_zdr_model_feed_evidence_matches_other_provider_model_ids() -> None: + """OpenRouter model evidence selects matching candidates from other providers.""" feed = frozenset({"openrouter/deepseek/deepseek-r1:free"}) assert ( zdr_policy.is_zdr_model( @@ -118,7 +118,7 @@ def test_is_zdr_model_feed_evidence_remains_provider_specific() -> None: model="deepseek/deepseek-r1:free", zdr_endpoints=feed, ) - is False + is True ) assert ( zdr_policy.is_zdr_model( @@ -134,7 +134,7 @@ def test_is_zdr_model_feed_evidence_remains_provider_specific() -> None: model="deepseek-r1:free", zdr_endpoints=feed, ) - is False + is True ) assert ( zdr_policy.is_zdr_model( From 919cb9e7866c58b2e8f1b41b9f44f16829aa159e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 04:39:36 +0900 Subject: [PATCH 38/76] docs: align ZDR changelog with cross-provider evidence --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 740ac94ea2..b477cb6912 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,9 +11,9 @@ Semantic Versioning where the repository publishes a release. - Accept valid leading-dot repository names such as `ContextualWisdomLab/.github` in both OpenCode receipt and coverage-identity gates while rejecting dot-path segments and option-like names before any GitHub CLI call. -- Keep OpenRouter endpoint-feed ZDR evidence scoped to OpenRouter routes; - direct provider routes remain non-ZDR until a dated provider-specific - attestation exists. +- Keep OpenRouter endpoint-feed ZDR evidence exact for OpenRouter routes while + allowing an unambiguous matching feed model identity to attest supplied + non-OpenRouter provider rows; nonmatching or ambiguous rows remain non-ZDR. - Add a bounded hourly LineageWeave stacked-PR review-repair caller while preserving the existing review-agent, model-routing, and protected-merge boundaries. Product-gap development remains a separately gated coordinator From ede8c885242229beb5e25823911d1576d602f134 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 04:45:32 +0900 Subject: [PATCH 39/76] fix: mark ZDR catalog members with privacy tag --- scripts/ci/contextual_orchestrator_review_policy.py | 6 +++++- tests/test_contextual_orchestrator_review_policy.py | 6 +++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/scripts/ci/contextual_orchestrator_review_policy.py b/scripts/ci/contextual_orchestrator_review_policy.py index 50d5e38325..a18fb70dfc 100644 --- a/scripts/ci/contextual_orchestrator_review_policy.py +++ b/scripts/ci/contextual_orchestrator_review_policy.py @@ -251,7 +251,11 @@ def family_is_open(family: str) -> bool: "base_url": row["base_url"], "api_key_env": "", "credential_key": row["credential_key"], - "tags": ["review", "cost:free", "zdr" if zdr else "non-zdr"], + "tags": [ + "review", + "cost:free", + "privacy:zdr" if zdr else "non-zdr", + ], "priority": -rank, "disabled": False, "provider_name": row["provider"], diff --git a/tests/test_contextual_orchestrator_review_policy.py b/tests/test_contextual_orchestrator_review_policy.py index 36ae47d660..7b28a68add 100644 --- a/tests/test_contextual_orchestrator_review_policy.py +++ b/tests/test_contextual_orchestrator_review_policy.py @@ -169,7 +169,7 @@ def test_build_catalog_is_free_only_and_applies_model_evidence_across_providers( agents = result["agents"] assert agents[0]["model"] == "deepseek/deepseek-r1:free" assert agents[0]["provider_name"] == "nvidia_nim" - assert "zdr" in agents[0]["tags"] + assert "privacy:zdr" in agents[0]["tags"] assert all(agent["provider_name"] != "openrouter" for agent in agents) models = [agent["model"] for agent in agents] assert "gpt-4.1" not in models @@ -201,7 +201,7 @@ def test_build_catalog_applies_feed_evidence_to_a_direct_provider_row() -> None: zdr_endpoints=ZDR_FEED, ) assert result["agents"][0]["provider_name"] == "nvidia_nim" - assert "zdr" in result["agents"][0]["tags"] + assert "privacy:zdr" in result["agents"][0]["tags"] assert result["report"]["zdr_selected_count"] == 1 assert result["report"]["zdr_sources"] == [zdr_policy.OPENROUTER_ZDR_ENDPOINTS_SOURCE] @@ -235,7 +235,7 @@ def test_build_catalog_accepts_provider_specific_attestation(monkeypatch) -> Non ) assert result["report"]["zdr_selected_count"] == 1 assert result["report"]["zdr_sources"] == ["https://provider.example/zdr"] - assert result["agents"][0]["tags"][-1] == "zdr" + assert result["agents"][0]["tags"][-1] == "privacy:zdr" def test_build_catalog_assigns_unique_priorities() -> None: From ef58525564d0f0befec8b34d9081e00517c7cf33 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 05:27:47 +0900 Subject: [PATCH 40/76] fix: reject noncanonical ZDR feed keys --- CHANGELOG.md | 7 ++++--- ...0003-contextual-orchestrator-vendored-free-zdr.md | 11 ++++++----- scripts/ci/zdr_policy.py | 6 +++++- tests/test_zdr_policy.py | 12 ++++++++++++ 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b477cb6912..b99f45a9cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,9 +11,10 @@ Semantic Versioning where the repository publishes a release. - Accept valid leading-dot repository names such as `ContextualWisdomLab/.github` in both OpenCode receipt and coverage-identity gates while rejecting dot-path segments and option-like names before any GitHub CLI call. -- Keep OpenRouter endpoint-feed ZDR evidence exact for OpenRouter routes while - allowing an unambiguous matching feed model identity to attest supplied - non-OpenRouter provider rows; nonmatching or ambiguous rows remain non-ZDR. +- Keep canonical `openrouter/` endpoint-feed ZDR evidence exact for + OpenRouter routes while allowing an unambiguous matching feed model identity + to attest supplied non-OpenRouter provider rows; noncanonical, nonmatching, or + ambiguous rows remain non-ZDR. - Add a bounded hourly LineageWeave stacked-PR review-repair caller while preserving the existing review-agent, model-routing, and protected-merge boundaries. Product-gap development remains a separately gated coordinator diff --git a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md index e5cb9c6b23..26dfb9edf8 100644 --- a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md +++ b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md @@ -43,11 +43,12 @@ all five, and auto-optimize routing by cost. attested from a machine-readable, dated source is treated as non-ZDR, mirroring OpenRouter's stance on unascertained policies. The OpenRouter `/api/v1/endpoints/zdr` feed (documented, auto-updated) is - fetched when egress allows it. OpenRouter routes require exact feed - membership; for every other discovered provider, the feed's model identity - is used as selection evidence for a matching candidate row. Nonmatching or - ambiguous candidates remain non-ZDR. Otherwise the dated static attestation - table is used, never a fabricated policy. + fetched when egress allows it. Only canonical `openrouter/` feed keys + are accepted. OpenRouter routes require exact feed membership; for every + other discovered provider, that canonical feed's model identity is used as + selection evidence for a matching candidate row. Noncanonical, nonmatching, + or ambiguous candidates remain non-ZDR. Otherwise the dated static + attestation table is used, never a fabricated policy. `scripts/ci/contextual_orchestrator_review_policy.py` turns the free-tier discovery report into a ZDR-prioritized, provider-family-diverse agents catalog (primary/secondary NVIDIA keys share one outage-domain family), diff --git a/scripts/ci/zdr_policy.py b/scripts/ci/zdr_policy.py index fcda7516e2..9352dcf32c 100644 --- a/scripts/ci/zdr_policy.py +++ b/scripts/ci/zdr_policy.py @@ -216,7 +216,11 @@ def zdr_evidence_source( else None ) candidate = model.strip().lstrip("/").casefold() - feed = {str(endpoint).strip().casefold() for endpoint in zdr_endpoints if str(endpoint).strip()} + feed = { + endpoint + for endpoint in (str(value).strip().casefold() for value in zdr_endpoints) + if endpoint.startswith("openrouter/") and endpoint.removeprefix("openrouter/") + } if scope.openrouter_endpoints_feed: return ( OPENROUTER_ZDR_ENDPOINTS_SOURCE diff --git a/tests/test_zdr_policy.py b/tests/test_zdr_policy.py index 64ee647274..b3090f41ea 100644 --- a/tests/test_zdr_policy.py +++ b/tests/test_zdr_policy.py @@ -151,6 +151,18 @@ def test_is_zdr_model_feed_evidence_matches_other_provider_model_ids() -> None: ) +def test_is_zdr_model_rejects_noncanonical_feed_provider_keys() -> None: + """Only canonical OpenRouter feed routes may provide cross-provider evidence.""" + assert ( + zdr_policy.is_zdr_model( + "nvidia_nim", + model="deepseek/deepseek-r1:free", + zdr_endpoints=frozenset({"nvidia_nim/deepseek/deepseek-r1:free"}), + ) + is False + ) + + @pytest.mark.parametrize( ("value", "expected"), [ From 611a456d63a2bcfa67f65fb1afe2d84720a40f17 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 07:31:08 +0900 Subject: [PATCH 41/76] fix(sidecar): pin evidence-only orchestrator revision --- docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md | 2 +- scripts/ci/contextual_orchestrator_review_sidecar.sh | 2 +- tests/test_contextual_orchestrator_review_sidecar_contract.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md index 26dfb9edf8..17a2447929 100644 --- a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md +++ b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md @@ -22,7 +22,7 @@ all five, and auto-optimize routing by cost. 1. **Vendoring, pinned**: `scripts/ci/contextual_orchestrator_review_sidecar.sh` clones `ContextualWisdomLab/contextual-orchestrator` at an exact SHA - (`29d9493fcdbf11aaa3d43bc6c7e10857bb85ca73` today) into `RUNNER_TEMP`. The + (`952996ecd5905dc9938a2119f59a0b1cbf3b7993` today) into `RUNNER_TEMP`. The source's `requirements.lock` is installed with `--require-hashes` and `--no-deps`, so dependency resolution cannot silently move the reviewed runtime. diff --git a/scripts/ci/contextual_orchestrator_review_sidecar.sh b/scripts/ci/contextual_orchestrator_review_sidecar.sh index 33e1a751de..8909017e1d 100755 --- a/scripts/ci/contextual_orchestrator_review_sidecar.sh +++ b/scripts/ci/contextual_orchestrator_review_sidecar.sh @@ -14,7 +14,7 @@ # (fail-closed zero-cost) pool. set -euo pipefail -ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-29d9493fcdbf11aaa3d43bc6c7e10857bb85ca73}" +ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-952996ecd5905dc9938a2119f59a0b1cbf3b7993}" ORCHESTRATOR_GIT_URL="${ORCHESTRATOR_GIT_URL:-https://github.com/ContextualWisdomLab/contextual-orchestrator.git}" # The Strix gate and Noema SSRF guard accept this one process-local origin. # Keep it fixed so an environment override cannot create an unvalidated sidecar. diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index 189645ba71..6ba8f2c0d5 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -38,7 +38,7 @@ ) GATEWAY_MODEL = "contextual-orchestrator/orchestrator/free" -ORCH_PIN_SHA = "29d9493fcdbf11aaa3d43bc6c7e10857bb85ca73" +ORCH_PIN_SHA = "952996ecd5905dc9938a2119f59a0b1cbf3b7993" def _read(path: Path) -> str: From 25cc5e5c26972880346cf16b6704a2eb1f2e48d3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 07:42:10 +0900 Subject: [PATCH 42/76] fix(review): require ZDR for gateway scans --- .github/workflows/noema-review.yml | 7 ++----- .../workflows/opencode-review-dispatch.yml | 2 +- .github/workflows/strix.yml | 2 +- ...ntextual-orchestrator-vendored-free-zdr.md | 21 ++++++++++--------- .../contextual_orchestrator_review_sidecar.sh | 2 +- scripts/ci/test_strix_quick_gate.sh | 2 +- ...al_orchestrator_review_sidecar_contract.py | 10 +++++---- ...st_noema_orchestrator_workflow_contract.py | 2 ++ .../test_required_workflow_queue_contract.py | 2 ++ ..._strix_contextual_orchestrator_contract.py | 6 +++--- 10 files changed, 30 insertions(+), 26 deletions(-) diff --git a/.github/workflows/noema-review.yml b/.github/workflows/noema-review.yml index 5c60782adb..fe90cf7ea5 100644 --- a/.github/workflows/noema-review.yml +++ b/.github/workflows/noema-review.yml @@ -275,12 +275,9 @@ jobs: fi done case "$visibility" in - private|internal) + private|internal|public) echo "require_zdr=true" >>"$GITHUB_OUTPUT" - echo "::notice::Private/internal target requires an attested ZDR-only review pool." - ;; - public) - echo "require_zdr=false" >>"$GITHUB_OUTPUT" + echo "::notice::Every target requires an attested ZDR-only review pool." ;; *) echo "::error::Noema target repository visibility is missing or unsupported: ${visibility:-}." diff --git a/.github/workflows/opencode-review-dispatch.yml b/.github/workflows/opencode-review-dispatch.yml index 3068fbc365..60406250a2 100644 --- a/.github/workflows/opencode-review-dispatch.yml +++ b/.github/workflows/opencode-review-dispatch.yml @@ -2371,7 +2371,7 @@ jobs: NVIDIA_NIM_API_KEY_SUB: ${{ secrets.NVIDIA_NIM_API_KEY_SUB }} OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - CONTEXTUAL_ORCHESTRATOR_REQUIRE_ZDR: ${{ needs.validate-pr-metadata.outputs.is_private }} + CONTEXTUAL_ORCHESTRATOR_REQUIRE_ZDR: "true" run: | set -euo pipefail bash "$GITHUB_WORKSPACE/scripts/ci/contextual_orchestrator_review_sidecar.sh" diff --git a/.github/workflows/strix.yml b/.github/workflows/strix.yml index 5107cde2e9..e1f86c3252 100644 --- a/.github/workflows/strix.yml +++ b/.github/workflows/strix.yml @@ -573,7 +573,7 @@ jobs: NVIDIA_NIM_API_KEY_SUB: ${{ secrets.NVIDIA_NIM_API_KEY_SUB }} OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - CONTEXTUAL_ORCHESTRATOR_REQUIRE_ZDR: ${{ steps.target_visibility.outputs.is_private }} + CONTEXTUAL_ORCHESTRATOR_REQUIRE_ZDR: "true" run: | set -euo pipefail bash "$TRUSTED_STRIX_SOURCE/scripts/ci/contextual_orchestrator_review_sidecar.sh" diff --git a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md index 17a2447929..249a9f4fbe 100644 --- a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md +++ b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md @@ -3,7 +3,7 @@ - Status: accepted - Date: 2026-08-27 - Scope: ContextualWisdomLab/.github central review pipelines (OpenCode autofix/dispatch + shared `opencode.jsonc` default + required Noema + Strix review) -- Decision: Route every central CI review write/model execution that touches contracts in this repository through the **vendored** `contextual-orchestrator` gateway, served as a per-runner sidecar, using the fail-closed zero-cost virtual model id `orchestrator/free`, with **Zero Data Retention (ZDR)-compliant routes prioritized** inside that pool. +- Decision: Route every central CI review write/model execution that touches contracts in this repository through the **vendored** `contextual-orchestrator` gateway, served as a per-runner sidecar, using the fail-closed zero-cost virtual model id `orchestrator/free`, with **Zero Data Retention (ZDR)-compliant routes required** inside that pool. - Ownership: `.github` owns control-plane evidence; `ContextualWisdomLab/contextual-orchestrator` owns the gateway. The 2026-08-18 org decision (recorded in `ContextualWisdomLab/contextual-orchestrator` AGENTS.md) already migrated OpenCode/Noema/Strix to the orchestrator backend; this ADR is the org-repo (provider-config) half of that decision. - Figma File ID: N/A (no customer UI). @@ -58,10 +58,11 @@ all five, and auto-optimize routing by cost. every model/diagnosis candidate at `contextual-orchestrator/orchestrator/free`; the generated dispatch config contains only the gateway provider. The shared `opencode.jsonc` default `model`/`small_model` is the same gateway route. - `noema-review.yml` and `strix.yml` provision the same sidecar and use the - loopback chat-completions/API-compatible URL with virtual model - `orchestrator/free`; Strix has no external fallback and private targets pass - visibility through to the gateway's ZDR requirement. Noema reviewer identity + `noema-review.yml`, `strix.yml`, and the Required OpenCode dispatch provision + the same sidecar and use the loopback chat-completions/API-compatible URL + with virtual model `orchestrator/free`; every target passes the explicit + ZDR-required policy to the gateway, while visibility remains independently + validated as trust metadata. Strix has no external fallback. Noema reviewer identity remains `NOEMA_REVIEW_TOKEN` / GitHub App / OIDC and is still never `github.token`; Autofix mutation still requires `PR_REVIEW_MERGE_TOKEN` / `OPENCODE_APPROVE_TOKEN` / the exchanged OpenCode app token, never @@ -82,7 +83,7 @@ all five, and auto-optimize routing by cost. - The autofix/OpenCode review paths no longer hard-code any provider base URL or model id; upstream model selection is delegated to the orchestrator's - discovery + cost routing, under the zero-cost pool, with ZDR routes first. + discovery + cost routing, under the zero-cost pool, with ZDR routes required. - Workers need egress to the five provider model-list hosts and, when reachable, `https://openrouter.ai/api/v1/endpoints/zdr`; the feed failure path is graceful (static table). @@ -106,7 +107,7 @@ all five, and auto-optimize routing by cost. - OpenAI. (n.d.). *Create file* [API reference]. https://developers.openai.com/api/reference/resources/files/methods/create -- **Private-target boundary (2026-08-27):** Noema resolves target visibility with - the selected repository-scoped reviewer token. Private/internal repositories - set `CONTEXTUAL_ORCHESTRATOR_REQUIRE_ZDR=true`; the catalog then excludes - every non-ZDR route and fails closed when no attested free ZDR route exists. +- **ZDR-required boundary (2026-08-29):** Noema, Strix, and Required OpenCode + validate target visibility independently, but always set + `CONTEXTUAL_ORCHESTRATOR_REQUIRE_ZDR=true`; the catalog excludes every + non-ZDR route and fails closed when no attested free ZDR route exists. diff --git a/scripts/ci/contextual_orchestrator_review_sidecar.sh b/scripts/ci/contextual_orchestrator_review_sidecar.sh index 8909017e1d..9078da3140 100755 --- a/scripts/ci/contextual_orchestrator_review_sidecar.sh +++ b/scripts/ci/contextual_orchestrator_review_sidecar.sh @@ -210,7 +210,7 @@ fi case "${CONTEXTUAL_ORCHESTRATOR_REQUIRE_ZDR:-false}" in true) privacy_args=(--require-zdr) - log "private/internal target: requiring attested ZDR routes" + log "requiring attested ZDR routes for every central review target" ;; false|"") privacy_args=() diff --git a/scripts/ci/test_strix_quick_gate.sh b/scripts/ci/test_strix_quick_gate.sh index 9aaf8df857..c80b0b32a9 100644 --- a/scripts/ci/test_strix_quick_gate.sh +++ b/scripts/ci/test_strix_quick_gate.sh @@ -626,7 +626,7 @@ assert_opencode_review_uses_codegraph_and_contextual_orchestrator() { assert_file_matches "$workflow_file" 'uses:[[:space:]]+actions/checkout@[0-9a-fA-F]{40}([[:space:]]|$)' "opencode review workflow pins checkout to a full commit SHA" assert_file_contains "$workflow_file" "Provision contextual-orchestrator review sidecar" "opencode review provisions the central contextual-orchestrator sidecar" assert_file_contains "$workflow_file" 'NVIDIA_NIM_API_KEY: ${{ secrets.NVIDIA_NIM_API_KEY }}' "opencode review passes the scoped provider credentials only to sidecar bootstrap" - assert_file_contains "$workflow_file" "CONTEXTUAL_ORCHESTRATOR_REQUIRE_ZDR" "opencode review passes repository privacy to the gateway ZDR policy" + assert_file_contains "$workflow_file" 'CONTEXTUAL_ORCHESTRATOR_REQUIRE_ZDR: "true"' "opencode review requires ZDR for every gateway review" assert_file_contains "$workflow_file" 'is_private: ${{ steps.validate.outputs.is_private }}' "opencode review carries validated repository privacy into gateway routing" assert_file_contains "$workflow_file" '"model": "contextual-orchestrator/orchestrator/free"' "opencode review uses the gateway free pool" assert_file_contains "$workflow_file" '"small_model": "contextual-orchestrator/orchestrator/free"' "opencode review uses the gateway for the small model" diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index 6ba8f2c0d5..ae620486f5 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -418,8 +418,8 @@ def test_noema_review_workflow_provisions_sidecar_with_all_five_secrets() -> Non assert "NOEMA_REVIEW_TOKEN: ${{ secrets.NOEMA_REVIEW_TOKEN }}" in workflow -def test_noema_private_targets_require_zdr_only_sidecar_routing() -> None: - """Repository visibility binds private review content to an attested ZDR-only pool.""" +def test_noema_review_targets_require_zdr_only_sidecar_routing() -> None: + """Every Noema review target uses an attested ZDR-only pool.""" workflow = _read(NOEMA_WORKFLOW) sidecar = _read(SIDECAR) launcher = _read(LAUNCHER) @@ -427,6 +427,8 @@ def test_noema_private_targets_require_zdr_only_sidecar_routing() -> None: assert "Resolve Noema target repository visibility" in workflow assert "target_visibility.outputs.require_zdr" in workflow assert "CONTEXTUAL_ORCHESTRATOR_REQUIRE_ZDR" in workflow + assert 'private|internal|public)' in workflow + assert 'echo "require_zdr=false"' not in workflow assert "CONTEXTUAL_ORCHESTRATOR_REQUIRE_ZDR" in sidecar assert "--require-zdr" in sidecar assert 'parser.add_argument("--require-zdr", action="store_true")' in launcher @@ -450,10 +452,10 @@ def test_required_opencode_dispatch_uses_the_gateway_for_model_pool_and_diagnosi def test_required_strix_uses_the_gateway_and_zdr_visibility_contract() -> None: - """Strix accepts only the gateway route and binds private scans to ZDR.""" + """Strix accepts only the gateway route and requires ZDR for every scan.""" workflow = _read(STRIX_WORKFLOW) assert "Provision contextual-orchestrator Strix sidecar" in workflow - assert "CONTEXTUAL_ORCHESTRATOR_REQUIRE_ZDR" in workflow + assert 'CONTEXTUAL_ORCHESTRATOR_REQUIRE_ZDR: "true"' in workflow assert 'STRIX_MODEL: contextual-orchestrator/orchestrator/free' in workflow assert "provider_mode=contextual_orchestrator" in workflow assert "STRIX_LLM_DEFAULT_PROVIDER: contextual_orchestrator" in workflow diff --git a/tests/test_noema_orchestrator_workflow_contract.py b/tests/test_noema_orchestrator_workflow_contract.py index 481b3356aa..628a76ebcc 100644 --- a/tests/test_noema_orchestrator_workflow_contract.py +++ b/tests/test_noema_orchestrator_workflow_contract.py @@ -31,6 +31,8 @@ def test_noema_review_credentials_and_llm_use_orchestrator_free() -> None: assert "nvidia/nemotron-3-ultra-550b-a55b" not in workflow assert "Resolve Noema target repository visibility" in workflow assert "target_visibility.outputs.require_zdr" in workflow + assert 'private|internal|public)' in workflow + assert 'echo "require_zdr=false"' not in workflow assert "CONTEXTUAL_ORCHESTRATOR_REQUIRE_ZDR" in workflow assert ( "NOEMA_LLM_API_KEY: ${{ secrets.NOEMA_LLM_API_KEY || secrets.OPENAI_API_KEY || '' }}" diff --git a/tests/test_required_workflow_queue_contract.py b/tests/test_required_workflow_queue_contract.py index 80d34abdf6..0844f874bb 100644 --- a/tests/test_required_workflow_queue_contract.py +++ b/tests/test_required_workflow_queue_contract.py @@ -531,6 +531,8 @@ def test_noema_review_credentials_and_orchestrator_configuration_fail_closed() - ) assert "Resolve Noema target repository visibility" in workflow assert "target_visibility.outputs.require_zdr" in workflow + assert 'private|internal|public)' in workflow + assert 'echo "require_zdr=false"' not in workflow assert "CONTEXTUAL_ORCHESTRATOR_REQUIRE_ZDR" in workflow assert "https://integrate.api.nvidia.com/v1/chat/completions" not in workflow assert "nvidia/nemotron-3-ultra-550b-a55b" not in workflow diff --git a/tests/test_strix_contextual_orchestrator_contract.py b/tests/test_strix_contextual_orchestrator_contract.py index 0278b018c5..34fd32f4a1 100644 --- a/tests/test_strix_contextual_orchestrator_contract.py +++ b/tests/test_strix_contextual_orchestrator_contract.py @@ -54,10 +54,10 @@ def test_model_override_cannot_escape_the_gateway(self) -> None: for direct_route in ("nvidia_nim/*)", "openrouter/free", "openai-direct/gpt-5.4"): self.assertNotIn(direct_route, self.workflow) - def test_private_gateway_scans_require_zdr_only_routing(self) -> None: - """Private source never enters the gateway's non-ZDR fallback tier.""" + def test_gateway_scans_require_zdr_only_routing(self) -> None: + """Every source never enters the gateway's non-ZDR fallback tier.""" self.assertIn( - "CONTEXTUAL_ORCHESTRATOR_REQUIRE_ZDR: ${{ steps.target_visibility.outputs.is_private }}", + 'CONTEXTUAL_ORCHESTRATOR_REQUIRE_ZDR: "true"', self.workflow, ) From 58536358bfe97c7ba62fa56556879b55ebe1758b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 07:43:50 +0900 Subject: [PATCH 43/76] test(review): update dispatch workflow blob pin --- tests/test_pr_review_autofix_nvidia_nim_contract.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_pr_review_autofix_nvidia_nim_contract.py b/tests/test_pr_review_autofix_nvidia_nim_contract.py index 25f74765ac..02039ee355 100644 --- a/tests/test_pr_review_autofix_nvidia_nim_contract.py +++ b/tests/test_pr_review_autofix_nvidia_nim_contract.py @@ -19,7 +19,7 @@ DOCTORING_RECORD = Path("docs/doctoring/hourly-nvidia-nim-autofix.md") CHANGELOG = Path("CHANGELOG.md") REVIEW_DISPATCH_WORKFLOW = Path(".github/workflows/opencode-review-dispatch.yml") -REVIEW_DISPATCH_BLOB_SHA = "3068fbc365dfe22d82adb523c3b36c0703e9c0a8" +REVIEW_DISPATCH_BLOB_SHA = "60406250a282430329a57405d2a6cb8bc6d319aa" def _workflow_text(path: Path) -> str: From a8ce145b1d1936d9c459347d1204b3d8e0716451 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 07:48:04 +0900 Subject: [PATCH 44/76] fix(review): require ZDR for autofix sidecar --- .github/workflows/pr-review-autofix.yml | 1 + tests/test_contextual_orchestrator_review_sidecar_contract.py | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/pr-review-autofix.yml b/.github/workflows/pr-review-autofix.yml index 005303b822..dd24bb0bad 100644 --- a/.github/workflows/pr-review-autofix.yml +++ b/.github/workflows/pr-review-autofix.yml @@ -254,6 +254,7 @@ jobs: NVIDIA_NIM_API_KEY_SUB: ${{ secrets.NVIDIA_NIM_API_KEY_SUB }} OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + CONTEXTUAL_ORCHESTRATOR_REQUIRE_ZDR: "true" run: | set -euo pipefail bash "$GITHUB_WORKSPACE/trusted-autofix-source/scripts/ci/contextual_orchestrator_review_sidecar.sh" diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index ae620486f5..c997adf00c 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -377,6 +377,7 @@ def test_autofix_workflow_provisions_sidecar_with_all_five_secrets() -> None: assert "contextual_orchestrator_review_sidecar.sh" in workflow for secret in FIVE_SECRETS: assert f"{secret}: ${{{{ secrets.{secret} }}}}" in workflow + assert 'CONTEXTUAL_ORCHESTRATOR_REQUIRE_ZDR: "true"' in workflow assert GATEWAY_MODEL in workflow assert workflow.count(f"MODEL: {GATEWAY_MODEL}") == 2 assert "https://integrate.api.nvidia.com/v1" not in workflow From 4e7926a1c1cac0244f8ef1036c8db4cc645b57a3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 07:52:49 +0900 Subject: [PATCH 45/76] fix(review): fail closed when ZDR is unset --- scripts/ci/contextual_orchestrator_review_sidecar.sh | 4 ++-- tests/test_contextual_orchestrator_review_sidecar_contract.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/ci/contextual_orchestrator_review_sidecar.sh b/scripts/ci/contextual_orchestrator_review_sidecar.sh index 9078da3140..3225720518 100755 --- a/scripts/ci/contextual_orchestrator_review_sidecar.sh +++ b/scripts/ci/contextual_orchestrator_review_sidecar.sh @@ -207,13 +207,13 @@ else zdr_args=() fi -case "${CONTEXTUAL_ORCHESTRATOR_REQUIRE_ZDR:-false}" in +case "${CONTEXTUAL_ORCHESTRATOR_REQUIRE_ZDR:-}" in true) privacy_args=(--require-zdr) log "requiring attested ZDR routes for every central review target" ;; false|"") - privacy_args=() + fail "central review sidecar requires CONTEXTUAL_ORCHESTRATOR_REQUIRE_ZDR=true" ;; *) fail "CONTEXTUAL_ORCHESTRATOR_REQUIRE_ZDR must be true or false" diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index c997adf00c..034c596834 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -432,6 +432,7 @@ def test_noema_review_targets_require_zdr_only_sidecar_routing() -> None: assert 'echo "require_zdr=false"' not in workflow assert "CONTEXTUAL_ORCHESTRATOR_REQUIRE_ZDR" in sidecar assert "--require-zdr" in sidecar + assert 'fail "central review sidecar requires CONTEXTUAL_ORCHESTRATOR_REQUIRE_ZDR=true"' in sidecar assert 'parser.add_argument("--require-zdr", action="store_true")' in launcher assert "require_zdr=args.require_zdr" in launcher From 126efe87daf7e21b5f78a2c9c975f43fa2d8ce1e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 07:53:34 +0900 Subject: [PATCH 46/76] docs(review): record mandatory ZDR boundary --- docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md index 249a9f4fbe..f413d7b2b1 100644 --- a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md +++ b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md @@ -62,7 +62,8 @@ all five, and auto-optimize routing by cost. the same sidecar and use the loopback chat-completions/API-compatible URL with virtual model `orchestrator/free`; every target passes the explicit ZDR-required policy to the gateway, while visibility remains independently - validated as trust metadata. Strix has no external fallback. Noema reviewer identity + validated as trust metadata. The sidecar also fails closed when the ZDR + requirement is missing or false. Strix has no external fallback. Noema reviewer identity remains `NOEMA_REVIEW_TOKEN` / GitHub App / OIDC and is still never `github.token`; Autofix mutation still requires `PR_REVIEW_MERGE_TOKEN` / `OPENCODE_APPROVE_TOKEN` / the exchanged OpenCode app token, never From 26338887b2b13e2000f0b1325d826ddc0862d959 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 08:43:12 +0900 Subject: [PATCH 47/76] fix(review): align scheduler repository validation --- scripts/ci/pr_auto_rebase.py | 4 +++- scripts/ci/pr_review_autofix_context.py | 4 +++- scripts/ci/pr_review_fix_scheduler.py | 4 +++- ...est_repository_branch_coverage_review_schedulers.py | 10 ++++++++++ 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/scripts/ci/pr_auto_rebase.py b/scripts/ci/pr_auto_rebase.py index c0f04c3aa2..c62c70ebf3 100755 --- a/scripts/ci/pr_auto_rebase.py +++ b/scripts/ci/pr_auto_rebase.py @@ -80,7 +80,9 @@ ) -REPO_RE = re.compile(r"^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$") +REPO_RE = re.compile( + r"^[A-Za-z0-9_][A-Za-z0-9_.-]*/(?!\.{1,2}$)[A-Za-z0-9_.][A-Za-z0-9_.-]*$" +) OPEN_PRS_PAGE_SIZE = 25 LABELS_PAGE_SIZE = 50 DEFAULT_MAX_PER_RUN = 10 diff --git a/scripts/ci/pr_review_autofix_context.py b/scripts/ci/pr_review_autofix_context.py index f3f652b8d4..f9463eb9a3 100755 --- a/scripts/ci/pr_review_autofix_context.py +++ b/scripts/ci/pr_review_autofix_context.py @@ -14,7 +14,9 @@ from typing import Any -REPO_RE = re.compile(r"^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$") +REPO_RE = re.compile( + r"^[A-Za-z0-9_][A-Za-z0-9_.-]*/(?!\.{1,2}$)[A-Za-z0-9_.][A-Za-z0-9_.-]*$" +) SHA_RE = re.compile(r"^[0-9a-fA-F]{40}$") _AUTOFIX_CONTROL_PREFIXES = (".github/", "scripts/ci/") _REPAIR_MODES = ("review", "rca", "conflict") diff --git a/scripts/ci/pr_review_fix_scheduler.py b/scripts/ci/pr_review_fix_scheduler.py index cde53977a4..c03b969ced 100755 --- a/scripts/ci/pr_review_fix_scheduler.py +++ b/scripts/ci/pr_review_fix_scheduler.py @@ -44,7 +44,9 @@ r"" ) -REPO_RE = re.compile(r"^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$") +REPO_RE = re.compile( + r"^[A-Za-z0-9_][A-Za-z0-9_.-]*/(?!\.{1,2}$)[A-Za-z0-9_.][A-Za-z0-9_.-]*$" +) REPAIR_MODES = frozenset({"review", "rca", "conflict"}) NON_AUTOFIX_CHANGE_REQUEST_MARKERS = ( "merge conflict", diff --git a/tests/test_repository_branch_coverage_review_schedulers.py b/tests/test_repository_branch_coverage_review_schedulers.py index d50f94f055..6673a0dd75 100644 --- a/tests/test_repository_branch_coverage_review_schedulers.py +++ b/tests/test_repository_branch_coverage_review_schedulers.py @@ -122,6 +122,16 @@ def test_autofix_context_renders_legacy_status_context() -> None: ) == ["- security: SUCCESS"] +@pytest.mark.parametrize("module", [autofix_context, fix_scheduler, auto_rebase]) +def test_review_schedulers_reject_path_like_repository_names(module: Any) -> None: + """All sibling scheduler entrypoints reject dot path segments consistently.""" + + assert module.REPO_RE.fullmatch("ContextualWisdomLab/.github") + assert not module.REPO_RE.fullmatch("owner/.") + assert not module.REPO_RE.fullmatch("owner/..") + assert not module.REPO_RE.fullmatch("owner/-repo") + + def test_fix_scheduler_queue_includes_eligible_pr_without_fix_need( monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str], From 0a4f58124ca807da26af741e44aa13bd89730842 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 08:56:12 +0900 Subject: [PATCH 48/76] chore(sidecar): pin latest ZDR routing fix --- docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md | 2 +- scripts/ci/contextual_orchestrator_review_sidecar.sh | 2 +- tests/test_contextual_orchestrator_review_sidecar_contract.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md index f413d7b2b1..c79f6803dd 100644 --- a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md +++ b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md @@ -22,7 +22,7 @@ all five, and auto-optimize routing by cost. 1. **Vendoring, pinned**: `scripts/ci/contextual_orchestrator_review_sidecar.sh` clones `ContextualWisdomLab/contextual-orchestrator` at an exact SHA - (`952996ecd5905dc9938a2119f59a0b1cbf3b7993` today) into `RUNNER_TEMP`. The + (`ea0f9a030536ceb56fbea1119dad38064fdbe14e` today) into `RUNNER_TEMP`. The source's `requirements.lock` is installed with `--require-hashes` and `--no-deps`, so dependency resolution cannot silently move the reviewed runtime. diff --git a/scripts/ci/contextual_orchestrator_review_sidecar.sh b/scripts/ci/contextual_orchestrator_review_sidecar.sh index 3225720518..90b0192832 100755 --- a/scripts/ci/contextual_orchestrator_review_sidecar.sh +++ b/scripts/ci/contextual_orchestrator_review_sidecar.sh @@ -14,7 +14,7 @@ # (fail-closed zero-cost) pool. set -euo pipefail -ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-952996ecd5905dc9938a2119f59a0b1cbf3b7993}" +ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-ea0f9a030536ceb56fbea1119dad38064fdbe14e}" ORCHESTRATOR_GIT_URL="${ORCHESTRATOR_GIT_URL:-https://github.com/ContextualWisdomLab/contextual-orchestrator.git}" # The Strix gate and Noema SSRF guard accept this one process-local origin. # Keep it fixed so an environment override cannot create an unvalidated sidecar. diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index 034c596834..3c73bb5b2a 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -38,7 +38,7 @@ ) GATEWAY_MODEL = "contextual-orchestrator/orchestrator/free" -ORCH_PIN_SHA = "952996ecd5905dc9938a2119f59a0b1cbf3b7993" +ORCH_PIN_SHA = "ea0f9a030536ceb56fbea1119dad38064fdbe14e" def _read(path: Path) -> str: From 3bb0cade859f78d0c64075790b10e23c64cf8fd5 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 08:58:28 +0900 Subject: [PATCH 49/76] fix(noema): keep ZDR mandatory when visibility audit is unavailable --- .github/workflows/noema-review.yml | 34 +++++++++---------- ...st_noema_orchestrator_workflow_contract.py | 4 ++- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/.github/workflows/noema-review.yml b/.github/workflows/noema-review.yml index fe90cf7ea5..dcfef3d010 100644 --- a/.github/workflows/noema-review.yml +++ b/.github/workflows/noema-review.yml @@ -258,32 +258,32 @@ jobs: run: | set -euo pipefail if [ -z "${GH_TOKEN:-}" ]; then - echo "::error::Noema target visibility cannot be resolved without the selected repository-scoped reviewer token." - exit 1 + echo "::notice::Noema target visibility audit was unavailable; continuing with the mandatory ZDR-only review pool." fi visibility="" - for target_visibility_attempt in 1 2 3 4 5 6; do - if visibility="$( - gh api "/repos/${TARGET_REPOSITORY}" --jq '.visibility // (if .private then "private" else "public" end)' - )"; then - break - fi - visibility="" - if [ "$target_visibility_attempt" -lt 6 ]; then - echo "Repository visibility lookup failed (attempt ${target_visibility_attempt}/6), possibly a transient GitHub API rate limit; retrying after backoff." >&2 - sleep "$(( target_visibility_attempt * 5 ))" - fi - done + if [ -n "${GH_TOKEN:-}" ]; then + for target_visibility_attempt in 1 2 3 4 5 6; do + if visibility="$( + gh api "/repos/${TARGET_REPOSITORY}" --jq '.visibility // (if .private then "private" else "public" end)' + )"; then + break + fi + visibility="" + if [ "$target_visibility_attempt" -lt 6 ]; then + echo "Repository visibility lookup failed (attempt ${target_visibility_attempt}/6), possibly a transient GitHub API rate limit; retrying after backoff." >&2 + sleep "$(( target_visibility_attempt * 5 ))" + fi + done + fi case "$visibility" in private|internal|public) - echo "require_zdr=true" >>"$GITHUB_OUTPUT" echo "::notice::Every target requires an attested ZDR-only review pool." ;; *) - echo "::error::Noema target repository visibility is missing or unsupported: ${visibility:-}." - exit 1 + echo "::notice::Noema target visibility was unavailable; the review remains bound to the mandatory ZDR-only pool." ;; esac + echo "require_zdr=true" >>"$GITHUB_OUTPUT" - name: Provision contextual-orchestrator review sidecar if: env.PR_NUMBER != '' diff --git a/tests/test_noema_orchestrator_workflow_contract.py b/tests/test_noema_orchestrator_workflow_contract.py index 628a76ebcc..81d2363e88 100644 --- a/tests/test_noema_orchestrator_workflow_contract.py +++ b/tests/test_noema_orchestrator_workflow_contract.py @@ -57,7 +57,7 @@ def test_noema_review_credentials_and_llm_use_orchestrator_free() -> None: def test_noema_visibility_lookup_retries_transient_api_failures() -> None: - """Bound transient GitHub API failures without weakening visibility validation.""" + """Keep the visibility audit bounded without making it a routing dependency.""" workflow = workflow_text("noema-review.yml") start = workflow.index(" - name: Resolve Noema target repository visibility") end = workflow.index(" - name: Provision contextual-orchestrator review sidecar", start) @@ -68,6 +68,8 @@ def test_noema_visibility_lookup_retries_transient_api_failures() -> None: assert 'sleep "$(( target_visibility_attempt * 5 ))"' in visibility_step assert "possibly a transient GitHub API rate limit; retrying after backoff." in visibility_step assert "case \"$visibility\" in" in visibility_step + assert 'echo "require_zdr=true" >>"$GITHUB_OUTPUT"' in visibility_step + assert "Noema target visibility was unavailable; the review remains bound to the mandatory ZDR-only pool." in visibility_step def test_strix_gateway_default_and_noema_sidecar_fail_closed(tmp_path: Path) -> None: From 37714f6fac5cbf67727ecbc36e17c5e5eb413f8a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 09:11:27 +0900 Subject: [PATCH 50/76] chore(sidecar): pin authenticated ZDR discovery --- docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md | 2 +- scripts/ci/contextual_orchestrator_review_sidecar.sh | 6 ++++-- .../test_contextual_orchestrator_review_sidecar_contract.py | 3 ++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md index c79f6803dd..69d8fc0fdf 100644 --- a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md +++ b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md @@ -22,7 +22,7 @@ all five, and auto-optimize routing by cost. 1. **Vendoring, pinned**: `scripts/ci/contextual_orchestrator_review_sidecar.sh` clones `ContextualWisdomLab/contextual-orchestrator` at an exact SHA - (`ea0f9a030536ceb56fbea1119dad38064fdbe14e` today) into `RUNNER_TEMP`. The + (`6dcfc077ef53b503176b1b2cecf0a54c9d1304f7` today) into `RUNNER_TEMP`. The source's `requirements.lock` is installed with `--require-hashes` and `--no-deps`, so dependency resolution cannot silently move the reviewed runtime. diff --git a/scripts/ci/contextual_orchestrator_review_sidecar.sh b/scripts/ci/contextual_orchestrator_review_sidecar.sh index 90b0192832..69bf73509c 100755 --- a/scripts/ci/contextual_orchestrator_review_sidecar.sh +++ b/scripts/ci/contextual_orchestrator_review_sidecar.sh @@ -14,7 +14,7 @@ # (fail-closed zero-cost) pool. set -euo pipefail -ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-ea0f9a030536ceb56fbea1119dad38064fdbe14e}" +ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-6dcfc077ef53b503176b1b2cecf0a54c9d1304f7}" ORCHESTRATOR_GIT_URL="${ORCHESTRATOR_GIT_URL:-https://github.com/ContextualWisdomLab/contextual-orchestrator.git}" # The Strix gate and Noema SSRF guard accept this one process-local origin. # Keep it fixed so an environment override cannot create an unvalidated sidecar. @@ -199,7 +199,9 @@ policy_report="$ORCHESTRATOR_WORK/policy-report.json" # Optional authoritative ZDR route feed. Failure is non-fatal: the policy falls # back to the dated static attestation table in scripts/ci/zdr_policy.py. -if curl -fsSL --max-time 15 "https://openrouter.ai/api/v1/endpoints/zdr" -o "$zdr_feed" 2>/dev/null; then +if [ -n "${OPENROUTER_API_KEY:-}" ] && curl -fsSL --max-time 15 \ + -H "Authorization: Bearer ${OPENROUTER_API_KEY}" \ + "https://openrouter.ai/api/v1/endpoints/zdr" -o "$zdr_feed" 2>/dev/null; then log "using live OpenRouter ZDR endpoint feed" zdr_args=(--zdr-endpoints "$zdr_feed") else diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index 3c73bb5b2a..83fc205d7f 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -38,7 +38,7 @@ ) GATEWAY_MODEL = "contextual-orchestrator/orchestrator/free" -ORCH_PIN_SHA = "ea0f9a030536ceb56fbea1119dad38064fdbe14e" +ORCH_PIN_SHA = "6dcfc077ef53b503176b1b2cecf0a54c9d1304f7" def _read(path: Path) -> str: @@ -87,6 +87,7 @@ def test_sidecar_feeds_discovery_and_policy_artifacts_to_the_launcher() -> None: "--zdr-endpoints \"$zdr_feed\"", ): assert arg in text + assert 'Authorization: Bearer ${OPENROUTER_API_KEY}' in text assert "https://openrouter.ai/api/v1/endpoints/zdr" in text From 0162962501b538cd0a64d3c6b8aa2ccb4dba54fa Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 09:22:51 +0900 Subject: [PATCH 51/76] fix(sidecar): require ZDR evidence credential --- .../contextual_orchestrator_review_sidecar.sh | 19 ++++++++++++++----- ...al_orchestrator_review_sidecar_contract.py | 7 ++++--- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/scripts/ci/contextual_orchestrator_review_sidecar.sh b/scripts/ci/contextual_orchestrator_review_sidecar.sh index 69bf73509c..247497d43b 100755 --- a/scripts/ci/contextual_orchestrator_review_sidecar.sh +++ b/scripts/ci/contextual_orchestrator_review_sidecar.sh @@ -32,17 +32,26 @@ log() { printf '[contextual-orchestrator-sidecar] %s\n' "$*"; } fail() { log "error: $*" >&2; exit 1; } -# Require at least one of the five provider secrets so we never boot an empty -# (or mock) pool. Missing individual secrets are allowed — discovery skips the -# unregistered provider — matching the review gateway contract. +# Require the OpenRouter evidence credential plus at least one serving-provider +# credential for the mandatory-ZDR review pool. Missing other individual +# secrets are allowed — discovery skips that unregistered provider. provider_secret_count=0 for secret_name in BYTEZ_API_KEY NVIDIA_NIM_API_KEY NVIDIA_NIM_API_KEY_SUB OPENROUTER_API_KEY OPENAI_API_KEY; do if [ -n "${!secret_name:-}" ]; then provider_secret_count=$((provider_secret_count + 1)) fi done -if [ "$provider_secret_count" -lt 1 ]; then - fail "at least one of BYTEZ_API_KEY / NVIDIA_NIM_API_KEY / NVIDIA_NIM_API_KEY_SUB / OPENROUTER_API_KEY / OPENAI_API_KEY is required" +serving_provider_secret_count=0 +for secret_name in BYTEZ_API_KEY NVIDIA_NIM_API_KEY NVIDIA_NIM_API_KEY_SUB OPENAI_API_KEY; do + if [ -n "${!secret_name:-}" ]; then + serving_provider_secret_count=$((serving_provider_secret_count + 1)) + fi +done +if [ -z "${OPENROUTER_API_KEY:-}" ]; then + fail "OPENROUTER_API_KEY is required for mandatory-ZDR evidence discovery" +fi +if [ "$serving_provider_secret_count" -lt 1 ]; then + fail "at least one non-OpenRouter serving provider credential is required for mandatory-ZDR review" fi log "provider secrets present: $provider_secret_count of 5" diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index 83fc205d7f..fe75823ef3 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -69,10 +69,11 @@ def test_sidecar_adr_names_the_current_vendored_revision() -> None: assert ORCH_PIN_SHA in _read(SIDECAR_ADR) -def test_sidecar_requires_the_five_provider_secrets() -> None: - """At least one of the five secrets must be present as bootstrap transport.""" +def test_sidecar_requires_zdr_evidence_and_serving_provider() -> None: + """Mandatory ZDR needs the evidence key and a non-evidence provider.""" text = _read(SIDECAR) - assert '"$provider_secret_count" -lt 1 ]; then' in text + assert 'OPENROUTER_API_KEY is required for mandatory-ZDR evidence discovery' in text + assert '"$serving_provider_secret_count" -lt 1 ]; then' in text for secret in FIVE_SECRETS: assert secret in text From 5eaa3c07adecb170e7788a6295ac80fb9827e181 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 09:45:47 +0900 Subject: [PATCH 52/76] fix(sidecar): pin chat capability boundary --- docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md | 2 +- scripts/ci/contextual_orchestrator_review_sidecar.sh | 2 +- tests/test_contextual_orchestrator_review_sidecar_contract.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md index 69d8fc0fdf..8d05db59d7 100644 --- a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md +++ b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md @@ -22,7 +22,7 @@ all five, and auto-optimize routing by cost. 1. **Vendoring, pinned**: `scripts/ci/contextual_orchestrator_review_sidecar.sh` clones `ContextualWisdomLab/contextual-orchestrator` at an exact SHA - (`6dcfc077ef53b503176b1b2cecf0a54c9d1304f7` today) into `RUNNER_TEMP`. The + (`2bd4139508655c908bb7c07169d31e591d814057` today) into `RUNNER_TEMP`. The source's `requirements.lock` is installed with `--require-hashes` and `--no-deps`, so dependency resolution cannot silently move the reviewed runtime. diff --git a/scripts/ci/contextual_orchestrator_review_sidecar.sh b/scripts/ci/contextual_orchestrator_review_sidecar.sh index 247497d43b..a181c9dd38 100755 --- a/scripts/ci/contextual_orchestrator_review_sidecar.sh +++ b/scripts/ci/contextual_orchestrator_review_sidecar.sh @@ -14,7 +14,7 @@ # (fail-closed zero-cost) pool. set -euo pipefail -ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-6dcfc077ef53b503176b1b2cecf0a54c9d1304f7}" +ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-2bd4139508655c908bb7c07169d31e591d814057}" ORCHESTRATOR_GIT_URL="${ORCHESTRATOR_GIT_URL:-https://github.com/ContextualWisdomLab/contextual-orchestrator.git}" # The Strix gate and Noema SSRF guard accept this one process-local origin. # Keep it fixed so an environment override cannot create an unvalidated sidecar. diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index fe75823ef3..8289bc9892 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -38,7 +38,7 @@ ) GATEWAY_MODEL = "contextual-orchestrator/orchestrator/free" -ORCH_PIN_SHA = "6dcfc077ef53b503176b1b2cecf0a54c9d1304f7" +ORCH_PIN_SHA = "2bd4139508655c908bb7c07169d31e591d814057" def _read(path: Path) -> str: From 8d170d76a6d5ea9031b5287dedd34c149e6229a6 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 09:57:44 +0900 Subject: [PATCH 53/76] fix: pin metadata-aware orchestrator sidecar --- docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md | 2 +- scripts/ci/contextual_orchestrator_review_sidecar.sh | 2 +- tests/test_contextual_orchestrator_review_sidecar_contract.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md index 8d05db59d7..42dbd356ce 100644 --- a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md +++ b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md @@ -22,7 +22,7 @@ all five, and auto-optimize routing by cost. 1. **Vendoring, pinned**: `scripts/ci/contextual_orchestrator_review_sidecar.sh` clones `ContextualWisdomLab/contextual-orchestrator` at an exact SHA - (`2bd4139508655c908bb7c07169d31e591d814057` today) into `RUNNER_TEMP`. The + (`a8b9da095669e143c2866dab7760b7de4d84148f` today) into `RUNNER_TEMP`. The source's `requirements.lock` is installed with `--require-hashes` and `--no-deps`, so dependency resolution cannot silently move the reviewed runtime. diff --git a/scripts/ci/contextual_orchestrator_review_sidecar.sh b/scripts/ci/contextual_orchestrator_review_sidecar.sh index a181c9dd38..f205cbff57 100755 --- a/scripts/ci/contextual_orchestrator_review_sidecar.sh +++ b/scripts/ci/contextual_orchestrator_review_sidecar.sh @@ -14,7 +14,7 @@ # (fail-closed zero-cost) pool. set -euo pipefail -ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-2bd4139508655c908bb7c07169d31e591d814057}" +ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-a8b9da095669e143c2866dab7760b7de4d84148f}" ORCHESTRATOR_GIT_URL="${ORCHESTRATOR_GIT_URL:-https://github.com/ContextualWisdomLab/contextual-orchestrator.git}" # The Strix gate and Noema SSRF guard accept this one process-local origin. # Keep it fixed so an environment override cannot create an unvalidated sidecar. diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index 8289bc9892..4e9cf1e23f 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -38,7 +38,7 @@ ) GATEWAY_MODEL = "contextual-orchestrator/orchestrator/free" -ORCH_PIN_SHA = "2bd4139508655c908bb7c07169d31e591d814057" +ORCH_PIN_SHA = "a8b9da095669e143c2866dab7760b7de4d84148f" def _read(path: Path) -> str: From 28a90a789ffa0b36470a88c60f3d05e81bd1a26b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 10:10:07 +0900 Subject: [PATCH 54/76] fix: pin orchestrator tool-limit handling --- docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md | 2 +- scripts/ci/contextual_orchestrator_review_sidecar.sh | 2 +- tests/test_contextual_orchestrator_review_sidecar_contract.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md index 42dbd356ce..162842cbf5 100644 --- a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md +++ b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md @@ -22,7 +22,7 @@ all five, and auto-optimize routing by cost. 1. **Vendoring, pinned**: `scripts/ci/contextual_orchestrator_review_sidecar.sh` clones `ContextualWisdomLab/contextual-orchestrator` at an exact SHA - (`a8b9da095669e143c2866dab7760b7de4d84148f` today) into `RUNNER_TEMP`. The + (`ae69abb73602ecfa6176b971fdc0fd87075207c3` today) into `RUNNER_TEMP`. The source's `requirements.lock` is installed with `--require-hashes` and `--no-deps`, so dependency resolution cannot silently move the reviewed runtime. diff --git a/scripts/ci/contextual_orchestrator_review_sidecar.sh b/scripts/ci/contextual_orchestrator_review_sidecar.sh index f205cbff57..99c3b3baf0 100755 --- a/scripts/ci/contextual_orchestrator_review_sidecar.sh +++ b/scripts/ci/contextual_orchestrator_review_sidecar.sh @@ -14,7 +14,7 @@ # (fail-closed zero-cost) pool. set -euo pipefail -ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-a8b9da095669e143c2866dab7760b7de4d84148f}" +ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-ae69abb73602ecfa6176b971fdc0fd87075207c3}" ORCHESTRATOR_GIT_URL="${ORCHESTRATOR_GIT_URL:-https://github.com/ContextualWisdomLab/contextual-orchestrator.git}" # The Strix gate and Noema SSRF guard accept this one process-local origin. # Keep it fixed so an environment override cannot create an unvalidated sidecar. diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index 4e9cf1e23f..700135119b 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -38,7 +38,7 @@ ) GATEWAY_MODEL = "contextual-orchestrator/orchestrator/free" -ORCH_PIN_SHA = "a8b9da095669e143c2866dab7760b7de4d84148f" +ORCH_PIN_SHA = "ae69abb73602ecfa6176b971fdc0fd87075207c3" def _read(path: Path) -> str: From 4ae57f1ca84e5f4c3107dc17fc176470e3accdaf Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 10:19:35 +0900 Subject: [PATCH 55/76] fix: reject malformed OpenRouter feed keys --- scripts/ci/zdr_policy.py | 3 ++- tests/test_zdr_policy.py | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/scripts/ci/zdr_policy.py b/scripts/ci/zdr_policy.py index 9352dcf32c..978293ad35 100644 --- a/scripts/ci/zdr_policy.py +++ b/scripts/ci/zdr_policy.py @@ -219,7 +219,8 @@ def zdr_evidence_source( feed = { endpoint for endpoint in (str(value).strip().casefold() for value in zdr_endpoints) - if endpoint.startswith("openrouter/") and endpoint.removeprefix("openrouter/") + if endpoint.startswith("openrouter/") + and all(segment for segment in endpoint.split("/")) } if scope.openrouter_endpoints_feed: return ( diff --git a/tests/test_zdr_policy.py b/tests/test_zdr_policy.py index b3090f41ea..7b1cbe66e0 100644 --- a/tests/test_zdr_policy.py +++ b/tests/test_zdr_policy.py @@ -163,6 +163,26 @@ def test_is_zdr_model_rejects_noncanonical_feed_provider_keys() -> None: ) +@pytest.mark.parametrize( + "feed_key", + [ + "openrouter//deepseek/deepseek-r1:free", + "openrouter/deepseek/deepseek-r1:free/", + "openrouter/", + ], +) +def test_is_zdr_model_rejects_feed_keys_with_empty_segments(feed_key: str) -> None: + """Malformed feed paths cannot grant suffix-based ZDR evidence.""" + assert ( + zdr_policy.is_zdr_model( + "nvidia_nim", + model="deepseek/deepseek-r1:free", + zdr_endpoints=frozenset({feed_key}), + ) + is False + ) + + @pytest.mark.parametrize( ("value", "expected"), [ From ceeee014b0acc902f168763880dd37417bc33f3b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 10:56:04 +0900 Subject: [PATCH 56/76] chore: pin latest orchestrator sidecar --- docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md | 2 +- scripts/ci/contextual_orchestrator_review_sidecar.sh | 2 +- tests/test_contextual_orchestrator_review_sidecar_contract.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md index 162842cbf5..24e4e68ad1 100644 --- a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md +++ b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md @@ -22,7 +22,7 @@ all five, and auto-optimize routing by cost. 1. **Vendoring, pinned**: `scripts/ci/contextual_orchestrator_review_sidecar.sh` clones `ContextualWisdomLab/contextual-orchestrator` at an exact SHA - (`ae69abb73602ecfa6176b971fdc0fd87075207c3` today) into `RUNNER_TEMP`. The + (`6cb6654f8f4eeb006ba9cd9b896a08938020a03d` exact contextual-orchestrator revision) into `RUNNER_TEMP`. The source's `requirements.lock` is installed with `--require-hashes` and `--no-deps`, so dependency resolution cannot silently move the reviewed runtime. diff --git a/scripts/ci/contextual_orchestrator_review_sidecar.sh b/scripts/ci/contextual_orchestrator_review_sidecar.sh index 99c3b3baf0..e468bca282 100755 --- a/scripts/ci/contextual_orchestrator_review_sidecar.sh +++ b/scripts/ci/contextual_orchestrator_review_sidecar.sh @@ -14,7 +14,7 @@ # (fail-closed zero-cost) pool. set -euo pipefail -ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-ae69abb73602ecfa6176b971fdc0fd87075207c3}" +ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-6cb6654f8f4eeb006ba9cd9b896a08938020a03d}" ORCHESTRATOR_GIT_URL="${ORCHESTRATOR_GIT_URL:-https://github.com/ContextualWisdomLab/contextual-orchestrator.git}" # The Strix gate and Noema SSRF guard accept this one process-local origin. # Keep it fixed so an environment override cannot create an unvalidated sidecar. diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index 700135119b..b0a50d1f23 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -38,7 +38,7 @@ ) GATEWAY_MODEL = "contextual-orchestrator/orchestrator/free" -ORCH_PIN_SHA = "ae69abb73602ecfa6176b971fdc0fd87075207c3" +ORCH_PIN_SHA = "6cb6654f8f4eeb006ba9cd9b896a08938020a03d" def _read(path: Path) -> str: From cf43947b4d40df0c6c1ba3d2dfa8be676d2251f5 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 11:05:51 +0900 Subject: [PATCH 57/76] chore: pin streaming usage fix --- docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md | 2 +- scripts/ci/contextual_orchestrator_review_sidecar.sh | 2 +- tests/test_contextual_orchestrator_review_sidecar_contract.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md index 24e4e68ad1..3871aa6ae4 100644 --- a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md +++ b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md @@ -22,7 +22,7 @@ all five, and auto-optimize routing by cost. 1. **Vendoring, pinned**: `scripts/ci/contextual_orchestrator_review_sidecar.sh` clones `ContextualWisdomLab/contextual-orchestrator` at an exact SHA - (`6cb6654f8f4eeb006ba9cd9b896a08938020a03d` exact contextual-orchestrator revision) into `RUNNER_TEMP`. The + (`9d421ea002c34a1a833ee069708b57c9e96c1573` exact contextual-orchestrator revision) into `RUNNER_TEMP`. The source's `requirements.lock` is installed with `--require-hashes` and `--no-deps`, so dependency resolution cannot silently move the reviewed runtime. diff --git a/scripts/ci/contextual_orchestrator_review_sidecar.sh b/scripts/ci/contextual_orchestrator_review_sidecar.sh index e468bca282..c5bfdb8414 100755 --- a/scripts/ci/contextual_orchestrator_review_sidecar.sh +++ b/scripts/ci/contextual_orchestrator_review_sidecar.sh @@ -14,7 +14,7 @@ # (fail-closed zero-cost) pool. set -euo pipefail -ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-6cb6654f8f4eeb006ba9cd9b896a08938020a03d}" +ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-9d421ea002c34a1a833ee069708b57c9e96c1573}" ORCHESTRATOR_GIT_URL="${ORCHESTRATOR_GIT_URL:-https://github.com/ContextualWisdomLab/contextual-orchestrator.git}" # The Strix gate and Noema SSRF guard accept this one process-local origin. # Keep it fixed so an environment override cannot create an unvalidated sidecar. diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index b0a50d1f23..0705bec4df 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -38,7 +38,7 @@ ) GATEWAY_MODEL = "contextual-orchestrator/orchestrator/free" -ORCH_PIN_SHA = "6cb6654f8f4eeb006ba9cd9b896a08938020a03d" +ORCH_PIN_SHA = "9d421ea002c34a1a833ee069708b57c9e96c1573" def _read(path: Path) -> str: From e77fc2fcfa0c58184abfa2691a01f8dc503bec19 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 11:17:41 +0900 Subject: [PATCH 58/76] chore: repin review sidecar --- docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md | 2 +- scripts/ci/contextual_orchestrator_review_sidecar.sh | 2 +- tests/test_contextual_orchestrator_review_sidecar_contract.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md index 3871aa6ae4..2c6b0101a5 100644 --- a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md +++ b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md @@ -22,7 +22,7 @@ all five, and auto-optimize routing by cost. 1. **Vendoring, pinned**: `scripts/ci/contextual_orchestrator_review_sidecar.sh` clones `ContextualWisdomLab/contextual-orchestrator` at an exact SHA - (`9d421ea002c34a1a833ee069708b57c9e96c1573` exact contextual-orchestrator revision) into `RUNNER_TEMP`. The + (`8b08305cd0fa16e688ef5a594a7b70f92043287f` exact contextual-orchestrator revision) into `RUNNER_TEMP`. The source's `requirements.lock` is installed with `--require-hashes` and `--no-deps`, so dependency resolution cannot silently move the reviewed runtime. diff --git a/scripts/ci/contextual_orchestrator_review_sidecar.sh b/scripts/ci/contextual_orchestrator_review_sidecar.sh index c5bfdb8414..4168c71ad3 100755 --- a/scripts/ci/contextual_orchestrator_review_sidecar.sh +++ b/scripts/ci/contextual_orchestrator_review_sidecar.sh @@ -14,7 +14,7 @@ # (fail-closed zero-cost) pool. set -euo pipefail -ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-9d421ea002c34a1a833ee069708b57c9e96c1573}" +ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-8b08305cd0fa16e688ef5a594a7b70f92043287f}" ORCHESTRATOR_GIT_URL="${ORCHESTRATOR_GIT_URL:-https://github.com/ContextualWisdomLab/contextual-orchestrator.git}" # The Strix gate and Noema SSRF guard accept this one process-local origin. # Keep it fixed so an environment override cannot create an unvalidated sidecar. diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index 0705bec4df..1b2072dba9 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -38,7 +38,7 @@ ) GATEWAY_MODEL = "contextual-orchestrator/orchestrator/free" -ORCH_PIN_SHA = "9d421ea002c34a1a833ee069708b57c9e96c1573" +ORCH_PIN_SHA = "8b08305cd0fa16e688ef5a594a7b70f92043287f" def _read(path: Path) -> str: From deca56446d6279457c33da2e74827c7af4d5e125 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 11:31:29 +0900 Subject: [PATCH 59/76] chore: repin sidecar for response fixes --- docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md | 2 +- scripts/ci/contextual_orchestrator_review_sidecar.sh | 2 +- tests/test_contextual_orchestrator_review_sidecar_contract.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md index 2c6b0101a5..242842050c 100644 --- a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md +++ b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md @@ -22,7 +22,7 @@ all five, and auto-optimize routing by cost. 1. **Vendoring, pinned**: `scripts/ci/contextual_orchestrator_review_sidecar.sh` clones `ContextualWisdomLab/contextual-orchestrator` at an exact SHA - (`8b08305cd0fa16e688ef5a594a7b70f92043287f` exact contextual-orchestrator revision) into `RUNNER_TEMP`. The + (`ef68a3353823e74a1b33cf90a241bfbfe8a2e0c9` exact contextual-orchestrator revision) into `RUNNER_TEMP`. The source's `requirements.lock` is installed with `--require-hashes` and `--no-deps`, so dependency resolution cannot silently move the reviewed runtime. diff --git a/scripts/ci/contextual_orchestrator_review_sidecar.sh b/scripts/ci/contextual_orchestrator_review_sidecar.sh index 4168c71ad3..a9eb834e71 100755 --- a/scripts/ci/contextual_orchestrator_review_sidecar.sh +++ b/scripts/ci/contextual_orchestrator_review_sidecar.sh @@ -14,7 +14,7 @@ # (fail-closed zero-cost) pool. set -euo pipefail -ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-8b08305cd0fa16e688ef5a594a7b70f92043287f}" +ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-ef68a3353823e74a1b33cf90a241bfbfe8a2e0c9}" ORCHESTRATOR_GIT_URL="${ORCHESTRATOR_GIT_URL:-https://github.com/ContextualWisdomLab/contextual-orchestrator.git}" # The Strix gate and Noema SSRF guard accept this one process-local origin. # Keep it fixed so an environment override cannot create an unvalidated sidecar. diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index 1b2072dba9..e8f83b334e 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -38,7 +38,7 @@ ) GATEWAY_MODEL = "contextual-orchestrator/orchestrator/free" -ORCH_PIN_SHA = "8b08305cd0fa16e688ef5a594a7b70f92043287f" +ORCH_PIN_SHA = "ef68a3353823e74a1b33cf90a241bfbfe8a2e0c9" def _read(path: Path) -> str: From 43e6403eb208ceaefb8aae95209b69b397bc121a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 11:36:42 +0900 Subject: [PATCH 60/76] chore: repin sidecar for stream compatibility --- ...ntextual-orchestrator-vendored-free-zdr.md | 2 +- .../contextual_orchestrator_review_sidecar.sh | 44 ++++++++++++++++++- ...al_orchestrator_review_sidecar_contract.py | 6 ++- 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md index 242842050c..780cfdcc45 100644 --- a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md +++ b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md @@ -22,7 +22,7 @@ all five, and auto-optimize routing by cost. 1. **Vendoring, pinned**: `scripts/ci/contextual_orchestrator_review_sidecar.sh` clones `ContextualWisdomLab/contextual-orchestrator` at an exact SHA - (`ef68a3353823e74a1b33cf90a241bfbfe8a2e0c9` exact contextual-orchestrator revision) into `RUNNER_TEMP`. The + (`69f825655f5af4bac5b39210bafb5f99b8471127` exact contextual-orchestrator revision) into `RUNNER_TEMP`. The source's `requirements.lock` is installed with `--require-hashes` and `--no-deps`, so dependency resolution cannot silently move the reviewed runtime. diff --git a/scripts/ci/contextual_orchestrator_review_sidecar.sh b/scripts/ci/contextual_orchestrator_review_sidecar.sh index a9eb834e71..8a7e2ad687 100755 --- a/scripts/ci/contextual_orchestrator_review_sidecar.sh +++ b/scripts/ci/contextual_orchestrator_review_sidecar.sh @@ -14,7 +14,7 @@ # (fail-closed zero-cost) pool. set -euo pipefail -ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-ef68a3353823e74a1b33cf90a241bfbfe8a2e0c9}" +ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-69f825655f5af4bac5b39210bafb5f99b8471127}" ORCHESTRATOR_GIT_URL="${ORCHESTRATOR_GIT_URL:-https://github.com/ContextualWisdomLab/contextual-orchestrator.git}" # The Strix gate and Noema SSRF guard accept this one process-local origin. # Keep it fixed so an environment override cannot create an unvalidated sidecar. @@ -169,6 +169,31 @@ try: finally: connection.close() + def post_stream_payload(payload): + encoded = json.dumps(payload, ensure_ascii=False).encode("utf-8") + connection = http.client.HTTPConnection( + "127.0.0.1", server.server_address[1], timeout=5 + ) + try: + connection.request( + "POST", + "/v1/chat/completions", + body=encoded, + headers={ + "Authorization": "Bearer contract", + "Content-Type": "application/json", + "Content-Length": str(len(encoded)), + }, + ) + response = connection.getresponse() + return ( + response.status, + response.getheader("Content-Type", ""), + response.read().decode("utf-8"), + ) + finally: + connection.close() + large_status, large_body, encoded_size = post_payload({ "model": "openai/gpt-5", "messages": [{"role": "user", "content": "x" * accepted_size}], @@ -195,6 +220,23 @@ try: assert len(description) == description_length forwarded = client.proxy_payloads[-1]["tools"][0]["function"]["description"] assert forwarded.encode("utf-8") == description.encode("utf-8") + + stream_status, content_type, stream_body = post_stream_payload({ + "model": "openai/gpt-5", + "messages": [{"role": "user", "content": "tool stream probe"}], + "stream": True, + "stream_options": {"include_usage": True}, + "tools": [{ + "type": "function", + "function": { + "name": "scan_target", + "parameters": {"type": "object", "properties": {}}, + }, + }], + }) + assert stream_status == 200, stream_body + assert content_type.startswith("text/event-stream") + assert "usage_source" in stream_body finally: server.shutdown() server.server_close() diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index e8f83b334e..1365086ce2 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -38,7 +38,7 @@ ) GATEWAY_MODEL = "contextual-orchestrator/orchestrator/free" -ORCH_PIN_SHA = "ef68a3353823e74a1b33cf90a241bfbfe8a2e0c9" +ORCH_PIN_SHA = "69f825655f5af4bac5b39210bafb5f99b8471127" def _read(path: Path) -> str: @@ -371,6 +371,10 @@ def test_sidecar_probes_the_pinned_server_body_limit_at_http_boundary() -> None: assert "assert status == 200" in text assert "proxy_payloads[-1]" in text assert '"utf-8"' in text + assert '"stream_options": {"include_usage": True}' in text + assert '"stream": True' in text + assert "post_stream_payload" in text + assert 'text/event-stream' in text def test_autofix_workflow_provisions_sidecar_with_all_five_secrets() -> None: From c1ab509c9512defce3b805c005bfefe8ae696309 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 11:42:17 +0900 Subject: [PATCH 61/76] ci: repin orchestrator sidecar --- docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md | 2 +- scripts/ci/contextual_orchestrator_review_sidecar.sh | 2 +- tests/test_contextual_orchestrator_review_sidecar_contract.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md index 780cfdcc45..0e0afa5c45 100644 --- a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md +++ b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md @@ -22,7 +22,7 @@ all five, and auto-optimize routing by cost. 1. **Vendoring, pinned**: `scripts/ci/contextual_orchestrator_review_sidecar.sh` clones `ContextualWisdomLab/contextual-orchestrator` at an exact SHA - (`69f825655f5af4bac5b39210bafb5f99b8471127` exact contextual-orchestrator revision) into `RUNNER_TEMP`. The + (`5a5d6a4004a73ae5f981e48de45efdc6273b1cad` exact contextual-orchestrator revision) into `RUNNER_TEMP`. The source's `requirements.lock` is installed with `--require-hashes` and `--no-deps`, so dependency resolution cannot silently move the reviewed runtime. diff --git a/scripts/ci/contextual_orchestrator_review_sidecar.sh b/scripts/ci/contextual_orchestrator_review_sidecar.sh index 8a7e2ad687..61e597c629 100755 --- a/scripts/ci/contextual_orchestrator_review_sidecar.sh +++ b/scripts/ci/contextual_orchestrator_review_sidecar.sh @@ -14,7 +14,7 @@ # (fail-closed zero-cost) pool. set -euo pipefail -ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-69f825655f5af4bac5b39210bafb5f99b8471127}" +ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-5a5d6a4004a73ae5f981e48de45efdc6273b1cad}" ORCHESTRATOR_GIT_URL="${ORCHESTRATOR_GIT_URL:-https://github.com/ContextualWisdomLab/contextual-orchestrator.git}" # The Strix gate and Noema SSRF guard accept this one process-local origin. # Keep it fixed so an environment override cannot create an unvalidated sidecar. diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index 1365086ce2..fbff378cc4 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -38,7 +38,7 @@ ) GATEWAY_MODEL = "contextual-orchestrator/orchestrator/free" -ORCH_PIN_SHA = "69f825655f5af4bac5b39210bafb5f99b8471127" +ORCH_PIN_SHA = "5a5d6a4004a73ae5f981e48de45efdc6273b1cad" def _read(path: Path) -> str: From 64958506efbc4a7ee18c63419906cfa0a7179719 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 11:46:20 +0900 Subject: [PATCH 62/76] ci: repin orchestrator sidecar after ZDR fix --- docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md | 2 +- scripts/ci/contextual_orchestrator_review_sidecar.sh | 2 +- tests/test_contextual_orchestrator_review_sidecar_contract.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md index 0e0afa5c45..94236bf6f0 100644 --- a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md +++ b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md @@ -22,7 +22,7 @@ all five, and auto-optimize routing by cost. 1. **Vendoring, pinned**: `scripts/ci/contextual_orchestrator_review_sidecar.sh` clones `ContextualWisdomLab/contextual-orchestrator` at an exact SHA - (`5a5d6a4004a73ae5f981e48de45efdc6273b1cad` exact contextual-orchestrator revision) into `RUNNER_TEMP`. The + (`f47981a9bbcd212344afc38ff975f5cf275810b9` exact contextual-orchestrator revision) into `RUNNER_TEMP`. The source's `requirements.lock` is installed with `--require-hashes` and `--no-deps`, so dependency resolution cannot silently move the reviewed runtime. diff --git a/scripts/ci/contextual_orchestrator_review_sidecar.sh b/scripts/ci/contextual_orchestrator_review_sidecar.sh index 61e597c629..bd4ed64a1c 100755 --- a/scripts/ci/contextual_orchestrator_review_sidecar.sh +++ b/scripts/ci/contextual_orchestrator_review_sidecar.sh @@ -14,7 +14,7 @@ # (fail-closed zero-cost) pool. set -euo pipefail -ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-5a5d6a4004a73ae5f981e48de45efdc6273b1cad}" +ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-f47981a9bbcd212344afc38ff975f5cf275810b9}" ORCHESTRATOR_GIT_URL="${ORCHESTRATOR_GIT_URL:-https://github.com/ContextualWisdomLab/contextual-orchestrator.git}" # The Strix gate and Noema SSRF guard accept this one process-local origin. # Keep it fixed so an environment override cannot create an unvalidated sidecar. diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index fbff378cc4..86a7fbaf9a 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -38,7 +38,7 @@ ) GATEWAY_MODEL = "contextual-orchestrator/orchestrator/free" -ORCH_PIN_SHA = "5a5d6a4004a73ae5f981e48de45efdc6273b1cad" +ORCH_PIN_SHA = "f47981a9bbcd212344afc38ff975f5cf275810b9" def _read(path: Path) -> str: From 4f91c7239bff3cb34d5f64141433139269c7422c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 11:50:09 +0900 Subject: [PATCH 63/76] ci: repin sidecar after documentation fix --- docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md | 2 +- scripts/ci/contextual_orchestrator_review_sidecar.sh | 2 +- tests/test_contextual_orchestrator_review_sidecar_contract.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md index 94236bf6f0..d63f44df05 100644 --- a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md +++ b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md @@ -22,7 +22,7 @@ all five, and auto-optimize routing by cost. 1. **Vendoring, pinned**: `scripts/ci/contextual_orchestrator_review_sidecar.sh` clones `ContextualWisdomLab/contextual-orchestrator` at an exact SHA - (`f47981a9bbcd212344afc38ff975f5cf275810b9` exact contextual-orchestrator revision) into `RUNNER_TEMP`. The + (`a952ac739518e4783e7b42b64cc43d80c0aab4b9` exact contextual-orchestrator revision) into `RUNNER_TEMP`. The source's `requirements.lock` is installed with `--require-hashes` and `--no-deps`, so dependency resolution cannot silently move the reviewed runtime. diff --git a/scripts/ci/contextual_orchestrator_review_sidecar.sh b/scripts/ci/contextual_orchestrator_review_sidecar.sh index bd4ed64a1c..8de5b0678e 100755 --- a/scripts/ci/contextual_orchestrator_review_sidecar.sh +++ b/scripts/ci/contextual_orchestrator_review_sidecar.sh @@ -14,7 +14,7 @@ # (fail-closed zero-cost) pool. set -euo pipefail -ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-f47981a9bbcd212344afc38ff975f5cf275810b9}" +ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-a952ac739518e4783e7b42b64cc43d80c0aab4b9}" ORCHESTRATOR_GIT_URL="${ORCHESTRATOR_GIT_URL:-https://github.com/ContextualWisdomLab/contextual-orchestrator.git}" # The Strix gate and Noema SSRF guard accept this one process-local origin. # Keep it fixed so an environment override cannot create an unvalidated sidecar. diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index 86a7fbaf9a..9bd4b1e15a 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -38,7 +38,7 @@ ) GATEWAY_MODEL = "contextual-orchestrator/orchestrator/free" -ORCH_PIN_SHA = "f47981a9bbcd212344afc38ff975f5cf275810b9" +ORCH_PIN_SHA = "a952ac739518e4783e7b42b64cc43d80c0aab4b9" def _read(path: Path) -> str: From d90c73473ae2bb644ec6fb7404e1d3da9a875b87 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 12:10:35 +0900 Subject: [PATCH 64/76] ci: repin contextual sidecar after contract test fix --- docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md | 2 +- scripts/ci/contextual_orchestrator_review_sidecar.sh | 2 +- tests/test_contextual_orchestrator_review_sidecar_contract.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md index d63f44df05..7c92d7e3db 100644 --- a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md +++ b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md @@ -22,7 +22,7 @@ all five, and auto-optimize routing by cost. 1. **Vendoring, pinned**: `scripts/ci/contextual_orchestrator_review_sidecar.sh` clones `ContextualWisdomLab/contextual-orchestrator` at an exact SHA - (`a952ac739518e4783e7b42b64cc43d80c0aab4b9` exact contextual-orchestrator revision) into `RUNNER_TEMP`. The + (`1b417c438b1f37a5b56d5163a3f0896cd6ee5f57` exact contextual-orchestrator revision) into `RUNNER_TEMP`. The source's `requirements.lock` is installed with `--require-hashes` and `--no-deps`, so dependency resolution cannot silently move the reviewed runtime. diff --git a/scripts/ci/contextual_orchestrator_review_sidecar.sh b/scripts/ci/contextual_orchestrator_review_sidecar.sh index 8de5b0678e..e603f6cba2 100755 --- a/scripts/ci/contextual_orchestrator_review_sidecar.sh +++ b/scripts/ci/contextual_orchestrator_review_sidecar.sh @@ -14,7 +14,7 @@ # (fail-closed zero-cost) pool. set -euo pipefail -ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-a952ac739518e4783e7b42b64cc43d80c0aab4b9}" +ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-1b417c438b1f37a5b56d5163a3f0896cd6ee5f57}" ORCHESTRATOR_GIT_URL="${ORCHESTRATOR_GIT_URL:-https://github.com/ContextualWisdomLab/contextual-orchestrator.git}" # The Strix gate and Noema SSRF guard accept this one process-local origin. # Keep it fixed so an environment override cannot create an unvalidated sidecar. diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index 9bd4b1e15a..418f2c5d58 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -38,7 +38,7 @@ ) GATEWAY_MODEL = "contextual-orchestrator/orchestrator/free" -ORCH_PIN_SHA = "a952ac739518e4783e7b42b64cc43d80c0aab4b9" +ORCH_PIN_SHA = "1b417c438b1f37a5b56d5163a3f0896cd6ee5f57" def _read(path: Path) -> str: From 06495f23926c216c453cfb3865095a5c01b0be71 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 13:28:08 +0900 Subject: [PATCH 65/76] fix: accept hashed organization archive locks --- .../materialize_base_python_requirements.py | 29 +++++++++---- tests/test_uv_export_isolation_contract.py | 42 +++++++++++++++++++ 2 files changed, 64 insertions(+), 7 deletions(-) diff --git a/scripts/ci/materialize_base_python_requirements.py b/scripts/ci/materialize_base_python_requirements.py index a052123547..c0583b0142 100755 --- a/scripts/ci/materialize_base_python_requirements.py +++ b/scripts/ci/materialize_base_python_requirements.py @@ -43,6 +43,16 @@ r"(?P[A-Za-z0-9_.-]{1,100})\.git@" r"(?P[0-9a-fA-F]{40})" ) +UV_EXACT_ORG_ARCHIVE_RE = re.compile( + r"[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?" + r"(?:\[[A-Za-z0-9._-]+(?:,[A-Za-z0-9._-]+)*\])?\s+@\s+" + r"https://github\.com/ContextualWisdomLab/" + r"[A-Za-z0-9_.-]{1,100}/archive/" + r"(?:refs/(?:tags|heads)/)?" + r"[A-Za-z0-9][A-Za-z0-9._-]*(?:/[A-Za-z0-9._-]+)*" + r"\.(?:tar\.gz|zip)" + r"(?:\s*;\s*\S(?:.*\S)?)?" +) UV_EXPORT_TIMEOUT_SECONDS = 120 TRUSTED_UV_VERSION = "0.12.1" TRUSTED_UV_TARGET_TRIPLE = "x86_64-unknown-linux-gnu" @@ -261,7 +271,8 @@ def _is_flat_materializable_lock(content: bytes) -> bool: Selected sources are renamed to generated flat files. Relative ``-r`` and ``--requirement`` edges therefore lose the source directory that gives them - meaning. Only independent exact package pins cross this publication boundary + meaning. Only independent exact package pins or hash-pinned organization + archive URLs cross this publication boundary until a complete immutable include graph can be reconstructed and rewritten. """ lines = _requirement_lines(content) @@ -270,12 +281,15 @@ def _is_flat_materializable_lock(content: bytes) -> bool: _is_fully_hash_pinned_requirement(line) for line in requirement_lines ) def _is_fully_hash_pinned_requirement(line: str) -> bool: - """Return whether one uv-export line is an exact package pin with SHA-256 hashes.""" + """Return whether one uv-export line is an exact hash-pinned package or archive.""" fields = re.split(r"\s+(?=--hash=)", line) if len(fields) < 2: return False requirement, *hashes = fields - if UV_EXACT_REQUIREMENT_RE.fullmatch(requirement) is None: + if ( + UV_EXACT_REQUIREMENT_RE.fullmatch(requirement) is None + and UV_EXACT_ORG_ARCHIVE_RE.fullmatch(requirement) is None + ): return False return all(UV_SHA256_HASH_RE.fullmatch(hash_value) for hash_value in hashes) @@ -285,16 +299,17 @@ def _is_fully_hash_pinned_export(content: bytes) -> bool: The fixed exporter invocation does not request index, find-links, binary, or global hash directives. Every non-comment logical line must therefore be one - normalized package ``==`` pin with at least one complete SHA-256 hash. Option - lines, local/direct references, other algorithms, and truncated hashes are - rejected even when they contain a ``--hash=`` substring. + normalized package ``==`` pin or a hash-pinned archive URL from the trusted + organization, each with at least one complete SHA-256 hash. Option lines, + local/direct references, other origins, other algorithms, and truncated + hashes are rejected even when they contain a ``--hash=`` substring. """ lines = _requirement_lines(content) return bool(lines) and all(_is_fully_hash_pinned_requirement(line) for line in lines) def _partition_uv_export(content: bytes) -> tuple[bytes, list[dict[str, str]]]: - """Separate registry hash pins from exact organization VCS source pins.""" + """Separate hash pins from exact organization VCS source pins.""" registry_requirements: list[str] = [] vcs_by_repository: dict[str, dict[str, str]] = {} for line in _requirement_lines(content): diff --git a/tests/test_uv_export_isolation_contract.py b/tests/test_uv_export_isolation_contract.py index 76b72fdc7f..51ea2c2ca2 100644 --- a/tests/test_uv_export_isolation_contract.py +++ b/tests/test_uv_export_isolation_contract.py @@ -97,6 +97,30 @@ def test_uv_export_accepts_exact_package_pins_with_markers_and_multiple_hashes() assert materializer._is_fully_hash_pinned_export(content) is True +def test_uv_export_accepts_hash_pinned_organization_archive_as_registry_lock() -> None: + """A trusted HTTPS archive with a complete hash remains a pip lock entry.""" + content = ( + b"fast-mlsirm @ https://github.com/ContextualWisdomLab/fast-mlsirm/" + b"archive/refs/tags/v0.9.1.tar.gz ; python_full_version >= '3.12' \\\n" + b" --hash=sha256:" + b"a" * 64 + b"\n" + ) + + registry, vcs_sources = materializer._partition_uv_export(content) + + assert materializer._is_fully_hash_pinned_export(content) is True + assert registry.split() == [ + b"fast-mlsirm", + b"@", + b"https://github.com/ContextualWisdomLab/fast-mlsirm/archive/refs/tags/v0.9.1.tar.gz", + b";", + b"python_full_version", + b">=", + b"'3.12'", + b"--hash=sha256:" + b"a" * 64, + ] + assert vcs_sources == [] + + def test_uv_export_partitions_hashes_and_exact_organization_vcs_sources() -> None: """An immutable organization source pin is separated from pip hash locks.""" content = ( @@ -135,6 +159,24 @@ def test_uv_export_rejects_unbounded_vcs_sources(requirement: str) -> None: materializer._partition_uv_export(f"{requirement}\n".encode()) +@pytest.mark.parametrize( + "requirement", + [ + "demo @ http://github.com/ContextualWisdomLab/demo/archive/v1.tar.gz", + "demo @ https://github.com/other/demo/archive/v1.tar.gz", + "demo @ https://github.com/ContextualWisdomLab/demo/archive/v1.tar.gz?download=1", + "demo @ https://github.com/ContextualWisdomLab/demo/archive/v1.tar.gz#fragment", + "demo @ https://github.com/ContextualWisdomLab/demo/archive/../v1.tar.gz", + ], +) +def test_uv_export_rejects_unbounded_archive_sources(requirement: str) -> None: + """Only archive URLs from the exact organization origin are accepted.""" + with pytest.raises(ValueError, match="unsupported dependency"): + materializer._partition_uv_export( + f"{requirement} --hash=sha256:{'a' * 64}\n".encode() + ) + + def test_uv_export_rejects_conflicting_commits_for_one_repository() -> None: """One import path cannot ambiguously combine two repository revisions.""" with pytest.raises(ValueError, match="conflicting commits"): From 4c0f41bcd472b94aaf5a92bd43b4a80666421590 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 14:00:36 +0900 Subject: [PATCH 66/76] fix: isolate trusted archive build hooks --- .../workflows/opencode-review-dispatch.yml | 50 +++++ CHANGELOG.md | 4 + requirements-opencode-review-ci-hashes.txt | 179 ++++++++++------- requirements-opencode-review-ci.txt | 1 + scripts/ci/install_base_python_locks.py | 127 +++++++++++- .../materialize_base_python_requirements.py | 182 ++++++++++++++++-- tests/test_install_base_python_locks.py | 46 ++++- ...st_materialize_base_python_requirements.py | 51 ++++- tests/test_opencode_agent_contract.py | 6 + tests/test_uv_export_isolation_contract.py | 23 ++- 10 files changed, 561 insertions(+), 108 deletions(-) diff --git a/.github/workflows/opencode-review-dispatch.yml b/.github/workflows/opencode-review-dispatch.yml index 60406250a2..dbb134bf53 100644 --- a/.github/workflows/opencode-review-dispatch.yml +++ b/.github/workflows/opencode-review-dispatch.yml @@ -725,6 +725,7 @@ jobs: -r /tmp/requirements-opencode-review-ci-hashes.txt \ && rm -f /tmp/requirements-opencode-review-ci-hashes.txt COPY base-python-requirements /tmp/base-python-requirements + ENV CARGO_HOME=/opt/base-cargo RUN set -eu; \ mkdir -p /opt/base-vcs-dependencies; \ site_packages="$(python3 -c 'import site; print(site.getsitepackages()[0])')"; \ @@ -802,6 +803,55 @@ jobs: COPY install-base-python-locks.py /usr/local/libexec/install-base-python-locks.py RUN python3 -I /usr/local/libexec/install-base-python-locks.py \ --requirements-root /tmp/base-python-requirements \ + --no-archives + RUN mkdir -p /opt/base-python-archive-sources \ + && python3 - <<'PYTHON' + import json + import pathlib + import stat + import tarfile + import zipfile + + requirements_root = pathlib.Path("/tmp/base-python-requirements") + source_root = pathlib.Path("/opt/base-python-archive-sources").resolve() + manifest = json.loads( + (requirements_root / "archive-manifest.json").read_text(encoding="utf-8") + ) + for index, entry in enumerate(manifest): + relative_file = entry["file"] + archive = (requirements_root / relative_file).resolve() + destination = (source_root / f"archive-{index:03d}").resolve() + destination.mkdir(parents=True, exist_ok=True) + if not archive.is_file() or archive.is_symlink(): + raise SystemExit(f"archive input is not a regular file: {relative_file}") + if relative_file.endswith(".tar.gz"): + with tarfile.open(archive, "r:gz") as bundle: + members = bundle.getmembers() + for member in members: + target = (destination / member.name).resolve() + if not target.is_relative_to(destination) or member.issym() or member.islnk() or member.isdev(): + raise SystemExit(f"archive contains an unsafe member: {relative_file}") + bundle.extractall(destination) + elif relative_file.endswith(".zip"): + with zipfile.ZipFile(archive) as bundle: + for member in bundle.infolist(): + target = (destination / member.filename).resolve() + mode = (member.external_attr >> 16) & 0o170000 + if not target.is_relative_to(destination) or mode == stat.S_IFLNK: + raise SystemExit(f"archive contains an unsafe member: {relative_file}") + bundle.extractall(destination) + else: + raise SystemExit(f"unsupported archive suffix: {relative_file}") + PYTHON + RUN set -eu; \ + find /opt/base-python-archive-sources -name Cargo.toml -print0 \ + | sort -z -u \ + | while IFS= read -r -d "" manifest_path; do \ + cargo fetch --locked --manifest-path "$manifest_path"; \ + done + RUN --network=none python3 -I /usr/local/libexec/install-base-python-locks.py \ + --requirements-root /tmp/base-python-requirements \ + --archives-only \ && rm -rf /tmp/base-python-requirements \ && rm -f /usr/local/libexec/install-base-python-locks.py DOCKERFILE diff --git a/CHANGELOG.md b/CHANGELOG.md index b99f45a9cc..31948cec45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ this file. The format follows Keep a Changelog, and versioned releases follow Semantic Versioning where the repository publishes a release. ## [Unreleased] +- Materialize hash-pinned organization archive dependencies as verified local + inputs and install them in a separate Docker `--network=none` phase with the + pinned `maturin` build backend, so archive build hooks cannot use image-build + network access or alter the regular pip lock closure. - Preserve the private contextual-orchestrator bearer-file owner and mode gate across GNU, BusyBox, and BSD/macOS `stat` implementations without relaxing the required current-user ownership or exact mode `600` contract. diff --git a/requirements-opencode-review-ci-hashes.txt b/requirements-opencode-review-ci-hashes.txt index 367ac62858..a9ea1b7b50 100644 --- a/requirements-opencode-review-ci-hashes.txt +++ b/requirements-opencode-review-ci-hashes.txt @@ -4,9 +4,9 @@ attrs==26.1.0 \ --hash=sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309 \ --hash=sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32 # via interrogate -click==8.4.2 \ - --hash=sha256:9a6cea6e60b17ebe0a44c5cc636d94f09bd66142c1cd7d8b4cd731c4917a15f6 \ - --hash=sha256:e6f9f66136c816745b9d65817da91d61d957fb16e02e4dcd0552553c5a197b76 +click==8.5.0 \ + --hash=sha256:255bc9599cf7748b4b1a446ccc735421bd08a2ae529a8b88597d3de5664ee360 \ + --hash=sha256:ba0d2089de75ea0310e2dde03160e6ca10009947fb95a182f9b54021bb272e34 # via interrogate colorama==0.4.6 \ --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ @@ -107,69 +107,88 @@ coverage==7.14.3 \ # via # -r requirements-opencode-review-ci.txt # pytest-cov -hypothesis==6.163.0 \ - --hash=sha256:002a9709345892279fb0e81b5a05b72d08cfe81f937339827be0d588607ca9b0 \ - --hash=sha256:00d3091b28de83c5116e0ccd9a4bcb28ef61d2aace5df91093bb22434fd2350c \ - --hash=sha256:0a0c396244c13805edcb73ff467c4c8178ccefc41c4ef5ed00a68e612fd773e9 \ - --hash=sha256:0a933aca9ebf9daf951d07cf01200c94c321b6ee0b42cc7b67675c9686d914c2 \ - --hash=sha256:0cba5202f74e7e4cdb676d86f26e8cc1b4fdc88f7f58ba73c8ac45b6b22f3070 \ - --hash=sha256:213527755f0fc2b1f3721e73fd60023e2752a48f914e3e2df8d35111956ae5c8 \ - --hash=sha256:21e72e8d5818e5ef8cd6a2191c386e3fd1a6d9e3739cf97289b4d9b5dbc8e38d \ - --hash=sha256:2849c23b2e0fe2eef4c1ec336b01eac7ad7397c49fca43c264f59ec1e6046eac \ - --hash=sha256:28a6cc1c25a6cc9b6ec079eaabd32ac769994831ecddd57123ce43c9056dcf34 \ - --hash=sha256:31dc46c48aa53c3ec92d03120978ca7f19b9cf96d195ed3fc93503f1433c94a6 \ - --hash=sha256:320b076bf6436f971f1c73ee651e60001226d1b4e341f2c4a1ca87248261ca03 \ - --hash=sha256:331906cb029b6b360b8ebac3ec00c3cfa720037fe2efb294a503a1979c9a9a8f \ - --hash=sha256:34fc895691a2420595506eb17f3a104f2fa9039f013c0770a6cc2743ccaf6fed \ - --hash=sha256:3b6cee2afe6c67b31a4a64b63a876e0b020befdc61daabea80f7a0e14f19203a \ - --hash=sha256:3f3cceb4720a39127622fbf3bcebe1775b894372c53b5edddfdef10bbdeef9ec \ - --hash=sha256:40dfab6fe6a02a80abef81aebf88e53cd529e3f2f6ba3486b674a67b1f4a3512 \ - --hash=sha256:4159a1c2560e10de51b1c14956e277eb1b37526c9abef9e87c1e531760486448 \ - --hash=sha256:487ab8ec2f01a225d6a1e2ceadc5290cde2c691952bd2e7f76199cf82e06fb25 \ - --hash=sha256:4ab0dadc09c537d4ac57e564039dfe7daf09c98375306d54bfc0fd6c218efcca \ - --hash=sha256:50073f8e63c1e7d3403899755657a990d8bba7b5b5bff66b1c56796d4969bb28 \ - --hash=sha256:520480d4bd3a17557616c25923640953e360332c89d012fffcebd69857e674a9 \ - --hash=sha256:52f16840add2eb02c2416f3b83cec4f527b6c19699f2d31eff4859233c715526 \ - --hash=sha256:56ed585baab75cb98462c57ca88bbdc6a9d935a14118dd572fb476c3ecec2a06 \ - --hash=sha256:58be45d1737bf8c2e10cf29505c0f10f8a23d61bc82e4339182a6c8251cbc2d9 \ - --hash=sha256:59f5fdb8addb44c17520a60d50542d9db6ceba577bbf54efefa9c10ee20be140 \ - --hash=sha256:5a3ac6c62d49f7fe518dfe7fa924fa03aac839993702207802b0e45f9e1b0dab \ - --hash=sha256:67d1593941ede41052b4a35ec25b50d0e280358c7674ef7812d520010e7e8bdf \ - --hash=sha256:6ae63dec6d1d467b7f4737455f81a7a82f14a41c14510937fcfbc726a085b5f8 \ - --hash=sha256:7a3db868a943c814cc557104712d43bf609adfe5ea9f708f38377d366b4855f8 \ - --hash=sha256:7ca7b20bf38d51e15f7808b0239791c4792b1709ce0c63093acaff56a09c31e6 \ - --hash=sha256:7cb3d927360fe73f9a06d646e6082237142ee39c24679c7133d22bf06dd03b45 \ - --hash=sha256:7ef8954e37c80e0c46e6161eef1c72c71059b95250e620a77bd646f6c7a52a2d \ - --hash=sha256:8aac96db8a6c7ee43aba2ee0d3c43893da1fb7c38ed54790c1be2b6d8fd87b96 \ - --hash=sha256:8c5d1e6bad47edf6fb1d7406cf6d67314ac08325c63a49550d782a4596ea302b \ - --hash=sha256:9105c66ea8dbc108adc42058bb7b65bd953f53ee178bf63bf9ebb0cded6c8c96 \ - --hash=sha256:9be37b7ddf0af9e3f9112cd133afc34e78a56da1f96db5f2b4fc289fe1c4d1c3 \ - --hash=sha256:9c084749c115ea7918cf7efa144682783da17eec70d1276689182b871126e715 \ - --hash=sha256:9d23f0f3a14bb6e6f99c793d340196dba4af95ba25bfcab624d1794f540f5e27 \ - --hash=sha256:a16ebce774755a7a652bd44c62101dc914372ed1a98935969624848c9627b4a4 \ - --hash=sha256:a2a20e9835d3c4b293a709ee6ef769bcb18c6ed4ef337a9e251c1a9496d5e8be \ - --hash=sha256:a57352efa938889ea9992667a5014c0fc870d03945de71918574d1cf28276378 \ - --hash=sha256:ab34c61d9249f1a8129cb4276062c04e3e47b5be8de6446e7c7fe11362d6fe43 \ - --hash=sha256:b123b4995a7612f1130e2b2362c9a5d0568df887bf7e7bdb45c23af8cd5423c9 \ - --hash=sha256:b268211e625cd550e361fc387bf1db5deb1e9cae0ce4041116f0a0aafeef7c06 \ - --hash=sha256:b2ddcdaf6691101e06dc4a5add7b8c8fdf1e68daba599255a281f3f3550d3331 \ - --hash=sha256:b4ad2134405d5345434c22dea96bbc12c85abcfc3c253a8063dbc9ff01164555 \ - --hash=sha256:b839dfd1342bb50570cb0c66b80322307cdb468abf14faf5df4dab022bc1b9ce \ - --hash=sha256:b8f22fb8218ba6a452bf9000fc656e1ed57625d17cc8a3871a0fcea3b1b69ebf \ - --hash=sha256:bd312b15044b1c1a0920a5827a830559b2d1fa380851cedf509f8b835309c5b9 \ - --hash=sha256:c0ec3b709508ccd835d8ded1db025b7800618f2289a22a6bfd4927da5f4eb33c \ - --hash=sha256:c4f5be1482189c7b0a1dcac269fffe97a7d18cc04ac9a9a4d6613212dd87f38b \ - --hash=sha256:ca1b48bde68c528a79dec2a2859e05035802e5b1c9c3579f388c9de6ed6d0148 \ - --hash=sha256:d0838a28e9943d5b834ebae59b02adda76e2cd1e65caa808104c72102052057d \ - --hash=sha256:e165f6cc2075059b7c95dac1612bfb25494f72d90f56880e84c288b089f8a896 \ - --hash=sha256:e568a3d766b7ba8df00e0c33efc4c6530cde14fbc72daabe4824eed211ed7596 \ - --hash=sha256:ee47c2cb1be03a052ebd3549dad07f636a98b3ccfd7acbe5e17b3b7da0ab9e37 \ - --hash=sha256:f1fe222f50a1898e87a1e7323ab35f9e956278efabe4dd55a1342808206d05ad \ - --hash=sha256:f28ad27193c1fbcfb52ef2ee63d2b721563525089e80962b4268b306dac45507 \ - --hash=sha256:f2f1b67a48da86d3e41c9445367b49a49f7efdb60fc8b5e3593f05e6afb2efbe \ - --hash=sha256:f7f706df6839dcc53f20833f2933cbcd126fd2fdee7c312e053de49df4b64e44 \ - --hash=sha256:fae7305ae20fddeea09df317b920c45d3e20bfedbdb041f4db6ca5267c458189 \ - --hash=sha256:ffdda3006a383a48f71a23b4f2b3fae3fe1b09af67925d885985f7ec34d66bcb +hypothesis==6.165.10 \ + --hash=sha256:00de0abdcf8c05c9d0eab735a3c49a276376b55151e6fcb903c2b39a90e5e5c3 \ + --hash=sha256:057d0232f1224dcd0b7698902551a4341a7399f90670b036db6c4376715fe889 \ + --hash=sha256:09772e328a26e50486ac572be34f9887f9aa185efe7ebb16bde4e8f6038db1f4 \ + --hash=sha256:0c4e6869817c3cfdf5a2b4d348497b95159bdecb3365be732c9b8570e36a4eef \ + --hash=sha256:10d9a650a4666b0914831f769703d36140ed8039fd19bf9b71f615b8541eccf2 \ + --hash=sha256:18a3ea838ddea183388f8788750afa8494d79abb5358823be9782585f34445d3 \ + --hash=sha256:1a380bc99aa3b035e6a95a2201bf792d4082a04ca75babcc21849c2d0914bb28 \ + --hash=sha256:1d305448e9bd8e2f4f3cea0eafd809efdaab4e998a0019bc615650c8463e42f1 \ + --hash=sha256:1ec53f08732e3cfd0342cbbd75dbd1b193c8f19390660466e536a748bb81f757 \ + --hash=sha256:1f2c4db25fb8ec1a16a8dba580666337b8ffb1887c4cf1750cc954313897cef7 \ + --hash=sha256:20f6236cfb90b7817bb1a6a087589ca4aa46d73170f0dd62963952ed5dadc589 \ + --hash=sha256:22cf19388f0ff6ced8eb3e49c903d14938e4ed909d93bf28383eef451511e424 \ + --hash=sha256:277f41801e88dad2eba082f91a75632b7584ff64044ba2cf9dadf511b0d19cd0 \ + --hash=sha256:2a2567b3a03a4a5a7c575c191cfcce321a967df3727803817e75bffbbeaecabe \ + --hash=sha256:2abb50cf1cf77d721de0a24c3f99d9c4ffdeb2cbd1e12aebb5a7a93e2b6b6d1f \ + --hash=sha256:2b112768cfb67f2b683e53e58c1a33d27811aacf60c942b8eb74635e469a73f6 \ + --hash=sha256:2b36aaffc88625a44f91074c5bbedfdefb9b376c38d1b3c342edcd2e4c8ed16c \ + --hash=sha256:2d0e0f8263d34dd8fa3b39eaa9a50bba56a8470b3dd9ebf6672d10840abe063e \ + --hash=sha256:30797f20ca45e57f526d2df872f63ba453cb4e1091ad542184a7a951af8da79d \ + --hash=sha256:3376f2594763aef14faa519b0fb27cae7ce9eeaab4c69efa07777499110306c9 \ + --hash=sha256:34ee6402df6f31274d89119f1561b5f7489c97866afc5b7a3ed3a13d7e762802 \ + --hash=sha256:37a7ac3d34220800e1107871cc391bca1b00439875925d7d821878b8b791f245 \ + --hash=sha256:3de69aa8b924b400291a3cc42aaf78e6ab65c905a3e7e1a5dc39d95ef1b428cb \ + --hash=sha256:4334058033e0214475f019e15492a50f3854fe8728cf51fe25c6191a2c3f8e52 \ + --hash=sha256:490c56b830772b0eca3b4b2cecb3741a1ed26b1d7206a279e1525dbf0aa95ee4 \ + --hash=sha256:4c68e983d0007d014bb01ad4bcbba78bc432c73a1755ff36d5102ceefa18299a \ + --hash=sha256:5671d2b2bf83bd4b6f02e55b32d432506eff5358c82f39b460a849ce19a2666e \ + --hash=sha256:56cb8c9055e50545fe6e3e5a560ec25a724673b2e4051f3c24d44e3ebc35dd72 \ + --hash=sha256:5841331c504e02d7c334591681cb8587cdd59dee7e149db6d3db8e3f9e9f02eb \ + --hash=sha256:592107a0faf6c9c3a63a8dbf13dfb1cbda1cf599b0bc11c953221b00204b9ce1 \ + --hash=sha256:5cf3b612542ba174c9da4000b59a4f4c81e8d66f87509be85d3a1b71b5c36413 \ + --hash=sha256:60cab3ab4ea468d31a33739ffd7e94ec3e37dea891d65a6582ecc8a477175191 \ + --hash=sha256:637445c1593a2a9d1024fda50082f07bb56baedda78d90a25f64b8111727ef94 \ + --hash=sha256:68b45e09834cd80523cb1eb274463073c7a9af4e4ef7cff34d9615f355572d32 \ + --hash=sha256:6caadcd1afb62630ff5c5ff353626eaa616553a5971295ad6dc2b19ca8a39620 \ + --hash=sha256:6e20a02775eb3cf0ffb4f0219b6d7c1f240336663d4e5d7028675ec247c790c4 \ + --hash=sha256:713f4ce4e82c26b53031f139de959bc9e8b54d3995aa824b89bbdf8229df2a45 \ + --hash=sha256:717aea574e0e5edba2868aa66b1caae335d8f1ad3fb29f01dd6502953fa823a1 \ + --hash=sha256:72df95fb1db41755b155c5f02106e0036a339250555c8d351d488704fd112cf9 \ + --hash=sha256:73e6df02a6a62f8045b511c272f894d08e56d174504c793c9effcbc6778051a8 \ + --hash=sha256:76a7be86d986223b9f1bdb7e7cbcdb048649901fdb956c598ef73bdab1786cd5 \ + --hash=sha256:7730d8197086f65d8969a991d6728a1d420a51b19fea06535c896cb43a1e05d0 \ + --hash=sha256:79900a9920a0b1d3a626c03a90ac6bf7042e78d46906a565b86a0dbe926f1d96 \ + --hash=sha256:7a7980a898a3e6ebe4de1896a0507e3d519edb53fb9b4bda478c9fbeb6514558 \ + --hash=sha256:8001925fa3dde51cb574e4c9de4c7efe77c4e4d64bd2fd2ef61d5651f9d04f3d \ + --hash=sha256:8660572b2d424bf5369ea8990985225f70bd1615b76ecd9c25588a3b9307009f \ + --hash=sha256:8b20f44773a9ab84400465e318712d8c2ca16418d35b9f80aa27fdf2d690ad10 \ + --hash=sha256:90915635b9648071129b0f72c0673cf8eac9eb84cfd445c5bedef30c714b1ec2 \ + --hash=sha256:9ccac776b2ca93b324806facd526ccb45da0fd035001c899a35b02c44431e209 \ + --hash=sha256:9d77c3be7b429875036ad0f0597c6e5cc6bb17894a4da005e3807de64d2673ad \ + --hash=sha256:9f07ae36c3b093e13687a894e79fe69e98a94c0b67fef656c575247682218143 \ + --hash=sha256:ab0f2e9d7d7d4db257f7cf53de3706c2baf124269571f20ffc2bcd6781f03063 \ + --hash=sha256:ad0764730e8e3421601c2cc7e1f054a9206c60ea0917165d8d9193dc453f34f1 \ + --hash=sha256:aff1f584c9538e8979cd180b1d70bf99bc16be19d4666414f49e5942b21a4f2c \ + --hash=sha256:b33dc30170a7402e03c180f2c5ef69dc077152f35b91621e9cebcde9c7d71746 \ + --hash=sha256:b5820d009aedb7ae9cfd32f98b1ab0c0bbd6268379c4fab042218b6b655c63f8 \ + --hash=sha256:bb8c7d05ea27a093a92b250904095d71d924b6b44e5795a415c1b20c265f0c65 \ + --hash=sha256:c01dd04044c472e47193b54f68e84e08d6ebf4f29551885aa959b015f7cd9747 \ + --hash=sha256:c53e9b1c36350df9965ec44d6c0d4e0bbbb38f720dd2b0e1256dc6524d411015 \ + --hash=sha256:c6559380469295c4009215fe1cab561301591a3bee2e2fb3f4f96d2273a3affc \ + --hash=sha256:cc2da5aa4edf14743fa9257e5ba3513963999f01211635702479d8e92b8207c8 \ + --hash=sha256:d1ea02fa8ab3d33eb1125eade81f7136341eb429152c6dbe2ae6f8bc33b3fbdd \ + --hash=sha256:d623801ae3dcd97b77b983400ef3d48bf976648e4efff19929175322eaae074d \ + --hash=sha256:d9145fe43ebb22e66672967c3fab411793b226ed776e4fe282271bca6ad3c0bb \ + --hash=sha256:dafa7c9dbe3d802f9bcdf261b29c8a70700fb22839947f06e471f62c46b6257f \ + --hash=sha256:dd207497bb985918409a1bb5db85d1875f74e1269487332113b73d1ee7c77647 \ + --hash=sha256:e10858f57ed0e74baa04393845f469fe8ad502c16ece4499bef7700c575611bd \ + --hash=sha256:e1bbeb7c506b07ee0422cf9b2f7212fefa4240957f03526d38d27bc6743a0a48 \ + --hash=sha256:e5f95f7b622e4171096d92175dda0a560f0955ade9b8a3a07bdcf151f7359611 \ + --hash=sha256:e9acb2c4d9cb532c3fedea74159f7b923c8c036328c9239b4049e7aa073bdd81 \ + --hash=sha256:e9f924aa610c0618445e1e8738c822c3190ce2a2699a0cb48ec3a351a96761f2 \ + --hash=sha256:ed1a5891e59472884a03cb9875483e8fc131c80a275c60967f8afc5458a0c8ff \ + --hash=sha256:ed68e27b8a61e57a3ccdc7c5a14499e00b54dfe223087204d5d40b3b5ef58b6d \ + --hash=sha256:eeab73050ea58c13dd56e329f594c1dfe32ebd7bb169bbdf4f8ceefbc31ec6b5 \ + --hash=sha256:f4dafd6d6ababfa3b14dd6e5f0378cb7c7d291895a31a40abcbb7cc74f396131 \ + --hash=sha256:f69ec5be85ef508e206153bed8eafd03f7995dc464356c8bbb279a1e2b7d56f3 \ + --hash=sha256:f76d1562643693b8a40066f1f96af795b93fd9bcfc9690a1af2ff4c5867ee29e \ + --hash=sha256:f839d29d0cc12048cf073d88ca4fdf94d420bc2b8afd69641ff6d496422ccd4f \ + --hash=sha256:f9180c362bde06fd05380298ded4e234fbc0d6ede0a864835bfd91c1e24283d5 \ + --hash=sha256:f9ff356e97e3ab09db07c8b675efa67340103874a0bae7465acb83dad7a35f7f \ + --hash=sha256:fa74636a49fc8077413ce8db3e85f1c4aff880788bb55bda56253118e036fe5b # via -r requirements-opencode-review-ci.txt iniconfig==2.3.0 \ --hash=sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730 \ @@ -179,9 +198,25 @@ interrogate==1.7.0 \ --hash=sha256:a320d6ec644dfd887cc58247a345054fc4d9f981100c45184470068f4b3719b0 \ --hash=sha256:b13ff4dd8403369670e2efe684066de9fcb868ad9d7f2b4095d8112142dc9d12 # via -r requirements-opencode-review-ci.txt -packaging==26.2 \ - --hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e \ - --hash=sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661 +maturin==1.15.0 \ + --hash=sha256:0ebf9767892725083138e671c34482c660317a2f3d6a29fc0e0f34e9d8c99136 \ + --hash=sha256:126e12e618b4db42f68c779a56d41f82a390145ba36ac3f621d057eb34f5ad9d \ + --hash=sha256:4f9d33e6c3f9615c8caceecbbbd440f8eb25a3ddeb687077682cd5eca2e9ae15 \ + --hash=sha256:552c2be4afd43fe8d5c9f3ec8d4c4756d973b8dcbe94c14084390301f50243e1 \ + --hash=sha256:653020a63525bb224e5ab0adf02e17a2e08bc86dbea7fc1399c9a56d7529b99e \ + --hash=sha256:6bf6dc62e22d4dcfd5a51244ff0d58975fa4979c48209fe84159617648956d82 \ + --hash=sha256:7ab7eebffd7b8debca2265985de4eaeb332141276d24b9560b5ad484d4b3add1 \ + --hash=sha256:7eb066372f541f8eb4909c79c5d9bd0b9e8125980bdf1ec9e8aba23c6c8d6c55 \ + --hash=sha256:94b26cc8e8aba61a5f2099715fe640e18c5f678e9a500408b38761263954228a \ + --hash=sha256:bf29beddd0c6708f112db51d5275fc28b28b9e9c9c5faae387eaef662918b176 \ + --hash=sha256:c40b4eae7bf5ef1f4b1af8d623fe4105016f93578fb15b764e741d08ec3b92dd \ + --hash=sha256:c7dc0c66c78d3debdd9c5aa807e861fbcbf07f3505d34b125df74c03986b0f48 \ + --hash=sha256:cd35772633f489841132bc8e71d6fc7f842df30b9c05cd5cdf1ee1ddcb744cc7 \ + --hash=sha256:da649988be98e87e009e51b1bf0d301b6a301bc0cecbdd60d40d8ba60748d1ca + # via -r requirements-opencode-review-ci.txt +packaging==26.3 \ + --hash=sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79 \ + --hash=sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c # via pytest pluggy==1.6.0 \ --hash=sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3 \ @@ -193,9 +228,9 @@ py==1.11.0 \ --hash=sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719 \ --hash=sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378 # via interrogate -pygments==2.20.0 \ - --hash=sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f \ - --hash=sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176 +pygments==2.21.0 \ + --hash=sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9 \ + --hash=sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c # via pytest pytest==9.1.1 \ --hash=sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313 \ diff --git a/requirements-opencode-review-ci.txt b/requirements-opencode-review-ci.txt index 0b585231f6..5381745c0f 100644 --- a/requirements-opencode-review-ci.txt +++ b/requirements-opencode-review-ci.txt @@ -1,4 +1,5 @@ coverage==7.14.3 +maturin>=1.10,<2.0 # hypothesis (MPL-2.0, permissive test tool) so the coverage-evidence sandbox can # run repos' always-on property tests (tests/fuzz/*) instead of ImportError-ing on # collection. Matches the >=6.100 floor used by consumer repos (e.g. contextual-orchestrator). diff --git a/scripts/ci/install_base_python_locks.py b/scripts/ci/install_base_python_locks.py index 1b9ab10693..ee1e00f937 100644 --- a/scripts/ci/install_base_python_locks.py +++ b/scripts/ci/install_base_python_locks.py @@ -14,6 +14,7 @@ from __future__ import annotations import argparse +import hashlib import json import pathlib import re @@ -26,6 +27,8 @@ GENERATED_LOCK_RE = re.compile(r"^requirements-[0-9]{3}\.txt$") +GENERATED_ARCHIVE_RE = re.compile(r"^archives/archive-[0-9]{3}\.(?:tar\.gz|zip)$") +SHA256_RE = re.compile(r"^[0-9a-f]{64}$") DEFERABLE_PREFLIGHT_FAILURES = ( re.compile( r"In --require-hashes mode, all requirements must have their versions " @@ -57,6 +60,65 @@ def source_directory(self) -> str: return "" if parent == "." else parent +@dataclass(frozen=True) +class ArchiveCandidate: + """One materialized archive source with a verified content digest.""" + + package: str + file: pathlib.Path + hashes: tuple[str, ...] + + +def _archive_entries( + requirements_root: pathlib.Path, +) -> list[ArchiveCandidate]: + """Load and validate the archive files materialized from the base lock.""" + manifest_path = requirements_root.resolve() / "archive-manifest.json" + if not manifest_path.exists(): + return [] + if not manifest_path.is_file() or manifest_path.is_symlink(): + raise ValueError("base Python archive manifest must be a regular non-symlink file") + try: + manifest: Any = json.loads(manifest_path.read_text(encoding="utf-8")) + except (OSError, json.JSONDecodeError) as exc: + raise ValueError(f"base Python archive manifest is invalid: {exc}") from exc + if not isinstance(manifest, list): + raise ValueError("base Python archive manifest must be a JSON array") + + entries: list[ArchiveCandidate] = [] + seen_files: set[str] = set() + for entry in manifest: + if not isinstance(entry, dict): + raise ValueError("base Python archive manifest entries must be objects") + package = entry.get("package") + relative_file = entry.get("file") + hashes = entry.get("hashes") + if ( + not isinstance(package, str) + or not package + or not isinstance(relative_file, str) + or GENERATED_ARCHIVE_RE.fullmatch(relative_file) is None + or not isinstance(hashes, list) + or not hashes + or any(not isinstance(value, str) or not SHA256_RE.fullmatch(value) for value in hashes) + ): + raise ValueError("base Python archive manifest contains an invalid entry") + if relative_file in seen_files: + raise ValueError("base Python archive manifest contains duplicate files") + seen_files.add(relative_file) + archive = requirements_root.resolve() / relative_file + if not archive.is_file() or archive.is_symlink(): + raise ValueError(f"materialized base Python archive {relative_file} must be a regular file") + digest = hashlib.sha256() + with archive.open("rb") as handle: + for chunk in iter(lambda: handle.read(1024 * 1024), b""): + digest.update(chunk) + if digest.hexdigest() not in hashes: + raise ValueError(f"materialized base Python archive {relative_file} failed hash verification") + entries.append(ArchiveCandidate(package, archive, tuple(hashes))) + return entries + + def _manifest_entries( requirements_root: pathlib.Path, ) -> list[LockCandidate]: @@ -129,6 +191,21 @@ def _pip_command(requirements: Sequence[pathlib.Path], *, preflight: bool) -> li return command +def _archive_pip_command(archive: pathlib.Path) -> list[str]: + """Build a no-network source-install command for one verified archive.""" + return [ + sys.executable, + "-m", + "pip", + "install", + "--break-system-packages", + "--disable-pip-version-check", + "--no-deps", + "--no-build-isolation", + str(archive), + ] + + def _bounded_failure_output(output: str, *, maximum_lines: int = 120) -> str: """Keep the dependency root cause visible without flooding Actions logs.""" lines = output.rstrip().splitlines() @@ -183,17 +260,40 @@ def _report_fatal_preflight_failure( def install_materialized_locks( requirements_root: pathlib.Path, *, + install_archives: bool = True, + archives_only: bool = False, runner: Runner = subprocess.run, stdout: TextIO = sys.stdout, stderr: TextIO = sys.stderr, ) -> int: """Preflight and install independent base lock closures.""" try: - entries = _manifest_entries(requirements_root) + archive_entries = _archive_entries(requirements_root) + entries = [] if archives_only else _manifest_entries(requirements_root) except (OSError, ValueError) as exc: - print(f"::error::Could not validate base Python locks: {exc}", file=stderr) + print(f"::error::Could not validate base Python lock inputs: {exc}", file=stderr) return 2 + if archives_only: + for archive in archive_entries: + print( + f"Installing verified trusted base Python archive {archive.package}.", + file=stdout, + flush=True, + ) + installation = runner(_archive_pip_command(archive.file), check=False) + if installation.returncode != 0: + print( + f"::error::Verified trusted base Python archive failed to install: {archive.package}.", + file=stderr, + ) + return installation.returncode or 1 + print( + f"Trusted base Python archive installation summary: installed={len(archive_entries)}.", + file=stdout, + ) + return 0 + installed = 0 skipped = 0 preflight_results: dict[str, subprocess.CompletedProcess[str]] = {} @@ -312,6 +412,21 @@ def install_materialized_locks( return installation.returncode or 1 installed += len(plan) + if install_archives: + for archive in archive_entries: + print( + f"Installing verified trusted base Python archive {archive.package}.", + file=stdout, + flush=True, + ) + installation = runner(_archive_pip_command(archive.file), check=False) + if installation.returncode != 0: + print( + f"::error::Verified trusted base Python archive failed to install: {archive.package}.", + file=stderr, + ) + return installation.returncode or 1 + print( "Trusted base Python lock installation summary: " f"candidates={len(entries)} installed={installed} skipped={skipped}.", @@ -324,8 +439,14 @@ def main(argv: Sequence[str] | None = None) -> int: """Install materialized lock candidates supplied by the trusted workflow.""" parser = argparse.ArgumentParser() parser.add_argument("--requirements-root", required=True, type=pathlib.Path) + parser.add_argument("--no-archives", action="store_true") + parser.add_argument("--archives-only", action="store_true") args = parser.parse_args(argv) - return install_materialized_locks(args.requirements_root) + return install_materialized_locks( + args.requirements_root, + install_archives=not args.no_archives, + archives_only=args.archives_only, + ) if __name__ == "__main__": # pragma: no cover diff --git a/scripts/ci/materialize_base_python_requirements.py b/scripts/ci/materialize_base_python_requirements.py index c0583b0142..b8a66a4f9b 100755 --- a/scripts/ci/materialize_base_python_requirements.py +++ b/scripts/ci/materialize_base_python_requirements.py @@ -19,6 +19,7 @@ import sys import tarfile import tempfile +import urllib.error import urllib.parse import urllib.request from typing import Any @@ -44,14 +45,14 @@ r"(?P[0-9a-fA-F]{40})" ) UV_EXACT_ORG_ARCHIVE_RE = re.compile( - r"[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?" - r"(?:\[[A-Za-z0-9._-]+(?:,[A-Za-z0-9._-]+)*\])?\s+@\s+" - r"https://github\.com/ContextualWisdomLab/" + r"(?P[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?" + r"(?:\[[A-Za-z0-9._-]+(?:,[A-Za-z0-9._-]+)*\])?)\s+@\s+" + r"(?Phttps://github\.com/ContextualWisdomLab/" r"[A-Za-z0-9_.-]{1,100}/archive/" r"(?:refs/(?:tags|heads)/)?" r"[A-Za-z0-9][A-Za-z0-9._-]*(?:/[A-Za-z0-9._-]+)*" r"\.(?:tar\.gz|zip)" - r"(?:\s*;\s*\S(?:.*\S)?)?" + r")(?:\s*;\s*\S(?:.*\S)?)?" ) UV_EXPORT_TIMEOUT_SECONDS = 120 TRUSTED_UV_VERSION = "0.12.1" @@ -80,6 +81,9 @@ TRUSTED_UV_ORIGIN_ERROR = ( "trusted uv archive redirected outside the fixed GitHub release HTTPS origin" ) +TRUSTED_ORG_ARCHIVE_HOSTS = frozenset({"github.com", "codeload.github.com"}) +TRUSTED_ORG_ARCHIVE_MAX_BYTES = 256 * 1024 * 1024 +TRUSTED_ORG_ARCHIVE_TIMEOUT_SECONDS = 120 def _https_default_port(parsed: urllib.parse.ParseResult) -> bool: @@ -120,6 +124,76 @@ def _is_trusted_uv_final_origin(url: str) -> bool: return _is_trusted_uv_https_host(url, TRUSTED_UV_FINAL_HOSTS) +def _is_trusted_org_archive_url(url: str) -> bool: + """Return whether one archive URL stays on GitHub's HTTPS origins.""" + parsed = urllib.parse.urlparse(url) + return ( + parsed.scheme == "https" + and parsed.hostname in TRUSTED_ORG_ARCHIVE_HOSTS + and parsed.username is None + and parsed.password is None + and _https_default_port(parsed) + ) + + +class _TrustedOrgArchiveRedirects(urllib.request.HTTPRedirectHandler): + """Follow GitHub archive redirects only onto GitHub's code-download host.""" + + def redirect_request( + self, + request: urllib.request.Request, + response: Any, + code: int, + message: str, + headers: Any, + new_url: str, + ) -> urllib.request.Request: + """Reject archive redirects that leave the fixed GitHub origins.""" + if not _is_trusted_org_archive_url(request.full_url) or not _is_trusted_org_archive_url(new_url): + raise RuntimeError("trusted organization archive redirected outside GitHub") + followed = super().redirect_request( + request, + response, + code, + message, + headers, + new_url, + ) + if followed is None: + raise RuntimeError("trusted organization archive redirect was rejected") + return followed + + +def _download_trusted_org_archive(url: str, hashes: list[str]) -> bytes: + """Download and verify one exact organization archive before image build.""" + if not _is_trusted_org_archive_url(url): + raise RuntimeError("trusted organization archive URL is not GitHub HTTPS") + opener = urllib.request.build_opener( + urllib.request.ProxyHandler({}), + _TrustedOrgArchiveRedirects(), + ) + try: + with opener.open(url, timeout=TRUSTED_ORG_ARCHIVE_TIMEOUT_SECONDS) as response: + if not _is_trusted_org_archive_url(response.geturl()): + raise RuntimeError("trusted organization archive left GitHub origins") + payload = bytearray() + while len(payload) <= TRUSTED_ORG_ARCHIVE_MAX_BYTES: + chunk = response.read(TRUSTED_ORG_ARCHIVE_MAX_BYTES + 1 - len(payload)) + if not chunk: + break + payload.extend(chunk) + except (OSError, urllib.error.URLError) as exc: + raise RuntimeError( + f"trusted organization archive download failed: {type(exc).__name__}" + ) from exc + if len(payload) > TRUSTED_ORG_ARCHIVE_MAX_BYTES: + raise RuntimeError("trusted organization archive exceeded the bounded size") + digest = hashlib.sha256(payload).hexdigest() + if digest not in hashes: + raise RuntimeError("trusted organization archive checksum verification failed") + return bytes(payload) + + class _TrustedUvReleaseAssetRedirects(urllib.request.HTTPRedirectHandler): """Follow one GitHub Releases hop onto the official asset CDN only.""" @@ -260,7 +334,7 @@ def _is_hash_pinned(content: bytes) -> bool: if not requirement_lines: return False return all( - _is_fully_hash_pinned_requirement(line) + _is_registry_hash_pinned_requirement(line) or _is_bounded_requirement_include(line) for line in requirement_lines ) @@ -278,7 +352,18 @@ def _is_flat_materializable_lock(content: bytes) -> bool: lines = _requirement_lines(content) requirement_lines = [line for line in lines if line != "--require-hashes"] return bool(requirement_lines) and all( - _is_fully_hash_pinned_requirement(line) for line in requirement_lines + _is_registry_hash_pinned_requirement(line) for line in requirement_lines + ) + + +def _is_registry_hash_pinned_requirement(line: str) -> bool: + """Return whether one registry requirement is an exact SHA-256 pin.""" + fields = re.split(r"\s+(?=--hash=)", line) + if len(fields) < 2: + return False + requirement, *hashes = fields + return UV_EXACT_REQUIREMENT_RE.fullmatch(requirement) is not None and all( + UV_SHA256_HASH_RE.fullmatch(hash_value) for hash_value in hashes ) def _is_fully_hash_pinned_requirement(line: str) -> bool: """Return whether one uv-export line is an exact hash-pinned package or archive.""" @@ -308,11 +393,41 @@ def _is_fully_hash_pinned_export(content: bytes) -> bool: return bool(lines) and all(_is_fully_hash_pinned_requirement(line) for line in lines) -def _partition_uv_export(content: bytes) -> tuple[bytes, list[dict[str, str]]]: - """Separate hash pins from exact organization VCS source pins.""" +def _archive_from_uv_line(line: str) -> dict[str, object] | None: + """Return one validated organization archive descriptor from an export line.""" + fields = re.split(r"\s+(?=--hash=)", line) + if len(fields) < 2: + return None + requirement, *hash_fields = fields + match = UV_EXACT_ORG_ARCHIVE_RE.fullmatch(requirement) + if match is None: + return None + hashes = [field.removeprefix("--hash=sha256:").lower() for field in hash_fields] + if not hashes or any(not re.fullmatch(r"[0-9a-fA-F]{64}", value) for value in hashes): + raise ValueError("organization archive must carry complete SHA-256 hashes") + return { + "package": match.group("package"), + "url": match.group("url"), + "hashes": hashes, + } + + +def _partition_uv_export( + content: bytes, +) -> tuple[bytes, list[dict[str, str]], list[dict[str, object]]]: + """Separate registry pins, VCS sources, and verified archive sources.""" registry_requirements: list[str] = [] vcs_by_repository: dict[str, dict[str, str]] = {} + archives_by_url: dict[str, dict[str, object]] = {} for line in _requirement_lines(content): + archive = _archive_from_uv_line(line) + if archive is not None: + url = str(archive["url"]) + previous = archives_by_url.get(url) + if previous is not None and previous["hashes"] != archive["hashes"]: + raise ValueError("uv export pins one archive URL to conflicting hashes") + archives_by_url[url] = archive + continue if _is_fully_hash_pinned_requirement(line): registry_requirements.append(line) continue @@ -338,9 +453,13 @@ def _partition_uv_export(content: bytes) -> tuple[bytes, list[dict[str, str]]]: if registry_requirements else b"" ) - return registry_content, sorted( - vcs_by_repository.values(), - key=lambda dependency: dependency["repository"].casefold(), + return ( + registry_content, + sorted( + vcs_by_repository.values(), + key=lambda dependency: dependency["repository"].casefold(), + ), + sorted(archives_by_url.values(), key=lambda dependency: str(dependency["url"])), ) @@ -550,7 +669,7 @@ def _reject_unsupported_uv_workspace( def _export_uv_lock( repo_root: pathlib.Path, base_sha: str, lock_path: str -) -> tuple[bytes, list[dict[str, str]]] | None: +) -> tuple[bytes, list[dict[str, str]], list[dict[str, object]]] | None: """Export one tracked base ``uv.lock`` into a trusted hash-pinned closure. The caller proves that the sibling ``pyproject.toml`` is a regular blob in @@ -632,8 +751,8 @@ def _regular_base_blob_paths(entries: bytes) -> list[tuple[str, pathlib.PurePosi def _base_python_inputs( repo_root: pathlib.Path, base_sha: str -) -> tuple[list[tuple[str, bytes]], list[dict[str, str]]]: - """Return hash locks and exact VCS sources from one validated base commit.""" +) -> tuple[list[tuple[str, bytes]], list[dict[str, str]], list[dict[str, object]]]: + """Return locks, exact VCS sources, and verified archive sources.""" if not SHA_RE.fullmatch(base_sha): raise ValueError("base SHA must be exactly 40 hexadecimal characters") @@ -642,6 +761,7 @@ def _base_python_inputs( regular_paths = {path for path, _candidate in regular_blobs} locks: list[tuple[str, bytes]] = [] vcs_by_repository: dict[str, dict[str, str]] = {} + archives_by_url: dict[str, dict[str, object]] = {} for path, candidate in regular_blobs: if _is_candidate_lock_path(candidate): content = _git(repo_root, "show", f"{base_sha}:{path}") @@ -652,7 +772,7 @@ def _base_python_inputs( continue exported = _export_uv_lock(repo_root, base_sha, path) if exported is not None: - registry_content, vcs_dependencies = exported + registry_content, vcs_dependencies, archive_dependencies = exported if registry_content: locks.append((path, registry_content)) for dependency in vcs_dependencies: @@ -668,12 +788,27 @@ def _base_python_inputs( "to conflicting commits" ) vcs_by_repository[repository_key] = dependency + for archive in archive_dependencies: + url = str(archive["url"]) + previous_archive = archives_by_url.get(url) + if ( + previous_archive is not None + and previous_archive["hashes"] != archive["hashes"] + ): + raise RuntimeError( + "base uv locks pin one archive URL to conflicting hashes" + ) + archives_by_url[url] = { + **archive, + "source": path, + } return ( sorted(locks, key=lambda item: item[0]), sorted( vcs_by_repository.values(), key=lambda dependency: dependency["repository"].casefold(), ), + sorted(archives_by_url.values(), key=lambda dependency: str(dependency["url"])), ) @@ -751,7 +886,7 @@ def materialize( regular_paths = { path for path, _candidate in _regular_base_blob_paths(entries) } - locks, vcs_manifest = _base_python_inputs(resolved_repo, base_sha) + locks, vcs_manifest, archive_sources = _base_python_inputs(resolved_repo, base_sha) manifest: list[dict[str, str]] = [] for index, (source_path, content) in enumerate(locks): generated_name = f"requirements-{index:03d}.txt" @@ -785,6 +920,21 @@ def materialize( json.dumps(vcs_manifest, indent=2, sort_keys=True) + "\n", encoding="utf-8", ) + archive_directory = output_dir / "archives" + archive_manifest: list[dict[str, object]] = [] + for index, archive in enumerate(archive_sources): + url = str(archive["url"]) + suffix = ".tar.gz" if url.endswith(".tar.gz") else ".zip" + archive_file = f"archive-{index:03d}{suffix}" + archive_directory.mkdir(parents=True, exist_ok=True) + (archive_directory / archive_file).write_bytes( + _download_trusted_org_archive(url, list(archive["hashes"])) + ) + archive_manifest.append({**archive, "file": f"archives/{archive_file}"}) + (output_dir / "archive-manifest.json").write_text( + json.dumps(archive_manifest, indent=2, sort_keys=True) + "\n", + encoding="utf-8", + ) return manifest diff --git a/tests/test_install_base_python_locks.py b/tests/test_install_base_python_locks.py index b6f1782a02..ac7731c7d2 100644 --- a/tests/test_install_base_python_locks.py +++ b/tests/test_install_base_python_locks.py @@ -3,6 +3,7 @@ from __future__ import annotations import io +import hashlib import json import pathlib import subprocess @@ -91,6 +92,49 @@ def fake_runner(command: list[str], **kwargs): assert "candidates=2 installed=2 skipped=0" in stdout.getvalue() +def test_installs_verified_archives_without_network_or_dependency_resolution(tmp_path) -> None: + """Archive build hooks run only in the caller's network-isolated phase.""" + archive = tmp_path / "archives" / "archive-000.tar.gz" + archive.parent.mkdir() + archive.write_bytes(b"verified archive") + (tmp_path / "archive-manifest.json").write_text( + json.dumps( + [ + { + "package": "demo", + "file": "archives/archive-000.tar.gz", + "hashes": [hashlib.sha256(archive.read_bytes()).hexdigest()], + } + ] + ), + encoding="utf-8", + ) + commands: list[list[str]] = [] + + def fake_runner(command: list[str], **kwargs): + commands.append(command) + return subprocess.CompletedProcess(command, 0, stdout="") + + assert installer.install_materialized_locks( + tmp_path, + archives_only=True, + runner=fake_runner, + ) == 0 + assert commands == [ + [ + installer.sys.executable, + "-m", + "pip", + "install", + "--break-system-packages", + "--disable-pip-version-check", + "--no-deps", + "--no-build-isolation", + str(archive), + ] + ] + + def test_skips_partial_candidate_without_completing_sibling(tmp_path) -> None: """An unrecoverable hash-bearing supplement remains visible and non-fatal.""" write_candidate( @@ -443,7 +487,7 @@ def test_main_forwards_requirements_root(monkeypatch, tmp_path) -> None: """The CLI delegates the exact requirements root to the installer.""" seen: list[pathlib.Path] = [] - def fake_install(root: pathlib.Path) -> int: + def fake_install(root: pathlib.Path, **_kwargs) -> int: seen.append(root) return 7 diff --git a/tests/test_materialize_base_python_requirements.py b/tests/test_materialize_base_python_requirements.py index 58ded37400..e4a1f66ffa 100644 --- a/tests/test_materialize_base_python_requirements.py +++ b/tests/test_materialize_base_python_requirements.py @@ -131,7 +131,7 @@ def test_materializes_exact_vcs_sources_in_a_separate_manifest( monkeypatch.setattr( materializer, "_base_python_inputs", - lambda *_args: ([("uv.lock", hash_lock)], vcs_sources), + lambda *_args: ([("uv.lock", hash_lock)], vcs_sources, []), ) output = tmp_path / "output" @@ -144,6 +144,46 @@ def test_materializes_exact_vcs_sources_in_a_separate_manifest( ) +def test_materializes_archive_sources_separately_from_pip_locks( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Archive sources are verified before the image's no-network install step.""" + repository = tmp_path / "repo" + repository.mkdir() + git(repository, "init") + git(repository, "config", "user.name", "Test") + git(repository, "config", "user.email", "test@example.invalid") + git(repository, "commit", "--allow-empty", "-m", "base") + base_sha = git(repository, "rev-parse", "HEAD") + archive = b"verified archive" + archive_source = { + "package": "demo", + "url": "https://github.com/ContextualWisdomLab/demo/archive/v1.tar.gz", + "hashes": [hashlib.sha256(archive).hexdigest()], + "source": "uv.lock", + } + monkeypatch.setattr( + materializer, + "_base_python_inputs", + lambda *_args: ([], [], [archive_source]), + ) + monkeypatch.setattr( + materializer, + "_download_trusted_org_archive", + lambda _url, _hashes: archive, + ) + + output = tmp_path / "output" + materializer.materialize(repository, base_sha, output) + + assert not list(output.glob("requirements-*.txt")) + assert json.loads((output / "archive-manifest.json").read_text()) == [ + {**archive_source, "file": "archives/archive-000.tar.gz"} + ] + assert (output / "archives/archive-000.tar.gz").read_bytes() == archive + + def test_base_inputs_preserve_a_vcs_only_export( tmp_path: Path, monkeypatch: pytest.MonkeyPatch, @@ -163,13 +203,16 @@ def test_base_inputs_preserve_a_vcs_only_export( monkeypatch.setattr( materializer, "_export_uv_lock", - lambda *_args: (b"", [dependency]), + lambda *_args: (b"", [dependency], []), ) - locks, vcs_sources = materializer._base_python_inputs(tmp_path, "a" * 40) + locks, vcs_sources, archive_sources = materializer._base_python_inputs( + tmp_path, "a" * 40 + ) assert locks == [] assert vcs_sources == [{**dependency, "source": "uv.lock"}] + assert archive_sources == [] def test_base_inputs_reject_conflicting_vcs_revisions_across_locks( @@ -190,7 +233,7 @@ def test_base_inputs_reject_conflicting_vcs_revisions_across_locks( def export(_repo: Path, _sha: str, lock_path: str): commit = "a" * 40 if lock_path.startswith("first/") else "b" * 40 - return b"", [{"package": "demo", "repository": "demo", "commit": commit}] + return b"", [{"package": "demo", "repository": "demo", "commit": commit}], [] monkeypatch.setattr(materializer, "_export_uv_lock", export) diff --git a/tests/test_opencode_agent_contract.py b/tests/test_opencode_agent_contract.py index c5d054e772..12930b02e3 100644 --- a/tests/test_opencode_agent_contract.py +++ b/tests/test_opencode_agent_contract.py @@ -699,12 +699,18 @@ def test_opencode_target_coverage_materializes_only_after_authorized_dispatch(): assert 'install -m 0755 "$trusted_base_python_installer"' in measure_step assert "COPY install-base-python-locks.py" in measure_step assert "python3 -I /usr/local/libexec/install-base-python-locks.py" in measure_step + assert "--no-archives" in measure_step + assert "--archives-only" in measure_step + assert "RUN --network=none python3 -I /usr/local/libexec/install-base-python-locks.py" in measure_step assert '"https://github.com/ContextualWisdomLab/${repository}.git"' in measure_step assert '--quiet --no-tags --depth=1 origin "$commit"' in measure_step assert 'rev-parse FETCH_HEAD)" = "$commit"' in measure_step assert 'rev-parse HEAD)" = "$commit"' in measure_step assert "opencode-base-vcs-dependencies.pth" in measure_step assert 'vcs-manifest.json >"$dependency_list"' in measure_step + assert 'maturin>=1.10,<2.0' in Path( + "requirements-opencode-review-ci.txt" + ).read_text(encoding="utf-8") assert 'done <"$dependency_list"' in measure_step assert 'candidate_count=$((candidate_count + 1))' in measure_step assert '[ "$candidate_count" -ne 1 ]' in measure_step diff --git a/tests/test_uv_export_isolation_contract.py b/tests/test_uv_export_isolation_contract.py index 51ea2c2ca2..7eec2675e0 100644 --- a/tests/test_uv_export_isolation_contract.py +++ b/tests/test_uv_export_isolation_contract.py @@ -105,20 +105,18 @@ def test_uv_export_accepts_hash_pinned_organization_archive_as_registry_lock() - b" --hash=sha256:" + b"a" * 64 + b"\n" ) - registry, vcs_sources = materializer._partition_uv_export(content) + registry, vcs_sources, archive_sources = materializer._partition_uv_export(content) assert materializer._is_fully_hash_pinned_export(content) is True - assert registry.split() == [ - b"fast-mlsirm", - b"@", - b"https://github.com/ContextualWisdomLab/fast-mlsirm/archive/refs/tags/v0.9.1.tar.gz", - b";", - b"python_full_version", - b">=", - b"'3.12'", - b"--hash=sha256:" + b"a" * 64, - ] + assert registry == b"" assert vcs_sources == [] + assert archive_sources == [ + { + "package": "fast-mlsirm", + "url": "https://github.com/ContextualWisdomLab/fast-mlsirm/archive/refs/tags/v0.9.1.tar.gz", + "hashes": ["a" * 64], + } + ] def test_uv_export_partitions_hashes_and_exact_organization_vcs_sources() -> None: @@ -129,7 +127,7 @@ def test_uv_export_partitions_hashes_and_exact_organization_vcs_sources() -> Non b"61c49c50d3b4a24fc9bd7c6d3a7f2f4ba19d7be6\n" ) - registry, vcs_sources = materializer._partition_uv_export(content) + registry, vcs_sources, archive_sources = materializer._partition_uv_export(content) assert registry == b"demo==1.2.3 --hash=sha256:" + b"a" * 64 + b"\n" assert vcs_sources == [ @@ -140,6 +138,7 @@ def test_uv_export_partitions_hashes_and_exact_organization_vcs_sources() -> Non "commit": "61c49c50d3b4a24fc9bd7c6d3a7f2f4ba19d7be6", } ] + assert archive_sources == [] @pytest.mark.parametrize( From b28ea51ad2659d6511a072a54efbe76044345df4 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 14:05:21 +0900 Subject: [PATCH 67/76] test: sync review workflow blob contract --- tests/test_pr_review_autofix_nvidia_nim_contract.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_pr_review_autofix_nvidia_nim_contract.py b/tests/test_pr_review_autofix_nvidia_nim_contract.py index 02039ee355..30703ac84a 100644 --- a/tests/test_pr_review_autofix_nvidia_nim_contract.py +++ b/tests/test_pr_review_autofix_nvidia_nim_contract.py @@ -19,7 +19,7 @@ DOCTORING_RECORD = Path("docs/doctoring/hourly-nvidia-nim-autofix.md") CHANGELOG = Path("CHANGELOG.md") REVIEW_DISPATCH_WORKFLOW = Path(".github/workflows/opencode-review-dispatch.yml") -REVIEW_DISPATCH_BLOB_SHA = "60406250a282430329a57405d2a6cb8bc6d319aa" +REVIEW_DISPATCH_BLOB_SHA = "dbb134bf535be289844ec93fa6e2ca5a42137216" def _workflow_text(path: Path) -> str: From a7003b1cd07a382eef5f59a32e741d919efeaac1 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 14:13:47 +0900 Subject: [PATCH 68/76] test: cover trusted archive boundaries --- ...st_materialize_base_python_requirements.py | 215 ++++++++++++++++++ tests/test_uv_export_isolation_contract.py | 23 ++ 2 files changed, 238 insertions(+) diff --git a/tests/test_materialize_base_python_requirements.py b/tests/test_materialize_base_python_requirements.py index e4a1f66ffa..173addee76 100644 --- a/tests/test_materialize_base_python_requirements.py +++ b/tests/test_materialize_base_python_requirements.py @@ -184,6 +184,194 @@ def test_materializes_archive_sources_separately_from_pip_locks( assert (output / "archives/archive-000.tar.gz").read_bytes() == archive +def test_download_trusted_org_archive_uses_bounded_verified_https_stream( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """A trusted archive is read in bounded chunks and checksum-verified.""" + url = "https://github.com/ContextualWisdomLab/demo/archive/v1.tar.gz" + payload = b"verified archive" + + class FakeOpener: + def open(self, request_url: str, *, timeout: int) -> FakeHttpResponse: + assert request_url == url + assert timeout == materializer.TRUSTED_ORG_ARCHIVE_TIMEOUT_SECONDS + return FakeHttpResponse(url, payload, maximum_chunk_size=3) + + monkeypatch.setattr( + materializer.urllib.request, + "build_opener", + lambda *_handlers: FakeOpener(), + ) + + assert materializer._download_trusted_org_archive( + url, [hashlib.sha256(payload).hexdigest()] + ) == payload + + +@pytest.mark.parametrize( + ("source_url", "target_url"), + [ + ( + "https://not-github.invalid/demo.tar.gz", + "https://codeload.github.com/ContextualWisdomLab/demo/legacy.tar.gz", + ), + ( + "https://github.com/ContextualWisdomLab/demo/archive/v1.tar.gz", + "https://not-github.invalid/demo.tar.gz", + ), + ], +) +def test_trusted_org_archive_redirects_reject_non_github_origins( + source_url: str, + target_url: str, +) -> None: + """Archive redirects must remain on the two explicit GitHub origins.""" + with pytest.raises(RuntimeError, match="redirected outside GitHub"): + materializer._TrustedOrgArchiveRedirects().redirect_request( + materializer.urllib.request.Request(source_url), + object(), + 302, + "Found", + {}, + target_url, + ) + + +def test_trusted_org_archive_redirects_follow_codeload() -> None: + """A valid archive redirect is passed through unchanged as a GET request.""" + followed = materializer._TrustedOrgArchiveRedirects().redirect_request( + materializer.urllib.request.Request( + "https://github.com/ContextualWisdomLab/demo/archive/v1.tar.gz" + ), + object(), + 302, + "Found", + {}, + "https://codeload.github.com/ContextualWisdomLab/demo/legacy.tar.gz", + ) + + assert followed.full_url == ( + "https://codeload.github.com/ContextualWisdomLab/demo/legacy.tar.gz" + ) + + +def test_trusted_org_archive_redirects_fail_when_parent_rejects_redirect( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """A redirect rejected by urllib remains a hard failure.""" + monkeypatch.setattr( + materializer.urllib.request.HTTPRedirectHandler, + "redirect_request", + lambda *_args: None, + ) + + with pytest.raises(RuntimeError, match="redirect was rejected"): + materializer._TrustedOrgArchiveRedirects().redirect_request( + materializer.urllib.request.Request( + "https://github.com/ContextualWisdomLab/demo/archive/v1.tar.gz" + ), + object(), + 302, + "Found", + {}, + "https://codeload.github.com/ContextualWisdomLab/demo/legacy.tar.gz", + ) + + +def test_download_trusted_org_archive_rejects_invalid_source_url() -> None: + """The initial archive URL must be an allowlisted GitHub HTTPS URL.""" + with pytest.raises(RuntimeError, match="URL is not GitHub HTTPS"): + materializer._download_trusted_org_archive( + "https://example.invalid/demo.tar.gz", ["a" * 64] + ) + + +def test_download_trusted_org_archive_rejects_untrusted_final_url( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """A response that leaves GitHub is rejected before its bytes are trusted.""" + url = "https://github.com/ContextualWisdomLab/demo/archive/v1.tar.gz" + + class FakeOpener: + def open(self, _request_url: str, *, timeout: int) -> FakeHttpResponse: + del timeout + return FakeHttpResponse("https://example.invalid/demo.tar.gz") + + monkeypatch.setattr( + materializer.urllib.request, + "build_opener", + lambda *_handlers: FakeOpener(), + ) + + with pytest.raises(RuntimeError, match="left GitHub origins"): + materializer._download_trusted_org_archive(url, ["a" * 64]) + + +def test_download_trusted_org_archive_wraps_network_errors( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Network failures do not escape as ambiguous low-level exceptions.""" + url = "https://github.com/ContextualWisdomLab/demo/archive/v1.tar.gz" + + class FakeOpener: + def open(self, _request_url: str, *, timeout: int) -> FakeHttpResponse: + del timeout + raise OSError("offline") + + monkeypatch.setattr( + materializer.urllib.request, + "build_opener", + lambda *_handlers: FakeOpener(), + ) + + with pytest.raises(RuntimeError, match="download failed: OSError"): + materializer._download_trusted_org_archive(url, ["a" * 64]) + + +def test_download_trusted_org_archive_rejects_oversized_payload( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Archive downloads remain bounded before checksum processing.""" + url = "https://github.com/ContextualWisdomLab/demo/archive/v1.tar.gz" + monkeypatch.setattr(materializer, "TRUSTED_ORG_ARCHIVE_MAX_BYTES", 3) + + class FakeOpener: + def open(self, _request_url: str, *, timeout: int) -> FakeHttpResponse: + del timeout + return FakeHttpResponse(url, b"1234") + + monkeypatch.setattr( + materializer.urllib.request, + "build_opener", + lambda *_handlers: FakeOpener(), + ) + + with pytest.raises(RuntimeError, match="bounded size"): + materializer._download_trusted_org_archive(url, ["a" * 64]) + + +def test_download_trusted_org_archive_rejects_checksum_mismatch( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """A trusted origin is insufficient without the exact exported hash.""" + url = "https://github.com/ContextualWisdomLab/demo/archive/v1.tar.gz" + payload = b"archive" + + class FakeOpener: + def open(self, _request_url: str, *, timeout: int) -> FakeHttpResponse: + del timeout + return FakeHttpResponse(url, payload) + + monkeypatch.setattr( + materializer.urllib.request, + "build_opener", + lambda *_handlers: FakeOpener(), + ) + + with pytest.raises(RuntimeError, match="checksum verification failed"): + materializer._download_trusted_org_archive(url, ["a" * 64]) + + def test_base_inputs_preserve_a_vcs_only_export( tmp_path: Path, monkeypatch: pytest.MonkeyPatch, @@ -241,6 +429,33 @@ def export(_repo: Path, _sha: str, lock_path: str): materializer._base_python_inputs(tmp_path, "a" * 40) +def test_base_inputs_reject_conflicting_archive_hashes_across_locks( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Separate uv projects cannot select conflicting hashes for one archive.""" + tree = b"".join( + b"100644 blob " + bytes(character, "ascii") * 40 + b"\t" + path + b"\0" + for character, path in ( + ("a", b"first/pyproject.toml"), + ("b", b"first/uv.lock"), + ("c", b"second/pyproject.toml"), + ("d", b"second/uv.lock"), + ) + ) + url = "https://github.com/ContextualWisdomLab/demo/archive/v1.tar.gz" + monkeypatch.setattr(materializer, "_git", lambda *_args: tree) + + def export(_repo: Path, _sha: str, lock_path: str): + digest = "a" * 64 if lock_path.startswith("first/") else "b" * 64 + return b"", [], [{"package": "demo", "url": url, "hashes": [digest]}] + + monkeypatch.setattr(materializer, "_export_uv_lock", export) + + with pytest.raises(RuntimeError, match="conflicting hashes"): + materializer._base_python_inputs(tmp_path, "a" * 40) + + def test_materializes_hash_pinned_locks_named_beyond_the_legacy_whitelist( tmp_path: Path, ) -> None: diff --git a/tests/test_uv_export_isolation_contract.py b/tests/test_uv_export_isolation_contract.py index 7eec2675e0..50ebdb38e9 100644 --- a/tests/test_uv_export_isolation_contract.py +++ b/tests/test_uv_export_isolation_contract.py @@ -119,6 +119,29 @@ def test_uv_export_accepts_hash_pinned_organization_archive_as_registry_lock() - ] +def test_uv_export_rejects_organization_archive_without_complete_sha256_hash() -> None: + """Organization archives must carry a complete SHA-256 hash before partitioning.""" + content = ( + b"demo @ https://github.com/ContextualWisdomLab/demo/archive/v1.tar.gz " + b"--hash=sha256:abcd\n" + ) + + with pytest.raises(ValueError, match="complete SHA-256 hashes"): + materializer._partition_uv_export(content) + + +def test_uv_export_rejects_conflicting_hashes_for_one_archive_url() -> None: + """One archive URL cannot be admitted with two different digests.""" + url = "https://github.com/ContextualWisdomLab/demo/archive/v1.tar.gz" + content = ( + f"demo @ {url} --hash=sha256:{'a' * 64}\n" + f"demo @ {url} --hash=sha256:{'b' * 64}\n" + ).encode() + + with pytest.raises(ValueError, match="conflicting hashes"): + materializer._partition_uv_export(content) + + def test_uv_export_partitions_hashes_and_exact_organization_vcs_sources() -> None: """An immutable organization source pin is separated from pip hash locks.""" content = ( From 3c000884da538825f59f0c6dfc33289d33f2d68a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 14:24:37 +0900 Subject: [PATCH 69/76] test: cover archive installer validation --- tests/test_install_base_python_locks.py | 238 ++++++++++++++++++++++++ 1 file changed, 238 insertions(+) diff --git a/tests/test_install_base_python_locks.py b/tests/test_install_base_python_locks.py index ac7731c7d2..6aa4e3b939 100644 --- a/tests/test_install_base_python_locks.py +++ b/tests/test_install_base_python_locks.py @@ -135,6 +135,244 @@ def fake_runner(command: list[str], **kwargs): ] +def test_archive_only_install_failure_is_fatal(tmp_path) -> None: + """A verified archive that fails to build must fail the isolated phase.""" + archive = tmp_path / "archives" / "archive-000.tar.gz" + archive.parent.mkdir() + archive.write_bytes(b"verified archive") + (tmp_path / "archive-manifest.json").write_text( + json.dumps( + [ + { + "package": "demo", + "file": "archives/archive-000.tar.gz", + "hashes": [hashlib.sha256(archive.read_bytes()).hexdigest()], + } + ] + ), + encoding="utf-8", + ) + + def fake_runner(command: list[str], **kwargs): + return subprocess.CompletedProcess(command, 23, stdout="") + + stderr = io.StringIO() + assert installer.install_materialized_locks( + tmp_path, + archives_only=True, + runner=fake_runner, + stderr=stderr, + ) == 23 + assert "failed to install: demo" in stderr.getvalue() + + +def _write_valid_archive_manifest(root: pathlib.Path) -> pathlib.Path: + """Create one valid archive manifest and return its materialized file.""" + archive = root / "archives" / "archive-000.tar.gz" + archive.parent.mkdir() + archive.write_bytes(b"verified archive") + (root / "archive-manifest.json").write_text( + json.dumps( + [ + { + "package": "demo", + "file": "archives/archive-000.tar.gz", + "hashes": [hashlib.sha256(archive.read_bytes()).hexdigest()], + } + ] + ), + encoding="utf-8", + ) + return archive + + +def test_installs_verified_archives_after_validated_locks(tmp_path) -> None: + """Normal lock installation includes verified archives by default.""" + write_candidate( + tmp_path, + generated_file="requirements-000.txt", + source="requirements-hashes.txt", + ) + archive = _write_valid_archive_manifest(tmp_path) + commands: list[list[str]] = [] + + def fake_runner(command: list[str], **kwargs): + commands.append(command) + return subprocess.CompletedProcess(command, 0, stdout="") + + assert installer.install_materialized_locks(tmp_path, runner=fake_runner) == 0 + assert commands[-1][-1] == str(archive) + + +def test_can_skip_verified_archives_after_validated_locks(tmp_path) -> None: + """The normal phase can explicitly defer archive installation.""" + write_candidate( + tmp_path, + generated_file="requirements-000.txt", + source="requirements-hashes.txt", + ) + archive = _write_valid_archive_manifest(tmp_path) + commands: list[list[str]] = [] + + def fake_runner(command: list[str], **kwargs): + commands.append(command) + return subprocess.CompletedProcess(command, 0, stdout="") + + assert installer.install_materialized_locks( + tmp_path, + install_archives=False, + runner=fake_runner, + ) == 0 + assert commands[-1][-1] != str(archive) + + +def test_archive_install_failure_after_validated_locks_is_fatal(tmp_path) -> None: + """A normal install cannot hide a verified archive build failure.""" + write_candidate( + tmp_path, + generated_file="requirements-000.txt", + source="requirements-hashes.txt", + ) + archive = _write_valid_archive_manifest(tmp_path) + call_count = 0 + + def fake_runner(command: list[str], **kwargs): + nonlocal call_count + call_count += 1 + return subprocess.CompletedProcess( + command, + 17 if call_count == 3 else 0, + stdout="", + ) + + stderr = io.StringIO() + assert installer.install_materialized_locks( + tmp_path, + runner=fake_runner, + stderr=stderr, + ) == 17 + assert str(archive) in stderr.getvalue() or "demo" in stderr.getvalue() + + +def test_archive_manifest_directory_is_rejected(tmp_path) -> None: + """The archive manifest itself must be a regular file.""" + (tmp_path / "archive-manifest.json").mkdir() + + with pytest.raises(ValueError, match="regular non-symlink file"): + installer._archive_entries(tmp_path) + + +@pytest.mark.parametrize("manifest_text", ["{not-json", "{}", "[1]"]) +def test_archive_manifest_json_shape_is_validated(tmp_path, manifest_text: str) -> None: + """Archive manifest syntax and top-level shape are fail-closed.""" + (tmp_path / "archive-manifest.json").write_text(manifest_text, encoding="utf-8") + + error = "invalid" if manifest_text == "{not-json" else ( + "JSON array" if manifest_text == "{}" else "entries must be objects" + ) + with pytest.raises(ValueError, match=error): + installer._archive_entries(tmp_path) + + +@pytest.mark.parametrize( + "entry", + [ + { + "package": "", + "file": "archives/archive-000.tar.gz", + "hashes": ["a" * 64], + }, + { + "package": "demo", + "file": "archive-000.tar.gz", + "hashes": ["a" * 64], + }, + { + "package": "demo", + "file": "archives/archive-000.tar.gz", + "hashes": ["not-a-sha256"], + }, + ], +) +def test_archive_manifest_entry_fields_are_validated(tmp_path, entry) -> None: + """Package, generated path, and digest fields must be exact types and shapes.""" + (tmp_path / "archive-manifest.json").write_text( + json.dumps([entry]), + encoding="utf-8", + ) + + with pytest.raises(ValueError, match="invalid entry"): + installer._archive_entries(tmp_path) + + +def test_archive_manifest_rejects_duplicate_files(tmp_path) -> None: + """One generated archive path cannot represent two source entries.""" + archive = _write_valid_archive_manifest(tmp_path) + entry = { + "package": "demo", + "file": "archives/archive-000.tar.gz", + "hashes": [hashlib.sha256(archive.read_bytes()).hexdigest()], + } + (tmp_path / "archive-manifest.json").write_text( + json.dumps([entry, entry]), + encoding="utf-8", + ) + + with pytest.raises(ValueError, match="duplicate files"): + installer._archive_entries(tmp_path) + + +def test_archive_manifest_rejects_missing_archive_file(tmp_path) -> None: + """Every manifest entry must resolve to a regular materialized file.""" + (tmp_path / "archive-manifest.json").write_text( + json.dumps( + [ + { + "package": "demo", + "file": "archives/archive-000.tar.gz", + "hashes": ["a" * 64], + } + ] + ), + encoding="utf-8", + ) + + with pytest.raises(ValueError, match="must be a regular file"): + installer._archive_entries(tmp_path) + + +def test_archive_manifest_rejects_symlink_archive_file(tmp_path) -> None: + """Archive entries cannot escape the materialized root through a symlink.""" + archive = _write_valid_archive_manifest(tmp_path) + target = tmp_path / "real-archive.tar.gz" + target.write_bytes(archive.read_bytes()) + archive.unlink() + archive.symlink_to(target) + + with pytest.raises(ValueError, match="must be a regular file"): + installer._archive_entries(tmp_path) + + +def test_archive_manifest_rejects_hash_mismatch(tmp_path) -> None: + """The local archive bytes must match the digest exported by the base lock.""" + _write_valid_archive_manifest(tmp_path) + (tmp_path / "archive-manifest.json").write_text( + json.dumps( + [ + { + "package": "demo", + "file": "archives/archive-000.tar.gz", + "hashes": ["a" * 64], + } + ] + ), + encoding="utf-8", + ) + + with pytest.raises(ValueError, match="failed hash verification"): + installer._archive_entries(tmp_path) + + def test_skips_partial_candidate_without_completing_sibling(tmp_path) -> None: """An unrecoverable hash-bearing supplement remains visible and non-fatal.""" write_candidate( From 69799e84223d6169e382cfa1af724d0addaa6743 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 14:33:44 +0900 Subject: [PATCH 70/76] fix: preserve archive markers and backend boundaries --- .../workflows/opencode-review-dispatch.yml | 32 ++++++++++++++++ .../trusted-uv-lock-materialization.md | 27 +++++++++++-- scripts/ci/install_base_python_locks.py | 19 +++++++--- .../materialize_base_python_requirements.py | 24 +++++++++--- tests/test_install_base_python_locks.py | 38 +++++++++++++++++++ tests/test_opencode_agent_contract.py | 5 +++ tests/test_uv_export_isolation_contract.py | 1 + 7 files changed, 131 insertions(+), 15 deletions(-) diff --git a/.github/workflows/opencode-review-dispatch.yml b/.github/workflows/opencode-review-dispatch.yml index dbb134bf53..0fc051bc35 100644 --- a/.github/workflows/opencode-review-dispatch.yml +++ b/.github/workflows/opencode-review-dispatch.yml @@ -808,8 +808,10 @@ jobs: && python3 - <<'PYTHON' import json import pathlib + import re import stat import tarfile + import tomllib import zipfile requirements_root = pathlib.Path("/tmp/base-python-requirements") @@ -842,6 +844,36 @@ jobs: bundle.extractall(destination) else: raise SystemExit(f"unsupported archive suffix: {relative_file}") + pyprojects = [ + path for path in destination.rglob("pyproject.toml") if path.is_file() + ] + if len(pyprojects) != 1: + raise SystemExit( + f"archive must expose exactly one pyproject.toml: {relative_file}" + ) + project = tomllib.loads(pyprojects[0].read_text(encoding="utf-8")) + build_system = project.get("build-system") + build_requires = ( + build_system.get("requires") + if isinstance(build_system, dict) + else None + ) + if ( + not isinstance(build_system, dict) + or build_system.get("build-backend") != "maturin" + or not isinstance(build_requires, list) + or not any( + isinstance(requirement, str) + and re.fullmatch( + r"maturin(?:\[.*\])?(?:\s*[<>=!~].*)?", + requirement, + ) + for requirement in build_requires + ) + ): + raise SystemExit( + f"archive build backend is not the installed maturin contract: {relative_file}" + ) PYTHON RUN set -eu; \ find /opt/base-python-archive-sources -name Cargo.toml -print0 \ diff --git a/docs/doctoring/trusted-uv-lock-materialization.md b/docs/doctoring/trusted-uv-lock-materialization.md index 2d83e8bda8..363a93abde 100644 --- a/docs/doctoring/trusted-uv-lock-materialization.md +++ b/docs/doctoring/trusted-uv-lock-materialization.md @@ -41,10 +41,19 @@ The implementation therefore: 10. keeps project metadata discovery enabled because the reconstructed `pyproject.toml` is an authoritative input; `--no-config` is deliberately not used because uv documents that it disables `pyproject.toml` discovery; -11. rejects every nonempty export unless every logical line is an exact normalized - package `==` pin followed only by complete SHA-256 hashes; and -12. exposes only generated requirements files and a source manifest to the later - networkless coverage environment. +11. accepts a direct archive only when it is an exact HTTPS + `github.com/ContextualWisdomLab//archive/...tar.gz|zip` reference with + a complete SHA-256 hash; its environment marker is preserved in the archive + manifest and later pip direct-reference command so false-marker archives are + skipped by pip rather than built unconditionally; +12. rejects every nonempty export unless every logical line is an exact normalized + package `==` pin, an allowlisted organization archive, or an exact immutable + organization VCS source; and +13. exposes generated requirements files, VCS metadata, and locally verified + archive inputs to the later networkless coverage environment. The coverage + image currently accepts only archives declaring the installed `maturin` build + backend and its `maturin` build requirement; unsupported PEP 517 backends + fail before extraction completes. ## Standards and current-tool rationale @@ -70,6 +79,16 @@ sets `UV_NO_ENV_FILE=1` and `UV_PYTHON_DOWNLOADS=never`, and passes only a fixed `PATH`. This preserves the exact reconstructed project metadata while excluding user-level and runner-level configuration state. +Organization archive entries are not installed from a networked pip build. The +materializer downloads each archive from the fixed GitHub HTTPS origins, +verifies its exported SHA-256 digest, and writes it separately from regular +`--require-hashes` locks. The coverage Dockerfile rejects unsafe tar/zip members, +requires exactly one `pyproject.toml` with the installed `maturin` backend and +requirement, fetches any Rust dependency manifests while networked, and invokes +archive build hooks only in the subsequent `RUN --network=none` layer. A Python +environment marker remains part of the direct-reference requirement, so pip +evaluates it for the coverage interpreter before any archive build starts. + Generic requirements discovery continues to accept a global `--require-hashes` directive because pip performs a later closure preflight. Trusted `uv export` output uses a stricter rule: every logical line must begin diff --git a/scripts/ci/install_base_python_locks.py b/scripts/ci/install_base_python_locks.py index ee1e00f937..cb4bf12ac9 100644 --- a/scripts/ci/install_base_python_locks.py +++ b/scripts/ci/install_base_python_locks.py @@ -67,6 +67,7 @@ class ArchiveCandidate: package: str file: pathlib.Path hashes: tuple[str, ...] + marker: str | None = None def _archive_entries( @@ -93,6 +94,7 @@ def _archive_entries( package = entry.get("package") relative_file = entry.get("file") hashes = entry.get("hashes") + marker = entry.get("marker") if ( not isinstance(package, str) or not package @@ -101,6 +103,10 @@ def _archive_entries( or not isinstance(hashes, list) or not hashes or any(not isinstance(value, str) or not SHA256_RE.fullmatch(value) for value in hashes) + or ( + marker is not None + and (not isinstance(marker, str) or not marker.strip()) + ) ): raise ValueError("base Python archive manifest contains an invalid entry") if relative_file in seen_files: @@ -115,7 +121,7 @@ def _archive_entries( digest.update(chunk) if digest.hexdigest() not in hashes: raise ValueError(f"materialized base Python archive {relative_file} failed hash verification") - entries.append(ArchiveCandidate(package, archive, tuple(hashes))) + entries.append(ArchiveCandidate(package, archive, tuple(hashes), marker)) return entries @@ -191,8 +197,11 @@ def _pip_command(requirements: Sequence[pathlib.Path], *, preflight: bool) -> li return command -def _archive_pip_command(archive: pathlib.Path) -> list[str]: +def _archive_pip_command(archive: ArchiveCandidate) -> list[str]: """Build a no-network source-install command for one verified archive.""" + requirement = str(archive.file) + if archive.marker is not None: + requirement = f"{archive.package} @ {archive.file.as_uri()} ; {archive.marker}" return [ sys.executable, "-m", @@ -202,7 +211,7 @@ def _archive_pip_command(archive: pathlib.Path) -> list[str]: "--disable-pip-version-check", "--no-deps", "--no-build-isolation", - str(archive), + requirement, ] @@ -281,7 +290,7 @@ def install_materialized_locks( file=stdout, flush=True, ) - installation = runner(_archive_pip_command(archive.file), check=False) + installation = runner(_archive_pip_command(archive), check=False) if installation.returncode != 0: print( f"::error::Verified trusted base Python archive failed to install: {archive.package}.", @@ -419,7 +428,7 @@ def install_materialized_locks( file=stdout, flush=True, ) - installation = runner(_archive_pip_command(archive.file), check=False) + installation = runner(_archive_pip_command(archive), check=False) if installation.returncode != 0: print( f"::error::Verified trusted base Python archive failed to install: {archive.package}.", diff --git a/scripts/ci/materialize_base_python_requirements.py b/scripts/ci/materialize_base_python_requirements.py index b8a66a4f9b..ad7f005037 100755 --- a/scripts/ci/materialize_base_python_requirements.py +++ b/scripts/ci/materialize_base_python_requirements.py @@ -52,7 +52,7 @@ r"(?:refs/(?:tags|heads)/)?" r"[A-Za-z0-9][A-Za-z0-9._-]*(?:/[A-Za-z0-9._-]+)*" r"\.(?:tar\.gz|zip)" - r")(?:\s*;\s*\S(?:.*\S)?)?" + r")(?:\s*;\s*(?P\S(?:.*\S)?))?" ) UV_EXPORT_TIMEOUT_SECONDS = 120 TRUSTED_UV_VERSION = "0.12.1" @@ -405,11 +405,15 @@ def _archive_from_uv_line(line: str) -> dict[str, object] | None: hashes = [field.removeprefix("--hash=sha256:").lower() for field in hash_fields] if not hashes or any(not re.fullmatch(r"[0-9a-fA-F]{64}", value) for value in hashes): raise ValueError("organization archive must carry complete SHA-256 hashes") - return { + descriptor: dict[str, object] = { "package": match.group("package"), "url": match.group("url"), "hashes": hashes, } + marker = match.group("marker") + if marker is not None: + descriptor["marker"] = marker + return descriptor def _partition_uv_export( @@ -424,8 +428,13 @@ def _partition_uv_export( if archive is not None: url = str(archive["url"]) previous = archives_by_url.get(url) - if previous is not None and previous["hashes"] != archive["hashes"]: - raise ValueError("uv export pins one archive URL to conflicting hashes") + if previous is not None and ( + previous["hashes"] != archive["hashes"] + or previous.get("marker") != archive.get("marker") + ): + raise ValueError( + "uv export pins one archive URL to conflicting hashes or markers" + ) archives_by_url[url] = archive continue if _is_fully_hash_pinned_requirement(line): @@ -793,10 +802,13 @@ def _base_python_inputs( previous_archive = archives_by_url.get(url) if ( previous_archive is not None - and previous_archive["hashes"] != archive["hashes"] + and ( + previous_archive["hashes"] != archive["hashes"] + or previous_archive.get("marker") != archive.get("marker") + ) ): raise RuntimeError( - "base uv locks pin one archive URL to conflicting hashes" + "base uv locks pin one archive URL to conflicting hashes or markers" ) archives_by_url[url] = { **archive, diff --git a/tests/test_install_base_python_locks.py b/tests/test_install_base_python_locks.py index 6aa4e3b939..1175e97a83 100644 --- a/tests/test_install_base_python_locks.py +++ b/tests/test_install_base_python_locks.py @@ -135,6 +135,44 @@ def fake_runner(command: list[str], **kwargs): ] +@pytest.mark.parametrize( + "marker", + ["python_version >= '3.12'", "python_version < '3.10'"], +) +def test_archive_marker_is_preserved_for_pip_to_evaluate(tmp_path, marker: str) -> None: + """Archive markers remain attached to the direct local requirement.""" + archive = tmp_path / "archives" / "archive-000.tar.gz" + archive.parent.mkdir() + archive.write_bytes(b"verified archive") + (tmp_path / "archive-manifest.json").write_text( + json.dumps( + [ + { + "package": "demo", + "file": "archives/archive-000.tar.gz", + "hashes": [hashlib.sha256(archive.read_bytes()).hexdigest()], + "marker": marker, + } + ] + ), + encoding="utf-8", + ) + commands: list[list[str]] = [] + + def fake_runner(command: list[str], **kwargs): + commands.append(command) + return subprocess.CompletedProcess(command, 0, stdout="") + + assert installer.install_materialized_locks( + tmp_path, + archives_only=True, + runner=fake_runner, + ) == 0 + assert commands[0][-1] == ( + f"demo @ {archive.as_uri()} ; {marker}" + ) + + def test_archive_only_install_failure_is_fatal(tmp_path) -> None: """A verified archive that fails to build must fail the isolated phase.""" archive = tmp_path / "archives" / "archive-000.tar.gz" diff --git a/tests/test_opencode_agent_contract.py b/tests/test_opencode_agent_contract.py index 12930b02e3..b504df642b 100644 --- a/tests/test_opencode_agent_contract.py +++ b/tests/test_opencode_agent_contract.py @@ -702,6 +702,11 @@ def test_opencode_target_coverage_materializes_only_after_authorized_dispatch(): assert "--no-archives" in measure_step assert "--archives-only" in measure_step assert "RUN --network=none python3 -I /usr/local/libexec/install-base-python-locks.py" in measure_step + assert "import tomllib" in measure_step + assert 'build_system.get("build-backend") != "maturin"' in measure_step + assert 'r"maturin(?:\\[.*\\])?(?:\\s*[<>=!~].*)?"' in measure_step + assert "archive must expose exactly one pyproject.toml" in measure_step + assert "archive build backend is not the installed maturin contract" in measure_step assert '"https://github.com/ContextualWisdomLab/${repository}.git"' in measure_step assert '--quiet --no-tags --depth=1 origin "$commit"' in measure_step assert 'rev-parse FETCH_HEAD)" = "$commit"' in measure_step diff --git a/tests/test_uv_export_isolation_contract.py b/tests/test_uv_export_isolation_contract.py index 50ebdb38e9..c913d742b3 100644 --- a/tests/test_uv_export_isolation_contract.py +++ b/tests/test_uv_export_isolation_contract.py @@ -115,6 +115,7 @@ def test_uv_export_accepts_hash_pinned_organization_archive_as_registry_lock() - "package": "fast-mlsirm", "url": "https://github.com/ContextualWisdomLab/fast-mlsirm/archive/refs/tags/v0.9.1.tar.gz", "hashes": ["a" * 64], + "marker": "python_full_version >= '3.12'", } ] From 5d05fe38572876993ec28f283644ddf056e999de Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 14:48:58 +0900 Subject: [PATCH 71/76] fix: preserve conditional archive alternatives --- .../workflows/opencode-review-dispatch.yml | 5 ++ .../trusted-uv-lock-materialization.md | 7 +-- .../materialize_base_python_requirements.py | 47 ++++++++++++------- ...st_materialize_base_python_requirements.py | 44 +++++++++++++++++ tests/test_opencode_agent_contract.py | 9 ++++ ...t_pr_review_autofix_nvidia_nim_contract.py | 2 +- tests/test_uv_export_isolation_contract.py | 16 +++++++ 7 files changed, 109 insertions(+), 21 deletions(-) diff --git a/.github/workflows/opencode-review-dispatch.yml b/.github/workflows/opencode-review-dispatch.yml index 0fc051bc35..a54babc2dc 100644 --- a/.github/workflows/opencode-review-dispatch.yml +++ b/.github/workflows/opencode-review-dispatch.yml @@ -813,13 +813,18 @@ jobs: import tarfile import tomllib import zipfile + from packaging.markers import Marker, default_environment requirements_root = pathlib.Path("/tmp/base-python-requirements") source_root = pathlib.Path("/opt/base-python-archive-sources").resolve() manifest = json.loads( (requirements_root / "archive-manifest.json").read_text(encoding="utf-8") ) + coverage_environment = default_environment() for index, entry in enumerate(manifest): + marker = entry.get("marker") + if marker is not None and not Marker(marker).evaluate(coverage_environment): + continue relative_file = entry["file"] archive = (requirements_root / relative_file).resolve() destination = (source_root / f"archive-{index:03d}").resolve() diff --git a/docs/doctoring/trusted-uv-lock-materialization.md b/docs/doctoring/trusted-uv-lock-materialization.md index 363a93abde..73070fecf3 100644 --- a/docs/doctoring/trusted-uv-lock-materialization.md +++ b/docs/doctoring/trusted-uv-lock-materialization.md @@ -85,9 +85,10 @@ verifies its exported SHA-256 digest, and writes it separately from regular `--require-hashes` locks. The coverage Dockerfile rejects unsafe tar/zip members, requires exactly one `pyproject.toml` with the installed `maturin` backend and requirement, fetches any Rust dependency manifests while networked, and invokes -archive build hooks only in the subsequent `RUN --network=none` layer. A Python -environment marker remains part of the direct-reference requirement, so pip -evaluates it for the coverage interpreter before any archive build starts. +archive build hooks only in the subsequent `RUN --network=none` layer. The image +preparation evaluates each archive marker for the coverage interpreter before +extraction or Cargo fetching, while the marker remains part of the later +direct-reference requirement so pip evaluates it again before any build hook. Generic requirements discovery continues to accept a global `--require-hashes` directive because pip performs a later closure preflight. diff --git a/scripts/ci/materialize_base_python_requirements.py b/scripts/ci/materialize_base_python_requirements.py index ad7f005037..6eaed8ebae 100755 --- a/scripts/ci/materialize_base_python_requirements.py +++ b/scripts/ci/materialize_base_python_requirements.py @@ -416,26 +416,30 @@ def _archive_from_uv_line(line: str) -> dict[str, object] | None: return descriptor +def _archive_identity(archive: dict[str, Any]) -> tuple[str, str, str | None]: + """Return the dependency identity used to deduplicate archive entries.""" + return str(archive["package"]), str(archive["url"]), archive.get("marker") + + def _partition_uv_export( content: bytes, ) -> tuple[bytes, list[dict[str, str]], list[dict[str, object]]]: """Separate registry pins, VCS sources, and verified archive sources.""" registry_requirements: list[str] = [] vcs_by_repository: dict[str, dict[str, str]] = {} - archives_by_url: dict[str, dict[str, object]] = {} + archives_by_identity: dict[tuple[str, str, str | None], dict[str, object]] = {} for line in _requirement_lines(content): archive = _archive_from_uv_line(line) if archive is not None: - url = str(archive["url"]) - previous = archives_by_url.get(url) + identity = _archive_identity(archive) + previous = archives_by_identity.get(identity) if previous is not None and ( previous["hashes"] != archive["hashes"] - or previous.get("marker") != archive.get("marker") ): raise ValueError( - "uv export pins one archive URL to conflicting hashes or markers" + "uv export pins one archive requirement to conflicting hashes" ) - archives_by_url[url] = archive + archives_by_identity[identity] = archive continue if _is_fully_hash_pinned_requirement(line): registry_requirements.append(line) @@ -468,7 +472,13 @@ def _partition_uv_export( vcs_by_repository.values(), key=lambda dependency: dependency["repository"].casefold(), ), - sorted(archives_by_url.values(), key=lambda dependency: str(dependency["url"])), + sorted( + archives_by_identity.values(), + key=lambda dependency: ( + str(dependency["url"]), + str(dependency.get("marker", "")), + ), + ), ) @@ -770,7 +780,7 @@ def _base_python_inputs( regular_paths = {path for path, _candidate in regular_blobs} locks: list[tuple[str, bytes]] = [] vcs_by_repository: dict[str, dict[str, str]] = {} - archives_by_url: dict[str, dict[str, object]] = {} + archives_by_identity: dict[tuple[str, str, str | None], dict[str, object]] = {} for path, candidate in regular_blobs: if _is_candidate_lock_path(candidate): content = _git(repo_root, "show", f"{base_sha}:{path}") @@ -798,19 +808,16 @@ def _base_python_inputs( ) vcs_by_repository[repository_key] = dependency for archive in archive_dependencies: - url = str(archive["url"]) - previous_archive = archives_by_url.get(url) + identity = _archive_identity(archive) + previous_archive = archives_by_identity.get(identity) if ( previous_archive is not None - and ( - previous_archive["hashes"] != archive["hashes"] - or previous_archive.get("marker") != archive.get("marker") - ) + and previous_archive["hashes"] != archive["hashes"] ): raise RuntimeError( - "base uv locks pin one archive URL to conflicting hashes or markers" + "base uv locks pin one archive requirement to conflicting hashes" ) - archives_by_url[url] = { + archives_by_identity[identity] = { **archive, "source": path, } @@ -820,7 +827,13 @@ def _base_python_inputs( vcs_by_repository.values(), key=lambda dependency: dependency["repository"].casefold(), ), - sorted(archives_by_url.values(), key=lambda dependency: str(dependency["url"])), + sorted( + archives_by_identity.values(), + key=lambda dependency: ( + str(dependency["url"]), + str(dependency.get("marker", "")), + ), + ), ) diff --git a/tests/test_materialize_base_python_requirements.py b/tests/test_materialize_base_python_requirements.py index 173addee76..295205a417 100644 --- a/tests/test_materialize_base_python_requirements.py +++ b/tests/test_materialize_base_python_requirements.py @@ -456,6 +456,50 @@ def export(_repo: Path, _sha: str, lock_path: str): materializer._base_python_inputs(tmp_path, "a" * 40) +def test_base_inputs_keeps_same_archive_url_for_distinct_markers( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Separate uv projects may retain conditional alternatives for one URL.""" + tree = b"".join( + b"100644 blob " + bytes(character, "ascii") * 40 + b"\t" + path + b"\0" + for character, path in ( + ("a", b"first/pyproject.toml"), + ("b", b"first/uv.lock"), + ("c", b"second/pyproject.toml"), + ("d", b"second/uv.lock"), + ) + ) + url = "https://github.com/ContextualWisdomLab/demo/archive/v1.tar.gz" + monkeypatch.setattr(materializer, "_git", lambda *_args: tree) + + def export(_repo: Path, _sha: str, lock_path: str): + marker = ( + "python_version < '3.10'" + if lock_path.startswith("first/") + else "python_version >= '3.10'" + ) + return b"", [], [ + { + "package": "demo", + "url": url, + "hashes": ["a" * 64], + "marker": marker, + } + ] + + monkeypatch.setattr(materializer, "_export_uv_lock", export) + + _locks, _vcs_sources, archive_sources = materializer._base_python_inputs( + tmp_path, "a" * 40 + ) + + assert {archive["marker"] for archive in archive_sources} == { + "python_version < '3.10'", + "python_version >= '3.10'", + } + + def test_materializes_hash_pinned_locks_named_beyond_the_legacy_whitelist( tmp_path: Path, ) -> None: diff --git a/tests/test_opencode_agent_contract.py b/tests/test_opencode_agent_contract.py index b504df642b..a928ebb003 100644 --- a/tests/test_opencode_agent_contract.py +++ b/tests/test_opencode_agent_contract.py @@ -702,6 +702,15 @@ def test_opencode_target_coverage_materializes_only_after_authorized_dispatch(): assert "--no-archives" in measure_step assert "--archives-only" in measure_step assert "RUN --network=none python3 -I /usr/local/libexec/install-base-python-locks.py" in measure_step + assert "from packaging.markers import Marker, default_environment" in measure_step + assert "coverage_environment = default_environment()" in measure_step + assert "not Marker(marker).evaluate(coverage_environment)" in measure_step + assert measure_step.index("not Marker(marker).evaluate") < measure_step.index( + "archive = (requirements_root / relative_file).resolve()" + ) + assert measure_step.index("not Marker(marker).evaluate") < measure_step.index( + "cargo fetch --locked" + ) assert "import tomllib" in measure_step assert 'build_system.get("build-backend") != "maturin"' in measure_step assert 'r"maturin(?:\\[.*\\])?(?:\\s*[<>=!~].*)?"' in measure_step diff --git a/tests/test_pr_review_autofix_nvidia_nim_contract.py b/tests/test_pr_review_autofix_nvidia_nim_contract.py index 30703ac84a..4181fe131b 100644 --- a/tests/test_pr_review_autofix_nvidia_nim_contract.py +++ b/tests/test_pr_review_autofix_nvidia_nim_contract.py @@ -19,7 +19,7 @@ DOCTORING_RECORD = Path("docs/doctoring/hourly-nvidia-nim-autofix.md") CHANGELOG = Path("CHANGELOG.md") REVIEW_DISPATCH_WORKFLOW = Path(".github/workflows/opencode-review-dispatch.yml") -REVIEW_DISPATCH_BLOB_SHA = "dbb134bf535be289844ec93fa6e2ca5a42137216" +REVIEW_DISPATCH_BLOB_SHA = "a54babc2dcaf0e6c8f2fef3bfdbc5444c7200a1c" def _workflow_text(path: Path) -> str: diff --git a/tests/test_uv_export_isolation_contract.py b/tests/test_uv_export_isolation_contract.py index c913d742b3..d5199eef08 100644 --- a/tests/test_uv_export_isolation_contract.py +++ b/tests/test_uv_export_isolation_contract.py @@ -143,6 +143,22 @@ def test_uv_export_rejects_conflicting_hashes_for_one_archive_url() -> None: materializer._partition_uv_export(content) +def test_uv_export_keeps_same_archive_url_for_distinct_markers() -> None: + """Conditional alternatives sharing a URL remain separate requirements.""" + url = "https://github.com/ContextualWisdomLab/demo/archive/v1.tar.gz" + content = ( + f"demo @ {url} ; python_version < '3.10' --hash=sha256:{'a' * 64}\n" + f"demo @ {url} ; python_version >= '3.10' --hash=sha256:{'a' * 64}\n" + ).encode() + + _registry, _vcs_sources, archive_sources = materializer._partition_uv_export(content) + + assert [archive["marker"] for archive in archive_sources] == [ + "python_version < '3.10'", + "python_version >= '3.10'", + ] + + def test_uv_export_partitions_hashes_and_exact_organization_vcs_sources() -> None: """An immutable organization source pin is separated from pip hash locks.""" content = ( From 78287e2d45877bcd80094b71efd4771d32bc3bef Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 14:56:59 +0900 Subject: [PATCH 72/76] fix: enforce archive digest consistency --- .../materialize_base_python_requirements.py | 27 +++++++-------- ...st_materialize_base_python_requirements.py | 34 +++++++++++++++++++ tests/test_uv_export_isolation_contract.py | 12 +++++++ 3 files changed, 59 insertions(+), 14 deletions(-) diff --git a/scripts/ci/materialize_base_python_requirements.py b/scripts/ci/materialize_base_python_requirements.py index 6eaed8ebae..0c372a4579 100755 --- a/scripts/ci/materialize_base_python_requirements.py +++ b/scripts/ci/materialize_base_python_requirements.py @@ -427,18 +427,17 @@ def _partition_uv_export( """Separate registry pins, VCS sources, and verified archive sources.""" registry_requirements: list[str] = [] vcs_by_repository: dict[str, dict[str, str]] = {} + archive_hashes_by_url: dict[str, object] = {} archives_by_identity: dict[tuple[str, str, str | None], dict[str, object]] = {} for line in _requirement_lines(content): archive = _archive_from_uv_line(line) if archive is not None: + url = str(archive["url"]) + previous_hashes = archive_hashes_by_url.get(url) + if previous_hashes is not None and previous_hashes != archive["hashes"]: + raise ValueError("uv export pins one archive URL to conflicting hashes") + archive_hashes_by_url[url] = archive["hashes"] identity = _archive_identity(archive) - previous = archives_by_identity.get(identity) - if previous is not None and ( - previous["hashes"] != archive["hashes"] - ): - raise ValueError( - "uv export pins one archive requirement to conflicting hashes" - ) archives_by_identity[identity] = archive continue if _is_fully_hash_pinned_requirement(line): @@ -780,6 +779,7 @@ def _base_python_inputs( regular_paths = {path for path, _candidate in regular_blobs} locks: list[tuple[str, bytes]] = [] vcs_by_repository: dict[str, dict[str, str]] = {} + archive_hashes_by_url: dict[str, object] = {} archives_by_identity: dict[tuple[str, str, str | None], dict[str, object]] = {} for path, candidate in regular_blobs: if _is_candidate_lock_path(candidate): @@ -808,15 +808,14 @@ def _base_python_inputs( ) vcs_by_repository[repository_key] = dependency for archive in archive_dependencies: - identity = _archive_identity(archive) - previous_archive = archives_by_identity.get(identity) - if ( - previous_archive is not None - and previous_archive["hashes"] != archive["hashes"] - ): + url = str(archive["url"]) + previous_hashes = archive_hashes_by_url.get(url) + if previous_hashes is not None and previous_hashes != archive["hashes"]: raise RuntimeError( - "base uv locks pin one archive requirement to conflicting hashes" + "base uv locks pin one archive URL to conflicting hashes" ) + archive_hashes_by_url[url] = archive["hashes"] + identity = _archive_identity(archive) archives_by_identity[identity] = { **archive, "source": path, diff --git a/tests/test_materialize_base_python_requirements.py b/tests/test_materialize_base_python_requirements.py index 295205a417..7ec3135334 100644 --- a/tests/test_materialize_base_python_requirements.py +++ b/tests/test_materialize_base_python_requirements.py @@ -500,6 +500,40 @@ def export(_repo: Path, _sha: str, lock_path: str): } +def test_base_inputs_rejects_different_hashes_for_same_url_across_markers( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Conditional alternatives cannot change one immutable archive payload.""" + tree = b"".join( + b"100644 blob " + bytes(character, "ascii") * 40 + b"\t" + path + b"\0" + for character, path in ( + ("a", b"first/pyproject.toml"), + ("b", b"first/uv.lock"), + ("c", b"second/pyproject.toml"), + ("d", b"second/uv.lock"), + ) + ) + url = "https://github.com/ContextualWisdomLab/demo/archive/v1.tar.gz" + monkeypatch.setattr(materializer, "_git", lambda *_args: tree) + + def export(_repo: Path, _sha: str, lock_path: str): + marker = ( + "python_version < '3.10'" + if lock_path.startswith("first/") + else "python_version >= '3.10'" + ) + digest = "a" * 64 if lock_path.startswith("first/") else "b" * 64 + return b"", [], [ + {"package": "demo", "url": url, "hashes": [digest], "marker": marker} + ] + + monkeypatch.setattr(materializer, "_export_uv_lock", export) + + with pytest.raises(RuntimeError, match="conflicting hashes"): + materializer._base_python_inputs(tmp_path, "a" * 40) + + def test_materializes_hash_pinned_locks_named_beyond_the_legacy_whitelist( tmp_path: Path, ) -> None: diff --git a/tests/test_uv_export_isolation_contract.py b/tests/test_uv_export_isolation_contract.py index d5199eef08..95e3afb459 100644 --- a/tests/test_uv_export_isolation_contract.py +++ b/tests/test_uv_export_isolation_contract.py @@ -159,6 +159,18 @@ def test_uv_export_keeps_same_archive_url_for_distinct_markers() -> None: ] +def test_uv_export_rejects_different_hashes_for_same_url_across_markers() -> None: + """Conditional alternatives cannot change the immutable archive payload.""" + url = "https://github.com/ContextualWisdomLab/demo/archive/v1.tar.gz" + content = ( + f"demo @ {url} ; python_version < '3.10' --hash=sha256:{'a' * 64}\n" + f"demo @ {url} ; python_version >= '3.10' --hash=sha256:{'b' * 64}\n" + ).encode() + + with pytest.raises(ValueError, match="conflicting hashes"): + materializer._partition_uv_export(content) + + def test_uv_export_partitions_hashes_and_exact_organization_vcs_sources() -> None: """An immutable organization source pin is separated from pip hash locks.""" content = ( From 94980e82c35bd0e70cf7108c8924f7d6bcb0a0e6 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 30 Aug 2026 17:26:54 +0000 Subject: [PATCH 73/76] fix(ci): skip unreachable archive downloads, scope archive redirects to origin repo materialize_base_python_requirements.py's materialize() downloaded every uv-export-pinned organization archive unconditionally, even one whose own PEP 508 marker already excludes it for the coverage image's pinned Python 3.14 (e.g. python_version == '3.11'). A transient outage on that irrelevant archive's URL failed the whole coverage-evidence job for no reason, since the image would never install it anyway. Add a minimal, stdlib-only (ast-based) evaluator for python_version/python_full_version markers and use it, together with a new --target-python-version flag wired from the workflow's pinned coverage image tag, to skip the download only when the marker can be confidently proven false; anything it cannot evaluate still downloads exactly as before, so this never drops real dependency coverage. Separately, harden _TrustedOrgArchiveRedirects: it verified that a redirect stayed on the github.com/codeload.github.com allowlist but not that it named the same repository, so a same-host redirect from github.com/ContextualWisdomLab/ to codeload.github.com// would have been followed (SHA-256 verification of the downloaded bytes against the pinned hash makes this impractical to exploit today, but the check should not rely on that alone). Add an owner/repo-aware comparison used both in redirect_request and as a defense-in-depth check on the final resolved URL. --- .../workflows/opencode-review-dispatch.yml | 7 +- .../materialize_base_python_requirements.py | 179 +++++++++++- ...st_materialize_base_python_requirements.py | 263 +++++++++++++++++- ...t_pr_review_autofix_nvidia_nim_contract.py | 2 +- 4 files changed, 441 insertions(+), 10 deletions(-) diff --git a/.github/workflows/opencode-review-dispatch.yml b/.github/workflows/opencode-review-dispatch.yml index 1b7002d0f5..c5812ed493 100644 --- a/.github/workflows/opencode-review-dispatch.yml +++ b/.github/workflows/opencode-review-dispatch.yml @@ -664,10 +664,15 @@ jobs: esac done <"$python_change_files" if [ "$python_coverage_required" -eq 1 ]; then + # --target-python-version must track the coverage image's pinned + # "FROM docker.io/library/python:3.14-slim@sha256:..." tag below + # so an archive marker excluding that version skips its download + # instead of failing the whole job on an unrelated URL outage. python3 -I "$GITHUB_WORKSPACE/scripts/ci/materialize_base_python_requirements.py" \ --repo-root "$COVERAGE_SOURCE_WORKDIR" \ --base-sha "$PR_BASE_SHA" \ - --output-dir "$coverage_build_dir/base-python-requirements" + --output-dir "$coverage_build_dir/base-python-requirements" \ + --target-python-version 3.14 else mkdir -p "$coverage_build_dir/base-python-requirements" printf '[]\n' >"$coverage_build_dir/base-python-requirements/manifest.json" diff --git a/scripts/ci/materialize_base_python_requirements.py b/scripts/ci/materialize_base_python_requirements.py index 0c372a4579..94d0af025a 100755 --- a/scripts/ci/materialize_base_python_requirements.py +++ b/scripts/ci/materialize_base_python_requirements.py @@ -4,6 +4,7 @@ from __future__ import annotations import argparse +import ast import atexit import fnmatch import functools @@ -136,6 +137,43 @@ def _is_trusted_org_archive_url(url: str) -> bool: ) +_GITHUB_COM_ARCHIVE_PATH_RE = re.compile(r"^/(?P[^/]+)/(?P[^/]+)/archive/") +_CODELOAD_ARCHIVE_PATH_RE = re.compile( + r"^/(?P[^/]+)/(?P[^/]+)/(?:tar\.gz|zip|legacy\.tar\.gz|legacy\.zip)(?:/|$)" +) + + +def _org_archive_repository(url: str) -> tuple[str, str] | None: + """Return the ``(owner, repository)`` one organization archive URL names. + + ``github.com`` archive links and their ``codeload.github.com`` redirect + target use different path shapes for the exact same repository + (``/{owner}/{repo}/archive/...`` versus + ``/{owner}/{repo}/{tar.gz|zip}[/...]``). Parsing both here lets a redirect + be proven to stay on the exact same repository rather than merely the + same allowlisted host, so ``codeload.github.com/other-org/other-repo`` + cannot be reached through a host-only allowlist. Returns ``None`` when the + URL is not one of the two known trusted archive path shapes. + """ + parsed = urllib.parse.urlparse(url) + if parsed.hostname == "github.com": + match = _GITHUB_COM_ARCHIVE_PATH_RE.match(parsed.path) + elif parsed.hostname == "codeload.github.com": + match = _CODELOAD_ARCHIVE_PATH_RE.match(parsed.path) + else: + return None + if match is None: + return None + return (match.group("owner").casefold(), match.group("repo").casefold()) + + +def _is_same_org_archive_repository(source_url: str, target_url: str) -> bool: + """Return whether two trusted archive URLs name the identical repository.""" + source_repository = _org_archive_repository(source_url) + target_repository = _org_archive_repository(target_url) + return source_repository is not None and source_repository == target_repository + + class _TrustedOrgArchiveRedirects(urllib.request.HTTPRedirectHandler): """Follow GitHub archive redirects only onto GitHub's code-download host.""" @@ -148,8 +186,12 @@ def redirect_request( headers: Any, new_url: str, ) -> urllib.request.Request: - """Reject archive redirects that leave the fixed GitHub origins.""" - if not _is_trusted_org_archive_url(request.full_url) or not _is_trusted_org_archive_url(new_url): + """Reject archive redirects that leave the fixed GitHub origins or repository.""" + if ( + not _is_trusted_org_archive_url(request.full_url) + or not _is_trusted_org_archive_url(new_url) + or not _is_same_org_archive_repository(request.full_url, new_url) + ): raise RuntimeError("trusted organization archive redirected outside GitHub") followed = super().redirect_request( request, @@ -174,7 +216,10 @@ def _download_trusted_org_archive(url: str, hashes: list[str]) -> bytes: ) try: with opener.open(url, timeout=TRUSTED_ORG_ARCHIVE_TIMEOUT_SECONDS) as response: - if not _is_trusted_org_archive_url(response.geturl()): + final_url = response.geturl() + if not _is_trusted_org_archive_url(final_url) or not _is_same_org_archive_repository( + url, final_url + ): raise RuntimeError("trusted organization archive left GitHub origins") payload = bytearray() while len(payload) <= TRUSTED_ORG_ARCHIVE_MAX_BYTES: @@ -895,12 +940,109 @@ def _rewrite_materialized_includes( return "".join(rewritten).encode("utf-8") +_SUPPORTED_MARKER_VARIABLES = frozenset({"python_version", "python_full_version"}) + + +class _UnsupportedMarkerError(Exception): + """Raised when a marker shape is outside the narrow supported subset.""" + + +def _marker_version_tuple(value: str) -> tuple[int, ...]: + """Parse a dotted numeric version literal into a comparable integer tuple.""" + if not re.fullmatch(r"[0-9]+(?:\.[0-9]+)*", value): + raise _UnsupportedMarkerError(f"unsupported marker version literal: {value!r}") + return tuple(int(part) for part in value.split(".")) + + +def _compare_marker_versions(left: str, operator: type, right: str) -> bool: + """Compare two dotted version literals as zero-padded integer tuples.""" + left_tuple = _marker_version_tuple(left) + right_tuple = _marker_version_tuple(right) + length = max(len(left_tuple), len(right_tuple)) + left_padded = left_tuple + (0,) * (length - len(left_tuple)) + right_padded = right_tuple + (0,) * (length - len(right_tuple)) + if operator is ast.Eq: + return left_padded == right_padded + if operator is ast.NotEq: + return left_padded != right_padded + if operator is ast.Lt: + return left_padded < right_padded + if operator is ast.LtE: + return left_padded <= right_padded + if operator is ast.Gt: + return left_padded > right_padded + if operator is ast.GtE: + return left_padded >= right_padded + raise _UnsupportedMarkerError("unsupported marker comparison operator") + + +def _evaluate_marker_node(node: ast.AST, target_python_version: str) -> bool: + """Evaluate one parsed marker expression node against a fixed Python version. + + Only boolean combinations (``and``/``or``/``not``/parentheses) of + ``python_version``/``python_full_version`` comparisons against a literal + dotted version string are understood -- the shape ``uv export`` emits for + Python-version-gated organization archive sources. Any other marker shape + (``sys_platform``, ``extra``, ``in``/``not in``, function calls, chained + comparisons, and so on) raises ``_UnsupportedMarkerError`` so the caller + fails open and still downloads the archive. + """ + if isinstance(node, ast.Expression): + return _evaluate_marker_node(node.body, target_python_version) + if isinstance(node, ast.BoolOp): + values = [_evaluate_marker_node(value, target_python_version) for value in node.values] + # ast.BoolOp.op is exhaustively either And or Or in Python's grammar; + # there is no third case to fail open on. + return all(values) if isinstance(node.op, ast.And) else any(values) + if isinstance(node, ast.UnaryOp) and isinstance(node.op, ast.Not): + return not _evaluate_marker_node(node.operand, target_python_version) + if isinstance(node, ast.Compare) and len(node.ops) == 1 and len(node.comparators) == 1: + left, right = node.left, node.comparators[0] + variable_node, literal_node = (left, right) if isinstance(left, ast.Name) else (right, left) + if not isinstance(variable_node, ast.Name) or variable_node.id not in _SUPPORTED_MARKER_VARIABLES: + raise _UnsupportedMarkerError("unsupported marker variable") + if not isinstance(literal_node, ast.Constant) or not isinstance(literal_node.value, str): + raise _UnsupportedMarkerError("unsupported marker literal") + operator = type(node.ops[0]) + if variable_node is left: + return _compare_marker_versions(target_python_version, operator, literal_node.value) + return _compare_marker_versions(literal_node.value, operator, target_python_version) + raise _UnsupportedMarkerError("unsupported marker expression shape") + + +def _marker_excludes_target_python(marker: str, target_python_version: str) -> bool: + """Return whether ``marker`` can be proven false for one fixed Python version. + + This only ever removes redundant network work: an archive this evaluator + cannot confidently rule out (an unsupported marker shape, or a parse + failure) is treated as included, exactly like today's unconditional + download, so it can never wrongly drop a dependency the target Python + version actually needs. + """ + try: + tree = ast.parse(marker, mode="eval") + return not _evaluate_marker_node(tree, target_python_version) + except (SyntaxError, _UnsupportedMarkerError, RecursionError, ValueError): + return False + + def materialize( repo_root: pathlib.Path, base_sha: str, output_dir: pathlib.Path, + *, + target_python_version: str | None = None, ) -> list[dict[str, str]]: - """Write base locks and resolvable bounded includes into a safe context.""" + """Write base locks and resolvable bounded includes into a safe context. + + ``target_python_version`` (a plain ``"major.minor"`` string such as + ``"3.14"``, matching the coverage image's pinned interpreter) lets an + archive whose ``marker`` field can be proven false for that version skip + the network download entirely -- the coverage image would never install + it anyway. Leave it ``None`` to download every archive unconditionally, + as before. This is purely an optimization: an archive that this cannot + confidently exclude is still downloaded and verified exactly as today. + """ if output_dir.exists() and output_dir.is_symlink(): raise ValueError("output directory must not be a symlink") output_dir.mkdir(parents=True, exist_ok=True) @@ -947,6 +1089,18 @@ def materialize( archive_directory = output_dir / "archives" archive_manifest: list[dict[str, object]] = [] for index, archive in enumerate(archive_sources): + marker = archive.get("marker") + if ( + target_python_version is not None + and marker is not None + and _marker_excludes_target_python(str(marker), target_python_version) + ): + # This archive's own marker rules it out for the coverage image's + # pinned interpreter; the download would never be installed, so it + # is dropped entirely rather than recorded as an unmaterialized + # manifest entry (see _archive_entries in install_base_python_locks.py, + # which requires every manifest entry to have a real file on disk). + continue url = str(archive["url"]) suffix = ".tar.gz" if url.endswith(".tar.gz") else ".zip" archive_file = f"archive-{index:03d}{suffix}" @@ -968,10 +1122,25 @@ def main(argv: list[str] | None = None) -> int: parser.add_argument("--repo-root", required=True, type=pathlib.Path) parser.add_argument("--base-sha", required=True) parser.add_argument("--output-dir", required=True, type=pathlib.Path) + parser.add_argument( + "--target-python-version", + default=None, + help=( + "major.minor Python version of the coverage image (e.g. 3.14), " + "used only to skip downloading an organization archive whose " + "marker already excludes it. Omit to download every archive " + "unconditionally." + ), + ) args = parser.parse_args(argv) try: - manifest = materialize(args.repo_root, args.base_sha, args.output_dir) + manifest = materialize( + args.repo_root, + args.base_sha, + args.output_dir, + target_python_version=args.target_python_version, + ) except (OSError, RuntimeError, ValueError) as exc: print( f"::error::Could not materialize base Python locks: {exc}", file=sys.stderr diff --git a/tests/test_materialize_base_python_requirements.py b/tests/test_materialize_base_python_requirements.py index 7ec3135334..63d2962f67 100644 --- a/tests/test_materialize_base_python_requirements.py +++ b/tests/test_materialize_base_python_requirements.py @@ -184,6 +184,160 @@ def test_materializes_archive_sources_separately_from_pip_locks( assert (output / "archives/archive-000.tar.gz").read_bytes() == archive +def test_materialize_skips_downloading_an_archive_excluded_for_the_target_python( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """A marker-excluded archive's unreachable URL must not fail the whole job. + + The coverage image would never install this archive for its pinned + interpreter anyway, so an unrelated outage on its URL (404, network blip, + a removed tag) must not fail base Python lock materialization. + """ + repository = tmp_path / "repo" + repository.mkdir() + git(repository, "init") + git(repository, "config", "user.name", "Test") + git(repository, "config", "user.email", "test@example.invalid") + git(repository, "commit", "--allow-empty", "-m", "base") + base_sha = git(repository, "rev-parse", "HEAD") + included_archive = b"included archive" + excluded_source = { + "package": "demo-py311-only", + "url": "https://github.com/ContextualWisdomLab/demo/archive/py311.tar.gz", + "hashes": ["a" * 64], + "marker": "python_version == '3.11'", + "source": "uv.lock", + } + included_source = { + "package": "demo", + "url": "https://github.com/ContextualWisdomLab/demo/archive/v1.tar.gz", + "hashes": [hashlib.sha256(included_archive).hexdigest()], + "marker": "python_version == '3.14'", + "source": "uv.lock", + } + monkeypatch.setattr( + materializer, + "_base_python_inputs", + lambda *_args: ([], [], [excluded_source, included_source]), + ) + + def fail_if_called_for_excluded_archive(url: str, _hashes: list[str]) -> bytes: + if url == excluded_source["url"]: + raise AssertionError( + "materialize() must not download an archive its own marker " + "excludes for the target coverage Python version" + ) + return included_archive + + monkeypatch.setattr( + materializer, + "_download_trusted_org_archive", + fail_if_called_for_excluded_archive, + ) + + output = tmp_path / "output" + manifest = materializer.materialize( + repository, base_sha, output, target_python_version="3.14" + ) + + assert manifest == [] + archive_manifest = json.loads((output / "archive-manifest.json").read_text()) + assert archive_manifest == [ + {**included_source, "file": "archives/archive-001.tar.gz"} + ] + assert (output / "archives/archive-001.tar.gz").read_bytes() == included_archive + assert not (output / "archives/archive-000.tar.gz").exists() + + +def test_materialize_downloads_every_archive_when_no_target_python_is_given( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Omitting the target version keeps today's unconditional download behavior.""" + repository = tmp_path / "repo" + repository.mkdir() + git(repository, "init") + git(repository, "config", "user.name", "Test") + git(repository, "config", "user.email", "test@example.invalid") + git(repository, "commit", "--allow-empty", "-m", "base") + base_sha = git(repository, "rev-parse", "HEAD") + archive_source = { + "package": "demo-py311-only", + "url": "https://github.com/ContextualWisdomLab/demo/archive/py311.tar.gz", + "hashes": ["a" * 64], + "marker": "python_version == '3.11'", + "source": "uv.lock", + } + monkeypatch.setattr( + materializer, + "_base_python_inputs", + lambda *_args: ([], [], [archive_source]), + ) + calls: list[str] = [] + + def record_call(url: str, _hashes: list[str]) -> bytes: + calls.append(url) + return b"payload" + + monkeypatch.setattr(materializer, "_download_trusted_org_archive", record_call) + + output = tmp_path / "output" + materializer.materialize(repository, base_sha, output) + + assert calls == [archive_source["url"]] + + +@pytest.mark.parametrize( + ("marker", "target_python_version", "expected"), + [ + ("python_version == '3.11'", "3.14", True), + ("python_version == '3.14'", "3.14", False), + ("python_version != '3.14'", "3.14", True), + ("python_version < '3.11'", "3.14", True), + ("python_version < '3.11'", "3.9", False), + ("python_version <= '3.14'", "3.14", False), + ("python_version > '3.14'", "3.14", True), + ("python_version >= '3.14'", "3.14", False), + ("python_version >= '3.9'", "3.14", False), + ("python_version == '3.9' or python_version == '3.14'", "3.14", False), + ("python_version == '3.9' or python_version == '3.10'", "3.14", True), + ( + "python_version >= '3.10' and python_version < '3.12'", + "3.14", + True, + ), + ( + "python_version >= '3.10' and python_version < '3.14'", + "3.9", + True, + ), + ("not python_version == '3.14'", "3.14", True), + ("python_full_version == '3.14.0'", "3.14", False), + ("python_full_version == '3.9.0'", "3.14", True), + # A literal on the left of the comparison is handled the same way. + ("'3.10' <= python_version", "3.9", True), + ("'3.10' <= python_version", "3.11", False), + # Unsupported/unparseable marker shapes must fail open (never exclude). + ("sys_platform == 'linux'", "3.14", False), + ("extra == 'test'", "3.14", False), + ("python_version in '3.11'", "3.14", False), + ("python_version == 'not-a-version'", "3.14", False), + ("not a valid marker (((", "3.14", False), + ("python_version == python_full_version", "3.14", False), + ("extra", "3.14", False), + ], +) +def test_marker_excludes_target_python( + marker: str, target_python_version: str, expected: bool +) -> None: + """Only a confidently-false python_version/python_full_version marker excludes.""" + assert ( + materializer._marker_excludes_target_python(marker, target_python_version) + == expected + ) + + def test_download_trusted_org_archive_uses_bounded_verified_https_stream( monkeypatch: pytest.MonkeyPatch, ) -> None: @@ -255,6 +409,69 @@ def test_trusted_org_archive_redirects_follow_codeload() -> None: ) +def test_trusted_org_archive_redirects_reject_cross_repository_hop() -> None: + """A same-host redirect to a different repository must not be followed. + + Both hosts are allowlisted, so a host-only check would let + ``github.com/ContextualWisdomLab/demo`` redirect onto + ``codeload.github.com/some-other-org/some-other-repo``. The redirect must + stay on the exact repository the original request named. + """ + with pytest.raises(RuntimeError, match="redirected outside GitHub"): + materializer._TrustedOrgArchiveRedirects().redirect_request( + materializer.urllib.request.Request( + "https://github.com/ContextualWisdomLab/demo/archive/v1.tar.gz" + ), + object(), + 302, + "Found", + {}, + "https://codeload.github.com/some-other-org/some-other-repo/tar.gz/v1", + ) + + +def test_trusted_org_archive_redirects_accept_case_insensitive_same_repository() -> None: + """GitHub repository names are case-insensitive; the same-repo check must match.""" + followed = materializer._TrustedOrgArchiveRedirects().redirect_request( + materializer.urllib.request.Request( + "https://github.com/ContextualWisdomLab/demo/archive/v1.tar.gz" + ), + object(), + 302, + "Found", + {}, + "https://codeload.github.com/contextualwisdomlab/DEMO/tar.gz/v1", + ) + + assert followed.full_url == ( + "https://codeload.github.com/contextualwisdomlab/DEMO/tar.gz/v1" + ) + + +def test_trusted_org_archive_redirects_reject_unrecognized_archive_path() -> None: + """An allowlisted-host URL that is not a recognized archive path shape is rejected.""" + with pytest.raises(RuntimeError, match="redirected outside GitHub"): + materializer._TrustedOrgArchiveRedirects().redirect_request( + materializer.urllib.request.Request( + "https://github.com/ContextualWisdomLab/demo/archive/v1.tar.gz" + ), + object(), + 302, + "Found", + {}, + "https://github.com/ContextualWisdomLab", + ) + + +def test_org_archive_repository_rejects_unrecognized_hosts() -> None: + """A host outside the two trusted archive hosts names no repository.""" + assert materializer._org_archive_repository("https://example.invalid/demo/archive/v1.tar.gz") is None + assert not materializer._is_same_org_archive_repository( + "https://example.invalid/demo/archive/v1.tar.gz", + "https://example.invalid/demo/archive/v1.tar.gz", + ) + + def test_trusted_org_archive_redirects_fail_when_parent_rejects_redirect( monkeypatch: pytest.MonkeyPatch, ) -> None: @@ -307,6 +524,34 @@ def open(self, _request_url: str, *, timeout: int) -> FakeHttpResponse: materializer._download_trusted_org_archive(url, ["a" * 64]) +def test_download_trusted_org_archive_rejects_final_url_naming_another_repository( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """A same-allowlisted-host final URL for a different repository is rejected. + + Defense in depth alongside the ``redirect_request`` check: even if the + observed final URL reached the response through some other path, it must + still name the exact repository that was originally requested. + """ + url = "https://github.com/ContextualWisdomLab/demo/archive/v1.tar.gz" + + class FakeOpener: + def open(self, _request_url: str, *, timeout: int) -> FakeHttpResponse: + del timeout + return FakeHttpResponse( + "https://codeload.github.com/some-other-org/some-other-repo/tar.gz/v1" + ) + + monkeypatch.setattr( + materializer.urllib.request, + "build_opener", + lambda *_handlers: FakeOpener(), + ) + + with pytest.raises(RuntimeError, match="left GitHub origins"): + materializer._download_trusted_org_archive(url, ["a" * 64]) + + def test_download_trusted_org_archive_wraps_network_errors( monkeypatch: pytest.MonkeyPatch, ) -> None: @@ -900,8 +1145,13 @@ def test_main_reports_each_materialized_lock( """The CLI identifies the exact trusted source and generated lock name.""" def fake_materialize( - _repo_root: Path, _base_sha: str, _output_dir: Path + _repo_root: Path, + _base_sha: str, + _output_dir: Path, + *, + target_python_version: str | None = None, ) -> list[dict[str, str]]: + del target_python_version return [ { "file": "requirements-000.txt", @@ -936,7 +1186,7 @@ def test_main_reports_when_no_locks_exist( capsys: pytest.CaptureFixture[str], ) -> None: """The CLI distinguishes an empty trusted base from a failed extraction.""" - monkeypatch.setattr(materializer, "materialize", lambda *_args: []) + monkeypatch.setattr(materializer, "materialize", lambda *_args, **_kwargs: []) assert ( materializer.main( @@ -964,7 +1214,14 @@ def test_main_fails_with_the_materialization_reason( ) -> None: """A materialization exception fails closed and remains diagnosable in CI.""" - def fail_materialize(_repo_root: Path, _base_sha: str, _output_dir: Path) -> None: + def fail_materialize( + _repo_root: Path, + _base_sha: str, + _output_dir: Path, + *, + target_python_version: str | None = None, + ) -> None: + del target_python_version raise OSError("fixture failure") monkeypatch.setattr(materializer, "materialize", fail_materialize) diff --git a/tests/test_pr_review_autofix_nvidia_nim_contract.py b/tests/test_pr_review_autofix_nvidia_nim_contract.py index ef78af2970..4e5f8a9558 100644 --- a/tests/test_pr_review_autofix_nvidia_nim_contract.py +++ b/tests/test_pr_review_autofix_nvidia_nim_contract.py @@ -19,7 +19,7 @@ DOCTORING_RECORD = Path("docs/doctoring/hourly-nvidia-nim-autofix.md") CHANGELOG = Path("CHANGELOG.md") REVIEW_DISPATCH_WORKFLOW = Path(".github/workflows/opencode-review-dispatch.yml") -REVIEW_DISPATCH_BLOB_SHA = "1b7002d0f54eb83ac9774fa1c43265598cdf84ed" +REVIEW_DISPATCH_BLOB_SHA = "c5812ed49385d93947393a60f8f006e339d176ef" def _workflow_text(path: Path) -> str: From 10207c451a62c45273596317a5bd137c7a5ce8b3 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 30 Aug 2026 17:28:45 +0000 Subject: [PATCH 74/76] fix(ci): tolerate a missing archive manifest, fix dash-incompatible cargo fetch loop Two bugs in the coverage tool image's Dockerfile heredoc (embedded in opencode-review-dispatch.yml's "Measure test and docstring evidence" step): - The archive-manifest.json read at image-build time was unconditional (json.loads(path.read_text(...))), but the "no Python source or dependency-manifest changes" skip path never creates that file (only manifest.json/manifest.txt/vcs-manifest.json). Any PR touching no Python files therefore crashed the coverage image build. Make the read tolerant of a missing manifest, treating it as an empty archive list, matching the existing convention in install_base_python_locks.py's _archive_entries(). - The Cargo dependency cache warm-up used `while IFS= read -r -d "" ...` to walk `find -print0` output, but python:3.14-slim's default shell for Docker's shell-form RUN is dash, and dash's `read` does not implement bash's `-d` extension. Under dash this `read` fails immediately with "Illegal option -d", the loop body (cargo fetch) never runs, and because a failing while-condition is not itself a `set -e` trigger, the RUN step still reports success -- leaving the Cargo archive cache empty and only surfacing later as a confusing offline-build failure. Replace the loop with `xargs -0 -r -n1`, which is POSIX-sh portable and (unlike the original) also propagates a real cargo fetch failure through set -e. Both were verified against a real dash before and after the fix. --- .../workflows/opencode-review-dispatch.yml | 11 ++--- tests/test_opencode_agent_contract.py | 12 +++++ tests/test_opencode_workflow_shell_syntax.py | 45 +++++++++++++++++++ ...t_pr_review_autofix_nvidia_nim_contract.py | 2 +- 4 files changed, 64 insertions(+), 6 deletions(-) diff --git a/.github/workflows/opencode-review-dispatch.yml b/.github/workflows/opencode-review-dispatch.yml index c5812ed493..ff774865a3 100644 --- a/.github/workflows/opencode-review-dispatch.yml +++ b/.github/workflows/opencode-review-dispatch.yml @@ -875,8 +875,11 @@ jobs: requirements_root = pathlib.Path("/tmp/base-python-requirements") source_root = pathlib.Path("/opt/base-python-archive-sources").resolve() - manifest = json.loads( - (requirements_root / "archive-manifest.json").read_text(encoding="utf-8") + archive_manifest_path = requirements_root / "archive-manifest.json" + manifest = ( + json.loads(archive_manifest_path.read_text(encoding="utf-8")) + if archive_manifest_path.exists() + else [] ) coverage_environment = default_environment() for index, entry in enumerate(manifest): @@ -941,9 +944,7 @@ jobs: RUN set -eu; \ find /opt/base-python-archive-sources -name Cargo.toml -print0 \ | sort -z -u \ - | while IFS= read -r -d "" manifest_path; do \ - cargo fetch --locked --manifest-path "$manifest_path"; \ - done + | xargs -0 -r -n1 cargo fetch --locked --manifest-path RUN --network=none python3 -I /usr/local/libexec/install-base-python-locks.py \ --requirements-root /tmp/base-python-requirements \ --archives-only \ diff --git a/tests/test_opencode_agent_contract.py b/tests/test_opencode_agent_contract.py index 396120faa4..f156f84d46 100644 --- a/tests/test_opencode_agent_contract.py +++ b/tests/test_opencode_agent_contract.py @@ -745,6 +745,18 @@ def test_opencode_target_coverage_materializes_only_after_authorized_dispatch(): assert "--archives-only" in measure_step assert "RUN --network=none python3 -I /usr/local/libexec/install-base-python-locks.py" in measure_step assert "from packaging.markers import Marker, default_environment" in measure_step + assert ( + 'archive_manifest_path = requirements_root / "archive-manifest.json"' + in measure_step + ) + assert ( + "json.loads(archive_manifest_path.read_text(encoding=\"utf-8\"))\n" + " if archive_manifest_path.exists()\n" + " else []" + ) in measure_step + assert measure_step.index("archive_manifest_path.exists()") < measure_step.index( + "coverage_environment = default_environment()" + ) assert "coverage_environment = default_environment()" in measure_step assert "not Marker(marker).evaluate(coverage_environment)" in measure_step assert measure_step.index("not Marker(marker).evaluate") < measure_step.index( diff --git a/tests/test_opencode_workflow_shell_syntax.py b/tests/test_opencode_workflow_shell_syntax.py index b0a672b1a2..c4f4eb2a1a 100644 --- a/tests/test_opencode_workflow_shell_syntax.py +++ b/tests/test_opencode_workflow_shell_syntax.py @@ -95,6 +95,51 @@ def test_opencode_review_comment_helpers_are_shared_and_valid_bash(): assert result.returncode == 0, result.stderr +def test_cargo_fetch_run_step_is_valid_posix_sh_not_only_bash(): + """The coverage image's base is Debian, whose default ``/bin/sh`` is dash. + + Docker's shell-form ``RUN`` executes under the image's default shell, not + bash, and dash does not implement bash's ``read -d`` extension. A prior + version of this step used ``while IFS= read -r -d "" ...`` to walk + NUL-delimited ``find -print0`` output; under dash that ``read`` fails on + every invocation, the ``while`` loop body never runs, and -- because a + failing loop condition is not itself a ``set -e`` trigger -- the whole + ``RUN`` step still reports success with the Cargo archive cache left + empty. Assert both that the bash-only construct is gone and that the + replacement genuinely parses under a real POSIX ``dash``. + """ + workflow_text = (REPO_ROOT / ".github/workflows/opencode-review-dispatch.yml").read_text( + encoding="utf-8" + ) + start_anchor = "RUN set -eu; \\\n find /opt/base-python-archive-sources" + start = workflow_text.index(start_anchor) + end_anchor = ( + "\n RUN --network=none " + "python3 -I /usr/local/libexec/install-base-python-locks.py" + ) + end = workflow_text.index(end_anchor, start) + script = workflow_text[start:end] + + assert "read -d" not in script + assert "xargs -0" in script + assert "cargo fetch --locked --manifest-path" in script + + if sys.platform == "win32": + return + dash = shutil.which("dash") + if dash is None: + return + result = subprocess.run( + [dash, "-n"], + input=script.removeprefix("RUN "), + text=True, + capture_output=True, + check=False, + ) + + assert result.returncode == 0, result.stderr + + def test_merge_scheduler_review_followup_run_block_is_valid_bash(): """The App-review follow-up keeps its dynamic wait logic valid Bash.""" if sys.platform == "win32": diff --git a/tests/test_pr_review_autofix_nvidia_nim_contract.py b/tests/test_pr_review_autofix_nvidia_nim_contract.py index 4e5f8a9558..f9f2521aaf 100644 --- a/tests/test_pr_review_autofix_nvidia_nim_contract.py +++ b/tests/test_pr_review_autofix_nvidia_nim_contract.py @@ -19,7 +19,7 @@ DOCTORING_RECORD = Path("docs/doctoring/hourly-nvidia-nim-autofix.md") CHANGELOG = Path("CHANGELOG.md") REVIEW_DISPATCH_WORKFLOW = Path(".github/workflows/opencode-review-dispatch.yml") -REVIEW_DISPATCH_BLOB_SHA = "c5812ed49385d93947393a60f8f006e339d176ef" +REVIEW_DISPATCH_BLOB_SHA = "ff774865a3fdd2df75874522c926b581e3f21bae" def _workflow_text(path: Path) -> str: From 0542c672cb6ac9846109ac4c91100a4a08dc2ab1 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 30 Aug 2026 17:42:58 +0000 Subject: [PATCH 75/76] fix(ci): stop treating python_full_version markers as patch-exact from a major.minor target _evaluate_marker_node fed the coverage workflow's major.minor --target-python-version (e.g. "3.14") into the same zero-padded comparison path for both python_version and python_full_version marker variables. Zero-padding "3.14" into "3.14.0" is correct for python_version (which genuinely is major.minor) but silently assumes a patch release of .0 for python_full_version, which is patch- sensitive and not something the target string actually claims. That let patch-sensitive comparisons resolve confidently and wrongly, e.g. `python_full_version >= "3.14.1"` evaluated to False against the padded "3.14.0" even though the real interpreter (say 3.14.5) would make the marker True -- causing a needed archive to be silently skipped before the Docker build. Add a python_full_version-specific guard: only resolve such a comparison confidently when the target string itself already carries patch precision (3+ dotted components); otherwise raise _UnsupportedMarkerError so the caller keeps its existing fail-open default (download, never skip). python_version comparisons are unaffected since that variable's target value is already exact. Add regression coverage for both an equality and an inequality python_full_version marker against a major.minor-only target (must not confidently skip), a confirmation that python_full_version resolves confidently once the target is patch-precise, and keep the existing python_version confidence tests. --- .../materialize_base_python_requirements.py | 30 ++++++++++++++++++- ...st_materialize_base_python_requirements.py | 23 ++++++++++++-- 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/scripts/ci/materialize_base_python_requirements.py b/scripts/ci/materialize_base_python_requirements.py index 94d0af025a..8df0ee1b22 100755 --- a/scripts/ci/materialize_base_python_requirements.py +++ b/scripts/ci/materialize_base_python_requirements.py @@ -942,6 +942,18 @@ def _rewrite_materialized_includes( _SUPPORTED_MARKER_VARIABLES = frozenset({"python_version", "python_full_version"}) +# ``target_python_version`` is a plain "major.minor" string (e.g. "3.14") -- +# the coverage workflow genuinely cannot know the exact patch release of its +# pinned interpreter ahead of time. That value is a confident, exact stand-in +# for the ``python_version`` marker variable (which is itself major.minor), +# but it is NOT a confident stand-in for ``python_full_version`` (patch- +# sensitive): padding "3.14" into "3.14.0" would silently assume the patch +# component is zero, which is not something the target string actually says. +# Only variables in this set may have their missing components zero-padded +# for a comparison; a ``python_full_version`` comparison is only trusted when +# the target string itself already carries patch precision (3+ components). +_PATCH_INSENSITIVE_MARKER_VARIABLES = frozenset({"python_version"}) + class _UnsupportedMarkerError(Exception): """Raised when a marker shape is outside the narrow supported subset.""" @@ -985,7 +997,11 @@ def _evaluate_marker_node(node: ast.AST, target_python_version: str) -> bool: Python-version-gated organization archive sources. Any other marker shape (``sys_platform``, ``extra``, ``in``/``not in``, function calls, chained comparisons, and so on) raises ``_UnsupportedMarkerError`` so the caller - fails open and still downloads the archive. + fails open and still downloads the archive. A ``python_full_version`` + comparison is patch-sensitive and additionally requires ``target_python_version`` + itself to carry patch precision (3+ dotted components) to be resolved + confidently; given only a major.minor target it also raises + ``_UnsupportedMarkerError`` rather than assume the missing patch is ``.0``. """ if isinstance(node, ast.Expression): return _evaluate_marker_node(node.body, target_python_version) @@ -1003,6 +1019,18 @@ def _evaluate_marker_node(node: ast.AST, target_python_version: str) -> bool: raise _UnsupportedMarkerError("unsupported marker variable") if not isinstance(literal_node, ast.Constant) or not isinstance(literal_node.value, str): raise _UnsupportedMarkerError("unsupported marker literal") + if ( + variable_node.id not in _PATCH_INSENSITIVE_MARKER_VARIABLES + and len(_marker_version_tuple(target_python_version)) < 3 + ): + # ``python_full_version`` is patch-sensitive, but the target is + # only major.minor -- the real interpreter's patch component is + # genuinely unknown here, so this comparison cannot be resolved + # confidently in either direction. Fail open rather than assume + # the missing patch is ".0" (see the module docstring above). + raise _UnsupportedMarkerError( + "python_full_version comparison requires a patch-precise target version" + ) operator = type(node.ops[0]) if variable_node is left: return _compare_marker_versions(target_python_version, operator, literal_node.value) diff --git a/tests/test_materialize_base_python_requirements.py b/tests/test_materialize_base_python_requirements.py index 63d2962f67..ec9481db01 100644 --- a/tests/test_materialize_base_python_requirements.py +++ b/tests/test_materialize_base_python_requirements.py @@ -313,8 +313,6 @@ def record_call(url: str, _hashes: list[str]) -> bytes: True, ), ("not python_version == '3.14'", "3.14", True), - ("python_full_version == '3.14.0'", "3.14", False), - ("python_full_version == '3.9.0'", "3.14", True), # A literal on the left of the comparison is handled the same way. ("'3.10' <= python_version", "3.9", True), ("'3.10' <= python_version", "3.11", False), @@ -326,6 +324,27 @@ def record_call(url: str, _hashes: list[str]) -> bytes: ("not a valid marker (((", "3.14", False), ("python_version == python_full_version", "3.14", False), ("extra", "3.14", False), + # python_full_version is patch-sensitive: a major.minor-only target + # (as the coverage workflow genuinely only ever has) must NOT be + # zero-padded into a confident patch value. Every one of these must + # fail open (never exclude/skip the download), regardless of operator + # or of whether the zero-padded guess would have happened to match. + ("python_full_version == '3.14.0'", "3.14", False), + ("python_full_version == '3.9.0'", "3.14", False), + ("python_full_version != '3.14.0'", "3.14", False), + ("python_full_version >= '3.14.1'", "3.14", False), + ("python_full_version > '3.14.0'", "3.14", False), + ("python_full_version < '3.14.5'", "3.14", False), + ( + "python_full_version >= '3.10.0' and python_full_version < '3.15.0'", + "3.14", + False, + ), + # Once the target itself carries patch precision, python_full_version + # comparisons become confident again. + ("python_full_version >= '3.14.1'", "3.14.5", False), + ("python_full_version >= '3.14.6'", "3.14.5", True), + ("python_full_version == '3.14.5'", "3.14.5", False), ], ) def test_marker_excludes_target_python( From 3fb8b911444e1059118ed4302700a90a7eab90a9 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 30 Aug 2026 17:59:04 +0000 Subject: [PATCH 76/76] fix(ci): resolve python_full_version markers confidently across minor series _evaluate_marker_node previously failed open for every python_full_version comparison whenever the target was major.minor-only, even when the marker's literal falls in a different major.minor series entirely (e.g. python_full_version == "3.9.0" or < "3.0.0" against a "3.14" target). Those are decidable for every possible patch of the target's minor series, since the major.minor mismatch alone settles the comparison. Add _python_full_version_comparison_is_ambiguous to scope the fail-open path to the genuinely unknown case: the literal's major.minor matches the target's. Only then does the outcome depend on the interpreter's real, not-yet-known patch digit, for any operator. Update the marker-evaluation regression tests: the previously-failing-open cross-minor case now resolves confidently, with new cases covering an out-of-series lower/upper bound, a literal-on-the-left comparison, and a short literal that zero-pads into a different major.minor. The existing same-minor-series cases (which must keep failing open) are unchanged. --- .../materialize_base_python_requirements.py | 72 ++++++++++++++++--- ...st_materialize_base_python_requirements.py | 35 +++++++-- 2 files changed, 92 insertions(+), 15 deletions(-) diff --git a/scripts/ci/materialize_base_python_requirements.py b/scripts/ci/materialize_base_python_requirements.py index 8df0ee1b22..e16aede453 100755 --- a/scripts/ci/materialize_base_python_requirements.py +++ b/scripts/ci/materialize_base_python_requirements.py @@ -950,8 +950,11 @@ def _rewrite_materialized_includes( # sensitive): padding "3.14" into "3.14.0" would silently assume the patch # component is zero, which is not something the target string actually says. # Only variables in this set may have their missing components zero-padded -# for a comparison; a ``python_full_version`` comparison is only trusted when -# the target string itself already carries patch precision (3+ components). +# for a comparison against an equal-or-shorter target; a ``python_full_version`` +# comparison against a major.minor-only target is trusted only when the +# literal's own major.minor already falls outside the target's series (see +# ``_python_full_version_comparison_is_ambiguous``) -- otherwise it needs the +# target string to carry patch precision (3+ components) itself. _PATCH_INSENSITIVE_MARKER_VARIABLES = frozenset({"python_version"}) @@ -988,6 +991,44 @@ def _compare_marker_versions(left: str, operator: type, right: str) -> bool: raise _UnsupportedMarkerError("unsupported marker comparison operator") +def _python_full_version_comparison_is_ambiguous( + target_python_version: str, literal: str +) -> bool: + """Return whether a ``python_full_version`` comparison needs the unknown patch. + + ``target_python_version`` is only known to major.minor precision; the real + interpreter's trailing components (patch, and beyond) are genuinely + unknown. ``literal`` is a fully specified PEP 440 version constant, so + treating any of *its* missing trailing components as zero is exact, not a + guess -- that is standard version-comparison normalization, not an + assumption about the target. + + Comparing the two, element by element, up to the target's known length: + if a real difference already shows up within that shared, fully-known + prefix, the comparison is decided right there and no later component -- + real or unknown -- can change it, because ordinary tuple/lexicographic + comparison stops at the first differing position. That covers both kinds + of confidently-decidable case: a different minor (``3.9`` vs. ``3.14``) + and a minor entirely outside the target's series (``3.0`` or ``4.0`` vs. + ``3.14``). + + The comparison is ambiguous only when the literal's prefix, truncated (or + zero-padded, if shorter) to the target's known length, exactly equals the + target's known prefix -- i.e. the literal shares the target's major.minor + series and therefore the outcome hinges on the interpreter's real, + not-yet-known trailing components. That is the only case left to fail + open, regardless of which operator is being evaluated (``==``, ``!=``, + ``<``, ``<=``, ``>``, ``>=`` all depend on the unknown patch digit once + the known prefix already matches). + """ + target_tuple = _marker_version_tuple(target_python_version) + literal_tuple = _marker_version_tuple(literal) + known_length = len(target_tuple) + literal_prefix = literal_tuple[:known_length] + literal_prefix = literal_prefix + (0,) * (known_length - len(literal_prefix)) + return literal_prefix == target_tuple + + def _evaluate_marker_node(node: ast.AST, target_python_version: str) -> bool: """Evaluate one parsed marker expression node against a fixed Python version. @@ -998,10 +1039,13 @@ def _evaluate_marker_node(node: ast.AST, target_python_version: str) -> bool: (``sys_platform``, ``extra``, ``in``/``not in``, function calls, chained comparisons, and so on) raises ``_UnsupportedMarkerError`` so the caller fails open and still downloads the archive. A ``python_full_version`` - comparison is patch-sensitive and additionally requires ``target_python_version`` - itself to carry patch precision (3+ dotted components) to be resolved - confidently; given only a major.minor target it also raises - ``_UnsupportedMarkerError`` rather than assume the missing patch is ``.0``. + comparison is patch-sensitive: given only a major.minor + ``target_python_version``, it resolves confidently when the literal's + major.minor differs from the target's (the outcome cannot depend on the + target's unknown patch digit in that case -- see + ``_python_full_version_comparison_is_ambiguous``), and otherwise raises + ``_UnsupportedMarkerError`` rather than assume the missing patch is + ``.0``. """ if isinstance(node, ast.Expression): return _evaluate_marker_node(node.body, target_python_version) @@ -1022,12 +1066,18 @@ def _evaluate_marker_node(node: ast.AST, target_python_version: str) -> bool: if ( variable_node.id not in _PATCH_INSENSITIVE_MARKER_VARIABLES and len(_marker_version_tuple(target_python_version)) < 3 + and _python_full_version_comparison_is_ambiguous( + target_python_version, literal_node.value + ) ): - # ``python_full_version`` is patch-sensitive, but the target is - # only major.minor -- the real interpreter's patch component is - # genuinely unknown here, so this comparison cannot be resolved - # confidently in either direction. Fail open rather than assume - # the missing patch is ".0" (see the module docstring above). + # ``python_full_version`` is patch-sensitive, and the target is + # only major.minor -- but that only makes a comparison unknown + # when the literal shares the target's major.minor series (see + # ``_python_full_version_comparison_is_ambiguous``). A literal in + # a different minor series is decidable for every possible patch + # and falls through to the ordinary comparison below instead. + # Fail open rather than assume the missing patch is ".0" (see the + # module docstring above). raise _UnsupportedMarkerError( "python_full_version comparison requires a patch-precise target version" ) diff --git a/tests/test_materialize_base_python_requirements.py b/tests/test_materialize_base_python_requirements.py index ec9481db01..2894ab7006 100644 --- a/tests/test_materialize_base_python_requirements.py +++ b/tests/test_materialize_base_python_requirements.py @@ -326,20 +326,47 @@ def record_call(url: str, _hashes: list[str]) -> bytes: ("extra", "3.14", False), # python_full_version is patch-sensitive: a major.minor-only target # (as the coverage workflow genuinely only ever has) must NOT be - # zero-padded into a confident patch value. Every one of these must - # fail open (never exclude/skip the download), regardless of operator - # or of whether the zero-padded guess would have happened to match. + # zero-padded into a confident patch value *when the literal shares + # the target's own major.minor series* -- the outcome then genuinely + # depends on the interpreter's real, unknown patch digit, so these + # must fail open (never exclude/skip the download) regardless of + # operator or of whether the zero-padded guess would have happened + # to match. ("python_full_version == '3.14.0'", "3.14", False), - ("python_full_version == '3.9.0'", "3.14", False), ("python_full_version != '3.14.0'", "3.14", False), ("python_full_version >= '3.14.1'", "3.14", False), ("python_full_version > '3.14.0'", "3.14", False), ("python_full_version < '3.14.5'", "3.14", False), + # But a literal whose major.minor falls OUTSIDE the target's series + # is decidable for every possible patch of the target's minor series + # -- the major.minor mismatch alone settles it, so these must now + # resolve confidently instead of failing open. + # Cross-minor equality: 3.14.x is never version 3.9.0. + ("python_full_version == '3.9.0'", "3.14", True), + # Below the target's entire minor series: 3.14.x is never < 3.0.0. + ("python_full_version < '3.0.0'", "3.14", True), + # Above the target's entire minor series: 3.14.x is never >= 4.0.0. + ("python_full_version >= '4.0.0'", "3.14", True), + # A cross-minor literal with the variable on the left of the compare. + ("'3.9.0' == python_full_version", "3.14", True), + # A literal with fewer components than the target still resolves + # confidently once zero-padded, provided the padded major.minor + # differs from the target's. + ("python_full_version == '3'", "3.14", True), ( "python_full_version >= '3.10.0' and python_full_version < '3.15.0'", "3.14", False, ), + # A same-minor-series operand alongside a decidable one: the overall + # marker still must not be resolved confidently, because ``and`` + # cannot decide without the ambiguous operand's true value, and + # ``_evaluate_marker_node`` propagates the ambiguity by raising. + ( + "python_full_version >= '3.14.1' and python_full_version < '3.15.0'", + "3.14", + False, + ), # Once the target itself carries patch precision, python_full_version # comparisons become confident again. ("python_full_version >= '3.14.1'", "3.14.5", False),