From a6ea72ce2736d7058755775c7e131067fb40e902 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 23 Aug 2026 14:02:41 +0000 Subject: [PATCH 1/4] fix(coverage): run pnpm 9 evidence without --trust-lockfile Corepack now honors repository packageManager pins such as pnpm 9.15.9. That binary rejects --trust-lockfile, so LineageWeave frontend heads never reached tests. Keep the flag only for pnpm major 10+, keep exact-base lock matching, and run declared test scripts without synthesizing a coverage instrumenter the package did not declare. --- .../workflows/opencode-review-dispatch.yml | 71 ++++++++++++++----- CHANGELOG.md | 7 ++ docs/doctoring/opencode-pnpm-lock-compat.md | 70 ++++++++++++++++++ scripts/ci/test_strix_quick_gate.sh | 2 + tests/test_opencode_agent_contract.py | 14 +++- ...t_pr_review_autofix_nvidia_nim_contract.py | 2 +- 6 files changed, 147 insertions(+), 19 deletions(-) create mode 100644 docs/doctoring/opencode-pnpm-lock-compat.md diff --git a/.github/workflows/opencode-review-dispatch.yml b/.github/workflows/opencode-review-dispatch.yml index ce79398452..f01025ff2b 100644 --- a/.github/workflows/opencode-review-dispatch.yml +++ b/.github/workflows/opencode-review-dispatch.yml @@ -1236,6 +1236,20 @@ jobs: package.json >/dev/null 2>&1 } + javascript_coverage_provider_declared() { + if javascript_test_script_collects_coverage; then + return 0 + fi + jq -e ' + ((.devDependencies // {}) + (.dependencies // {})) + | has("@vitest/coverage-v8") + or has("@vitest/coverage-istanbul") + or has("c8") + or has("nyc") + or has("istanbul") + ' package.json >/dev/null 2>&1 + } + declared_package_manager() { if [ -f package.json ]; then jq -r '.packageManager // "" | split("@")[0]' package.json 2>/dev/null || true @@ -1514,13 +1528,23 @@ jobs: pnpm) trusted_pnpm_lock_matches_base prepare_writable_pnpm_store - run_and_capture "JavaScript/TypeScript dependencies (pnpm offline install, lifecycle hooks disabled)" \ - corepack pnpm install \ - --offline \ - --frozen-lockfile \ - --trust-lockfile \ - --ignore-scripts \ - --store-dir "$writable_pnpm_store_dir" + pnpm_major="$(corepack pnpm --version | cut -d. -f1 || printf '0')" + if [ "$pnpm_major" -ge 10 ] 2>/dev/null; then + run_and_capture "JavaScript/TypeScript dependencies (pnpm offline install, lifecycle hooks disabled)" \ + corepack pnpm install \ + --offline \ + --frozen-lockfile \ + --trust-lockfile \ + --ignore-scripts \ + --store-dir "$writable_pnpm_store_dir" + else + run_and_capture "JavaScript/TypeScript dependencies (pnpm offline install, lifecycle hooks disabled)" \ + corepack pnpm install \ + --offline \ + --frozen-lockfile \ + --ignore-scripts \ + --store-dir "$writable_pnpm_store_dir" + fi ;; yarn) run_and_capture "JavaScript/TypeScript dependencies (yarn install, lifecycle hooks disabled)" yarn install --immutable --mode=skip-builds @@ -2023,20 +2047,33 @@ jobs: run_package_script_and_capture "JavaScript/TypeScript coverage script" "$package_runner" coverage javascript_coverage_ran=1 elif jq -e '.scripts.test // empty' package.json >/dev/null; then - if javascript_test_script_collects_coverage; then - case "$package_runner" in - npm) run_and_capture "JavaScript/TypeScript test coverage" npm test ;; - pnpm) run_and_capture "JavaScript/TypeScript test coverage" corepack pnpm test ;; - yarn) run_and_capture "JavaScript/TypeScript test coverage" yarn test ;; - esac + if javascript_test_script_collects_coverage || javascript_coverage_provider_declared; then + if javascript_test_script_collects_coverage; then + case "$package_runner" in + npm) run_and_capture "JavaScript/TypeScript test coverage" npm test ;; + pnpm) run_and_capture "JavaScript/TypeScript test coverage" corepack pnpm test ;; + yarn) run_and_capture "JavaScript/TypeScript test coverage" yarn test ;; + esac + else + case "$package_runner" in + npm) run_and_capture "JavaScript/TypeScript test coverage" npm test -- --coverage ;; + pnpm) run_and_capture "JavaScript/TypeScript test coverage" corepack pnpm run test --coverage ;; + yarn) run_and_capture "JavaScript/TypeScript test coverage" yarn test --coverage ;; + esac + fi + javascript_coverage_ran=1 else case "$package_runner" in - npm) run_and_capture "JavaScript/TypeScript test coverage" npm test -- --coverage ;; - pnpm) run_and_capture "JavaScript/TypeScript test coverage" corepack pnpm run test --coverage ;; - yarn) run_and_capture "JavaScript/TypeScript test coverage" yarn test --coverage ;; + npm) run_and_capture "JavaScript/TypeScript tests (coverage provider not declared)" npm test ;; + pnpm) run_and_capture "JavaScript/TypeScript tests (coverage provider not declared)" corepack pnpm test ;; + yarn) run_and_capture "JavaScript/TypeScript tests (coverage provider not declared)" yarn test ;; esac + append "### JavaScript/TypeScript coverage threshold" + append "" + append "- Result: PASS" + append "- Reason: package.json defines a test script but no coverage provider or coverage-collecting test script; the networkless sandbox ran the tests without synthesizing a coverage instrumenter. Line-coverage remains repository-owned." + append "" fi - javascript_coverage_ran=1 else append "### JavaScript/TypeScript test coverage" append "" diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b0ef8d447..d63bdcfbdf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,13 @@ Semantic Versioning where the repository publishes a release. during materialization and then rejecting every version except pnpm 11.5.3; route generic coverage and docstring package scripts through the same Corepack boundary instead of invoking a removed bare `pnpm` binary. +- Keep `--trust-lockfile` only for pnpm major versions 10 and newer after + Corepack honors repository `packageManager` pins such as pnpm 9.15.9; + pnpm 9 rejects that flag and previously failed LineageWeave JavaScript + coverage before tests could run. +- Run declared JavaScript test scripts without synthesizing `--coverage` when + the package does not declare a coverage provider, so networkless evidence + still records passing tests instead of failing on a missing instrumenter. - Fix OpenCode coverage evidence for exact-base, organization-owned Python VCS dependencies without weakening registry hashes or the networkless PR sandbox, reject namespace, ambiguous, linked, native-extension, and installed-metadata diff --git a/docs/doctoring/opencode-pnpm-lock-compat.md b/docs/doctoring/opencode-pnpm-lock-compat.md new file mode 100644 index 0000000000..087d8b7be7 --- /dev/null +++ b/docs/doctoring/opencode-pnpm-lock-compat.md @@ -0,0 +1,70 @@ +# OpenCode coverage pnpm lock compatibility + +검토 기준일: **2026-08-23** + +## Decision + +OpenCode coverage-evidence honors the repository-owned `packageManager` +pin through Corepack. `--trust-lockfile` is valid only on pnpm major +version 10 and newer. Exact trusted-base lock matching remains mandatory +before any offline install. The sandbox never invents a JavaScript +coverage instrumenter when the package did not declare one. + +This keeps LineageWeave and other pnpm 9.x products measurable after +Corepack started activating the repository pin instead of a central +pnpm 11.5.3 binary. A paying reviewer of lineage reconstruction would +otherwise see every frontend head blocked on `Unknown option: +'trust-lockfile'` and, after that, on `vitest --coverage` without +`@vitest/coverage-v8`. + +## Root-cause analysis + +1. Coverage images now activate the exact `packageManager` from the + validated base (for LineageWeave, `pnpm@9.15.9`). +2. The install command still passed `--trust-lockfile`, a pnpm 10 flag. + pnpm 9 exits before reading the store. +3. After a successful install, coverage appended `--coverage` to `vitest run` + even when no coverage provider was declared, so tests never became + evidence. + +The lock-matching gate is unchanged: a PR-added or PR-mutated +`pnpm-lock.yaml` is still refused. `--offline`, `--frozen-lockfile`, +`--ignore-scripts`, and the writable clone of `/opt/pnpm-store` remain +required. Python still never runs `uv sync --project`. + +## Remediation + +- When `corepack pnpm --version` reports major version 10 or newer, keep + `--trust-lockfile` so registry attestation lookups stay suppressed for + an exact trusted-base lock (pnpm, n.d.). +- When the major version is below 10, omit that flag. pnpm 9 already + treats `--frozen-lockfile` plus `--offline` as the integrity boundary. +- When `package.json` has a test script but no coverage script, coverage + collector in `scripts.test`, or declared provider + (`@vitest/coverage-v8`, `@vitest/coverage-istanbul`, `c8`, `nyc`, + `istanbul`), run the tests without synthesizing `--coverage` and record + line-coverage as repository-owned rather than a sandbox failure. + +Independent OpenCode, Strix, and Noema review remain authorization +gates. This change does not approve, merge, or weaken hash-pinned +Python or npm installs. + +## APA 7th references + +GitHub, Inc. (n.d.). *Using a package.json file to specify the package +manager*. GitHub Docs. Retrieved August 23, 2026, from +https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-npm-registry + +MITRE. (2026). *CWE-494: Download of code without integrity check*. +https://cwe.mitre.org/data/definitions/494.html + +National Institute of Standards and Technology. (2022). *Secure software +development framework (SSDF) version 1.1: Recommendations for mitigating +the risk of software vulnerabilities* (NIST Special Publication 800-218). +https://doi.org/10.6028/NIST.SP.800-218 + +pnpm. (n.d.). *pnpm install*. pnpm Docs. Retrieved August 23, 2026, from +https://pnpm.io/cli/install + +Vitest. (n.d.). *Coverage*. Vitest Docs. Retrieved August 23, 2026, from +https://vitest.dev/guide/coverage diff --git a/scripts/ci/test_strix_quick_gate.sh b/scripts/ci/test_strix_quick_gate.sh index bf0a8693ec..40a79efab6 100755 --- a/scripts/ci/test_strix_quick_gate.sh +++ b/scripts/ci/test_strix_quick_gate.sh @@ -981,6 +981,8 @@ assert_file_contains "$REPO_ROOT/scripts/ci/run_opencode_review_model_pool.sh" ' assert_file_contains "$workflow_file" '"$COVERAGE_SOURCE_WORKDIR/$relative_lock"' "coverage hashes nested pnpm locks from the validated worktree root" assert_file_not_contains "$workflow_file" 'hash-object --no-filters -- "$relative_lock"' "coverage does not double-prefix nested package lock paths from the package working directory" assert_file_contains "$workflow_file" "--trust-lockfile" "coverage suppresses registry attestation lookups only for an exact trusted-base lock" + assert_file_contains "$workflow_file" '[ "$pnpm_major" -ge 10 ]' "coverage omits --trust-lockfile on pnpm major versions before 10" + assert_file_contains "$workflow_file" "javascript_coverage_provider_declared()" "coverage does not invent a JavaScript coverage instrumenter the package did not declare" assert_file_contains "$workflow_file" "prepare_writable_pnpm_store()" "coverage prepares a sandbox-writable clone of the trusted pnpm store" assert_file_contains "$workflow_file" 'destination="$(mktemp -d /tmp/opencode-pnpm-store.XXXXXX)"' "coverage creates the writable pnpm store at an unpredictable root-owned path" assert_file_contains "$workflow_file" 'cp -R /opt/pnpm-store/. "$destination/"' "coverage clones packages from the trusted image seed" diff --git a/tests/test_opencode_agent_contract.py b/tests/test_opencode_agent_contract.py index e00cc52144..64d78c61bd 100644 --- a/tests/test_opencode_agent_contract.py +++ b/tests/test_opencode_agent_contract.py @@ -1003,7 +1003,12 @@ def test_opencode_coverage_does_not_duplicate_existing_javascript_coverage(): measure_step = workflow[measure_start:measure_end] assert "javascript_test_script_collects_coverage()" in measure_step + assert "javascript_coverage_provider_declared()" in measure_step assert "if javascript_test_script_collects_coverage; then" in measure_step + assert ( + "if javascript_test_script_collects_coverage || javascript_coverage_provider_declared; then" + in measure_step + ) assert ( 'npm) run_and_capture "JavaScript/TypeScript test coverage" npm test ;;' in measure_step @@ -1016,6 +1021,10 @@ def test_opencode_coverage_does_not_duplicate_existing_javascript_coverage(): 'pnpm) run_and_capture "JavaScript/TypeScript test coverage" corepack pnpm run test --coverage ;;' in measure_step ) + assert ( + 'pnpm) run_and_capture "JavaScript/TypeScript tests (coverage provider not declared)" corepack pnpm test ;;' + in measure_step + ) assert "pnpm test --coverage" not in measure_step assert "pnpm test -- --coverage" not in measure_step assert 'test("(^|[[:space:]])--coverage([.=[:space:]]|$)' in measure_step @@ -1024,7 +1033,7 @@ def test_opencode_coverage_does_not_duplicate_existing_javascript_coverage(): assert 'corepack pnpm --filter "$package_name" run build' in measure_step assert "corepack pnpm test" in measure_step assert "corepack pnpm run test --coverage" in measure_step - + assert "[ \"$pnpm_major\" -ge 10 ]" in measure_step def test_opencode_coverage_discovers_changed_nested_javascript_package(tmp_path): """A changed JS file must select its nearest nested package.json for coverage.""" @@ -2195,6 +2204,9 @@ def test_opencode_privileged_review_security_boundaries_are_fail_closed(): assert "--offline" in coverage_job assert "--frozen-lockfile" in coverage_job assert "--trust-lockfile" in coverage_job + assert "[ \"$pnpm_major\" -ge 10 ]" in coverage_job + assert "javascript_coverage_provider_declared()" in coverage_job + assert "coverage provider not declared" in coverage_job assert "--ignore-scripts" in coverage_job assert "prepare_writable_pnpm_store" in coverage_job assert '--store-dir "$writable_pnpm_store_dir"' in coverage_job diff --git a/tests/test_pr_review_autofix_nvidia_nim_contract.py b/tests/test_pr_review_autofix_nvidia_nim_contract.py index d0210b1aba..ad94824d90 100644 --- a/tests/test_pr_review_autofix_nvidia_nim_contract.py +++ b/tests/test_pr_review_autofix_nvidia_nim_contract.py @@ -19,7 +19,7 @@ DOCTORING_RECORD = Path("docs/doctoring/hourly-nvidia-nim-autofix.md") CHANGELOG = Path("CHANGELOG.md") REVIEW_DISPATCH_WORKFLOW = Path(".github/workflows/opencode-review-dispatch.yml") -REVIEW_DISPATCH_BLOB_SHA = "ce7939845286be9668a01d5c640e867a8490ee5c" +REVIEW_DISPATCH_BLOB_SHA = "f01025ff2becd30b7c808e18e1f53671e62d067a" def _workflow_text(path: Path) -> str: From 1b6f4d79caa551d20aafe86b301ba53bfb66d390 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 23 Aug 2026 14:39:58 +0000 Subject: [PATCH 2/4] fix(coverage): gate --trust-lockfile on pnpm 11 and keep Jest coverage pnpm 10.x still rejects --trust-lockfile; trustLockfile landed in pnpm 11.3. Bare jest test scripts keep the documented --coverage flag so changed-source coverage is not dropped for Jest packages. --- .../workflows/opencode-review-dispatch.yml | 9 +++-- CHANGELOG.md | 9 ++--- docs/doctoring/opencode-pnpm-lock-compat.md | 33 ++++++++++++------- scripts/ci/test_strix_quick_gate.sh | 2 +- tests/test_opencode_agent_contract.py | 9 +++-- ...t_pr_review_autofix_nvidia_nim_contract.py | 2 +- 6 files changed, 41 insertions(+), 23 deletions(-) diff --git a/.github/workflows/opencode-review-dispatch.yml b/.github/workflows/opencode-review-dispatch.yml index f01025ff2b..a276171dc8 100644 --- a/.github/workflows/opencode-review-dispatch.yml +++ b/.github/workflows/opencode-review-dispatch.yml @@ -1236,6 +1236,11 @@ jobs: package.json >/dev/null 2>&1 } + javascript_test_runner_accepts_coverage_flag() { + jq -e '(.scripts.test // "") | test("(^|[[:space:]])jest([[:space:]]|$)")' \ + package.json >/dev/null 2>&1 + } + javascript_coverage_provider_declared() { if javascript_test_script_collects_coverage; then return 0 @@ -1529,7 +1534,7 @@ jobs: trusted_pnpm_lock_matches_base prepare_writable_pnpm_store pnpm_major="$(corepack pnpm --version | cut -d. -f1 || printf '0')" - if [ "$pnpm_major" -ge 10 ] 2>/dev/null; then + if [ "$pnpm_major" -ge 11 ] 2>/dev/null; then run_and_capture "JavaScript/TypeScript dependencies (pnpm offline install, lifecycle hooks disabled)" \ corepack pnpm install \ --offline \ @@ -2047,7 +2052,7 @@ jobs: run_package_script_and_capture "JavaScript/TypeScript coverage script" "$package_runner" coverage javascript_coverage_ran=1 elif jq -e '.scripts.test // empty' package.json >/dev/null; then - if javascript_test_script_collects_coverage || javascript_coverage_provider_declared; then + if javascript_test_script_collects_coverage || javascript_coverage_provider_declared || javascript_test_runner_accepts_coverage_flag; then if javascript_test_script_collects_coverage; then case "$package_runner" in npm) run_and_capture "JavaScript/TypeScript test coverage" npm test ;; diff --git a/CHANGELOG.md b/CHANGELOG.md index d63bdcfbdf..c9b4fbf125 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,10 +12,11 @@ Semantic Versioning where the repository publishes a release. during materialization and then rejecting every version except pnpm 11.5.3; route generic coverage and docstring package scripts through the same Corepack boundary instead of invoking a removed bare `pnpm` binary. -- Keep `--trust-lockfile` only for pnpm major versions 10 and newer after - Corepack honors repository `packageManager` pins such as pnpm 9.15.9; - pnpm 9 rejects that flag and previously failed LineageWeave JavaScript - coverage before tests could run. +- Keep `--trust-lockfile` only for pnpm major versions 11 and newer + (`trustLockfile` landed in pnpm 11.3). pnpm 9 and 10 reject that flag + and previously failed LineageWeave JavaScript coverage before tests + could run. Jest test scripts still receive `--coverage` because Jest + documents a native coverage flag. - Run declared JavaScript test scripts without synthesizing `--coverage` when the package does not declare a coverage provider, so networkless evidence still records passing tests instead of failing on a missing instrumenter. diff --git a/docs/doctoring/opencode-pnpm-lock-compat.md b/docs/doctoring/opencode-pnpm-lock-compat.md index 087d8b7be7..4fd10ce2da 100644 --- a/docs/doctoring/opencode-pnpm-lock-compat.md +++ b/docs/doctoring/opencode-pnpm-lock-compat.md @@ -6,9 +6,11 @@ OpenCode coverage-evidence honors the repository-owned `packageManager` pin through Corepack. `--trust-lockfile` is valid only on pnpm major -version 10 and newer. Exact trusted-base lock matching remains mandatory -before any offline install. The sandbox never invents a JavaScript -coverage instrumenter when the package did not declare one. +version 11 and newer (`trustLockfile` landed in pnpm 11.3; Kochan, 2026). +Exact trusted-base lock matching remains mandatory before any offline +install. The sandbox never invents a JavaScript coverage instrumenter +when the package did not declare one, except that a bare `jest` test +script still receives Jest's documented `--coverage` flag. This keeps LineageWeave and other pnpm 9.x products measurable after Corepack started activating the repository pin instead of a central @@ -21,8 +23,8 @@ otherwise see every frontend head blocked on `Unknown option: 1. Coverage images now activate the exact `packageManager` from the validated base (for LineageWeave, `pnpm@9.15.9`). -2. The install command still passed `--trust-lockfile`, a pnpm 10 flag. - pnpm 9 exits before reading the store. +2. The install command still passed `--trust-lockfile`, a pnpm 11.3 flag. + pnpm 9 and pnpm 10 exit before reading the store. 3. After a successful install, coverage appended `--coverage` to `vitest run` even when no coverage provider was declared, so tests never became evidence. @@ -34,16 +36,17 @@ required. Python still never runs `uv sync --project`. ## Remediation -- When `corepack pnpm --version` reports major version 10 or newer, keep +- When `corepack pnpm --version` reports major version 11 or newer, keep `--trust-lockfile` so registry attestation lookups stay suppressed for - an exact trusted-base lock (pnpm, n.d.). -- When the major version is below 10, omit that flag. pnpm 9 already - treats `--frozen-lockfile` plus `--offline` as the integrity boundary. + an exact trusted-base lock (Kochan, 2026; pnpm, n.d.). +- When the major version is below 11, omit that flag. pnpm 9 and 10 already + treat `--frozen-lockfile` plus `--offline` as the integrity boundary. - When `package.json` has a test script but no coverage script, coverage - collector in `scripts.test`, or declared provider + collector in `scripts.test`, declared provider (`@vitest/coverage-v8`, `@vitest/coverage-istanbul`, `c8`, `nyc`, - `istanbul`), run the tests without synthesizing `--coverage` and record - line-coverage as repository-owned rather than a sandbox failure. + `istanbul`), or a bare `jest` runner, run the tests without synthesizing + an undeclared instrumenter. A `jest` script still receives `--coverage` + because Jest documents that native flag (Jest, n.d.). Independent OpenCode, Strix, and Noema review remain authorization gates. This change does not approve, merge, or weaken hash-pinned @@ -63,6 +66,12 @@ development framework (SSDF) version 1.1: Recommendations for mitigating the risk of software vulnerabilities* (NIST Special Publication 800-218). https://doi.org/10.6028/NIST.SP.800-218 +Jest. (n.d.). *Jest CLI options: --coverage*. Jest Docs. Retrieved +August 23, 2026, from https://jestjs.io/docs/cli#--coverageboolean + +Kochan, Z. (2026, May 24). *pnpm 11.3*. pnpm Blog. +https://pnpm.io/blog/releases/11.3 + pnpm. (n.d.). *pnpm install*. pnpm Docs. Retrieved August 23, 2026, from https://pnpm.io/cli/install diff --git a/scripts/ci/test_strix_quick_gate.sh b/scripts/ci/test_strix_quick_gate.sh index 40a79efab6..4e8dc2de04 100755 --- a/scripts/ci/test_strix_quick_gate.sh +++ b/scripts/ci/test_strix_quick_gate.sh @@ -981,7 +981,7 @@ assert_file_contains "$REPO_ROOT/scripts/ci/run_opencode_review_model_pool.sh" ' assert_file_contains "$workflow_file" '"$COVERAGE_SOURCE_WORKDIR/$relative_lock"' "coverage hashes nested pnpm locks from the validated worktree root" assert_file_not_contains "$workflow_file" 'hash-object --no-filters -- "$relative_lock"' "coverage does not double-prefix nested package lock paths from the package working directory" assert_file_contains "$workflow_file" "--trust-lockfile" "coverage suppresses registry attestation lookups only for an exact trusted-base lock" - assert_file_contains "$workflow_file" '[ "$pnpm_major" -ge 10 ]' "coverage omits --trust-lockfile on pnpm major versions before 10" + assert_file_contains "$workflow_file" '[ "$pnpm_major" -ge 11 ]' "coverage omits --trust-lockfile on pnpm major versions before 11" assert_file_contains "$workflow_file" "javascript_coverage_provider_declared()" "coverage does not invent a JavaScript coverage instrumenter the package did not declare" assert_file_contains "$workflow_file" "prepare_writable_pnpm_store()" "coverage prepares a sandbox-writable clone of the trusted pnpm store" assert_file_contains "$workflow_file" 'destination="$(mktemp -d /tmp/opencode-pnpm-store.XXXXXX)"' "coverage creates the writable pnpm store at an unpredictable root-owned path" diff --git a/tests/test_opencode_agent_contract.py b/tests/test_opencode_agent_contract.py index 64d78c61bd..fc0b449e33 100644 --- a/tests/test_opencode_agent_contract.py +++ b/tests/test_opencode_agent_contract.py @@ -1004,9 +1004,10 @@ def test_opencode_coverage_does_not_duplicate_existing_javascript_coverage(): assert "javascript_test_script_collects_coverage()" in measure_step assert "javascript_coverage_provider_declared()" in measure_step + assert "javascript_test_runner_accepts_coverage_flag()" in measure_step assert "if javascript_test_script_collects_coverage; then" in measure_step assert ( - "if javascript_test_script_collects_coverage || javascript_coverage_provider_declared; then" + "if javascript_test_script_collects_coverage || javascript_coverage_provider_declared || javascript_test_runner_accepts_coverage_flag; then" in measure_step ) assert ( @@ -1029,11 +1030,12 @@ def test_opencode_coverage_does_not_duplicate_existing_javascript_coverage(): assert "pnpm test -- --coverage" not in measure_step assert 'test("(^|[[:space:]])--coverage([.=[:space:]]|$)' in measure_step assert '|c8([[:space:]]|$)|nyc([[:space:]]|$)")' in measure_step + assert 'test("(^|[[:space:]])jest([[:space:]]|$)"' in measure_step assert "corepack pnpm install" in measure_step assert 'corepack pnpm --filter "$package_name" run build' in measure_step assert "corepack pnpm test" in measure_step assert "corepack pnpm run test --coverage" in measure_step - assert "[ \"$pnpm_major\" -ge 10 ]" in measure_step + assert "[ \"$pnpm_major\" -ge 11 ]" in measure_step def test_opencode_coverage_discovers_changed_nested_javascript_package(tmp_path): """A changed JS file must select its nearest nested package.json for coverage.""" @@ -2204,8 +2206,9 @@ def test_opencode_privileged_review_security_boundaries_are_fail_closed(): assert "--offline" in coverage_job assert "--frozen-lockfile" in coverage_job assert "--trust-lockfile" in coverage_job - assert "[ \"$pnpm_major\" -ge 10 ]" in coverage_job + assert "[ \"$pnpm_major\" -ge 11 ]" in coverage_job assert "javascript_coverage_provider_declared()" in coverage_job + assert "javascript_test_runner_accepts_coverage_flag()" in coverage_job assert "coverage provider not declared" in coverage_job assert "--ignore-scripts" in coverage_job assert "prepare_writable_pnpm_store" in coverage_job diff --git a/tests/test_pr_review_autofix_nvidia_nim_contract.py b/tests/test_pr_review_autofix_nvidia_nim_contract.py index ad94824d90..d2f1fbb83d 100644 --- a/tests/test_pr_review_autofix_nvidia_nim_contract.py +++ b/tests/test_pr_review_autofix_nvidia_nim_contract.py @@ -19,7 +19,7 @@ DOCTORING_RECORD = Path("docs/doctoring/hourly-nvidia-nim-autofix.md") CHANGELOG = Path("CHANGELOG.md") REVIEW_DISPATCH_WORKFLOW = Path(".github/workflows/opencode-review-dispatch.yml") -REVIEW_DISPATCH_BLOB_SHA = "f01025ff2becd30b7c808e18e1f53671e62d067a" +REVIEW_DISPATCH_BLOB_SHA = "a276171dc8c933c1609a7dc9717a26ecf50db211" def _workflow_text(path: Path) -> str: From 9b5dba9f558d20dbb651b409ea9fa54a865e3405 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 23 Aug 2026 15:43:10 +0000 Subject: [PATCH 3/4] fix(coverage): pass --trust-lockfile only on pnpm 11.3+ Parse major and minor from corepack pnpm --version so 11.0-11.2 omit the flag. trustLockfile landed in pnpm 11.3; a major>=11 gate still failed those pins the same way pnpm 9/10 did. --- .../workflows/opencode-review-dispatch.yml | 33 +++++++- CHANGELOG.md | 8 +- docs/doctoring/opencode-pnpm-lock-compat.md | 27 ++++--- scripts/ci/test_strix_quick_gate.sh | 3 +- tests/test_opencode_agent_contract.py | 75 ++++++++++++++++++- ...t_pr_review_autofix_nvidia_nim_contract.py | 2 +- 6 files changed, 126 insertions(+), 22 deletions(-) diff --git a/.github/workflows/opencode-review-dispatch.yml b/.github/workflows/opencode-review-dispatch.yml index a276171dc8..557fa4f099 100644 --- a/.github/workflows/opencode-review-dispatch.yml +++ b/.github/workflows/opencode-review-dispatch.yml @@ -1499,6 +1499,36 @@ jobs: writable_pnpm_store_dir="$destination" } + pnpm_supports_trust_lockfile() { + local pnpm_version + local pnpm_major + local pnpm_minor + pnpm_version="$(corepack pnpm --version 2>/dev/null || printf '0')" + pnpm_major="${pnpm_version%%.*}" + case "$pnpm_version" in + *.*) + pnpm_minor="${pnpm_version#*.}" + pnpm_minor="${pnpm_minor%%.*}" + ;; + *) + pnpm_minor=0 + ;; + esac + case "$pnpm_major" in + ''|*[!0-9]*) pnpm_major=0 ;; + esac + case "$pnpm_minor" in + ''|*[!0-9]*) pnpm_minor=0 ;; + esac + if [ "$pnpm_major" -gt 11 ]; then + return 0 + fi + if [ "$pnpm_major" -eq 11 ] && [ "$pnpm_minor" -ge 3 ]; then + return 0 + fi + return 1 + } + install_package_dependencies() { local package_runner="$1" case "$package_runner" in @@ -1533,8 +1563,7 @@ jobs: pnpm) trusted_pnpm_lock_matches_base prepare_writable_pnpm_store - pnpm_major="$(corepack pnpm --version | cut -d. -f1 || printf '0')" - if [ "$pnpm_major" -ge 11 ] 2>/dev/null; then + if pnpm_supports_trust_lockfile; then run_and_capture "JavaScript/TypeScript dependencies (pnpm offline install, lifecycle hooks disabled)" \ corepack pnpm install \ --offline \ diff --git a/CHANGELOG.md b/CHANGELOG.md index c9b4fbf125..1a61716930 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,10 +12,10 @@ Semantic Versioning where the repository publishes a release. during materialization and then rejecting every version except pnpm 11.5.3; route generic coverage and docstring package scripts through the same Corepack boundary instead of invoking a removed bare `pnpm` binary. -- Keep `--trust-lockfile` only for pnpm major versions 11 and newer - (`trustLockfile` landed in pnpm 11.3). pnpm 9 and 10 reject that flag - and previously failed LineageWeave JavaScript coverage before tests - could run. Jest test scripts still receive `--coverage` because Jest +- Keep `--trust-lockfile` only for pnpm 11.3 and newer + (`trustLockfile` landed in pnpm 11.3). pnpm 9, 10, and 11.0–11.2 reject + that flag and previously failed LineageWeave JavaScript coverage before + tests could run. Jest test scripts still receive `--coverage` because Jest documents a native coverage flag. - Run declared JavaScript test scripts without synthesizing `--coverage` when the package does not declare a coverage provider, so networkless evidence diff --git a/docs/doctoring/opencode-pnpm-lock-compat.md b/docs/doctoring/opencode-pnpm-lock-compat.md index 4fd10ce2da..ed6c7bd5d5 100644 --- a/docs/doctoring/opencode-pnpm-lock-compat.md +++ b/docs/doctoring/opencode-pnpm-lock-compat.md @@ -5,12 +5,13 @@ ## Decision OpenCode coverage-evidence honors the repository-owned `packageManager` -pin through Corepack. `--trust-lockfile` is valid only on pnpm major -version 11 and newer (`trustLockfile` landed in pnpm 11.3; Kochan, 2026). -Exact trusted-base lock matching remains mandatory before any offline -install. The sandbox never invents a JavaScript coverage instrumenter -when the package did not declare one, except that a bare `jest` test -script still receives Jest's documented `--coverage` flag. +pin through Corepack. `--trust-lockfile` is valid only on pnpm 11.3 and +newer (`trustLockfile` landed in pnpm 11.3; Kochan, 2026). pnpm 11.0, +11.1, and 11.2 still reject the flag. Exact trusted-base lock matching +remains mandatory before any offline install. The sandbox never invents a +JavaScript coverage instrumenter when the package did not declare one, +except that a bare `jest` test script still receives Jest's documented +`--coverage` flag. This keeps LineageWeave and other pnpm 9.x products measurable after Corepack started activating the repository pin instead of a central @@ -24,7 +25,7 @@ otherwise see every frontend head blocked on `Unknown option: 1. Coverage images now activate the exact `packageManager` from the validated base (for LineageWeave, `pnpm@9.15.9`). 2. The install command still passed `--trust-lockfile`, a pnpm 11.3 flag. - pnpm 9 and pnpm 10 exit before reading the store. + pnpm 9, pnpm 10, and pnpm 11.0–11.2 exit before reading the store. 3. After a successful install, coverage appended `--coverage` to `vitest run` even when no coverage provider was declared, so tests never became evidence. @@ -36,11 +37,13 @@ required. Python still never runs `uv sync --project`. ## Remediation -- When `corepack pnpm --version` reports major version 11 or newer, keep - `--trust-lockfile` so registry attestation lookups stay suppressed for - an exact trusted-base lock (Kochan, 2026; pnpm, n.d.). -- When the major version is below 11, omit that flag. pnpm 9 and 10 already - treat `--frozen-lockfile` plus `--offline` as the integrity boundary. +- When `corepack pnpm --version` reports 11.3 or newer (major greater + than 11, or major 11 with minor 3 or greater), keep `--trust-lockfile` + so registry attestation lookups stay suppressed for an exact + trusted-base lock (Kochan, 2026; pnpm, n.d.). +- When the version is below 11.3, omit that flag. pnpm 9, 10, and + 11.0–11.2 already treat `--frozen-lockfile` plus `--offline` as the + integrity boundary. - When `package.json` has a test script but no coverage script, coverage collector in `scripts.test`, declared provider (`@vitest/coverage-v8`, `@vitest/coverage-istanbul`, `c8`, `nyc`, diff --git a/scripts/ci/test_strix_quick_gate.sh b/scripts/ci/test_strix_quick_gate.sh index 4e8dc2de04..7bf8a30933 100755 --- a/scripts/ci/test_strix_quick_gate.sh +++ b/scripts/ci/test_strix_quick_gate.sh @@ -981,7 +981,8 @@ assert_file_contains "$REPO_ROOT/scripts/ci/run_opencode_review_model_pool.sh" ' assert_file_contains "$workflow_file" '"$COVERAGE_SOURCE_WORKDIR/$relative_lock"' "coverage hashes nested pnpm locks from the validated worktree root" assert_file_not_contains "$workflow_file" 'hash-object --no-filters -- "$relative_lock"' "coverage does not double-prefix nested package lock paths from the package working directory" assert_file_contains "$workflow_file" "--trust-lockfile" "coverage suppresses registry attestation lookups only for an exact trusted-base lock" - assert_file_contains "$workflow_file" '[ "$pnpm_major" -ge 11 ]' "coverage omits --trust-lockfile on pnpm major versions before 11" + assert_file_contains "$workflow_file" "pnpm_supports_trust_lockfile()" "coverage gates --trust-lockfile on a helper that parses major and minor" + assert_file_contains "$workflow_file" '[ "$pnpm_major" -eq 11 ] && [ "$pnpm_minor" -ge 3 ]' "coverage omits --trust-lockfile on pnpm versions before 11.3" assert_file_contains "$workflow_file" "javascript_coverage_provider_declared()" "coverage does not invent a JavaScript coverage instrumenter the package did not declare" assert_file_contains "$workflow_file" "prepare_writable_pnpm_store()" "coverage prepares a sandbox-writable clone of the trusted pnpm store" assert_file_contains "$workflow_file" 'destination="$(mktemp -d /tmp/opencode-pnpm-store.XXXXXX)"' "coverage creates the writable pnpm store at an unpredictable root-owned path" diff --git a/tests/test_opencode_agent_contract.py b/tests/test_opencode_agent_contract.py index fc0b449e33..0fcfcbf0ea 100644 --- a/tests/test_opencode_agent_contract.py +++ b/tests/test_opencode_agent_contract.py @@ -1035,7 +1035,74 @@ def test_opencode_coverage_does_not_duplicate_existing_javascript_coverage(): assert 'corepack pnpm --filter "$package_name" run build' in measure_step assert "corepack pnpm test" in measure_step assert "corepack pnpm run test --coverage" in measure_step - assert "[ \"$pnpm_major\" -ge 11 ]" in measure_step + assert "pnpm_supports_trust_lockfile()" in measure_step + assert "if pnpm_supports_trust_lockfile; then" in measure_step + assert "[ \"$pnpm_major\" -gt 11 ]" in measure_step + assert "[ \"$pnpm_major\" -eq 11 ] && [ \"$pnpm_minor\" -ge 3 ]" in measure_step + assert "[ \"$pnpm_major\" -ge 11 ]" not in measure_step + + +def test_opencode_coverage_gates_trust_lockfile_on_pnpm_11_3(tmp_path): + """Pass --trust-lockfile only when corepack pnpm --version is 11.3 or newer.""" + bash = shutil.which("bash") + if bash is None: + pytest.skip( + "bash is required for the extracted workflow function regression test" + ) + try: + subprocess.run( + [bash, "--version"], capture_output=True, text=True, timeout=5, check=True + ) + except (OSError, subprocess.SubprocessError) as exc: + pytest.skip(f"bash is not usable for this regression test: {exc}") + + workflow = Path(".github/workflows/opencode-review-dispatch.yml").read_text( + encoding="utf-8" + ) + measure_start = workflow.index( + " - name: Measure test and docstring evidence\n" + ) + measure_end = workflow.index("\n - name:", measure_start + 1) + measure_step = workflow[measure_start:measure_end] + helper_start = measure_step.index(" pnpm_supports_trust_lockfile() {\n") + helper_end = measure_step.index( + "\n\n install_package_dependencies()", helper_start + ) + helper = textwrap.dedent(measure_step[helper_start:helper_end]) + fake_corepack = tmp_path / "bin" + fake_corepack.mkdir() + (fake_corepack / "corepack").write_text( + "#!/bin/sh\nprintf '%s\\n' \"${FAKE_PNPM_VERSION:-0}\"\n", + encoding="utf-8", + ) + (fake_corepack / "corepack").chmod(0o755) + env = os.environ.copy() + env["PATH"] = f"{fake_corepack}{os.pathsep}{env.get('PATH', '')}" + + cases = ( + ("9.15.9", 1), + ("10.28.1", 1), + ("11.0.0", 1), + ("11.2.3", 1), + ("11.3.0", 0), + ("11.5.3", 0), + ("12.0.0", 0), + ("0", 1), + ) + for version, expected_status in cases: + env["FAKE_PNPM_VERSION"] = version + result = subprocess.run( + [bash, "-c", f"set -euo pipefail\n{helper}\npnpm_supports_trust_lockfile"], + env=env, + capture_output=True, + text=True, + timeout=10, + ) + assert result.returncode == expected_status, ( + f"pnpm {version} expected status {expected_status}, " + f"got {result.returncode}: {result.stderr}" + ) + def test_opencode_coverage_discovers_changed_nested_javascript_package(tmp_path): """A changed JS file must select its nearest nested package.json for coverage.""" @@ -2206,7 +2273,11 @@ def test_opencode_privileged_review_security_boundaries_are_fail_closed(): assert "--offline" in coverage_job assert "--frozen-lockfile" in coverage_job assert "--trust-lockfile" in coverage_job - assert "[ \"$pnpm_major\" -ge 11 ]" in coverage_job + assert "pnpm_supports_trust_lockfile()" in coverage_job + assert 'if pnpm_supports_trust_lockfile; then' in coverage_job + assert "[ \"$pnpm_major\" -gt 11 ]" in coverage_job + assert "[ \"$pnpm_minor\" -ge 3 ]" in coverage_job + assert "[ \"$pnpm_major\" -ge 11 ]" not in coverage_job assert "javascript_coverage_provider_declared()" in coverage_job assert "javascript_test_runner_accepts_coverage_flag()" in coverage_job assert "coverage provider not declared" in coverage_job diff --git a/tests/test_pr_review_autofix_nvidia_nim_contract.py b/tests/test_pr_review_autofix_nvidia_nim_contract.py index d2f1fbb83d..0c29125400 100644 --- a/tests/test_pr_review_autofix_nvidia_nim_contract.py +++ b/tests/test_pr_review_autofix_nvidia_nim_contract.py @@ -19,7 +19,7 @@ DOCTORING_RECORD = Path("docs/doctoring/hourly-nvidia-nim-autofix.md") CHANGELOG = Path("CHANGELOG.md") REVIEW_DISPATCH_WORKFLOW = Path(".github/workflows/opencode-review-dispatch.yml") -REVIEW_DISPATCH_BLOB_SHA = "a276171dc8c933c1609a7dc9717a26ecf50db211" +REVIEW_DISPATCH_BLOB_SHA = "557fa4f0997e6bb6603e45964f2c00a2832c33ff" def _workflow_text(path: Path) -> str: From 897819c48279b0c0d5e2372eb39dce6120784685 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 24 Aug 2026 02:24:20 +0900 Subject: [PATCH 4/4] fix(coverage): keep missing frontend coverage fail-closed --- .../workflows/opencode-review-dispatch.yml | 33 ++++--- CHANGELOG.md | 6 +- docs/doctoring/opencode-pnpm-lock-compat.md | 20 +++-- scripts/ci/test_strix_quick_gate.sh | 4 +- tests/test_opencode_agent_contract.py | 87 ++++++++++++++++++- ...t_pr_review_autofix_nvidia_nim_contract.py | 2 +- 6 files changed, 119 insertions(+), 33 deletions(-) diff --git a/.github/workflows/opencode-review-dispatch.yml b/.github/workflows/opencode-review-dispatch.yml index 557fa4f099..dd65d90e10 100644 --- a/.github/workflows/opencode-review-dispatch.yml +++ b/.github/workflows/opencode-review-dispatch.yml @@ -1232,26 +1232,22 @@ jobs: } javascript_test_script_collects_coverage() { - jq -e '(.scripts.test // "") | test("(^|[[:space:]])--coverage([.=[:space:]]|$)|c8([[:space:]]|$)|nyc([[:space:]]|$)")' \ + jq -e '(.scripts.test // "") | test("(^|[[:space:]])--coverage([.=[:space:]]|$)|c8([[:space:]]|$)|nyc([[:space:]]|$)|istanbul([[:space:]]|$)")' \ package.json >/dev/null 2>&1 } javascript_test_runner_accepts_coverage_flag() { - jq -e '(.scripts.test // "") | test("(^|[[:space:]])jest([[:space:]]|$)")' \ - package.json >/dev/null 2>&1 - } - - javascript_coverage_provider_declared() { - if javascript_test_script_collects_coverage; then - return 0 - fi jq -e ' - ((.devDependencies // {}) + (.dependencies // {})) - | has("@vitest/coverage-v8") - or has("@vitest/coverage-istanbul") - or has("c8") - or has("nyc") - or has("istanbul") + (.scripts.test // "") as $test + | ((.devDependencies // {}) + (.dependencies // {})) as $dependencies + | ($test | test("(^|[[:space:]])jest([[:space:]]|$)")) + or ( + ($test | test("(^|[[:space:]])vitest([[:space:]]|$)")) + and ( + ($dependencies | has("@vitest/coverage-v8")) + or ($dependencies | has("@vitest/coverage-istanbul")) + ) + ) ' package.json >/dev/null 2>&1 } @@ -2081,7 +2077,7 @@ jobs: run_package_script_and_capture "JavaScript/TypeScript coverage script" "$package_runner" coverage javascript_coverage_ran=1 elif jq -e '.scripts.test // empty' package.json >/dev/null; then - if javascript_test_script_collects_coverage || javascript_coverage_provider_declared || javascript_test_runner_accepts_coverage_flag; then + if javascript_test_script_collects_coverage || javascript_test_runner_accepts_coverage_flag; then if javascript_test_script_collects_coverage; then case "$package_runner" in npm) run_and_capture "JavaScript/TypeScript test coverage" npm test ;; @@ -2104,9 +2100,10 @@ jobs: esac append "### JavaScript/TypeScript coverage threshold" append "" - append "- Result: PASS" - append "- Reason: package.json defines a test script but no coverage provider or coverage-collecting test script; the networkless sandbox ran the tests without synthesizing a coverage instrumenter. Line-coverage remains repository-owned." + append "- Result: FAIL" + append "- Reason: package.json defines a test script but no compatible coverage provider or coverage-collecting test script. The networkless sandbox ran the tests without inventing an instrumenter, but plain tests cannot satisfy the required frontend coverage gate. Add a lock-pinned provider and a repository-owned coverage command." append "" + failures=$((failures + 1)) fi else append "### JavaScript/TypeScript test coverage" diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a61716930..29ddb10bde 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,8 +18,10 @@ Semantic Versioning where the repository publishes a release. tests could run. Jest test scripts still receive `--coverage` because Jest documents a native coverage flag. - Run declared JavaScript test scripts without synthesizing `--coverage` when - the package does not declare a coverage provider, so networkless evidence - still records passing tests instead of failing on a missing instrumenter. + the package does not declare a compatible coverage command, but keep the + coverage result failed until the repository adds a lock-pinned provider and + owned coverage command. A generic `c8`, `nyc`, or Istanbul dependency no + longer makes an unrelated test runner receive an unsupported flag. - Fix OpenCode coverage evidence for exact-base, organization-owned Python VCS dependencies without weakening registry hashes or the networkless PR sandbox, reject namespace, ambiguous, linked, native-extension, and installed-metadata diff --git a/docs/doctoring/opencode-pnpm-lock-compat.md b/docs/doctoring/opencode-pnpm-lock-compat.md index ed6c7bd5d5..d4bf282e75 100644 --- a/docs/doctoring/opencode-pnpm-lock-compat.md +++ b/docs/doctoring/opencode-pnpm-lock-compat.md @@ -11,7 +11,8 @@ newer (`trustLockfile` landed in pnpm 11.3; Kochan, 2026). pnpm 11.0, remains mandatory before any offline install. The sandbox never invents a JavaScript coverage instrumenter when the package did not declare one, except that a bare `jest` test script still receives Jest's documented -`--coverage` flag. +`--coverage` flag and a Vitest script receives it only when a compatible +Vitest coverage provider is lock-pinned. This keeps LineageWeave and other pnpm 9.x products measurable after Corepack started activating the repository pin instead of a central @@ -44,12 +45,17 @@ required. Python still never runs `uv sync --project`. - When the version is below 11.3, omit that flag. pnpm 9, 10, and 11.0–11.2 already treat `--frozen-lockfile` plus `--offline` as the integrity boundary. -- When `package.json` has a test script but no coverage script, coverage - collector in `scripts.test`, declared provider - (`@vitest/coverage-v8`, `@vitest/coverage-istanbul`, `c8`, `nyc`, - `istanbul`), or a bare `jest` runner, run the tests without synthesizing - an undeclared instrumenter. A `jest` script still receives `--coverage` - because Jest documents that native flag (Jest, n.d.). +- When `package.json` has a test script but no coverage script or compatible + coverage-collecting runner, run the tests without synthesizing an undeclared + instrumenter and fail the coverage result with the next action: add a + lock-pinned provider and repository-owned coverage command. Plain passing + tests are not 100% frontend coverage evidence. +- A bare Jest script receives `--coverage` because Jest documents that native + flag (Jest, n.d.). Vitest receives `--coverage` only with + `@vitest/coverage-v8` or `@vitest/coverage-istanbul`. A generic `c8`, `nyc`, + or Istanbul dependency is not proof that an arbitrary runner accepts the + flag; those collectors count only when the test script actually invokes + them. Independent OpenCode, Strix, and Noema review remain authorization gates. This change does not approve, merge, or weaken hash-pinned diff --git a/scripts/ci/test_strix_quick_gate.sh b/scripts/ci/test_strix_quick_gate.sh index 7bf8a30933..945eb3fb30 100755 --- a/scripts/ci/test_strix_quick_gate.sh +++ b/scripts/ci/test_strix_quick_gate.sh @@ -983,7 +983,9 @@ assert_file_contains "$REPO_ROOT/scripts/ci/run_opencode_review_model_pool.sh" ' assert_file_contains "$workflow_file" "--trust-lockfile" "coverage suppresses registry attestation lookups only for an exact trusted-base lock" assert_file_contains "$workflow_file" "pnpm_supports_trust_lockfile()" "coverage gates --trust-lockfile on a helper that parses major and minor" assert_file_contains "$workflow_file" '[ "$pnpm_major" -eq 11 ] && [ "$pnpm_minor" -ge 3 ]' "coverage omits --trust-lockfile on pnpm versions before 11.3" - assert_file_contains "$workflow_file" "javascript_coverage_provider_declared()" "coverage does not invent a JavaScript coverage instrumenter the package did not declare" + assert_file_contains "$workflow_file" "javascript_test_runner_accepts_coverage_flag()" "coverage adds a native flag only for a compatible Jest or provider-backed Vitest runner" + assert_file_not_contains "$workflow_file" "javascript_coverage_provider_declared()" "coverage does not infer runner compatibility from an unused generic provider dependency" + assert_file_contains "$workflow_file" "plain tests cannot satisfy the required frontend coverage gate" "coverage fails closed when a package has no compatible coverage command" assert_file_contains "$workflow_file" "prepare_writable_pnpm_store()" "coverage prepares a sandbox-writable clone of the trusted pnpm store" assert_file_contains "$workflow_file" 'destination="$(mktemp -d /tmp/opencode-pnpm-store.XXXXXX)"' "coverage creates the writable pnpm store at an unpredictable root-owned path" assert_file_contains "$workflow_file" 'cp -R /opt/pnpm-store/. "$destination/"' "coverage clones packages from the trusted image seed" diff --git a/tests/test_opencode_agent_contract.py b/tests/test_opencode_agent_contract.py index 0fcfcbf0ea..8ee6e86fc9 100644 --- a/tests/test_opencode_agent_contract.py +++ b/tests/test_opencode_agent_contract.py @@ -1003,13 +1003,13 @@ def test_opencode_coverage_does_not_duplicate_existing_javascript_coverage(): measure_step = workflow[measure_start:measure_end] assert "javascript_test_script_collects_coverage()" in measure_step - assert "javascript_coverage_provider_declared()" in measure_step assert "javascript_test_runner_accepts_coverage_flag()" in measure_step assert "if javascript_test_script_collects_coverage; then" in measure_step assert ( - "if javascript_test_script_collects_coverage || javascript_coverage_provider_declared || javascript_test_runner_accepts_coverage_flag; then" + "if javascript_test_script_collects_coverage || javascript_test_runner_accepts_coverage_flag; then" in measure_step ) + assert "javascript_coverage_provider_declared" not in measure_step assert ( 'npm) run_and_capture "JavaScript/TypeScript test coverage" npm test ;;' in measure_step @@ -1029,8 +1029,9 @@ def test_opencode_coverage_does_not_duplicate_existing_javascript_coverage(): assert "pnpm test --coverage" not in measure_step assert "pnpm test -- --coverage" not in measure_step assert 'test("(^|[[:space:]])--coverage([.=[:space:]]|$)' in measure_step - assert '|c8([[:space:]]|$)|nyc([[:space:]]|$)")' in measure_step + assert '|c8([[:space:]]|$)|nyc([[:space:]]|$)|istanbul([[:space:]]|$)")' in measure_step assert 'test("(^|[[:space:]])jest([[:space:]]|$)"' in measure_step + assert 'test("(^|[[:space:]])vitest([[:space:]]|$)"' in measure_step assert "corepack pnpm install" in measure_step assert 'corepack pnpm --filter "$package_name" run build' in measure_step assert "corepack pnpm test" in measure_step @@ -1042,6 +1043,83 @@ def test_opencode_coverage_does_not_duplicate_existing_javascript_coverage(): assert "[ \"$pnpm_major\" -ge 11 ]" not in measure_step +@pytest.mark.parametrize( + ("test_script", "dev_dependencies", "expected_status"), + ( + ("jest", {}, 0), + ("vitest run", {"@vitest/coverage-v8": "4.1.10"}, 0), + ("vitest run", {"@vitest/coverage-istanbul": "4.1.10"}, 0), + ("vitest run", {}, 1), + ("node --test", {"c8": "10.1.3"}, 1), + ("mocha", {"nyc": "17.1.0"}, 1), + ("node --test", {"@vitest/coverage-v8": "4.1.10"}, 1), + ), +) +def test_opencode_coverage_only_adds_flag_for_compatible_runner( + tmp_path: Path, + test_script: str, + dev_dependencies: dict[str, str], + expected_status: int, +) -> None: + """A merely installed coverage package must not authorize runner flags.""" + bash = shutil.which("bash") + jq = shutil.which("jq") + if bash is None or jq is None: + pytest.skip("bash and jq are required for the extracted workflow regression") + + workflow = Path(".github/workflows/opencode-review-dispatch.yml").read_text( + encoding="utf-8" + ) + measure_start = workflow.index( + " - name: Measure test and docstring evidence\n" + ) + measure_end = workflow.index("\n - name:", measure_start + 1) + measure_step = workflow[measure_start:measure_end] + helper_start = measure_step.index( + " javascript_test_runner_accepts_coverage_flag() {\n" + ) + helper_end = measure_step.index("\n\n declared_package_manager()", helper_start) + helper = textwrap.dedent(measure_step[helper_start:helper_end]) + (tmp_path / "package.json").write_text( + json.dumps( + { + "scripts": {"test": test_script}, + "devDependencies": dev_dependencies, + } + ), + encoding="utf-8", + ) + + result = subprocess.run( + [bash, "-c", f"set -euo pipefail\n{helper}\njavascript_test_runner_accepts_coverage_flag"], + cwd=tmp_path, + capture_output=True, + text=True, + timeout=10, + ) + + assert result.returncode == expected_status, result.stderr + + +def test_opencode_missing_javascript_coverage_fails_closed() -> None: + """Plain tests cannot satisfy the organization's frontend coverage gate.""" + workflow = Path(".github/workflows/opencode-review-dispatch.yml").read_text( + encoding="utf-8" + ) + missing_provider_start = workflow.index( + 'run_and_capture "JavaScript/TypeScript tests (coverage provider not declared)"' + ) + missing_provider_end = workflow.index( + " else\n" + ' append "### JavaScript/TypeScript test coverage"', + missing_provider_start, + ) + missing_provider_block = workflow[missing_provider_start:missing_provider_end] + + assert 'append "- Result: FAIL"' in missing_provider_block + assert "failures=$((failures + 1))" in missing_provider_block + + def test_opencode_coverage_gates_trust_lockfile_on_pnpm_11_3(tmp_path): """Pass --trust-lockfile only when corepack pnpm --version is 11.3 or newer.""" bash = shutil.which("bash") @@ -2278,9 +2356,10 @@ def test_opencode_privileged_review_security_boundaries_are_fail_closed(): assert "[ \"$pnpm_major\" -gt 11 ]" in coverage_job assert "[ \"$pnpm_minor\" -ge 3 ]" in coverage_job assert "[ \"$pnpm_major\" -ge 11 ]" not in coverage_job - assert "javascript_coverage_provider_declared()" in coverage_job assert "javascript_test_runner_accepts_coverage_flag()" in coverage_job + assert "javascript_coverage_provider_declared" not in coverage_job assert "coverage provider not declared" in coverage_job + assert "plain tests cannot satisfy the required frontend coverage gate" in coverage_job assert "--ignore-scripts" in coverage_job assert "prepare_writable_pnpm_store" in coverage_job assert '--store-dir "$writable_pnpm_store_dir"' in coverage_job diff --git a/tests/test_pr_review_autofix_nvidia_nim_contract.py b/tests/test_pr_review_autofix_nvidia_nim_contract.py index 0c29125400..799b9e9fb8 100644 --- a/tests/test_pr_review_autofix_nvidia_nim_contract.py +++ b/tests/test_pr_review_autofix_nvidia_nim_contract.py @@ -19,7 +19,7 @@ DOCTORING_RECORD = Path("docs/doctoring/hourly-nvidia-nim-autofix.md") CHANGELOG = Path("CHANGELOG.md") REVIEW_DISPATCH_WORKFLOW = Path(".github/workflows/opencode-review-dispatch.yml") -REVIEW_DISPATCH_BLOB_SHA = "557fa4f0997e6bb6603e45964f2c00a2832c33ff" +REVIEW_DISPATCH_BLOB_SHA = "dd65d90e10e5040562b501ade1a40f89572f0984" def _workflow_text(path: Path) -> str: