From 8344a2b2e24d6952fe2442e68b9ee514cb1d7ee8 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 5 Aug 2026 21:08:28 +0900 Subject: [PATCH 01/33] test(coverage): require requirements directory lock discovery --- ...irements_directory_lock_materialization.py | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 tests/test_requirements_directory_lock_materialization.py diff --git a/tests/test_requirements_directory_lock_materialization.py b/tests/test_requirements_directory_lock_materialization.py new file mode 100644 index 000000000..229ab86c0 --- /dev/null +++ b/tests/test_requirements_directory_lock_materialization.py @@ -0,0 +1,64 @@ +"""Regression contracts for trusted locks kept in a requirements directory.""" + +from __future__ import annotations + +import subprocess +from pathlib import Path, PurePosixPath + +from scripts.ci import materialize_base_python_requirements as materializer + + +def _git(repo: Path, *args: str) -> str: + """Run one deterministic Git command in a temporary fixture repository.""" + return subprocess.run( + ["git", "-C", str(repo), *args], + check=True, + capture_output=True, + text=True, + ).stdout.strip() + + +def test_requirements_directory_txt_is_a_candidate_lock_path() -> None: + """A direct ``requirements/*.txt`` lock is discoverable by its safe path.""" + assert materializer._is_candidate_lock_path(PurePosixPath("requirements/ci.txt")) + assert materializer._is_candidate_lock_path( + PurePosixPath("services/scoring_service/requirements/package.txt") + ) + assert not materializer._is_candidate_lock_path( + PurePosixPath("requirements/nested/ci.txt") + ) + assert not materializer._is_candidate_lock_path(PurePosixPath("docs/ci.txt")) + + +def test_materializes_hash_pinned_requirements_directory_lock( + tmp_path: Path, +) -> None: + """The exact base ``requirements/ci.txt`` closure reaches offline coverage.""" + repo = tmp_path / "repo" + requirements_dir = repo / "requirements" + requirements_dir.mkdir(parents=True) + _git(repo, "init") + _git(repo, "config", "user.name", "Test") + _git(repo, "config", "user.email", "test@example.invalid") + + (requirements_dir / "ci.txt").write_text( + "numpy==2.5.1 --hash=sha256:" + ("a" * 64) + "\n", + encoding="utf-8", + ) + (requirements_dir / "ci.in").write_text("numpy>=2\n", encoding="utf-8") + (requirements_dir / "notes.txt").write_text( + "human-readable notes only\n", encoding="utf-8" + ) + _git(repo, "add", ".") + _git(repo, "commit", "-m", "base") + base_sha = _git(repo, "rev-parse", "HEAD") + + output = tmp_path / "output" + manifest = materializer.materialize(repo, base_sha, output) + + assert manifest == [ + {"file": "requirements-000.txt", "source": "requirements/ci.txt"} + ] + assert (output / "requirements-000.txt").read_text(encoding="utf-8").startswith( + "numpy==2.5.1" + ) From 28bd2c982ca7e3b79d05f5badc2c995313816dcd Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 5 Aug 2026 21:11:54 +0900 Subject: [PATCH 02/33] ci: stage bounded requirements-directory lock repair --- .../repair-requirements-directory-locks.yml | 278 ++++++++++++++++++ 1 file changed, 278 insertions(+) create mode 100644 .github/workflows/repair-requirements-directory-locks.yml diff --git a/.github/workflows/repair-requirements-directory-locks.yml b/.github/workflows/repair-requirements-directory-locks.yml new file mode 100644 index 000000000..7da8cfd1e --- /dev/null +++ b/.github/workflows/repair-requirements-directory-locks.yml @@ -0,0 +1,278 @@ +name: Repair requirements-directory lock discovery + +on: + push: + branches: + - fix/coverage-materialize-requirements-directory-locks + paths: + - .github/workflows/repair-requirements-directory-locks.yml + +permissions: + contents: write + +concurrency: + group: repair-requirements-directory-locks + cancel-in-progress: false + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + +jobs: + repair: + if: >- + github.repository == 'ContextualWisdomLab/.github' && + github.actor != 'github-actions[bot]' && + github.ref == 'refs/heads/fix/coverage-materialize-requirements-directory-locks' + runs-on: ubuntu-24.04 + timeout-minutes: 25 + steps: + - name: Harden runner + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 + with: + egress-policy: audit + + - name: Check out exact trigger + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + ref: ${{ github.sha }} + fetch-depth: 2 + persist-credentials: false + + - name: Verify exact RED parent + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + test "$(git rev-parse HEAD^)" = "8344a2b2e24d6952fe2442e68b9ee514cb1d7ee8" + test "$(git rev-parse HEAD)" = "$GITHUB_SHA" + + - name: Set up current stable Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.14" + cache: pip + cache-dependency-path: requirements-opencode-review-ci-hashes.txt + + - name: Install hash-locked quality tooling + run: >- + python -m pip install --disable-pip-version-check --require-hashes + -r requirements-opencode-review-ci-hashes.txt + + - name: Preserve the failing nested-lock contract + shell: bash --noprofile --norc {0} + run: | + set -euo pipefail + set +e + python -m pytest -q \ + tests/test_requirements_directory_lock_materialization.py \ + >"${RUNNER_TEMP}/requirements-directory-red.log" 2>&1 + status=$? + set -e + cat "${RUNNER_TEMP}/requirements-directory-red.log" + test "$status" -ne 0 + grep -F '_is_candidate_lock_path' \ + "${RUNNER_TEMP}/requirements-directory-red.log" + + - name: Implement the reviewed direct-child discovery boundary + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + python - <<'PY' + from pathlib import Path + + source_path = Path("scripts/ci/materialize_base_python_requirements.py") + source = source_path.read_text(encoding="utf-8") + old_function = '''def _is_candidate_lock_name(name: str) -> bool: + """Return whether a file name is a possible pip requirements lock.""" + return name == "requirements.lock" or ( + fnmatch.fnmatch(name, "requirements*.txt") + and not fnmatch.fnmatch(name, "requirements-*-ci-hashes.txt") + ) + '''.replace(" ", "") + new_function = old_function + ''' + + def _is_candidate_lock_path(path: pathlib.PurePosixPath) -> bool: + """Return whether one safe tracked path can name a pip requirements lock. + + In addition to conventional ``requirements*.txt`` names, repositories + commonly keep concrete environment closures as direct children such as + ``requirements/ci.txt`` or ``service/requirements/package.txt``. Only + direct ``.txt`` children of a directory named ``requirements`` gain this + path-based eligibility; content must still pass the independent complete + hash-pin validation before it reaches the trusted image build context. + """ + return _is_candidate_lock_name(path.name) or ( + path.suffix == ".txt" and path.parent.name == "requirements" + ) + '''.replace(" ", "") + if source.count(old_function) != 1: + raise SystemExit("expected one candidate-name function anchor") + source = source.replace(old_function, new_function, 1) + old_call = " if _is_candidate_lock_name(candidate.name):\n" + new_call = " if _is_candidate_lock_path(candidate):\n" + if source.count(old_call) != 1: + raise SystemExit("expected one candidate-path call anchor") + source_path.write_text(source.replace(old_call, new_call, 1), encoding="utf-8") + + workflow_path = Path( + ".github/workflows/trusted-uv-materializer-quality-ci.yml" + ) + workflow = workflow_path.read_text(encoding="utf-8") + path_anchor = ' - "tests/test_materialize*.py"\n' + path_replacement = ( + path_anchor + + ' - "tests/test_requirements_directory_lock_materialization.py"\n' + ) + if workflow.count(path_anchor) != 2: + raise SystemExit("expected two materializer path-filter anchors") + workflow = workflow.replace(path_anchor, path_replacement) + pytest_anchor = ( + " tests/test_materialize_uv_export_hash_contract.py \\\n" + ) + pytest_replacement = ( + pytest_anchor + + " tests/test_requirements_directory_lock_materialization.py \\\n" + ) + if workflow.count(pytest_anchor) != 1: + raise SystemExit("expected one focused pytest anchor") + workflow = workflow.replace(pytest_anchor, pytest_replacement, 1) + compile_anchor = ( + " tests/test_materialize_uv_export_hash_contract.py \\\n" + ) + compile_replacement = ( + compile_anchor + + " tests/test_requirements_directory_lock_materialization.py \\\n" + ) + if workflow.count(compile_anchor) != 1: + raise SystemExit("expected one compile target anchor after pytest update") + workflow_path.write_text( + workflow.replace(compile_anchor, compile_replacement, 1), + encoding="utf-8", + ) + + doctoring = Path( + "docs/doctoring/trusted-requirements-directory-lock-discovery.md" + ) + doctoring.write_text( + """# Trusted requirements-directory lock discovery + +## Decision + +The central OpenCode coverage image materializes dependency closures only from +regular files in the authenticated pull-request base commit. In addition to the +conventional `requirements*.txt` and `requirements.lock` names, it recognizes a +`.txt` file that is a **direct child** of a directory named `requirements`, such +as `requirements/ci.txt` or `services/scoring_service/requirements/package.txt`. + +The path rule grants candidate status only. The existing content boundary still +requires nonempty hash-pinned logical requirements, the manifest records the +exact trusted source path, and the image installer preflights each candidate as +an independently installable `pip --require-hashes` closure. Unpinned notes, +input files, nested descendants, symlinks, pull-request-only files, and malformed +Git tree entries remain excluded. + +## Operational reason + +Concrete environment locks are frequently organized below a `requirements` +directory and may use role names such as `ci.txt` or `package.txt`. Ignoring those +safe base-owned locks leaves the isolated coverage environment without runtime +dependencies even though the repository already maintains a complete generated +closure. The result is a misleading collection failure such as a missing NumPy +import rather than evidence about the changed production code. + +## Verification + +- A failing contract first proved that `requirements/ci.txt` was undiscoverable. +- Direct `requirements/*.txt` and nested-service equivalents are accepted. +- A deeper `requirements/nested/ci.txt` path and unrelated `docs/ci.txt` remain + ineligible. +- Only the hash-pinned candidate is emitted from a realistic temporary Git base; + unpinned `.in` and human-readable `.txt` files remain absent. +- The focused materializer suite, complete central suite, statement/branch + coverage, docstring gate, compilation, and exact-head security workflows are + required before merge. + +## References + +Python Packaging Authority. (2026). *Install requires vs requirements files*. +Python Packaging User Guide. +https://packaging.python.org/en/latest/discussions/install-requires-vs-requirements/ + +Python Packaging Authority. (2026). *Repeatable installs*. pip documentation. +https://pip.pypa.io/en/stable/topics/repeatable-installs/ + +Python Packaging Authority. (2026). *Requirements file format*. pip +documentation. +https://pip.pypa.io/en/stable/reference/requirements-file-format/ +""", + encoding="utf-8", + ) + + changelog_path = Path("CHANGELOG.md") + changelog = changelog_path.read_text(encoding="utf-8") + fixed_anchor = "### Fixed\n\n" + entry = ( + "- Materialize complete hash-pinned `requirements/ci.txt` and other " + "direct `requirements/*.txt` base-owned closures so isolated " + "OpenCode coverage can import repository runtime dependencies " + "without trusting pull-request metadata or broadening network access.\n" + ) + if changelog.count(fixed_anchor) != 1: + raise SystemExit("expected one Unreleased Fixed anchor") + changelog_path.write_text( + changelog.replace(fixed_anchor, fixed_anchor + entry, 1), + encoding="utf-8", + ) + PY + git diff --check + + - name: Run focused and complete quality gates + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + cat >"${RUNNER_TEMP}/requirements-directory-coveragerc" <<'EOF' + [run] + branch = True + include = + scripts/ci/materialize_base_python_requirements.py + + [report] + fail_under = 100 + show_missing = True + EOF + export COVERAGE_RCFILE="${RUNNER_TEMP}/requirements-directory-coveragerc" + python -m coverage erase + python -m coverage run -m pytest -q \ + tests/test_materialize_base_python_requirements.py \ + tests/test_materialize_uv_export_hash_contract.py \ + tests/test_requirements_directory_lock_materialization.py \ + tests/test_trusted_uv_download_contract.py \ + tests/test_trusted_uv_portability_and_streaming.py \ + tests/test_uv_export_isolation_contract.py \ + tests/test_uv_redirect_and_coverage_contract.py \ + tests/test_uv_redirect_boundary.py \ + tests/test_uv_workspace_fail_closed.py \ + tests/test_trusted_uv_materializer_quality_workflow_contract.py + python -m coverage report + unset COVERAGE_RCFILE + python -m coverage erase + python -m coverage run --branch -m pytest tests -q + python -m coverage report --fail-under=100 + python -m interrogate --fail-under 100 \ + scripts/ci/materialize_base_python_requirements.py + python -m compileall -q \ + scripts/ci/materialize_base_python_requirements.py \ + tests/test_requirements_directory_lock_materialization.py + + - name: Publish verified product change and remove temporary machinery + env: + PUSH_TOKEN: ${{ github.token }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + git rm .github/workflows/repair-requirements-directory-locks.yml + git diff --check + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add -A + git diff --cached --quiet && exit 1 + git commit -m "fix(coverage): materialize requirements directory locks" + auth_header="$(printf 'x-access-token:%s' "$PUSH_TOKEN" | base64 | tr -d '\n')" + echo "::add-mask::$auth_header" + git -c http.extraheader="AUTHORIZATION: basic ${auth_header}" \ + push origin "HEAD:refs/heads/fix/coverage-materialize-requirements-directory-locks" From 9d24c19d860cbd8fef5e4fc60501fc156f057abb Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 5 Aug 2026 21:47:48 +0900 Subject: [PATCH 03/33] ci(coverage): harden requirements lock repair --- .../repair-requirements-directory-locks.yml | 58 +++++++++++++------ 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/.github/workflows/repair-requirements-directory-locks.yml b/.github/workflows/repair-requirements-directory-locks.yml index 7da8cfd1e..9462aba7d 100644 --- a/.github/workflows/repair-requirements-directory-locks.yml +++ b/.github/workflows/repair-requirements-directory-locks.yml @@ -8,11 +8,11 @@ on: - .github/workflows/repair-requirements-directory-locks.yml permissions: - contents: write + contents: read concurrency: group: repair-requirements-directory-locks - cancel-in-progress: false + cancel-in-progress: true env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true @@ -21,10 +21,12 @@ jobs: repair: if: >- github.repository == 'ContextualWisdomLab/.github' && - github.actor != 'github-actions[bot]' && + github.actor == 'seonghobae' && github.ref == 'refs/heads/fix/coverage-materialize-requirements-directory-locks' + permissions: + contents: write runs-on: ubuntu-24.04 - timeout-minutes: 25 + timeout-minutes: 30 steps: - name: Harden runner uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 @@ -32,17 +34,29 @@ jobs: egress-policy: audit - name: Check out exact trigger - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ github.sha }} - fetch-depth: 2 + fetch-depth: 0 persist-credentials: false - - name: Verify exact RED parent + - name: Verify exact RED lineage and bounded setup delta + env: + RED_CONTRACT_SHA: 8344a2b2e24d6952fe2442e68b9ee514cb1d7ee8 shell: bash --noprofile --norc -e -o pipefail {0} run: | - test "$(git rev-parse HEAD^)" = "8344a2b2e24d6952fe2442e68b9ee514cb1d7ee8" test "$(git rev-parse HEAD)" = "$GITHUB_SHA" + git cat-file -e "${RED_CONTRACT_SHA}^{commit}" + git merge-base --is-ancestor "$RED_CONTRACT_SHA" "$GITHUB_SHA" + mapfile -t changed_paths < <(git diff --name-only "$RED_CONTRACT_SHA" "$GITHUB_SHA" | sort) + expected_paths=( + ".github/workflows/repair-requirements-directory-locks.yml" + "tests/test_requirements_directory_lock_materialization.py" + ) + test "${#changed_paths[@]}" -eq "${#expected_paths[@]}" + for index in "${!expected_paths[@]}"; do + test "${changed_paths[$index]}" = "${expected_paths[$index]}" + done - name: Set up current stable Python uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 @@ -52,6 +66,7 @@ jobs: cache-dependency-path: requirements-opencode-review-ci-hashes.txt - name: Install hash-locked quality tooling + shell: bash --noprofile --norc -e -o pipefail {0} run: >- python -m pip install --disable-pip-version-check --require-hashes -r requirements-opencode-review-ci-hashes.txt @@ -59,15 +74,16 @@ jobs: - name: Preserve the failing nested-lock contract shell: bash --noprofile --norc {0} run: | - set -euo pipefail - set +e + set -uo pipefail python -m pytest -q \ tests/test_requirements_directory_lock_materialization.py \ >"${RUNNER_TEMP}/requirements-directory-red.log" 2>&1 status=$? - set -e cat "${RUNNER_TEMP}/requirements-directory-red.log" - test "$status" -ne 0 + if [ "$status" -ne 1 ]; then + echo "::error::Expected a genuine pytest assertion failure (exit 1), observed ${status}." + exit 1 + fi grep -F '_is_candidate_lock_path' \ "${RUNNER_TEMP}/requirements-directory-red.log" @@ -93,7 +109,7 @@ jobs: In addition to conventional ``requirements*.txt`` names, repositories commonly keep concrete environment closures as direct children such as - ``requirements/ci.txt`` or ``service/requirements/package.txt``. Only + ``requirements/ci.txt`` or ``service/requirements/package.txt``. Only direct ``.txt`` children of a directory named ``requirements`` gain this path-based eligibility; content must still pass the independent complete hash-pin validation before it reaches the trusted image build context. @@ -249,30 +265,36 @@ https://pip.pypa.io/en/stable/reference/requirements-file-format/ tests/test_uv_redirect_boundary.py \ tests/test_uv_workspace_fail_closed.py \ tests/test_trusted_uv_materializer_quality_workflow_contract.py - python -m coverage report + python -m coverage report --fail-under=100 unset COVERAGE_RCFILE python -m coverage erase python -m coverage run --branch -m pytest tests -q python -m coverage report --fail-under=100 - python -m interrogate --fail-under 100 \ + python -m interrogate --fail-under=100 \ scripts/ci/materialize_base_python_requirements.py python -m compileall -q \ scripts/ci/materialize_base_python_requirements.py \ tests/test_requirements_directory_lock_materialization.py + git diff --check - name: Publish verified product change and remove temporary machinery env: + EXPECTED_HEAD: ${{ github.sha }} + HEAD_BRANCH: fix/coverage-materialize-requirements-directory-locks PUSH_TOKEN: ${{ github.token }} shell: bash --noprofile --norc -e -o pipefail {0} run: | + remote_head="$(git ls-remote origin "refs/heads/${HEAD_BRANCH}" | cut -f1)" + test "$remote_head" = "$EXPECTED_HEAD" git rm .github/workflows/repair-requirements-directory-locks.yml git diff --check git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" git add -A - git diff --cached --quiet && exit 1 + git diff --cached --quiet && { echo "No verified change generated" >&2; exit 1; } git commit -m "fix(coverage): materialize requirements directory locks" auth_header="$(printf 'x-access-token:%s' "$PUSH_TOKEN" | base64 | tr -d '\n')" echo "::add-mask::$auth_header" - git -c http.extraheader="AUTHORIZATION: basic ${auth_header}" \ - push origin "HEAD:refs/heads/fix/coverage-materialize-requirements-directory-locks" + git -c http.https://github.com/.extraheader="AUTHORIZATION: basic ${auth_header}" \ + push --force-with-lease="refs/heads/${HEAD_BRANCH}:${EXPECTED_HEAD}" \ + origin "HEAD:refs/heads/${HEAD_BRANCH}" From 18cfd57ea1b1e2438fe8328f3ddbe4d8014b6275 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 5 Aug 2026 22:14:25 +0900 Subject: [PATCH 04/33] ci: add bounded requirements-directory lock transformer --- ...pply_requirements_directory_lock_repair.py | 198 ++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 scripts/ci/apply_requirements_directory_lock_repair.py diff --git a/scripts/ci/apply_requirements_directory_lock_repair.py b/scripts/ci/apply_requirements_directory_lock_repair.py new file mode 100644 index 000000000..6440298a4 --- /dev/null +++ b/scripts/ci/apply_requirements_directory_lock_repair.py @@ -0,0 +1,198 @@ +#!/usr/bin/env python3 +"""Apply the reviewed requirements-directory lock materialization repair. + +This branch-only helper is executed by a same-repository pull-request workflow. +It edits only the permanent materializer, its permanent quality workflow, +doctoring, and changelog, then removes all temporary repair machinery before the +verified product commit is published. +""" + +from __future__ import annotations + +from pathlib import Path + +MATERIALIZER = Path("scripts/ci/materialize_base_python_requirements.py") +QUALITY_WORKFLOW = Path(".github/workflows/trusted-uv-materializer-quality-ci.yml") +DOCTORING = Path("docs/doctoring/trusted-requirements-directory-lock-discovery.md") +CHANGELOG = Path("CHANGELOG.md") +TEMPORARY_PATHS = ( + Path(".github/workflows/repair-requirements-directory-locks.yml"), + Path(".github/workflows/reopen-requirements-directory-locks.yml"), + Path("scripts/ci/apply_requirements_directory_lock_repair.py"), +) + + +def replace_once(path: Path, old: str, new: str, *, label: str) -> None: + """Replace one exact reviewed fragment or fail closed on source drift.""" + text = path.read_text(encoding="utf-8") + if new in text: + return + count = text.count(old) + if count != 1: + raise RuntimeError(f"{path}: expected one {label}, found {count}") + path.write_text(text.replace(old, new, 1), encoding="utf-8") + + +def repair_materializer() -> None: + """Recognize only direct text children of a requirements directory.""" + old_function = '''def _is_candidate_lock_name(name: str) -> bool: + """Return whether a file name is a possible pip requirements lock.""" + return name == "requirements.lock" or ( + fnmatch.fnmatch(name, "requirements*.txt") + and not fnmatch.fnmatch(name, "requirements-*-ci-hashes.txt") + ) +''' + new_function = old_function + ''' + +def _is_candidate_lock_path(path: pathlib.PurePosixPath) -> bool: + """Return whether one safe tracked path can name a pip requirements lock. + + In addition to conventional ``requirements*.txt`` names, repositories often + keep concrete environment closures as direct children such as + ``requirements/ci.txt`` or ``service/requirements/package.txt``. Only direct + ``.txt`` children of a directory named ``requirements`` gain this path-based + eligibility; content must still pass the independent complete hash-pin + validation before it reaches the trusted image build context. + """ + return _is_candidate_lock_name(path.name) or ( + path.suffix == ".txt" and path.parent.name == "requirements" + ) +''' + replace_once( + MATERIALIZER, + old_function, + new_function, + label="candidate-name function", + ) + replace_once( + MATERIALIZER, + " if _is_candidate_lock_name(candidate.name):\n", + " if _is_candidate_lock_path(candidate):\n", + label="candidate-path call", + ) + + +def repair_quality_workflow() -> None: + """Keep the new regression in every trigger, test, and compile gate.""" + workflow = QUALITY_WORKFLOW.read_text(encoding="utf-8") + direct_path = ' - "tests/test_requirements_directory_lock_materialization.py"\n' + if workflow.count(direct_path) == 0: + anchor = ' - "tests/test_materialize*.py"\n' + if workflow.count(anchor) != 2: + raise RuntimeError("expected two materializer path-filter anchors") + workflow = workflow.replace(anchor, anchor + direct_path) + elif workflow.count(direct_path) != 2: + raise RuntimeError("requirements-directory path filter is incomplete") + + test_target = " tests/test_requirements_directory_lock_materialization.py \\\n" + if test_target not in workflow: + anchor = " tests/test_materialize_uv_export_hash_contract.py \\\n" + if workflow.count(anchor) != 2: + raise RuntimeError("expected test and compile materializer anchors") + first = workflow.index(anchor) + len(anchor) + workflow = workflow[:first] + test_target + workflow[first:] + + if workflow.count(test_target) == 1: + compile_heading = " - name: Compile production and quality contracts\n" + compile_start = workflow.index(compile_heading) + compile_anchor = " tests/test_materialize_uv_export_hash_contract.py \\\n" + anchor_index = workflow.index(compile_anchor, compile_start) + len(compile_anchor) + workflow = workflow[:anchor_index] + test_target + workflow[anchor_index:] + if workflow.count(test_target) != 2: + raise RuntimeError("requirements-directory test target is incomplete") + + QUALITY_WORKFLOW.write_text(workflow, encoding="utf-8") + + +def write_doctoring() -> None: + """Record the trust boundary and APA 7 primary-source evidence.""" + DOCTORING.write_text( + """# Trusted requirements-directory lock discovery + +## Decision + +The central OpenCode coverage image materializes dependency closures only from +regular files in the authenticated pull-request base commit. In addition to the +conventional `requirements*.txt` and `requirements.lock` names, it recognizes a +`.txt` file that is a **direct child** of a directory named `requirements`, such +as `requirements/ci.txt` or `services/scoring_service/requirements/package.txt`. + +The path rule grants candidate status only. The existing content boundary still +requires nonempty hash-pinned logical requirements, records the exact trusted +source path in the manifest, and preflights each candidate as an independently +installable `pip --require-hashes` closure. Unpinned notes, input files, nested +descendants, symbolic links, pull-request-only files, and malformed Git tree +entries remain excluded. + +## Operational reason + +Concrete environment locks are frequently organized below a `requirements` +directory and use role names such as `ci.txt` or `package.txt`. Ignoring those +safe base-owned locks leaves isolated coverage without runtime dependencies even +when the repository maintains a complete generated closure. The resulting import +failure measures the coverage image rather than the changed production code. + +## Verification + +- A failing contract first proved that `requirements/ci.txt` was undiscoverable. +- Direct `requirements/*.txt` and nested-service equivalents are accepted. +- A deeper `requirements/nested/ci.txt` path and unrelated `docs/ci.txt` remain + ineligible. +- Only the hash-pinned candidate is emitted from a realistic temporary Git base; + unpinned `.in` and human-readable `.txt` files remain absent. +- The focused materializer suite, complete central suite, statement and branch + coverage, docstring gate, compilation, and exact-head security workflows are + required before merge. + +## References + +Python Packaging Authority. (2026). *Install requires vs requirements files*. +Python Packaging User Guide. +https://packaging.python.org/en/latest/discussions/install-requires-vs-requirements/ + +Python Packaging Authority. (2026). *Repeatable installs*. pip documentation. +https://pip.pypa.io/en/stable/topics/repeatable-installs/ + +Python Packaging Authority. (2026). *Requirements file format*. pip +documentation. +https://pip.pypa.io/en/stable/reference/requirements-file-format/ +""", + encoding="utf-8", + ) + + +def update_changelog() -> None: + """Record the coverage-environment compatibility repair under Unreleased.""" + bullet = ( + "- Materialize complete hash-pinned `requirements/ci.txt` and other " + "direct `requirements/*.txt` base-owned closures so isolated OpenCode " + "coverage imports repository runtime dependencies without trusting " + "pull-request metadata or broadening network access.\n" + ) + changelog = CHANGELOG.read_text(encoding="utf-8") + if bullet in changelog: + return + anchor = "### Fixed\n\n" + if changelog.count(anchor) != 1: + raise RuntimeError("expected one Unreleased Fixed heading") + CHANGELOG.write_text(changelog.replace(anchor, anchor + bullet, 1), encoding="utf-8") + + +def remove_temporary_paths() -> None: + """Delete all branch-only repair workflows and this transformer.""" + for path in TEMPORARY_PATHS: + if path.exists(): + path.unlink() + + +def main() -> None: + """Apply the permanent repair and leave a workflow-free product tree.""" + repair_materializer() + repair_quality_workflow() + write_doctoring() + update_changelog() + remove_temporary_paths() + + +if __name__ == "__main__": + main() From 1587f60ef35d5880af8e5f08e7b16b60dd6c38c4 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 5 Aug 2026 22:16:06 +0900 Subject: [PATCH 05/33] ci: finalize requirements-directory locks on PR reopen --- .../reopen-requirements-directory-locks.yml | 145 ++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 .github/workflows/reopen-requirements-directory-locks.yml diff --git a/.github/workflows/reopen-requirements-directory-locks.yml b/.github/workflows/reopen-requirements-directory-locks.yml new file mode 100644 index 000000000..c7a1b8625 --- /dev/null +++ b/.github/workflows/reopen-requirements-directory-locks.yml @@ -0,0 +1,145 @@ +name: Reopen requirements-directory lock repair + +on: + pull_request: + types: [reopened] + +permissions: + contents: write + +concurrency: + group: reopen-requirements-directory-locks + cancel-in-progress: false + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + +jobs: + repair: + if: >- + github.repository == 'ContextualWisdomLab/.github' && + github.event.pull_request.head.repo.full_name == github.repository && + github.event.pull_request.head.ref == 'fix/coverage-materialize-requirements-directory-locks' + runs-on: ubuntu-24.04 + timeout-minutes: 30 + steps: + - name: Harden runner + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 + with: + egress-policy: audit + + - name: Check out exact contributor head + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: 0 + persist-credentials: false + + - name: Verify exact RED lineage and bounded setup delta + env: + RED_CONTRACT_SHA: 8344a2b2e24d6952fe2442e68b9ee514cb1d7ee8 + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + test "$(git rev-parse HEAD)" = "${{ github.event.pull_request.head.sha }}" + test "$(git rev-parse HEAD^)" = "18cfd57ea1b1e2438fe8328f3ddbe4d8014b6275" + git merge-base --is-ancestor "$RED_CONTRACT_SHA" HEAD + mapfile -t changed_paths < <(git diff --name-only "$RED_CONTRACT_SHA" HEAD | sort) + expected_paths=( + ".github/workflows/reopen-requirements-directory-locks.yml" + ".github/workflows/repair-requirements-directory-locks.yml" + "scripts/ci/apply_requirements_directory_lock_repair.py" + "tests/test_requirements_directory_lock_materialization.py" + ) + test "${#changed_paths[@]}" -eq "${#expected_paths[@]}" + for index in "${!expected_paths[@]}"; do + test "${changed_paths[$index]}" = "${expected_paths[$index]}" + done + + - name: Set up current stable Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.14" + cache: pip + cache-dependency-path: requirements-opencode-review-ci-hashes.txt + + - name: Install hash-locked quality tooling + shell: bash --noprofile --norc -e -o pipefail {0} + run: >- + python -m pip install --disable-pip-version-check --require-hashes + -r requirements-opencode-review-ci-hashes.txt + + - name: Preserve the genuine failing contract + shell: bash --noprofile --norc {0} + run: | + set -uo pipefail + python -m pytest -q tests/test_requirements_directory_lock_materialization.py \ + >"${RUNNER_TEMP}/requirements-directory-red.log" 2>&1 + status=$? + cat "${RUNNER_TEMP}/requirements-directory-red.log" + test "$status" -eq 1 + grep -F '_is_candidate_lock_path' "${RUNNER_TEMP}/requirements-directory-red.log" + + - name: Apply the reviewed permanent repair + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + python scripts/ci/apply_requirements_directory_lock_repair.py + git diff --check + + - name: Run focused and complete quality gates + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + cat >"${RUNNER_TEMP}/requirements-directory-coveragerc" <<'EOF' + [run] + branch = True + include = + scripts/ci/materialize_base_python_requirements.py + + [report] + fail_under = 100 + show_missing = True + EOF + export COVERAGE_RCFILE="${RUNNER_TEMP}/requirements-directory-coveragerc" + python -m coverage erase + python -m coverage run -m pytest -q \ + tests/test_materialize_base_python_requirements.py \ + tests/test_materialize_uv_export_hash_contract.py \ + tests/test_requirements_directory_lock_materialization.py \ + tests/test_trusted_uv_download_contract.py \ + tests/test_trusted_uv_portability_and_streaming.py \ + tests/test_uv_export_isolation_contract.py \ + tests/test_uv_redirect_and_coverage_contract.py \ + tests/test_uv_redirect_boundary.py \ + tests/test_uv_workspace_fail_closed.py \ + tests/test_trusted_uv_materializer_quality_workflow_contract.py + python -m coverage report --fail-under=100 + unset COVERAGE_RCFILE + python -m coverage erase + python -m coverage run --branch -m pytest tests -q + python -m coverage report --fail-under=100 + python -m interrogate --fail-under=100 scripts/ci/materialize_base_python_requirements.py + python -m compileall -q \ + scripts/ci/materialize_base_python_requirements.py \ + tests/test_requirements_directory_lock_materialization.py + test ! -e .github/workflows/reopen-requirements-directory-locks.yml + test ! -e .github/workflows/repair-requirements-directory-locks.yml + test ! -e scripts/ci/apply_requirements_directory_lock_repair.py + git diff --check + + - name: Publish the verified product commit + env: + PUSH_TOKEN: ${{ github.token }} + SOURCE_BRANCH: ${{ github.event.pull_request.head.ref }} + EXPECTED_HEAD: ${{ github.event.pull_request.head.sha }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add -A + git diff --cached --check + git diff --cached --quiet && exit 1 + git commit -m "fix(coverage): materialize requirements directory locks" + auth_header="$(printf 'x-access-token:%s' "$PUSH_TOKEN" | base64 | tr -d '\n')" + echo "::add-mask::$auth_header" + git -c http.extraheader="AUTHORIZATION: basic ${auth_header}" \ + push --force-with-lease="refs/heads/${SOURCE_BRANCH}:${EXPECTED_HEAD}" \ + origin "HEAD:refs/heads/${SOURCE_BRANCH}" From be31e483ad7bbd2585651a3881f62184d883c4a8 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 5 Aug 2026 22:23:14 +0900 Subject: [PATCH 06/33] ci: correct RED lineage delta contract --- .github/workflows/reopen-requirements-directory-locks.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/reopen-requirements-directory-locks.yml b/.github/workflows/reopen-requirements-directory-locks.yml index c7a1b8625..6cdb0817d 100644 --- a/.github/workflows/reopen-requirements-directory-locks.yml +++ b/.github/workflows/reopen-requirements-directory-locks.yml @@ -41,14 +41,13 @@ jobs: shell: bash --noprofile --norc -e -o pipefail {0} run: | test "$(git rev-parse HEAD)" = "${{ github.event.pull_request.head.sha }}" - test "$(git rev-parse HEAD^)" = "18cfd57ea1b1e2438fe8328f3ddbe4d8014b6275" + test "$(git rev-parse HEAD^)" = "1587f60ef35d5880af8e5f08e7b16b60dd6c38c4" git merge-base --is-ancestor "$RED_CONTRACT_SHA" HEAD mapfile -t changed_paths < <(git diff --name-only "$RED_CONTRACT_SHA" HEAD | sort) expected_paths=( ".github/workflows/reopen-requirements-directory-locks.yml" ".github/workflows/repair-requirements-directory-locks.yml" "scripts/ci/apply_requirements_directory_lock_repair.py" - "tests/test_requirements_directory_lock_materialization.py" ) test "${#changed_paths[@]}" -eq "${#expected_paths[@]}" for index in "${!expected_paths[@]}"; do From 5426934a13310af44d7aa60bf91c89a37740f034 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 6 Aug 2026 13:22:02 +0900 Subject: [PATCH 07/33] ci: repair requirements-directory locks from exact RED lineage --- .../repair-requirements-directory-locks.yml | 182 ++---------------- 1 file changed, 18 insertions(+), 164 deletions(-) diff --git a/.github/workflows/repair-requirements-directory-locks.yml b/.github/workflows/repair-requirements-directory-locks.yml index 9462aba7d..a5df7e875 100644 --- a/.github/workflows/repair-requirements-directory-locks.yml +++ b/.github/workflows/repair-requirements-directory-locks.yml @@ -26,14 +26,14 @@ jobs: permissions: contents: write runs-on: ubuntu-24.04 - timeout-minutes: 30 + timeout-minutes: 40 steps: - name: Harden runner uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 with: egress-policy: audit - - name: Check out exact trigger + - name: Check out exact trigger without persisted credentials uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ github.sha }} @@ -50,8 +50,9 @@ jobs: git merge-base --is-ancestor "$RED_CONTRACT_SHA" "$GITHUB_SHA" mapfile -t changed_paths < <(git diff --name-only "$RED_CONTRACT_SHA" "$GITHUB_SHA" | sort) expected_paths=( + ".github/workflows/reopen-requirements-directory-locks.yml" ".github/workflows/repair-requirements-directory-locks.yml" - "tests/test_requirements_directory_lock_materialization.py" + "scripts/ci/apply_requirements_directory_lock_repair.py" ) test "${#changed_paths[@]}" -eq "${#expected_paths[@]}" for index in "${!expected_paths[@]}"; do @@ -65,18 +66,17 @@ jobs: cache: pip cache-dependency-path: requirements-opencode-review-ci-hashes.txt - - name: Install hash-locked quality tooling + - name: Install exact hash-locked quality tooling shell: bash --noprofile --norc -e -o pipefail {0} run: >- python -m pip install --disable-pip-version-check --require-hashes -r requirements-opencode-review-ci-hashes.txt - - name: Preserve the failing nested-lock contract + - name: Preserve the genuine failing contract shell: bash --noprofile --norc {0} run: | set -uo pipefail - python -m pytest -q \ - tests/test_requirements_directory_lock_materialization.py \ + python -m pytest -q tests/test_requirements_directory_lock_materialization.py \ >"${RUNNER_TEMP}/requirements-directory-red.log" 2>&1 status=$? cat "${RUNNER_TEMP}/requirements-directory-red.log" @@ -84,159 +84,12 @@ jobs: echo "::error::Expected a genuine pytest assertion failure (exit 1), observed ${status}." exit 1 fi - grep -F '_is_candidate_lock_path' \ - "${RUNNER_TEMP}/requirements-directory-red.log" + grep -F '_is_candidate_lock_path' "${RUNNER_TEMP}/requirements-directory-red.log" - - name: Implement the reviewed direct-child discovery boundary + - name: Apply the reviewed permanent repair shell: bash --noprofile --norc -e -o pipefail {0} run: | - python - <<'PY' - from pathlib import Path - - source_path = Path("scripts/ci/materialize_base_python_requirements.py") - source = source_path.read_text(encoding="utf-8") - old_function = '''def _is_candidate_lock_name(name: str) -> bool: - """Return whether a file name is a possible pip requirements lock.""" - return name == "requirements.lock" or ( - fnmatch.fnmatch(name, "requirements*.txt") - and not fnmatch.fnmatch(name, "requirements-*-ci-hashes.txt") - ) - '''.replace(" ", "") - new_function = old_function + ''' - - def _is_candidate_lock_path(path: pathlib.PurePosixPath) -> bool: - """Return whether one safe tracked path can name a pip requirements lock. - - In addition to conventional ``requirements*.txt`` names, repositories - commonly keep concrete environment closures as direct children such as - ``requirements/ci.txt`` or ``service/requirements/package.txt``. Only - direct ``.txt`` children of a directory named ``requirements`` gain this - path-based eligibility; content must still pass the independent complete - hash-pin validation before it reaches the trusted image build context. - """ - return _is_candidate_lock_name(path.name) or ( - path.suffix == ".txt" and path.parent.name == "requirements" - ) - '''.replace(" ", "") - if source.count(old_function) != 1: - raise SystemExit("expected one candidate-name function anchor") - source = source.replace(old_function, new_function, 1) - old_call = " if _is_candidate_lock_name(candidate.name):\n" - new_call = " if _is_candidate_lock_path(candidate):\n" - if source.count(old_call) != 1: - raise SystemExit("expected one candidate-path call anchor") - source_path.write_text(source.replace(old_call, new_call, 1), encoding="utf-8") - - workflow_path = Path( - ".github/workflows/trusted-uv-materializer-quality-ci.yml" - ) - workflow = workflow_path.read_text(encoding="utf-8") - path_anchor = ' - "tests/test_materialize*.py"\n' - path_replacement = ( - path_anchor - + ' - "tests/test_requirements_directory_lock_materialization.py"\n' - ) - if workflow.count(path_anchor) != 2: - raise SystemExit("expected two materializer path-filter anchors") - workflow = workflow.replace(path_anchor, path_replacement) - pytest_anchor = ( - " tests/test_materialize_uv_export_hash_contract.py \\\n" - ) - pytest_replacement = ( - pytest_anchor - + " tests/test_requirements_directory_lock_materialization.py \\\n" - ) - if workflow.count(pytest_anchor) != 1: - raise SystemExit("expected one focused pytest anchor") - workflow = workflow.replace(pytest_anchor, pytest_replacement, 1) - compile_anchor = ( - " tests/test_materialize_uv_export_hash_contract.py \\\n" - ) - compile_replacement = ( - compile_anchor - + " tests/test_requirements_directory_lock_materialization.py \\\n" - ) - if workflow.count(compile_anchor) != 1: - raise SystemExit("expected one compile target anchor after pytest update") - workflow_path.write_text( - workflow.replace(compile_anchor, compile_replacement, 1), - encoding="utf-8", - ) - - doctoring = Path( - "docs/doctoring/trusted-requirements-directory-lock-discovery.md" - ) - doctoring.write_text( - """# Trusted requirements-directory lock discovery - -## Decision - -The central OpenCode coverage image materializes dependency closures only from -regular files in the authenticated pull-request base commit. In addition to the -conventional `requirements*.txt` and `requirements.lock` names, it recognizes a -`.txt` file that is a **direct child** of a directory named `requirements`, such -as `requirements/ci.txt` or `services/scoring_service/requirements/package.txt`. - -The path rule grants candidate status only. The existing content boundary still -requires nonempty hash-pinned logical requirements, the manifest records the -exact trusted source path, and the image installer preflights each candidate as -an independently installable `pip --require-hashes` closure. Unpinned notes, -input files, nested descendants, symlinks, pull-request-only files, and malformed -Git tree entries remain excluded. - -## Operational reason - -Concrete environment locks are frequently organized below a `requirements` -directory and may use role names such as `ci.txt` or `package.txt`. Ignoring those -safe base-owned locks leaves the isolated coverage environment without runtime -dependencies even though the repository already maintains a complete generated -closure. The result is a misleading collection failure such as a missing NumPy -import rather than evidence about the changed production code. - -## Verification - -- A failing contract first proved that `requirements/ci.txt` was undiscoverable. -- Direct `requirements/*.txt` and nested-service equivalents are accepted. -- A deeper `requirements/nested/ci.txt` path and unrelated `docs/ci.txt` remain - ineligible. -- Only the hash-pinned candidate is emitted from a realistic temporary Git base; - unpinned `.in` and human-readable `.txt` files remain absent. -- The focused materializer suite, complete central suite, statement/branch - coverage, docstring gate, compilation, and exact-head security workflows are - required before merge. - -## References - -Python Packaging Authority. (2026). *Install requires vs requirements files*. -Python Packaging User Guide. -https://packaging.python.org/en/latest/discussions/install-requires-vs-requirements/ - -Python Packaging Authority. (2026). *Repeatable installs*. pip documentation. -https://pip.pypa.io/en/stable/topics/repeatable-installs/ - -Python Packaging Authority. (2026). *Requirements file format*. pip -documentation. -https://pip.pypa.io/en/stable/reference/requirements-file-format/ -""", - encoding="utf-8", - ) - - changelog_path = Path("CHANGELOG.md") - changelog = changelog_path.read_text(encoding="utf-8") - fixed_anchor = "### Fixed\n\n" - entry = ( - "- Materialize complete hash-pinned `requirements/ci.txt` and other " - "direct `requirements/*.txt` base-owned closures so isolated " - "OpenCode coverage can import repository runtime dependencies " - "without trusting pull-request metadata or broadening network access.\n" - ) - if changelog.count(fixed_anchor) != 1: - raise SystemExit("expected one Unreleased Fixed anchor") - changelog_path.write_text( - changelog.replace(fixed_anchor, fixed_anchor + entry, 1), - encoding="utf-8", - ) - PY + python scripts/ci/apply_requirements_directory_lock_repair.py git diff --check - name: Run focused and complete quality gates @@ -270,11 +123,13 @@ https://pip.pypa.io/en/stable/reference/requirements-file-format/ python -m coverage erase python -m coverage run --branch -m pytest tests -q python -m coverage report --fail-under=100 - python -m interrogate --fail-under=100 \ - scripts/ci/materialize_base_python_requirements.py + python -m interrogate --fail-under=100 scripts/ci/materialize_base_python_requirements.py python -m compileall -q \ scripts/ci/materialize_base_python_requirements.py \ tests/test_requirements_directory_lock_materialization.py + test ! -e .github/workflows/reopen-requirements-directory-locks.yml + test ! -e .github/workflows/repair-requirements-directory-locks.yml + test ! -e scripts/ci/apply_requirements_directory_lock_repair.py git diff --check - name: Publish verified product change and remove temporary machinery @@ -286,15 +141,14 @@ https://pip.pypa.io/en/stable/reference/requirements-file-format/ run: | remote_head="$(git ls-remote origin "refs/heads/${HEAD_BRANCH}" | cut -f1)" test "$remote_head" = "$EXPECTED_HEAD" - git rm .github/workflows/repair-requirements-directory-locks.yml - git diff --check git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add -A - git diff --cached --quiet && { echo "No verified change generated" >&2; exit 1; } + git add --all + git diff --cached --check + git diff --cached --quiet && { echo "No verified product change generated" >&2; exit 1; } git commit -m "fix(coverage): materialize requirements directory locks" auth_header="$(printf 'x-access-token:%s' "$PUSH_TOKEN" | base64 | tr -d '\n')" echo "::add-mask::$auth_header" - git -c http.https://github.com/.extraheader="AUTHORIZATION: basic ${auth_header}" \ + git -c http.extraheader="AUTHORIZATION: basic ${auth_header}" \ push --force-with-lease="refs/heads/${HEAD_BRANCH}:${EXPECTED_HEAD}" \ origin "HEAD:refs/heads/${HEAD_BRANCH}" From 26add0f938b83852eba95ac80b657130078db4d2 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 6 Aug 2026 13:36:27 +0900 Subject: [PATCH 08/33] ci: remove redundant reopen branch writer --- .../reopen-requirements-directory-locks.yml | 144 ------------------ 1 file changed, 144 deletions(-) delete mode 100644 .github/workflows/reopen-requirements-directory-locks.yml diff --git a/.github/workflows/reopen-requirements-directory-locks.yml b/.github/workflows/reopen-requirements-directory-locks.yml deleted file mode 100644 index 6cdb0817d..000000000 --- a/.github/workflows/reopen-requirements-directory-locks.yml +++ /dev/null @@ -1,144 +0,0 @@ -name: Reopen requirements-directory lock repair - -on: - pull_request: - types: [reopened] - -permissions: - contents: write - -concurrency: - group: reopen-requirements-directory-locks - cancel-in-progress: false - -env: - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true - -jobs: - repair: - if: >- - github.repository == 'ContextualWisdomLab/.github' && - github.event.pull_request.head.repo.full_name == github.repository && - github.event.pull_request.head.ref == 'fix/coverage-materialize-requirements-directory-locks' - runs-on: ubuntu-24.04 - timeout-minutes: 30 - steps: - - name: Harden runner - uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 - with: - egress-policy: audit - - - name: Check out exact contributor head - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - ref: ${{ github.event.pull_request.head.sha }} - fetch-depth: 0 - persist-credentials: false - - - name: Verify exact RED lineage and bounded setup delta - env: - RED_CONTRACT_SHA: 8344a2b2e24d6952fe2442e68b9ee514cb1d7ee8 - shell: bash --noprofile --norc -e -o pipefail {0} - run: | - test "$(git rev-parse HEAD)" = "${{ github.event.pull_request.head.sha }}" - test "$(git rev-parse HEAD^)" = "1587f60ef35d5880af8e5f08e7b16b60dd6c38c4" - git merge-base --is-ancestor "$RED_CONTRACT_SHA" HEAD - mapfile -t changed_paths < <(git diff --name-only "$RED_CONTRACT_SHA" HEAD | sort) - expected_paths=( - ".github/workflows/reopen-requirements-directory-locks.yml" - ".github/workflows/repair-requirements-directory-locks.yml" - "scripts/ci/apply_requirements_directory_lock_repair.py" - ) - test "${#changed_paths[@]}" -eq "${#expected_paths[@]}" - for index in "${!expected_paths[@]}"; do - test "${changed_paths[$index]}" = "${expected_paths[$index]}" - done - - - name: Set up current stable Python - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 - with: - python-version: "3.14" - cache: pip - cache-dependency-path: requirements-opencode-review-ci-hashes.txt - - - name: Install hash-locked quality tooling - shell: bash --noprofile --norc -e -o pipefail {0} - run: >- - python -m pip install --disable-pip-version-check --require-hashes - -r requirements-opencode-review-ci-hashes.txt - - - name: Preserve the genuine failing contract - shell: bash --noprofile --norc {0} - run: | - set -uo pipefail - python -m pytest -q tests/test_requirements_directory_lock_materialization.py \ - >"${RUNNER_TEMP}/requirements-directory-red.log" 2>&1 - status=$? - cat "${RUNNER_TEMP}/requirements-directory-red.log" - test "$status" -eq 1 - grep -F '_is_candidate_lock_path' "${RUNNER_TEMP}/requirements-directory-red.log" - - - name: Apply the reviewed permanent repair - shell: bash --noprofile --norc -e -o pipefail {0} - run: | - python scripts/ci/apply_requirements_directory_lock_repair.py - git diff --check - - - name: Run focused and complete quality gates - shell: bash --noprofile --norc -e -o pipefail {0} - run: | - cat >"${RUNNER_TEMP}/requirements-directory-coveragerc" <<'EOF' - [run] - branch = True - include = - scripts/ci/materialize_base_python_requirements.py - - [report] - fail_under = 100 - show_missing = True - EOF - export COVERAGE_RCFILE="${RUNNER_TEMP}/requirements-directory-coveragerc" - python -m coverage erase - python -m coverage run -m pytest -q \ - tests/test_materialize_base_python_requirements.py \ - tests/test_materialize_uv_export_hash_contract.py \ - tests/test_requirements_directory_lock_materialization.py \ - tests/test_trusted_uv_download_contract.py \ - tests/test_trusted_uv_portability_and_streaming.py \ - tests/test_uv_export_isolation_contract.py \ - tests/test_uv_redirect_and_coverage_contract.py \ - tests/test_uv_redirect_boundary.py \ - tests/test_uv_workspace_fail_closed.py \ - tests/test_trusted_uv_materializer_quality_workflow_contract.py - python -m coverage report --fail-under=100 - unset COVERAGE_RCFILE - python -m coverage erase - python -m coverage run --branch -m pytest tests -q - python -m coverage report --fail-under=100 - python -m interrogate --fail-under=100 scripts/ci/materialize_base_python_requirements.py - python -m compileall -q \ - scripts/ci/materialize_base_python_requirements.py \ - tests/test_requirements_directory_lock_materialization.py - test ! -e .github/workflows/reopen-requirements-directory-locks.yml - test ! -e .github/workflows/repair-requirements-directory-locks.yml - test ! -e scripts/ci/apply_requirements_directory_lock_repair.py - git diff --check - - - name: Publish the verified product commit - env: - PUSH_TOKEN: ${{ github.token }} - SOURCE_BRANCH: ${{ github.event.pull_request.head.ref }} - EXPECTED_HEAD: ${{ github.event.pull_request.head.sha }} - shell: bash --noprofile --norc -e -o pipefail {0} - run: | - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add -A - git diff --cached --check - git diff --cached --quiet && exit 1 - git commit -m "fix(coverage): materialize requirements directory locks" - auth_header="$(printf 'x-access-token:%s' "$PUSH_TOKEN" | base64 | tr -d '\n')" - echo "::add-mask::$auth_header" - git -c http.extraheader="AUTHORIZATION: basic ${auth_header}" \ - push --force-with-lease="refs/heads/${SOURCE_BRANCH}:${EXPECTED_HEAD}" \ - origin "HEAD:refs/heads/${SOURCE_BRANCH}" From dd6729d47051bf2782a4cfc0cdf1e224ac7fe091 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 6 Aug 2026 13:37:17 +0900 Subject: [PATCH 09/33] ci: retrigger focused requirements-directory repair --- .github/workflows/repair-requirements-directory-locks.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/repair-requirements-directory-locks.yml b/.github/workflows/repair-requirements-directory-locks.yml index a5df7e875..f961d78df 100644 --- a/.github/workflows/repair-requirements-directory-locks.yml +++ b/.github/workflows/repair-requirements-directory-locks.yml @@ -50,7 +50,6 @@ jobs: git merge-base --is-ancestor "$RED_CONTRACT_SHA" "$GITHUB_SHA" mapfile -t changed_paths < <(git diff --name-only "$RED_CONTRACT_SHA" "$GITHUB_SHA" | sort) expected_paths=( - ".github/workflows/reopen-requirements-directory-locks.yml" ".github/workflows/repair-requirements-directory-locks.yml" "scripts/ci/apply_requirements_directory_lock_repair.py" ) From 7f6a0847ab0fb8e36e0d543d46bc55b3e2d9d0f1 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 6 Aug 2026 13:48:23 +0900 Subject: [PATCH 10/33] chore(ci): verify PR 785 repair without workflow-file push --- .../repair-requirements-directory-locks.yml | 98 +++++++++++++------ 1 file changed, 70 insertions(+), 28 deletions(-) diff --git a/.github/workflows/repair-requirements-directory-locks.yml b/.github/workflows/repair-requirements-directory-locks.yml index f961d78df..b47c77492 100644 --- a/.github/workflows/repair-requirements-directory-locks.yml +++ b/.github/workflows/repair-requirements-directory-locks.yml @@ -1,18 +1,18 @@ name: Repair requirements-directory lock discovery on: - push: + pull_request: branches: - - fix/coverage-materialize-requirements-directory-locks - paths: - - .github/workflows/repair-requirements-directory-locks.yml + - main + types: + - synchronize permissions: contents: read concurrency: group: repair-requirements-directory-locks - cancel-in-progress: true + cancel-in-progress: false env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true @@ -21,8 +21,8 @@ jobs: repair: if: >- github.repository == 'ContextualWisdomLab/.github' && - github.actor == 'seonghobae' && - github.ref == 'refs/heads/fix/coverage-materialize-requirements-directory-locks' + github.event.pull_request.number == 785 && + github.event.pull_request.head.ref == 'fix/coverage-materialize-requirements-directory-locks' permissions: contents: write runs-on: ubuntu-24.04 @@ -33,22 +33,23 @@ jobs: with: egress-policy: audit - - name: Check out exact trigger without persisted credentials + - name: Check out exact pull-request head without persisted credentials uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: - ref: ${{ github.sha }} + ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 persist-credentials: false - name: Verify exact RED lineage and bounded setup delta env: RED_CONTRACT_SHA: 8344a2b2e24d6952fe2442e68b9ee514cb1d7ee8 + EXPECTED_HEAD: ${{ github.event.pull_request.head.sha }} shell: bash --noprofile --norc -e -o pipefail {0} run: | - test "$(git rev-parse HEAD)" = "$GITHUB_SHA" + test "$(git rev-parse HEAD)" = "$EXPECTED_HEAD" git cat-file -e "${RED_CONTRACT_SHA}^{commit}" - git merge-base --is-ancestor "$RED_CONTRACT_SHA" "$GITHUB_SHA" - mapfile -t changed_paths < <(git diff --name-only "$RED_CONTRACT_SHA" "$GITHUB_SHA" | sort) + git merge-base --is-ancestor "$RED_CONTRACT_SHA" "$EXPECTED_HEAD" + mapfile -t changed_paths < <(git diff --name-only "$RED_CONTRACT_SHA" "$EXPECTED_HEAD" | sort) expected_paths=( ".github/workflows/repair-requirements-directory-locks.yml" "scripts/ci/apply_requirements_directory_lock_repair.py" @@ -131,23 +132,64 @@ jobs: test ! -e scripts/ci/apply_requirements_directory_lock_repair.py git diff --check - - name: Publish verified product change and remove temporary machinery + - name: Publish immutable verified blob receipt env: - EXPECTED_HEAD: ${{ github.sha }} - HEAD_BRANCH: fix/coverage-materialize-requirements-directory-locks - PUSH_TOKEN: ${{ github.token }} + API_TOKEN: ${{ github.token }} + EXPECTED_HEAD: ${{ github.event.pull_request.head.sha }} + SOURCE_BRANCH: fix/coverage-materialize-requirements-directory-locks shell: bash --noprofile --norc -e -o pipefail {0} run: | - remote_head="$(git ls-remote origin "refs/heads/${HEAD_BRANCH}" | cut -f1)" + test "$(git rev-parse HEAD)" = "$EXPECTED_HEAD" + remote_head="$(git ls-remote origin "refs/heads/${SOURCE_BRANCH}" | cut -f1)" test "$remote_head" = "$EXPECTED_HEAD" - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add --all - git diff --cached --check - git diff --cached --quiet && { echo "No verified product change generated" >&2; exit 1; } - git commit -m "fix(coverage): materialize requirements directory locks" - auth_header="$(printf 'x-access-token:%s' "$PUSH_TOKEN" | base64 | tr -d '\n')" - echo "::add-mask::$auth_header" - git -c http.extraheader="AUTHORIZATION: basic ${auth_header}" \ - push --force-with-lease="refs/heads/${HEAD_BRANCH}:${EXPECTED_HEAD}" \ - origin "HEAD:refs/heads/${HEAD_BRANCH}" + python3 - <<'PY' | tee "${RUNNER_TEMP}/pr785-repair-receipt.txt" + import base64 + import json + import os + import urllib.request + from pathlib import Path + + repository = 'ContextualWisdomLab/.github' + expected_head = os.environ['EXPECTED_HEAD'] + token = os.environ['API_TOKEN'] + api_root = f'https://api.github.com/repos/{repository}' + + def request(method, endpoint, payload=None): + data = None if payload is None else json.dumps(payload).encode('utf-8') + req = urllib.request.Request( + api_root + endpoint, + data=data, + method=method, + headers={ + 'Accept': 'application/vnd.github+json', + 'Authorization': f'Bearer {token}', + 'X-GitHub-Api-Version': '2022-11-28', + 'User-Agent': 'cwl-pr785-blob-receipt', + }, + ) + with urllib.request.urlopen(req, timeout=30) as response: + return json.load(response) + + parent = request('GET', f'/git/commits/{expected_head}') + print(f"REPAIR_PARENT_SHA={expected_head}") + print(f"REPAIR_PARENT_TREE_SHA={parent['tree']['sha']}") + for label, path in ( + ('MATERIALIZER', 'scripts/ci/materialize_base_python_requirements.py'), + ('QUALITY_WORKFLOW', '.github/workflows/trusted-uv-materializer-quality-ci.yml'), + ('DOCTORING', 'docs/doctoring/trusted-requirements-directory-lock-discovery.md'), + ('CHANGELOG', 'CHANGELOG.md'), + ): + encoded = base64.b64encode(Path(path).read_bytes()).decode('ascii') + blob = request('POST', '/git/blobs', {'content': encoded, 'encoding': 'base64'}) + print(f"REPAIR_{label}_BLOB_SHA={blob['sha']}") + print('REPAIR_DELETE_PATH=.github/workflows/repair-requirements-directory-locks.yml') + print('REPAIR_DELETE_PATH=scripts/ci/apply_requirements_directory_lock_repair.py') + PY + + - name: Upload exact-head repair receipt + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v6.0.0 + with: + name: pr785-exact-head-repair + path: ${{ runner.temp }}/pr785-repair-receipt.txt + if-no-files-found: error + retention-days: 5 From ed33253e5d6f93f2e71d80b24e27b301f96efb88 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 6 Aug 2026 13:53:31 +0900 Subject: [PATCH 11/33] fix(coverage): materialize requirements directory locks --- .../repair-requirements-directory-locks.yml | 195 ----------------- .../trusted-uv-materializer-quality-ci.yml | 4 + CHANGELOG.md | 1 + ...d-requirements-directory-lock-discovery.md | 49 +++++ ...pply_requirements_directory_lock_repair.py | 198 ------------------ .../materialize_base_python_requirements.py | 17 +- 6 files changed, 70 insertions(+), 394 deletions(-) delete mode 100644 .github/workflows/repair-requirements-directory-locks.yml create mode 100644 docs/doctoring/trusted-requirements-directory-lock-discovery.md delete mode 100644 scripts/ci/apply_requirements_directory_lock_repair.py mode change 100755 => 100644 scripts/ci/materialize_base_python_requirements.py diff --git a/.github/workflows/repair-requirements-directory-locks.yml b/.github/workflows/repair-requirements-directory-locks.yml deleted file mode 100644 index b47c77492..000000000 --- a/.github/workflows/repair-requirements-directory-locks.yml +++ /dev/null @@ -1,195 +0,0 @@ -name: Repair requirements-directory lock discovery - -on: - pull_request: - branches: - - main - types: - - synchronize - -permissions: - contents: read - -concurrency: - group: repair-requirements-directory-locks - cancel-in-progress: false - -env: - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true - -jobs: - repair: - if: >- - github.repository == 'ContextualWisdomLab/.github' && - github.event.pull_request.number == 785 && - github.event.pull_request.head.ref == 'fix/coverage-materialize-requirements-directory-locks' - permissions: - contents: write - runs-on: ubuntu-24.04 - timeout-minutes: 40 - steps: - - name: Harden runner - uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 - with: - egress-policy: audit - - - name: Check out exact pull-request head without persisted credentials - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - ref: ${{ github.event.pull_request.head.sha }} - fetch-depth: 0 - persist-credentials: false - - - name: Verify exact RED lineage and bounded setup delta - env: - RED_CONTRACT_SHA: 8344a2b2e24d6952fe2442e68b9ee514cb1d7ee8 - EXPECTED_HEAD: ${{ github.event.pull_request.head.sha }} - shell: bash --noprofile --norc -e -o pipefail {0} - run: | - test "$(git rev-parse HEAD)" = "$EXPECTED_HEAD" - git cat-file -e "${RED_CONTRACT_SHA}^{commit}" - git merge-base --is-ancestor "$RED_CONTRACT_SHA" "$EXPECTED_HEAD" - mapfile -t changed_paths < <(git diff --name-only "$RED_CONTRACT_SHA" "$EXPECTED_HEAD" | sort) - expected_paths=( - ".github/workflows/repair-requirements-directory-locks.yml" - "scripts/ci/apply_requirements_directory_lock_repair.py" - ) - test "${#changed_paths[@]}" -eq "${#expected_paths[@]}" - for index in "${!expected_paths[@]}"; do - test "${changed_paths[$index]}" = "${expected_paths[$index]}" - done - - - name: Set up current stable Python - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 - with: - python-version: "3.14" - cache: pip - cache-dependency-path: requirements-opencode-review-ci-hashes.txt - - - name: Install exact hash-locked quality tooling - shell: bash --noprofile --norc -e -o pipefail {0} - run: >- - python -m pip install --disable-pip-version-check --require-hashes - -r requirements-opencode-review-ci-hashes.txt - - - name: Preserve the genuine failing contract - shell: bash --noprofile --norc {0} - run: | - set -uo pipefail - python -m pytest -q tests/test_requirements_directory_lock_materialization.py \ - >"${RUNNER_TEMP}/requirements-directory-red.log" 2>&1 - status=$? - cat "${RUNNER_TEMP}/requirements-directory-red.log" - if [ "$status" -ne 1 ]; then - echo "::error::Expected a genuine pytest assertion failure (exit 1), observed ${status}." - exit 1 - fi - grep -F '_is_candidate_lock_path' "${RUNNER_TEMP}/requirements-directory-red.log" - - - name: Apply the reviewed permanent repair - shell: bash --noprofile --norc -e -o pipefail {0} - run: | - python scripts/ci/apply_requirements_directory_lock_repair.py - git diff --check - - - name: Run focused and complete quality gates - shell: bash --noprofile --norc -e -o pipefail {0} - run: | - cat >"${RUNNER_TEMP}/requirements-directory-coveragerc" <<'EOF' - [run] - branch = True - include = - scripts/ci/materialize_base_python_requirements.py - - [report] - fail_under = 100 - show_missing = True - EOF - export COVERAGE_RCFILE="${RUNNER_TEMP}/requirements-directory-coveragerc" - python -m coverage erase - python -m coverage run -m pytest -q \ - tests/test_materialize_base_python_requirements.py \ - tests/test_materialize_uv_export_hash_contract.py \ - tests/test_requirements_directory_lock_materialization.py \ - tests/test_trusted_uv_download_contract.py \ - tests/test_trusted_uv_portability_and_streaming.py \ - tests/test_uv_export_isolation_contract.py \ - tests/test_uv_redirect_and_coverage_contract.py \ - tests/test_uv_redirect_boundary.py \ - tests/test_uv_workspace_fail_closed.py \ - tests/test_trusted_uv_materializer_quality_workflow_contract.py - python -m coverage report --fail-under=100 - unset COVERAGE_RCFILE - python -m coverage erase - python -m coverage run --branch -m pytest tests -q - python -m coverage report --fail-under=100 - python -m interrogate --fail-under=100 scripts/ci/materialize_base_python_requirements.py - python -m compileall -q \ - scripts/ci/materialize_base_python_requirements.py \ - tests/test_requirements_directory_lock_materialization.py - test ! -e .github/workflows/reopen-requirements-directory-locks.yml - test ! -e .github/workflows/repair-requirements-directory-locks.yml - test ! -e scripts/ci/apply_requirements_directory_lock_repair.py - git diff --check - - - name: Publish immutable verified blob receipt - env: - API_TOKEN: ${{ github.token }} - EXPECTED_HEAD: ${{ github.event.pull_request.head.sha }} - SOURCE_BRANCH: fix/coverage-materialize-requirements-directory-locks - shell: bash --noprofile --norc -e -o pipefail {0} - run: | - test "$(git rev-parse HEAD)" = "$EXPECTED_HEAD" - remote_head="$(git ls-remote origin "refs/heads/${SOURCE_BRANCH}" | cut -f1)" - test "$remote_head" = "$EXPECTED_HEAD" - python3 - <<'PY' | tee "${RUNNER_TEMP}/pr785-repair-receipt.txt" - import base64 - import json - import os - import urllib.request - from pathlib import Path - - repository = 'ContextualWisdomLab/.github' - expected_head = os.environ['EXPECTED_HEAD'] - token = os.environ['API_TOKEN'] - api_root = f'https://api.github.com/repos/{repository}' - - def request(method, endpoint, payload=None): - data = None if payload is None else json.dumps(payload).encode('utf-8') - req = urllib.request.Request( - api_root + endpoint, - data=data, - method=method, - headers={ - 'Accept': 'application/vnd.github+json', - 'Authorization': f'Bearer {token}', - 'X-GitHub-Api-Version': '2022-11-28', - 'User-Agent': 'cwl-pr785-blob-receipt', - }, - ) - with urllib.request.urlopen(req, timeout=30) as response: - return json.load(response) - - parent = request('GET', f'/git/commits/{expected_head}') - print(f"REPAIR_PARENT_SHA={expected_head}") - print(f"REPAIR_PARENT_TREE_SHA={parent['tree']['sha']}") - for label, path in ( - ('MATERIALIZER', 'scripts/ci/materialize_base_python_requirements.py'), - ('QUALITY_WORKFLOW', '.github/workflows/trusted-uv-materializer-quality-ci.yml'), - ('DOCTORING', 'docs/doctoring/trusted-requirements-directory-lock-discovery.md'), - ('CHANGELOG', 'CHANGELOG.md'), - ): - encoded = base64.b64encode(Path(path).read_bytes()).decode('ascii') - blob = request('POST', '/git/blobs', {'content': encoded, 'encoding': 'base64'}) - print(f"REPAIR_{label}_BLOB_SHA={blob['sha']}") - print('REPAIR_DELETE_PATH=.github/workflows/repair-requirements-directory-locks.yml') - print('REPAIR_DELETE_PATH=scripts/ci/apply_requirements_directory_lock_repair.py') - PY - - - name: Upload exact-head repair receipt - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v6.0.0 - with: - name: pr785-exact-head-repair - path: ${{ runner.temp }}/pr785-repair-receipt.txt - if-no-files-found: error - retention-days: 5 diff --git a/.github/workflows/trusted-uv-materializer-quality-ci.yml b/.github/workflows/trusted-uv-materializer-quality-ci.yml index 95642b55c..8f6ab968c 100644 --- a/.github/workflows/trusted-uv-materializer-quality-ci.yml +++ b/.github/workflows/trusted-uv-materializer-quality-ci.yml @@ -8,6 +8,7 @@ on: - "scripts/ci/materialize_base_python_requirements.py" - "tests/conftest.py" - "tests/test_materialize*.py" + - "tests/test_requirements_directory_lock_materialization.py" - "tests/test_trusted_uv*.py" - "tests/test_uv*.py" - "tests/test_repository_branch_coverage_*.py" @@ -20,6 +21,7 @@ on: - "scripts/ci/materialize_base_python_requirements.py" - "tests/conftest.py" - "tests/test_materialize*.py" + - "tests/test_requirements_directory_lock_materialization.py" - "tests/test_trusted_uv*.py" - "tests/test_uv*.py" - "tests/test_repository_branch_coverage_*.py" @@ -126,6 +128,7 @@ jobs: python -m coverage run -m pytest \ tests/test_materialize_base_python_requirements.py \ tests/test_materialize_uv_export_hash_contract.py \ + tests/test_requirements_directory_lock_materialization.py \ tests/test_trusted_uv_download_contract.py \ tests/test_trusted_uv_portability_and_streaming.py \ tests/test_uv_export_isolation_contract.py \ @@ -152,6 +155,7 @@ jobs: scripts/ci/materialize_base_python_requirements.py \ tests/test_materialize_base_python_requirements.py \ tests/test_materialize_uv_export_hash_contract.py \ + tests/test_requirements_directory_lock_materialization.py \ tests/test_trusted_uv_download_contract.py \ tests/test_trusted_uv_portability_and_streaming.py \ tests/test_uv_export_isolation_contract.py \ diff --git a/CHANGELOG.md b/CHANGELOG.md index e601de81b..6d0733a04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,5 +12,6 @@ Semantic Versioning where the repository publishes a release. ### Fixed +- Materialize complete hash-pinned `requirements/ci.txt` and other direct `requirements/*.txt` base-owned closures so isolated OpenCode coverage imports repository runtime dependencies without trusting pull-request metadata or broadening network access. - Bound both trusted-uv quality jobs to `github.event.pull_request.head.sha` and added a permanent two-checkout regression contract so exact-head compatibility, coverage, docstring, and compilation claims cannot silently measure GitHub's generated pull-request merge revision. - Made Strix treat only a single LiteLLM provider-error line containing NVIDIA NIM context and model-catalog 404 evidence as cross-model fallback evidence, rejecting cross-line signal assembly and provider-like target source literals; moved the public default to Nemotron 3 Super 120B and added a second NVIDIA hosted candidate before GitHub Models without neutralizing reported vulnerabilities. diff --git a/docs/doctoring/trusted-requirements-directory-lock-discovery.md b/docs/doctoring/trusted-requirements-directory-lock-discovery.md new file mode 100644 index 000000000..bb12f4b41 --- /dev/null +++ b/docs/doctoring/trusted-requirements-directory-lock-discovery.md @@ -0,0 +1,49 @@ +# Trusted requirements-directory lock discovery + +## Decision + +The central OpenCode coverage image materializes dependency closures only from +regular files in the authenticated pull-request base commit. In addition to the +conventional `requirements*.txt` and `requirements.lock` names, it recognizes a +`.txt` file that is a **direct child** of a directory named `requirements`, such +as `requirements/ci.txt` or `services/scoring_service/requirements/package.txt`. + +The path rule grants candidate status only. The existing content boundary still +requires nonempty hash-pinned logical requirements, records the exact trusted +source path in the manifest, and preflights each candidate as an independently +installable `pip --require-hashes` closure. Unpinned notes, input files, nested +descendants, symbolic links, pull-request-only files, and malformed Git tree +entries remain excluded. + +## Operational reason + +Concrete environment locks are frequently organized below a `requirements` +directory and use role names such as `ci.txt` or `package.txt`. Ignoring those +safe base-owned locks leaves isolated coverage without runtime dependencies even +when the repository maintains a complete generated closure. The resulting import +failure measures the coverage image rather than the changed production code. + +## Verification + +- A failing contract first proved that `requirements/ci.txt` was undiscoverable. +- Direct `requirements/*.txt` and nested-service equivalents are accepted. +- A deeper `requirements/nested/ci.txt` path and unrelated `docs/ci.txt` remain + ineligible. +- Only the hash-pinned candidate is emitted from a realistic temporary Git base; + unpinned `.in` and human-readable `.txt` files remain absent. +- The focused materializer suite, complete central suite, statement and branch + coverage, docstring gate, compilation, and exact-head security workflows are + required before merge. + +## References + +Python Packaging Authority. (2026). *Install requires vs requirements files*. +Python Packaging User Guide. +https://packaging.python.org/en/latest/discussions/install-requires-vs-requirements/ + +Python Packaging Authority. (2026). *Repeatable installs*. pip documentation. +https://pip.pypa.io/en/stable/topics/repeatable-installs/ + +Python Packaging Authority. (2026). *Requirements file format*. pip +documentation. +https://pip.pypa.io/en/stable/reference/requirements-file-format/ diff --git a/scripts/ci/apply_requirements_directory_lock_repair.py b/scripts/ci/apply_requirements_directory_lock_repair.py deleted file mode 100644 index 6440298a4..000000000 --- a/scripts/ci/apply_requirements_directory_lock_repair.py +++ /dev/null @@ -1,198 +0,0 @@ -#!/usr/bin/env python3 -"""Apply the reviewed requirements-directory lock materialization repair. - -This branch-only helper is executed by a same-repository pull-request workflow. -It edits only the permanent materializer, its permanent quality workflow, -doctoring, and changelog, then removes all temporary repair machinery before the -verified product commit is published. -""" - -from __future__ import annotations - -from pathlib import Path - -MATERIALIZER = Path("scripts/ci/materialize_base_python_requirements.py") -QUALITY_WORKFLOW = Path(".github/workflows/trusted-uv-materializer-quality-ci.yml") -DOCTORING = Path("docs/doctoring/trusted-requirements-directory-lock-discovery.md") -CHANGELOG = Path("CHANGELOG.md") -TEMPORARY_PATHS = ( - Path(".github/workflows/repair-requirements-directory-locks.yml"), - Path(".github/workflows/reopen-requirements-directory-locks.yml"), - Path("scripts/ci/apply_requirements_directory_lock_repair.py"), -) - - -def replace_once(path: Path, old: str, new: str, *, label: str) -> None: - """Replace one exact reviewed fragment or fail closed on source drift.""" - text = path.read_text(encoding="utf-8") - if new in text: - return - count = text.count(old) - if count != 1: - raise RuntimeError(f"{path}: expected one {label}, found {count}") - path.write_text(text.replace(old, new, 1), encoding="utf-8") - - -def repair_materializer() -> None: - """Recognize only direct text children of a requirements directory.""" - old_function = '''def _is_candidate_lock_name(name: str) -> bool: - """Return whether a file name is a possible pip requirements lock.""" - return name == "requirements.lock" or ( - fnmatch.fnmatch(name, "requirements*.txt") - and not fnmatch.fnmatch(name, "requirements-*-ci-hashes.txt") - ) -''' - new_function = old_function + ''' - -def _is_candidate_lock_path(path: pathlib.PurePosixPath) -> bool: - """Return whether one safe tracked path can name a pip requirements lock. - - In addition to conventional ``requirements*.txt`` names, repositories often - keep concrete environment closures as direct children such as - ``requirements/ci.txt`` or ``service/requirements/package.txt``. Only direct - ``.txt`` children of a directory named ``requirements`` gain this path-based - eligibility; content must still pass the independent complete hash-pin - validation before it reaches the trusted image build context. - """ - return _is_candidate_lock_name(path.name) or ( - path.suffix == ".txt" and path.parent.name == "requirements" - ) -''' - replace_once( - MATERIALIZER, - old_function, - new_function, - label="candidate-name function", - ) - replace_once( - MATERIALIZER, - " if _is_candidate_lock_name(candidate.name):\n", - " if _is_candidate_lock_path(candidate):\n", - label="candidate-path call", - ) - - -def repair_quality_workflow() -> None: - """Keep the new regression in every trigger, test, and compile gate.""" - workflow = QUALITY_WORKFLOW.read_text(encoding="utf-8") - direct_path = ' - "tests/test_requirements_directory_lock_materialization.py"\n' - if workflow.count(direct_path) == 0: - anchor = ' - "tests/test_materialize*.py"\n' - if workflow.count(anchor) != 2: - raise RuntimeError("expected two materializer path-filter anchors") - workflow = workflow.replace(anchor, anchor + direct_path) - elif workflow.count(direct_path) != 2: - raise RuntimeError("requirements-directory path filter is incomplete") - - test_target = " tests/test_requirements_directory_lock_materialization.py \\\n" - if test_target not in workflow: - anchor = " tests/test_materialize_uv_export_hash_contract.py \\\n" - if workflow.count(anchor) != 2: - raise RuntimeError("expected test and compile materializer anchors") - first = workflow.index(anchor) + len(anchor) - workflow = workflow[:first] + test_target + workflow[first:] - - if workflow.count(test_target) == 1: - compile_heading = " - name: Compile production and quality contracts\n" - compile_start = workflow.index(compile_heading) - compile_anchor = " tests/test_materialize_uv_export_hash_contract.py \\\n" - anchor_index = workflow.index(compile_anchor, compile_start) + len(compile_anchor) - workflow = workflow[:anchor_index] + test_target + workflow[anchor_index:] - if workflow.count(test_target) != 2: - raise RuntimeError("requirements-directory test target is incomplete") - - QUALITY_WORKFLOW.write_text(workflow, encoding="utf-8") - - -def write_doctoring() -> None: - """Record the trust boundary and APA 7 primary-source evidence.""" - DOCTORING.write_text( - """# Trusted requirements-directory lock discovery - -## Decision - -The central OpenCode coverage image materializes dependency closures only from -regular files in the authenticated pull-request base commit. In addition to the -conventional `requirements*.txt` and `requirements.lock` names, it recognizes a -`.txt` file that is a **direct child** of a directory named `requirements`, such -as `requirements/ci.txt` or `services/scoring_service/requirements/package.txt`. - -The path rule grants candidate status only. The existing content boundary still -requires nonempty hash-pinned logical requirements, records the exact trusted -source path in the manifest, and preflights each candidate as an independently -installable `pip --require-hashes` closure. Unpinned notes, input files, nested -descendants, symbolic links, pull-request-only files, and malformed Git tree -entries remain excluded. - -## Operational reason - -Concrete environment locks are frequently organized below a `requirements` -directory and use role names such as `ci.txt` or `package.txt`. Ignoring those -safe base-owned locks leaves isolated coverage without runtime dependencies even -when the repository maintains a complete generated closure. The resulting import -failure measures the coverage image rather than the changed production code. - -## Verification - -- A failing contract first proved that `requirements/ci.txt` was undiscoverable. -- Direct `requirements/*.txt` and nested-service equivalents are accepted. -- A deeper `requirements/nested/ci.txt` path and unrelated `docs/ci.txt` remain - ineligible. -- Only the hash-pinned candidate is emitted from a realistic temporary Git base; - unpinned `.in` and human-readable `.txt` files remain absent. -- The focused materializer suite, complete central suite, statement and branch - coverage, docstring gate, compilation, and exact-head security workflows are - required before merge. - -## References - -Python Packaging Authority. (2026). *Install requires vs requirements files*. -Python Packaging User Guide. -https://packaging.python.org/en/latest/discussions/install-requires-vs-requirements/ - -Python Packaging Authority. (2026). *Repeatable installs*. pip documentation. -https://pip.pypa.io/en/stable/topics/repeatable-installs/ - -Python Packaging Authority. (2026). *Requirements file format*. pip -documentation. -https://pip.pypa.io/en/stable/reference/requirements-file-format/ -""", - encoding="utf-8", - ) - - -def update_changelog() -> None: - """Record the coverage-environment compatibility repair under Unreleased.""" - bullet = ( - "- Materialize complete hash-pinned `requirements/ci.txt` and other " - "direct `requirements/*.txt` base-owned closures so isolated OpenCode " - "coverage imports repository runtime dependencies without trusting " - "pull-request metadata or broadening network access.\n" - ) - changelog = CHANGELOG.read_text(encoding="utf-8") - if bullet in changelog: - return - anchor = "### Fixed\n\n" - if changelog.count(anchor) != 1: - raise RuntimeError("expected one Unreleased Fixed heading") - CHANGELOG.write_text(changelog.replace(anchor, anchor + bullet, 1), encoding="utf-8") - - -def remove_temporary_paths() -> None: - """Delete all branch-only repair workflows and this transformer.""" - for path in TEMPORARY_PATHS: - if path.exists(): - path.unlink() - - -def main() -> None: - """Apply the permanent repair and leave a workflow-free product tree.""" - repair_materializer() - repair_quality_workflow() - write_doctoring() - update_changelog() - remove_temporary_paths() - - -if __name__ == "__main__": - main() diff --git a/scripts/ci/materialize_base_python_requirements.py b/scripts/ci/materialize_base_python_requirements.py old mode 100755 new mode 100644 index 98cdad459..147f024a2 --- a/scripts/ci/materialize_base_python_requirements.py +++ b/scripts/ci/materialize_base_python_requirements.py @@ -87,6 +87,21 @@ def _is_candidate_lock_name(name: str) -> bool: ) +def _is_candidate_lock_path(path: pathlib.PurePosixPath) -> bool: + """Return whether one safe tracked path can name a pip requirements lock. + + In addition to conventional ``requirements*.txt`` names, repositories often + keep concrete environment closures as direct children such as + ``requirements/ci.txt`` or ``service/requirements/package.txt``. Only direct + ``.txt`` children of a directory named ``requirements`` gain this path-based + eligibility; content must still pass the independent complete hash-pin + validation before it reaches the trusted image build context. + """ + return _is_candidate_lock_name(path.name) or ( + path.suffix == ".txt" and path.parent.name == "requirements" + ) + + def _requirement_lines(content: bytes) -> list[str]: """Return logical requirement lines, joining backslash line-continuations. @@ -459,7 +474,7 @@ def base_hash_locks(repo_root: pathlib.Path, base_sha: str) -> list[tuple[str, b regular_paths = {path for path, _candidate in regular_blobs} locks: list[tuple[str, bytes]] = [] for path, candidate in regular_blobs: - if _is_candidate_lock_name(candidate.name): + if _is_candidate_lock_path(candidate): content = _git(repo_root, "show", f"{base_sha}:{path}") if _is_hash_pinned(content): locks.append((path, content)) From bca94a74c8d2fe8d806a59075a25fb47a7887b20 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 08:49:32 +0900 Subject: [PATCH 12/33] test(coverage): reject unpinned requirements-directory locks --- ...irements_directory_lock_materialization.py | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/test_requirements_directory_lock_materialization.py b/tests/test_requirements_directory_lock_materialization.py index 229ab86c0..8070278b0 100644 --- a/tests/test_requirements_directory_lock_materialization.py +++ b/tests/test_requirements_directory_lock_materialization.py @@ -62,3 +62,30 @@ def test_materializes_hash_pinned_requirements_directory_lock( assert (output / "requirements-000.txt").read_text(encoding="utf-8").startswith( "numpy==2.5.1" ) + + +def test_rejects_global_hash_directive_with_unpinned_requirement( + tmp_path: Path, +) -> None: + """A global directive cannot make an unpinned direct-child lock trusted.""" + repo = tmp_path / "repo" + requirements_dir = repo / "requirements" + requirements_dir.mkdir(parents=True) + _git(repo, "init") + _git(repo, "config", "user.name", "Test") + _git(repo, "config", "user.email", "test@example.invalid") + + (requirements_dir / "ci.txt").write_text( + "--require-hashes\ndemo==1\n", + encoding="utf-8", + ) + _git(repo, "add", ".") + _git(repo, "commit", "-m", "base") + base_sha = _git(repo, "rev-parse", "HEAD") + + output = tmp_path / "output" + manifest = materializer.materialize(repo, base_sha, output) + + assert manifest == [] + assert not materializer._is_hash_pinned(b"--require-hashes\ndemo==1\n") + assert (output / "manifest.json").read_text(encoding="utf-8") == "[]\n" From 5a16611ef0ed2d7f2f538ceafd5f580d07348553 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 08:55:46 +0900 Subject: [PATCH 13/33] ci: add PR 785 global hash directive repair --- .../repair-pr785-global-require-hashes.yml | 173 ++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 .github/workflows/repair-pr785-global-require-hashes.yml diff --git a/.github/workflows/repair-pr785-global-require-hashes.yml b/.github/workflows/repair-pr785-global-require-hashes.yml new file mode 100644 index 000000000..54575769e --- /dev/null +++ b/.github/workflows/repair-pr785-global-require-hashes.yml @@ -0,0 +1,173 @@ +name: Repair PR 785 global hash directive boundary + +on: + push: + branches: [fix/coverage-materialize-requirements-directory-locks] + paths: + - ".github/pr785-global-hash.trigger" + +permissions: + contents: read + +concurrency: + group: repair-pr785-global-hash-directive + cancel-in-progress: false + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + +jobs: + repair: + if: >- + github.repository == 'ContextualWisdomLab/.github' && + github.ref == 'refs/heads/fix/coverage-materialize-requirements-directory-locks' + permissions: + contents: write + runs-on: ubuntu-24.04 + timeout-minutes: 40 + steps: + - name: Harden runner + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 + with: + egress-policy: audit + + - name: Check out exact test-first head + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ github.sha }} + fetch-depth: 0 + persist-credentials: false + + - name: Prove the new regression is red + shell: bash --noprofile --norc {0} + run: | + set +e + python -m pytest -q \ + tests/test_requirements_directory_lock_materialization.py::test_rejects_global_hash_directive_with_unpinned_requirement + status=$? + set -e + test "$status" -eq 1 + + - name: Require every non-directive logical requirement to carry trust evidence + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + python3 - <<'PY' + from pathlib import Path + + path = Path('scripts/ci/materialize_base_python_requirements.py') + source = path.read_text(encoding='utf-8') + old = ''' lines = _requirement_lines(content) + if not lines: + return False + return any(line == "--require-hashes" for line in lines) or all( + "--hash=" in line or line.startswith(("-r ", "--requirement ")) + for line in lines + ) +''' + new = ''' lines = _requirement_lines(content) + requirement_lines = [line for line in lines if line != "--require-hashes"] + if not requirement_lines: + return False + return all( + "--hash=" in line or line.startswith(("-r ", "--requirement ")) + for line in requirement_lines + ) +''' + if source.count(old) != 1: + raise SystemExit('expected one hash-pinned requirement classifier anchor') + path.write_text(source.replace(old, new, 1), encoding='utf-8') + + doctoring = Path('docs/doctoring/trusted-requirements-directory-lock-discovery.md') + text = doctoring.read_text(encoding='utf-8') + marker = 'The path rule grants candidate status only. The existing content boundary still\n' + addition = ( + 'A global `--require-hashes` directive is not evidence by itself: every non-directive ' + 'logical requirement must still carry an inline hash or a bounded requirement include.\n\n' + ) + if addition not in text: + if marker not in text: + raise SystemExit('doctoring trust-boundary anchor is absent') + text = text.replace(marker, addition + marker, 1) + doctoring.write_text(text, encoding='utf-8') + + changelog = Path('CHANGELOG.md') + text = changelog.read_text(encoding='utf-8') + entry = ( + '- Reject `requirements/*.txt` candidates where a global `--require-hashes` directive ' + 'coexists with an unpinned logical requirement.\n' + ) + if entry not in text: + marker = '### Fixed\n\n' + if marker not in text: + raise SystemExit('CHANGELOG Fixed anchor is absent') + changelog.write_text(text.replace(marker, marker + entry, 1), encoding='utf-8') + PY + rm -f \ + .github/workflows/repair-pr785-global-require-hashes.yml \ + .github/pr785-global-hash.trigger + git diff --check + + - name: Set up Python 3.14 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.14" + cache: pip + cache-dependency-path: requirements-opencode-review-ci-hashes.txt + + - name: Install exact hash-locked tooling + run: >- + python -m pip install --disable-pip-version-check --require-hashes + -r requirements-opencode-review-ci-hashes.txt + + - name: Verify focused and complete quality + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + cat >"${RUNNER_TEMP}/pr785-coveragerc" <<'EOF' + [run] + branch = True + include = + scripts/ci/materialize_base_python_requirements.py + [report] + fail_under = 100 + show_missing = True + EOF + export COVERAGE_RCFILE="${RUNNER_TEMP}/pr785-coveragerc" + python -m coverage erase + python -m coverage run -m pytest -q \ + tests/test_materialize_base_python_requirements.py \ + tests/test_materialize_uv_export_hash_contract.py \ + tests/test_requirements_directory_lock_materialization.py \ + tests/test_trusted_uv_download_contract.py \ + tests/test_trusted_uv_portability_and_streaming.py \ + tests/test_uv_export_isolation_contract.py \ + tests/test_uv_redirect_and_coverage_contract.py \ + tests/test_uv_redirect_boundary.py \ + tests/test_uv_workspace_fail_closed.py \ + tests/test_trusted_uv_materializer_quality_workflow_contract.py + python -m coverage report --fail-under=100 + unset COVERAGE_RCFILE + python -m pytest -q tests + python -m interrogate --fail-under=100 scripts/ci/materialize_base_python_requirements.py + python -m compileall -q scripts/ci/materialize_base_python_requirements.py tests/test_requirements_directory_lock_materialization.py + git diff --check + + - name: Publish verified workflow-free repair + env: + PUSH_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN }} + TARGET_BRANCH: fix/coverage-materialize-requirements-directory-locks + EXPECTED_HEAD: ${{ github.sha }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + test -n "${PUSH_TOKEN:-}" + test "$(git rev-parse HEAD)" = "$EXPECTED_HEAD" + remote_head="$(git ls-remote origin "refs/heads/${TARGET_BRANCH}" | cut -f1)" + test "$remote_head" = "$EXPECTED_HEAD" + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add -A + git diff --cached --check + git commit -m "fix(coverage): reject unpinned global-hash locks" + echo "::add-mask::$PUSH_TOKEN" + git remote set-url origin "https://x-access-token:${PUSH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" + git push --force-with-lease="refs/heads/${TARGET_BRANCH}:${EXPECTED_HEAD}" \ + origin "HEAD:refs/heads/${TARGET_BRANCH}" From 2860ce08571f5700eef81100458f3913d0e89e12 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 08:56:01 +0900 Subject: [PATCH 14/33] ci: trigger PR 785 hash directive repair --- .github/pr785-global-hash.trigger | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/pr785-global-hash.trigger diff --git a/.github/pr785-global-hash.trigger b/.github/pr785-global-hash.trigger new file mode 100644 index 000000000..99b8e01c9 --- /dev/null +++ b/.github/pr785-global-hash.trigger @@ -0,0 +1 @@ +Trigger the reviewed global `--require-hashes` fail-closed repair. From 7be7b6d5fbb5aab378e231764200207d31b0369f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 09:00:31 +0900 Subject: [PATCH 15/33] chore(coverage): remove branch-writing repair workflow --- .../repair-pr785-global-require-hashes.yml | 173 ------------------ 1 file changed, 173 deletions(-) delete mode 100644 .github/workflows/repair-pr785-global-require-hashes.yml diff --git a/.github/workflows/repair-pr785-global-require-hashes.yml b/.github/workflows/repair-pr785-global-require-hashes.yml deleted file mode 100644 index 54575769e..000000000 --- a/.github/workflows/repair-pr785-global-require-hashes.yml +++ /dev/null @@ -1,173 +0,0 @@ -name: Repair PR 785 global hash directive boundary - -on: - push: - branches: [fix/coverage-materialize-requirements-directory-locks] - paths: - - ".github/pr785-global-hash.trigger" - -permissions: - contents: read - -concurrency: - group: repair-pr785-global-hash-directive - cancel-in-progress: false - -env: - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true - -jobs: - repair: - if: >- - github.repository == 'ContextualWisdomLab/.github' && - github.ref == 'refs/heads/fix/coverage-materialize-requirements-directory-locks' - permissions: - contents: write - runs-on: ubuntu-24.04 - timeout-minutes: 40 - steps: - - name: Harden runner - uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 - with: - egress-policy: audit - - - name: Check out exact test-first head - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - ref: ${{ github.sha }} - fetch-depth: 0 - persist-credentials: false - - - name: Prove the new regression is red - shell: bash --noprofile --norc {0} - run: | - set +e - python -m pytest -q \ - tests/test_requirements_directory_lock_materialization.py::test_rejects_global_hash_directive_with_unpinned_requirement - status=$? - set -e - test "$status" -eq 1 - - - name: Require every non-directive logical requirement to carry trust evidence - shell: bash --noprofile --norc -e -o pipefail {0} - run: | - python3 - <<'PY' - from pathlib import Path - - path = Path('scripts/ci/materialize_base_python_requirements.py') - source = path.read_text(encoding='utf-8') - old = ''' lines = _requirement_lines(content) - if not lines: - return False - return any(line == "--require-hashes" for line in lines) or all( - "--hash=" in line or line.startswith(("-r ", "--requirement ")) - for line in lines - ) -''' - new = ''' lines = _requirement_lines(content) - requirement_lines = [line for line in lines if line != "--require-hashes"] - if not requirement_lines: - return False - return all( - "--hash=" in line or line.startswith(("-r ", "--requirement ")) - for line in requirement_lines - ) -''' - if source.count(old) != 1: - raise SystemExit('expected one hash-pinned requirement classifier anchor') - path.write_text(source.replace(old, new, 1), encoding='utf-8') - - doctoring = Path('docs/doctoring/trusted-requirements-directory-lock-discovery.md') - text = doctoring.read_text(encoding='utf-8') - marker = 'The path rule grants candidate status only. The existing content boundary still\n' - addition = ( - 'A global `--require-hashes` directive is not evidence by itself: every non-directive ' - 'logical requirement must still carry an inline hash or a bounded requirement include.\n\n' - ) - if addition not in text: - if marker not in text: - raise SystemExit('doctoring trust-boundary anchor is absent') - text = text.replace(marker, addition + marker, 1) - doctoring.write_text(text, encoding='utf-8') - - changelog = Path('CHANGELOG.md') - text = changelog.read_text(encoding='utf-8') - entry = ( - '- Reject `requirements/*.txt` candidates where a global `--require-hashes` directive ' - 'coexists with an unpinned logical requirement.\n' - ) - if entry not in text: - marker = '### Fixed\n\n' - if marker not in text: - raise SystemExit('CHANGELOG Fixed anchor is absent') - changelog.write_text(text.replace(marker, marker + entry, 1), encoding='utf-8') - PY - rm -f \ - .github/workflows/repair-pr785-global-require-hashes.yml \ - .github/pr785-global-hash.trigger - git diff --check - - - name: Set up Python 3.14 - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 - with: - python-version: "3.14" - cache: pip - cache-dependency-path: requirements-opencode-review-ci-hashes.txt - - - name: Install exact hash-locked tooling - run: >- - python -m pip install --disable-pip-version-check --require-hashes - -r requirements-opencode-review-ci-hashes.txt - - - name: Verify focused and complete quality - shell: bash --noprofile --norc -e -o pipefail {0} - run: | - cat >"${RUNNER_TEMP}/pr785-coveragerc" <<'EOF' - [run] - branch = True - include = - scripts/ci/materialize_base_python_requirements.py - [report] - fail_under = 100 - show_missing = True - EOF - export COVERAGE_RCFILE="${RUNNER_TEMP}/pr785-coveragerc" - python -m coverage erase - python -m coverage run -m pytest -q \ - tests/test_materialize_base_python_requirements.py \ - tests/test_materialize_uv_export_hash_contract.py \ - tests/test_requirements_directory_lock_materialization.py \ - tests/test_trusted_uv_download_contract.py \ - tests/test_trusted_uv_portability_and_streaming.py \ - tests/test_uv_export_isolation_contract.py \ - tests/test_uv_redirect_and_coverage_contract.py \ - tests/test_uv_redirect_boundary.py \ - tests/test_uv_workspace_fail_closed.py \ - tests/test_trusted_uv_materializer_quality_workflow_contract.py - python -m coverage report --fail-under=100 - unset COVERAGE_RCFILE - python -m pytest -q tests - python -m interrogate --fail-under=100 scripts/ci/materialize_base_python_requirements.py - python -m compileall -q scripts/ci/materialize_base_python_requirements.py tests/test_requirements_directory_lock_materialization.py - git diff --check - - - name: Publish verified workflow-free repair - env: - PUSH_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN }} - TARGET_BRANCH: fix/coverage-materialize-requirements-directory-locks - EXPECTED_HEAD: ${{ github.sha }} - shell: bash --noprofile --norc -e -o pipefail {0} - run: | - test -n "${PUSH_TOKEN:-}" - test "$(git rev-parse HEAD)" = "$EXPECTED_HEAD" - remote_head="$(git ls-remote origin "refs/heads/${TARGET_BRANCH}" | cut -f1)" - test "$remote_head" = "$EXPECTED_HEAD" - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add -A - git diff --cached --check - git commit -m "fix(coverage): reject unpinned global-hash locks" - echo "::add-mask::$PUSH_TOKEN" - git remote set-url origin "https://x-access-token:${PUSH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" - git push --force-with-lease="refs/heads/${TARGET_BRANCH}:${EXPECTED_HEAD}" \ - origin "HEAD:refs/heads/${TARGET_BRANCH}" From e2c2daed4bd47be30960b9dfba141828d90c7a20 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 09:00:42 +0900 Subject: [PATCH 16/33] chore(coverage): remove repair trigger artifact --- .github/pr785-global-hash.trigger | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .github/pr785-global-hash.trigger diff --git a/.github/pr785-global-hash.trigger b/.github/pr785-global-hash.trigger deleted file mode 100644 index 99b8e01c9..000000000 --- a/.github/pr785-global-hash.trigger +++ /dev/null @@ -1 +0,0 @@ -Trigger the reviewed global `--require-hashes` fail-closed repair. From 574b1998c1def074b821f1d3df1f62d71c5a77db Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 09:04:45 +0900 Subject: [PATCH 17/33] ci: add bounded PR 785 hash boundary applier --- .../workflows/apply-pr785-hash-boundary.yml | 135 ++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 .github/workflows/apply-pr785-hash-boundary.yml diff --git a/.github/workflows/apply-pr785-hash-boundary.yml b/.github/workflows/apply-pr785-hash-boundary.yml new file mode 100644 index 000000000..5a4b25150 --- /dev/null +++ b/.github/workflows/apply-pr785-hash-boundary.yml @@ -0,0 +1,135 @@ +name: Apply PR 785 hash boundary + +on: + push: + branches: [fix/coverage-materialize-requirements-directory-locks] + paths: + - ".github/pr785-hash-boundary.trigger" + +permissions: + contents: read + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + +jobs: + apply: + if: >- + github.repository == 'ContextualWisdomLab/.github' && + github.ref == 'refs/heads/fix/coverage-materialize-requirements-directory-locks' + permissions: + contents: write + runs-on: ubuntu-24.04 + timeout-minutes: 25 + steps: + - name: Harden runner + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 + with: + egress-policy: audit + + - name: Check out exact trigger + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ github.sha }} + fetch-depth: 0 + persist-credentials: false + + - name: Apply fail-closed global directive repair + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + python3 - <<'PY' + from pathlib import Path + + source_path = Path('scripts/ci/materialize_base_python_requirements.py') + source = source_path.read_text(encoding='utf-8') + old = ''' lines = _requirement_lines(content) + if not lines: + return False + return any(line == "--require-hashes" for line in lines) or all( + "--hash=" in line or line.startswith(("-r ", "--requirement ")) + for line in lines + ) +''' + new = ''' lines = _requirement_lines(content) + requirement_lines = [line for line in lines if line != "--require-hashes"] + if not requirement_lines: + return False + return all( + "--hash=" in line or line.startswith(("-r ", "--requirement ")) + for line in requirement_lines + ) +''' + if source.count(old) != 1: + raise SystemExit('hash classifier anchor changed') + source_path.write_text(source.replace(old, new, 1), encoding='utf-8') + + doctoring = Path('docs/doctoring/trusted-requirements-directory-lock-discovery.md') + text = doctoring.read_text(encoding='utf-8') + sentence = ( + 'A global `--require-hashes` directive is not trust evidence by itself; ' + 'every non-directive logical requirement must still carry an inline hash ' + 'or a bounded requirement include.\n\n' + ) + anchor = 'The path rule grants candidate status only. ' + if sentence not in text: + if anchor not in text: + raise SystemExit('doctoring anchor changed') + doctoring.write_text(text.replace(anchor, sentence + anchor, 1), encoding='utf-8') + + changelog = Path('CHANGELOG.md') + text = changelog.read_text(encoding='utf-8') + entry = ( + '- Reject requirement candidates where a global `--require-hashes` directive ' + 'coexists with an unpinned logical requirement.\n' + ) + if entry not in text: + anchor = '### Fixed\n\n' + if anchor not in text: + raise SystemExit('changelog anchor changed') + changelog.write_text(text.replace(anchor, anchor + entry, 1), encoding='utf-8') + PY + rm -f \ + .github/workflows/apply-pr785-hash-boundary.yml \ + .github/pr785-hash-boundary.trigger + git diff --check + + - name: Set up Python 3.14 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.14" + cache: pip + cache-dependency-path: requirements-opencode-review-ci-hashes.txt + + - name: Install exact tooling + run: >- + python -m pip install --disable-pip-version-check --require-hashes + -r requirements-opencode-review-ci-hashes.txt + + - name: Verify regression and complete tests + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + python -m pytest -q \ + tests/test_requirements_directory_lock_materialization.py::test_rejects_global_hash_directive_with_unpinned_requirement + python -m pytest -q tests + python -m compileall -q scripts/ci/materialize_base_python_requirements.py tests/test_requirements_directory_lock_materialization.py + git diff --check + + - name: Publish verified source repair + env: + PUSH_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN }} + TARGET_BRANCH: fix/coverage-materialize-requirements-directory-locks + EXPECTED_HEAD: ${{ github.sha }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + test -n "${PUSH_TOKEN:-}" + remote_head="$(git ls-remote origin "refs/heads/${TARGET_BRANCH}" | cut -f1)" + test "$remote_head" = "$EXPECTED_HEAD" + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add -A + git diff --cached --check + git commit -m "fix(coverage): reject unpinned global-hash locks" + echo "::add-mask::$PUSH_TOKEN" + git remote set-url origin "https://x-access-token:${PUSH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" + git push --force-with-lease="refs/heads/${TARGET_BRANCH}:${EXPECTED_HEAD}" \ + origin "HEAD:refs/heads/${TARGET_BRANCH}" From ca744889ed4178cd2fad670f643e8a045d4a7e09 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 09:05:02 +0900 Subject: [PATCH 18/33] ci: trigger bounded PR 785 hash repair --- .github/pr785-hash-boundary.trigger | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/pr785-hash-boundary.trigger diff --git a/.github/pr785-hash-boundary.trigger b/.github/pr785-hash-boundary.trigger new file mode 100644 index 000000000..839a59c80 --- /dev/null +++ b/.github/pr785-hash-boundary.trigger @@ -0,0 +1 @@ +Trigger the bounded global hash-directive repair after its workflow exists. From cc49028867e336547b341d4bdc256331a2c3ad51 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 09:10:32 +0900 Subject: [PATCH 19/33] test(coverage): define global hash directive trust boundary --- ...irements_directory_lock_materialization.py | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/test_requirements_directory_lock_materialization.py b/tests/test_requirements_directory_lock_materialization.py index 8070278b0..a5f7e38f8 100644 --- a/tests/test_requirements_directory_lock_materialization.py +++ b/tests/test_requirements_directory_lock_materialization.py @@ -5,6 +5,8 @@ import subprocess from pathlib import Path, PurePosixPath +import pytest + from scripts.ci import materialize_base_python_requirements as materializer @@ -64,6 +66,35 @@ def test_materializes_hash_pinned_requirements_directory_lock( ) +@pytest.mark.parametrize( + ("content", "expected"), + ( + (b"--require-hashes\n", False), + (b"--require-hashes\ndemo==1\n", False), + ( + b"--require-hashes\ndemo==1 --hash=sha256:" + + (b"a" * 64) + + b"\n", + True, + ), + ( + b"pinned==1 --hash=sha256:" + + (b"b" * 64) + + b"\nunpinned==2\n", + False, + ), + (b"--index-url https://packages.example.invalid/simple\n", False), + (b"--requirement other.txt\n", True), + ), +) +def test_global_hash_directive_does_not_replace_per_requirement_trust( + content: bytes, + expected: bool, +) -> None: + """Only substantive hashed pins or bounded requirement includes qualify.""" + assert materializer._is_hash_pinned(content) is expected + + def test_rejects_global_hash_directive_with_unpinned_requirement( tmp_path: Path, ) -> None: From e4c823725ff5e50a20436c2c82a8122abf26de22 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 09:12:30 +0900 Subject: [PATCH 20/33] fix(coverage): reject unpinned global-hash locks --- scripts/ci/materialize_base_python_requirements.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/scripts/ci/materialize_base_python_requirements.py b/scripts/ci/materialize_base_python_requirements.py index 147f024a2..807c33aa4 100644 --- a/scripts/ci/materialize_base_python_requirements.py +++ b/scripts/ci/materialize_base_python_requirements.py @@ -130,15 +130,19 @@ def _is_hash_pinned(content: bytes) -> bool: unpinned or PR-mutable requirements file is still excluded from the networked build context. Hash syntax cannot prove that a file includes every transitive dependency, so the trusted image installer separately preflights every - candidate as an independent ``--require-hashes`` closure. An empty file - carries no installable dependency and is not materialized. + candidate as an independent ``--require-hashes`` closure. A global + ``--require-hashes`` directive is not trust evidence by itself: every + non-directive logical requirement must still carry an inline hash or a + bounded requirement include. An empty file or directive-only file carries no + installable dependency and is not materialized. """ lines = _requirement_lines(content) - if not lines: + requirement_lines = [line for line in lines if line != "--require-hashes"] + if not requirement_lines: return False - return any(line == "--require-hashes" for line in lines) or all( + return all( "--hash=" in line or line.startswith(("-r ", "--requirement ")) - for line in lines + for line in requirement_lines ) From 080fb77f8fee7f37f8729acfe1673f05cd105d21 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 09:13:09 +0900 Subject: [PATCH 21/33] chore(ci): remove PR 785 repair trigger --- .github/pr785-hash-boundary.trigger | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .github/pr785-hash-boundary.trigger diff --git a/.github/pr785-hash-boundary.trigger b/.github/pr785-hash-boundary.trigger deleted file mode 100644 index 839a59c80..000000000 --- a/.github/pr785-hash-boundary.trigger +++ /dev/null @@ -1 +0,0 @@ -Trigger the bounded global hash-directive repair after its workflow exists. From 74c210c8d91716bb4aa66d3b8ab8335b8ec83a9f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 09:13:21 +0900 Subject: [PATCH 22/33] chore(ci): remove PR 785 repair workflow --- .../workflows/apply-pr785-hash-boundary.yml | 135 ------------------ 1 file changed, 135 deletions(-) delete mode 100644 .github/workflows/apply-pr785-hash-boundary.yml diff --git a/.github/workflows/apply-pr785-hash-boundary.yml b/.github/workflows/apply-pr785-hash-boundary.yml deleted file mode 100644 index 5a4b25150..000000000 --- a/.github/workflows/apply-pr785-hash-boundary.yml +++ /dev/null @@ -1,135 +0,0 @@ -name: Apply PR 785 hash boundary - -on: - push: - branches: [fix/coverage-materialize-requirements-directory-locks] - paths: - - ".github/pr785-hash-boundary.trigger" - -permissions: - contents: read - -env: - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true - -jobs: - apply: - if: >- - github.repository == 'ContextualWisdomLab/.github' && - github.ref == 'refs/heads/fix/coverage-materialize-requirements-directory-locks' - permissions: - contents: write - runs-on: ubuntu-24.04 - timeout-minutes: 25 - steps: - - name: Harden runner - uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 - with: - egress-policy: audit - - - name: Check out exact trigger - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - ref: ${{ github.sha }} - fetch-depth: 0 - persist-credentials: false - - - name: Apply fail-closed global directive repair - shell: bash --noprofile --norc -e -o pipefail {0} - run: | - python3 - <<'PY' - from pathlib import Path - - source_path = Path('scripts/ci/materialize_base_python_requirements.py') - source = source_path.read_text(encoding='utf-8') - old = ''' lines = _requirement_lines(content) - if not lines: - return False - return any(line == "--require-hashes" for line in lines) or all( - "--hash=" in line or line.startswith(("-r ", "--requirement ")) - for line in lines - ) -''' - new = ''' lines = _requirement_lines(content) - requirement_lines = [line for line in lines if line != "--require-hashes"] - if not requirement_lines: - return False - return all( - "--hash=" in line or line.startswith(("-r ", "--requirement ")) - for line in requirement_lines - ) -''' - if source.count(old) != 1: - raise SystemExit('hash classifier anchor changed') - source_path.write_text(source.replace(old, new, 1), encoding='utf-8') - - doctoring = Path('docs/doctoring/trusted-requirements-directory-lock-discovery.md') - text = doctoring.read_text(encoding='utf-8') - sentence = ( - 'A global `--require-hashes` directive is not trust evidence by itself; ' - 'every non-directive logical requirement must still carry an inline hash ' - 'or a bounded requirement include.\n\n' - ) - anchor = 'The path rule grants candidate status only. ' - if sentence not in text: - if anchor not in text: - raise SystemExit('doctoring anchor changed') - doctoring.write_text(text.replace(anchor, sentence + anchor, 1), encoding='utf-8') - - changelog = Path('CHANGELOG.md') - text = changelog.read_text(encoding='utf-8') - entry = ( - '- Reject requirement candidates where a global `--require-hashes` directive ' - 'coexists with an unpinned logical requirement.\n' - ) - if entry not in text: - anchor = '### Fixed\n\n' - if anchor not in text: - raise SystemExit('changelog anchor changed') - changelog.write_text(text.replace(anchor, anchor + entry, 1), encoding='utf-8') - PY - rm -f \ - .github/workflows/apply-pr785-hash-boundary.yml \ - .github/pr785-hash-boundary.trigger - git diff --check - - - name: Set up Python 3.14 - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 - with: - python-version: "3.14" - cache: pip - cache-dependency-path: requirements-opencode-review-ci-hashes.txt - - - name: Install exact tooling - run: >- - python -m pip install --disable-pip-version-check --require-hashes - -r requirements-opencode-review-ci-hashes.txt - - - name: Verify regression and complete tests - shell: bash --noprofile --norc -e -o pipefail {0} - run: | - python -m pytest -q \ - tests/test_requirements_directory_lock_materialization.py::test_rejects_global_hash_directive_with_unpinned_requirement - python -m pytest -q tests - python -m compileall -q scripts/ci/materialize_base_python_requirements.py tests/test_requirements_directory_lock_materialization.py - git diff --check - - - name: Publish verified source repair - env: - PUSH_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN }} - TARGET_BRANCH: fix/coverage-materialize-requirements-directory-locks - EXPECTED_HEAD: ${{ github.sha }} - shell: bash --noprofile --norc -e -o pipefail {0} - run: | - test -n "${PUSH_TOKEN:-}" - remote_head="$(git ls-remote origin "refs/heads/${TARGET_BRANCH}" | cut -f1)" - test "$remote_head" = "$EXPECTED_HEAD" - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add -A - git diff --cached --check - git commit -m "fix(coverage): reject unpinned global-hash locks" - echo "::add-mask::$PUSH_TOKEN" - git remote set-url origin "https://x-access-token:${PUSH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" - git push --force-with-lease="refs/heads/${TARGET_BRANCH}:${EXPECTED_HEAD}" \ - origin "HEAD:refs/heads/${TARGET_BRANCH}" From 7d669fb5402f420c29bca778b551b2fde9385112 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 09:13:55 +0900 Subject: [PATCH 23/33] docs(coverage): document global hash directive boundary --- ...sted-requirements-directory-lock-discovery.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/docs/doctoring/trusted-requirements-directory-lock-discovery.md b/docs/doctoring/trusted-requirements-directory-lock-discovery.md index bb12f4b41..e21ee7f3b 100644 --- a/docs/doctoring/trusted-requirements-directory-lock-discovery.md +++ b/docs/doctoring/trusted-requirements-directory-lock-discovery.md @@ -8,12 +8,14 @@ conventional `requirements*.txt` and `requirements.lock` names, it recognizes a `.txt` file that is a **direct child** of a directory named `requirements`, such as `requirements/ci.txt` or `services/scoring_service/requirements/package.txt`. -The path rule grants candidate status only. The existing content boundary still -requires nonempty hash-pinned logical requirements, records the exact trusted -source path in the manifest, and preflights each candidate as an independently -installable `pip --require-hashes` closure. Unpinned notes, input files, nested -descendants, symbolic links, pull-request-only files, and malformed Git tree -entries remain excluded. +The path rule grants candidate status only. A global `--require-hashes` +directive is not trust evidence by itself: every non-directive logical +requirement must still carry an inline hash or a bounded requirement include. +The existing content boundary records the exact trusted source path in the +manifest and preflights each candidate as an independently installable +`pip --require-hashes` closure. Unpinned notes, directive-only files, input +files, nested descendants, symbolic links, pull-request-only files, and +malformed Git tree entries remain excluded. ## Operational reason @@ -29,6 +31,8 @@ failure measures the coverage image rather than the changed production code. - Direct `requirements/*.txt` and nested-service equivalents are accepted. - A deeper `requirements/nested/ci.txt` path and unrelated `docs/ci.txt` remain ineligible. +- A global `--require-hashes` directive combined with an unpinned requirement is + rejected rather than promoted into the networked coverage image. - Only the hash-pinned candidate is emitted from a realistic temporary Git base; unpinned `.in` and human-readable `.txt` files remain absent. - The focused materializer suite, complete central suite, statement and branch From 23d1bcec305e89b5928775a3170d44bc3ffcfcdb Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 09:29:46 +0900 Subject: [PATCH 24/33] ci: add pointer-based PR 785 finalizer --- .github/workflows/finalize-pr785-pointer.yml | 132 +++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 .github/workflows/finalize-pr785-pointer.yml diff --git a/.github/workflows/finalize-pr785-pointer.yml b/.github/workflows/finalize-pr785-pointer.yml new file mode 100644 index 000000000..64f9523b1 --- /dev/null +++ b/.github/workflows/finalize-pr785-pointer.yml @@ -0,0 +1,132 @@ +name: Finalize PR 785 by immutable pointer + +on: + push: + branches: [fix/coverage-materialize-requirements-directory-locks] + paths: + - ".github/pr785-pointer.trigger" + +permissions: + contents: read + +jobs: + finalize: + permissions: + contents: write + issues: write + runs-on: ubuntu-24.04 + timeout-minutes: 40 + steps: + - name: Harden runner + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 + with: + egress-policy: audit + + - name: Check out exact trigger + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + ref: ${{ github.sha }} + fetch-depth: 1 + persist-credentials: false + + - name: Reconcile legacy test and remove transient files + shell: bash --noprofile --norc -e -o pipefail {0} + env: + EXPECTED_HEAD: ${{ github.sha }} + run: | + test "$(git rev-parse HEAD)" = "$EXPECTED_HEAD" + python3 - <<'PY' + from pathlib import Path + + path = Path('tests/test_materialize_base_python_requirements.py') + source = path.read_text(encoding='utf-8') + old = 'assert materializer._is_hash_pinned(b"--require-hashes\\ndemo==1\\n")' + new = 'assert not materializer._is_hash_pinned(b"--require-hashes\\ndemo==1\\n")' + if source.count(old) != 1: + raise SystemExit(f'legacy hash-directive assertion count={source.count(old)}') + path.write_text(source.replace(old, new, 1), encoding='utf-8') + PY + rm -f \ + .github/pr785-pointer.trigger \ + .github/workflows/finalize-pr785-pointer.yml + git diff --check + + - name: Set up Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 + with: + python-version: '3.14' + cache: pip + cache-dependency-path: requirements-opencode-review-ci-hashes.txt + + - name: Install tooling + run: python -m pip install --disable-pip-version-check --require-hashes -r requirements-opencode-review-ci-hashes.txt + + - name: Verify full materializer quality + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + cat >"${RUNNER_TEMP}/pr785-coveragerc" <<'EOF' + [run] + branch = True + include = + scripts/ci/materialize_base_python_requirements.py + [report] + fail_under = 100 + show_missing = True + EOF + export COVERAGE_RCFILE="${RUNNER_TEMP}/pr785-coveragerc" + python -m coverage erase + python -m coverage run -m pytest -q tests/test_materialize_base_python_requirements.py tests/test_materialize_uv_export_hash_contract.py tests/test_requirements_directory_lock_materialization.py tests/test_trusted_uv_download_contract.py tests/test_trusted_uv_portability_and_streaming.py tests/test_uv_export_isolation_contract.py tests/test_uv_redirect_and_coverage_contract.py tests/test_uv_redirect_boundary.py tests/test_uv_workspace_fail_closed.py tests/test_trusted_uv_materializer_quality_workflow_contract.py + python -m coverage report --fail-under=100 + unset COVERAGE_RCFILE + python -m pytest -q tests + python -m interrogate --fail-under=100 scripts/ci/materialize_base_python_requirements.py + git diff --check + + - name: Create immutable workflow-free commit + shell: bash --noprofile --norc -e -o pipefail {0} + env: + API_TOKEN: ${{ github.token }} + EXPECTED_HEAD: ${{ github.sha }} + run: | + python3 - <<'PY' | tee "${RUNNER_TEMP}/pr785-final.txt" + import base64, json, os, subprocess, urllib.request + from pathlib import Path + repo='ContextualWisdomLab/.github' + parent=os.environ['EXPECTED_HEAD'] + token=os.environ['API_TOKEN'] + root=f'https://api.github.com/repos/{repo}' + expected={'.github/pr785-pointer.trigger','.github/workflows/finalize-pr785-pointer.yml','tests/test_materialize_base_python_requirements.py'} + def request(method, endpoint, payload=None): + req=urllib.request.Request(root+endpoint,data=None if payload is None else json.dumps(payload).encode(),method=method,headers={'Accept':'application/vnd.github+json','Authorization':f'Bearer {token}','X-GitHub-Api-Version':'2022-11-28','User-Agent':'cwl-pr785-finalizer'}) + with urllib.request.urlopen(req,timeout=60) as response: + return json.load(response) + raw=subprocess.check_output(['git','diff','--name-status','-z','HEAD']).decode().split('\0') + changes=[]; i=0 + while i < len(raw)-1: + changes.append((raw[i],raw[i+1])); i += 2 + actual={p for _,p in changes} + if actual != expected: + raise SystemExit(f'path mismatch missing={sorted(expected-actual)} extra={sorted(actual-expected)}') + parent_obj=request('GET',f'/git/commits/{parent}') + entries=[] + for status,path in changes: + if status == 'D': + entries.append({'path':path,'mode':'100644','type':'blob','sha':None}) + else: + blob=request('POST','/git/blobs',{'content':base64.b64encode(Path(path).read_bytes()).decode(),'encoding':'base64'}) + entries.append({'path':path,'mode':'100644','type':'blob','sha':blob['sha']}) + tree=request('POST','/git/trees',{'base_tree':parent_obj['tree']['sha'],'tree':entries}) + commit=request('POST','/git/commits',{'message':'test(coverage): reject unpinned global-hash requirements','tree':tree['sha'],'parents':[parent]}) + print('PR785_FINAL_PARENT_SHA='+parent) + print('PR785_FINAL_COMMIT_SHA='+commit['sha']) + PY + + - name: Publish final pointer + shell: bash --noprofile --norc -e -o pipefail {0} + env: + GH_TOKEN: ${{ github.token }} + EXPECTED_HEAD: ${{ github.sha }} + run: | + commit_sha="$(sed -n 's/^PR785_FINAL_COMMIT_SHA=//p' "${RUNNER_TEMP}/pr785-final.txt")" + test "${#commit_sha}" -eq 40 + gh api --method POST repos/ContextualWisdomLab/.github/issues/785/comments -f "body=PR785_FINAL_PARENT_SHA=${EXPECTED_HEAD}%0APR785_FINAL_COMMIT_SHA=${commit_sha}" From cd4e5f8a6717ca154f468d3189a3cba549c8f5b7 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 09:29:56 +0900 Subject: [PATCH 25/33] ci: trigger pointer-based PR 785 finalizer --- .github/pr785-pointer.trigger | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/pr785-pointer.trigger diff --git a/.github/pr785-pointer.trigger b/.github/pr785-pointer.trigger new file mode 100644 index 000000000..0e146859e --- /dev/null +++ b/.github/pr785-pointer.trigger @@ -0,0 +1 @@ +Trigger the pointer-based workflow-free PR 785 finalization. From bbeeab2dbb750a54cc420e86d22537874678ac59 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 7 Aug 2026 00:31:31 +0000 Subject: [PATCH 26/33] test(coverage): reject unpinned global-hash requirements --- .github/pr785-pointer.trigger | 1 - .github/workflows/finalize-pr785-pointer.yml | 132 ------------------ ...st_materialize_base_python_requirements.py | 2 +- 3 files changed, 1 insertion(+), 134 deletions(-) delete mode 100644 .github/pr785-pointer.trigger delete mode 100644 .github/workflows/finalize-pr785-pointer.yml diff --git a/.github/pr785-pointer.trigger b/.github/pr785-pointer.trigger deleted file mode 100644 index 0e146859e..000000000 --- a/.github/pr785-pointer.trigger +++ /dev/null @@ -1 +0,0 @@ -Trigger the pointer-based workflow-free PR 785 finalization. diff --git a/.github/workflows/finalize-pr785-pointer.yml b/.github/workflows/finalize-pr785-pointer.yml deleted file mode 100644 index 64f9523b1..000000000 --- a/.github/workflows/finalize-pr785-pointer.yml +++ /dev/null @@ -1,132 +0,0 @@ -name: Finalize PR 785 by immutable pointer - -on: - push: - branches: [fix/coverage-materialize-requirements-directory-locks] - paths: - - ".github/pr785-pointer.trigger" - -permissions: - contents: read - -jobs: - finalize: - permissions: - contents: write - issues: write - runs-on: ubuntu-24.04 - timeout-minutes: 40 - steps: - - name: Harden runner - uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 - with: - egress-policy: audit - - - name: Check out exact trigger - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 - with: - ref: ${{ github.sha }} - fetch-depth: 1 - persist-credentials: false - - - name: Reconcile legacy test and remove transient files - shell: bash --noprofile --norc -e -o pipefail {0} - env: - EXPECTED_HEAD: ${{ github.sha }} - run: | - test "$(git rev-parse HEAD)" = "$EXPECTED_HEAD" - python3 - <<'PY' - from pathlib import Path - - path = Path('tests/test_materialize_base_python_requirements.py') - source = path.read_text(encoding='utf-8') - old = 'assert materializer._is_hash_pinned(b"--require-hashes\\ndemo==1\\n")' - new = 'assert not materializer._is_hash_pinned(b"--require-hashes\\ndemo==1\\n")' - if source.count(old) != 1: - raise SystemExit(f'legacy hash-directive assertion count={source.count(old)}') - path.write_text(source.replace(old, new, 1), encoding='utf-8') - PY - rm -f \ - .github/pr785-pointer.trigger \ - .github/workflows/finalize-pr785-pointer.yml - git diff --check - - - name: Set up Python - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 - with: - python-version: '3.14' - cache: pip - cache-dependency-path: requirements-opencode-review-ci-hashes.txt - - - name: Install tooling - run: python -m pip install --disable-pip-version-check --require-hashes -r requirements-opencode-review-ci-hashes.txt - - - name: Verify full materializer quality - shell: bash --noprofile --norc -e -o pipefail {0} - run: | - cat >"${RUNNER_TEMP}/pr785-coveragerc" <<'EOF' - [run] - branch = True - include = - scripts/ci/materialize_base_python_requirements.py - [report] - fail_under = 100 - show_missing = True - EOF - export COVERAGE_RCFILE="${RUNNER_TEMP}/pr785-coveragerc" - python -m coverage erase - python -m coverage run -m pytest -q tests/test_materialize_base_python_requirements.py tests/test_materialize_uv_export_hash_contract.py tests/test_requirements_directory_lock_materialization.py tests/test_trusted_uv_download_contract.py tests/test_trusted_uv_portability_and_streaming.py tests/test_uv_export_isolation_contract.py tests/test_uv_redirect_and_coverage_contract.py tests/test_uv_redirect_boundary.py tests/test_uv_workspace_fail_closed.py tests/test_trusted_uv_materializer_quality_workflow_contract.py - python -m coverage report --fail-under=100 - unset COVERAGE_RCFILE - python -m pytest -q tests - python -m interrogate --fail-under=100 scripts/ci/materialize_base_python_requirements.py - git diff --check - - - name: Create immutable workflow-free commit - shell: bash --noprofile --norc -e -o pipefail {0} - env: - API_TOKEN: ${{ github.token }} - EXPECTED_HEAD: ${{ github.sha }} - run: | - python3 - <<'PY' | tee "${RUNNER_TEMP}/pr785-final.txt" - import base64, json, os, subprocess, urllib.request - from pathlib import Path - repo='ContextualWisdomLab/.github' - parent=os.environ['EXPECTED_HEAD'] - token=os.environ['API_TOKEN'] - root=f'https://api.github.com/repos/{repo}' - expected={'.github/pr785-pointer.trigger','.github/workflows/finalize-pr785-pointer.yml','tests/test_materialize_base_python_requirements.py'} - def request(method, endpoint, payload=None): - req=urllib.request.Request(root+endpoint,data=None if payload is None else json.dumps(payload).encode(),method=method,headers={'Accept':'application/vnd.github+json','Authorization':f'Bearer {token}','X-GitHub-Api-Version':'2022-11-28','User-Agent':'cwl-pr785-finalizer'}) - with urllib.request.urlopen(req,timeout=60) as response: - return json.load(response) - raw=subprocess.check_output(['git','diff','--name-status','-z','HEAD']).decode().split('\0') - changes=[]; i=0 - while i < len(raw)-1: - changes.append((raw[i],raw[i+1])); i += 2 - actual={p for _,p in changes} - if actual != expected: - raise SystemExit(f'path mismatch missing={sorted(expected-actual)} extra={sorted(actual-expected)}') - parent_obj=request('GET',f'/git/commits/{parent}') - entries=[] - for status,path in changes: - if status == 'D': - entries.append({'path':path,'mode':'100644','type':'blob','sha':None}) - else: - blob=request('POST','/git/blobs',{'content':base64.b64encode(Path(path).read_bytes()).decode(),'encoding':'base64'}) - entries.append({'path':path,'mode':'100644','type':'blob','sha':blob['sha']}) - tree=request('POST','/git/trees',{'base_tree':parent_obj['tree']['sha'],'tree':entries}) - commit=request('POST','/git/commits',{'message':'test(coverage): reject unpinned global-hash requirements','tree':tree['sha'],'parents':[parent]}) - print('PR785_FINAL_PARENT_SHA='+parent) - print('PR785_FINAL_COMMIT_SHA='+commit['sha']) - PY - - - name: Publish final pointer - shell: bash --noprofile --norc -e -o pipefail {0} - env: - GH_TOKEN: ${{ github.token }} - EXPECTED_HEAD: ${{ github.sha }} - run: | - commit_sha="$(sed -n 's/^PR785_FINAL_COMMIT_SHA=//p' "${RUNNER_TEMP}/pr785-final.txt")" - test "${#commit_sha}" -eq 40 - gh api --method POST repos/ContextualWisdomLab/.github/issues/785/comments -f "body=PR785_FINAL_PARENT_SHA=${EXPECTED_HEAD}%0APR785_FINAL_COMMIT_SHA=${commit_sha}" diff --git a/tests/test_materialize_base_python_requirements.py b/tests/test_materialize_base_python_requirements.py index 8a383f0c2..0b7907ae9 100644 --- a/tests/test_materialize_base_python_requirements.py +++ b/tests/test_materialize_base_python_requirements.py @@ -150,7 +150,7 @@ def test_lock_name_candidates_are_pip_requirements_files() -> None: def test_hash_pin_detection_includes_pinned_and_excludes_unpinned_or_empty() -> None: """Only fully hash-pinned, non-empty lock content is materialized.""" assert not materializer._is_hash_pinned(b"# comment only\n\n") - assert materializer._is_hash_pinned(b"--require-hashes\ndemo==1\n") + assert not materializer._is_hash_pinned(b"--require-hashes\ndemo==1\n") assert materializer._is_hash_pinned(b"demo==1 --hash=sha256:" + b"a" * 64 + b"\n") assert materializer._is_hash_pinned(b"-r other-hashes.txt\n") assert not materializer._is_hash_pinned(b"untrusted==1\n") From 7de73e7ecfdee655617fb5fc227aea90bc73b968 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 9 Aug 2026 19:28:48 +0900 Subject: [PATCH 27/33] docs(coverage): preserve current-main changelog for requirements locks --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d0733a04..bda4a462c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,10 +8,14 @@ Semantic Versioning where the repository publishes a release. ### Added +- Added a trusted pull-request comment router for `@cwl-noema-review` and review-only `@opencode-agent` dispatches, with an organization sweep, exact-head receipts, repository allowlisting, fixed runners, immutable checkout pins, and a permanent 100% statement/branch/docstring quality gate. - Added exact-base `uv.lock` materialization that reconstructs standalone nested projects with a checksum-pinned official `uv` exporter, isolated frozen/offline execution, strict exact-pin and SHA-256 output validation, and complete Python 3.10/3.14 quality evidence. ### Fixed +- 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. - Materialize complete hash-pinned `requirements/ci.txt` and other direct `requirements/*.txt` base-owned closures so isolated OpenCode coverage imports repository runtime dependencies without trusting pull-request metadata or broadening network access. +- 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. +- Bound each review-agent invocation key to the wrapper's complete canonical payload, including the base branch and requesting actor; altered fields with a valid-format key now fail before durable-leader election or forwarding, and wrapper write permission is job-scoped. - Bound both trusted-uv quality jobs to `github.event.pull_request.head.sha` and added a permanent two-checkout regression contract so exact-head compatibility, coverage, docstring, and compilation claims cannot silently measure GitHub's generated pull-request merge revision. - Made Strix treat only a single LiteLLM provider-error line containing NVIDIA NIM context and model-catalog 404 evidence as cross-model fallback evidence, rejecting cross-line signal assembly and provider-like target source literals; moved the public default to Nemotron 3 Super 120B and added a second NVIDIA hosted candidate before GitHub Models without neutralizing reported vulnerabilities. From d45faed14cc4cb5f1ef1ab767be62989b6a6a50c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 9 Aug 2026 19:29:32 +0900 Subject: [PATCH 28/33] docs(coverage): isolate requirements-lock changelog hunk --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bda4a462c..25e54054d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,8 +14,11 @@ Semantic Versioning where the repository publishes a release. ### Fixed - 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. -- Materialize complete hash-pinned `requirements/ci.txt` and other direct `requirements/*.txt` base-owned closures so isolated OpenCode coverage imports repository runtime dependencies without trusting pull-request metadata or broadening network access. - 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. - Bound each review-agent invocation key to the wrapper's complete canonical payload, including the base branch and requesting actor; altered fields with a valid-format key now fail before durable-leader election or forwarding, and wrapper write permission is job-scoped. - Bound both trusted-uv quality jobs to `github.event.pull_request.head.sha` and added a permanent two-checkout regression contract so exact-head compatibility, coverage, docstring, and compilation claims cannot silently measure GitHub's generated pull-request merge revision. - Made Strix treat only a single LiteLLM provider-error line containing NVIDIA NIM context and model-catalog 404 evidence as cross-model fallback evidence, rejecting cross-line signal assembly and provider-like target source literals; moved the public default to Nemotron 3 Super 120B and added a second NVIDIA hosted candidate before GitHub Models without neutralizing reported vulnerabilities. + +### Changed + +- Materialize complete hash-pinned `requirements/ci.txt` and other direct `requirements/*.txt` base-owned closures so isolated OpenCode coverage imports repository runtime dependencies without trusting pull-request metadata or broadening network access. From 4914e124c339f93bac5da42aeaf649ed893315d4 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 9 Aug 2026 22:50:15 +0900 Subject: [PATCH 29/33] test(coverage): reject unsafe requirements candidates before materialization --- ...irements_directory_lock_materialization.py | 58 ++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/tests/test_requirements_directory_lock_materialization.py b/tests/test_requirements_directory_lock_materialization.py index a5f7e38f8..d9f850e54 100644 --- a/tests/test_requirements_directory_lock_materialization.py +++ b/tests/test_requirements_directory_lock_materialization.py @@ -95,6 +95,62 @@ def test_global_hash_directive_does_not_replace_per_requirement_trust( assert materializer._is_hash_pinned(content) is expected +@pytest.mark.parametrize( + "unsafe_content", + ( + b"demo>=1 --hash=sha256:" + (b"a" * 64) + b"\n", + b"demo==1 --hash=sha256:not-a-complete-digest\n", + b"--index-url https://packages.example.invalid/simple --hash=sha256:" + + (b"a" * 64) + + b"\n", + b"-r /tmp/absolute.txt\n", + b"--requirement ../parent.txt\n", + b"-r nested/../../escape.txt\n", + b"--requirement other.txt --hash=sha256:" + (b"a" * 64) + b"\n", + ), +) +def test_unsafe_requirement_lines_are_rejected_before_materialization( + unsafe_content: bytes, +) -> None: + """Unsafe package and include syntax never gains trusted candidate status.""" + assert not materializer._is_hash_pinned(unsafe_content) + + +@pytest.mark.parametrize( + "unsafe_text", + ( + "demo>=1 --hash=sha256:" + ("a" * 64) + "\n", + "demo==1 --hash=sha256:not-a-complete-digest\n", + "--index-url https://packages.example.invalid/simple --hash=sha256:" + + ("a" * 64) + + "\n", + "-r /tmp/absolute.txt\n", + "--requirement ../parent.txt\n", + ), +) +def test_unsafe_requirements_directory_candidate_is_excluded_from_manifest( + tmp_path: Path, + unsafe_text: str, +) -> None: + """Unsafe direct-child content is excluded before entering the build context.""" + repo = tmp_path / "repo" + requirements_dir = repo / "requirements" + requirements_dir.mkdir(parents=True) + _git(repo, "init") + _git(repo, "config", "user.name", "Test") + _git(repo, "config", "user.email", "test@example.invalid") + (requirements_dir / "ci.txt").write_text(unsafe_text, encoding="utf-8") + _git(repo, "add", ".") + _git(repo, "commit", "-m", "base") + base_sha = _git(repo, "rev-parse", "HEAD") + + output = tmp_path / "output" + manifest = materializer.materialize(repo, base_sha, output) + + assert manifest == [] + assert (output / "manifest.json").read_text(encoding="utf-8") == "[]\n" + + def test_rejects_global_hash_directive_with_unpinned_requirement( tmp_path: Path, ) -> None: @@ -119,4 +175,4 @@ def test_rejects_global_hash_directive_with_unpinned_requirement( assert manifest == [] assert not materializer._is_hash_pinned(b"--require-hashes\ndemo==1\n") - assert (output / "manifest.json").read_text(encoding="utf-8") == "[]\n" + assert (output / "manifest.json").read_text(encoding="utf-8") == "[]\n" \ No newline at end of file From a9f03f9a1fc299e1d2d1f76748f4db8d3b97897d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 10 Aug 2026 12:50:16 +0900 Subject: [PATCH 30/33] fix(coverage): validate trusted requirement lines before materialization --- .../materialize_base_python_requirements.py | 67 +++++++++++++------ 1 file changed, 47 insertions(+), 20 deletions(-) diff --git a/scripts/ci/materialize_base_python_requirements.py b/scripts/ci/materialize_base_python_requirements.py index 807c33aa4..841fc7235 100644 --- a/scripts/ci/materialize_base_python_requirements.py +++ b/scripts/ci/materialize_base_python_requirements.py @@ -121,28 +121,31 @@ def _requirement_lines(content: bytes) -> list[str]: return lines -def _is_hash_pinned(content: bytes) -> bool: - """Return whether content carries hash pins and is safe to preflight. - - Discovery is content-based rather than name-based so hash-pinned locks in any - location (a service subdirectory, ``requirements-dev.txt``, - ``requirements-test.txt``) can be considered for offline coverage, while an - unpinned or PR-mutable requirements file is still excluded from the networked - build context. Hash syntax cannot prove that a file includes every transitive - dependency, so the trusted image installer separately preflights every - candidate as an independent ``--require-hashes`` closure. A global - ``--require-hashes`` directive is not trust evidence by itself: every - non-directive logical requirement must still carry an inline hash or a - bounded requirement include. An empty file or directive-only file carries no - installable dependency and is not materialized. +def _is_bounded_requirement_include(line: str) -> bool: + """Return whether one requirements include names a bounded relative file. + + Includes are accepted only as a two-token ``-r``/``--requirement`` form. + Absolute paths, traversal, URLs, option-like targets, shell/Windows path + separators, fragments, queries, and extra inline options or hashes are + rejected before a base-owned file can enter the trusted build context. + The downstream installer still proves that the candidate is an independently + complete hash closure; this predicate grants syntax eligibility only. """ - lines = _requirement_lines(content) - requirement_lines = [line for line in lines if line != "--require-hashes"] - if not requirement_lines: + fields = line.split() + if len(fields) != 2 or fields[0] not in {"-r", "--requirement"}: return False - return all( - "--hash=" in line or line.startswith(("-r ", "--requirement ")) - for line in requirement_lines + target = fields[1] + if ( + target.startswith(("-", "~")) + or "\\" in target + or ":" in target + or "?" in target + or "#" in target + ): + return False + include_path = pathlib.PurePosixPath(target) + return bool(include_path.parts) and not include_path.is_absolute() and ( + ".." not in include_path.parts ) @@ -157,6 +160,30 @@ def _is_fully_hash_pinned_requirement(line: str) -> bool: return all(UV_SHA256_HASH_RE.fullmatch(hash_value) for hash_value in hashes) +def _is_hash_pinned(content: bytes) -> bool: + """Return whether content carries only trusted pins or bounded includes. + + Discovery is content-based rather than name-based so exact hash-pinned locks + in service subdirectories and role-specific requirements files can be + considered for offline coverage. Candidate syntax is deliberately stricter + than a substring search: each package line must be an exact ``==`` pin with + one or more complete SHA-256 hashes, or a bounded relative requirements + include. A global ``--require-hashes`` directive is not trust evidence by + itself. The downstream installer separately preflights every candidate as an + independent ``pip --require-hashes`` closure, so syntax eligibility never + substitutes for dependency-closure proof. + """ + lines = _requirement_lines(content) + requirement_lines = [line for line in lines if line != "--require-hashes"] + if not requirement_lines: + return False + return all( + _is_fully_hash_pinned_requirement(line) + or _is_bounded_requirement_include(line) + for line in requirement_lines + ) + + def _is_fully_hash_pinned_export(content: bytes) -> bool: """Return whether every emitted uv requirement is exactly SHA-256 pinned. From 17a9b604641e80a77cbddbc555200cd70b27ad59 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 10 Aug 2026 12:51:01 +0900 Subject: [PATCH 31/33] test(coverage): cover bounded requirements include grammar --- ...test_requirements_directory_lock_materialization.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/test_requirements_directory_lock_materialization.py b/tests/test_requirements_directory_lock_materialization.py index d9f850e54..3a677d7db 100644 --- a/tests/test_requirements_directory_lock_materialization.py +++ b/tests/test_requirements_directory_lock_materialization.py @@ -85,6 +85,7 @@ def test_materializes_hash_pinned_requirements_directory_lock( ), (b"--index-url https://packages.example.invalid/simple\n", False), (b"--requirement other.txt\n", True), + (b"-r ./locks/other.txt\n", True), ), ) def test_global_hash_directive_does_not_replace_per_requirement_trust( @@ -107,6 +108,12 @@ def test_global_hash_directive_does_not_replace_per_requirement_trust( b"--requirement ../parent.txt\n", b"-r nested/../../escape.txt\n", b"--requirement other.txt --hash=sha256:" + (b"a" * 64) + b"\n", + b"--requirement https://packages.example.invalid/lock.txt\n", + b"--requirement ~/private-lock.txt\n", + b"--requirement -option-like.txt\n", + b"--requirement locks\\windows.txt\n", + b"--requirement other.txt?variant=1\n", + b"--requirement other.txt#fragment\n", ), ) def test_unsafe_requirement_lines_are_rejected_before_materialization( @@ -126,6 +133,7 @@ def test_unsafe_requirement_lines_are_rejected_before_materialization( + "\n", "-r /tmp/absolute.txt\n", "--requirement ../parent.txt\n", + "--requirement https://packages.example.invalid/lock.txt\n", ), ) def test_unsafe_requirements_directory_candidate_is_excluded_from_manifest( @@ -175,4 +183,4 @@ def test_rejects_global_hash_directive_with_unpinned_requirement( assert manifest == [] assert not materializer._is_hash_pinned(b"--require-hashes\ndemo==1\n") - assert (output / "manifest.json").read_text(encoding="utf-8") == "[]\n" \ No newline at end of file + assert (output / "manifest.json").read_text(encoding="utf-8") == "[]\n" From cac1bc80f666fbc94f791ddd93c4a89e659d75b9 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 10 Aug 2026 12:58:37 +0900 Subject: [PATCH 32/33] docs(coverage): specify trusted requirements grammar and proof boundary --- ...d-requirements-directory-lock-discovery.md | 70 ++++++++++++++----- 1 file changed, 54 insertions(+), 16 deletions(-) diff --git a/docs/doctoring/trusted-requirements-directory-lock-discovery.md b/docs/doctoring/trusted-requirements-directory-lock-discovery.md index e21ee7f3b..8167f7199 100644 --- a/docs/doctoring/trusted-requirements-directory-lock-discovery.md +++ b/docs/doctoring/trusted-requirements-directory-lock-discovery.md @@ -9,13 +9,31 @@ conventional `requirements*.txt` and `requirements.lock` names, it recognizes a as `requirements/ci.txt` or `services/scoring_service/requirements/package.txt`. The path rule grants candidate status only. A global `--require-hashes` -directive is not trust evidence by itself: every non-directive logical -requirement must still carry an inline hash or a bounded requirement include. -The existing content boundary records the exact trusted source path in the -manifest and preflights each candidate as an independently installable -`pip --require-hashes` closure. Unpinned notes, directive-only files, input -files, nested descendants, symbolic links, pull-request-only files, and -malformed Git tree entries remain excluded. +directive is not trust evidence by itself. Every non-directive logical line must +be either: + +- an exact package `==` requirement with one or more complete 64-hex SHA-256 + `--hash=` values; or +- a two-token `-r` / `--requirement` include naming a bounded relative path. + +The include grammar rejects absolute paths, `..` traversal, URL/scheme syntax, +query or fragment syntax, shell-home expansion, Windows-style separators, +option-like targets, and additional inline options or hashes. Package lines +using ranges such as `>=`, truncated/non-SHA-256-looking hash values, index or +other option lines, local/direct references, and other syntax that merely +contains a `--hash=` substring do not gain trusted candidate status. + +This parser is a **pre-materialization eligibility boundary**, not a dependency +solver. The exact trusted source path is recorded in the manifest and the +existing installer separately preflights every candidate as an independently +installable `pip --require-hashes` closure. That second proof remains mandatory: +pip's hash-checking mode intentionally fails when a requirement participating in +the installation is not fully hashed. Syntax qualification therefore cannot +substitute for dependency-closure proof. + +Unpinned notes, directive-only files, input files, deeper descendants, symbolic +links, pull-request-only files, malformed Git tree entries, and unsafe include +syntax remain excluded from the networked coverage image. ## Operational reason @@ -25,29 +43,49 @@ safe base-owned locks leaves isolated coverage without runtime dependencies even when the repository maintains a complete generated closure. The resulting import failure measures the coverage image rather than the changed production code. +Conversely, treating the presence of the substring `--hash=` as trust evidence +would let a range requirement, malformed digest, pip option, or path/URL include +cross the materialization boundary. The accepted design therefore combines +base-commit provenance, a narrow grammar, and an independent pip closure +preflight rather than relying on file names or hash-looking text alone. + ## Verification - A failing contract first proved that `requirements/ci.txt` was undiscoverable. -- Direct `requirements/*.txt` and nested-service equivalents are accepted. +- A later RED security contract proved that range requirements, malformed + digests, pip option lines, absolute/traversing includes, and includes carrying + extra inline options could be materialized by the earlier substring test. +- Direct `requirements/*.txt` and nested-service equivalents remain eligible. - A deeper `requirements/nested/ci.txt` path and unrelated `docs/ci.txt` remain ineligible. +- Exact `==` package pins with complete SHA-256 hashes are accepted; `>=` and + malformed/truncated hash forms are rejected. +- Bounded relative includes such as `--requirement other.txt` and + `-r ./locks/other.txt` are accepted; URL, absolute, traversal, home-expansion, + query/fragment, backslash, and option-like forms are rejected. - A global `--require-hashes` directive combined with an unpinned requirement is rejected rather than promoted into the networked coverage image. -- Only the hash-pinned candidate is emitted from a realistic temporary Git base; - unpinned `.in` and human-readable `.txt` files remain absent. -- The focused materializer suite, complete central suite, statement and branch - coverage, docstring gate, compilation, and exact-head security workflows are - required before merge. +- Only qualifying base-owned candidates are emitted from realistic temporary Git + bases; unpinned `.in`, note, and hostile direct-child files remain absent. +- Exact-head Python 3.14 quality requires the focused suite, complete central + suite, 100% production statement and branch coverage, 100% public docstrings, + compilation, and security/supply-chain workflows. Python 3.10 compatibility + remains a separate minimum-runtime contract. ## References Python Packaging Authority. (2026). *Install requires vs requirements files*. -Python Packaging User Guide. +Python Packaging User Guide. Retrieved August 10, 2026, from https://packaging.python.org/en/latest/discussions/install-requires-vs-requirements/ Python Packaging Authority. (2026). *Repeatable installs*. pip documentation. -https://pip.pypa.io/en/stable/topics/repeatable-installs/ +Retrieved August 10, 2026, from +https://pip.pypa.io/en/latest/topics/repeatable-installs/ Python Packaging Authority. (2026). *Requirements file format*. pip -documentation. +26.1.2 documentation. Retrieved August 10, 2026, from https://pip.pypa.io/en/stable/reference/requirements-file-format/ + +Python Packaging Authority. (2026). *Secure installs*. pip 26.1.2 +documentation. Retrieved August 10, 2026, from +https://pip.pypa.io/en/stable/topics/secure-installs/ From dcc539176271658f024de7419044f513c6fb7317 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 10 Aug 2026 12:59:11 +0900 Subject: [PATCH 33/33] docs(coverage): record trusted requirements grammar hardening --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 25e54054d..7ab8a7ed9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ Semantic Versioning where the repository publishes a release. - Bound each review-agent invocation key to the wrapper's complete canonical payload, including the base branch and requesting actor; altered fields with a valid-format key now fail before durable-leader election or forwarding, and wrapper write permission is job-scoped. - Bound both trusted-uv quality jobs to `github.event.pull_request.head.sha` and added a permanent two-checkout regression contract so exact-head compatibility, coverage, docstring, and compilation claims cannot silently measure GitHub's generated pull-request merge revision. - Made Strix treat only a single LiteLLM provider-error line containing NVIDIA NIM context and model-catalog 404 evidence as cross-model fallback evidence, rejecting cross-line signal assembly and provider-like target source literals; moved the public default to Nemotron 3 Super 120B and added a second NVIDIA hosted candidate before GitHub Models without neutralizing reported vulnerabilities. +- Hardened base-owned Python requirements materialization so candidate package lines require exact `==` pins with complete SHA-256 hashes and requirement includes use only bounded relative paths; range pins, malformed digests, pip option lines, URL/absolute/traversing/home/query/fragment/backslash/option-like includes, and include lines carrying extra inline options are rejected before the trusted build context. ### Changed