From 045b19b0f2d35d68a35df4a6c9c212612ad2c90e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 24 Jun 2026 04:54:28 +0900 Subject: [PATCH 1/3] Refresh governance audit after clearfolio rollout --- PR_GOVERNANCE_AUDIT.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/PR_GOVERNANCE_AUDIT.md b/PR_GOVERNANCE_AUDIT.md index 35195d59e..d909701e2 100644 --- a/PR_GOVERNANCE_AUDIT.md +++ b/PR_GOVERNANCE_AUDIT.md @@ -18,13 +18,13 @@ OpenCode decides; GitHub Actions mutates. ## Live Repository Inventory -Live generated: 2026-06-23 04:18 KST. PR #28 post-merge refresh: 2026-06-23 16:05 KST. PR #37 post-merge refresh: 2026-06-23 21:50 KST. +Live generated: 2026-06-23 04:18 KST. PR #28 post-merge refresh: 2026-06-23 16:05 KST. PR #37 post-merge refresh: 2026-06-23 21:50 KST. clearfolio PR #13 post-merge refresh: 2026-06-24 04:48 KST. | Repo | Flow | Default | Auto | Rulesets | Required checks | Stale dismissal | Merge queue | Workflows | Recent merged actor | |---|---:|---:|---:|---|---|---:|---:|---|---| -| `ContextualWisdomLab/.github` | GitHub Flow | `main` | on | `Lock default branch` | none | true | no | OpenCode Review; PR Review Merge Scheduler; Strix Security Scan | #28 `seonghobae` merge `a025be1`; #18 `seonghobae`; #17 `seonghobae` | +| `ContextualWisdomLab/.github` | GitHub Flow | `main` | on | `Lock default branch` | none | true | no | OpenCode Review; PR Review Merge Scheduler; Strix Security Scan | #41 `seonghobae` merge `b3393d5`; #38 `seonghobae` merge `928e43b`; #37 `seonghobae` merge `3c3695f` | | `ContextualWisdomLab/bandscope` | Git Flow | `develop` | on | `Lock default branch` | `ci / build-and-test`, `dependency-review`, `security-audit`, `CodeQL`, `sbom`, `release-preflight`, `gate / build / windows`, `gate / build / macos`, `trivy-fs-scan` | false | no | OpenCode Review; PR Review Merge Scheduler; Strix Security Scan | #427 `github-actions`; #408 `seonghobae`; #405 `seonghobae` | -| `ContextualWisdomLab/clearfolio` | GitHub Flow | `main` | off | `PR` | none | false | no | OpenCode Review; Strix Security Scan | #9 `seonghobae`; #8 `seonghobae`; #7 `seonghobae` | +| `ContextualWisdomLab/clearfolio` | GitHub Flow | `main` | off | `PR` | none | false | no | OpenCode Review; PR Review Merge Scheduler; Strix Security Scan | #13 `seonghobae` merge `4bc17c6`; #9 `seonghobae`; #8 `seonghobae` | | `ContextualWisdomLab/codec-carver` | GitHub Flow | `main` | on | `Lock default branch` | none | true | no | OpenCode Review; Scheduled PR Review Merge; Strix Security Scan | #94 `opencode-agent`; #93 `seonghobae`; #90 `seonghobae` | | `ContextualWisdomLab/contextual-orchestrator` | GitHub Flow | `main` | off | none | none | unknown | unknown | none matched | none | | `ContextualWisdomLab/ContextualWisdomLab.github.io` | GitHub Flow | `main` | on | `Lock default branch` | none | true | no | OpenCode Review; PR Review Merge Scheduler; Strix Security Scan | #15 `seonghobae`; #14 `seonghobae`; #13 `github-actions` auto by `github-actions` | @@ -38,9 +38,9 @@ Live generated: 2026-06-23 04:18 KST. PR #28 post-merge refresh: 2026-06-23 16:0 | Repo | Gap | |---|---| -| `.github` | PR #37 is merged at `3c3695f` after current-head Strix run `28025893898`, current-head manual OpenCode run `28026724674`, unresolved review threads `0`, and guarded merge against head `8b25761`. Remaining open PRs #19-#27 and #29-#36 are still blocked by `CHANGES_REQUESTED` and/or `DIRTY`. | +| `.github` | PR #37, #38, and #41 are merged. Remaining open PRs #19-#27, #29-#36, #39, #40, and #42 still need current-head review/check evaluation; #42 has PR-target Strix run `28052498149` in progress for head `36cc8ca`. | | `bandscope` | Required checks are repo-specific and broad; keep GitHub native auto-merge as the check interpreter. | -| `clearfolio` | Auto-merge is off. PR #13 adds the central PR Review Merge Scheduler and `opencode.jsonc`; current PR-target Strix still fails because base `strix.yml` does not copy PR-head `opencode.jsonc` into the trusted workspace. | +| `clearfolio` | PR #13 is merged at `4bc17c6` after same-head manual Strix run `28051319530`, same-head manual OpenCode run `28051665082`, unresolved review threads `0`, and guarded merge against head `5fe1791`. Auto-merge remains off, so direct guarded merge is the repo path. | | `codec-carver` | Latest merged sample #94 still used `opencode-agent`; PR #98 replaces the legacy scheduler with the central GitHub Actions path and is waiting on existing OpenCode/Strix checks. | | `contextual-orchestrator` | No matching rulesets or review workflows; either opt in deliberately or mark unmanaged. | | `naruon` | Canonical strict check source, but open PRs still need the updated contract observed through one full outdated -> update -> new-head review trace. | @@ -105,7 +105,7 @@ PR #36: block: merge conflict: DIRTY 1. Keep `naruon`, `.github`, `VibeSec`, `bandscope`, `newsdom-api`, `pg-erd-cloud`, and `scopeweave` on `PR Review Merge Scheduler`. 2. Merge `codec-carver` PR #98 to replace legacy `Scheduled PR Review Merge` with `PR Review Merge Scheduler`; current checks were still in progress at the 2026-06-23 22:13 KST snapshot. -3. Resolve `clearfolio` PR #13's trusted-base Strix blocker, then merge it to add `PR Review Merge Scheduler`; auto-merge is currently off. +3. `clearfolio` PR #13 is complete; keep the repo on direct guarded merge until auto-merge is deliberately enabled. 4. Decide whether `contextual-orchestrator` should join the central PR governance surface; no matching workflows or rulesets were returned. 5. Keep `pg-erd-cloud` autofix workflows repo-local; do not make autofix part of the central merge contract. @@ -128,7 +128,7 @@ PR #36: block: merge conflict: DIRTY - Strix run `28022323798` caught that the first label repair changed normalizer parsing too narrowly: inline approval summaries in `test_strix_quick_gate.sh` no longer normalized. Label parsing now accepts inline verification labels while excluding the `Coverage:` suffix inside `Docstring coverage:`, preserving both inline transcript controls and appended evidence repair. - PR #37 same-head manual Strix run `28023392848` succeeded for head `07a6b76`, but the concurrently dispatched same-head manual OpenCode run `28023401894` spent its early lifetime waiting in `Prepare bounded OpenCode review evidence`. That exposed a scheduler-level resource issue: dispatching Strix and OpenCode together can turn OpenCode into a long poller whenever Strix is queued or slow. The scheduler now serializes the process: first dispatch Strix, then wait for a later scheduler pass to dispatch OpenCode after Strix evidence is complete. - The base-branch automatic OpenCode run `28025023007` still posted a current-head `CHANGES_REQUESTED` review before cancellation on head `1d05f52`, even though that automatic trigger is removed by this PR. The scheduler previously treated any current-head OpenCode `CHANGES_REQUESTED` as permanent. It now reads the latest OpenCode review on the current head, so a later same-head OpenCode approval can supersede an earlier false negative from the same reviewer. -- `clearfolio` PR #13 and `codec-carver` PR #98 are opened as thin rollouts. Their scheduler workflows now pin `scripts/ci/pr_review_merge_scheduler.py` to central commit `7be2d99` and verify SHA-256 `f954b62efa4ad60964a65501d777cb4ba26f1ac5746c2d11406d610a5ab695f6` before running the script self-test and inspecting their own PR queues. `codec-carver` PR #98 also deletes the legacy OpenCode app-token merge workflow. -- `clearfolio` PR #13 first failed Strix run `28027843973` because `opencode.jsonc` was missing. Commit `38e9a82` added the central `opencode.jsonc`, but Strix runs `28028155386`, `28030126946`, and `28030438259` still failed because clearfolio's trusted-base `strix.yml` did not copy `PR_HEAD_SHA:opencode.jsonc` into the trusted workspace. Commit `2618c41` adds the PR-head `opencode.jsonc` and scheduler-policy materialization to `strix.yml`; PR-target Strix run `28030872994` and same-head manual Strix run `28030912898` were still in progress or queued at the 2026-06-23 22:48 KST snapshot. +- `clearfolio` PR #13 and `codec-carver` PR #98 were opened as thin rollouts. `clearfolio` PR #13 is now merged at `4bc17c6`; `codec-carver` PR #98 remains the thin rollout that deletes the legacy OpenCode app-token merge workflow. +- `clearfolio` PR #13 first failed Strix run `28027843973` because `opencode.jsonc` was missing. Later current-head proof used manual Strix run `28051319530` and manual OpenCode run `28051665082`; the final approval named the changed review-tooling files and head `5fe1791d48ddcf03dbc365cc6fa407e7cbe70a89` before guarded merge. - `codec-carver` PR #98 already has base `opencode.jsonc`. PR #98 now pins the central scheduler instead of downloading from `main`; same-head Strix run `28030439830` and OpenCode runs `28030438605`/`28030439065` were still in progress at the 2026-06-23 22:48 KST snapshot. - `.github` PR #38 exposed two central gaps after PR #37 merged: the `review_dispatch` reason lost the `same-head Strix and OpenCode dispatched` contract string, and `failed_status_checks()` treated failed PR-target Strix check runs as blockers even when a later manual `strix` status could supersede them. Commit `7be2d99` restores the reason string, materializes PR-head scheduler policy as non-executed data for Strix self-test, and ignores stale Strix check-run failures when the same head has a successful `strix` status context. Manual Strix run `28030448032` had passed self-test and was still running `Run Strix (quick)` at the 2026-06-23 22:48 KST snapshot. From f70545f761098d62e68a872e84d5fc8bfa57f744 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 24 Jun 2026 05:13:27 +0900 Subject: [PATCH 2/3] Require exact changed-file evidence for OpenCode approvals --- .github/workflows/opencode-review.yml | 7 ++++ PR_GOVERNANCE_AUDIT.md | 1 + .../ci/opencode_review_normalize_output.py | 26 +++++++++++- scripts/ci/test_strix_quick_gate.sh | 41 +++++++++++++++++++ 4 files changed, 74 insertions(+), 1 deletion(-) diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml index 17d2ce50b..c5d4d77cc 100644 --- a/.github/workflows/opencode-review.yml +++ b/.github/workflows/opencode-review.yml @@ -320,11 +320,13 @@ jobs: OPENCODE_SOURCE_WORKDIR: ${{ runner.temp }}/opencode-pr-head OPENCODE_EVIDENCE_FILE: ${{ runner.temp }}/opencode-review-evidence.md OPENCODE_FAILED_CHECK_EVIDENCE_FILE: ${{ runner.temp }}/opencode-failed-check-evidence.md + OPENCODE_CHANGED_FILES_FILE: ${{ runner.temp }}/opencode-changed-files.txt COVERAGE_EVIDENCE_SUMMARY: ${{ needs.coverage-evidence.outputs.coverage_summary || 'Coverage evidence job did not run or did not publish coverage evidence.' }} FAILED_CHECK_EVIDENCE_ATTEMPTS: "75" FAILED_CHECK_EVIDENCE_SLEEP_SECONDS: "30" run: | set -euo pipefail + printf 'OPENCODE_CHANGED_FILES_FILE=%s\n' "$OPENCODE_CHANGED_FILES_FILE" >>"$GITHUB_ENV" current_peer_checks_still_running() { local owner="${GH_REPOSITORY%%/*}" @@ -650,6 +652,7 @@ jobs: printf -- "- Head SHA: \`%s\`\n\n" "$PR_HEAD_SHA" PR_MERGE_BASE="$(git -C "$OPENCODE_SOURCE_WORKDIR" merge-base "$PR_BASE_SHA" "$PR_HEAD_SHA")" printf -- "- Merge base SHA: \`%s\`\n\n" "$PR_MERGE_BASE" + git -C "$OPENCODE_SOURCE_WORKDIR" diff --name-only --find-renames "$PR_MERGE_BASE" "$PR_HEAD_SHA" >"$OPENCODE_CHANGED_FILES_FILE" printf '## CodeGraph evidence\n\n' printf 'The workflow initialized CodeGraph before this evidence file was built.\n' @@ -720,6 +723,7 @@ jobs: OPENCODE_REVIEW_WORKDIR: ${{ runner.temp }}/opencode-review-project OPENCODE_EVIDENCE_FILE: ${{ runner.temp }}/opencode-review-evidence.md OPENCODE_FAILED_CHECK_EVIDENCE_FILE: ${{ runner.temp }}/opencode-failed-check-evidence.md + OPENCODE_CHANGED_FILES_FILE: ${{ runner.temp }}/opencode-changed-files.txt OPENCODE_SOURCE_WORKDIR: ${{ runner.temp }}/opencode-pr-head run: | set -euo pipefail @@ -736,6 +740,9 @@ jobs: if [ -s "$OPENCODE_FAILED_CHECK_EVIDENCE_FILE" ]; then cp "$OPENCODE_FAILED_CHECK_EVIDENCE_FILE" "$OPENCODE_REVIEW_WORKDIR/failed-check-evidence.md" fi + if [ -s "$OPENCODE_CHANGED_FILES_FILE" ]; then + cp "$OPENCODE_CHANGED_FILES_FILE" "$OPENCODE_REVIEW_WORKDIR/changed-files.txt" + fi cat >"${OPENCODE_REVIEW_WORKDIR}/AGENTS.md" <<'EOF' # OpenCode CI Review Rules diff --git a/PR_GOVERNANCE_AUDIT.md b/PR_GOVERNANCE_AUDIT.md index d909701e2..50c9ede08 100644 --- a/PR_GOVERNANCE_AUDIT.md +++ b/PR_GOVERNANCE_AUDIT.md @@ -130,5 +130,6 @@ PR #36: block: merge conflict: DIRTY - The base-branch automatic OpenCode run `28025023007` still posted a current-head `CHANGES_REQUESTED` review before cancellation on head `1d05f52`, even though that automatic trigger is removed by this PR. The scheduler previously treated any current-head OpenCode `CHANGES_REQUESTED` as permanent. It now reads the latest OpenCode review on the current head, so a later same-head OpenCode approval can supersede an earlier false negative from the same reviewer. - `clearfolio` PR #13 and `codec-carver` PR #98 were opened as thin rollouts. `clearfolio` PR #13 is now merged at `4bc17c6`; `codec-carver` PR #98 remains the thin rollout that deletes the legacy OpenCode app-token merge workflow. - `clearfolio` PR #13 first failed Strix run `28027843973` because `opencode.jsonc` was missing. Later current-head proof used manual Strix run `28051319530` and manual OpenCode run `28051665082`; the final approval named the changed review-tooling files and head `5fe1791d48ddcf03dbc365cc6fa407e7cbe70a89` before guarded merge. +- `.github` PR #42 exposed that central approval normalization should not accept generic path-looking evidence when exact current-head changed files are available. The OpenCode workflow now writes `git diff --name-only --find-renames "$PR_MERGE_BASE" "$PR_HEAD_SHA"` to `OPENCODE_CHANGED_FILES_FILE`, gives the isolated review workspace `changed-files.txt`, and the normalizer rejects `APPROVE` unless the approval names one of those exact files. - `codec-carver` PR #98 already has base `opencode.jsonc`. PR #98 now pins the central scheduler instead of downloading from `main`; same-head Strix run `28030439830` and OpenCode runs `28030438605`/`28030439065` were still in progress at the 2026-06-23 22:48 KST snapshot. - `.github` PR #38 exposed two central gaps after PR #37 merged: the `review_dispatch` reason lost the `same-head Strix and OpenCode dispatched` contract string, and `failed_status_checks()` treated failed PR-target Strix check runs as blockers even when a later manual `strix` status could supersede them. Commit `7be2d99` restores the reason string, materializes PR-head scheduler policy as non-executed data for Strix self-test, and ignores stale Strix check-run failures when the same head has a successful `strix` status context. Manual Strix run `28030448032` had passed self-test and was still running `Run Strix (quick)` at the 2026-06-23 22:48 KST snapshot. diff --git a/scripts/ci/opencode_review_normalize_output.py b/scripts/ci/opencode_review_normalize_output.py index a624030c7..03fb200db 100755 --- a/scripts/ci/opencode_review_normalize_output.py +++ b/scripts/ci/opencode_review_normalize_output.py @@ -137,6 +137,30 @@ def mentions_changed_file_evidence(reason: str, summary: str) -> bool: return bool(CHANGED_FILE_EVIDENCE_PATTERN.search(f"{reason}\n{summary}")) +def current_changed_files() -> set[str]: + """Return the exact current-head changed files when the workflow provides them.""" + changed_files_path = os.environ.get("OPENCODE_CHANGED_FILES_FILE") + if not changed_files_path: + return set() + try: + return { + line.strip() + for line in Path(changed_files_path).read_text(encoding="utf-8").splitlines() + if line.strip() + } + except OSError: + return set() + + +def mentions_actual_changed_file(reason: str, summary: str) -> bool: + """Return whether an approval names an exact current-head changed file.""" + changed_files = current_changed_files() + if not changed_files: + return mentions_changed_file_evidence(reason, summary) + combined = f"{reason}\n{summary}" + return any(changed_file in combined for changed_file in changed_files) + + def mentions_verification_posture(reason: str, summary: str) -> bool: """Return whether an approval records the concrete review surfaces checked.""" combined = f"{reason}\n{summary}".casefold() @@ -379,7 +403,7 @@ def valid_control( if admits_missing_structural_review(reason, summary): return None summary = repair_approval_summary(reason, summary) - if not mentions_changed_file_evidence(reason, summary): + if not mentions_actual_changed_file(reason, summary): return None if not mentions_verification_posture(reason, summary): return None diff --git a/scripts/ci/test_strix_quick_gate.sh b/scripts/ci/test_strix_quick_gate.sh index dcc81dbab..af28b1ef6 100755 --- a/scripts/ci/test_strix_quick_gate.sh +++ b/scripts/ci/test_strix_quick_gate.sh @@ -1076,10 +1076,12 @@ EOF assert_opencode_review_gate_rejects_approve_without_changed_file_evidence() { local tmp_dir local output_file + local changed_files_file local rc local gate_result tmp_dir="$(mktemp -d)" output_file="$tmp_dir/opencode-output.md" + changed_files_file="$tmp_dir/changed-files.txt" cat >"$output_file" <<'EOF' OpenCode transcript text before the review control block. @@ -1115,6 +1117,45 @@ EOF assert_equals "4" "$rc" "opencode approval gate rejects approvals without changed-file evidence" assert_equals "NO_CONCLUSION" "$gate_result" "missing changed-file evidence rejection gate result" assert_file_contains "$REPO_ROOT/.github/workflows/opencode-review.yml" "Before APPROVE, the summary must include at least one exact changed file path inspected as changed-file evidence" "opencode prompt requires changed-file evidence before approval" + assert_file_contains "$REPO_ROOT/.github/workflows/opencode-review.yml" "OPENCODE_CHANGED_FILES_FILE" "opencode workflow exports exact current-head changed files" + assert_file_contains "$REPO_ROOT/.github/workflows/opencode-review.yml" 'diff --name-only --find-renames "$PR_MERGE_BASE" "$PR_HEAD_SHA" >"$OPENCODE_CHANGED_FILES_FILE"' "opencode workflow writes exact changed files for the normalizer" + assert_file_contains "$REPO_ROOT/.github/workflows/opencode-review.yml" "changed-files.txt" "opencode workflow copies exact changed-file evidence into the isolated review workspace" + + cat >"$changed_files_file" <<'EOF' +.github/workflows/opencode-review.yml +scripts/ci/opencode_review_normalize_output.py +EOF + + cat >"$output_file" <<'EOF' +OpenCode transcript text before the review control block. + +{"head_sha":"abc123","run_id":"42","run_attempt":"1","result":"APPROVE","reason":"No blockers found after inspecting README.md.","summary":"Reviewed README.md. Verification posture: Linter/static: actionlint and bash syntax evidence passed. TDD/regression: scripts/ci/test_strix_quick_gate.sh self-test evidence passed. Coverage: Coverage execution evidence reported 100% test coverage. Docstring coverage: Coverage execution evidence reported 100% docstring coverage. DAG: Change Flow DAG rendered README.md to docs review path. PoC/execution: scratch PoC executed bash scripts/ci/test_strix_quick_gate.sh and passed. DDD/domain: no product domain boundary changed. CDD/context: CodeGraph structural MCP evidence covered the blast radius. Similar issues: checked related OpenCode gate cases. Claim/concept check: no unverified user concept accepted. Standards search: checked current GitHub Actions docs. Compatibility/convention: conventions match existing code. Breaking-change/backcompat: no public contract changed. Performance: no runtime path affected. Design/UX: no user-facing UI affected. Security/privacy: token boundaries preserved.","findings":[]} +EOF + + set +e + OPENCODE_CHANGED_FILES_FILE="$changed_files_file" \ + python3 "$REPO_ROOT/scripts/ci/opencode_review_normalize_output.py" \ + "abc123" "42" "1" "$output_file" >"$tmp_dir/nonchanged-normalize.out" 2>"$tmp_dir/nonchanged-normalize.err" + rc=$? + set -e + + assert_equals "4" "$rc" "opencode normalizer rejects approvals that cite non-changed files when exact changed-file evidence is available" + assert_file_contains "$tmp_dir/nonchanged-normalize.err" "NO_CONCLUSION" "opencode normalizer reports no conclusion for non-changed-file approval evidence" + + cat >"$output_file" <<'EOF' +OpenCode transcript text before the review control block. + +{"head_sha":"abc123","run_id":"42","run_attempt":"1","result":"APPROVE","reason":"No blockers found after inspecting .github/workflows/opencode-review.yml.","summary":"Reviewed .github/workflows/opencode-review.yml and scripts/ci/opencode_review_normalize_output.py. Verification posture: Linter/static: actionlint and Python syntax evidence passed. TDD/regression: normalizer self-test evidence passed. Coverage: Coverage execution evidence reported 100% test coverage. Docstring coverage: Coverage execution evidence reported 100% docstring coverage. DAG: Change Flow DAG rendered .github/workflows/opencode-review.yml to scripts/ci/opencode_review_normalize_output.py to review decision path. PoC/execution: scratch PoC executed the normalizer with exact changed-file evidence and passed. DDD/domain: no product domain boundary changed. CDD/context: CodeGraph structural MCP evidence covered the workflow and script blast radius. Similar issues: checked related OpenCode gate cases. Claim/concept check: no unverified user concept accepted. Standards search: checked current GitHub Actions/OpenCode docs where applicable. Compatibility/convention: workflow naming and Python conventions match existing code. Breaking-change/backcompat: no deployed public contract changed. Performance: no runtime path affected. Design/UX: no user-facing UI affected. Security/privacy: token and pull_request_target boundaries preserved.","findings":[]} +EOF + + set +e + OPENCODE_CHANGED_FILES_FILE="$changed_files_file" \ + python3 "$REPO_ROOT/scripts/ci/opencode_review_normalize_output.py" \ + "abc123" "42" "1" "$output_file" >"$tmp_dir/changed-normalize.out" 2>"$tmp_dir/changed-normalize.err" + rc=$? + set -e + + assert_equals "0" "$rc" "opencode normalizer accepts approvals that cite exact current changed files" rm -rf "$tmp_dir" } From d7db070bcadcbde00982385eecde8a824b75442e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 24 Jun 2026 05:26:06 +0900 Subject: [PATCH 3/3] Cover exact changed-file approval gating --- .../test_opencode_review_normalize_output.py | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tests/test_opencode_review_normalize_output.py b/tests/test_opencode_review_normalize_output.py index b85bb8d79..b715f8e6c 100644 --- a/tests/test_opencode_review_normalize_output.py +++ b/tests/test_opencode_review_normalize_output.py @@ -70,6 +70,46 @@ def test_changed_file_and_verification_posture_detection(): assert not norm.mentions_verification_posture("", FULL_SUMMARY.replace("CodeGraph", "graph")) +def test_actual_changed_file_detection_prefers_current_head_file_list(tmp_path, monkeypatch): + monkeypatch.delenv("OPENCODE_CHANGED_FILES_FILE", raising=False) + assert norm.current_changed_files() == set() + assert norm.mentions_actual_changed_file("scripts/ci/example.py", "") + + changed_files = tmp_path / "changed-files.txt" + changed_files.write_text( + "\n".join( + [ + ".github/workflows/opencode-review.yml", + "scripts/ci/opencode_review_normalize_output.py", + "", + ] + ), + encoding="utf-8", + ) + monkeypatch.setenv("OPENCODE_CHANGED_FILES_FILE", str(changed_files)) + + assert norm.current_changed_files() == { + ".github/workflows/opencode-review.yml", + "scripts/ci/opencode_review_normalize_output.py", + } + assert norm.mentions_actual_changed_file( + "Reviewed .github/workflows/opencode-review.yml.", + "", + ) + assert norm.mentions_actual_changed_file( + "", + "Reviewed scripts/ci/opencode_review_normalize_output.py.", + ) + assert not norm.mentions_actual_changed_file( + "Reviewed README.md.", + "Ran scripts/ci/test_strix_quick_gate.sh.", + ) + + monkeypatch.setenv("OPENCODE_CHANGED_FILES_FILE", str(tmp_path / "missing.txt")) + assert norm.current_changed_files() == set() + assert norm.mentions_actual_changed_file("scripts/ci/example.py", "") + + def test_label_and_full_coverage_detection(): combined = FULL_SUMMARY.casefold() assert "100%" in norm.label_section(combined, "coverage:")