Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
### Contextual-orchestrator pin advance to `012beaac`, and an unattested provider no longer takes review down

- Advanced the central sidecar's pinned immutable CO revision from `414f2297` to protected `main@012beaacd0631f8cd3391c77744eeb626269b5de`, a clean ancestor 175 commits back (`ahead_by=175, behind_by=0`). The advance was gated on executed evidence, not on reading the range: `requirements.lock` is byte-identical across all 175 commits so the `--require-hashes --no-deps` install resolves the same wheels (`pyproject.toml`'s `fast-mlsirm` spec did move from a release tarball to a `git+https` commit pin, but the sidecar never runs `pip install -e .`); the sidecar's own `python - <<'PY'` startup contract -- which subclasses `ModelClient`, overrides `proxy_send`, calls the private `_mock_raw`, and asserts the 413 envelope, a 64 KiB+ accepted body, and byte-exact tool-description passthrough -- was extracted and executed unmodified against a `012beaac` worktree and passes, which is the check that matters because `orchestrator.py` changed +1092/-251 in the range; and all eleven symbols the sidecar and launcher import were resolved and signature-inspected at the target, with `DiscoveredModel` purely additive (one new `supports_parallel_tool_calls` field). `ModelClient.__init__`'s `timeout` default is now `float | None = None` rather than `int = 90`, so the vendored runtime no longer carries the implicit per-request wall-clock cap ADR-0003's 2026-08-31 amendment forbids -- that removal landed upstream in contextual-orchestrator#1118 (merged 2026-09-10), not in the still-open contextual-orchestrator#1053 this pin was previously recorded as waiting on. Three range changes are the reason to take it: `build_review_orchestrator` now builds `ModelClient()` instead of `ModelClient(max_output_tokens=32768)` and the new `effective_max_output_tokens(agent)` resolves the cap request-scope -> client -> the agent's provider-published ceiling, so a flat 32768 stops over-asking models whose published ceiling is lower (a provider 400, not a routing failure) and stops under-using models with a higher one; error payloads and the `request_failed` log line now carry a `request_id`, the correlation field whose absence made #2000's caller-retry question unanswerable; and the range carries `8586e9aa fix(discovery): recover usable Bytez chat models` plus several OpenRouter/models.dev discovery fixes, against a free pool whose last five recorded preflights admitted only `nvidia_nim`/`nvidia_nim_sub` routes with Bytez failing discovery at `http_status_500` every time (#1915). `docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md` gains a 2026-09-13 amendment and `tests/test_contextual_orchestrator_review_sidecar_contract.py`'s `ORCH_PIN_SHA` moves with it. All callers still consume an exact SHA; no branch or tag is introduced.
- Guarded the latent outage the advance creates. `012beaac` ships two new provider sources, `opencode_zen` and `opencode_go`, both keyed on an optional `OPENCODE_ZEN_API_KEY`, and `review_gateway.REVIEW_CREDENTIAL_NAMES` now defaults to `PROVIDER_ACCEPTED_CREDENTIAL_NAMES`, so the launcher's `register_review_credentials(os.environ)` would register that key straight out of the job environment if it were ever present. `scripts/ci/zdr_policy.py` has no entry for either provider and `contextual_orchestrator_review_policy.parse_discovery_report` *raises* `PolicyError` for an unregistered provider rather than skipping the row -- so adding that one secret to a workflow's `env:` would have failed every central review sidecar closed, on a provider nobody had asked to route to. The secret is not seeded today, so nothing is failing; the exposure is latent and one line of workflow YAML away. `_report_rows` in `contextual_orchestrator_review_launcher.py` now drops a discovered row whose provider is absent from the org ZDR policy table and prints a bounded `discovery_row_skipped_unattested_provider provider=<name> model=<id>` diagnostic to stderr. The safety property is unchanged and is the point: an unattested provider still never serves review traffic, and `parse_discovery_report` keeps its fail-closed contract verbatim for rows that are submitted. Attesting OpenCode Zen/Go for the free pool needs a real retention-posture determination for `opencode.ai` and is deliberately not granted here. Tests: `tests/test_contextual_orchestrator_review_unattested_provider_skip.py`.

### Failed-check finding names the Strix sandbox instead of the gateway

- `opencode-review-dispatch.yml`'s `emit_strix_provider_failure_finding` rendered one fixed finding for every `STRIX_PROVIDER_UNAVAILABLE` line, whose Root cause read "The contextual-orchestrator gateway or its discovered provider pool was unavailable for this run". `#1953` had just given the Strix sandbox bootstrap failure its own second verdict token (`STRIX_SANDBOX_UNAVAILABLE`) precisely because that attribution is wrong for it -- the sandbox container never reaches its Caido proxy, so the run dies before the gateway serves anything -- and this consumer re-applied the wrong attribution one step downstream, into the review findings and the failure census. The emitter now branches on the second token: a sandbox verdict gets a finding that names Strix's sandbox, says the verdict does not name the gateway, and tells the reader not to change gateway or provider configuration on its strength. A `STRIX_PROVIDER_UNAVAILABLE` line without the token keeps its existing text verbatim, so the gateway class has no regression surface. No test covered this finding text at all before (`gateway or its discovered provider pool` matched nothing under `tests/`); `tests/test_opencode_dispatch_strix_sandbox_finding.py` now runs the production emitter from the published run block and pins both directions plus the no-signal case. Refs #1953, #1935.
Expand Down
72 changes: 69 additions & 3 deletions docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ 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
(`414f22973658c4ddc3d4320fcf7acd9b4e8ba991` today) into `RUNNER_TEMP`. The
(`012beaacd0631f8cd3391c77744eeb626269b5de` 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.
runtime entry (`contextual_orchestrator_review_launcher.py`) registers the
runtime. The runtime entry
(`contextual_orchestrator_review_launcher.py`) registers the
five provider secrets plus the gateway bearer token into the process-local
KV in the **same process** that performs model discovery and serves
`/v1/chat/completions` and `/v1/responses` on loopback. Env is bootstrap
Expand Down Expand Up @@ -282,3 +282,69 @@ all five, and auto-optimize routing by cost.
per-agent attempt; it changes only *which* agent gets tried next, never any
per-attempt timeout, consistent with the 2026-08-31 amendment above. No
other contextual-orchestrator behavior changes with this pin advance.
- **2026-09-13 amendment: advance the governed runtime pin to
`012beaacd0631f8cd3391c77744eeb626269b5de`.** The vendored pin advances 175
commits from `414f22973658c4ddc3d4320fcf7acd9b4e8ba991`, a clean ancestor of
contextual-orchestrator `main` (`ahead_by=175, behind_by=0`). Four properties
were verified against the target revision before the advance, not inferred
from it:
1. **The install path does not move.** `requirements.lock` is byte-identical
across the whole range (`git diff 414f2297..012beaac -- requirements.lock`
is empty), so the `--require-hashes --no-deps` install resolves the same
wheels. `pyproject.toml`'s `fast-mlsirm` spec did change (release tarball →
`git+https` commit pin), but the sidecar never runs `pip install -e .`, so
that spec is not on this execution path.
2. **The sidecar's own startup contract passes.** The `python - <<'PY'` block
in `contextual_orchestrator_review_sidecar.sh` — which subclasses
`ModelClient`, overrides `proxy_send`, calls the private `_mock_raw`,
drives `build_server` over loopback, and asserts the 413 envelope, the
64 KiB+ accepted body, and byte-exact tool-description passthrough — was
extracted and executed against a `012beaac` worktree. It passes unmodified.
This matters because `contextual_orchestrator/orchestrator.py` changed
+1092/-251 in the range and the block depends on a private method.
3. **Every imported symbol still resolves compatibly.** All eleven names the
sidecar and `contextual_orchestrator_review_launcher.py` import were
imported and their signatures inspected at `012beaac`. `DiscoveredModel`
is purely additive (one new `supports_parallel_tool_calls` field), and the
launcher reads every discovery field through `getattr` with a default.
4. **No wall-clock cap is introduced on the inference path.**
`ModelClient.__init__`'s `timeout` default is now `float | None = None`
(was `int = 90`), so the implicit 90-second per-request cap this ADR's
2026-08-31 amendment forbids is gone from the vendored runtime.

Three range changes are the reason to take it rather than incidental to it.
`build_review_orchestrator` now constructs `ModelClient()` instead of
`ModelClient(max_output_tokens=32768)`, and the new
`effective_max_output_tokens(agent)` resolves the cap request-scope → client →
the selected agent's provider-published `max_output_tokens`; a flat 32768 both
over-asked models whose published ceiling is lower (a provider 400, not a
routing failure) and under-used models with a higher one. Error payloads and
the `request_failed` log line now carry a `request_id`, which is the
correlation field whose absence made `#2000`'s caller-retry question
unanswerable from the evidence. And the range carries
`8586e9aa fix(discovery): recover usable Bytez chat models` plus several
OpenRouter/models.dev discovery fixes, against a free pool whose last five
recorded preflights admitted only `nvidia_nim`/`nvidia_nim_sub` routes with
Bytez failing discovery at `http_status_500` every time (`#1915`).

**One risk the advance creates, and the guard for it.** `012beaac` ships two
new provider sources — `opencode_zen` and `opencode_go`, both keyed on an
optional `OPENCODE_ZEN_API_KEY` — and `review_gateway.REVIEW_CREDENTIAL_NAMES`
now defaults to `PROVIDER_ACCEPTED_CREDENTIAL_NAMES`, so the launcher's
`register_review_credentials(os.environ)` would register that key from the job
environment if it were ever present. This repository's
`scripts/ci/zdr_policy.py` has no entry for either provider, and
`contextual_orchestrator_review_policy.parse_discovery_report` *raises*
`PolicyError` for an unregistered provider rather than skipping the row — so
adding that one secret to a workflow's `env:` would have taken every central
review sidecar down. The secret is not seeded today, so nothing is failing;
the exposure is latent and one line of workflow YAML away.
`_report_rows` in `contextual_orchestrator_review_launcher.py` now drops a
discovered row whose provider is absent from the org ZDR policy table, with a
bounded `discovery_row_skipped_unattested_provider` stderr diagnostic. This
preserves the actual safety property — an unattested provider never serves
review traffic — while removing the outage. `parse_discovery_report` keeps its
fail-closed contract unchanged for rows that *are* submitted. Attesting
OpenCode Zen/Go for the free pool remains a separate, deliberate change
requiring a real retention-posture determination for `opencode.ai`; it is not
granted here and must not be inferred from this amendment.
22 changes: 22 additions & 0 deletions scripts/ci/contextual_orchestrator_review_launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,20 @@ def _report_rows(
read from the discovered model when present and otherwise falls back to the
org ZDR policy table (``scripts/ci/zdr_policy.py``).

A discovered row whose provider has no entry in that org policy table is
dropped here with a bounded stderr diagnostic. The gateway ships provider
sources this repository has not yet attested for retention posture (as of
contextual-orchestrator ``012beaac``: ``opencode_zen`` and ``opencode_go``,
both keyed on an optional ``OPENCODE_ZEN_API_KEY``), and
``register_review_credentials(os.environ)`` registers every accepted
provider credential the job environment happens to carry. Submitting such a
row would make ``parse_discovery_report`` raise ``PolicyError`` and take the
whole review sidecar down, so the unattested provider is excluded from the
pool -- the actual safety requirement -- without turning its mere presence
into an outage. Attesting a provider is a deliberate
``scripts/ci/zdr_policy.py`` change, never an implicit consequence of a pin
advance.

Args:
discovered: Selected ``discover_all_models()`` result.
free_route_identities: Routes the orchestrator attested as zero-priced.
Expand All @@ -245,6 +259,14 @@ def _report_rows(
model_id = str(getattr(model, "model_id", None) or "")
if not provider or not model_id:
continue
if provider not in zdr_policy.PROVIDER_CREDENTIAL_NAMES:
print(
"discovery_row_skipped_unattested_provider "
f"provider={provider} model={model_id}",
file=sys.stderr,
flush=True,
)
continue
base_url = str(getattr(model, "chat_base_url", None) or zdr_policy.PROVIDER_BASE_URLS[provider])
credential_key = str(
getattr(model, "credential_name", None) or zdr_policy.PROVIDER_CREDENTIAL_NAMES[provider]
Expand Down
2 changes: 1 addition & 1 deletion scripts/ci/contextual_orchestrator_review_sidecar.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
# (fail-closed zero-cost) pool.
set -euo pipefail

ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-414f22973658c4ddc3d4320fcf7acd9b4e8ba991}"
ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-012beaacd0631f8cd3391c77744eeb626269b5de}"
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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
)

GATEWAY_MODEL = "contextual-orchestrator/orchestrator/free"
ORCH_PIN_SHA = "414f22973658c4ddc3d4320fcf7acd9b4e8ba991"
ORCH_PIN_SHA = "012beaacd0631f8cd3391c77744eeb626269b5de"


def _read(path: Path) -> str:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
"""An unattested discovery provider is excluded from the pool, not fatal.

``register_review_credentials(os.environ)`` registers every accepted provider
credential the sidecar job environment happens to carry, and the vendored
gateway ships provider sources this repository has not attested for retention
posture. Submitting such a row to
``contextual_orchestrator_review_policy.parse_discovery_report`` raises
``PolicyError``, which would take the whole review sidecar down rather than
merely refusing that provider. These tests pin the launcher-side exclusion and
the policy-side strictness it protects, so neither half can drift alone.
"""

from __future__ import annotations

import importlib
import io
import contextlib

from scripts.ci import zdr_policy
from scripts.ci.contextual_orchestrator_review_policy import (
PolicyError,
parse_discovery_report,
)

_launcher = importlib.import_module("scripts.ci.contextual_orchestrator_review_launcher")


class _Row:
"""Minimal stand-in for one ``contextual_orchestrator`` discovery result."""

def __init__(
self,
provider_name: str,
model_id: str,
chat_base_url: str = "",
credential_name: str = "",
auth_scheme: str = "",
) -> None:
"""Record the discovery fields ``_report_rows`` reads."""
self.provider_name = provider_name
self.model_id = model_id
self.chat_base_url = chat_base_url
self.credential_name = credential_name
self.auth_scheme = auth_scheme
self.evidence_only = False


def _report_rows(models: list[object]) -> tuple[list[dict[str, object]], str]:
"""Run the production row builder and return its rows plus captured stderr."""
captured = io.StringIO()
with contextlib.redirect_stderr(captured):
rows = _launcher._report_rows(models, frozenset())
return rows, captured.getvalue()


def test_opencode_zen_is_not_registered_in_the_org_zdr_policy_table() -> None:
"""The premise: the gateway's OpenCode sources have no org attestation."""
assert "opencode_zen" not in zdr_policy.PROVIDER_CREDENTIAL_NAMES
assert "opencode_go" not in zdr_policy.PROVIDER_CREDENTIAL_NAMES


def test_unattested_provider_row_is_dropped_with_a_bounded_diagnostic() -> None:
"""An unattested provider never reaches the policy, and says so once."""
rows, stderr = _report_rows(
[_Row("opencode_zen", "grok-code", "https://opencode.ai/zen/v1", "OPENCODE_ZEN_API_KEY", "Bearer")]
)
assert rows == []
assert (
"discovery_row_skipped_unattested_provider provider=opencode_zen model=grok-code"
in stderr
)


def test_the_diagnostic_carries_no_credential_value() -> None:
"""The bounded line names provider and model only -- never a secret."""
_rows, stderr = _report_rows(
[_Row("opencode_go", "some-model", "https://opencode.ai/zen/go/v1", "OPENCODE_ZEN_API_KEY", "Bearer")]
)
assert "OPENCODE_ZEN_API_KEY" not in stderr
assert stderr.count("discovery_row_skipped_unattested_provider") == 1


def test_an_attested_provider_alongside_it_still_reaches_the_pool() -> None:
"""Excluding one provider must not drop the rest of the discovery batch."""
rows, _stderr = _report_rows(
[
_Row("opencode_zen", "grok-code", "https://opencode.ai/zen/v1", "OPENCODE_ZEN_API_KEY", "Bearer"),
_Row(
"nvidia_nim",
"meta/llama-3.2-11b",
"https://integrate.api.nvidia.com/v1",
"NVIDIA_NIM_API_KEY",
"Bearer",
),
]
)
assert [row["provider"] for row in rows] == ["nvidia_nim"]
assert rows[0]["model"] == "meta/llama-3.2-11b"


def test_attested_rows_are_untouched_when_nothing_is_unattested() -> None:
"""The guard is inert for a batch the org policy table already covers."""
rows, stderr = _report_rows(
[
_Row(
"nvidia_nim",
"meta/llama-3.2-11b",
"https://integrate.api.nvidia.com/v1",
"NVIDIA_NIM_API_KEY",
"Bearer",
)
]
)
assert len(rows) == 1
assert "discovery_row_skipped_unattested_provider" not in stderr


def test_the_policy_keeps_raising_for_a_row_that_does_reach_it() -> None:
"""The launcher guard protects the policy; it does not relax the policy."""
report = {
"models": [
{
"provider": "opencode_zen",
"model": "grok-code",
"is_free": True,
"base_url": "https://opencode.ai/zen/v1",
"credential_key": "OPENCODE_ZEN_API_KEY",
"auth_scheme": "Bearer",
}
]
}
try:
parse_discovery_report(report)
except PolicyError as exc:
assert "not registered in the ZDR policy table" in str(exc)
else: # pragma: no cover - the policy must stay fail-closed
raise AssertionError("parse_discovery_report accepted an unattested provider")
Loading