diff --git a/scripts/ci/strix_required_workflow_smoke.sh b/scripts/ci/strix_required_workflow_smoke.sh index e2f1fda40a..0b5a4a54d4 100755 --- a/scripts/ci/strix_required_workflow_smoke.sh +++ b/scripts/ci/strix_required_workflow_smoke.sh @@ -21,7 +21,6 @@ full_gate_test="$repo_root/scripts/ci/test_strix_quick_gate.sh" sidecar_script="$repo_root/scripts/ci/contextual_orchestrator_review_sidecar.sh" token_loader_script="$repo_root/scripts/ci/load_contextual_orchestrator_token.sh" decision_record="$repo_root/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md" -agent_policy="$repo_root/AGENTS.md" failures=0 @@ -185,7 +184,6 @@ active_strix_models="$(sed -n -E 's/^[[:space:]]*STRIX_MODEL:[[:space:]]*([^#[:s assert_file_not_contains "$workflow_file" "STRIX_MODEL: contextual-orchestrator/orchestrator/auto" "Strix must not retain the paid-inclusive auto default route" assert_file_contains "$decision_record" "2026-08-30 amendment: Strix uses \`orchestrator/free\`" "The binding ADR amendment records the owner's explicit free-only override" assert_file_contains "$decision_record" "Zero Data Retention (ZDR)-compliant routes remain mandatory for private targets" "The binding ADR preserves private-target privacy" -assert_file_contains "$agent_policy" "Strix uses the zero-cost \`orchestrator/free\`" "Repository guidance agrees with the binding Strix route" assert_file_contains "$workflow_file" "provider_mode=contextual_orchestrator" "Strix workflow selects the contextual-orchestrator provider mode" assert_file_contains "$workflow_file" "STRIX_FALLBACK_MODELS: \"\"" "Strix delegates provider discovery and failover to the gateway" assert_file_not_contains "$workflow_file" "Resolve live NVIDIA NIM Strix models" "Strix does not resolve a direct provider outside the gateway" diff --git a/tests/test_strix_required_smoke_availability.py b/tests/test_strix_required_smoke_availability.py new file mode 100644 index 0000000000..fe204e3a0e --- /dev/null +++ b/tests/test_strix_required_smoke_availability.py @@ -0,0 +1,83 @@ +"""Regression tests for bounded Strix required-smoke availability.""" + +from __future__ import annotations + +from pathlib import Path +import shutil +import subprocess +import tempfile +import unittest + +ROOT = Path(__file__).resolve().parents[1] +SMOKE = ROOT / "scripts/ci/strix_required_workflow_smoke.sh" +WORKFLOW = ROOT / ".github/workflows/strix.yml" +SIDECAR = ROOT / "scripts/ci/contextual_orchestrator_review_sidecar.sh" +TOKEN_LOADER = ROOT / "scripts/ci/load_contextual_orchestrator_token.sh" +GATE = ROOT / "scripts/ci/strix_quick_gate.sh" +GATE_TEST = ROOT / "scripts/ci/test_strix_quick_gate.sh" +DECISION_RECORD = ROOT / "docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md" +AGENT_POLICY = ROOT / "AGENTS.md" + + +class StrixRequiredSmokeAvailabilityTest(unittest.TestCase): + """Keep consumer scans independent from non-executable guidance wording.""" + + @staticmethod + def _copy_smoke_fixture(root: Path) -> None: + """Copy real smoke dependencies plus the separately checked guidance.""" + for source in ( + SMOKE, + WORKFLOW, + SIDECAR, + TOKEN_LOADER, + GATE, + GATE_TEST, + DECISION_RECORD, + AGENT_POLICY, + ): + destination = root / source.relative_to(ROOT) + destination.parent.mkdir(parents=True, exist_ok=True) + shutil.copy2(source, destination) + + def test_agent_guidance_prose_cannot_block_consumer_scans(self) -> None: + """Changing AGENTS prose must not stop an otherwise valid Strix scan.""" + with tempfile.TemporaryDirectory() as temp_dir: + root = Path(temp_dir) + self._copy_smoke_fixture(root) + (root / "AGENTS.md").write_text( + "# Agent guidance\n\nThis prose is not an executable Strix contract.\n", + encoding="utf-8", + ) + + result = subprocess.run( + ["bash", str(root / SMOKE.relative_to(ROOT))], + cwd=root, + text=True, + capture_output=True, + check=False, + timeout=10, + ) + + output = result.stdout + result.stderr + self.assertEqual(result.returncode, 0, output) + self.assertIn("Strix required workflow smoke test passed.", output) + + def test_repository_guidance_still_documents_the_free_route(self) -> None: + """Central quality tests, not consumer runtime, keep guidance aligned.""" + paragraphs = ( + " ".join(paragraph.split()) + for paragraph in AGENT_POLICY.read_text(encoding="utf-8").split("\n\n") + ) + self.assertTrue( + any( + "Strix" in paragraph + and "zero-cost" in paragraph + and "`orchestrator/free`" in paragraph + for paragraph in paragraphs + ), + "AGENTS.md must document Strix on the zero-cost orchestrator/free route", + ) + + +if __name__ == "__main__": + unittest.main()