From 1fd8e6bb0215f23772a97b07e6558cbd90ecf7d4 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 16 Aug 2026 22:28:03 +0900 Subject: [PATCH 1/4] fix(strix): trigger quality CI on dependency manifest updates Replay the unique Strix lock-trigger contract onto current main so a manifest-only lock change cannot skip install, policy, and full-suite evidence. --- .../strix-changed-path-quality-ci.yml | 2 ++ CHANGELOG.md | 1 + .../strix-dependency-manifest-trigger.md | 26 +++++++++++++++++++ .../test_strix_workflow_dependency_hashes.py | 9 +++++++ 4 files changed, 38 insertions(+) create mode 100644 docs/doctoring/strix-dependency-manifest-trigger.md diff --git a/.github/workflows/strix-changed-path-quality-ci.yml b/.github/workflows/strix-changed-path-quality-ci.yml index 75e9b7d8e..cfc76a8d8 100644 --- a/.github/workflows/strix-changed-path-quality-ci.yml +++ b/.github/workflows/strix-changed-path-quality-ci.yml @@ -8,6 +8,8 @@ on: - "CHANGELOG.md" - "docs/doctoring/strix-legal-git-paths.md" - "docs/doctoring/strix-quality-timeout-fixtures.md" + - "docs/doctoring/strix-dependency-manifest-trigger.md" + - "requirements-strix-ci-hashes.txt" - "scripts/ci/strix_quick_gate.sh" - "scripts/ci/test_strix_quick_gate.sh" - "tests/test_strix_changed_path_policy.py" diff --git a/CHANGELOG.md b/CHANGELOG.md index fd1aebf43..dbb80797b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ Semantic Versioning where the repository publishes a release. - Download the pinned `uv` 0.12.1 exporter from the official GitHub Releases URL instead of `releases.astral.sh`, which now returns HTTP 403 and blocks org-wide OpenCode `coverage-evidence`. The SHA-256 pin is unchanged. The opener may follow one hop onto `release-assets.githubusercontent.com` or `objects.githubusercontent.com` and still rejects every other host, userinfo, non-HTTPS scheme, and nondefault port (ContextualWisdomLab/.github#1109). - Compared the trusted `uv` executable's post-install `--version` output against the real GitHub Releases build's full string, `uv 0.12.1 (x86_64-unknown-linux-gnu)`, instead of the bare `uv 0.12.1` the prior check required; the genuine release binary always prints the target triple, so every installation was failing the pin check immediately after the archive download itself was fixed (ContextualWisdomLab/.github#1109). - Excluded relative `-r` and `--requirement` referrers from generated flat base-lock publication while retaining bounded include syntax diagnostics and discovering independently complete direct `.txt` children of `requirements` directories. +- Bound `requirements-strix-ci-hashes.txt` to the permanent Strix changed-path quality workflow so a manifest-only lock update cannot skip the install, policy, and full-suite contract. - Refused a conflict-scope repository root whose immediate parent is a symbolic link, so a swapped parent cannot redirect the canonical worktree after the last-component check (CWE-367). - Bounded the Strix quality self-test's deterministic timeout fixtures to 3-second process and 5-second fake-sleep budgets so exact-head policy evidence completes inside the existing job limit without changing production Strix scanner timeouts, providers, credentials, or review semantics. - Allowed commas and ASCII parentheses in the bounded Strix changed-file path policy so legal tracked Packrat fixtures can receive exact-head security analysis, while rejecting raw `..` components before normalization and keeping controls, backslashes, whitespace ambiguity, and shell punctuation fail-closed. diff --git a/docs/doctoring/strix-dependency-manifest-trigger.md b/docs/doctoring/strix-dependency-manifest-trigger.md new file mode 100644 index 000000000..d5ca08267 --- /dev/null +++ b/docs/doctoring/strix-dependency-manifest-trigger.md @@ -0,0 +1,26 @@ +# Strix dependency-manifest quality trigger + +## Incident and buyer impact + +`requirements-strix-ci-hashes.txt` is executable supply-chain input for the +organization-required Strix gate. The permanent changed-path quality +workflow did not list that file. A Dependabot lock-only pull request could +therefore merge without running the Strix install, policy, shell-regression, +and full-suite contract. + +## Decision + +Add the exact repository-root manifest path to +`.github/workflows/strix-changed-path-quality-ci.yml` and bind it with +`test_strix_workflow_reruns_when_dependency_manifest_changes`. Scanner +models, credentials, timeouts, and result semantics are unchanged. + +## References + +National Institute of Standards and Technology. (2024). *Cybersecurity +supply chain risk management practices for systems and organizations* +(NIST Special Publication 800-161 Rev. 1). +https://doi.org/10.6028/NIST.SP.800-161r1 + +The Linux Foundation. (2023). *SLSA: Supply-chain levels for software +artifacts* (Version 1.0). https://slsa.dev/spec/v1.0/ diff --git a/tests/test_strix_workflow_dependency_hashes.py b/tests/test_strix_workflow_dependency_hashes.py index e2509c18b..c549cdccd 100644 --- a/tests/test_strix_workflow_dependency_hashes.py +++ b/tests/test_strix_workflow_dependency_hashes.py @@ -40,6 +40,15 @@ def test_strix_workflow_reruns_when_hash_contract_changes() -> None: assert ' - "tests/test_strix_workflow_dependency_hashes.py"' in workflow +def test_strix_workflow_reruns_when_dependency_manifest_changes() -> None: + """Changing the Strix dependency lock must trigger its install contract.""" + workflow = WORKFLOW.read_text(encoding="utf-8") + + assert (ROOT / "requirements-strix-ci-hashes.txt").is_file() + assert ' - "requirements-strix-ci-hashes.txt"' in workflow + assert ' - "docs/doctoring/strix-dependency-manifest-trigger.md"' in workflow + + def test_strix_workflow_rejects_branch_selected_manual_dispatch() -> None: """Central executable workflows load no branch-selected manual source.""" workflow = WORKFLOW.read_text(encoding="utf-8") From 14f5ac68ba0280c89bb94a6ef839b38fe2528767 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 22 Aug 2026 04:46:15 +0900 Subject: [PATCH 2/4] ci: refresh audit and scheduler contracts --- requirements-pip-audit-ci-hashes.txt | 6 +++--- scripts/ci/test_strix_quick_gate.sh | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/requirements-pip-audit-ci-hashes.txt b/requirements-pip-audit-ci-hashes.txt index ade197a49..0ae099d8f 100644 --- a/requirements-pip-audit-ci-hashes.txt +++ b/requirements-pip-audit-ci-hashes.txt @@ -213,9 +213,9 @@ packaging==26.2 \ # via # pip-audit # pip-requirements-parser -pip==26.1.2 \ - --hash=sha256:382ff9f685ee3bc25864f820aa50505825f10f5458ffff07e30a6d96e5715cab \ - --hash=sha256:f49cd134c61cf2fd75e0ce2676db03e4054504a5a4986d00f8299ae632dc4605 +pip==26.2.1 \ + --hash=sha256:71138adf1f4ca900cdb7d289c21b7494329f2332b6d85f0e1c42108c0384ed3e \ + --hash=sha256:f6ad667e89a1fe78046c8f13232b247200f5258d7828f3f7883d660878e0813f # via pip-api pip-api==0.0.34 \ --hash=sha256:8b2d7d7c37f2447373aa2cf8b1f60a2f2b27a84e1e9e0294a3f6ef10eb3ba6bb \ diff --git a/scripts/ci/test_strix_quick_gate.sh b/scripts/ci/test_strix_quick_gate.sh index ac9ce1d8b..04f58bf31 100755 --- a/scripts/ci/test_strix_quick_gate.sh +++ b/scripts/ci/test_strix_quick_gate.sh @@ -1506,8 +1506,8 @@ assert_pr_review_merge_scheduler_uses_github_actions_bot_token() { assert_file_contains "$workflow_file" "github.event_name == 'pull_request_target' && format('pr-{0}', github.event.pull_request.number)" "scheduler scopes pull_request_target concurrency to the active PR" assert_file_contains "$workflow_file" "github.event_name == 'workflow_run' && github.event.workflow_run.pull_requests[0].number && format('pr-{0}', github.event.workflow_run.pull_requests[0].number)" "scheduler scopes workflow_run concurrency to the completed review PR" assert_file_contains "$workflow_file" "github.event_name == 'schedule' && format('schedule-{0}', github.event.schedule)" "scheduler isolates the 15-minute organization sweep from the separate 30-minute scheduled scan" - assert_file_contains "$workflow_file" "github.event_name == 'repository_dispatch' && github.run_id" "scheduler keeps manual queue scans isolated per run" - assert_file_contains "$workflow_file" "cancel-in-progress: \${{ github.event_name == 'pull_request_target' || github.event_name == 'pull_request_review' || github.event_name == 'repository_dispatch' }}" "scheduler cancels stale PR/review/manual queue scans instead of accumulating merge/update attempts" + assert_file_contains "$workflow_file" "github.event_name == 'repository_dispatch' && format('repo-dispatch-{0}', github.repository)" "scheduler keeps manual queue scans isolated per repository dispatch target" + assert_file_contains "$workflow_file" "cancel-in-progress: \${{ github.event_name == 'pull_request_target' || github.event_name == 'pull_request_review' || github.event_name == 'repository_dispatch' || (github.event_name == 'workflow_run' && !github.event.workflow_run.pull_requests[0].number) }}" "scheduler cancels stale PR/review/manual queue scans instead of accumulating merge/update attempts" assert_file_contains "$workflow_file" "timeout-minutes: 60" "organization sweep has enough headroom to finish the complete repository walk" assert_file_contains "$workflow_file" "ORG_SWEEP_TRIGGER_REVIEWS: \${{ github.event_name == 'schedule' ||" "scheduled organization sweeps retry missing current-head OpenCode reviews" assert_file_contains "$workflow_file" "ORG_SWEEP_ENABLE_AUTO_MERGE: \${{ github.event_name == 'schedule' ||" "scheduled organization sweeps merge approved current heads" From 0caebde71764c0985b588e40b9e55c72193e16ea Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 23 Aug 2026 17:26:08 +0900 Subject: [PATCH 3/4] fix(strix): preflight production dependency lock --- .../strix-changed-path-quality-ci.yml | 18 ++++++++++++++++++ .../strix-dependency-manifest-trigger.md | 7 +++++-- tests/test_strix_workflow_dependency_hashes.py | 14 ++++++++++++++ 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/.github/workflows/strix-changed-path-quality-ci.yml b/.github/workflows/strix-changed-path-quality-ci.yml index cfc76a8d8..e3d814a41 100644 --- a/.github/workflows/strix-changed-path-quality-ci.yml +++ b/.github/workflows/strix-changed-path-quality-ci.yml @@ -71,3 +71,21 @@ jobs: python -m compileall -q tests/test_strix_changed_path_policy.py tests/test_strix_workflow_dependency_hashes.py tests/test_strix_quality_timeout_fixture_budget.py bash -n scripts/ci/strix_quick_gate.sh git diff --exit-code + + - name: Set up production Strix lock Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.13" + + - name: Preflight exact hashed Strix dependency closure + env: + PIP_DISABLE_PIP_VERSION_CHECK: "1" + PIP_NO_INPUT: "1" + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + python -m pip install \ + --dry-run \ + --ignore-installed \ + --only-binary=:all: \ + --require-hashes \ + -r requirements-strix-ci-hashes.txt diff --git a/docs/doctoring/strix-dependency-manifest-trigger.md b/docs/doctoring/strix-dependency-manifest-trigger.md index d5ca08267..83f46e500 100644 --- a/docs/doctoring/strix-dependency-manifest-trigger.md +++ b/docs/doctoring/strix-dependency-manifest-trigger.md @@ -12,8 +12,11 @@ and full-suite contract. Add the exact repository-root manifest path to `.github/workflows/strix-changed-path-quality-ci.yml` and bind it with -`test_strix_workflow_reruns_when_dependency_manifest_changes`. Scanner -models, credentials, timeouts, and result semantics are unchanged. +`test_strix_workflow_reruns_when_dependency_manifest_changes`. The same gate +uses production Python 3.13 to perform a binary-only, hash-enforced dry-run of +the complete lock, so a corrupt, incomplete, or incompatible manifest cannot +pass merely because it triggered the workflow. Scanner models, credentials, +timeouts, and result semantics are unchanged. ## References diff --git a/tests/test_strix_workflow_dependency_hashes.py b/tests/test_strix_workflow_dependency_hashes.py index c549cdccd..764bcf509 100644 --- a/tests/test_strix_workflow_dependency_hashes.py +++ b/tests/test_strix_workflow_dependency_hashes.py @@ -49,6 +49,20 @@ def test_strix_workflow_reruns_when_dependency_manifest_changes() -> None: assert ' - "docs/doctoring/strix-dependency-manifest-trigger.md"' in workflow +def test_strix_workflow_preflights_dependency_manifest_hashes() -> None: + """The specialized gate resolves the production lock with enforced hashes.""" + + workflow = WORKFLOW.read_text(encoding="utf-8") + + assert 'python-version: "3.13"' in workflow + assert "python -m pip install \\" in workflow + assert "--dry-run \\" in workflow + assert "--ignore-installed \\" in workflow + assert "--only-binary=:all: \\" in workflow + assert "--require-hashes \\" in workflow + assert "-r requirements-strix-ci-hashes.txt" in workflow + + def test_strix_workflow_rejects_branch_selected_manual_dispatch() -> None: """Central executable workflows load no branch-selected manual source.""" workflow = WORKFLOW.read_text(encoding="utf-8") From adbaaa142267fe5b58a94ddc835ce6a33c15290c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 23 Aug 2026 18:32:06 +0900 Subject: [PATCH 4/4] fix(strix): mirror production lock semantics --- .../workflows/strix-changed-path-quality-ci.yml | 2 +- CHANGELOG.md | 3 +++ .../strix-dependency-manifest-trigger.md | 15 +++++++++------ tests/test_strix_workflow_dependency_hashes.py | 16 ++++++++++------ 4 files changed, 23 insertions(+), 13 deletions(-) diff --git a/.github/workflows/strix-changed-path-quality-ci.yml b/.github/workflows/strix-changed-path-quality-ci.yml index e3d814a41..f75140afe 100644 --- a/.github/workflows/strix-changed-path-quality-ci.yml +++ b/.github/workflows/strix-changed-path-quality-ci.yml @@ -86,6 +86,6 @@ jobs: python -m pip install \ --dry-run \ --ignore-installed \ - --only-binary=:all: \ + --no-deps \ --require-hashes \ -r requirements-strix-ci-hashes.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index 11eca8ea3..51854d065 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,6 +51,9 @@ Semantic Versioning where the repository publishes a release. ### Fixed +- Bound the Strix changed-path quality gate to the complete production hash + lock and mirrored production's deliberate `--no-deps` security-override + boundary without rejecting source distributions that production accepts. - Restored the hourly organization commercial-readiness loop after five consecutive startup failures caused by its mandatory but unprovisioned `PR_REVIEW_MERGE_TOKEN`. Protected scheduled jobs now prefer that maintainer diff --git a/docs/doctoring/strix-dependency-manifest-trigger.md b/docs/doctoring/strix-dependency-manifest-trigger.md index 83f46e500..7d01041b8 100644 --- a/docs/doctoring/strix-dependency-manifest-trigger.md +++ b/docs/doctoring/strix-dependency-manifest-trigger.md @@ -13,10 +13,13 @@ and full-suite contract. Add the exact repository-root manifest path to `.github/workflows/strix-changed-path-quality-ci.yml` and bind it with `test_strix_workflow_reruns_when_dependency_manifest_changes`. The same gate -uses production Python 3.13 to perform a binary-only, hash-enforced dry-run of -the complete lock, so a corrupt, incomplete, or incompatible manifest cannot -pass merely because it triggered the workflow. Scanner models, credentials, -timeouts, and result semantics are unchanged. +uses production Python 3.13 to perform a hash-enforced dry-run of every pinned +lock entry. It mirrors production's deliberate `--no-deps` boundary because +the reviewed `cryptography==50.0.0` security override is newer than the range +declared by `strix-agent==1.5.3`; every installed entry is still version- and +hash-pinned. The preflight permits source distributions because production +does too, so it does not invent a stricter platform contract. Scanner models, +credentials, timeouts, and result semantics are unchanged. ## References @@ -25,5 +28,5 @@ supply chain risk management practices for systems and organizations* (NIST Special Publication 800-161 Rev. 1). https://doi.org/10.6028/NIST.SP.800-161r1 -The Linux Foundation. (2023). *SLSA: Supply-chain levels for software -artifacts* (Version 1.0). https://slsa.dev/spec/v1.0/ +Open Source Security Foundation. (2025). *SLSA specification version 1.2*. +https://slsa.dev/spec/v1.2/ diff --git a/tests/test_strix_workflow_dependency_hashes.py b/tests/test_strix_workflow_dependency_hashes.py index 764bcf509..dbdc74548 100644 --- a/tests/test_strix_workflow_dependency_hashes.py +++ b/tests/test_strix_workflow_dependency_hashes.py @@ -53,14 +53,18 @@ def test_strix_workflow_preflights_dependency_manifest_hashes() -> None: """The specialized gate resolves the production lock with enforced hashes.""" workflow = WORKFLOW.read_text(encoding="utf-8") + preflight = workflow.split( + " - name: Preflight exact hashed Strix dependency closure\n", 1 + )[1].split("\n - name:", 1)[0] assert 'python-version: "3.13"' in workflow - assert "python -m pip install \\" in workflow - assert "--dry-run \\" in workflow - assert "--ignore-installed \\" in workflow - assert "--only-binary=:all: \\" in workflow - assert "--require-hashes \\" in workflow - assert "-r requirements-strix-ci-hashes.txt" in workflow + assert "python -m pip install \\" in preflight + assert "--dry-run \\" in preflight + assert "--ignore-installed \\" in preflight + assert "--no-deps \\" in preflight + assert "--only-binary=:all:" not in preflight + assert "--require-hashes \\" in preflight + assert "-r requirements-strix-ci-hashes.txt" in preflight def test_strix_workflow_rejects_branch_selected_manual_dispatch() -> None: