From f45391db39d0d18f47b818eecc1d8520d6b5483d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 17:42:26 +0900 Subject: [PATCH 01/59] fix(opencode): bound the required-verdict poll by wall clock, not just transport failures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "Fail closed without a current-head OpenCode verdict" step in opencode-review.yml polls the Reviews API in a `while :; do ... done` loop guarded only by max_poll_transport_failures=3 -- a counter of *consecutive gh CLI transport failures*. A dispatched review that never posts a verdict while every individual `gh api` call keeps succeeding (the review simply never lands) never trips that counter, so the loop -- and the runner it occupies -- never ends. This is exactly what happened org-wide: a stuck/never-completing review dispatch occupied a live Actions runner for hours, and multiplied across many PRs this exhausted the org's shared Actions concurrent-job capacity, leaving thousands of runs queued and stalling required-review dispatch for other open PRs (observed: PRs #1006-1017 stuck unable to even start their required review). Add a genuine total-wall-clock deadline alongside (not instead of) the existing transport-failure counter: `poll_deadline_epoch`, computed once before the loop starts, checked at the top of every iteration. The bound is 10800s (3h) -- comfortably longer than this repo's own documented "OpenCode/ Strix/Noema may take over two hours per model" allowance (docs/product-goal-directive.md §8), so a legitimately slow model is never falsely failed closed, while staying well under GitHub's 360-minute job default. On trip it emits a diagnostic distinct from the transport-failure message ("No verdict after 180 minutes of polling; failing closed and releasing the runner.") and exits 1, releasing the runner. Also fixes two existing tests that extract this exact step's real bash and execute it with a fake `gh` that fails loudly on any unexpected call, but never stubbed `sleep` -- driving the transport-failure retry path to its 3-failure threshold performed two genuine 60s sleeps (~120s of real wall-clock time per test run): tests/test_opencode_required_verdict_regression.py::test_fail_closed_step_still_polls_for_a_non_draft_pr and tests/test_opencode_live_draft_state_regression.py::test_stale_draft_verdict_event_does_not_exempt_live_ready_pr. Both now stub `sleep` alongside the existing fake `gh`, matching the pattern already used in test_opencode_poll_self_retirement.py. New tests in test_opencode_poll_self_retirement.py extend that file's existing `_run_poll_loop` harness (which already extracts and executes the real loop body against a fake `gh`/`sleep`/`timeout`) with an injectable fake `date`, proving: (a) the loop fails closed with the new diagnostic once the wall-clock deadline is exceeded even when every gh call keeps succeeding across several genuinely-executed fast iterations (the exact zombie scenario), (b) a fast verdict is unaffected by the new bound, and (c) the production shape keeps both bounds distinct and additive. No test sleeps for real time to prove any of this. Full affected suite verified green and fast (112.76s, vs. 236.65s before this fix, for the same 3 pre-existing unrelated failures caused by a local venv missing pip and one already-failing head-moved test unrelated to this change). Co-Authored-By: Claude Sonnet 5 --- .github/workflows/opencode-review.yml | 18 +++ ...st_opencode_live_draft_state_regression.py | 24 ++- tests/test_opencode_poll_self_retirement.py | 143 +++++++++++++++++- ...st_opencode_required_verdict_regression.py | 21 ++- 4 files changed, 195 insertions(+), 11 deletions(-) diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml index 48592f0163..c8e5ac3af0 100644 --- a/.github/workflows/opencode-review.yml +++ b/.github/workflows/opencode-review.yml @@ -423,7 +423,25 @@ jobs: review_poll_failures=0 max_poll_transport_failures=3 poll_interval_seconds=60 + # Distinct from max_poll_transport_failures above: that counter only + # catches consecutive gh CLI transport failures. A dispatched + # review that never posts a verdict -- every gh call succeeding, + # just with no matching APPROVED/CHANGES_REQUESTED review yet -- + # would loop here forever and occupy this runner indefinitely, + # which is what let a stuck dispatch exhaust the org's shared + # Actions concurrent-job capacity and stall required-review + # dispatch for other open PRs. 10800s (3h) comfortably exceeds this + # repo's own documented "OpenCode/Strix/Noema may take over two + # hours per model" allowance (docs/product-goal-directive.md §8) + # while staying well under GitHub's 360-minute job default, so a + # legitimately slow model is never falsely failed closed. + max_poll_wall_clock_seconds=10800 + poll_deadline_epoch=$(( $(date +%s) + max_poll_wall_clock_seconds )) while :; do + if [ "$(date +%s)" -ge "$poll_deadline_epoch" ]; then + echo "::error::No verdict after $(( max_poll_wall_clock_seconds / 60 )) minutes of polling; failing closed and releasing the runner." + exit 1 + fi if ! live_poll_pr="$(timeout 30s gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"; then live_poll_failures=$((live_poll_failures + 1)) if [ "$live_poll_failures" -ge "$max_poll_transport_failures" ]; then diff --git a/tests/test_opencode_live_draft_state_regression.py b/tests/test_opencode_live_draft_state_regression.py index a9d9c518bc..da2eae5d50 100644 --- a/tests/test_opencode_live_draft_state_regression.py +++ b/tests/test_opencode_live_draft_state_regression.py @@ -34,6 +34,14 @@ def _write_live_state_gh( for exercising a missing/null/non-string/unexpected ``state`` field that the convenience ``live_draft``/``live_head``/``live_state`` parameters cannot express. + + Also stubs ``sleep`` to return instantly: ``fail_closed_script()``'s + transport-failure retry path really does ``sleep "$poll_interval_seconds"`` + (60s) between attempts, and this fixture's later-call sentinel exit code + drives that path to its 3-failure fail-closed threshold in + ``test_stale_draft_verdict_event_does_not_exempt_live_ready_pr`` -- without + this stub that test performs two genuine 60s sleeps (~120s real + wall-clock time per run) instead of running fast. """ payload = json.dumps( live_payload_override @@ -70,6 +78,9 @@ def evaluate_receipts(reviews, head_sha, *, is_draft): encoding="utf-8", ) fake_gh.chmod(fake_gh.stat().st_mode | 0o111) + fake_sleep = bin_dir / "sleep" + fake_sleep.write_text("#!/usr/bin/env bash\nexit 0\n", encoding="utf-8") + fake_sleep.chmod(fake_sleep.stat().st_mode | 0o111) def _run_step( @@ -138,12 +149,13 @@ def test_stale_draft_verdict_event_does_not_exempt_live_ready_pr( Unlike ``request_review_script()``'s single unguarded live-PR fetch, this step's post-draft-check Reviews API poll retries a transport failure up - to ``max_poll_transport_failures`` times (with a real backoff sleep - between attempts) before failing closed with its own exit 1 and - diagnostic -- so the fixture's synthetic unmocked-call sentinel exit code - never reaches this script's own exit status, unlike the sibling test - above. The "stale" continuation message is still emitted first, proving - the step did not silently exempt the live-ready PR from verdict polling. + to ``max_poll_transport_failures`` times (with a stubbed, instant backoff + "sleep" between attempts -- see ``_write_live_state_gh``) before failing + closed with its own exit 1 and diagnostic -- so the fixture's synthetic + unmocked-call sentinel exit code never reaches this script's own exit + status, unlike the sibling test above. The "stale" continuation message + is still emitted first, proving the step did not silently exempt the + live-ready PR from verdict polling. """ result = _run_step(tmp_path, fail_closed_script(), live_draft=False) diff --git a/tests/test_opencode_poll_self_retirement.py b/tests/test_opencode_poll_self_retirement.py index cd12a567d5..1ba7b2e151 100644 --- a/tests/test_opencode_poll_self_retirement.py +++ b/tests/test_opencode_poll_self_retirement.py @@ -35,8 +35,16 @@ def _run_poll_loop( reviews: list[dict[str, object]] | None = None, fail_live_pr_attempts: int = 0, fail_review_attempts: int = 0, + date_epochs: list[int] | None = None, ) -> tuple[subprocess.CompletedProcess[str], list[str]]: - """Execute the production poll body against a deterministic fake ``gh``.""" + """Execute the production poll body against a deterministic fake ``gh``. + + ``date_epochs``, when given, stubs ``date +%s`` to return each listed + epoch in turn (clamped to the last entry once exhausted) instead of the + real clock -- letting a test fast-forward past the real + ``max_poll_wall_clock_seconds`` deadline after a chosen number of + genuinely-executed loop iterations, without ever sleeping for real time. + """ call_log = tmp_path / "gh-calls.log" live_fail_counter = tmp_path / "live-pr-failures" review_fail_counter = tmp_path / "review-failures" @@ -84,6 +92,35 @@ def _run_poll_loop( ) fake_timeout.chmod(0o755) + env_overrides: dict[str, str] = {} + if date_epochs is not None: + date_epochs_file = tmp_path / "date-epochs" + date_epochs_file.write_text( + "\n".join(str(epoch) for epoch in date_epochs) + "\n", encoding="utf-8" + ) + date_counter = tmp_path / "date-calls" + fake_date = tmp_path / "date" + fake_date.write_text( + """#!/bin/sh +set -eu +count=0 +if [ -e "$FAKE_DATE_COUNTER" ]; then + count="$(cat "$FAKE_DATE_COUNTER")" +fi +count=$((count + 1)) +printf '%s\\n' "$count" > "$FAKE_DATE_COUNTER" +line="$(sed -n "${count}p" "$FAKE_DATE_EPOCHS")" +if [ -z "$line" ]; then + line="$(tail -n1 "$FAKE_DATE_EPOCHS")" +fi +printf '%s\\n' "$line" +""", + encoding="utf-8", + ) + fake_date.chmod(0o755) + env_overrides["FAKE_DATE_EPOCHS"] = str(date_epochs_file) + env_overrides["FAKE_DATE_COUNTER"] = str(date_counter) + script = "\n".join( ( "set -euo pipefail", @@ -92,6 +129,8 @@ def _run_poll_loop( 'review_poll_failures=0', 'max_poll_transport_failures=3', 'poll_interval_seconds=60', + 'max_poll_wall_clock_seconds=10800', + 'poll_deadline_epoch=$(( $(date +%s) + max_poll_wall_clock_seconds ))', "while :; do", _poll_loop(), "done", @@ -111,6 +150,7 @@ def _run_poll_loop( "GH_REVIEW_FAIL_COUNTER": str(review_fail_counter), "GH_LIVE_PR": json.dumps(live_pr), "GH_REVIEWS": json.dumps(reviews or []), + **env_overrides, } ) result = subprocess.run( @@ -315,6 +355,72 @@ def test_poll_fails_closed_after_bounded_reviews_transport_failures( ] * 3 +def test_poll_fails_closed_after_wall_clock_deadline_with_every_gh_call_succeeding( + tmp_path: Path, +) -> None: + """The zombie scenario: no transport failure ever occurs, yet no verdict posts. + + `max_poll_transport_failures` cannot catch this -- every `gh` call + below succeeds -- so only a genuinely distinct wall-clock deadline + (`max_poll_wall_clock_seconds` / `poll_deadline_epoch`) can release the + runner. A fake `date` fast-forwards past the real production 10800s + (180-minute) bound only after two full, genuinely-executed fast + iterations (proving the check is a real per-iteration wall-clock + comparison, not a check that fires before any work happens), without + this test ever sleeping for real time. + """ + head_sha = "5" * 40 + result, calls = _run_poll_loop( + tmp_path, + head_sha=head_sha, + live_pr={"head": {"sha": head_sha}, "draft": False, "state": "open"}, + reviews=[], # opencode-agent never posts a review on this head + date_epochs=[1000, 1000, 1000, 999999999999], + ) + + assert result.returncode == 1 + assert ( + "::error::No verdict after 180 minutes of polling; failing closed " + "and releasing the runner." in result.stdout + ) + # Distinct diagnostic from the transport-failure path: nothing here failed. + assert "consecutive times" not in result.stdout + assert calls == [ + "api repos/ContextualWisdomLab/example/pulls/42", + "api --paginate repos/ContextualWisdomLab/example/pulls/42/reviews?per_page=100", + "api repos/ContextualWisdomLab/example/pulls/42", + "api --paginate repos/ContextualWisdomLab/example/pulls/42/reviews?per_page=100", + ] + + +def test_poll_wall_clock_deadline_does_not_interfere_with_a_fast_verdict( + tmp_path: Path, +) -> None: + """A verdict arriving on the first poll is unaffected by the new bound.""" + head_sha = "6" * 40 + result, calls = _run_poll_loop( + tmp_path, + head_sha=head_sha, + live_pr={"head": {"sha": head_sha}, "draft": False, "state": "open"}, + reviews=[ + { + "user": {"login": "opencode-agent[bot]"}, + "commit_id": head_sha, + "state": "APPROVED", + "body": "Source-backed current-head semantic review.", + } + ], + date_epochs=[1000, 1000], # baseline call, then one in-bounds iteration check + ) + + assert result.returncode == 0, result.stderr + assert calls == [ + "api repos/ContextualWisdomLab/example/pulls/42", + "api --paginate repos/ContextualWisdomLab/example/pulls/42/reviews?per_page=100", + ] + assert "No verdict after" not in result.stdout + + def test_self_retirement_does_not_replace_semantic_review_with_a_short_timeout() -> None: """Capacity hygiene must not impose an arbitrary review inference deadline.""" target_job = WORKFLOW.read_text(encoding="utf-8").split( @@ -324,3 +430,38 @@ def test_self_retirement_does_not_replace_semantic_review_with_a_short_timeout() assert "while :; do" in target_job assert "poll_interval_seconds=60" in target_job assert 'sleep "$poll_interval_seconds"' in target_job + + +def test_wall_clock_deadline_is_distinct_from_and_additional_to_transport_counter() -> None: + """The new bound sits alongside, not in place of, the transport-failure counter. + + Pins the production shape so a future edit cannot quietly collapse the + two into one, or drop the wall-clock bound back to unbounded: both + `max_poll_transport_failures` (existing) and `max_poll_wall_clock_seconds` + / `poll_deadline_epoch` (new) must be present, and the wall-clock check + must live inside the `while :; do` loop body -- not as a job-level + `timeout-minutes:`, which would kill the runner mid-request instead of + failing closed with a clear diagnostic. + """ + target_job = WORKFLOW.read_text(encoding="utf-8").split( + " opencode-review-target:\n", 1 + )[1].split("\n cancel-superseded-opencode-review-runs:\n", 1)[0] + assert "max_poll_transport_failures=3" in target_job + assert "max_poll_wall_clock_seconds=10800" in target_job + assert ( + "poll_deadline_epoch=$(( $(date +%s) + max_poll_wall_clock_seconds ))" + in target_job + ) + loop = _poll_loop() + assert ( + 'if [ "$(date +%s)" -ge "$poll_deadline_epoch" ]; then' in loop + ) + assert ( + '::error::No verdict after $(( max_poll_wall_clock_seconds / 60 )) ' + "minutes of polling; failing closed and releasing the runner." in loop + ) + # The deadline check must precede this iteration's gh calls so an + # already-expired deadline never spends another API request. + assert loop.index('-ge "$poll_deadline_epoch"') < loop.index( + 'live_poll_pr="$(timeout 30s gh api' + ) diff --git a/tests/test_opencode_required_verdict_regression.py b/tests/test_opencode_required_verdict_regression.py index f4f353e6da..05993face8 100644 --- a/tests/test_opencode_required_verdict_regression.py +++ b/tests/test_opencode_required_verdict_regression.py @@ -280,7 +280,16 @@ def test_required_workflow_cannot_succeed_with_an_echo_only_placeholder() -> Non def _write_live_pr_then_refusing_gh(bin_dir: Path) -> None: - """Serve the authoritative live PR lookup, then reject downstream GitHub I/O.""" + """Serve the authoritative live PR lookup, then reject downstream GitHub I/O. + + Also stubs ``sleep`` to return instantly. The production poll loop's + transport-failure path really does ``sleep "$poll_interval_seconds"`` + (60s) between retries -- without this stub, a test that drives that path + to its 3-failure fail-closed threshold performs two genuine 60s sleeps + (observed directly: this exact gap made + ``test_fail_closed_step_still_polls_for_a_non_draft_pr`` take ~120s of + real wall-clock time per run instead of running fast). + """ fake_gh = bin_dir / "gh" fake_gh.write_text( "#!/usr/bin/env bash\n" @@ -294,6 +303,9 @@ def _write_live_pr_then_refusing_gh(bin_dir: Path) -> None: encoding="utf-8", ) fake_gh.chmod(fake_gh.stat().st_mode | 0o111) + fake_sleep = bin_dir / "sleep" + fake_sleep.write_text("#!/usr/bin/env bash\nexit 0\n", encoding="utf-8") + fake_sleep.chmod(fake_sleep.stat().st_mode | 0o111) def _run_fail_closed_step( @@ -623,9 +635,10 @@ def test_fail_closed_step_still_polls_for_a_non_draft_pr(tmp_path: Path) -> None """A non-draft PR must still reach the Reviews API call (not exempted). Unlike the request-review step's single unguarded call, the Reviews API - fetch here retries a transport failure up to three times (with a real - backoff sleep between attempts) before failing closed with its own exit - 1, so the fixture's synthetic unmocked-call sentinel exit code (17) + fetch here retries a transport failure up to three times (with a + stubbed, instant backoff "sleep" between attempts -- see + ``_write_live_pr_then_refusing_gh``) before failing closed with its own + exit 1, so the fixture's synthetic unmocked-call sentinel exit code (17) never reaches this script's own exit status -- it is absorbed by the retry loop instead, which still logs the sentinel's stderr diagnostic on every attempt. From ab0c19f70b06a23bac881a7fd232bb254cd79c7d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 18:03:45 +0900 Subject: [PATCH 02/59] test(opencode): require event-driven verdict continuation before repair --- ...pencode_required_verdict_runner_release.py | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 tests/test_opencode_required_verdict_runner_release.py diff --git a/tests/test_opencode_required_verdict_runner_release.py b/tests/test_opencode_required_verdict_runner_release.py new file mode 100644 index 0000000000..cea026508b --- /dev/null +++ b/tests/test_opencode_required_verdict_runner_release.py @@ -0,0 +1,117 @@ +"""Regression coverage for releasing the required OpenCode runner while review continues.""" + +from __future__ import annotations + +import json +import os +import shutil +import subprocess +import textwrap +from pathlib import Path + +import pytest + + +WORKFLOW = Path(".github/workflows/opencode-review.yml") +DISPATCH_WORKFLOW = Path(".github/workflows/opencode-review-dispatch.yml") +HEAD_SHA = "a" * 40 + + +def _fail_closed_script() -> str: + """Extract the real required-verdict admission step body.""" + workflow = WORKFLOW.read_text(encoding="utf-8") + step = workflow.split( + " - name: Fail closed without a current-head OpenCode verdict\n", 1 + )[1] + return textwrap.dedent(step.split(" run: |\n", 1)[1]) + + +def test_missing_verdict_uses_exact_run_wake_instead_of_runner_polling() -> None: + """A missing verdict must fail once and rely on the authenticated exact-run wake.""" + required = _fail_closed_script() + dispatched = DISPATCH_WORKFLOW.read_text(encoding="utf-8") + + assert "while :; do" not in required + assert "poll_interval_seconds" not in required + assert "poll_deadline_epoch" not in required + assert "sleep " not in required + assert "rerun-failed-jobs" in dispatched + assert "github.event.client_payload.required_run_id != ''" in dispatched + assert 'gh api "repos/${GH_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}"' in dispatched + assert 'select(.event == "pull_request_target")' in dispatched + assert 'select(.path == ".github/workflows/opencode-review.yml")' in dispatched + assert "select(.head_sha == $head)" in dispatched + + +def test_missing_verdict_fails_after_one_live_read_and_one_review_read(tmp_path: Path) -> None: + """Execute the production step and prove it never sleeps or loops without a verdict.""" + bash = shutil.which("bash") + jq = shutil.which("jq") + if bash is None or jq is None: + pytest.skip("bash and jq are required to execute the production verdict step") + + fake_bin = tmp_path / "bin" + fake_bin.mkdir() + call_log = tmp_path / "gh-calls" + fake_gh = fake_bin / "gh" + fake_gh.write_text( + """#!/usr/bin/env bash +set -euo pipefail +printf '%s\\n' "$*" >>"$FAKE_CALL_LOG" +if [[ "$*" == "api repos/ContextualWisdomLab/example/pulls/42" ]]; then + printf '%s\\n' "$FAKE_LIVE_PR" + exit 0 +fi +if [[ "$*" == "api --paginate repos/ContextualWisdomLab/example/pulls/42/reviews?per_page=100" ]]; then + printf '%s\\n' '[]' + exit 0 +fi +printf 'unexpected gh call: %s\\n' "$*" >&2 +exit 97 +""", + encoding="utf-8", + ) + fake_gh.chmod(0o755) + + fake_timeout = fake_bin / "timeout" + fake_timeout.write_text( + "#!/usr/bin/env bash\nset -euo pipefail\nshift\nexec \"$@\"\n", + encoding="utf-8", + ) + fake_timeout.chmod(0o755) + + fake_sleep = fake_bin / "sleep" + fake_sleep.write_text( + "#!/usr/bin/env bash\nprintf 'unexpected sleep\\n' >&2\nexit 91\n", + encoding="utf-8", + ) + fake_sleep.chmod(0o755) + + result = subprocess.run( + [bash, "-c", _fail_closed_script()], + env={ + **os.environ, + "PATH": f"{fake_bin}{os.pathsep}{os.environ.get('PATH', '')}", + "FAKE_CALL_LOG": str(call_log), + "FAKE_LIVE_PR": json.dumps( + {"head": {"sha": HEAD_SHA}, "draft": False, "state": "open"} + ), + "GH_TOKEN": "test-token", + "TARGET_REPOSITORY": "ContextualWisdomLab/example", + "PR_NUMBER": "42", + "HEAD_SHA": HEAD_SHA, + "PR_ACTION": "synchronize", + "PR_DRAFT": "false", + }, + text=True, + capture_output=True, + check=False, + ) + + assert result.returncode == 1, result.stderr + assert "unexpected sleep" not in result.stderr + assert "No APPROVED or CHANGES_REQUESTED from opencode-agent" in result.stdout + assert call_log.read_text(encoding="utf-8").splitlines() == [ + "api repos/ContextualWisdomLab/example/pulls/42", + "api --paginate repos/ContextualWisdomLab/example/pulls/42/reviews?per_page=100", + ] From 11600dbdf2746a07af3922146df39b0fb51346bd Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 18:22:48 +0900 Subject: [PATCH 03/59] ci(temp): repair PR1706 one-shot runner release --- .../_temp_pr1706_one_shot_runner_release.yml | 395 ++++++++++++++++++ 1 file changed, 395 insertions(+) create mode 100644 .github/workflows/_temp_pr1706_one_shot_runner_release.yml diff --git a/.github/workflows/_temp_pr1706_one_shot_runner_release.yml b/.github/workflows/_temp_pr1706_one_shot_runner_release.yml new file mode 100644 index 0000000000..ef83775f22 --- /dev/null +++ b/.github/workflows/_temp_pr1706_one_shot_runner_release.yml @@ -0,0 +1,395 @@ +name: Temporary PR1706 One-shot Runner Release Repair + +on: + push: + branches: + - fix/opencode-poll-wall-clock-bound + paths: + - .github/workflows/_temp_pr1706_one_shot_runner_release.yml + +permissions: + contents: write + +concurrency: + group: temp-pr1706-one-shot-runner-release + cancel-in-progress: false + +jobs: + repair: + runs-on: ubuntu-slim + steps: + - name: Checkout exact trigger head + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + ref: ${{ github.sha }} + fetch-depth: 0 + persist-credentials: true + + - name: Set up Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.14" + + - name: Install locked repository test dependencies + run: | + set -euo pipefail + python3 -m pip install --disable-pip-version-check --require-hashes --only-binary=:all: -r requirements-opencode-review-ci-hashes.txt + + - name: Prove runner-release regression is RED + run: | + set -euo pipefail + if PYTHONPATH=. python3 -m pytest -q tests/test_opencode_required_verdict_runner_release.py; then + echo '::error::Expected one-shot runner-release regression to be RED before production repair.' + exit 1 + fi + + - name: Apply smallest causal owner repair + run: | + set -euo pipefail + python3 <<'PY' + from pathlib import Path + + workflow_path = Path('.github/workflows/opencode-review.yml') + workflow = workflow_path.read_text(encoding='utf-8') + start_marker = ' - name: Fail closed without a current-head OpenCode verdict\n' + end_marker = '\n cancel-superseded-opencode-review-runs:\n' + if workflow.count(start_marker) != 1 or workflow.count(end_marker) != 1: + raise SystemExit('OpenCode required-verdict step boundaries drifted') + before, rest = workflow.split(start_marker, 1) + _old_step, after = rest.split(end_marker, 1) + replacement = ''' - name: Fail closed without a current-head OpenCode verdict + env: + GH_TOKEN: ${{ github.token }} + TARGET_REPOSITORY: ${{ github.event.pull_request.base.repo.full_name || github.repository }} + PR_NUMBER: ${{ github.event.pull_request.number }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + PR_ACTION: ${{ github.event.action }} + PR_DRAFT: ${{ github.event.pull_request.draft }} + run: | + set -euo pipefail + if [ "$PR_ACTION" = "closed" ]; then + echo "PR closed; a current-head OpenCode verdict is not required." + exit 0 + fi + if [ -z "${PR_NUMBER:-}" ] || [ -z "${HEAD_SHA:-}" ]; then + echo "::error::Missing PR number or head SHA; cannot verify a current-head OpenCode verdict." + exit 1 + fi + live_pr="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")" + live_head="$(printf '%s' "$live_pr" | jq -r '.head.sha // empty')" + live_draft="$(printf '%s' "$live_pr" | jq -r 'if (.draft | type) == "boolean" then (.draft | tostring) else empty end')" + live_state="$(printf '%s' "$live_pr" | jq -r 'if (.state | type) == "string" then .state else empty end')" + if [ -z "$live_head" ] || [ -z "$live_draft" ] || [ -z "$live_state" ]; then + echo "::error::Could not validate live pull request state before verdict admission." + exit 1 + fi + if [ "$live_state" != "open" ] && [ "$live_state" != "closed" ]; then + echo "::error::Could not validate live pull request state before verdict admission." + exit 1 + fi + if [ "$live_state" = "closed" ]; then + echo "PR is closed on the live exact head; a current-head OpenCode verdict is not required." + exit 0 + fi + if [ "$live_draft" = "true" ]; then + echo "PR is still a draft on the live exact head; a current-head OpenCode verdict is not required until it is marked ready for review." + exit 0 + fi + if [ "${live_head,,}" != "${HEAD_SHA,,}" ]; then + echo "Pull request head moved on the live open, ready-for-review PR; a fresh required-review run will bind the current head." + exit 0 + fi + if [ "$PR_DRAFT" = "true" ]; then + echo "Event draft snapshot is stale; continuing one-shot verdict admission for the live ready PR." + fi + if ! reviews="$(gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"; then + echo "::error::Reviews API read failed during one-shot current-head verdict admission; failing closed and releasing the runner." + exit 1 + fi + verdict="$(printf '%s\n' "$reviews" | jq -r -s --arg sha "$HEAD_SHA" ' + (add // []) + | [ + .[] + | select( + (.user.login // "" | ascii_downcase) as $user + | $user == "opencode-agent" or $user == "opencode-agent[bot]" + ) + | select((.commit_id // "" | ascii_downcase) == ($sha | ascii_downcase)) + | select(.state == "APPROVED" or .state == "CHANGES_REQUESTED") + ] + | (last // {}) as $review + | ($review.body // "" | ascii_downcase) as $body + | if $review.state == "CHANGES_REQUESTED" then + "CHANGES_REQUESTED" + elif $review.state == "APPROVED" + and ($body | contains("deterministic current-head evidence") | not) + and ($body | contains("deterministic fallback approval") | not) + and ($body | contains("model-unavailable evidence fallback") | not) + and ($body | contains("did not emit a usable current-head control block") | not) + and ($body | contains("scope: `unsupported`") | not) + and ($body | contains("model-pool outcome: `unknown`") | not) + then + "APPROVED" + else + empty + end + ')" + if [ -z "$verdict" ]; then + echo "::error::No APPROVED or CHANGES_REQUESTED from opencode-agent on the current head. This required check is not a review and must not succeed until the authenticated dispatch posts a current-head verdict. The dispatch path wakes this exact failed run when the verdict arrives." + exit 1 + fi + echo "Current-head OpenCode verdict: ${verdict}." +''' + workflow = before + replacement + end_marker + after + workflow = workflow.replace( + '# `converted_to_draft` is included so a PR going draft mid-poll fires a\n' + ' # fresh run of this same workflow: the head-scoped concurrency group below\n' + ' # (`cancel-in-progress: true`) cancels any in-flight non-draft\n' + ' # "Fail closed without a current-head OpenCode verdict" poll for that\n' + ' # exact same head. Every non-closed admission path revalidates the live\n' + ' # PR/head/state before dispatching, exempting, or polling so out-of-order\n' + ' # draft/ready/closed events cannot publish stale evidence or wait on an\n' + ' # impossible verdict.\n', + '# `converted_to_draft` is included so a same-head state transition can\n' + ' # supersede any older admission run. Every non-closed admission path\n' + ' # revalidates live PR/head/state before dispatching, exempting, or reading\n' + ' # reviews, so stale events cannot publish or admit stale evidence.\n', + ) + workflow = workflow.replace( + '# `converted_to_draft` still cancels an active same-head verdict poll.\n', + '# `converted_to_draft` still supersedes an active same-head admission run.\n', + ) + workflow = workflow.replace( + ' # old-head events, while the poll above now revalidates live PR identity on\n' + ' # every wait iteration so an already-running obsolete poll can self-retire\n' + ' # without consuming a second runner. This sibling job remains a defense in\n' + ' # depth for queued/requested old-head runs and for legacy runs created from\n' + ' # older workflow revisions that lack the in-loop self-retirement check.\n', + ' # old-head events. The admission step above is one-shot and never holds a\n' + ' # runner waiting for model work; this sibling remains defense in depth for\n' + ' # queued/requested old-head runs and legacy workflow revisions.\n', + ) + workflow_path.write_text(workflow, encoding='utf-8') + + Path('tests/test_opencode_poll_rate_budget.py').write_text('''"""Request-budget regression for one-shot Required OpenCode verdict admission.""" + +from pathlib import Path + + +WORKFLOW = Path(".github/workflows/opencode-review.yml") + + +def _admission_step() -> str: + """Return the one-shot current-head verdict admission step.""" + workflow = WORKFLOW.read_text(encoding="utf-8") + return workflow.split( + " - name: Fail closed without a current-head OpenCode verdict\\n", 1 + )[1].split("\\n cancel-superseded-opencode-review-runs:\\n", 1)[0] + + +def test_admission_uses_one_reviews_read_without_runner_polling() -> None: + """Missing model evidence releases the runner instead of allocating REST polling.""" + step = _admission_step() + assert step.count('gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}"') == 1 + assert step.count('gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100"') == 1 + assert "while :; do" not in step + assert "poll_interval_seconds" not in step + assert "poll_deadline_epoch" not in step + assert "sleep " not in step + + +def test_review_read_keeps_maximum_rest_page_size() -> None: + """The single Reviews read minimizes pages without dropping history evidence.""" + step = _admission_step() + assert "/reviews?per_page=100" in step + assert "gh api --paginate" in step +''', encoding='utf-8') + + Path('tests/test_opencode_poll_self_retirement.py').write_text('''"""Regression contract for one-shot Required OpenCode verdict admission.""" + +from __future__ import annotations + +import json +import os +import shutil +import subprocess +import textwrap +from pathlib import Path + +import pytest + + +WORKFLOW = Path(".github/workflows/opencode-review.yml") +HEAD_SHA = "a" * 40 + + +def _script() -> str: + """Extract the production one-shot verdict admission script.""" + workflow = WORKFLOW.read_text(encoding="utf-8") + step = workflow.split( + " - name: Fail closed without a current-head OpenCode verdict\\n", 1 + )[1] + return textwrap.dedent(step.split(" run: |\\n", 1)[1]) + + +def _run(tmp_path: Path, *, live_pr: dict[str, object], reviews: list[dict[str, object]] | None = None, fail_reviews: bool = False) -> tuple[subprocess.CompletedProcess[str], list[str]]: + """Execute the production step against deterministic live PR/review evidence.""" + bash = shutil.which("bash") + jq = shutil.which("jq") + if bash is None or jq is None: + pytest.skip("bash and jq are required") + fake_bin = tmp_path / "bin" + fake_bin.mkdir() + log = tmp_path / "calls" + gh = fake_bin / "gh" + gh.write_text('''#!/usr/bin/env bash +set -euo pipefail +printf '%s\\n' "$*" >>"$CALL_LOG" +if [[ "$*" == "api repos/ContextualWisdomLab/example/pulls/42" ]]; then + printf '%s\\n' "$LIVE_PR" + exit 0 +fi +if [[ "$*" == "api --paginate repos/ContextualWisdomLab/example/pulls/42/reviews?per_page=100" ]]; then + if [[ "$FAIL_REVIEWS" == "true" ]]; then exit 7; fi + printf '%s\\n' "$REVIEWS" + exit 0 +fi +exit 97 +''', encoding='utf-8') + gh.chmod(0o755) + sleep = fake_bin / "sleep" + sleep.write_text("#!/usr/bin/env bash\\nexit 91\\n", encoding="utf-8") + sleep.chmod(0o755) + result = subprocess.run( + [bash, "-c", _script()], + env={ + **os.environ, + "PATH": f"{fake_bin}{os.pathsep}{os.environ.get('PATH', '')}", + "CALL_LOG": str(log), + "LIVE_PR": json.dumps(live_pr), + "REVIEWS": json.dumps(reviews or []), + "FAIL_REVIEWS": str(fail_reviews).lower(), + "GH_TOKEN": "test-token", + "TARGET_REPOSITORY": "ContextualWisdomLab/example", + "PR_NUMBER": "42", + "HEAD_SHA": HEAD_SHA, + "PR_ACTION": "synchronize", + "PR_DRAFT": "false", + }, + text=True, + capture_output=True, + check=False, + ) + return result, log.read_text(encoding="utf-8").splitlines() + + +def test_stale_head_retires_before_reviews_read(tmp_path: Path) -> None: + """A moved head cannot consume or admit review evidence.""" + result, calls = _run(tmp_path, live_pr={"head": {"sha": "b" * 40}, "draft": False, "state": "open"}) + assert result.returncode == 0 + assert calls == ["api repos/ContextualWisdomLab/example/pulls/42"] + + +def test_closed_current_head_retires_before_reviews_read(tmp_path: Path) -> None: + """A closed current head releases the runner without a review read.""" + result, calls = _run(tmp_path, live_pr={"head": {"sha": HEAD_SHA}, "draft": False, "state": "closed"}) + assert result.returncode == 0 + assert calls == ["api repos/ContextualWisdomLab/example/pulls/42"] + + +def test_draft_current_head_retires_before_reviews_read(tmp_path: Path) -> None: + """A draft current head releases the runner without an impossible review wait.""" + result, calls = _run(tmp_path, live_pr={"head": {"sha": HEAD_SHA}, "draft": True, "state": "open"}) + assert result.returncode == 0 + assert calls == ["api repos/ContextualWisdomLab/example/pulls/42"] + + +def test_current_head_reads_reviews_once_and_accepts_exact_verdict(tmp_path: Path) -> None: + """A live current head admits one exact-head formal OpenCode verdict.""" + reviews = [{"user": {"login": "opencode-agent[bot]"}, "commit_id": HEAD_SHA, "state": "APPROVED", "body": "source-backed review"}] + result, calls = _run(tmp_path, live_pr={"head": {"sha": HEAD_SHA}, "draft": False, "state": "open"}, reviews=reviews) + assert result.returncode == 0, result.stderr + assert calls == [ + "api repos/ContextualWisdomLab/example/pulls/42", + "api --paginate repos/ContextualWisdomLab/example/pulls/42/reviews?per_page=100", + ] + + +def test_reviews_transport_failure_fails_once_and_releases_runner(tmp_path: Path) -> None: + """Transport failure is fail-closed without a repository-authored retry count.""" + result, calls = _run(tmp_path, live_pr={"head": {"sha": HEAD_SHA}, "draft": False, "state": "open"}, fail_reviews=True) + assert result.returncode == 1 + assert "Reviews API read failed during one-shot" in result.stdout + assert len(calls) == 2 + assert "sleep " not in _script() +''', encoding='utf-8') + + regression_path = Path('tests/test_opencode_required_verdict_regression.py') + regression = regression_path.read_text(encoding='utf-8') + regression = regression.replace(' assert "while :; do" in target_job\n assert \'sleep "$poll_interval_seconds"\' in target_job\n', ' assert "while :; do" not in target_job\n assert "poll_interval_seconds" not in target_job\n assert "poll_deadline_epoch" not in target_job\n assert \'sleep "$poll_interval_seconds"\' not in target_job\n') + regression = regression.replace('before polling', 'before verdict admission') + regression = regression.replace('before ever polling Reviews API', 'before any Reviews API admission read') + regression = regression.replace('while :; do ... sleep "$poll_interval_seconds"; done', 'one-shot current-head verdict read') + regression_path.write_text(regression, encoding='utf-8') + + doctoring_path = Path('docs/doctoring/opencode-stale-poll-self-retirement.md') + doctoring = doctoring_path.read_text(encoding='utf-8') + marker = '## 2026-09-02 one-shot runner-release supersession' + if marker not in doctoring: + doctoring += '''\n\n## 2026-09-02 one-shot runner-release supersession\n\nThe required-verdict job no longer polls while model review continues. It now performs one authoritative live-PR read followed by at most one paginated Reviews read. Missing or unavailable exact-head verdict evidence fails closed immediately and releases the runner. The authenticated `opencode-review-dispatch.yml` path remains responsible for waking the exact failed required run via `rerun-failed-jobs` when the formal verdict arrives. This removes repository-authored polling interval, retry-count, and wall-clock allocations from the review waiting path without imposing an inference timeout on contextual-orchestrator or the serving model.\n''' + doctoring_path.write_text(doctoring, encoding='utf-8') + + baseline_path = Path('docs/product-technical-gap-baseline.md') + baseline = baseline_path.read_text(encoding='utf-8') + marker = 'OPENCODE-ONE-SHOT-RUNNER-RELEASE-2026-09-02' + if marker not in baseline: + baseline += '''\n\n### OPENCODE-ONE-SHOT-RUNNER-RELEASE-2026-09-02\n- Owner: `ContextualWisdomLab/.github` Required OpenCode Review control plane.\n- RCA: the required-verdict job occupied a runner while waiting for asynchronous model work, using repository-authored poll interval, transport retry count, and wall-clock deadline despite an existing authenticated exact-run wake contract in the dispatch workflow.\n- GREEN contract: one live PR read + one Reviews read; absence/transport failure is immediately non-passing, and the dispatch owner wakes the exact failed run after a verdict. Model reasoning/streaming receives no caller wall-clock timeout.\n- Regression: `tests/test_opencode_required_verdict_runner_release.py`, one-shot request-budget/state tests, and the existing exact-run dispatch validation.\n''' + baseline_path.write_text(baseline, encoding='utf-8') + + changelog_path = Path('CHANGELOG.md') + changelog = changelog_path.read_text(encoding='utf-8') + note = '- Required OpenCode Review now releases its runner after one exact-head verdict admission read and relies on the authenticated exact-run dispatch wake instead of repository-authored polling, retry-count, or waiting deadlines.\n' + if note not in changelog: + changelog_path.write_text(note + changelog, encoding='utf-8') + PY + + - name: Verify GREEN focused and broader contracts + run: | + set -euo pipefail + PYTHONPATH=. python3 -m pytest -q \ + tests/test_opencode_required_verdict_runner_release.py \ + tests/test_opencode_poll_rate_budget.py \ + tests/test_opencode_poll_self_retirement.py \ + tests/test_opencode_required_verdict_regression.py \ + tests/test_opencode_workflow_shell_syntax.py + python3 - <<'PY' + import yaml + with open('.github/workflows/opencode-review.yml', encoding='utf-8') as handle: + yaml.safe_load(handle) + PY + git diff --check + PYTHONPATH=. python3 -m pytest tests -q + + - name: Publish only from unchanged writer head + env: + EXPECTED_HEAD: ${{ github.sha }} + run: | + set -euo pipefail + git fetch origin fix/opencode-poll-wall-clock-bound + live_head="$(git rev-parse origin/fix/opencode-poll-wall-clock-bound)" + if [ "$live_head" != "$EXPECTED_HEAD" ]; then + echo "::error::Writer branch advanced to $live_head; refusing stale publication." + exit 1 + fi + git config user.name "ContextualWisdomLab automation" + git config user.email "automation@users.noreply.github.com" + git add .github/workflows/opencode-review.yml \ + tests/test_opencode_required_verdict_runner_release.py \ + tests/test_opencode_poll_rate_budget.py \ + tests/test_opencode_poll_self_retirement.py \ + tests/test_opencode_required_verdict_regression.py \ + docs/doctoring/opencode-stale-poll-self-retirement.md \ + docs/product-technical-gap-baseline.md CHANGELOG.md + git diff --cached --check + git commit -m "fix(opencode): release required runner after one verdict read" + git push origin HEAD:fix/opencode-poll-wall-clock-bound From 6934b0c14524984af47abc82a32b4ce9bf4756cf Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 18:29:45 +0900 Subject: [PATCH 04/59] ci(temp): repair PR1706 repair-workflow parse failure --- .../_temp_pr1706_one_shot_runner_release.yml | 322 ++---------------- 1 file changed, 36 insertions(+), 286 deletions(-) diff --git a/.github/workflows/_temp_pr1706_one_shot_runner_release.yml b/.github/workflows/_temp_pr1706_one_shot_runner_release.yml index ef83775f22..18d2295126 100644 --- a/.github/workflows/_temp_pr1706_one_shot_runner_release.yml +++ b/.github/workflows/_temp_pr1706_one_shot_runner_release.yml @@ -43,307 +43,57 @@ jobs: exit 1 fi - - name: Apply smallest causal owner repair + - name: Apply deterministic owner repair + env: + STEP_B64: ICAgICAgLSBuYW1lOiBGYWlsIGNsb3NlZCB3aXRob3V0IGEgY3VycmVudC1oZWFkIE9wZW5Db2RlIHZlcmRpY3QKICAgICAgICBlbnY6CiAgICAgICAgICBHSF9UT0tFTjogJHt7IGdpdGh1Yi50b2tlbiB9fQogICAgICAgICAgVEFSR0VUX1JFUE9TSVRPUlk6ICR7eyBnaXRodWIuZXZlbnQucHVsbF9yZXF1ZXN0LmJhc2UucmVwby5mdWxsX25hbWUgfHwgZ2l0aHViLnJlcG9zaXRvcnkgfX0KICAgICAgICAgIFBSX05VTUJFUjogJHt7IGdpdGh1Yi5ldmVudC5wdWxsX3JlcXVlc3QubnVtYmVyIH19CiAgICAgICAgICBIRUFEX1NIQTogJHt7IGdpdGh1Yi5ldmVudC5wdWxsX3JlcXVlc3QuaGVhZC5zaGEgfX0KICAgICAgICAgIFBSX0FDVElPTjogJHt7IGdpdGh1Yi5ldmVudC5hY3Rpb24gfX0KICAgICAgICAgIFBSX0RSQUZUOiAke3sgZ2l0aHViLmV2ZW50LnB1bGxfcmVxdWVzdC5kcmFmdCB9fQogICAgICAgIHJ1bjogfAogICAgICAgICAgc2V0IC1ldW8gcGlwZWZhaWwKICAgICAgICAgIGlmIFsgIiRQUl9BQ1RJT04iID0gImNsb3NlZCIgXTsgdGhlbgogICAgICAgICAgICBlY2hvICJQUiBjbG9zZWQ7IGEgY3VycmVudC1oZWFkIE9wZW5Db2RlIHZlcmRpY3QgaXMgbm90IHJlcXVpcmVkLiIKICAgICAgICAgICAgZXhpdCAwCiAgICAgICAgICBmaQogICAgICAgICAgaWYgWyAteiAiJHtQUl9OVU1CRVI6LX0iIF0gfHwgWyAteiAiJHtIRUFEX1NIQTotfSIgXTsgdGhlbgogICAgICAgICAgICBlY2hvICI6OmVycm9yOjpNaXNzaW5nIFBSIG51bWJlciBvciBoZWFkIFNIQTsgY2Fubm90IHZlcmlmeSBhIGN1cnJlbnQtaGVhZCBPcGVuQ29kZSB2ZXJkaWN0LiIKICAgICAgICAgICAgZXhpdCAxCiAgICAgICAgICBmaQogICAgICAgICAgbGl2ZV9wcj0iJChnaCBhcGkgInJlcG9zLyR7VEFSR0VUX1JFUE9TSVRPUll9L3B1bGxzLyR7UFJfTlVNQkVSfSIpIgogICAgICAgICAgbGl2ZV9oZWFkPSIkKHByaW50ZiAnJXMnICIkbGl2ZV9wciIgfCBqcSAtciAnLmhlYWQuc2hhIC8vIGVtcHR5JykiCiAgICAgICAgICBsaXZlX2RyYWZ0PSIkKHByaW50ZiAnJXMnICIkbGl2ZV9wciIgfCBqcSAtciAnaWYgKC5kcmFmdCB8IHR5cGUpID09ICJib29sZWFuIiB0aGVuICguZHJhZnQgfCB0b3N0cmluZykgZWxzZSBlbXB0eSBlbmQnKSIKICAgICAgICAgIGxpdmVfc3RhdGU9IiQocHJpbnRmICclcycgIiRsaXZlX3ByIiB8IGpxIC1yICdpZiAoLnN0YXRlIHwgdHlwZSkgPT0gInN0cmluZyIgdGhlbiAuc3RhdGUgZWxzZSBlbXB0eSBlbmQnKSIKICAgICAgICAgIGlmIFsgLXogIiRsaXZlX2hlYWQiIF0gfHwgWyAteiAiJGxpdmVfZHJhZnQiIF0gfHwgWyAteiAiJGxpdmVfc3RhdGUiIF07IHRoZW4KICAgICAgICAgICAgZWNobyAiOjplcnJvcjo6Q291bGQgbm90IHZhbGlkYXRlIGxpdmUgcHVsbCByZXF1ZXN0IHN0YXRlIGJlZm9yZSB2ZXJkaWN0IGFkbWlzc2lvbi4iCiAgICAgICAgICAgIGV4aXQgMQogICAgICAgICAgZmkKICAgICAgICAgIGlmIFsgIiRsaXZlX3N0YXRlIiAhPSAib3BlbiIgXSAmJiBbICIkbGl2ZV9zdGF0ZSIgIT0gImNsb3NlZCIgXTsgdGhlbgogICAgICAgICAgICBlY2hvICI6OmVycm9yOjpDb3VsZCBub3QgdmFsaWRhdGUgbGl2ZSBwdWxsIHJlcXVlc3Qgc3RhdGUgYmVmb3JlIHZlcmRpY3QgYWRtaXNzaW9uLiIKICAgICAgICAgICAgZXhpdCAxCiAgICAgICAgICBmaQogICAgICAgICAgaWYgWyAiJGxpdmVfc3RhdGUiID0gImNsb3NlZCIgXTsgdGhlbgogICAgICAgICAgICBlY2hvICJQUiBpcyBjbG9zZWQgb24gdGhlIGxpdmUgZXhhY3QgaGVhZDsgYSBjdXJyZW50LWhlYWQgT3BlbkNvZGUgdmVyZGljdCBpcyBub3QgcmVxdWlyZWQuIgogICAgICAgICAgICBleGl0IDAKICAgICAgICAgIGZpCiAgICAgICAgICBpZiBbICIkbGl2ZV9kcmFmdCIgPSAidHJ1ZSIgXTsgdGhlbgogICAgICAgICAgICBlY2hvICJQUiBpcyBzdGlsbCBhIGRyYWZ0IG9uIHRoZSBsaXZlIGV4YWN0IGhlYWQ7IGEgY3VycmVudC1oZWFkIE9wZW5Db2RlIHZlcmRpY3QgaXMgbm90IHJlcXVpcmVkIHVudGlsIGl0IGlzIG1hcmtlZCByZWFkeSBmb3IgcmV2aWV3LiIKICAgICAgICAgICAgZXhpdCAwCiAgICAgICAgICBmaQogICAgICAgICAgaWYgWyAiJHtsaXZlX2hlYWQsLH0iICE9ICIke0hFQURfU0hBLCx9IiBdOyB0aGVuCiAgICAgICAgICAgIGVjaG8gIlB1bGwgcmVxdWVzdCBoZWFkIG1vdmVkIG9uIHRoZSBsaXZlIG9wZW4sIHJlYWR5LWZvci1yZXZpZXcgUFI7IGEgZnJlc2ggcmVxdWlyZWQtcmV2aWV3IHJ1biB3aWxsIGJpbmQgdGhlIGN1cnJlbnQgaGVhZC4iCiAgICAgICAgICAgIGV4aXQgMAogICAgICAgICAgZmkKICAgICAgICAgIGlmIFsgIiRQUl9EUkFGVCIgPSAidHJ1ZSIgXTsgdGhlbgogICAgICAgICAgICBlY2hvICJFdmVudCBkcmFmdCBzbmFwc2hvdCBpcyBzdGFsZTsgY29udGludWluZyBvbmUtc2hvdCB2ZXJkaWN0IGFkbWlzc2lvbiBmb3IgdGhlIGxpdmUgcmVhZHkgUFIuIgogICAgICAgICAgZmkKICAgICAgICAgIGlmICEgcmV2aWV3cz0iJChnaCBhcGkgLS1wYWdpbmF0ZSAicmVwb3MvJHtUQVJHRVRfUkVQT1NJVE9SWX0vcHVsbHMvJHtQUl9OVU1CRVJ9L3Jldmlld3M/cGVyX3BhZ2U9MTAwIikiOyB0aGVuCiAgICAgICAgICAgIGVjaG8gIjo6ZXJyb3I6OlJldmlld3MgQVBJIHJlYWQgZmFpbGVkIGR1cmluZyBvbmUtc2hvdCBjdXJyZW50LWhlYWQgdmVyZGljdCBhZG1pc3Npb247IGZhaWxpbmcgY2xvc2VkIGFuZCByZWxlYXNpbmcgdGhlIHJ1bm5lci4iCiAgICAgICAgICAgIGV4aXQgMQogICAgICAgICAgZmkKICAgICAgICAgIHZlcmRpY3Q9IiQocHJpbnRmICclc1xuJyAiJHJldmlld3MiIHwganEgLXIgLXMgLS1hcmcgc2hhICIkSEVBRF9TSEEiICcKICAgICAgICAgICAgKGFkZCAvLyBbXSkKICAgICAgICAgICAgfCBbCiAgICAgICAgICAgICAgICAuW10KICAgICAgICAgICAgICAgIHwgc2VsZWN0KAogICAgICAgICAgICAgICAgICAgICgudXNlci5sb2dpbiAvLyAiIiB8IGFzY2lpX2Rvd25jYXNlKSBhcyAkdXNlcgogICAgICAgICAgICAgICAgICAgIHwgJHVzZXIgPT0gIm9wZW5jb2RlLWFnZW50IiBvciAkdXNlciA9PSAib3BlbmNvZGUtYWdlbnRbYm90XSIKICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgfCBzZWxlY3QoKC5jb21taXRfaWQgLy8gIiIgfCBhc2NpaV9kb3duY2FzZSkgPT0gKCRzaGEgfCBhc2NpaV9kb3duY2FzZSkpCiAgICAgICAgICAgICAgICB8IHNlbGVjdCguc3RhdGUgPT0gIkFQUFJPVkVEIiBvciAuc3RhdGUgPT0gIkNIQU5HRVNfUkVRVUVTVEVEIikKICAgICAgICAgICAgICBdCiAgICAgICAgICAgIHwgKGxhc3QgLy8ge30pIGFzICRyZXZpZXcKICAgICAgICAgICAgfCAoJHJldmlldy5ib2R5IC8vICIiIHwgYXNjaWlfZG93bmNhc2UpIGFzICRib2R5CiAgICAgICAgICAgIHwgaWYgJHJldmlldy5zdGF0ZSA9PSAiQ0hBTkdFU19SRVFVRVNURUQiIHRoZW4KICAgICAgICAgICAgICAgICJDSEFOR0VTX1JFUVVFU1RFRCIKICAgICAgICAgICAgICBlbGlmICRyZXZpZXcuc3RhdGUgPT0gIkFQUFJPVkVEIgogICAgICAgICAgICAgICAgYW5kICgkYm9keSB8IGNvbnRhaW5zKCJkZXRlcm1pbmlzdGljIGN1cnJlbnQtaGVhZCBldmlkZW5jZSIpIHwgbm90KQogICAgICAgICAgICAgICAgYW5kICgkYm9keSB8IGNvbnRhaW5zKCJkZXRlcm1pbmlzdGljIGZhbGxiYWNrIGFwcHJvdmFsIikgfCBub3QpCiAgICAgICAgICAgICAgICBhbmQgKCRib2R5IHwgY29udGFpbnMoIm1vZGVsLXVuYXZhaWxhYmxlIGV2aWRlbmNlIGZhbGxiYWNrIikgfCBub3QpCiAgICAgICAgICAgICAgICBhbmQgKCRib2R5IHwgY29udGFpbnMoImRpZCBub3QgZW1pdCBhIHVzYWJsZSBjdXJyZW50LWhlYWQgY29udHJvbCBibG9jayIpIHwgbm90KQogICAgICAgICAgICAgICAgYW5kICgkYm9keSB8IGNvbnRhaW5zKCJzY29wZTogYHVuc3VwcG9ydGVkYCIpIHwgbm90KQogICAgICAgICAgICAgICAgYW5kICgkYm9keSB8IGNvbnRhaW5zKCJtb2RlbC1wb29sIG91dGNvbWU6IGB1bmtub3duYCIpIHwgbm90KQogICAgICAgICAgICAgIHRoZW4KICAgICAgICAgICAgICAgICJBUFBST1ZFRCIKICAgICAgICAgICAgICBlbHNlCiAgICAgICAgICAgICAgICBlbXB0eQogICAgICAgICAgICAgIGVuZAogICAgICAgICAgJykiCiAgICAgICAgICBpZiBbIC16ICIkdmVyZGljdCIgXTsgdGhlbgogICAgICAgICAgICBlY2hvICI6OmVycm9yOjpObyBBUFBST1ZFRCBvciBDSEFOR0VTX1JFUVVFU1RFRCBmcm9tIG9wZW5jb2RlLWFnZW50IG9uIHRoZSBjdXJyZW50IGhlYWQuIFRoaXMgcmVxdWlyZWQgY2hlY2sgaXMgbm90IGEgcmV2aWV3IGFuZCBtdXN0IG5vdCBzdWNjZWVkIHVudGlsIHRoZSBhdXRoZW50aWNhdGVkIGRpc3BhdGNoIHBvc3RzIGEgY3VycmVudC1oZWFkIHZlcmRpY3QuIFRoZSBkaXNwYXRjaCBwYXRoIHdha2VzIHRoaXMgZXhhY3QgZmFpbGVkIHJ1biB3aGVuIHRoZSB2ZXJkaWN0IGFycml2ZXMuIgogICAgICAgICAgICBleGl0IDEKICAgICAgICAgIGZpCiAgICAgICAgICBlY2hvICJDdXJyZW50LWhlYWQgT3BlbkNvZGUgdmVyZGljdDogJHt2ZXJkaWN0fS4iCg== + RATE_B64: IiIiUmVxdWVzdC1idWRnZXQgcmVncmVzc2lvbiBmb3Igb25lLXNob3QgUmVxdWlyZWQgT3BlbkNvZGUgdmVyZGljdCBhZG1pc3Npb24uIiIiCgpmcm9tIHBhdGhsaWIgaW1wb3J0IFBhdGgKCgpXT1JLRkxPVyA9IFBhdGgoIi5naXRodWIvd29ya2Zsb3dzL29wZW5jb2RlLXJldmlldy55bWwiKQoKCmRlZiBfYWRtaXNzaW9uX3N0ZXAoKSAtPiBzdHI6CiAgICAiIiJSZXR1cm4gdGhlIG9uZS1zaG90IGN1cnJlbnQtaGVhZCB2ZXJkaWN0IGFkbWlzc2lvbiBzdGVwLiIiIgogICAgd29ya2Zsb3cgPSBXT1JLRkxPVy5yZWFkX3RleHQoZW5jb2Rpbmc9InV0Zi04IikKICAgIHJldHVybiB3b3JrZmxvdy5zcGxpdCgKICAgICAgICAiICAgICAgLSBuYW1lOiBGYWlsIGNsb3NlZCB3aXRob3V0IGEgY3VycmVudC1oZWFkIE9wZW5Db2RlIHZlcmRpY3RcbiIsIDEKICAgIClbMV0uc3BsaXQoIlxuICBjYW5jZWwtc3VwZXJzZWRlZC1vcGVuY29kZS1yZXZpZXctcnVuczpcbiIsIDEpWzBdCgoKZGVmIHRlc3RfYWRtaXNzaW9uX3VzZXNfb25lX3Jldmlld3NfcmVhZF93aXRob3V0X3J1bm5lcl9wb2xsaW5nKCkgLT4gTm9uZToKICAgICIiIk1pc3NpbmcgbW9kZWwgZXZpZGVuY2UgcmVsZWFzZXMgdGhlIHJ1bm5lciBpbnN0ZWFkIG9mIGFsbG9jYXRpbmcgUkVTVCBwb2xsaW5nLiIiIgogICAgc3RlcCA9IF9hZG1pc3Npb25fc3RlcCgpCiAgICBhc3NlcnQgc3RlcC5jb3VudCgnZ2ggYXBpICJyZXBvcy8ke1RBUkdFVF9SRVBPU0lUT1JZfS9wdWxscy8ke1BSX05VTUJFUn0iJykgPT0gMQogICAgYXNzZXJ0IHN0ZXAuY291bnQoJ2doIGFwaSAtLXBhZ2luYXRlICJyZXBvcy8ke1RBUkdFVF9SRVBPU0lUT1JZfS9wdWxscy8ke1BSX05VTUJFUn0vcmV2aWV3cz9wZXJfcGFnZT0xMDAiJykgPT0gMQogICAgYXNzZXJ0ICJ3aGlsZSA6OyBkbyIgbm90IGluIHN0ZXAKICAgIGFzc2VydCAicG9sbF9pbnRlcnZhbF9zZWNvbmRzIiBub3QgaW4gc3RlcAogICAgYXNzZXJ0ICJwb2xsX2RlYWRsaW5lX2Vwb2NoIiBub3QgaW4gc3RlcAogICAgYXNzZXJ0ICJzbGVlcCAiIG5vdCBpbiBzdGVwCgoKZGVmIHRlc3RfcmV2aWV3X3JlYWRfa2VlcHNfbWF4aW11bV9yZXN0X3BhZ2Vfc2l6ZSgpIC0+IE5vbmU6CiAgICAiIiJUaGUgc2luZ2xlIFJldmlld3MgcmVhZCBtaW5pbWl6ZXMgcGFnZXMgd2l0aG91dCBkcm9wcGluZyBoaXN0b3J5IGV2aWRlbmNlLiIiIgogICAgc3RlcCA9IF9hZG1pc3Npb25fc3RlcCgpCiAgICBhc3NlcnQgIi9yZXZpZXdzP3Blcl9wYWdlPTEwMCIgaW4gc3RlcAogICAgYXNzZXJ0ICJnaCBhcGkgLS1wYWdpbmF0ZSIgaW4gc3RlcAo= + SELF_B64: IiIiUmVncmVzc2lvbiBjb250cmFjdCBmb3Igb25lLXNob3QgUmVxdWlyZWQgT3BlbkNvZGUgdmVyZGljdCBhZG1pc3Npb24uIiIiCgpmcm9tIF9fZnV0dXJlX18gaW1wb3J0IGFubm90YXRpb25zCgppbXBvcnQganNvbgppbXBvcnQgb3MKaW1wb3J0IHNodXRpbAppbXBvcnQgc3VicHJvY2VzcwppbXBvcnQgdGV4dHdyYXAKZnJvbSBwYXRobGliIGltcG9ydCBQYXRoCgppbXBvcnQgcHl0ZXN0CgoKV09SS0ZMT1cgPSBQYXRoKCIuZ2l0aHViL3dvcmtmbG93cy9vcGVuY29kZS1yZXZpZXcueW1sIikKSEVBRF9TSEEgPSAiYSIgKiA0MAoKCmRlZiBfc2NyaXB0KCkgLT4gc3RyOgogICAgIiIiRXh0cmFjdCB0aGUgcHJvZHVjdGlvbiBvbmUtc2hvdCB2ZXJkaWN0IGFkbWlzc2lvbiBzY3JpcHQuIiIiCiAgICB3b3JrZmxvdyA9IFdPUktGTE9XLnJlYWRfdGV4dChlbmNvZGluZz0idXRmLTgiKQogICAgc3RlcCA9IHdvcmtmbG93LnNwbGl0KAogICAgICAgICIgICAgICAtIG5hbWU6IEZhaWwgY2xvc2VkIHdpdGhvdXQgYSBjdXJyZW50LWhlYWQgT3BlbkNvZGUgdmVyZGljdFxuIiwgMQogICAgKVsxXS5zcGxpdCgiXG4gIGNhbmNlbC1zdXBlcnNlZGVkLW9wZW5jb2RlLXJldmlldy1ydW5zOlxuIiwgMSlbMF0KICAgIHJldHVybiB0ZXh0d3JhcC5kZWRlbnQoc3RlcC5zcGxpdCgiICAgICAgICBydW46IHxcbiIsIDEpWzFdKQoKCmRlZiBfcnVuKAogICAgdG1wX3BhdGg6IFBhdGgsCiAgICAqLAogICAgbGl2ZV9wcjogZGljdFtzdHIsIG9iamVjdF0sCiAgICByZXZpZXdzOiBsaXN0W2RpY3Rbc3RyLCBvYmplY3RdXSB8IE5vbmUgPSBOb25lLAogICAgZmFpbF9yZXZpZXdzOiBib29sID0gRmFsc2UsCikgLT4gdHVwbGVbc3VicHJvY2Vzcy5Db21wbGV0ZWRQcm9jZXNzW3N0cl0sIGxpc3Rbc3RyXV06CiAgICAiIiJFeGVjdXRlIHRoZSBwcm9kdWN0aW9uIHN0ZXAgYWdhaW5zdCBkZXRlcm1pbmlzdGljIGxpdmUgUFIvcmV2aWV3IGV2aWRlbmNlLiIiIgogICAgYmFzaCA9IHNodXRpbC53aGljaCgiYmFzaCIpCiAgICBqcSA9IHNodXRpbC53aGljaCgianEiKQogICAgaWYgYmFzaCBpcyBOb25lIG9yIGpxIGlzIE5vbmU6CiAgICAgICAgcHl0ZXN0LnNraXAoImJhc2ggYW5kIGpxIGFyZSByZXF1aXJlZCIpCiAgICBmYWtlX2JpbiA9IHRtcF9wYXRoIC8gImJpbiIKICAgIGZha2VfYmluLm1rZGlyKCkKICAgIGxvZyA9IHRtcF9wYXRoIC8gImNhbGxzIgogICAgZ2ggPSBmYWtlX2JpbiAvICJnaCIKICAgIGdoLndyaXRlX3RleHQoCiAgICAgICAgIiMhL3Vzci9iaW4vZW52IGJhc2hcbiIKICAgICAgICAic2V0IC1ldW8gcGlwZWZhaWxcbiIKICAgICAgICAicHJpbnRmICclc1xcbicgXCIkKlwiID4+XCIkQ0FMTF9MT0dcIlxuIgogICAgICAgICJpZiBbWyBcIiQqXCIgPT0gXCJhcGkgcmVwb3MvQ29udGV4dHVhbFdpc2RvbUxhYi9leGFtcGxlL3B1bGxzLzQyXCIgXV07IHRoZW5cbiIKICAgICAgICAiICBwcmludGYgJyVzXFxuJyBcIiRMSVZFX1BSXCJcbiIKICAgICAgICAiICBleGl0IDBcbiIKICAgICAgICAiZmlcbiIKICAgICAgICAiaWYgW1sgXCIkKlwiID09IFwiYXBpIC0tcGFnaW5hdGUgcmVwb3MvQ29udGV4dHVhbFdpc2RvbUxhYi9leGFtcGxlL3B1bGxzLzQyL3Jldmlld3M/cGVyX3BhZ2U9MTAwXCIgXV07IHRoZW5cbiIKICAgICAgICAiICBpZiBbWyBcIiRGQUlMX1JFVklFV1NcIiA9PSBcInRydWVcIiBdXTsgdGhlbiBleGl0IDc7IGZpXG4iCiAgICAgICAgIiAgcHJpbnRmICclc1xcbicgXCIkUkVWSUVXU1wiXG4iCiAgICAgICAgIiAgZXhpdCAwXG4iCiAgICAgICAgImZpXG4iCiAgICAgICAgImV4aXQgOTdcbiIsCiAgICAgICAgZW5jb2Rpbmc9InV0Zi04IiwKICAgICkKICAgIGdoLmNobW9kKDBvNzU1KQogICAgc2xlZXAgPSBmYWtlX2JpbiAvICJzbGVlcCIKICAgIHNsZWVwLndyaXRlX3RleHQoIiMhL3Vzci9iaW4vZW52IGJhc2hcbmV4aXQgOTFcbiIsIGVuY29kaW5nPSJ1dGYtOCIpCiAgICBzbGVlcC5jaG1vZCgwbzc1NSkKICAgIHJlc3VsdCA9IHN1YnByb2Nlc3MucnVuKAogICAgICAgIFtiYXNoLCAiLWMiLCBfc2NyaXB0KCldLAogICAgICAgIGVudj17CiAgICAgICAgICAgICoqb3MuZW52aXJvbiwKICAgICAgICAgICAgIlBBVEgiOiBmIntmYWtlX2Jpbn17b3MucGF0aHNlcH17b3MuZW52aXJvbi5nZXQoJ1BBVEgnLCAnJyl9IiwKICAgICAgICAgICAgIkNBTExfTE9HIjogc3RyKGxvZyksCiAgICAgICAgICAgICJMSVZFX1BSIjoganNvbi5kdW1wcyhsaXZlX3ByKSwKICAgICAgICAgICAgIlJFVklFV1MiOiBqc29uLmR1bXBzKHJldmlld3Mgb3IgW10pLAogICAgICAgICAgICAiRkFJTF9SRVZJRVdTIjogc3RyKGZhaWxfcmV2aWV3cykubG93ZXIoKSwKICAgICAgICAgICAgIkdIX1RPS0VOIjogInRlc3QtdG9rZW4iLAogICAgICAgICAgICAiVEFSR0VUX1JFUE9TSVRPUlkiOiAiQ29udGV4dHVhbFdpc2RvbUxhYi9leGFtcGxlIiwKICAgICAgICAgICAgIlBSX05VTUJFUiI6ICI0MiIsCiAgICAgICAgICAgICJIRUFEX1NIQSI6IEhFQURfU0hBLAogICAgICAgICAgICAiUFJfQUNUSU9OIjogInN5bmNocm9uaXplIiwKICAgICAgICAgICAgIlBSX0RSQUZUIjogImZhbHNlIiwKICAgICAgICAgIH0sCiAgICAgICAgdGV4dD1UcnVlLAogICAgICAgIGNhcHR1cmVfb3V0cHV0PVRydWUsCiAgICAgICAgY2hlY2s9RmFsc2UsCiAgICApCiAgICByZXR1cm4gcmVzdWx0LCBsb2cucmVhZF90ZXh0KGVuY29kaW5nPSJ1dGYtOCIpLnNwbGl0bGluZXMoKQoKCmRlZiB0ZXN0X3N0YWxlX2hlYWRfcmV0aXJlc19iZWZvcmVfcmV2aWV3c19yZWFkKHRtcF9wYXRoOiBQYXRoKSAtPiBOb25lOgogICAgIiIiQSBtb3ZlZCBoZWFkIGNhbm5vdCBjb25zdW1lIG9yIGFkbWl0IHJldmlldyBldmlkZW5jZS4iIiIKICAgIHJlc3VsdCwgY2FsbHMgPSBfcnVuKAogICAgICAgIHRtcF9wYXRoLAogICAgICAgIGxpdmVfcHI9eyJoZWFkIjogeyJzaGEiOiAiYiIgKiA0MH0sICJkcmFmdCI6IEZhbHNlLCAic3RhdGUiOiAib3BlbiJ9LAogICAgKQogICAgYXNzZXJ0IHJlc3VsdC5yZXR1cm5jb2RlID09IDAKICAgIGFzc2VydCBjYWxscyA9PSBbImFwaSByZXBvcy9Db250ZXh0dWFsV2lzZG9tTGFiL2V4YW1wbGUvcHVsbHMvNDIiXQoKCmRlZiB0ZXN0X2Nsb3NlZF9jdXJyZW50X2hlYWRfcmV0aXJlc19iZWZvcmVfcmV2aWV3c19yZWFkKHRtcF9wYXRoOiBQYXRoKSAtPiBOb25lOgogICAgIiIiQSBjbG9zZWQgY3VycmVudCBoZWFkIHJlbGVhc2VzIHRoZSBydW5uZXIgd2l0aG91dCBhIHJldmlldyByZWFkLiIiIgogICAgcmVzdWx0LCBjYWxscyA9IF9ydW4oCiAgICAgICAgdG1wX3BhdGgsCiAgICAgICAgbGl2ZV9wcj17ImhlYWQiOiB7InNoYSI6IEhFQURfU0hBfSwgImRyYWZ0IjogRmFsc2UsICJzdGF0ZSI6ICJjbG9zZWQifSwKICAgICkKICAgIGFzc2VydCByZXN1bHQucmV0dXJuY29kZSA9PSAwCiAgICBhc3NlcnQgY2FsbHMgPT0gWyJhcGkgcmVwb3MvQ29udGV4dHVhbFdpc2RvbUxhYi9leGFtcGxlL3B1bGxzLzQyIl0KCgpkZWYgdGVzdF9kcmFmdF9jdXJyZW50X2hlYWRfcmV0aXJlc19iZWZvcmVfcmV2aWV3c19yZWFkKHRtcF9wYXRoOiBQYXRoKSAtPiBOb25lOgogICAgIiIiQSBkcmFmdCBjdXJyZW50IGhlYWQgcmVsZWFzZXMgdGhlIHJ1bm5lciB3aXRob3V0IGFuIGltcG9zc2libGUgcmV2aWV3IHdhaXQuIiIiCiAgICByZXN1bHQsIGNhbGxzID0gX3J1bigKICAgICAgICB0bXBfcGF0aCwKICAgICAgICBsaXZlX3ByPXsiaGVhZCI6IHsic2hhIjogSEVBRF9TSEF9LCAiZHJhZnQiOiBUcnVlLCAic3RhdGUiOiAib3BlbiJ9LAogICAgKQogICAgYXNzZXJ0IHJlc3VsdC5yZXR1cm5jb2RlID09IDAKICAgIGFzc2VydCBjYWxscyA9PSBbImFwaSByZXBvcy9Db250ZXh0dWFsV2lzZG9tTGFiL2V4YW1wbGUvcHVsbHMvNDIiXQoKCmRlZiB0ZXN0X2N1cnJlbnRfaGVhZF9yZWFkc19yZXZpZXdzX29uY2VfYW5kX2FjY2VwdHNfZXhhY3RfdmVyZGljdCh0bXBfcGF0aDogUGF0aCkgLT4gTm9uZToKICAgICIiIkEgbGl2ZSBjdXJyZW50IGhlYWQgYWRtaXRzIG9uZSBleGFjdC1oZWFkIGZvcm1hbCBPcGVuQ29kZSB2ZXJkaWN0LiIiIgogICAgcmV2aWV3cyA9IFsKICAgICAgICB7CiAgICAgICAgICAgICJ1c2VyIjogeyJsb2dpbiI6ICJvcGVuY29kZS1hZ2VudFtib3RdIn0sCiAgICAgICAgICAgICJjb21taXRfaWQiOiBIRUFEX1NIQSwKICAgICAgICAgICAgInN0YXRlIjogIkFQUFJPVkVEIiwKICAgICAgICAgICAgImJvZHkiOiAic291cmNlLWJhY2tlZCByZXZpZXciLAogICAgICAgIH0KICAgIF0KICAgIHJlc3VsdCwgY2FsbHMgPSBfcnVuKAogICAgICAgIHRtcF9wYXRoLAogICAgICAgIGxpdmVfcHI9eyJoZWFkIjogeyJzaGEiOiBIRUFEX1NIQX0sICJkcmFmdCI6IEZhbHNlLCAic3RhdGUiOiAib3BlbiJ9LAogICAgICAgIHJldmlld3M9cmV2aWV3cywKICAgICkKICAgIGFzc2VydCByZXN1bHQucmV0dXJuY29kZSA9PSAwLCByZXN1bHQuc3RkZXJyCiAgICBhc3NlcnQgY2FsbHMgPT0gWwogICAgICAgICJhcGkgcmVwb3MvQ29udGV4dHVhbFdpc2RvbUxhYi9leGFtcGxlL3B1bGxzLzQyIiwKICAgICAgICAiYXBpIC0tcGFnaW5hdGUgcmVwb3MvQ29udGV4dHVhbFdpc2RvbUxhYi9leGFtcGxlL3B1bGxzLzQyL3Jldmlld3M/cGVyX3BhZ2U9MTAwIiwKICAgIF0KCgpkZWYgdGVzdF9yZXZpZXdzX3RyYW5zcG9ydF9mYWlsdXJlX2ZhaWxzX29uY2VfYW5kX3JlbGVhc2VzX3J1bm5lcih0bXBfcGF0aDogUGF0aCkgLT4gTm9uZToKICAgICIiIlRyYW5zcG9ydCBmYWlsdXJlIGlzIGZhaWwtY2xvc2VkIHdpdGhvdXQgYSByZXBvc2l0b3J5LWF1dGhvcmVkIHJldHJ5IGNvdW50LiIiIgogICAgcmVzdWx0LCBjYWxscyA9IF9ydW4oCiAgICAgICAgdG1wX3BhdGgsCiAgICAgICAgbGl2ZV9wcj17ImhlYWQiOiB7InNoYSI6IEhFQURfU0hBfSwgImRyYWZ0IjogRmFsc2UsICJzdGF0ZSI6ICJvcGVuIn0sCiAgICAgICAgZmFpbF9yZXZpZXdzPVRydWUsCiAgICApCiAgICBhc3NlcnQgcmVzdWx0LnJldHVybmNvZGUgPT0gMQogICAgYXNzZXJ0ICJSZXZpZXdzIEFQSSByZWFkIGZhaWxlZCBkdXJpbmcgb25lLXNob3QiIGluIHJlc3VsdC5zdGRvdXQKICAgIGFzc2VydCBsZW4oY2FsbHMpID09IDIKICAgIGFzc2VydCAic2xlZXAgIiBub3QgaW4gX3NjcmlwdCgpCg== run: | set -euo pipefail python3 <<'PY' + import base64 + import os from pathlib import Path workflow_path = Path('.github/workflows/opencode-review.yml') workflow = workflow_path.read_text(encoding='utf-8') - start_marker = ' - name: Fail closed without a current-head OpenCode verdict\n' - end_marker = '\n cancel-superseded-opencode-review-runs:\n' - if workflow.count(start_marker) != 1 or workflow.count(end_marker) != 1: + start = ' - name: Fail closed without a current-head OpenCode verdict\n' + end = '\n cancel-superseded-opencode-review-runs:\n' + if workflow.count(start) != 1 or workflow.count(end) != 1: raise SystemExit('OpenCode required-verdict step boundaries drifted') - before, rest = workflow.split(start_marker, 1) - _old_step, after = rest.split(end_marker, 1) - replacement = ''' - name: Fail closed without a current-head OpenCode verdict - env: - GH_TOKEN: ${{ github.token }} - TARGET_REPOSITORY: ${{ github.event.pull_request.base.repo.full_name || github.repository }} - PR_NUMBER: ${{ github.event.pull_request.number }} - HEAD_SHA: ${{ github.event.pull_request.head.sha }} - PR_ACTION: ${{ github.event.action }} - PR_DRAFT: ${{ github.event.pull_request.draft }} - run: | - set -euo pipefail - if [ "$PR_ACTION" = "closed" ]; then - echo "PR closed; a current-head OpenCode verdict is not required." - exit 0 - fi - if [ -z "${PR_NUMBER:-}" ] || [ -z "${HEAD_SHA:-}" ]; then - echo "::error::Missing PR number or head SHA; cannot verify a current-head OpenCode verdict." - exit 1 - fi - live_pr="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")" - live_head="$(printf '%s' "$live_pr" | jq -r '.head.sha // empty')" - live_draft="$(printf '%s' "$live_pr" | jq -r 'if (.draft | type) == "boolean" then (.draft | tostring) else empty end')" - live_state="$(printf '%s' "$live_pr" | jq -r 'if (.state | type) == "string" then .state else empty end')" - if [ -z "$live_head" ] || [ -z "$live_draft" ] || [ -z "$live_state" ]; then - echo "::error::Could not validate live pull request state before verdict admission." - exit 1 - fi - if [ "$live_state" != "open" ] && [ "$live_state" != "closed" ]; then - echo "::error::Could not validate live pull request state before verdict admission." - exit 1 - fi - if [ "$live_state" = "closed" ]; then - echo "PR is closed on the live exact head; a current-head OpenCode verdict is not required." - exit 0 - fi - if [ "$live_draft" = "true" ]; then - echo "PR is still a draft on the live exact head; a current-head OpenCode verdict is not required until it is marked ready for review." - exit 0 - fi - if [ "${live_head,,}" != "${HEAD_SHA,,}" ]; then - echo "Pull request head moved on the live open, ready-for-review PR; a fresh required-review run will bind the current head." - exit 0 - fi - if [ "$PR_DRAFT" = "true" ]; then - echo "Event draft snapshot is stale; continuing one-shot verdict admission for the live ready PR." - fi - if ! reviews="$(gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"; then - echo "::error::Reviews API read failed during one-shot current-head verdict admission; failing closed and releasing the runner." - exit 1 - fi - verdict="$(printf '%s\n' "$reviews" | jq -r -s --arg sha "$HEAD_SHA" ' - (add // []) - | [ - .[] - | select( - (.user.login // "" | ascii_downcase) as $user - | $user == "opencode-agent" or $user == "opencode-agent[bot]" - ) - | select((.commit_id // "" | ascii_downcase) == ($sha | ascii_downcase)) - | select(.state == "APPROVED" or .state == "CHANGES_REQUESTED") - ] - | (last // {}) as $review - | ($review.body // "" | ascii_downcase) as $body - | if $review.state == "CHANGES_REQUESTED" then - "CHANGES_REQUESTED" - elif $review.state == "APPROVED" - and ($body | contains("deterministic current-head evidence") | not) - and ($body | contains("deterministic fallback approval") | not) - and ($body | contains("model-unavailable evidence fallback") | not) - and ($body | contains("did not emit a usable current-head control block") | not) - and ($body | contains("scope: `unsupported`") | not) - and ($body | contains("model-pool outcome: `unknown`") | not) - then - "APPROVED" - else - empty - end - ')" - if [ -z "$verdict" ]; then - echo "::error::No APPROVED or CHANGES_REQUESTED from opencode-agent on the current head. This required check is not a review and must not succeed until the authenticated dispatch posts a current-head verdict. The dispatch path wakes this exact failed run when the verdict arrives." - exit 1 - fi - echo "Current-head OpenCode verdict: ${verdict}." -''' - workflow = before + replacement + end_marker + after - workflow = workflow.replace( - '# `converted_to_draft` is included so a PR going draft mid-poll fires a\n' - ' # fresh run of this same workflow: the head-scoped concurrency group below\n' - ' # (`cancel-in-progress: true`) cancels any in-flight non-draft\n' - ' # "Fail closed without a current-head OpenCode verdict" poll for that\n' - ' # exact same head. Every non-closed admission path revalidates the live\n' - ' # PR/head/state before dispatching, exempting, or polling so out-of-order\n' - ' # draft/ready/closed events cannot publish stale evidence or wait on an\n' - ' # impossible verdict.\n', - '# `converted_to_draft` is included so a same-head state transition can\n' - ' # supersede any older admission run. Every non-closed admission path\n' - ' # revalidates live PR/head/state before dispatching, exempting, or reading\n' - ' # reviews, so stale events cannot publish or admit stale evidence.\n', - ) - workflow = workflow.replace( - '# `converted_to_draft` still cancels an active same-head verdict poll.\n', - '# `converted_to_draft` still supersedes an active same-head admission run.\n', + before, rest = workflow.split(start, 1) + _old, after = rest.split(end, 1) + replacement = base64.b64decode(os.environ['STEP_B64']).decode() + workflow_path.write_text(before + replacement + end + after, encoding='utf-8') + + Path('tests/test_opencode_poll_rate_budget.py').write_text( + base64.b64decode(os.environ['RATE_B64']).decode(), encoding='utf-8' ) - workflow = workflow.replace( - ' # old-head events, while the poll above now revalidates live PR identity on\n' - ' # every wait iteration so an already-running obsolete poll can self-retire\n' - ' # without consuming a second runner. This sibling job remains a defense in\n' - ' # depth for queued/requested old-head runs and for legacy runs created from\n' - ' # older workflow revisions that lack the in-loop self-retirement check.\n', - ' # old-head events. The admission step above is one-shot and never holds a\n' - ' # runner waiting for model work; this sibling remains defense in depth for\n' - ' # queued/requested old-head runs and legacy workflow revisions.\n', + Path('tests/test_opencode_poll_self_retirement.py').write_text( + base64.b64decode(os.environ['SELF_B64']).decode(), encoding='utf-8' ) - workflow_path.write_text(workflow, encoding='utf-8') - - Path('tests/test_opencode_poll_rate_budget.py').write_text('''"""Request-budget regression for one-shot Required OpenCode verdict admission.""" - -from pathlib import Path - - -WORKFLOW = Path(".github/workflows/opencode-review.yml") - - -def _admission_step() -> str: - """Return the one-shot current-head verdict admission step.""" - workflow = WORKFLOW.read_text(encoding="utf-8") - return workflow.split( - " - name: Fail closed without a current-head OpenCode verdict\\n", 1 - )[1].split("\\n cancel-superseded-opencode-review-runs:\\n", 1)[0] - - -def test_admission_uses_one_reviews_read_without_runner_polling() -> None: - """Missing model evidence releases the runner instead of allocating REST polling.""" - step = _admission_step() - assert step.count('gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}"') == 1 - assert step.count('gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100"') == 1 - assert "while :; do" not in step - assert "poll_interval_seconds" not in step - assert "poll_deadline_epoch" not in step - assert "sleep " not in step - - -def test_review_read_keeps_maximum_rest_page_size() -> None: - """The single Reviews read minimizes pages without dropping history evidence.""" - step = _admission_step() - assert "/reviews?per_page=100" in step - assert "gh api --paginate" in step -''', encoding='utf-8') - - Path('tests/test_opencode_poll_self_retirement.py').write_text('''"""Regression contract for one-shot Required OpenCode verdict admission.""" - -from __future__ import annotations - -import json -import os -import shutil -import subprocess -import textwrap -from pathlib import Path - -import pytest - - -WORKFLOW = Path(".github/workflows/opencode-review.yml") -HEAD_SHA = "a" * 40 - - -def _script() -> str: - """Extract the production one-shot verdict admission script.""" - workflow = WORKFLOW.read_text(encoding="utf-8") - step = workflow.split( - " - name: Fail closed without a current-head OpenCode verdict\\n", 1 - )[1] - return textwrap.dedent(step.split(" run: |\\n", 1)[1]) - - -def _run(tmp_path: Path, *, live_pr: dict[str, object], reviews: list[dict[str, object]] | None = None, fail_reviews: bool = False) -> tuple[subprocess.CompletedProcess[str], list[str]]: - """Execute the production step against deterministic live PR/review evidence.""" - bash = shutil.which("bash") - jq = shutil.which("jq") - if bash is None or jq is None: - pytest.skip("bash and jq are required") - fake_bin = tmp_path / "bin" - fake_bin.mkdir() - log = tmp_path / "calls" - gh = fake_bin / "gh" - gh.write_text('''#!/usr/bin/env bash -set -euo pipefail -printf '%s\\n' "$*" >>"$CALL_LOG" -if [[ "$*" == "api repos/ContextualWisdomLab/example/pulls/42" ]]; then - printf '%s\\n' "$LIVE_PR" - exit 0 -fi -if [[ "$*" == "api --paginate repos/ContextualWisdomLab/example/pulls/42/reviews?per_page=100" ]]; then - if [[ "$FAIL_REVIEWS" == "true" ]]; then exit 7; fi - printf '%s\\n' "$REVIEWS" - exit 0 -fi -exit 97 -''', encoding='utf-8') - gh.chmod(0o755) - sleep = fake_bin / "sleep" - sleep.write_text("#!/usr/bin/env bash\\nexit 91\\n", encoding="utf-8") - sleep.chmod(0o755) - result = subprocess.run( - [bash, "-c", _script()], - env={ - **os.environ, - "PATH": f"{fake_bin}{os.pathsep}{os.environ.get('PATH', '')}", - "CALL_LOG": str(log), - "LIVE_PR": json.dumps(live_pr), - "REVIEWS": json.dumps(reviews or []), - "FAIL_REVIEWS": str(fail_reviews).lower(), - "GH_TOKEN": "test-token", - "TARGET_REPOSITORY": "ContextualWisdomLab/example", - "PR_NUMBER": "42", - "HEAD_SHA": HEAD_SHA, - "PR_ACTION": "synchronize", - "PR_DRAFT": "false", - }, - text=True, - capture_output=True, - check=False, - ) - return result, log.read_text(encoding="utf-8").splitlines() - - -def test_stale_head_retires_before_reviews_read(tmp_path: Path) -> None: - """A moved head cannot consume or admit review evidence.""" - result, calls = _run(tmp_path, live_pr={"head": {"sha": "b" * 40}, "draft": False, "state": "open"}) - assert result.returncode == 0 - assert calls == ["api repos/ContextualWisdomLab/example/pulls/42"] - - -def test_closed_current_head_retires_before_reviews_read(tmp_path: Path) -> None: - """A closed current head releases the runner without a review read.""" - result, calls = _run(tmp_path, live_pr={"head": {"sha": HEAD_SHA}, "draft": False, "state": "closed"}) - assert result.returncode == 0 - assert calls == ["api repos/ContextualWisdomLab/example/pulls/42"] - - -def test_draft_current_head_retires_before_reviews_read(tmp_path: Path) -> None: - """A draft current head releases the runner without an impossible review wait.""" - result, calls = _run(tmp_path, live_pr={"head": {"sha": HEAD_SHA}, "draft": True, "state": "open"}) - assert result.returncode == 0 - assert calls == ["api repos/ContextualWisdomLab/example/pulls/42"] - - -def test_current_head_reads_reviews_once_and_accepts_exact_verdict(tmp_path: Path) -> None: - """A live current head admits one exact-head formal OpenCode verdict.""" - reviews = [{"user": {"login": "opencode-agent[bot]"}, "commit_id": HEAD_SHA, "state": "APPROVED", "body": "source-backed review"}] - result, calls = _run(tmp_path, live_pr={"head": {"sha": HEAD_SHA}, "draft": False, "state": "open"}, reviews=reviews) - assert result.returncode == 0, result.stderr - assert calls == [ - "api repos/ContextualWisdomLab/example/pulls/42", - "api --paginate repos/ContextualWisdomLab/example/pulls/42/reviews?per_page=100", - ] - - -def test_reviews_transport_failure_fails_once_and_releases_runner(tmp_path: Path) -> None: - """Transport failure is fail-closed without a repository-authored retry count.""" - result, calls = _run(tmp_path, live_pr={"head": {"sha": HEAD_SHA}, "draft": False, "state": "open"}, fail_reviews=True) - assert result.returncode == 1 - assert "Reviews API read failed during one-shot" in result.stdout - assert len(calls) == 2 - assert "sleep " not in _script() -''', encoding='utf-8') regression_path = Path('tests/test_opencode_required_verdict_regression.py') regression = regression_path.read_text(encoding='utf-8') - regression = regression.replace(' assert "while :; do" in target_job\n assert \'sleep "$poll_interval_seconds"\' in target_job\n', ' assert "while :; do" not in target_job\n assert "poll_interval_seconds" not in target_job\n assert "poll_deadline_epoch" not in target_job\n assert \'sleep "$poll_interval_seconds"\' not in target_job\n') - regression = regression.replace('before polling', 'before verdict admission') - regression = regression.replace('before ever polling Reviews API', 'before any Reviews API admission read') - regression = regression.replace('while :; do ... sleep "$poll_interval_seconds"; done', 'one-shot current-head verdict read') - regression_path.write_text(regression, encoding='utf-8') + old = ' assert "while :; do" in target_job\n assert \'sleep "$poll_interval_seconds"\' in target_job\n' + new = ' assert "while :; do" not in target_job\n assert "poll_interval_seconds" not in target_job\n assert "poll_deadline_epoch" not in target_job\n assert \'sleep "$poll_interval_seconds"\' not in target_job\n' + count = regression.count(old) + if count != 1: + raise SystemExit(f'obsolete polling assertions drifted: {count}') + regression_path.write_text(regression.replace(old, new, 1), encoding='utf-8') doctoring_path = Path('docs/doctoring/opencode-stale-poll-self-retirement.md') doctoring = doctoring_path.read_text(encoding='utf-8') marker = '## 2026-09-02 one-shot runner-release supersession' if marker not in doctoring: - doctoring += '''\n\n## 2026-09-02 one-shot runner-release supersession\n\nThe required-verdict job no longer polls while model review continues. It now performs one authoritative live-PR read followed by at most one paginated Reviews read. Missing or unavailable exact-head verdict evidence fails closed immediately and releases the runner. The authenticated `opencode-review-dispatch.yml` path remains responsible for waking the exact failed required run via `rerun-failed-jobs` when the formal verdict arrives. This removes repository-authored polling interval, retry-count, and wall-clock allocations from the review waiting path without imposing an inference timeout on contextual-orchestrator or the serving model.\n''' + doctoring += '\n\n' + marker + '\n\nThe required-verdict job no longer polls while model review continues. It performs one authoritative live-PR read followed by at most one paginated Reviews read. Missing or unavailable exact-head verdict evidence fails closed immediately and releases the runner. The authenticated `opencode-review-dispatch.yml` path remains responsible for waking the exact failed required run via `rerun-failed-jobs` when the formal verdict arrives. This removes repository-authored polling interval, retry-count, and wall-clock allocations from the review waiting path without imposing an inference timeout on contextual-orchestrator or the serving model.\n' doctoring_path.write_text(doctoring, encoding='utf-8') baseline_path = Path('docs/product-technical-gap-baseline.md') baseline = baseline_path.read_text(encoding='utf-8') - marker = 'OPENCODE-ONE-SHOT-RUNNER-RELEASE-2026-09-02' + marker = '### OPENCODE-ONE-SHOT-RUNNER-RELEASE-2026-09-02' if marker not in baseline: - baseline += '''\n\n### OPENCODE-ONE-SHOT-RUNNER-RELEASE-2026-09-02\n- Owner: `ContextualWisdomLab/.github` Required OpenCode Review control plane.\n- RCA: the required-verdict job occupied a runner while waiting for asynchronous model work, using repository-authored poll interval, transport retry count, and wall-clock deadline despite an existing authenticated exact-run wake contract in the dispatch workflow.\n- GREEN contract: one live PR read + one Reviews read; absence/transport failure is immediately non-passing, and the dispatch owner wakes the exact failed run after a verdict. Model reasoning/streaming receives no caller wall-clock timeout.\n- Regression: `tests/test_opencode_required_verdict_runner_release.py`, one-shot request-budget/state tests, and the existing exact-run dispatch validation.\n''' + baseline += '\n\n' + marker + '\n- Owner: `ContextualWisdomLab/.github` Required OpenCode Review control plane.\n- RCA: the required-verdict job occupied a runner while waiting for asynchronous model work, using repository-authored poll interval, transport retry count, and wall-clock deadline despite an existing authenticated exact-run wake contract in the dispatch workflow.\n- GREEN contract: one live PR read + one Reviews read; absence/transport failure is immediately non-passing, and the dispatch owner wakes the exact failed run after a verdict. Model reasoning/streaming receives no caller wall-clock timeout.\n- Regression: `tests/test_opencode_required_verdict_runner_release.py`, one-shot request-budget/state tests, and the existing exact-run dispatch validation.\n' baseline_path.write_text(baseline, encoding='utf-8') changelog_path = Path('CHANGELOG.md') @@ -353,22 +103,22 @@ def test_reviews_transport_failure_fails_once_and_releases_runner(tmp_path: Path changelog_path.write_text(note + changelog, encoding='utf-8') PY - - name: Verify GREEN focused and broader contracts + - name: Verify GREEN focused contracts run: | set -euo pipefail PYTHONPATH=. python3 -m pytest -q \ tests/test_opencode_required_verdict_runner_release.py \ tests/test_opencode_poll_rate_budget.py \ tests/test_opencode_poll_self_retirement.py \ - tests/test_opencode_required_verdict_regression.py \ - tests/test_opencode_workflow_shell_syntax.py - python3 - <<'PY' - import yaml - with open('.github/workflows/opencode-review.yml', encoding='utf-8') as handle: - yaml.safe_load(handle) - PY + tests/test_opencode_required_verdict_regression.py + python3 -m compileall -q scripts tests git diff --check + + - name: Verify broader repository suite + run: | + set -euo pipefail PYTHONPATH=. python3 -m pytest tests -q + git diff --check - name: Publish only from unchanged writer head env: From 30d50e9d7f4740c8c6338cb4d398845c71e6a8b1 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 18:41:18 +0900 Subject: [PATCH 05/59] ci(temp): retire failed PR1706 repair workflow --- .../_temp_pr1706_one_shot_runner_release.yml | 145 ------------------ 1 file changed, 145 deletions(-) delete mode 100644 .github/workflows/_temp_pr1706_one_shot_runner_release.yml diff --git a/.github/workflows/_temp_pr1706_one_shot_runner_release.yml b/.github/workflows/_temp_pr1706_one_shot_runner_release.yml deleted file mode 100644 index 18d2295126..0000000000 --- a/.github/workflows/_temp_pr1706_one_shot_runner_release.yml +++ /dev/null @@ -1,145 +0,0 @@ -name: Temporary PR1706 One-shot Runner Release Repair - -on: - push: - branches: - - fix/opencode-poll-wall-clock-bound - paths: - - .github/workflows/_temp_pr1706_one_shot_runner_release.yml - -permissions: - contents: write - -concurrency: - group: temp-pr1706-one-shot-runner-release - cancel-in-progress: false - -jobs: - repair: - runs-on: ubuntu-slim - steps: - - name: Checkout exact trigger head - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - with: - ref: ${{ github.sha }} - fetch-depth: 0 - persist-credentials: true - - - name: Set up Python - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 - with: - python-version: "3.14" - - - name: Install locked repository test dependencies - run: | - set -euo pipefail - python3 -m pip install --disable-pip-version-check --require-hashes --only-binary=:all: -r requirements-opencode-review-ci-hashes.txt - - - name: Prove runner-release regression is RED - run: | - set -euo pipefail - if PYTHONPATH=. python3 -m pytest -q tests/test_opencode_required_verdict_runner_release.py; then - echo '::error::Expected one-shot runner-release regression to be RED before production repair.' - exit 1 - fi - - - name: Apply deterministic owner repair - env: - STEP_B64: ICAgICAgLSBuYW1lOiBGYWlsIGNsb3NlZCB3aXRob3V0IGEgY3VycmVudC1oZWFkIE9wZW5Db2RlIHZlcmRpY3QKICAgICAgICBlbnY6CiAgICAgICAgICBHSF9UT0tFTjogJHt7IGdpdGh1Yi50b2tlbiB9fQogICAgICAgICAgVEFSR0VUX1JFUE9TSVRPUlk6ICR7eyBnaXRodWIuZXZlbnQucHVsbF9yZXF1ZXN0LmJhc2UucmVwby5mdWxsX25hbWUgfHwgZ2l0aHViLnJlcG9zaXRvcnkgfX0KICAgICAgICAgIFBSX05VTUJFUjogJHt7IGdpdGh1Yi5ldmVudC5wdWxsX3JlcXVlc3QubnVtYmVyIH19CiAgICAgICAgICBIRUFEX1NIQTogJHt7IGdpdGh1Yi5ldmVudC5wdWxsX3JlcXVlc3QuaGVhZC5zaGEgfX0KICAgICAgICAgIFBSX0FDVElPTjogJHt7IGdpdGh1Yi5ldmVudC5hY3Rpb24gfX0KICAgICAgICAgIFBSX0RSQUZUOiAke3sgZ2l0aHViLmV2ZW50LnB1bGxfcmVxdWVzdC5kcmFmdCB9fQogICAgICAgIHJ1bjogfAogICAgICAgICAgc2V0IC1ldW8gcGlwZWZhaWwKICAgICAgICAgIGlmIFsgIiRQUl9BQ1RJT04iID0gImNsb3NlZCIgXTsgdGhlbgogICAgICAgICAgICBlY2hvICJQUiBjbG9zZWQ7IGEgY3VycmVudC1oZWFkIE9wZW5Db2RlIHZlcmRpY3QgaXMgbm90IHJlcXVpcmVkLiIKICAgICAgICAgICAgZXhpdCAwCiAgICAgICAgICBmaQogICAgICAgICAgaWYgWyAteiAiJHtQUl9OVU1CRVI6LX0iIF0gfHwgWyAteiAiJHtIRUFEX1NIQTotfSIgXTsgdGhlbgogICAgICAgICAgICBlY2hvICI6OmVycm9yOjpNaXNzaW5nIFBSIG51bWJlciBvciBoZWFkIFNIQTsgY2Fubm90IHZlcmlmeSBhIGN1cnJlbnQtaGVhZCBPcGVuQ29kZSB2ZXJkaWN0LiIKICAgICAgICAgICAgZXhpdCAxCiAgICAgICAgICBmaQogICAgICAgICAgbGl2ZV9wcj0iJChnaCBhcGkgInJlcG9zLyR7VEFSR0VUX1JFUE9TSVRPUll9L3B1bGxzLyR7UFJfTlVNQkVSfSIpIgogICAgICAgICAgbGl2ZV9oZWFkPSIkKHByaW50ZiAnJXMnICIkbGl2ZV9wciIgfCBqcSAtciAnLmhlYWQuc2hhIC8vIGVtcHR5JykiCiAgICAgICAgICBsaXZlX2RyYWZ0PSIkKHByaW50ZiAnJXMnICIkbGl2ZV9wciIgfCBqcSAtciAnaWYgKC5kcmFmdCB8IHR5cGUpID09ICJib29sZWFuIiB0aGVuICguZHJhZnQgfCB0b3N0cmluZykgZWxzZSBlbXB0eSBlbmQnKSIKICAgICAgICAgIGxpdmVfc3RhdGU9IiQocHJpbnRmICclcycgIiRsaXZlX3ByIiB8IGpxIC1yICdpZiAoLnN0YXRlIHwgdHlwZSkgPT0gInN0cmluZyIgdGhlbiAuc3RhdGUgZWxzZSBlbXB0eSBlbmQnKSIKICAgICAgICAgIGlmIFsgLXogIiRsaXZlX2hlYWQiIF0gfHwgWyAteiAiJGxpdmVfZHJhZnQiIF0gfHwgWyAteiAiJGxpdmVfc3RhdGUiIF07IHRoZW4KICAgICAgICAgICAgZWNobyAiOjplcnJvcjo6Q291bGQgbm90IHZhbGlkYXRlIGxpdmUgcHVsbCByZXF1ZXN0IHN0YXRlIGJlZm9yZSB2ZXJkaWN0IGFkbWlzc2lvbi4iCiAgICAgICAgICAgIGV4aXQgMQogICAgICAgICAgZmkKICAgICAgICAgIGlmIFsgIiRsaXZlX3N0YXRlIiAhPSAib3BlbiIgXSAmJiBbICIkbGl2ZV9zdGF0ZSIgIT0gImNsb3NlZCIgXTsgdGhlbgogICAgICAgICAgICBlY2hvICI6OmVycm9yOjpDb3VsZCBub3QgdmFsaWRhdGUgbGl2ZSBwdWxsIHJlcXVlc3Qgc3RhdGUgYmVmb3JlIHZlcmRpY3QgYWRtaXNzaW9uLiIKICAgICAgICAgICAgZXhpdCAxCiAgICAgICAgICBmaQogICAgICAgICAgaWYgWyAiJGxpdmVfc3RhdGUiID0gImNsb3NlZCIgXTsgdGhlbgogICAgICAgICAgICBlY2hvICJQUiBpcyBjbG9zZWQgb24gdGhlIGxpdmUgZXhhY3QgaGVhZDsgYSBjdXJyZW50LWhlYWQgT3BlbkNvZGUgdmVyZGljdCBpcyBub3QgcmVxdWlyZWQuIgogICAgICAgICAgICBleGl0IDAKICAgICAgICAgIGZpCiAgICAgICAgICBpZiBbICIkbGl2ZV9kcmFmdCIgPSAidHJ1ZSIgXTsgdGhlbgogICAgICAgICAgICBlY2hvICJQUiBpcyBzdGlsbCBhIGRyYWZ0IG9uIHRoZSBsaXZlIGV4YWN0IGhlYWQ7IGEgY3VycmVudC1oZWFkIE9wZW5Db2RlIHZlcmRpY3QgaXMgbm90IHJlcXVpcmVkIHVudGlsIGl0IGlzIG1hcmtlZCByZWFkeSBmb3IgcmV2aWV3LiIKICAgICAgICAgICAgZXhpdCAwCiAgICAgICAgICBmaQogICAgICAgICAgaWYgWyAiJHtsaXZlX2hlYWQsLH0iICE9ICIke0hFQURfU0hBLCx9IiBdOyB0aGVuCiAgICAgICAgICAgIGVjaG8gIlB1bGwgcmVxdWVzdCBoZWFkIG1vdmVkIG9uIHRoZSBsaXZlIG9wZW4sIHJlYWR5LWZvci1yZXZpZXcgUFI7IGEgZnJlc2ggcmVxdWlyZWQtcmV2aWV3IHJ1biB3aWxsIGJpbmQgdGhlIGN1cnJlbnQgaGVhZC4iCiAgICAgICAgICAgIGV4aXQgMAogICAgICAgICAgZmkKICAgICAgICAgIGlmIFsgIiRQUl9EUkFGVCIgPSAidHJ1ZSIgXTsgdGhlbgogICAgICAgICAgICBlY2hvICJFdmVudCBkcmFmdCBzbmFwc2hvdCBpcyBzdGFsZTsgY29udGludWluZyBvbmUtc2hvdCB2ZXJkaWN0IGFkbWlzc2lvbiBmb3IgdGhlIGxpdmUgcmVhZHkgUFIuIgogICAgICAgICAgZmkKICAgICAgICAgIGlmICEgcmV2aWV3cz0iJChnaCBhcGkgLS1wYWdpbmF0ZSAicmVwb3MvJHtUQVJHRVRfUkVQT1NJVE9SWX0vcHVsbHMvJHtQUl9OVU1CRVJ9L3Jldmlld3M/cGVyX3BhZ2U9MTAwIikiOyB0aGVuCiAgICAgICAgICAgIGVjaG8gIjo6ZXJyb3I6OlJldmlld3MgQVBJIHJlYWQgZmFpbGVkIGR1cmluZyBvbmUtc2hvdCBjdXJyZW50LWhlYWQgdmVyZGljdCBhZG1pc3Npb247IGZhaWxpbmcgY2xvc2VkIGFuZCByZWxlYXNpbmcgdGhlIHJ1bm5lci4iCiAgICAgICAgICAgIGV4aXQgMQogICAgICAgICAgZmkKICAgICAgICAgIHZlcmRpY3Q9IiQocHJpbnRmICclc1xuJyAiJHJldmlld3MiIHwganEgLXIgLXMgLS1hcmcgc2hhICIkSEVBRF9TSEEiICcKICAgICAgICAgICAgKGFkZCAvLyBbXSkKICAgICAgICAgICAgfCBbCiAgICAgICAgICAgICAgICAuW10KICAgICAgICAgICAgICAgIHwgc2VsZWN0KAogICAgICAgICAgICAgICAgICAgICgudXNlci5sb2dpbiAvLyAiIiB8IGFzY2lpX2Rvd25jYXNlKSBhcyAkdXNlcgogICAgICAgICAgICAgICAgICAgIHwgJHVzZXIgPT0gIm9wZW5jb2RlLWFnZW50IiBvciAkdXNlciA9PSAib3BlbmNvZGUtYWdlbnRbYm90XSIKICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgfCBzZWxlY3QoKC5jb21taXRfaWQgLy8gIiIgfCBhc2NpaV9kb3duY2FzZSkgPT0gKCRzaGEgfCBhc2NpaV9kb3duY2FzZSkpCiAgICAgICAgICAgICAgICB8IHNlbGVjdCguc3RhdGUgPT0gIkFQUFJPVkVEIiBvciAuc3RhdGUgPT0gIkNIQU5HRVNfUkVRVUVTVEVEIikKICAgICAgICAgICAgICBdCiAgICAgICAgICAgIHwgKGxhc3QgLy8ge30pIGFzICRyZXZpZXcKICAgICAgICAgICAgfCAoJHJldmlldy5ib2R5IC8vICIiIHwgYXNjaWlfZG93bmNhc2UpIGFzICRib2R5CiAgICAgICAgICAgIHwgaWYgJHJldmlldy5zdGF0ZSA9PSAiQ0hBTkdFU19SRVFVRVNURUQiIHRoZW4KICAgICAgICAgICAgICAgICJDSEFOR0VTX1JFUVVFU1RFRCIKICAgICAgICAgICAgICBlbGlmICRyZXZpZXcuc3RhdGUgPT0gIkFQUFJPVkVEIgogICAgICAgICAgICAgICAgYW5kICgkYm9keSB8IGNvbnRhaW5zKCJkZXRlcm1pbmlzdGljIGN1cnJlbnQtaGVhZCBldmlkZW5jZSIpIHwgbm90KQogICAgICAgICAgICAgICAgYW5kICgkYm9keSB8IGNvbnRhaW5zKCJkZXRlcm1pbmlzdGljIGZhbGxiYWNrIGFwcHJvdmFsIikgfCBub3QpCiAgICAgICAgICAgICAgICBhbmQgKCRib2R5IHwgY29udGFpbnMoIm1vZGVsLXVuYXZhaWxhYmxlIGV2aWRlbmNlIGZhbGxiYWNrIikgfCBub3QpCiAgICAgICAgICAgICAgICBhbmQgKCRib2R5IHwgY29udGFpbnMoImRpZCBub3QgZW1pdCBhIHVzYWJsZSBjdXJyZW50LWhlYWQgY29udHJvbCBibG9jayIpIHwgbm90KQogICAgICAgICAgICAgICAgYW5kICgkYm9keSB8IGNvbnRhaW5zKCJzY29wZTogYHVuc3VwcG9ydGVkYCIpIHwgbm90KQogICAgICAgICAgICAgICAgYW5kICgkYm9keSB8IGNvbnRhaW5zKCJtb2RlbC1wb29sIG91dGNvbWU6IGB1bmtub3duYCIpIHwgbm90KQogICAgICAgICAgICAgIHRoZW4KICAgICAgICAgICAgICAgICJBUFBST1ZFRCIKICAgICAgICAgICAgICBlbHNlCiAgICAgICAgICAgICAgICBlbXB0eQogICAgICAgICAgICAgIGVuZAogICAgICAgICAgJykiCiAgICAgICAgICBpZiBbIC16ICIkdmVyZGljdCIgXTsgdGhlbgogICAgICAgICAgICBlY2hvICI6OmVycm9yOjpObyBBUFBST1ZFRCBvciBDSEFOR0VTX1JFUVVFU1RFRCBmcm9tIG9wZW5jb2RlLWFnZW50IG9uIHRoZSBjdXJyZW50IGhlYWQuIFRoaXMgcmVxdWlyZWQgY2hlY2sgaXMgbm90IGEgcmV2aWV3IGFuZCBtdXN0IG5vdCBzdWNjZWVkIHVudGlsIHRoZSBhdXRoZW50aWNhdGVkIGRpc3BhdGNoIHBvc3RzIGEgY3VycmVudC1oZWFkIHZlcmRpY3QuIFRoZSBkaXNwYXRjaCBwYXRoIHdha2VzIHRoaXMgZXhhY3QgZmFpbGVkIHJ1biB3aGVuIHRoZSB2ZXJkaWN0IGFycml2ZXMuIgogICAgICAgICAgICBleGl0IDEKICAgICAgICAgIGZpCiAgICAgICAgICBlY2hvICJDdXJyZW50LWhlYWQgT3BlbkNvZGUgdmVyZGljdDogJHt2ZXJkaWN0fS4iCg== - RATE_B64: IiIiUmVxdWVzdC1idWRnZXQgcmVncmVzc2lvbiBmb3Igb25lLXNob3QgUmVxdWlyZWQgT3BlbkNvZGUgdmVyZGljdCBhZG1pc3Npb24uIiIiCgpmcm9tIHBhdGhsaWIgaW1wb3J0IFBhdGgKCgpXT1JLRkxPVyA9IFBhdGgoIi5naXRodWIvd29ya2Zsb3dzL29wZW5jb2RlLXJldmlldy55bWwiKQoKCmRlZiBfYWRtaXNzaW9uX3N0ZXAoKSAtPiBzdHI6CiAgICAiIiJSZXR1cm4gdGhlIG9uZS1zaG90IGN1cnJlbnQtaGVhZCB2ZXJkaWN0IGFkbWlzc2lvbiBzdGVwLiIiIgogICAgd29ya2Zsb3cgPSBXT1JLRkxPVy5yZWFkX3RleHQoZW5jb2Rpbmc9InV0Zi04IikKICAgIHJldHVybiB3b3JrZmxvdy5zcGxpdCgKICAgICAgICAiICAgICAgLSBuYW1lOiBGYWlsIGNsb3NlZCB3aXRob3V0IGEgY3VycmVudC1oZWFkIE9wZW5Db2RlIHZlcmRpY3RcbiIsIDEKICAgIClbMV0uc3BsaXQoIlxuICBjYW5jZWwtc3VwZXJzZWRlZC1vcGVuY29kZS1yZXZpZXctcnVuczpcbiIsIDEpWzBdCgoKZGVmIHRlc3RfYWRtaXNzaW9uX3VzZXNfb25lX3Jldmlld3NfcmVhZF93aXRob3V0X3J1bm5lcl9wb2xsaW5nKCkgLT4gTm9uZToKICAgICIiIk1pc3NpbmcgbW9kZWwgZXZpZGVuY2UgcmVsZWFzZXMgdGhlIHJ1bm5lciBpbnN0ZWFkIG9mIGFsbG9jYXRpbmcgUkVTVCBwb2xsaW5nLiIiIgogICAgc3RlcCA9IF9hZG1pc3Npb25fc3RlcCgpCiAgICBhc3NlcnQgc3RlcC5jb3VudCgnZ2ggYXBpICJyZXBvcy8ke1RBUkdFVF9SRVBPU0lUT1JZfS9wdWxscy8ke1BSX05VTUJFUn0iJykgPT0gMQogICAgYXNzZXJ0IHN0ZXAuY291bnQoJ2doIGFwaSAtLXBhZ2luYXRlICJyZXBvcy8ke1RBUkdFVF9SRVBPU0lUT1JZfS9wdWxscy8ke1BSX05VTUJFUn0vcmV2aWV3cz9wZXJfcGFnZT0xMDAiJykgPT0gMQogICAgYXNzZXJ0ICJ3aGlsZSA6OyBkbyIgbm90IGluIHN0ZXAKICAgIGFzc2VydCAicG9sbF9pbnRlcnZhbF9zZWNvbmRzIiBub3QgaW4gc3RlcAogICAgYXNzZXJ0ICJwb2xsX2RlYWRsaW5lX2Vwb2NoIiBub3QgaW4gc3RlcAogICAgYXNzZXJ0ICJzbGVlcCAiIG5vdCBpbiBzdGVwCgoKZGVmIHRlc3RfcmV2aWV3X3JlYWRfa2VlcHNfbWF4aW11bV9yZXN0X3BhZ2Vfc2l6ZSgpIC0+IE5vbmU6CiAgICAiIiJUaGUgc2luZ2xlIFJldmlld3MgcmVhZCBtaW5pbWl6ZXMgcGFnZXMgd2l0aG91dCBkcm9wcGluZyBoaXN0b3J5IGV2aWRlbmNlLiIiIgogICAgc3RlcCA9IF9hZG1pc3Npb25fc3RlcCgpCiAgICBhc3NlcnQgIi9yZXZpZXdzP3Blcl9wYWdlPTEwMCIgaW4gc3RlcAogICAgYXNzZXJ0ICJnaCBhcGkgLS1wYWdpbmF0ZSIgaW4gc3RlcAo= - SELF_B64: IiIiUmVncmVzc2lvbiBjb250cmFjdCBmb3Igb25lLXNob3QgUmVxdWlyZWQgT3BlbkNvZGUgdmVyZGljdCBhZG1pc3Npb24uIiIiCgpmcm9tIF9fZnV0dXJlX18gaW1wb3J0IGFubm90YXRpb25zCgppbXBvcnQganNvbgppbXBvcnQgb3MKaW1wb3J0IHNodXRpbAppbXBvcnQgc3VicHJvY2VzcwppbXBvcnQgdGV4dHdyYXAKZnJvbSBwYXRobGliIGltcG9ydCBQYXRoCgppbXBvcnQgcHl0ZXN0CgoKV09SS0ZMT1cgPSBQYXRoKCIuZ2l0aHViL3dvcmtmbG93cy9vcGVuY29kZS1yZXZpZXcueW1sIikKSEVBRF9TSEEgPSAiYSIgKiA0MAoKCmRlZiBfc2NyaXB0KCkgLT4gc3RyOgogICAgIiIiRXh0cmFjdCB0aGUgcHJvZHVjdGlvbiBvbmUtc2hvdCB2ZXJkaWN0IGFkbWlzc2lvbiBzY3JpcHQuIiIiCiAgICB3b3JrZmxvdyA9IFdPUktGTE9XLnJlYWRfdGV4dChlbmNvZGluZz0idXRmLTgiKQogICAgc3RlcCA9IHdvcmtmbG93LnNwbGl0KAogICAgICAgICIgICAgICAtIG5hbWU6IEZhaWwgY2xvc2VkIHdpdGhvdXQgYSBjdXJyZW50LWhlYWQgT3BlbkNvZGUgdmVyZGljdFxuIiwgMQogICAgKVsxXS5zcGxpdCgiXG4gIGNhbmNlbC1zdXBlcnNlZGVkLW9wZW5jb2RlLXJldmlldy1ydW5zOlxuIiwgMSlbMF0KICAgIHJldHVybiB0ZXh0d3JhcC5kZWRlbnQoc3RlcC5zcGxpdCgiICAgICAgICBydW46IHxcbiIsIDEpWzFdKQoKCmRlZiBfcnVuKAogICAgdG1wX3BhdGg6IFBhdGgsCiAgICAqLAogICAgbGl2ZV9wcjogZGljdFtzdHIsIG9iamVjdF0sCiAgICByZXZpZXdzOiBsaXN0W2RpY3Rbc3RyLCBvYmplY3RdXSB8IE5vbmUgPSBOb25lLAogICAgZmFpbF9yZXZpZXdzOiBib29sID0gRmFsc2UsCikgLT4gdHVwbGVbc3VicHJvY2Vzcy5Db21wbGV0ZWRQcm9jZXNzW3N0cl0sIGxpc3Rbc3RyXV06CiAgICAiIiJFeGVjdXRlIHRoZSBwcm9kdWN0aW9uIHN0ZXAgYWdhaW5zdCBkZXRlcm1pbmlzdGljIGxpdmUgUFIvcmV2aWV3IGV2aWRlbmNlLiIiIgogICAgYmFzaCA9IHNodXRpbC53aGljaCgiYmFzaCIpCiAgICBqcSA9IHNodXRpbC53aGljaCgianEiKQogICAgaWYgYmFzaCBpcyBOb25lIG9yIGpxIGlzIE5vbmU6CiAgICAgICAgcHl0ZXN0LnNraXAoImJhc2ggYW5kIGpxIGFyZSByZXF1aXJlZCIpCiAgICBmYWtlX2JpbiA9IHRtcF9wYXRoIC8gImJpbiIKICAgIGZha2VfYmluLm1rZGlyKCkKICAgIGxvZyA9IHRtcF9wYXRoIC8gImNhbGxzIgogICAgZ2ggPSBmYWtlX2JpbiAvICJnaCIKICAgIGdoLndyaXRlX3RleHQoCiAgICAgICAgIiMhL3Vzci9iaW4vZW52IGJhc2hcbiIKICAgICAgICAic2V0IC1ldW8gcGlwZWZhaWxcbiIKICAgICAgICAicHJpbnRmICclc1xcbicgXCIkKlwiID4+XCIkQ0FMTF9MT0dcIlxuIgogICAgICAgICJpZiBbWyBcIiQqXCIgPT0gXCJhcGkgcmVwb3MvQ29udGV4dHVhbFdpc2RvbUxhYi9leGFtcGxlL3B1bGxzLzQyXCIgXV07IHRoZW5cbiIKICAgICAgICAiICBwcmludGYgJyVzXFxuJyBcIiRMSVZFX1BSXCJcbiIKICAgICAgICAiICBleGl0IDBcbiIKICAgICAgICAiZmlcbiIKICAgICAgICAiaWYgW1sgXCIkKlwiID09IFwiYXBpIC0tcGFnaW5hdGUgcmVwb3MvQ29udGV4dHVhbFdpc2RvbUxhYi9leGFtcGxlL3B1bGxzLzQyL3Jldmlld3M/cGVyX3BhZ2U9MTAwXCIgXV07IHRoZW5cbiIKICAgICAgICAiICBpZiBbWyBcIiRGQUlMX1JFVklFV1NcIiA9PSBcInRydWVcIiBdXTsgdGhlbiBleGl0IDc7IGZpXG4iCiAgICAgICAgIiAgcHJpbnRmICclc1xcbicgXCIkUkVWSUVXU1wiXG4iCiAgICAgICAgIiAgZXhpdCAwXG4iCiAgICAgICAgImZpXG4iCiAgICAgICAgImV4aXQgOTdcbiIsCiAgICAgICAgZW5jb2Rpbmc9InV0Zi04IiwKICAgICkKICAgIGdoLmNobW9kKDBvNzU1KQogICAgc2xlZXAgPSBmYWtlX2JpbiAvICJzbGVlcCIKICAgIHNsZWVwLndyaXRlX3RleHQoIiMhL3Vzci9iaW4vZW52IGJhc2hcbmV4aXQgOTFcbiIsIGVuY29kaW5nPSJ1dGYtOCIpCiAgICBzbGVlcC5jaG1vZCgwbzc1NSkKICAgIHJlc3VsdCA9IHN1YnByb2Nlc3MucnVuKAogICAgICAgIFtiYXNoLCAiLWMiLCBfc2NyaXB0KCldLAogICAgICAgIGVudj17CiAgICAgICAgICAgICoqb3MuZW52aXJvbiwKICAgICAgICAgICAgIlBBVEgiOiBmIntmYWtlX2Jpbn17b3MucGF0aHNlcH17b3MuZW52aXJvbi5nZXQoJ1BBVEgnLCAnJyl9IiwKICAgICAgICAgICAgIkNBTExfTE9HIjogc3RyKGxvZyksCiAgICAgICAgICAgICJMSVZFX1BSIjoganNvbi5kdW1wcyhsaXZlX3ByKSwKICAgICAgICAgICAgIlJFVklFV1MiOiBqc29uLmR1bXBzKHJldmlld3Mgb3IgW10pLAogICAgICAgICAgICAiRkFJTF9SRVZJRVdTIjogc3RyKGZhaWxfcmV2aWV3cykubG93ZXIoKSwKICAgICAgICAgICAgIkdIX1RPS0VOIjogInRlc3QtdG9rZW4iLAogICAgICAgICAgICAiVEFSR0VUX1JFUE9TSVRPUlkiOiAiQ29udGV4dHVhbFdpc2RvbUxhYi9leGFtcGxlIiwKICAgICAgICAgICAgIlBSX05VTUJFUiI6ICI0MiIsCiAgICAgICAgICAgICJIRUFEX1NIQSI6IEhFQURfU0hBLAogICAgICAgICAgICAiUFJfQUNUSU9OIjogInN5bmNocm9uaXplIiwKICAgICAgICAgICAgIlBSX0RSQUZUIjogImZhbHNlIiwKICAgICAgICAgIH0sCiAgICAgICAgdGV4dD1UcnVlLAogICAgICAgIGNhcHR1cmVfb3V0cHV0PVRydWUsCiAgICAgICAgY2hlY2s9RmFsc2UsCiAgICApCiAgICByZXR1cm4gcmVzdWx0LCBsb2cucmVhZF90ZXh0KGVuY29kaW5nPSJ1dGYtOCIpLnNwbGl0bGluZXMoKQoKCmRlZiB0ZXN0X3N0YWxlX2hlYWRfcmV0aXJlc19iZWZvcmVfcmV2aWV3c19yZWFkKHRtcF9wYXRoOiBQYXRoKSAtPiBOb25lOgogICAgIiIiQSBtb3ZlZCBoZWFkIGNhbm5vdCBjb25zdW1lIG9yIGFkbWl0IHJldmlldyBldmlkZW5jZS4iIiIKICAgIHJlc3VsdCwgY2FsbHMgPSBfcnVuKAogICAgICAgIHRtcF9wYXRoLAogICAgICAgIGxpdmVfcHI9eyJoZWFkIjogeyJzaGEiOiAiYiIgKiA0MH0sICJkcmFmdCI6IEZhbHNlLCAic3RhdGUiOiAib3BlbiJ9LAogICAgKQogICAgYXNzZXJ0IHJlc3VsdC5yZXR1cm5jb2RlID09IDAKICAgIGFzc2VydCBjYWxscyA9PSBbImFwaSByZXBvcy9Db250ZXh0dWFsV2lzZG9tTGFiL2V4YW1wbGUvcHVsbHMvNDIiXQoKCmRlZiB0ZXN0X2Nsb3NlZF9jdXJyZW50X2hlYWRfcmV0aXJlc19iZWZvcmVfcmV2aWV3c19yZWFkKHRtcF9wYXRoOiBQYXRoKSAtPiBOb25lOgogICAgIiIiQSBjbG9zZWQgY3VycmVudCBoZWFkIHJlbGVhc2VzIHRoZSBydW5uZXIgd2l0aG91dCBhIHJldmlldyByZWFkLiIiIgogICAgcmVzdWx0LCBjYWxscyA9IF9ydW4oCiAgICAgICAgdG1wX3BhdGgsCiAgICAgICAgbGl2ZV9wcj17ImhlYWQiOiB7InNoYSI6IEhFQURfU0hBfSwgImRyYWZ0IjogRmFsc2UsICJzdGF0ZSI6ICJjbG9zZWQifSwKICAgICkKICAgIGFzc2VydCByZXN1bHQucmV0dXJuY29kZSA9PSAwCiAgICBhc3NlcnQgY2FsbHMgPT0gWyJhcGkgcmVwb3MvQ29udGV4dHVhbFdpc2RvbUxhYi9leGFtcGxlL3B1bGxzLzQyIl0KCgpkZWYgdGVzdF9kcmFmdF9jdXJyZW50X2hlYWRfcmV0aXJlc19iZWZvcmVfcmV2aWV3c19yZWFkKHRtcF9wYXRoOiBQYXRoKSAtPiBOb25lOgogICAgIiIiQSBkcmFmdCBjdXJyZW50IGhlYWQgcmVsZWFzZXMgdGhlIHJ1bm5lciB3aXRob3V0IGFuIGltcG9zc2libGUgcmV2aWV3IHdhaXQuIiIiCiAgICByZXN1bHQsIGNhbGxzID0gX3J1bigKICAgICAgICB0bXBfcGF0aCwKICAgICAgICBsaXZlX3ByPXsiaGVhZCI6IHsic2hhIjogSEVBRF9TSEF9LCAiZHJhZnQiOiBUcnVlLCAic3RhdGUiOiAib3BlbiJ9LAogICAgKQogICAgYXNzZXJ0IHJlc3VsdC5yZXR1cm5jb2RlID09IDAKICAgIGFzc2VydCBjYWxscyA9PSBbImFwaSByZXBvcy9Db250ZXh0dWFsV2lzZG9tTGFiL2V4YW1wbGUvcHVsbHMvNDIiXQoKCmRlZiB0ZXN0X2N1cnJlbnRfaGVhZF9yZWFkc19yZXZpZXdzX29uY2VfYW5kX2FjY2VwdHNfZXhhY3RfdmVyZGljdCh0bXBfcGF0aDogUGF0aCkgLT4gTm9uZToKICAgICIiIkEgbGl2ZSBjdXJyZW50IGhlYWQgYWRtaXRzIG9uZSBleGFjdC1oZWFkIGZvcm1hbCBPcGVuQ29kZSB2ZXJkaWN0LiIiIgogICAgcmV2aWV3cyA9IFsKICAgICAgICB7CiAgICAgICAgICAgICJ1c2VyIjogeyJsb2dpbiI6ICJvcGVuY29kZS1hZ2VudFtib3RdIn0sCiAgICAgICAgICAgICJjb21taXRfaWQiOiBIRUFEX1NIQSwKICAgICAgICAgICAgInN0YXRlIjogIkFQUFJPVkVEIiwKICAgICAgICAgICAgImJvZHkiOiAic291cmNlLWJhY2tlZCByZXZpZXciLAogICAgICAgIH0KICAgIF0KICAgIHJlc3VsdCwgY2FsbHMgPSBfcnVuKAogICAgICAgIHRtcF9wYXRoLAogICAgICAgIGxpdmVfcHI9eyJoZWFkIjogeyJzaGEiOiBIRUFEX1NIQX0sICJkcmFmdCI6IEZhbHNlLCAic3RhdGUiOiAib3BlbiJ9LAogICAgICAgIHJldmlld3M9cmV2aWV3cywKICAgICkKICAgIGFzc2VydCByZXN1bHQucmV0dXJuY29kZSA9PSAwLCByZXN1bHQuc3RkZXJyCiAgICBhc3NlcnQgY2FsbHMgPT0gWwogICAgICAgICJhcGkgcmVwb3MvQ29udGV4dHVhbFdpc2RvbUxhYi9leGFtcGxlL3B1bGxzLzQyIiwKICAgICAgICAiYXBpIC0tcGFnaW5hdGUgcmVwb3MvQ29udGV4dHVhbFdpc2RvbUxhYi9leGFtcGxlL3B1bGxzLzQyL3Jldmlld3M/cGVyX3BhZ2U9MTAwIiwKICAgIF0KCgpkZWYgdGVzdF9yZXZpZXdzX3RyYW5zcG9ydF9mYWlsdXJlX2ZhaWxzX29uY2VfYW5kX3JlbGVhc2VzX3J1bm5lcih0bXBfcGF0aDogUGF0aCkgLT4gTm9uZToKICAgICIiIlRyYW5zcG9ydCBmYWlsdXJlIGlzIGZhaWwtY2xvc2VkIHdpdGhvdXQgYSByZXBvc2l0b3J5LWF1dGhvcmVkIHJldHJ5IGNvdW50LiIiIgogICAgcmVzdWx0LCBjYWxscyA9IF9ydW4oCiAgICAgICAgdG1wX3BhdGgsCiAgICAgICAgbGl2ZV9wcj17ImhlYWQiOiB7InNoYSI6IEhFQURfU0hBfSwgImRyYWZ0IjogRmFsc2UsICJzdGF0ZSI6ICJvcGVuIn0sCiAgICAgICAgZmFpbF9yZXZpZXdzPVRydWUsCiAgICApCiAgICBhc3NlcnQgcmVzdWx0LnJldHVybmNvZGUgPT0gMQogICAgYXNzZXJ0ICJSZXZpZXdzIEFQSSByZWFkIGZhaWxlZCBkdXJpbmcgb25lLXNob3QiIGluIHJlc3VsdC5zdGRvdXQKICAgIGFzc2VydCBsZW4oY2FsbHMpID09IDIKICAgIGFzc2VydCAic2xlZXAgIiBub3QgaW4gX3NjcmlwdCgpCg== - run: | - set -euo pipefail - python3 <<'PY' - import base64 - import os - from pathlib import Path - - workflow_path = Path('.github/workflows/opencode-review.yml') - workflow = workflow_path.read_text(encoding='utf-8') - start = ' - name: Fail closed without a current-head OpenCode verdict\n' - end = '\n cancel-superseded-opencode-review-runs:\n' - if workflow.count(start) != 1 or workflow.count(end) != 1: - raise SystemExit('OpenCode required-verdict step boundaries drifted') - before, rest = workflow.split(start, 1) - _old, after = rest.split(end, 1) - replacement = base64.b64decode(os.environ['STEP_B64']).decode() - workflow_path.write_text(before + replacement + end + after, encoding='utf-8') - - Path('tests/test_opencode_poll_rate_budget.py').write_text( - base64.b64decode(os.environ['RATE_B64']).decode(), encoding='utf-8' - ) - Path('tests/test_opencode_poll_self_retirement.py').write_text( - base64.b64decode(os.environ['SELF_B64']).decode(), encoding='utf-8' - ) - - regression_path = Path('tests/test_opencode_required_verdict_regression.py') - regression = regression_path.read_text(encoding='utf-8') - old = ' assert "while :; do" in target_job\n assert \'sleep "$poll_interval_seconds"\' in target_job\n' - new = ' assert "while :; do" not in target_job\n assert "poll_interval_seconds" not in target_job\n assert "poll_deadline_epoch" not in target_job\n assert \'sleep "$poll_interval_seconds"\' not in target_job\n' - count = regression.count(old) - if count != 1: - raise SystemExit(f'obsolete polling assertions drifted: {count}') - regression_path.write_text(regression.replace(old, new, 1), encoding='utf-8') - - doctoring_path = Path('docs/doctoring/opencode-stale-poll-self-retirement.md') - doctoring = doctoring_path.read_text(encoding='utf-8') - marker = '## 2026-09-02 one-shot runner-release supersession' - if marker not in doctoring: - doctoring += '\n\n' + marker + '\n\nThe required-verdict job no longer polls while model review continues. It performs one authoritative live-PR read followed by at most one paginated Reviews read. Missing or unavailable exact-head verdict evidence fails closed immediately and releases the runner. The authenticated `opencode-review-dispatch.yml` path remains responsible for waking the exact failed required run via `rerun-failed-jobs` when the formal verdict arrives. This removes repository-authored polling interval, retry-count, and wall-clock allocations from the review waiting path without imposing an inference timeout on contextual-orchestrator or the serving model.\n' - doctoring_path.write_text(doctoring, encoding='utf-8') - - baseline_path = Path('docs/product-technical-gap-baseline.md') - baseline = baseline_path.read_text(encoding='utf-8') - marker = '### OPENCODE-ONE-SHOT-RUNNER-RELEASE-2026-09-02' - if marker not in baseline: - baseline += '\n\n' + marker + '\n- Owner: `ContextualWisdomLab/.github` Required OpenCode Review control plane.\n- RCA: the required-verdict job occupied a runner while waiting for asynchronous model work, using repository-authored poll interval, transport retry count, and wall-clock deadline despite an existing authenticated exact-run wake contract in the dispatch workflow.\n- GREEN contract: one live PR read + one Reviews read; absence/transport failure is immediately non-passing, and the dispatch owner wakes the exact failed run after a verdict. Model reasoning/streaming receives no caller wall-clock timeout.\n- Regression: `tests/test_opencode_required_verdict_runner_release.py`, one-shot request-budget/state tests, and the existing exact-run dispatch validation.\n' - baseline_path.write_text(baseline, encoding='utf-8') - - changelog_path = Path('CHANGELOG.md') - changelog = changelog_path.read_text(encoding='utf-8') - note = '- Required OpenCode Review now releases its runner after one exact-head verdict admission read and relies on the authenticated exact-run dispatch wake instead of repository-authored polling, retry-count, or waiting deadlines.\n' - if note not in changelog: - changelog_path.write_text(note + changelog, encoding='utf-8') - PY - - - name: Verify GREEN focused contracts - run: | - set -euo pipefail - PYTHONPATH=. python3 -m pytest -q \ - tests/test_opencode_required_verdict_runner_release.py \ - tests/test_opencode_poll_rate_budget.py \ - tests/test_opencode_poll_self_retirement.py \ - tests/test_opencode_required_verdict_regression.py - python3 -m compileall -q scripts tests - git diff --check - - - name: Verify broader repository suite - run: | - set -euo pipefail - PYTHONPATH=. python3 -m pytest tests -q - git diff --check - - - name: Publish only from unchanged writer head - env: - EXPECTED_HEAD: ${{ github.sha }} - run: | - set -euo pipefail - git fetch origin fix/opencode-poll-wall-clock-bound - live_head="$(git rev-parse origin/fix/opencode-poll-wall-clock-bound)" - if [ "$live_head" != "$EXPECTED_HEAD" ]; then - echo "::error::Writer branch advanced to $live_head; refusing stale publication." - exit 1 - fi - git config user.name "ContextualWisdomLab automation" - git config user.email "automation@users.noreply.github.com" - git add .github/workflows/opencode-review.yml \ - tests/test_opencode_required_verdict_runner_release.py \ - tests/test_opencode_poll_rate_budget.py \ - tests/test_opencode_poll_self_retirement.py \ - tests/test_opencode_required_verdict_regression.py \ - docs/doctoring/opencode-stale-poll-self-retirement.md \ - docs/product-technical-gap-baseline.md CHANGELOG.md - git diff --cached --check - git commit -m "fix(opencode): release required runner after one verdict read" - git push origin HEAD:fix/opencode-poll-wall-clock-bound From 874d5bd3df7c4991a7b6f544d166a96777da4911 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 18:42:05 +0900 Subject: [PATCH 06/59] ci(temp): add deterministic PR1706 repair driver --- .../ci/temp_pr1706_one_shot_runner_release.py | 147 ++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 scripts/ci/temp_pr1706_one_shot_runner_release.py diff --git a/scripts/ci/temp_pr1706_one_shot_runner_release.py b/scripts/ci/temp_pr1706_one_shot_runner_release.py new file mode 100644 index 0000000000..90fadcf279 --- /dev/null +++ b/scripts/ci/temp_pr1706_one_shot_runner_release.py @@ -0,0 +1,147 @@ +"""Temporary exact-head repair driver for PR #1706; deleted after GREEN publication.""" + +from __future__ import annotations + +from pathlib import Path + + +WORKFLOW = Path(".github/workflows/opencode-review.yml") +REGRESSION = Path("tests/test_opencode_required_verdict_regression.py") +RATE = Path("tests/test_opencode_poll_rate_budget.py") +SELF = Path("tests/test_opencode_poll_self_retirement.py") + + +def replace_once(text: str, old: str, new: str, label: str) -> str: + count = text.count(old) + if count != 1: + raise SystemExit(f"{label} drifted: expected one exact match, found {count}") + return text.replace(old, new, 1) + + +workflow = WORKFLOW.read_text(encoding="utf-8") +start = " - name: Fail closed without a current-head OpenCode verdict\n" +end = "\n cancel-superseded-opencode-review-runs:\n" +if workflow.count(start) != 1 or workflow.count(end) != 1: + raise SystemExit("OpenCode required-verdict step boundaries drifted") +before, rest = workflow.split(start, 1) +_old_step, after = rest.split(end, 1) +replacement = r''' - name: Fail closed without a current-head OpenCode verdict + env: + GH_TOKEN: ${{ github.token }} + TARGET_REPOSITORY: ${{ github.event.pull_request.base.repo.full_name || github.repository }} + PR_NUMBER: ${{ github.event.pull_request.number }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + PR_ACTION: ${{ github.event.action }} + PR_DRAFT: ${{ github.event.pull_request.draft }} + run: | + set -euo pipefail + if [ "$PR_ACTION" = "closed" ]; then + echo "PR closed; a current-head OpenCode verdict is not required." + exit 0 + fi + if [ -z "${PR_NUMBER:-}" ] || [ -z "${HEAD_SHA:-}" ]; then + echo "::error::Missing PR number or head SHA; cannot verify a current-head OpenCode verdict." + exit 1 + fi + live_pr="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")" + live_head="$(printf '%s' "$live_pr" | jq -r '.head.sha // empty')" + live_draft="$(printf '%s' "$live_pr" | jq -r 'if (.draft | type) == "boolean" then (.draft | tostring) else empty end')" + live_state="$(printf '%s' "$live_pr" | jq -r 'if (.state | type) == "string" then .state else empty end')" + if [ -z "$live_head" ] || [ -z "$live_draft" ] || [ -z "$live_state" ]; then + echo "::error::Could not validate live pull request state before verdict admission." + exit 1 + fi + if [ "$live_state" != "open" ] && [ "$live_state" != "closed" ]; then + echo "::error::Could not validate live pull request state before verdict admission." + exit 1 + fi + if [ "$live_state" = "closed" ]; then + echo "PR is closed on the live exact head; a current-head OpenCode verdict is not required." + exit 0 + fi + if [ "$live_draft" = "true" ]; then + echo "PR is still a draft on the live exact head; a current-head OpenCode verdict is not required until it is marked ready for review." + exit 0 + fi + if [ "${live_head,,}" != "${HEAD_SHA,,}" ]; then + echo "Pull request head moved on the live open, ready-for-review PR; a fresh required-review run will bind the current head." + exit 0 + fi + if [ "$PR_DRAFT" = "true" ]; then + echo "Event draft snapshot is stale; continuing one-shot verdict admission for the live ready PR." + fi + if ! reviews="$(gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"; then + echo "::error::Reviews API read failed during one-shot current-head verdict admission; failing closed and releasing the runner." + exit 1 + fi + verdict="$(printf '%s\n' "$reviews" | jq -r -s --arg sha "$HEAD_SHA" ' + (add // []) + | [.[] + | select((.user.login // "" | ascii_downcase) as $user | $user == "opencode-agent" or $user == "opencode-agent[bot]") + | select((.commit_id // "" | ascii_downcase) == ($sha | ascii_downcase)) + | select(.state == "APPROVED" or .state == "CHANGES_REQUESTED")] + | (last // {}) as $review + | ($review.body // "" | ascii_downcase) as $body + | if $review.state == "CHANGES_REQUESTED" then "CHANGES_REQUESTED" + elif $review.state == "APPROVED" + and ($body | contains("deterministic current-head evidence") | not) + and ($body | contains("deterministic fallback approval") | not) + and ($body | contains("model-unavailable evidence fallback") | not) + and ($body | contains("did not emit a usable current-head control block") | not) + and ($body | contains("scope: `unsupported`") | not) + and ($body | contains("model-pool outcome: `unknown`") | not) + then "APPROVED" else empty end + ')" + if [ -z "$verdict" ]; then + echo "::error::No APPROVED or CHANGES_REQUESTED from opencode-agent on the current head. This required check is not a review and must not succeed until the authenticated dispatch posts a current-head verdict. The dispatch path wakes this exact failed run when the verdict arrives." + exit 1 + fi + echo "Current-head OpenCode verdict: ${verdict}." +''' +WORKFLOW.write_text(before + replacement + end + after, encoding="utf-8") + +RATE.write_text('''"""Request-budget regression for one-shot Required OpenCode verdict admission."""\n\nfrom pathlib import Path\n\nWORKFLOW = Path(".github/workflows/opencode-review.yml")\n\ndef _step() -> str:\n text = WORKFLOW.read_text(encoding="utf-8")\n return text.split(" - name: Fail closed without a current-head OpenCode verdict\\n", 1)[1].split("\\n cancel-superseded-opencode-review-runs:\\n", 1)[0]\n\ndef test_one_reviews_read_without_runner_polling() -> None:\n step = _step()\n assert step.count('gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}"') == 1\n assert step.count('gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100"') == 1\n for token in ("while :; do", "poll_interval_seconds", "poll_deadline_epoch", "sleep "):\n assert token not in step\n\ndef test_reviews_read_uses_full_page() -> None:\n step = _step()\n assert "/reviews?per_page=100" in step\n assert "gh api --paginate" in step\n''', encoding="utf-8") + +self_text = SELF.read_text(encoding="utf-8") +self_text = replace_once(self_text, '"""Regression contract for stale OpenCode poll self-retirement."""', '"""Regression contract for one-shot Required OpenCode verdict admission."""', "self-retirement module docstring") +self_text = self_text.replace("poll", "admission") +self_text = self_text.replace("Poll", "Admission") +self_text = self_text.replace("POLL", "ADMISSION") +# The behavioral tests execute production shell; keep them but remove retry/deadline assumptions. +for old, new in [ + ('a fresh poll will start for the current head.', 'a fresh required-review run will bind the current head.'), + ('Reviews API read failed 3 consecutive times', 'Reviews API read failed during one-shot current-head verdict admission'), +]: + self_text = self_text.replace(old, new) +SELF.write_text(self_text, encoding="utf-8") + +regression = REGRESSION.read_text(encoding="utf-8") +for old, new, label in [ + (' assert "while :; do" in target_job\n assert \'sleep "$poll_interval_seconds"\' in target_job\n', ' assert "while :; do" not in target_job\n assert "poll_interval_seconds" not in target_job\n assert "poll_deadline_epoch" not in target_job\n assert \'sleep "$poll_interval_seconds"\' not in target_job\n', "legacy poll assertions"), + ('a fresh poll will start for the current head.', 'a fresh required-review run will bind the current head.', "moved-head message"), + ('Reviews API read failed 3 consecutive times', 'Reviews API read failed during one-shot current-head verdict admission', "transport failure message"), + ('"""The receipt wake path coexists with the unbounded required review wait."""', '"""The receipt wake path reawakens the fail-closed one-shot required review."""', "receipt wake docstring"), + (' assert "while :; do" in required\n', ' assert "while :; do" not in required\n assert "poll_deadline_epoch" not in required\n', "receipt wake loop assertion"), +]: + regression = replace_once(regression, old, new, label) +REGRESSION.write_text(regression, encoding="utf-8") + +baseline = Path("docs/product-technical-gap-baseline.md") +text = baseline.read_text(encoding="utf-8") +marker = "### OPENCODE-ONE-SHOT-RUNNER-RELEASE-2026-09-02" +if marker not in text: + text += f'''\n\n{marker}\n- Owner: `ContextualWisdomLab/.github` Required OpenCode Review control plane.\n- RCA: required-verdict occupied a runner while asynchronous model work continued, using repository-authored polling/retry/wall-clock allocation despite an authenticated exact-run wake contract.\n- GREEN: one live PR read plus one Reviews read; missing/unavailable exact-head verdict fails closed immediately and dispatch wakes the exact failed run after the verdict. Model reasoning receives no caller wall-clock timeout.\n- Regression: `tests/test_opencode_required_verdict_runner_release.py` plus one-shot request/state and dispatch-wake contracts.\n''' + baseline.write_text(text, encoding="utf-8") + +doctoring = Path("docs/doctoring/opencode-stale-poll-self-retirement.md") +text = doctoring.read_text(encoding="utf-8") +marker = "## 2026-09-02 one-shot runner-release supersession" +if marker not in text: + text += f'''\n\n{marker}\n\nThe required-verdict job performs one authoritative live-PR read followed by at most one paginated Reviews read. Missing or unavailable exact-head verdict evidence fails closed immediately and releases the runner. Authenticated `opencode-review-dispatch.yml` wakes the exact failed run via `rerun-failed-jobs` when the formal verdict arrives; no repository-authored polling interval, retry count, or wall-clock deadline bounds model work.\n''' + doctoring.write_text(text, encoding="utf-8") + +changelog = Path("CHANGELOG.md") +text = changelog.read_text(encoding="utf-8") +note = "- Required OpenCode Review now releases its runner after one exact-head verdict admission read and relies on authenticated exact-run dispatch wake instead of repository-authored polling, retry-count, or waiting deadlines.\n" +if note not in text: + changelog.write_text(note + text, encoding="utf-8") From 0b533b2a37bc7f6c2cb283468041a7e7a0d4fac2 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 18:42:20 +0900 Subject: [PATCH 07/59] ci(temp): rerun PR1706 repair with durable driver --- .../_temp_pr1706_one_shot_runner_release.yml | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 .github/workflows/_temp_pr1706_one_shot_runner_release.yml diff --git a/.github/workflows/_temp_pr1706_one_shot_runner_release.yml b/.github/workflows/_temp_pr1706_one_shot_runner_release.yml new file mode 100644 index 0000000000..d87eb6df22 --- /dev/null +++ b/.github/workflows/_temp_pr1706_one_shot_runner_release.yml @@ -0,0 +1,83 @@ +name: Temporary PR1706 One-shot Runner Release Repair + +on: + push: + branches: + - fix/opencode-poll-wall-clock-bound + paths: + - .github/workflows/_temp_pr1706_one_shot_runner_release.yml + +permissions: + contents: write + +concurrency: + group: temp-pr1706-one-shot-runner-release + cancel-in-progress: false + +jobs: + repair: + runs-on: ubuntu-slim + steps: + - name: Checkout exact trigger head + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + ref: ${{ github.sha }} + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.14" + + - name: Install locked repository test dependencies + run: python3 -m pip install --disable-pip-version-check --require-hashes --only-binary=:all: -r requirements-opencode-review-ci-hashes.txt + + - name: Prove prerequisite regression is RED + run: | + set -euo pipefail + if PYTHONPATH=. python3 -m pytest -q tests/test_opencode_required_verdict_runner_release.py; then + echo '::error::Expected runner-release regression to be RED before production repair.' + exit 1 + fi + + - name: Apply deterministic causal repair + run: PYTHONPATH=. python3 scripts/ci/temp_pr1706_one_shot_runner_release.py + + - name: Verify focused GREEN + run: | + set -euo pipefail + PYTHONPATH=. python3 -m pytest -q \ + tests/test_opencode_required_verdict_runner_release.py \ + tests/test_opencode_poll_rate_budget.py \ + tests/test_opencode_poll_self_retirement.py \ + tests/test_opencode_required_verdict_regression.py + python3 -m compileall -q scripts tests + git diff --check + + - name: Verify broader repository suite + run: | + set -euo pipefail + PYTHONPATH=. python3 -m pytest tests -q + git diff --check + + - name: Publish only from unchanged writer head + env: + EXPECTED_HEAD: ${{ github.sha }} + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + git fetch origin fix/opencode-poll-wall-clock-bound + live_head="$(git rev-parse origin/fix/opencode-poll-wall-clock-bound)" + test "$live_head" = "$EXPECTED_HEAD" || { echo "::error::Writer head advanced to $live_head"; exit 1; } + git config user.name "ContextualWisdomLab automation" + git config user.email "automation@users.noreply.github.com" + git add .github/workflows/opencode-review.yml \ + tests/test_opencode_required_verdict_runner_release.py \ + tests/test_opencode_poll_rate_budget.py \ + tests/test_opencode_poll_self_retirement.py \ + tests/test_opencode_required_verdict_regression.py \ + docs/doctoring/opencode-stale-poll-self-retirement.md \ + docs/product-technical-gap-baseline.md CHANGELOG.md + git diff --cached --check + git commit -m "fix(opencode): release required runner after one verdict read" + git push origin HEAD:fix/opencode-poll-wall-clock-bound From fd33bab06c3a1fc24693673b02e0ebb10bbfebc6 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 18:45:37 +0900 Subject: [PATCH 08/59] ci(temp): repair PR1706 workflow validation --- .../_temp_pr1706_one_shot_runner_release.yml | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/.github/workflows/_temp_pr1706_one_shot_runner_release.yml b/.github/workflows/_temp_pr1706_one_shot_runner_release.yml index d87eb6df22..c50616027e 100644 --- a/.github/workflows/_temp_pr1706_one_shot_runner_release.yml +++ b/.github/workflows/_temp_pr1706_one_shot_runner_release.yml @@ -16,13 +16,14 @@ concurrency: jobs: repair: - runs-on: ubuntu-slim + runs-on: ubuntu-24.04 steps: - name: Checkout exact trigger head uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ github.sha }} fetch-depth: 0 + persist-credentials: true - name: Set up Python uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 @@ -30,7 +31,9 @@ jobs: python-version: "3.14" - name: Install locked repository test dependencies - run: python3 -m pip install --disable-pip-version-check --require-hashes --only-binary=:all: -r requirements-opencode-review-ci-hashes.txt + run: | + set -euo pipefail + python3 -m pip install --disable-pip-version-check --require-hashes --only-binary=:all: -r requirements-opencode-review-ci-hashes.txt - name: Prove prerequisite regression is RED run: | @@ -41,7 +44,9 @@ jobs: fi - name: Apply deterministic causal repair - run: PYTHONPATH=. python3 scripts/ci/temp_pr1706_one_shot_runner_release.py + run: | + set -euo pipefail + PYTHONPATH=. python3 scripts/ci/temp_pr1706_one_shot_runner_release.py - name: Verify focused GREEN run: | @@ -63,12 +68,14 @@ jobs: - name: Publish only from unchanged writer head env: EXPECTED_HEAD: ${{ github.sha }} - GH_TOKEN: ${{ github.token }} run: | set -euo pipefail git fetch origin fix/opencode-poll-wall-clock-bound live_head="$(git rev-parse origin/fix/opencode-poll-wall-clock-bound)" - test "$live_head" = "$EXPECTED_HEAD" || { echo "::error::Writer head advanced to $live_head"; exit 1; } + if [ "$live_head" != "$EXPECTED_HEAD" ]; then + echo "::error::Writer branch advanced to $live_head; refusing stale publication." + exit 1 + fi git config user.name "ContextualWisdomLab automation" git config user.email "automation@users.noreply.github.com" git add .github/workflows/opencode-review.yml \ From 003d81967c14bf107d04612513a0e1198db6ba4e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 18:46:46 +0900 Subject: [PATCH 09/59] ci(temp): retire invalid PR1706 workflow identity --- .../_temp_pr1706_one_shot_runner_release.yml | 90 ------------------- 1 file changed, 90 deletions(-) delete mode 100644 .github/workflows/_temp_pr1706_one_shot_runner_release.yml diff --git a/.github/workflows/_temp_pr1706_one_shot_runner_release.yml b/.github/workflows/_temp_pr1706_one_shot_runner_release.yml deleted file mode 100644 index c50616027e..0000000000 --- a/.github/workflows/_temp_pr1706_one_shot_runner_release.yml +++ /dev/null @@ -1,90 +0,0 @@ -name: Temporary PR1706 One-shot Runner Release Repair - -on: - push: - branches: - - fix/opencode-poll-wall-clock-bound - paths: - - .github/workflows/_temp_pr1706_one_shot_runner_release.yml - -permissions: - contents: write - -concurrency: - group: temp-pr1706-one-shot-runner-release - cancel-in-progress: false - -jobs: - repair: - runs-on: ubuntu-24.04 - steps: - - name: Checkout exact trigger head - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - with: - ref: ${{ github.sha }} - fetch-depth: 0 - persist-credentials: true - - - name: Set up Python - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 - with: - python-version: "3.14" - - - name: Install locked repository test dependencies - run: | - set -euo pipefail - python3 -m pip install --disable-pip-version-check --require-hashes --only-binary=:all: -r requirements-opencode-review-ci-hashes.txt - - - name: Prove prerequisite regression is RED - run: | - set -euo pipefail - if PYTHONPATH=. python3 -m pytest -q tests/test_opencode_required_verdict_runner_release.py; then - echo '::error::Expected runner-release regression to be RED before production repair.' - exit 1 - fi - - - name: Apply deterministic causal repair - run: | - set -euo pipefail - PYTHONPATH=. python3 scripts/ci/temp_pr1706_one_shot_runner_release.py - - - name: Verify focused GREEN - run: | - set -euo pipefail - PYTHONPATH=. python3 -m pytest -q \ - tests/test_opencode_required_verdict_runner_release.py \ - tests/test_opencode_poll_rate_budget.py \ - tests/test_opencode_poll_self_retirement.py \ - tests/test_opencode_required_verdict_regression.py - python3 -m compileall -q scripts tests - git diff --check - - - name: Verify broader repository suite - run: | - set -euo pipefail - PYTHONPATH=. python3 -m pytest tests -q - git diff --check - - - name: Publish only from unchanged writer head - env: - EXPECTED_HEAD: ${{ github.sha }} - run: | - set -euo pipefail - git fetch origin fix/opencode-poll-wall-clock-bound - live_head="$(git rev-parse origin/fix/opencode-poll-wall-clock-bound)" - if [ "$live_head" != "$EXPECTED_HEAD" ]; then - echo "::error::Writer branch advanced to $live_head; refusing stale publication." - exit 1 - fi - git config user.name "ContextualWisdomLab automation" - git config user.email "automation@users.noreply.github.com" - git add .github/workflows/opencode-review.yml \ - tests/test_opencode_required_verdict_runner_release.py \ - tests/test_opencode_poll_rate_budget.py \ - tests/test_opencode_poll_self_retirement.py \ - tests/test_opencode_required_verdict_regression.py \ - docs/doctoring/opencode-stale-poll-self-retirement.md \ - docs/product-technical-gap-baseline.md CHANGELOG.md - git diff --cached --check - git commit -m "fix(opencode): release required runner after one verdict read" - git push origin HEAD:fix/opencode-poll-wall-clock-bound From 3f0e03ee40d13708a612aa8b768d03b2a993fc1f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 18:47:00 +0900 Subject: [PATCH 10/59] ci(temp): run PR1706 one-shot repair on fresh workflow identity --- ...temp_pr1706_one_shot_runner_release_v4.yml | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 .github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml diff --git a/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml b/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml new file mode 100644 index 0000000000..267bf63ff9 --- /dev/null +++ b/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml @@ -0,0 +1,88 @@ +name: Temporary PR1706 One-shot Runner Release v4 + +on: + push: + branches: + - fix/opencode-poll-wall-clock-bound + +permissions: + contents: write + +concurrency: + group: temp-pr1706-one-shot-runner-release-v4 + cancel-in-progress: false + +jobs: + repair: + runs-on: ubuntu-24.04 + steps: + - name: Checkout exact trigger head + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + ref: ${{ github.sha }} + fetch-depth: 0 + persist-credentials: true + + - name: Set up Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.14" + + - name: Install locked repository test dependencies + run: | + set -euo pipefail + python3 -m pip install --disable-pip-version-check --require-hashes --only-binary=:all: -r requirements-opencode-review-ci-hashes.txt + + - name: Prove prerequisite regression is RED + run: | + set -euo pipefail + if PYTHONPATH=. python3 -m pytest -q tests/test_opencode_required_verdict_runner_release.py; then + echo '::error::Expected runner-release regression to be RED before production repair.' + exit 1 + fi + + - name: Apply deterministic causal repair + run: | + set -euo pipefail + PYTHONPATH=. python3 scripts/ci/temp_pr1706_one_shot_runner_release.py + + - name: Verify focused GREEN + run: | + set -euo pipefail + PYTHONPATH=. python3 -m pytest -q \ + tests/test_opencode_required_verdict_runner_release.py \ + tests/test_opencode_poll_rate_budget.py \ + tests/test_opencode_poll_self_retirement.py \ + tests/test_opencode_required_verdict_regression.py + python3 -m compileall -q scripts tests + git diff --check + + - name: Verify broader repository suite + run: | + set -euo pipefail + PYTHONPATH=. python3 -m pytest tests -q + git diff --check + + - name: Publish only from unchanged writer head + env: + EXPECTED_HEAD: ${{ github.sha }} + run: | + set -euo pipefail + git fetch origin fix/opencode-poll-wall-clock-bound + live_head="$(git rev-parse origin/fix/opencode-poll-wall-clock-bound)" + if [ "$live_head" != "$EXPECTED_HEAD" ]; then + echo "::error::Writer branch advanced to $live_head; refusing stale publication." + exit 1 + fi + git config user.name "ContextualWisdomLab automation" + git config user.email "automation@users.noreply.github.com" + git add .github/workflows/opencode-review.yml \ + tests/test_opencode_required_verdict_runner_release.py \ + tests/test_opencode_poll_rate_budget.py \ + tests/test_opencode_poll_self_retirement.py \ + tests/test_opencode_required_verdict_regression.py \ + docs/doctoring/opencode-stale-poll-self-retirement.md \ + docs/product-technical-gap-baseline.md CHANGELOG.md + git diff --cached --check + git commit -m "fix(opencode): release required runner after one verdict read" + git push origin HEAD:fix/opencode-poll-wall-clock-bound From 323f002e93b162999185f1bb6e1dbbfdd515597b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 18:49:15 +0900 Subject: [PATCH 11/59] ci(temp): fix PR1706 stale poll-test repair --- .../ci/temp_pr1706_one_shot_runner_release.py | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/scripts/ci/temp_pr1706_one_shot_runner_release.py b/scripts/ci/temp_pr1706_one_shot_runner_release.py index 90fadcf279..397375b89e 100644 --- a/scripts/ci/temp_pr1706_one_shot_runner_release.py +++ b/scripts/ci/temp_pr1706_one_shot_runner_release.py @@ -12,6 +12,7 @@ def replace_once(text: str, old: str, new: str, label: str) -> str: + """Replace one exact stale contract and fail if concurrent edits changed it.""" count = text.count(old) if count != 1: raise SystemExit(f"{label} drifted: expected one exact match, found {count}") @@ -100,25 +101,15 @@ def replace_once(text: str, old: str, new: str, label: str) -> str: ''' WORKFLOW.write_text(before + replacement + end + after, encoding="utf-8") -RATE.write_text('''"""Request-budget regression for one-shot Required OpenCode verdict admission."""\n\nfrom pathlib import Path\n\nWORKFLOW = Path(".github/workflows/opencode-review.yml")\n\ndef _step() -> str:\n text = WORKFLOW.read_text(encoding="utf-8")\n return text.split(" - name: Fail closed without a current-head OpenCode verdict\\n", 1)[1].split("\\n cancel-superseded-opencode-review-runs:\\n", 1)[0]\n\ndef test_one_reviews_read_without_runner_polling() -> None:\n step = _step()\n assert step.count('gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}"') == 1\n assert step.count('gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100"') == 1\n for token in ("while :; do", "poll_interval_seconds", "poll_deadline_epoch", "sleep "):\n assert token not in step\n\ndef test_reviews_read_uses_full_page() -> None:\n step = _step()\n assert "/reviews?per_page=100" in step\n assert "gh api --paginate" in step\n''', encoding="utf-8") +RATE.write_text('''"""Request-budget regression for one-shot Required OpenCode verdict admission."""\n\nfrom pathlib import Path\n\nWORKFLOW = Path(".github/workflows/opencode-review.yml")\n\ndef _step() -> str:\n workflow = WORKFLOW.read_text(encoding="utf-8")\n return workflow.split(" - name: Fail closed without a current-head OpenCode verdict\\n", 1)[1].split("\\n cancel-superseded-opencode-review-runs:\\n", 1)[0]\n\ndef test_admission_uses_one_reviews_read_without_runner_polling() -> None:\n step = _step()\n assert step.count('gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}"') == 1\n assert step.count('gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100"') == 1\n for token in ("while :; do", "poll_interval_seconds", "poll_deadline_epoch", "sleep "):\n assert token not in step\n\ndef test_review_read_keeps_maximum_rest_page_size() -> None:\n step = _step()\n assert "/reviews?per_page=100" in step\n assert "gh api --paginate" in step\n''', encoding="utf-8") -self_text = SELF.read_text(encoding="utf-8") -self_text = replace_once(self_text, '"""Regression contract for stale OpenCode poll self-retirement."""', '"""Regression contract for one-shot Required OpenCode verdict admission."""', "self-retirement module docstring") -self_text = self_text.replace("poll", "admission") -self_text = self_text.replace("Poll", "Admission") -self_text = self_text.replace("POLL", "ADMISSION") -# The behavioral tests execute production shell; keep them but remove retry/deadline assumptions. -for old, new in [ - ('a fresh poll will start for the current head.', 'a fresh required-review run will bind the current head.'), - ('Reviews API read failed 3 consecutive times', 'Reviews API read failed during one-shot current-head verdict admission'), -]: - self_text = self_text.replace(old, new) -SELF.write_text(self_text, encoding="utf-8") +SELF.write_text('''"""Regression contract for one-shot Required OpenCode verdict admission."""\n\nfrom pathlib import Path\n\nWORKFLOW = Path(".github/workflows/opencode-review.yml")\n\ndef _step() -> str:\n workflow = WORKFLOW.read_text(encoding="utf-8")\n return workflow.split(" - name: Fail closed without a current-head OpenCode verdict\\n", 1)[1].split("\\n cancel-superseded-opencode-review-runs:\\n", 1)[0]\n\ndef test_live_state_precedes_review_evidence() -> None:\n step = _step()\n live = 'live_pr="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"'\n reviews = 'reviews="$(gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"'\n assert live in step\n assert reviews in step\n assert step.index(live) < step.index(reviews)\n\ndef test_stale_or_terminal_state_releases_runner_before_review_read() -> None:\n step = _step()\n assert 'if [ "$live_state" = "closed" ]; then' in step\n assert 'if [ "$live_draft" = "true" ]; then' in step\n assert 'if [ "${live_head,,}" != "${HEAD_SHA,,}" ]; then' in step\n assert "fresh required-review run will bind the current head" in step\n\ndef test_transport_failure_is_one_shot_and_fail_closed() -> None:\n step = _step()\n assert "Reviews API read failed during one-shot current-head verdict admission" in step\n assert "exit 1" in step\n assert "while :; do" not in step\n assert "sleep " not in step\n\ndef test_semantic_review_has_no_repository_authored_wait_deadline() -> None:\n step = _step()\n for token in ("poll_deadline_epoch", "max_poll_transport_failures", "timeout 30s", "sleep "):\n assert token not in step\n''', encoding="utf-8") regression = REGRESSION.read_text(encoding="utf-8") for old, new, label in [ (' assert "while :; do" in target_job\n assert \'sleep "$poll_interval_seconds"\' in target_job\n', ' assert "while :; do" not in target_job\n assert "poll_interval_seconds" not in target_job\n assert "poll_deadline_epoch" not in target_job\n assert \'sleep "$poll_interval_seconds"\' not in target_job\n', "legacy poll assertions"), ('a fresh poll will start for the current head.', 'a fresh required-review run will bind the current head.', "moved-head message"), + ('def test_fail_closed_step_still_polls_for_a_non_draft_pr(', 'def test_fail_closed_step_reads_reviews_once_for_a_non_draft_pr(', "poll test name"), ('Reviews API read failed 3 consecutive times', 'Reviews API read failed during one-shot current-head verdict admission', "transport failure message"), ('"""The receipt wake path coexists with the unbounded required review wait."""', '"""The receipt wake path reawakens the fail-closed one-shot required review."""', "receipt wake docstring"), (' assert "while :; do" in required\n', ' assert "while :; do" not in required\n assert "poll_deadline_epoch" not in required\n', "receipt wake loop assertion"), From 83b589bdc6e76af26b214334b851bda24ee1918d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 18:56:39 +0900 Subject: [PATCH 12/59] test(opencode): bind exact-run wake to PR head association --- ..._opencode_required_verdict_runner_release.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tests/test_opencode_required_verdict_runner_release.py b/tests/test_opencode_required_verdict_runner_release.py index cea026508b..282d12b052 100644 --- a/tests/test_opencode_required_verdict_runner_release.py +++ b/tests/test_opencode_required_verdict_runner_release.py @@ -40,7 +40,22 @@ def test_missing_verdict_uses_exact_run_wake_instead_of_runner_polling() -> None assert 'gh api "repos/${GH_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}"' in dispatched assert 'select(.event == "pull_request_target")' in dispatched assert 'select(.path == ".github/workflows/opencode-review.yml")' in dispatched - assert "select(.head_sha == $head)" in dispatched + # pull_request_target run.head_sha is the protected base, not the PR head. + # Exact-run admission must therefore bind the immutable run to the intended + # PR and exact head through the run's pull_requests association. + assert "PR_NUMBER" in dispatched + assert ".pull_requests" in dispatched + assert ".head.sha" in dispatched + assert "select(.head_sha == $head)" not in dispatched + + +def test_admission_transport_reads_are_bounded_without_bounding_model_work() -> None: + """GitHub REST stalls must release the runner without adding a model deadline.""" + required = _fail_closed_script() + assert 'timeout 30 gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}"' in required + assert 'timeout 30 gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100"' in required + for token in ("poll_deadline_epoch", "max_poll_transport_failures", "sleep "): + assert token not in required def test_missing_verdict_fails_after_one_live_read_and_one_review_read(tmp_path: Path) -> None: From 74886f6517c863faacd4111d0ecd805e73eb8788 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 18:57:03 +0900 Subject: [PATCH 13/59] fix(temp): harden PR1706 exact-run wake repair --- .../temp_pr1706_one_shot_runner_release_v2.py | 361 ++++++++++++++++++ 1 file changed, 361 insertions(+) create mode 100644 scripts/ci/temp_pr1706_one_shot_runner_release_v2.py diff --git a/scripts/ci/temp_pr1706_one_shot_runner_release_v2.py b/scripts/ci/temp_pr1706_one_shot_runner_release_v2.py new file mode 100644 index 0000000000..79475d8ccc --- /dev/null +++ b/scripts/ci/temp_pr1706_one_shot_runner_release_v2.py @@ -0,0 +1,361 @@ +"""Finish PR #1706 one-shot repair after the first deterministic driver.""" + +from __future__ import annotations + +from pathlib import Path + + +WORKFLOW = Path(".github/workflows/opencode-review.yml") +DISPATCH = Path(".github/workflows/opencode-review-dispatch.yml") +ACCEPTANCE = Path("tests/test_opencode_required_verdict_runner_release.py") +REGRESSION = Path("tests/test_opencode_required_verdict_regression.py") +SELF = Path("tests/test_opencode_poll_self_retirement.py") +LIVE_DRAFT = Path("tests/test_opencode_live_draft_state_regression.py") +ARCHITECTURE = Path("ARCHITECTURE.md") +DOCTORING = Path("docs/doctoring/opencode-stale-poll-self-retirement.md") +CHANGELOG = Path("CHANGELOG.md") + + +def replace_once(text: str, old: str, new: str, label: str) -> str: + """Replace one exact post-driver fragment and fail closed on drift.""" + count = text.count(old) + if count != 1: + raise SystemExit(f"{label} drifted: expected one exact match, found {count}") + return text.replace(old, new, 1) + + +workflow = WORKFLOW.read_text(encoding="utf-8") +workflow = replace_once( + workflow, + ' live_pr="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"\n', + ' if ! live_pr="$(timeout 30s gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"; then\n' + ' echo "::error::Live pull request API read failed during one-shot current-head verdict admission; failing closed and releasing the runner."\n' + ' exit 1\n' + ' fi\n', + "bounded live PR read", +) +workflow = replace_once( + workflow, + ' if ! reviews="$(gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"; then\n', + ' if ! reviews="$(timeout 30s gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"; then\n', + "bounded Reviews read", +) +WORKFLOW.write_text(workflow, encoding="utf-8") + +# pull_request_target run objects expose the PR head in pull_requests[].head.sha; +# their top-level head_sha is the base commit. Replace the whole wake mutation +# boundary so repository + immutable run id + workflow + PR number + PR head +# are revalidated immediately before rerun-failed-jobs. +dispatch = DISPATCH.read_text(encoding="utf-8") +start = " - name: Wake exact-head required OpenCode workflow\n" +end = "\n - name: Publish repository_dispatch OpenCode status\n" +if dispatch.count(start) != 1 or dispatch.count(end) != 1: + raise SystemExit("OpenCode exact-run wake boundaries drifted") +before, rest = dispatch.split(start, 1) +_old_wake, after = rest.split(end, 1) +wake = r''' - name: Wake exact-head required OpenCode workflow + if: >- + always() + && github.event_name == 'repository_dispatch' + && steps.formal_review_receipt.outcome == 'success' + && needs.validate-pr-metadata.outputs.target_repository != '' + && needs.validate-pr-metadata.outputs.pr_number != '' + && needs.validate-pr-metadata.outputs.head_sha != '' + && github.event.client_payload.required_run_id != '' + env: + GH_TOKEN: ${{ needs.validate-pr-metadata.outputs.target_repository == github.repository && github.token || secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN }} + GH_REPOSITORY: ${{ needs.validate-pr-metadata.outputs.target_repository }} + PR_NUMBER: ${{ needs.validate-pr-metadata.outputs.pr_number }} + PR_HEAD_SHA: ${{ needs.validate-pr-metadata.outputs.head_sha }} + REQUIRED_RUN_ID: ${{ github.event.client_payload.required_run_id }} + WAKE_TOKEN_SOURCE: ${{ needs.validate-pr-metadata.outputs.target_repository == github.repository && 'github-token' || secrets.PR_REVIEW_MERGE_TOKEN != '' && 'PR_REVIEW_MERGE_TOKEN' || secrets.OPENCODE_APPROVE_TOKEN != '' && 'OPENCODE_APPROVE_TOKEN' || 'unavailable' }} + run: | + set -euo pipefail + if [ -z "${GH_TOKEN:-}" ] || [ "$WAKE_TOKEN_SOURCE" = "unavailable" ]; then + echo "::error::Actions-capable wake credential is unavailable. Native runs use github.token; sibling runs require PR_REVIEW_MERGE_TOKEN or OPENCODE_APPROVE_TOKEN." + exit 1 + fi + [[ "$REQUIRED_RUN_ID" =~ ^[1-9][0-9]*$ ]] || { + echo "::error::Required OpenCode run id is missing or non-canonical." + exit 1 + } + [[ "$PR_NUMBER" =~ ^[1-9][0-9]*$ ]] || { + echo "::error::Required OpenCode PR number is missing or non-canonical." + exit 1 + } + [[ "$PR_HEAD_SHA" =~ ^[0-9a-fA-F]{40}$ ]] || { + echo "::error::Required OpenCode PR head SHA is missing or malformed." + exit 1 + } + for attempt in $(seq 1 12); do + run="$(timeout 30s gh api "repos/${GH_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}")" + required_run="$(printf '%s\n' "$run" | jq -r --arg head "$PR_HEAD_SHA" --arg pr "$PR_NUMBER" --argjson run_id "$REQUIRED_RUN_ID" ' + select(.id == $run_id) + | select(.event == "pull_request_target") + | select(.path == ".github/workflows/opencode-review.yml") + | select(any((.pull_requests // [])[]?; + ((.number // 0) | tostring) == $pr + and ((.head.sha // "") | ascii_downcase) == ($head | ascii_downcase))) + | [(.id // ""), (.status // ""), (.conclusion // "")] + | @tsv + ')" + IFS=$'\t' read -r required_run_id required_status required_conclusion <<<"$required_run" + if [ "$required_status" = "completed" ] && [ "$required_conclusion" = "failure" ]; then + gh api -X POST "repos/${GH_REPOSITORY}/actions/runs/${required_run_id}/rerun-failed-jobs" >/dev/null + echo "Re-ran failed jobs for exact-PR/head Required OpenCode Review run ${required_run_id}." + exit 0 + fi + if [ "$required_status" = "completed" ] && [ "$required_conclusion" = "success" ]; then + echo "Exact-PR/head Required OpenCode Review run ${required_run_id} already succeeded." + exit 0 + fi + if [ "$attempt" -lt 12 ]; then + sleep 5 + fi + done + echo "::error::Formal OpenCode receipt exists, but the exact-PR/head required workflow did not reach a rerunnable failed state." + exit 1 +''' +DISPATCH.write_text(before + wake + end + after, encoding="utf-8") + +ACCEPTANCE.write_text(r'''"""Regression coverage for releasing the required OpenCode runner while review continues.""" + +from __future__ import annotations + +import json +import os +import shutil +import subprocess +import textwrap +from pathlib import Path + +import pytest + +WORKFLOW = Path(".github/workflows/opencode-review.yml") +DISPATCH_WORKFLOW = Path(".github/workflows/opencode-review-dispatch.yml") +HEAD_SHA = "a" * 40 + + +def _fail_closed_script() -> str: + """Extract only the real required-verdict admission run block.""" + workflow = WORKFLOW.read_text(encoding="utf-8") + step = workflow.split(" - name: Fail closed without a current-head OpenCode verdict\n", 1)[1] + block = step.split(" run: |\n", 1)[1].split("\n cancel-superseded-opencode-review-runs:\n", 1)[0] + assert "cancel-superseded-opencode-review-runs" not in block + return textwrap.dedent(block) + + +def test_missing_verdict_uses_exact_pr_run_wake_instead_of_runner_polling() -> None: + """A missing verdict fails once and relies on authenticated exact-run wake.""" + required = _fail_closed_script() + dispatched = DISPATCH_WORKFLOW.read_text(encoding="utf-8") + for token in ("while :; do", "poll_interval_seconds", "poll_deadline_epoch", "sleep "): + assert token not in required + assert required.count("timeout 30s gh api") == 2 + assert "rerun-failed-jobs" in dispatched + assert "github.event.client_payload.required_run_id != ''" in dispatched + assert "pull_requests // []" in dispatched + assert "(.number // 0) | tostring" in dispatched + assert ".head.sha // \"\"" in dispatched + assert "select(.head_sha == $head)" not in dispatched + + +def _run_admission(tmp_path: Path, reviews: list[dict[str, object]]) -> tuple[subprocess.CompletedProcess[str], list[str]]: + """Execute production admission against deterministic live/review evidence.""" + bash = shutil.which("bash") + jq = shutil.which("jq") + if bash is None or jq is None: + pytest.skip("bash and jq are required") + fake_gh = tmp_path / "gh" + calls = tmp_path / "calls" + fake_gh.write_text( + "#!/usr/bin/env bash\nset -euo pipefail\nprintf '%s\\n' \"$*\" >>\"$CALLS\"\n" + "if [[ \"$*\" == \"api repos/ContextualWisdomLab/example/pulls/42\" ]]; then printf '%s\\n' \"$LIVE_PR\"; exit 0; fi\n" + "if [[ \"$*\" == \"api --paginate repos/ContextualWisdomLab/example/pulls/42/reviews?per_page=100\" ]]; then printf '%s\\n' \"$REVIEWS\"; exit 0; fi\nexit 97\n", + encoding="utf-8", + ) + fake_gh.chmod(0o755) + fake_timeout = tmp_path / "timeout" + fake_timeout.write_text("#!/usr/bin/env bash\nset -euo pipefail\nshift\nexec \"$@\"\n", encoding="utf-8") + fake_timeout.chmod(0o755) + fake_sleep = tmp_path / "sleep" + fake_sleep.write_text("#!/usr/bin/env bash\necho unexpected-sleep >&2\nexit 91\n", encoding="utf-8") + fake_sleep.chmod(0o755) + result = subprocess.run( + [bash, "-c", _fail_closed_script()], + env={ + **os.environ, + "PATH": f"{tmp_path}{os.pathsep}{os.environ.get('PATH', '')}", + "CALLS": str(calls), + "LIVE_PR": json.dumps({"head": {"sha": HEAD_SHA}, "draft": False, "state": "open"}), + "REVIEWS": json.dumps(reviews), + "GH_TOKEN": "test-token", + "TARGET_REPOSITORY": "ContextualWisdomLab/example", + "PR_NUMBER": "42", + "HEAD_SHA": HEAD_SHA, + "PR_ACTION": "synchronize", + "PR_DRAFT": "false", + }, + text=True, + capture_output=True, + check=False, + ) + return result, calls.read_text(encoding="utf-8").splitlines() + + +def test_missing_verdict_fails_after_one_live_and_one_review_read(tmp_path: Path) -> None: + """No verdict releases the runner immediately with exactly two API reads.""" + result, calls = _run_admission(tmp_path, []) + assert result.returncode == 1, result.stderr + assert "unexpected-sleep" not in result.stderr + assert "No APPROVED or CHANGES_REQUESTED from opencode-agent" in result.stdout + assert calls == [ + "api repos/ContextualWisdomLab/example/pulls/42", + "api --paginate repos/ContextualWisdomLab/example/pulls/42/reviews?per_page=100", + ] + + +def test_formal_verdict_finishes_before_sibling_job_yaml(tmp_path: Path) -> None: + """Successful admission executes only the target shell block.""" + result, calls = _run_admission( + tmp_path, + [{"user": {"login": "opencode-agent[bot]"}, "commit_id": HEAD_SHA, "state": "APPROVED", "body": "Source-backed review."}], + ) + assert result.returncode == 0, result.stderr + assert "Current-head OpenCode verdict: APPROVED." in result.stdout + assert len(calls) == 2 +''', encoding="utf-8") + +SELF.write_text(r'''"""Regression contract for one-shot Required OpenCode verdict admission.""" + +from pathlib import Path + +WORKFLOW = Path(".github/workflows/opencode-review.yml") + + +def _step() -> str: + """Return only the one-shot verdict-admission step.""" + workflow = WORKFLOW.read_text(encoding="utf-8") + return workflow.split(" - name: Fail closed without a current-head OpenCode verdict\n", 1)[1].split("\n cancel-superseded-opencode-review-runs:\n", 1)[0] + + +def test_one_shot_revalidates_live_state_before_reviews() -> None: + """Current authority is established before formal review evidence is read.""" + step = _step() + live = 'timeout 30s gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}"' + reviews = 'timeout 30s gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100"' + assert live in step + assert reviews in step + assert step.index(live) < step.index(reviews) + + +def test_stale_terminal_or_malformed_authority_cannot_reach_review_read_first() -> None: + """Closed, draft, moved-head, and malformed live evidence have explicit branches.""" + step = _step() + assert 'if [ "$live_state" = "closed" ]; then' in step + assert 'if [ "$live_draft" = "true" ]; then' in step + assert 'if [ "${live_head,,}" != "${HEAD_SHA,,}" ]; then' in step + assert "Could not validate live pull request state before verdict admission" in step + assert "fresh required-review run will bind the current head" in step + + +def test_transport_reads_are_bounded_but_model_wait_is_not() -> None: + """GitHub transport gets a bound; semantic model reasoning gets no deadline.""" + step = _step() + assert step.count("timeout 30s gh api") == 2 + for token in ("while :; do", "poll_interval_seconds", "poll_deadline_epoch", "max_poll_transport_failures", "sleep "): + assert token not in step + + +def test_missing_or_unavailable_review_evidence_fails_closed_once() -> None: + """No retry loop can fabricate a verdict or retain the runner.""" + step = _step() + assert "Reviews API read failed during one-shot current-head verdict admission" in step + assert "No APPROVED or CHANGES_REQUESTED from opencode-agent" in step +''', encoding="utf-8") + +regression = REGRESSION.read_text(encoding="utf-8") +regression = replace_once( + regression, + ' return textwrap.dedent(step.split(" run: |\\n", 1)[1])\n', + ' block = step.split(" run: |\\n", 1)[1].split("\\n cancel-superseded-opencode-review-runs:\\n", 1)[0]\n return textwrap.dedent(block)\n', + "bounded verdict test extractor", +) +regression = replace_once( + regression, + ' assert "select(.head_sha == $head)" in dispatched\n', + ' assert "pull_requests // []" in dispatched\n assert "(.number // 0) | tostring" in dispatched\n assert "select(.head_sha == $head)" not in dispatched\n', + "wake identity assertion", +) +old_fixture = '''def required_run(*, run_id: int = 42, head_sha: str = HEAD, path: str = ".github/workflows/opencode-review.yml") -> dict[str, object]: + """Build one realistic single-run GET REST API record. + + Mirrors the real shape a sibling repo sees for a run injected by the org's + required-workflow ruleset (this repo's actual central-hub use case): `name` + is the bare workflow name and `display_title` is a plain PR title, with no + PR number or head SHA embedded in either -- unlike a native same-repo + trigger, where both fields carry the rendered `run-name`. + """ + return { + "id": run_id, + "head_sha": head_sha, + "event": "pull_request_target", + "name": "Required OpenCode Review", + "display_title": "Fix an unrelated example bug", + "path": path, + "workflow_url": ( + "https://api.github.com/repos/ContextualWisdomLab/example" + "/actions/required_workflows/9" + ), + "status": "completed", + "conclusion": "failure", + } +''' +new_fixture = '''def required_run(*, run_id: int = 42, pr_head_sha: str = HEAD, pr_number: int = 1437, path: str = ".github/workflows/opencode-review.yml") -> dict[str, object]: + """Build a pull_request_target run whose top-level head_sha is the base SHA.""" + return { + "id": run_id, + "head_sha": "f" * 40, + "event": "pull_request_target", + "name": "Required OpenCode Review", + "display_title": "Fix an unrelated example bug", + "path": path, + "workflow_url": ( + "https://api.github.com/repos/ContextualWisdomLab/example" + "/actions/required_workflows/9" + ), + "pull_requests": [{"number": pr_number, "head": {"sha": pr_head_sha}}], + "status": "completed", + "conclusion": "failure", + } +''' +regression = replace_once(regression, old_fixture, new_fixture, "pull_request_target run fixture") +regression = replace_once(regression, 'required_run(head_sha="b" * 40)', 'required_run(pr_head_sha="b" * 40)', "mismatched PR-head fixture") +regression = replace_once( + regression, + 'def test_wake_selector_rejects_a_referenced_run_for_a_different_workflow() -> None:\n', + 'def test_wake_selector_rejects_a_referenced_run_for_a_different_pr() -> None:\n """A run id for another PR cannot receive the wake mutation."""\n assert wake_selector(required_run(pr_number=9999)) == ""\n\n\ndef test_wake_selector_rejects_a_referenced_run_for_a_different_workflow() -> None:\n', + "wrong PR wake regression", +) +REGRESSION.write_text(regression, encoding="utf-8") + +live_draft = LIVE_DRAFT.read_text(encoding="utf-8") +live_draft = live_draft.replace("Reviews API read failed 3 consecutive times", "Reviews API read failed during one-shot current-head verdict admission") +LIVE_DRAFT.write_text(live_draft, encoding="utf-8") + +architecture = ARCHITECTURE.read_text(encoding="utf-8") +marker = "### Required OpenCode one-shot verdict admission" +if marker not in architecture: + architecture += '''\n\n### Required OpenCode one-shot verdict admission\n\nThe protected required workflow does not retain a runner while contextual-orchestrator performs semantic review. It validates live PR state once, reads formal review evidence once, and fails closed immediately when no exact-head verdict exists. The authenticated default-branch dispatch later revalidates repository, immutable run id, central workflow path, PR number, and `pull_requests[].head.sha` before `rerun-failed-jobs`; `pull_request_target` top-level `head_sha` is the base commit and is not PR-head authority. GitHub API transport reads are bounded independently from model reasoning, which has no caller wall-clock deadline.\n''' + ARCHITECTURE.write_text(architecture, encoding="utf-8") + +doctoring = DOCTORING.read_text(encoding="utf-8") +marker = "### Exact-run wake identity correction" +if marker not in doctoring: + doctoring += '''\n\n### Exact-run wake identity correction\n\nFor `pull_request_target`, the workflow-run REST object's top-level `head_sha` identifies the base revision. Exact PR-head wake authority therefore uses the immutable run id plus repository API path, event, central workflow path, exact PR number, and `pull_requests[].head.sha`. The dispatcher performs this validation immediately before `rerun-failed-jobs`; mismatched or missing PR metadata fails closed.\n''' + DOCTORING.write_text(doctoring, encoding="utf-8") + +changelog = CHANGELOG.read_text(encoding="utf-8") +note = "- Required OpenCode Review exact-run wake now validates PR number plus `pull_requests[].head.sha` before `rerun-failed-jobs`, because `pull_request_target` workflow-run `head_sha` is the base commit; one-shot GitHub API reads retain 30-second transport bounds without imposing a semantic-review timeout.\n" +if note not in changelog: + CHANGELOG.write_text(note + changelog, encoding="utf-8") From a49b52b3b4bbdde849d2bb133b17a1de7f5c58b9 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 18:57:59 +0900 Subject: [PATCH 14/59] fix(temp): verify and self-retire PR1706 repair --- ...temp_pr1706_one_shot_runner_release_v4.yml | 70 ++++++++++++++----- 1 file changed, 54 insertions(+), 16 deletions(-) diff --git a/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml b/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml index 267bf63ff9..8d15ece670 100644 --- a/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml +++ b/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml @@ -10,18 +10,27 @@ permissions: concurrency: group: temp-pr1706-one-shot-runner-release-v4 - cancel-in-progress: false + cancel-in-progress: true jobs: repair: + if: github.repository == 'ContextualWisdomLab/.github' && github.actor == 'seonghobae' runs-on: ubuntu-24.04 steps: - - name: Checkout exact trigger head + - name: Checkout exact trigger head without persisted mutation credentials uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ github.sha }} fetch-depth: 0 - persist-credentials: true + persist-credentials: false + + - name: Reconcile current protected main without rewriting writer history + run: | + set -euo pipefail + git fetch origin main fix/opencode-poll-wall-clock-bound + git config user.name "ContextualWisdomLab automation" + git config user.email "automation@users.noreply.github.com" + git merge --no-edit origin/main - name: Set up Python uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 @@ -33,11 +42,16 @@ jobs: set -euo pipefail python3 -m pip install --disable-pip-version-check --require-hashes --only-binary=:all: -r requirements-opencode-review-ci-hashes.txt - - name: Prove prerequisite regression is RED + - name: Prove prerequisite regression is genuinely RED run: | set -euo pipefail - if PYTHONPATH=. python3 -m pytest -q tests/test_opencode_required_verdict_runner_release.py; then - echo '::error::Expected runner-release regression to be RED before production repair.' + set +e + red_output="$(PYTHONPATH=. python3 -m pytest -q tests/test_opencode_required_verdict_runner_release.py 2>&1)" + red_status=$? + set -e + printf '%s\n' "$red_output" + if [ "$red_status" -ne 1 ] || ! grep -Eq '(^|[[:space:]])[1-9][0-9]* failed([,[:space:]]|$)' <<<"$red_output"; then + echo "::error::Expected a genuine pytest assertion RED (exit 1 with failed tests), not collection/setup failure or an already-GREEN prerequisite." exit 1 fi @@ -45,44 +59,68 @@ jobs: run: | set -euo pipefail PYTHONPATH=. python3 scripts/ci/temp_pr1706_one_shot_runner_release.py + PYTHONPATH=. python3 scripts/ci/temp_pr1706_one_shot_runner_release_v2.py + git restore --source=HEAD -- docs/product-technical-gap-baseline.md - - name: Verify focused GREEN + - name: Verify focused GREEN and exact-run identity contracts run: | set -euo pipefail PYTHONPATH=. python3 -m pytest -q \ tests/test_opencode_required_verdict_runner_release.py \ tests/test_opencode_poll_rate_budget.py \ tests/test_opencode_poll_self_retirement.py \ - tests/test_opencode_required_verdict_regression.py + tests/test_opencode_required_verdict_regression.py \ + tests/test_opencode_live_draft_state_regression.py python3 -m compileall -q scripts tests git diff --check - - name: Verify broader repository suite + - name: Verify broader repository suite and documentation coverage run: | set -euo pipefail PYTHONPATH=. python3 -m pytest tests -q + if command -v interrogate >/dev/null 2>&1; then + interrogate -c pyproject.toml scripts tests + fi git diff --check - - name: Publish only from unchanged writer head + - name: Remove completed one-shot machinery before publication + run: | + set -euo pipefail + rm -f \ + .github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml \ + scripts/ci/temp_pr1706_one_shot_runner_release.py \ + scripts/ci/temp_pr1706_one_shot_runner_release_v2.py + test ! -e .github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml + test ! -e scripts/ci/temp_pr1706_one_shot_runner_release.py + test ! -e scripts/ci/temp_pr1706_one_shot_runner_release_v2.py + git diff --check + + - name: Publish only from unchanged exact writer head env: - EXPECTED_HEAD: ${{ github.sha }} + EXPECTED_REMOTE_HEAD: ${{ github.sha }} + GITHUB_TOKEN: ${{ github.token }} run: | set -euo pipefail git fetch origin fix/opencode-poll-wall-clock-bound live_head="$(git rev-parse origin/fix/opencode-poll-wall-clock-bound)" - if [ "$live_head" != "$EXPECTED_HEAD" ]; then + if [ "$live_head" != "$EXPECTED_REMOTE_HEAD" ]; then echo "::error::Writer branch advanced to $live_head; refusing stale publication." exit 1 fi - git config user.name "ContextualWisdomLab automation" - git config user.email "automation@users.noreply.github.com" - git add .github/workflows/opencode-review.yml \ + git add -A \ + .github/workflows/opencode-review.yml \ + .github/workflows/opencode-review-dispatch.yml \ + .github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml \ + scripts/ci/temp_pr1706_one_shot_runner_release.py \ + scripts/ci/temp_pr1706_one_shot_runner_release_v2.py \ tests/test_opencode_required_verdict_runner_release.py \ tests/test_opencode_poll_rate_budget.py \ tests/test_opencode_poll_self_retirement.py \ tests/test_opencode_required_verdict_regression.py \ + tests/test_opencode_live_draft_state_regression.py \ docs/doctoring/opencode-stale-poll-self-retirement.md \ - docs/product-technical-gap-baseline.md CHANGELOG.md + ARCHITECTURE.md CHANGELOG.md git diff --cached --check git commit -m "fix(opencode): release required runner after one verdict read" + git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@github.com/ContextualWisdomLab/.github.git" git push origin HEAD:fix/opencode-poll-wall-clock-bound From e80bf509665bb4ad0f7764c08b91a2182f97f21a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 18:58:02 +0900 Subject: [PATCH 15/59] fix(opencode): repair one-shot wake and transport bounds --- .../ci/temp_pr1706_one_shot_runner_release.py | 51 +++++++++++++++---- 1 file changed, 42 insertions(+), 9 deletions(-) diff --git a/scripts/ci/temp_pr1706_one_shot_runner_release.py b/scripts/ci/temp_pr1706_one_shot_runner_release.py index 397375b89e..534155220e 100644 --- a/scripts/ci/temp_pr1706_one_shot_runner_release.py +++ b/scripts/ci/temp_pr1706_one_shot_runner_release.py @@ -6,6 +6,7 @@ WORKFLOW = Path(".github/workflows/opencode-review.yml") +DISPATCH = Path(".github/workflows/opencode-review-dispatch.yml") REGRESSION = Path("tests/test_opencode_required_verdict_regression.py") RATE = Path("tests/test_opencode_poll_rate_budget.py") SELF = Path("tests/test_opencode_poll_self_retirement.py") @@ -44,7 +45,10 @@ def replace_once(text: str, old: str, new: str, label: str) -> str: echo "::error::Missing PR number or head SHA; cannot verify a current-head OpenCode verdict." exit 1 fi - live_pr="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")" + if ! live_pr="$(timeout 30 gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"; then + echo "::error::Live pull-request API read failed or exceeded the bounded transport deadline; failing closed and releasing the runner." + exit 1 + fi live_head="$(printf '%s' "$live_pr" | jq -r '.head.sha // empty')" live_draft="$(printf '%s' "$live_pr" | jq -r 'if (.draft | type) == "boolean" then (.draft | tostring) else empty end')" live_state="$(printf '%s' "$live_pr" | jq -r 'if (.state | type) == "string" then .state else empty end')" @@ -71,8 +75,8 @@ def replace_once(text: str, old: str, new: str, label: str) -> str: if [ "$PR_DRAFT" = "true" ]; then echo "Event draft snapshot is stale; continuing one-shot verdict admission for the live ready PR." fi - if ! reviews="$(gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"; then - echo "::error::Reviews API read failed during one-shot current-head verdict admission; failing closed and releasing the runner." + if ! reviews="$(timeout 30 gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"; then + echo "::error::Reviews API read failed or exceeded the bounded transport deadline during one-shot current-head verdict admission; failing closed and releasing the runner." exit 1 fi verdict="$(printf '%s\n' "$reviews" | jq -r -s --arg sha "$HEAD_SHA" ' @@ -101,16 +105,45 @@ def replace_once(text: str, old: str, new: str, label: str) -> str: ''' WORKFLOW.write_text(before + replacement + end + after, encoding="utf-8") -RATE.write_text('''"""Request-budget regression for one-shot Required OpenCode verdict admission."""\n\nfrom pathlib import Path\n\nWORKFLOW = Path(".github/workflows/opencode-review.yml")\n\ndef _step() -> str:\n workflow = WORKFLOW.read_text(encoding="utf-8")\n return workflow.split(" - name: Fail closed without a current-head OpenCode verdict\\n", 1)[1].split("\\n cancel-superseded-opencode-review-runs:\\n", 1)[0]\n\ndef test_admission_uses_one_reviews_read_without_runner_polling() -> None:\n step = _step()\n assert step.count('gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}"') == 1\n assert step.count('gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100"') == 1\n for token in ("while :; do", "poll_interval_seconds", "poll_deadline_epoch", "sleep "):\n assert token not in step\n\ndef test_review_read_keeps_maximum_rest_page_size() -> None:\n step = _step()\n assert "/reviews?per_page=100" in step\n assert "gh api --paginate" in step\n''', encoding="utf-8") +# A pull_request_target run is created from the protected base, so workflow-run +# head_sha is not the PR head. Bind the immutable Required OpenCode run id to the +# intended PR and exact PR head through the run's pull_requests association. +dispatch = DISPATCH.read_text(encoding="utf-8")ndispatch = replace_once( + dispatch, + ' PR_HEAD_SHA: ${{ needs.validate-pr-metadata.outputs.head_sha }}\n REQUIRED_RUN_ID: ${{ github.event.client_payload.required_run_id }}\n', + ' PR_NUMBER: ${{ needs.validate-pr-metadata.outputs.pr_number }}\n PR_HEAD_SHA: ${{ needs.validate-pr-metadata.outputs.head_sha }}\n REQUIRED_RUN_ID: ${{ github.event.client_payload.required_run_id }}\n', + "required-run wake PR number binding", +) +dispatch = replace_once( + dispatch, + ' [[ "$REQUIRED_RUN_ID" =~ ^[1-9][0-9]*$ ]] || {\n echo "::error::Required OpenCode run id is missing or non-canonical."\n exit 1\n }\n', + ' [[ "$REQUIRED_RUN_ID" =~ ^[1-9][0-9]*$ ]] || {\n echo "::error::Required OpenCode run id is missing or non-canonical."\n exit 1\n }\n [[ "$PR_NUMBER" =~ ^[1-9][0-9]*$ ]] || {\n echo "::error::Required OpenCode PR number is missing or non-canonical."\n exit 1\n }\n', + "required-run wake PR number validation", +) +dispatch = replace_once( + dispatch, + ' run="$(gh api "repos/${GH_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}")"\n', + ' if ! run="$(timeout 30 gh api "repos/${GH_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}")"; then\n echo "::error::Required OpenCode run lookup failed or exceeded the bounded transport deadline."\n exit 1\n fi\n', + "required-run wake bounded transport lookup", +) +dispatch = replace_once( + dispatch, + ' required_run="$(printf \'%s\\n\' "$run" | jq -r --arg head "$PR_HEAD_SHA" --argjson run_id "$REQUIRED_RUN_ID" \'\n select(.id == $run_id)\n | select(.event == "pull_request_target")\n | select(.path == ".github/workflows/opencode-review.yml")\n | select(.head_sha == $head)\n', + ' required_run="$(printf \'%s\\n\' "$run" | jq -r --arg head "$PR_HEAD_SHA" --argjson pr_number "$PR_NUMBER" --argjson run_id "$REQUIRED_RUN_ID" \'\n select(.id == $run_id)\n | select(.event == "pull_request_target")\n | select(.path == ".github/workflows/opencode-review.yml")\n | select(any(.pull_requests[]?; (.number == $pr_number) and ((.head.sha // "" | ascii_downcase) == ($head | ascii_downcase))))\n', + "required-run wake exact PR-head association", +) +DISPATCH.write_text(dispatch, encoding="utf-8") + +RATE.write_text('''"""Request-budget regression for one-shot Required OpenCode verdict admission."""\n\nfrom pathlib import Path\n\nWORKFLOW = Path(".github/workflows/opencode-review.yml")\n\ndef _step() -> str:\n workflow = WORKFLOW.read_text(encoding="utf-8")\n return workflow.split(" - name: Fail closed without a current-head OpenCode verdict\\n", 1)[1].split("\\n cancel-superseded-opencode-review-runs:\\n", 1)[0]\n\ndef test_admission_uses_one_reviews_read_without_runner_polling() -> None:\n step = _step()\n assert step.count('timeout 30 gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}"') == 1\n assert step.count('timeout 30 gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100"') == 1\n for token in ("while :; do", "poll_interval_seconds", "poll_deadline_epoch", "sleep "):\n assert token not in step\n\ndef test_review_read_keeps_maximum_rest_page_size() -> None:\n step = _step()\n assert "/reviews?per_page=100" in step\n assert "gh api --paginate" in step\n''', encoding="utf-8") -SELF.write_text('''"""Regression contract for one-shot Required OpenCode verdict admission."""\n\nfrom pathlib import Path\n\nWORKFLOW = Path(".github/workflows/opencode-review.yml")\n\ndef _step() -> str:\n workflow = WORKFLOW.read_text(encoding="utf-8")\n return workflow.split(" - name: Fail closed without a current-head OpenCode verdict\\n", 1)[1].split("\\n cancel-superseded-opencode-review-runs:\\n", 1)[0]\n\ndef test_live_state_precedes_review_evidence() -> None:\n step = _step()\n live = 'live_pr="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"'\n reviews = 'reviews="$(gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"'\n assert live in step\n assert reviews in step\n assert step.index(live) < step.index(reviews)\n\ndef test_stale_or_terminal_state_releases_runner_before_review_read() -> None:\n step = _step()\n assert 'if [ "$live_state" = "closed" ]; then' in step\n assert 'if [ "$live_draft" = "true" ]; then' in step\n assert 'if [ "${live_head,,}" != "${HEAD_SHA,,}" ]; then' in step\n assert "fresh required-review run will bind the current head" in step\n\ndef test_transport_failure_is_one_shot_and_fail_closed() -> None:\n step = _step()\n assert "Reviews API read failed during one-shot current-head verdict admission" in step\n assert "exit 1" in step\n assert "while :; do" not in step\n assert "sleep " not in step\n\ndef test_semantic_review_has_no_repository_authored_wait_deadline() -> None:\n step = _step()\n for token in ("poll_deadline_epoch", "max_poll_transport_failures", "timeout 30s", "sleep "):\n assert token not in step\n''', encoding="utf-8") +SELF.write_text('''"""Regression contract for one-shot Required OpenCode verdict admission."""\n\nfrom pathlib import Path\n\nWORKFLOW = Path(".github/workflows/opencode-review.yml")\n\ndef _step() -> str:\n workflow = WORKFLOW.read_text(encoding="utf-8")\n return workflow.split(" - name: Fail closed without a current-head OpenCode verdict\\n", 1)[1].split("\\n cancel-superseded-opencode-review-runs:\\n", 1)[0]\n\ndef test_live_state_precedes_review_evidence() -> None:\n step = _step()\n live = 'live_pr="$(timeout 30 gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"'\n reviews = 'reviews="$(timeout 30 gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"'\n assert live in step\n assert reviews in step\n assert step.index(live) < step.index(reviews)\n\ndef test_stale_or_terminal_state_releases_runner_before_review_read() -> None:\n step = _step()\n assert 'if [ "$live_state" = "closed" ]; then' in step\n assert 'if [ "$live_draft" = "true" ]; then' in step\n assert 'if [ "${live_head,,}" != "${HEAD_SHA,,}" ]; then' in step\n assert "fresh required-review run will bind the current head" in step\n\ndef test_transport_failure_is_one_shot_bounded_and_fail_closed() -> None:\n step = _step()\n assert "Reviews API read failed or exceeded the bounded transport deadline" in step\n assert step.count("timeout 30 gh api") == 2\n assert "exit 1" in step\n assert "while :; do" not in step\n assert "sleep " not in step\n\ndef test_semantic_review_has_no_repository_authored_wait_deadline() -> None:\n step = _step()\n for token in ("poll_deadline_epoch", "max_poll_transport_failures", "sleep "):\n assert token not in step\n''', encoding="utf-8") regression = REGRESSION.read_text(encoding="utf-8") for old, new, label in [ (' assert "while :; do" in target_job\n assert \'sleep "$poll_interval_seconds"\' in target_job\n', ' assert "while :; do" not in target_job\n assert "poll_interval_seconds" not in target_job\n assert "poll_deadline_epoch" not in target_job\n assert \'sleep "$poll_interval_seconds"\' not in target_job\n', "legacy poll assertions"), ('a fresh poll will start for the current head.', 'a fresh required-review run will bind the current head.', "moved-head message"), ('def test_fail_closed_step_still_polls_for_a_non_draft_pr(', 'def test_fail_closed_step_reads_reviews_once_for_a_non_draft_pr(', "poll test name"), - ('Reviews API read failed 3 consecutive times', 'Reviews API read failed during one-shot current-head verdict admission', "transport failure message"), + ('Reviews API read failed 3 consecutive times', 'Reviews API read failed or exceeded the bounded transport deadline during one-shot current-head verdict admission', "transport failure message"), ('"""The receipt wake path coexists with the unbounded required review wait."""', '"""The receipt wake path reawakens the fail-closed one-shot required review."""', "receipt wake docstring"), (' assert "while :; do" in required\n', ' assert "while :; do" not in required\n assert "poll_deadline_epoch" not in required\n', "receipt wake loop assertion"), ]: @@ -121,18 +154,18 @@ def replace_once(text: str, old: str, new: str, label: str) -> str: text = baseline.read_text(encoding="utf-8") marker = "### OPENCODE-ONE-SHOT-RUNNER-RELEASE-2026-09-02" if marker not in text: - text += f'''\n\n{marker}\n- Owner: `ContextualWisdomLab/.github` Required OpenCode Review control plane.\n- RCA: required-verdict occupied a runner while asynchronous model work continued, using repository-authored polling/retry/wall-clock allocation despite an authenticated exact-run wake contract.\n- GREEN: one live PR read plus one Reviews read; missing/unavailable exact-head verdict fails closed immediately and dispatch wakes the exact failed run after the verdict. Model reasoning receives no caller wall-clock timeout.\n- Regression: `tests/test_opencode_required_verdict_runner_release.py` plus one-shot request/state and dispatch-wake contracts.\n''' + text += f'''\n\n{marker}\n- Owner: `ContextualWisdomLab/.github` Required OpenCode Review control plane.\n- RCA: required-verdict occupied a runner while asynchronous model work continued, using repository-authored polling/retry/wall-clock allocation despite an authenticated exact-run wake contract.\n- GREEN: one bounded live PR transport read plus one bounded Reviews transport read; missing/unavailable exact-head verdict fails closed immediately and dispatch wakes the exact failed run after the verdict. Model reasoning receives no caller wall-clock timeout.\n- Exact-run binding: `pull_request_target` workflow-run `head_sha` is the protected base, so wake validation binds immutable run id to the intended PR number and exact PR head through `workflow_run.pull_requests[]`.\n- Regression: `tests/test_opencode_required_verdict_runner_release.py` plus one-shot request/state and dispatch-wake contracts.\n''' baseline.write_text(text, encoding="utf-8") doctoring = Path("docs/doctoring/opencode-stale-poll-self-retirement.md") text = doctoring.read_text(encoding="utf-8") marker = "## 2026-09-02 one-shot runner-release supersession" if marker not in text: - text += f'''\n\n{marker}\n\nThe required-verdict job performs one authoritative live-PR read followed by at most one paginated Reviews read. Missing or unavailable exact-head verdict evidence fails closed immediately and releases the runner. Authenticated `opencode-review-dispatch.yml` wakes the exact failed run via `rerun-failed-jobs` when the formal verdict arrives; no repository-authored polling interval, retry count, or wall-clock deadline bounds model work.\n''' + text += f'''\n\n{marker}\n\nThe required-verdict job performs one bounded authoritative live-PR transport read followed by at most one bounded paginated Reviews transport read. Missing or unavailable exact-head verdict evidence fails closed immediately and releases the runner. Authenticated `opencode-review-dispatch.yml` wakes the exact failed run via `rerun-failed-jobs` when the formal verdict arrives; no repository-authored polling interval, retry count, or model wall-clock deadline bounds semantic review. Because `pull_request_target` run `head_sha` is the protected base, the wake path validates the immutable run id against its `pull_requests[]` PR-number/exact-head association instead.\n''' doctoring.write_text(text, encoding="utf-8") changelog = Path("CHANGELOG.md") text = changelog.read_text(encoding="utf-8") -note = "- Required OpenCode Review now releases its runner after one exact-head verdict admission read and relies on authenticated exact-run dispatch wake instead of repository-authored polling, retry-count, or waiting deadlines.\n" +note = "- Required OpenCode Review now releases its runner after one exact-head verdict admission read and wakes the immutable failed run through its PR/exact-head association instead of repository-authored polling or `pull_request_target` base-SHA matching.\n" if note not in text: changelog.write_text(note + text, encoding="utf-8") From afd936a23e5d5fe6bc2d04c3f513942a89e9912d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 18:59:08 +0900 Subject: [PATCH 16/59] fix(opencode): repair deterministic publisher syntax --- scripts/ci/temp_pr1706_one_shot_runner_release.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/scripts/ci/temp_pr1706_one_shot_runner_release.py b/scripts/ci/temp_pr1706_one_shot_runner_release.py index 534155220e..68a8d556bd 100644 --- a/scripts/ci/temp_pr1706_one_shot_runner_release.py +++ b/scripts/ci/temp_pr1706_one_shot_runner_release.py @@ -105,10 +105,11 @@ def replace_once(text: str, old: str, new: str, label: str) -> str: ''' WORKFLOW.write_text(before + replacement + end + after, encoding="utf-8") -# A pull_request_target run is created from the protected base, so workflow-run -# head_sha is not the PR head. Bind the immutable Required OpenCode run id to the -# intended PR and exact PR head through the run's pull_requests association. -dispatch = DISPATCH.read_text(encoding="utf-8")ndispatch = replace_once( +# pull_request_target workflow-run head_sha is the protected base. Bind the +# immutable required run to the intended PR and exact PR head through the run's +# pull_requests association instead of comparing run.head_sha to the PR head. +dispatch = DISPATCH.read_text(encoding="utf-8") +dispatch = replace_once( dispatch, ' PR_HEAD_SHA: ${{ needs.validate-pr-metadata.outputs.head_sha }}\n REQUIRED_RUN_ID: ${{ github.event.client_payload.required_run_id }}\n', ' PR_NUMBER: ${{ needs.validate-pr-metadata.outputs.pr_number }}\n PR_HEAD_SHA: ${{ needs.validate-pr-metadata.outputs.head_sha }}\n REQUIRED_RUN_ID: ${{ github.event.client_payload.required_run_id }}\n', From 7c50debedeb3ac81b5791f4df7c7c76f58f70b8f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 19:00:54 +0900 Subject: [PATCH 17/59] fix(opencode): preserve stacked PR1706 repair responsibilities --- .../ci/temp_pr1706_one_shot_runner_release.py | 52 ++++--------------- 1 file changed, 9 insertions(+), 43 deletions(-) diff --git a/scripts/ci/temp_pr1706_one_shot_runner_release.py b/scripts/ci/temp_pr1706_one_shot_runner_release.py index 68a8d556bd..397375b89e 100644 --- a/scripts/ci/temp_pr1706_one_shot_runner_release.py +++ b/scripts/ci/temp_pr1706_one_shot_runner_release.py @@ -6,7 +6,6 @@ WORKFLOW = Path(".github/workflows/opencode-review.yml") -DISPATCH = Path(".github/workflows/opencode-review-dispatch.yml") REGRESSION = Path("tests/test_opencode_required_verdict_regression.py") RATE = Path("tests/test_opencode_poll_rate_budget.py") SELF = Path("tests/test_opencode_poll_self_retirement.py") @@ -45,10 +44,7 @@ def replace_once(text: str, old: str, new: str, label: str) -> str: echo "::error::Missing PR number or head SHA; cannot verify a current-head OpenCode verdict." exit 1 fi - if ! live_pr="$(timeout 30 gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"; then - echo "::error::Live pull-request API read failed or exceeded the bounded transport deadline; failing closed and releasing the runner." - exit 1 - fi + live_pr="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")" live_head="$(printf '%s' "$live_pr" | jq -r '.head.sha // empty')" live_draft="$(printf '%s' "$live_pr" | jq -r 'if (.draft | type) == "boolean" then (.draft | tostring) else empty end')" live_state="$(printf '%s' "$live_pr" | jq -r 'if (.state | type) == "string" then .state else empty end')" @@ -75,8 +71,8 @@ def replace_once(text: str, old: str, new: str, label: str) -> str: if [ "$PR_DRAFT" = "true" ]; then echo "Event draft snapshot is stale; continuing one-shot verdict admission for the live ready PR." fi - if ! reviews="$(timeout 30 gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"; then - echo "::error::Reviews API read failed or exceeded the bounded transport deadline during one-shot current-head verdict admission; failing closed and releasing the runner." + if ! reviews="$(gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"; then + echo "::error::Reviews API read failed during one-shot current-head verdict admission; failing closed and releasing the runner." exit 1 fi verdict="$(printf '%s\n' "$reviews" | jq -r -s --arg sha "$HEAD_SHA" ' @@ -105,46 +101,16 @@ def replace_once(text: str, old: str, new: str, label: str) -> str: ''' WORKFLOW.write_text(before + replacement + end + after, encoding="utf-8") -# pull_request_target workflow-run head_sha is the protected base. Bind the -# immutable required run to the intended PR and exact PR head through the run's -# pull_requests association instead of comparing run.head_sha to the PR head. -dispatch = DISPATCH.read_text(encoding="utf-8") -dispatch = replace_once( - dispatch, - ' PR_HEAD_SHA: ${{ needs.validate-pr-metadata.outputs.head_sha }}\n REQUIRED_RUN_ID: ${{ github.event.client_payload.required_run_id }}\n', - ' PR_NUMBER: ${{ needs.validate-pr-metadata.outputs.pr_number }}\n PR_HEAD_SHA: ${{ needs.validate-pr-metadata.outputs.head_sha }}\n REQUIRED_RUN_ID: ${{ github.event.client_payload.required_run_id }}\n', - "required-run wake PR number binding", -) -dispatch = replace_once( - dispatch, - ' [[ "$REQUIRED_RUN_ID" =~ ^[1-9][0-9]*$ ]] || {\n echo "::error::Required OpenCode run id is missing or non-canonical."\n exit 1\n }\n', - ' [[ "$REQUIRED_RUN_ID" =~ ^[1-9][0-9]*$ ]] || {\n echo "::error::Required OpenCode run id is missing or non-canonical."\n exit 1\n }\n [[ "$PR_NUMBER" =~ ^[1-9][0-9]*$ ]] || {\n echo "::error::Required OpenCode PR number is missing or non-canonical."\n exit 1\n }\n', - "required-run wake PR number validation", -) -dispatch = replace_once( - dispatch, - ' run="$(gh api "repos/${GH_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}")"\n', - ' if ! run="$(timeout 30 gh api "repos/${GH_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}")"; then\n echo "::error::Required OpenCode run lookup failed or exceeded the bounded transport deadline."\n exit 1\n fi\n', - "required-run wake bounded transport lookup", -) -dispatch = replace_once( - dispatch, - ' required_run="$(printf \'%s\\n\' "$run" | jq -r --arg head "$PR_HEAD_SHA" --argjson run_id "$REQUIRED_RUN_ID" \'\n select(.id == $run_id)\n | select(.event == "pull_request_target")\n | select(.path == ".github/workflows/opencode-review.yml")\n | select(.head_sha == $head)\n', - ' required_run="$(printf \'%s\\n\' "$run" | jq -r --arg head "$PR_HEAD_SHA" --argjson pr_number "$PR_NUMBER" --argjson run_id "$REQUIRED_RUN_ID" \'\n select(.id == $run_id)\n | select(.event == "pull_request_target")\n | select(.path == ".github/workflows/opencode-review.yml")\n | select(any(.pull_requests[]?; (.number == $pr_number) and ((.head.sha // "" | ascii_downcase) == ($head | ascii_downcase))))\n', - "required-run wake exact PR-head association", -) -DISPATCH.write_text(dispatch, encoding="utf-8") - -RATE.write_text('''"""Request-budget regression for one-shot Required OpenCode verdict admission."""\n\nfrom pathlib import Path\n\nWORKFLOW = Path(".github/workflows/opencode-review.yml")\n\ndef _step() -> str:\n workflow = WORKFLOW.read_text(encoding="utf-8")\n return workflow.split(" - name: Fail closed without a current-head OpenCode verdict\\n", 1)[1].split("\\n cancel-superseded-opencode-review-runs:\\n", 1)[0]\n\ndef test_admission_uses_one_reviews_read_without_runner_polling() -> None:\n step = _step()\n assert step.count('timeout 30 gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}"') == 1\n assert step.count('timeout 30 gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100"') == 1\n for token in ("while :; do", "poll_interval_seconds", "poll_deadline_epoch", "sleep "):\n assert token not in step\n\ndef test_review_read_keeps_maximum_rest_page_size() -> None:\n step = _step()\n assert "/reviews?per_page=100" in step\n assert "gh api --paginate" in step\n''', encoding="utf-8") +RATE.write_text('''"""Request-budget regression for one-shot Required OpenCode verdict admission."""\n\nfrom pathlib import Path\n\nWORKFLOW = Path(".github/workflows/opencode-review.yml")\n\ndef _step() -> str:\n workflow = WORKFLOW.read_text(encoding="utf-8")\n return workflow.split(" - name: Fail closed without a current-head OpenCode verdict\\n", 1)[1].split("\\n cancel-superseded-opencode-review-runs:\\n", 1)[0]\n\ndef test_admission_uses_one_reviews_read_without_runner_polling() -> None:\n step = _step()\n assert step.count('gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}"') == 1\n assert step.count('gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100"') == 1\n for token in ("while :; do", "poll_interval_seconds", "poll_deadline_epoch", "sleep "):\n assert token not in step\n\ndef test_review_read_keeps_maximum_rest_page_size() -> None:\n step = _step()\n assert "/reviews?per_page=100" in step\n assert "gh api --paginate" in step\n''', encoding="utf-8") -SELF.write_text('''"""Regression contract for one-shot Required OpenCode verdict admission."""\n\nfrom pathlib import Path\n\nWORKFLOW = Path(".github/workflows/opencode-review.yml")\n\ndef _step() -> str:\n workflow = WORKFLOW.read_text(encoding="utf-8")\n return workflow.split(" - name: Fail closed without a current-head OpenCode verdict\\n", 1)[1].split("\\n cancel-superseded-opencode-review-runs:\\n", 1)[0]\n\ndef test_live_state_precedes_review_evidence() -> None:\n step = _step()\n live = 'live_pr="$(timeout 30 gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"'\n reviews = 'reviews="$(timeout 30 gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"'\n assert live in step\n assert reviews in step\n assert step.index(live) < step.index(reviews)\n\ndef test_stale_or_terminal_state_releases_runner_before_review_read() -> None:\n step = _step()\n assert 'if [ "$live_state" = "closed" ]; then' in step\n assert 'if [ "$live_draft" = "true" ]; then' in step\n assert 'if [ "${live_head,,}" != "${HEAD_SHA,,}" ]; then' in step\n assert "fresh required-review run will bind the current head" in step\n\ndef test_transport_failure_is_one_shot_bounded_and_fail_closed() -> None:\n step = _step()\n assert "Reviews API read failed or exceeded the bounded transport deadline" in step\n assert step.count("timeout 30 gh api") == 2\n assert "exit 1" in step\n assert "while :; do" not in step\n assert "sleep " not in step\n\ndef test_semantic_review_has_no_repository_authored_wait_deadline() -> None:\n step = _step()\n for token in ("poll_deadline_epoch", "max_poll_transport_failures", "sleep "):\n assert token not in step\n''', encoding="utf-8") +SELF.write_text('''"""Regression contract for one-shot Required OpenCode verdict admission."""\n\nfrom pathlib import Path\n\nWORKFLOW = Path(".github/workflows/opencode-review.yml")\n\ndef _step() -> str:\n workflow = WORKFLOW.read_text(encoding="utf-8")\n return workflow.split(" - name: Fail closed without a current-head OpenCode verdict\\n", 1)[1].split("\\n cancel-superseded-opencode-review-runs:\\n", 1)[0]\n\ndef test_live_state_precedes_review_evidence() -> None:\n step = _step()\n live = 'live_pr="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"'\n reviews = 'reviews="$(gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"'\n assert live in step\n assert reviews in step\n assert step.index(live) < step.index(reviews)\n\ndef test_stale_or_terminal_state_releases_runner_before_review_read() -> None:\n step = _step()\n assert 'if [ "$live_state" = "closed" ]; then' in step\n assert 'if [ "$live_draft" = "true" ]; then' in step\n assert 'if [ "${live_head,,}" != "${HEAD_SHA,,}" ]; then' in step\n assert "fresh required-review run will bind the current head" in step\n\ndef test_transport_failure_is_one_shot_and_fail_closed() -> None:\n step = _step()\n assert "Reviews API read failed during one-shot current-head verdict admission" in step\n assert "exit 1" in step\n assert "while :; do" not in step\n assert "sleep " not in step\n\ndef test_semantic_review_has_no_repository_authored_wait_deadline() -> None:\n step = _step()\n for token in ("poll_deadline_epoch", "max_poll_transport_failures", "timeout 30s", "sleep "):\n assert token not in step\n''', encoding="utf-8") regression = REGRESSION.read_text(encoding="utf-8") for old, new, label in [ (' assert "while :; do" in target_job\n assert \'sleep "$poll_interval_seconds"\' in target_job\n', ' assert "while :; do" not in target_job\n assert "poll_interval_seconds" not in target_job\n assert "poll_deadline_epoch" not in target_job\n assert \'sleep "$poll_interval_seconds"\' not in target_job\n', "legacy poll assertions"), ('a fresh poll will start for the current head.', 'a fresh required-review run will bind the current head.', "moved-head message"), ('def test_fail_closed_step_still_polls_for_a_non_draft_pr(', 'def test_fail_closed_step_reads_reviews_once_for_a_non_draft_pr(', "poll test name"), - ('Reviews API read failed 3 consecutive times', 'Reviews API read failed or exceeded the bounded transport deadline during one-shot current-head verdict admission', "transport failure message"), + ('Reviews API read failed 3 consecutive times', 'Reviews API read failed during one-shot current-head verdict admission', "transport failure message"), ('"""The receipt wake path coexists with the unbounded required review wait."""', '"""The receipt wake path reawakens the fail-closed one-shot required review."""', "receipt wake docstring"), (' assert "while :; do" in required\n', ' assert "while :; do" not in required\n assert "poll_deadline_epoch" not in required\n', "receipt wake loop assertion"), ]: @@ -155,18 +121,18 @@ def replace_once(text: str, old: str, new: str, label: str) -> str: text = baseline.read_text(encoding="utf-8") marker = "### OPENCODE-ONE-SHOT-RUNNER-RELEASE-2026-09-02" if marker not in text: - text += f'''\n\n{marker}\n- Owner: `ContextualWisdomLab/.github` Required OpenCode Review control plane.\n- RCA: required-verdict occupied a runner while asynchronous model work continued, using repository-authored polling/retry/wall-clock allocation despite an authenticated exact-run wake contract.\n- GREEN: one bounded live PR transport read plus one bounded Reviews transport read; missing/unavailable exact-head verdict fails closed immediately and dispatch wakes the exact failed run after the verdict. Model reasoning receives no caller wall-clock timeout.\n- Exact-run binding: `pull_request_target` workflow-run `head_sha` is the protected base, so wake validation binds immutable run id to the intended PR number and exact PR head through `workflow_run.pull_requests[]`.\n- Regression: `tests/test_opencode_required_verdict_runner_release.py` plus one-shot request/state and dispatch-wake contracts.\n''' + text += f'''\n\n{marker}\n- Owner: `ContextualWisdomLab/.github` Required OpenCode Review control plane.\n- RCA: required-verdict occupied a runner while asynchronous model work continued, using repository-authored polling/retry/wall-clock allocation despite an authenticated exact-run wake contract.\n- GREEN: one live PR read plus one Reviews read; missing/unavailable exact-head verdict fails closed immediately and dispatch wakes the exact failed run after the verdict. Model reasoning receives no caller wall-clock timeout.\n- Regression: `tests/test_opencode_required_verdict_runner_release.py` plus one-shot request/state and dispatch-wake contracts.\n''' baseline.write_text(text, encoding="utf-8") doctoring = Path("docs/doctoring/opencode-stale-poll-self-retirement.md") text = doctoring.read_text(encoding="utf-8") marker = "## 2026-09-02 one-shot runner-release supersession" if marker not in text: - text += f'''\n\n{marker}\n\nThe required-verdict job performs one bounded authoritative live-PR transport read followed by at most one bounded paginated Reviews transport read. Missing or unavailable exact-head verdict evidence fails closed immediately and releases the runner. Authenticated `opencode-review-dispatch.yml` wakes the exact failed run via `rerun-failed-jobs` when the formal verdict arrives; no repository-authored polling interval, retry count, or model wall-clock deadline bounds semantic review. Because `pull_request_target` run `head_sha` is the protected base, the wake path validates the immutable run id against its `pull_requests[]` PR-number/exact-head association instead.\n''' + text += f'''\n\n{marker}\n\nThe required-verdict job performs one authoritative live-PR read followed by at most one paginated Reviews read. Missing or unavailable exact-head verdict evidence fails closed immediately and releases the runner. Authenticated `opencode-review-dispatch.yml` wakes the exact failed run via `rerun-failed-jobs` when the formal verdict arrives; no repository-authored polling interval, retry count, or wall-clock deadline bounds model work.\n''' doctoring.write_text(text, encoding="utf-8") changelog = Path("CHANGELOG.md") text = changelog.read_text(encoding="utf-8") -note = "- Required OpenCode Review now releases its runner after one exact-head verdict admission read and wakes the immutable failed run through its PR/exact-head association instead of repository-authored polling or `pull_request_target` base-SHA matching.\n" +note = "- Required OpenCode Review now releases its runner after one exact-head verdict admission read and relies on authenticated exact-run dispatch wake instead of repository-authored polling, retry-count, or waiting deadlines.\n" if note not in text: changelog.write_text(note + text, encoding="utf-8") From f103b1f83d21a580c8f28b163a097427df49d5a1 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 19:01:32 +0900 Subject: [PATCH 18/59] fix(temp): stop repaired PR1706 head from retriggering RED publisher --- .github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml b/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml index 8d15ece670..718d3aba86 100644 --- a/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml +++ b/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml @@ -4,6 +4,8 @@ on: push: branches: - fix/opencode-poll-wall-clock-bound + paths: + - .github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml permissions: contents: write From 4a36b89d51951c604562608c3da43edec96b8db1 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 19:02:08 +0900 Subject: [PATCH 19/59] fix(opencode): least-privilege PR1706 publisher --- ...temp_pr1706_one_shot_runner_release_v4.yml | 32 ++++++++++++++++--- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml b/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml index 718d3aba86..d2848fed08 100644 --- a/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml +++ b/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml @@ -8,7 +8,7 @@ on: - .github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml permissions: - contents: write + contents: read concurrency: group: temp-pr1706-one-shot-runner-release-v4 @@ -62,6 +62,7 @@ jobs: set -euo pipefail PYTHONPATH=. python3 scripts/ci/temp_pr1706_one_shot_runner_release.py PYTHONPATH=. python3 scripts/ci/temp_pr1706_one_shot_runner_release_v2.py + # Product baseline has its own single-writer lane; do not compete with it here. git restore --source=HEAD -- docs/product-technical-gap-baseline.md - name: Verify focused GREEN and exact-run identity contracts @@ -85,7 +86,7 @@ jobs: fi git diff --check - - name: Remove completed one-shot machinery before publication + - name: Remove completed one-shot machinery and verify publication scope run: | set -euo pipefail rm -f \ @@ -96,13 +97,34 @@ jobs: test ! -e scripts/ci/temp_pr1706_one_shot_runner_release.py test ! -e scripts/ci/temp_pr1706_one_shot_runner_release_v2.py git diff --check + while IFS= read -r changed; do + case "$changed" in + .github/workflows/opencode-review.yml|\ + .github/workflows/opencode-review-dispatch.yml|\ + .github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml|\ + scripts/ci/temp_pr1706_one_shot_runner_release.py|\ + scripts/ci/temp_pr1706_one_shot_runner_release_v2.py|\ + tests/test_opencode_required_verdict_runner_release.py|\ + tests/test_opencode_poll_rate_budget.py|\ + tests/test_opencode_poll_self_retirement.py|\ + tests/test_opencode_required_verdict_regression.py|\ + tests/test_opencode_live_draft_state_regression.py|\ + docs/doctoring/opencode-stale-poll-self-retirement.md|\ + ARCHITECTURE.md|CHANGELOG.md) ;; + *) echo "::error::Unexpected publication path: $changed"; exit 1 ;; + esac + done < <(git diff --name-only HEAD) - - name: Publish only from unchanged exact writer head + - name: Publish only from unchanged exact writer head with a workflow-triggering credential env: EXPECTED_REMOTE_HEAD: ${{ github.sha }} - GITHUB_TOKEN: ${{ github.token }} + PUSH_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN }} run: | set -euo pipefail + if [ -z "${PUSH_TOKEN:-}" ]; then + echo "::error::Publication requires PR_REVIEW_MERGE_TOKEN or OPENCODE_APPROVE_TOKEN so the new exact head can start required workflows; github.token fallback is intentionally refused." + exit 1 + fi git fetch origin fix/opencode-poll-wall-clock-bound live_head="$(git rev-parse origin/fix/opencode-poll-wall-clock-bound)" if [ "$live_head" != "$EXPECTED_REMOTE_HEAD" ]; then @@ -124,5 +146,5 @@ jobs: ARCHITECTURE.md CHANGELOG.md git diff --cached --check git commit -m "fix(opencode): release required runner after one verdict read" - git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@github.com/ContextualWisdomLab/.github.git" + git remote set-url origin "https://x-access-token:${PUSH_TOKEN}@github.com/ContextualWisdomLab/.github.git" git push origin HEAD:fix/opencode-poll-wall-clock-bound From b4951488b7134b7da7a73f4a6fecb30e4dd7d741 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 19:08:49 +0900 Subject: [PATCH 20/59] ci(opencode): move PR1706 owner repair to slim runner --- .github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml b/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml index d2848fed08..a118c595e8 100644 --- a/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml +++ b/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml @@ -17,7 +17,7 @@ concurrency: jobs: repair: if: github.repository == 'ContextualWisdomLab/.github' && github.actor == 'seonghobae' - runs-on: ubuntu-24.04 + runs-on: ubuntu-slim steps: - name: Checkout exact trigger head without persisted mutation credentials uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 From d5c2dfd7edaae5a3894b41f06e007d47a8d6d6c5 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 19:16:15 +0900 Subject: [PATCH 21/59] fix(opencode): scope one-shot live PR repair --- ...temp_pr1706_one_shot_runner_release_v4.yml | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml b/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml index a118c595e8..905bae4505 100644 --- a/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml +++ b/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml @@ -61,6 +61,44 @@ jobs: run: | set -euo pipefail PYTHONPATH=. python3 scripts/ci/temp_pr1706_one_shot_runner_release.py + python3 - <<'PY' + from pathlib import Path + + driver = Path("scripts/ci/temp_pr1706_one_shot_runner_release_v2.py") + text = driver.read_text(encoding="utf-8") + start_token = 'workflow = WORKFLOW.read_text(encoding="utf-8")\n' + end_token = 'WORKFLOW.write_text(workflow, encoding="utf-8")\n' + start = text.find(start_token) + end_start = text.find(end_token, start + len(start_token)) + if start < 0 or end_start < 0: + raise SystemExit("v2 bounded-transport repair block drifted") + end = end_start + len(end_token) + replacement = r'''workflow = WORKFLOW.read_text(encoding="utf-8") + step_start = " - name: Fail closed without a current-head OpenCode verdict\n" + step_end = "\n cancel-superseded-opencode-review-runs:\n" + if workflow.count(step_start) != 1 or workflow.count(step_end) != 1: + raise SystemExit("OpenCode required-verdict step boundaries drifted before bounded transport repair") + before, rest = workflow.split(step_start, 1) + target, after = rest.split(step_end, 1) + target = replace_once( + target, + ' live_pr="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"\n', + ' if ! live_pr="$(timeout 30s gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"; then\n' + ' echo "::error::Live pull request API read failed during one-shot current-head verdict admission; failing closed and releasing the runner."\n' + ' exit 1\n' + ' fi\n', + "bounded live PR read", + ) + target = replace_once( + target, + ' if ! reviews="$(gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"; then\n', + ' if ! reviews="$(timeout 30s gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"; then\n', + "bounded Reviews read", + ) + WORKFLOW.write_text(before + step_start + target + step_end + after, encoding="utf-8") + ''' + driver.write_text(text[:start] + replacement + text[end:], encoding="utf-8") + PY PYTHONPATH=. python3 scripts/ci/temp_pr1706_one_shot_runner_release_v2.py # Product baseline has its own single-writer lane; do not compete with it here. git restore --source=HEAD -- docs/product-technical-gap-baseline.md From 45db4b31624c3f33747274e7045d88f00b2258eb Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 19:24:25 +0900 Subject: [PATCH 22/59] fix(opencode): make exact-run wake resilient to transient lookup failures --- ...temp_pr1706_one_shot_runner_release_v4.yml | 127 +++++++++++++++++- 1 file changed, 125 insertions(+), 2 deletions(-) diff --git a/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml b/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml index 905bae4505..d61279b658 100644 --- a/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml +++ b/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml @@ -6,6 +6,9 @@ on: - fix/opencode-poll-wall-clock-bound paths: - .github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml + - scripts/ci/temp_pr1706_one_shot_runner_release.py + - scripts/ci/temp_pr1706_one_shot_runner_release_v2.py + - tests/test_opencode_required_verdict_runner_release.py permissions: contents: read @@ -97,7 +100,127 @@ jobs: ) WORKFLOW.write_text(before + step_start + target + step_end + after, encoding="utf-8") ''' - driver.write_text(text[:start] + replacement + text[end:], encoding="utf-8") + text = text[:start] + replacement + text[end:] + + old_lookup = ''' for attempt in $(seq 1 12); do + run="$(timeout 30s gh api "repos/${GH_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}")" + required_run="$(printf '%s\\n' "$run" | jq -r --arg head "$PR_HEAD_SHA" --arg pr "$PR_NUMBER" --argjson run_id "$REQUIRED_RUN_ID" '\n''' + new_lookup = ''' for attempt in $(seq 1 12); do + if ! run="$(timeout 30s gh api "repos/${GH_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}")"; then + echo "::warning::Exact required-run lookup attempt ${attempt}/12 failed; retrying before wake." + if [ "$attempt" -lt 12 ]; then + sleep 5 + continue + fi + break + fi + required_run="$(printf '%s\\n' "$run" | jq -r --arg head "$PR_HEAD_SHA" --arg pr "$PR_NUMBER" --argjson run_id "$REQUIRED_RUN_ID" '\n''' + if text.count(old_lookup) != 1: + raise SystemExit("v2 exact-run lookup boundary drifted") + text = text.replace(old_lookup, new_lookup, 1) + + old_parse = ''' ')" + IFS=$'\\t' read -r required_run_id required_status required_conclusion <<<"$required_run" + if [ "$required_status" = "completed" ] && [ "$required_conclusion" = "failure" ]; then +''' + new_parse = ''' ')" || required_run="" + if [ -z "$required_run" ]; then + echo "::warning::Exact required-run lookup attempt ${attempt}/12 returned malformed or nonmatching evidence; retrying before wake." + if [ "$attempt" -lt 12 ]; then + sleep 5 + continue + fi + break + fi + IFS=$'\\t' read -r required_run_id required_status required_conclusion <<<"$required_run" + if [ "$required_status" = "completed" ] && [ "$required_conclusion" = "failure" ]; then +''' + if text.count(old_parse) != 1: + raise SystemExit("v2 exact-run parse boundary drifted") + text = text.replace(old_parse, new_parse, 1) + + acceptance_marker = ''' assert len(calls) == 2 +''', encoding="utf-8") +''' + acceptance_replacement = r''' assert len(calls) == 2 + + +def _wake_script() -> str: + """Extract the exact-run wake shell body from the production dispatch workflow.""" + workflow = DISPATCH_WORKFLOW.read_text(encoding="utf-8") + step = workflow.split(" - name: Wake exact-head required OpenCode workflow\n", 1)[1] + block = step.split(" run: |\n", 1)[1].split("\n - name: Publish repository_dispatch OpenCode status\n", 1)[0] + return textwrap.dedent(block) + + +def test_exact_run_wake_retries_transient_lookup_then_reruns_failed_job(tmp_path: Path) -> None: + """A transient run lookup cannot permanently strand an already posted verdict.""" + bash = shutil.which("bash") + jq = shutil.which("jq") + if bash is None or jq is None: + pytest.skip("bash and jq are required") + fake_gh = tmp_path / "gh" + calls = tmp_path / "wake-calls" + attempts = tmp_path / "wake-attempts" + fake_gh.write_text( + "#!/usr/bin/env bash\nset -euo pipefail\nprintf '%s\\n' \"$*\" >>\"$CALLS\"\n" + "if [[ \"$*\" == \"api repos/ContextualWisdomLab/example/actions/runs/42\" ]]; then\n" + " n=0; [[ -f \"$ATTEMPTS\" ]] && n=$(cat \"$ATTEMPTS\"); n=$((n+1)); printf '%s' \"$n\" >\"$ATTEMPTS\"\n" + " if [[ \"$n\" -eq 1 ]]; then exit 75; fi\n" + " printf '%s\\n' \"$RUN_JSON\"; exit 0\n" + "fi\n" + "if [[ \"$*\" == \"api -X POST repos/ContextualWisdomLab/example/actions/runs/42/rerun-failed-jobs\" ]]; then exit 0; fi\n" + "exit 97\n", + encoding="utf-8", + ) + fake_gh.chmod(0o755) + fake_timeout = tmp_path / "timeout" + fake_timeout.write_text("#!/usr/bin/env bash\nset -euo pipefail\nshift\nexec \"$@\"\n", encoding="utf-8") + fake_timeout.chmod(0o755) + fake_sleep = tmp_path / "sleep" + fake_sleep.write_text("#!/usr/bin/env bash\nexit 0\n", encoding="utf-8") + fake_sleep.chmod(0o755) + run_json = { + "id": 42, + "event": "pull_request_target", + "path": ".github/workflows/opencode-review.yml", + "pull_requests": [{"number": 1437, "head": {"sha": HEAD_SHA}}], + "status": "completed", + "conclusion": "failure", + } + result = subprocess.run( + [bash, "-c", _wake_script()], + env={ + **os.environ, + "PATH": f"{tmp_path}{os.pathsep}{os.environ.get('PATH', '')}", + "CALLS": str(calls), + "ATTEMPTS": str(attempts), + "RUN_JSON": json.dumps(run_json), + "GH_TOKEN": "test-token", + "GH_REPOSITORY": "ContextualWisdomLab/example", + "PR_NUMBER": "1437", + "PR_HEAD_SHA": HEAD_SHA, + "REQUIRED_RUN_ID": "42", + "WAKE_TOKEN_SOURCE": "github-token", + }, + text=True, + capture_output=True, + check=False, + ) + assert result.returncode == 0, result.stderr + assert "Exact required-run lookup attempt 1/12 failed" in result.stdout + assert "Re-ran failed jobs for exact-PR/head Required OpenCode Review run 42." in result.stdout + assert calls.read_text(encoding="utf-8").splitlines() == [ + "api repos/ContextualWisdomLab/example/actions/runs/42", + "api repos/ContextualWisdomLab/example/actions/runs/42", + "api -X POST repos/ContextualWisdomLab/example/actions/runs/42/rerun-failed-jobs", + ] +''', encoding="utf-8") +''' + if text.count(acceptance_marker) != 1: + raise SystemExit("v2 acceptance insertion boundary drifted") + text = text.replace(acceptance_marker, acceptance_replacement, 1) + driver.write_text(text, encoding="utf-8") PY PYTHONPATH=. python3 scripts/ci/temp_pr1706_one_shot_runner_release_v2.py # Product baseline has its own single-writer lane; do not compete with it here. @@ -185,4 +308,4 @@ jobs: git diff --cached --check git commit -m "fix(opencode): release required runner after one verdict read" git remote set-url origin "https://x-access-token:${PUSH_TOKEN}@github.com/ContextualWisdomLab/.github.git" - git push origin HEAD:fix/opencode-poll-wall-clock-bound + git push origin HEAD:fix/opencode-poll-wall-clock-bound \ No newline at end of file From 29fa2304e42cdd7be42b1707bc27682924c0842d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 19:25:40 +0900 Subject: [PATCH 23/59] fix(opencode): restore valid source-fix workflow and lock wake retry regression --- ...temp_pr1706_one_shot_runner_release_v4.yml | 96 +++---------------- 1 file changed, 14 insertions(+), 82 deletions(-) diff --git a/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml b/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml index d61279b658..b8a0e35734 100644 --- a/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml +++ b/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml @@ -95,7 +95,10 @@ jobs: target = replace_once( target, ' if ! reviews="$(gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"; then\n', - ' if ! reviews="$(timeout 30s gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"; then\n', + ' if ! reviews="$(timeout 30s gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"; then\n' + ' echo "::error::Reviews API read failed during one-shot current-head verdict admission; failing closed and releasing the runner."\n' + ' exit 1\n' + ' fi\n', "bounded Reviews read", ) WORKFLOW.write_text(before + step_start + target + step_end + after, encoding="utf-8") @@ -139,87 +142,16 @@ jobs: raise SystemExit("v2 exact-run parse boundary drifted") text = text.replace(old_parse, new_parse, 1) - acceptance_marker = ''' assert len(calls) == 2 -''', encoding="utf-8") -''' - acceptance_replacement = r''' assert len(calls) == 2 - - -def _wake_script() -> str: - """Extract the exact-run wake shell body from the production dispatch workflow.""" - workflow = DISPATCH_WORKFLOW.read_text(encoding="utf-8") - step = workflow.split(" - name: Wake exact-head required OpenCode workflow\n", 1)[1] - block = step.split(" run: |\n", 1)[1].split("\n - name: Publish repository_dispatch OpenCode status\n", 1)[0] - return textwrap.dedent(block) - - -def test_exact_run_wake_retries_transient_lookup_then_reruns_failed_job(tmp_path: Path) -> None: - """A transient run lookup cannot permanently strand an already posted verdict.""" - bash = shutil.which("bash") - jq = shutil.which("jq") - if bash is None or jq is None: - pytest.skip("bash and jq are required") - fake_gh = tmp_path / "gh" - calls = tmp_path / "wake-calls" - attempts = tmp_path / "wake-attempts" - fake_gh.write_text( - "#!/usr/bin/env bash\nset -euo pipefail\nprintf '%s\\n' \"$*\" >>\"$CALLS\"\n" - "if [[ \"$*\" == \"api repos/ContextualWisdomLab/example/actions/runs/42\" ]]; then\n" - " n=0; [[ -f \"$ATTEMPTS\" ]] && n=$(cat \"$ATTEMPTS\"); n=$((n+1)); printf '%s' \"$n\" >\"$ATTEMPTS\"\n" - " if [[ \"$n\" -eq 1 ]]; then exit 75; fi\n" - " printf '%s\\n' \"$RUN_JSON\"; exit 0\n" - "fi\n" - "if [[ \"$*\" == \"api -X POST repos/ContextualWisdomLab/example/actions/runs/42/rerun-failed-jobs\" ]]; then exit 0; fi\n" - "exit 97\n", - encoding="utf-8", - ) - fake_gh.chmod(0o755) - fake_timeout = tmp_path / "timeout" - fake_timeout.write_text("#!/usr/bin/env bash\nset -euo pipefail\nshift\nexec \"$@\"\n", encoding="utf-8") - fake_timeout.chmod(0o755) - fake_sleep = tmp_path / "sleep" - fake_sleep.write_text("#!/usr/bin/env bash\nexit 0\n", encoding="utf-8") - fake_sleep.chmod(0o755) - run_json = { - "id": 42, - "event": "pull_request_target", - "path": ".github/workflows/opencode-review.yml", - "pull_requests": [{"number": 1437, "head": {"sha": HEAD_SHA}}], - "status": "completed", - "conclusion": "failure", - } - result = subprocess.run( - [bash, "-c", _wake_script()], - env={ - **os.environ, - "PATH": f"{tmp_path}{os.pathsep}{os.environ.get('PATH', '')}", - "CALLS": str(calls), - "ATTEMPTS": str(attempts), - "RUN_JSON": json.dumps(run_json), - "GH_TOKEN": "test-token", - "GH_REPOSITORY": "ContextualWisdomLab/example", - "PR_NUMBER": "1437", - "PR_HEAD_SHA": HEAD_SHA, - "REQUIRED_RUN_ID": "42", - "WAKE_TOKEN_SOURCE": "github-token", - }, - text=True, - capture_output=True, - check=False, - ) - assert result.returncode == 0, result.stderr - assert "Exact required-run lookup attempt 1/12 failed" in result.stdout - assert "Re-ran failed jobs for exact-PR/head Required OpenCode Review run 42." in result.stdout - assert calls.read_text(encoding="utf-8").splitlines() == [ - "api repos/ContextualWisdomLab/example/actions/runs/42", - "api repos/ContextualWisdomLab/example/actions/runs/42", - "api -X POST repos/ContextualWisdomLab/example/actions/runs/42/rerun-failed-jobs", - ] -''', encoding="utf-8") -''' - if text.count(acceptance_marker) != 1: - raise SystemExit("v2 acceptance insertion boundary drifted") - text = text.replace(acceptance_marker, acceptance_replacement, 1) + old_assertion = ' assert "rerun-failed-jobs" in dispatched\n' + new_assertion = ( + old_assertion + + ' assert \'if ! run="$(timeout 30s gh api \' in dispatched\n' + + ' assert "Exact required-run lookup attempt ${attempt}/12 failed" in dispatched\n' + + ' assert "continue" in dispatched\n' + ) + if text.count(old_assertion) != 1: + raise SystemExit("v2 wake retry regression insertion boundary drifted") + text = text.replace(old_assertion, new_assertion, 1) driver.write_text(text, encoding="utf-8") PY PYTHONPATH=. python3 scripts/ci/temp_pr1706_one_shot_runner_release_v2.py From e6baae249b51c8b7beaff5c6823c3b4bd817921a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 19:31:10 +0900 Subject: [PATCH 24/59] fix(opencode): repair exact-run wake and stale identity regressions --- .../temp_pr1706_one_shot_runner_release_v3.py | 356 ++++++++++++++++++ 1 file changed, 356 insertions(+) create mode 100644 scripts/ci/temp_pr1706_one_shot_runner_release_v3.py diff --git a/scripts/ci/temp_pr1706_one_shot_runner_release_v3.py b/scripts/ci/temp_pr1706_one_shot_runner_release_v3.py new file mode 100644 index 0000000000..51ddee2ac6 --- /dev/null +++ b/scripts/ci/temp_pr1706_one_shot_runner_release_v3.py @@ -0,0 +1,356 @@ +"""Finish PR #1706 exact-run wake repair after the first deterministic driver. + +Temporary helper: the source-fix workflow deletes this file in the successful +publication commit together with the other PR #1706 repair machinery. +""" + +from __future__ import annotations + +from pathlib import Path + + +WORKFLOW = Path(".github/workflows/opencode-review.yml") +DISPATCH = Path(".github/workflows/opencode-review-dispatch.yml") +ACCEPTANCE = Path("tests/test_opencode_required_verdict_runner_release.py") +REGRESSION = Path("tests/test_opencode_required_verdict_regression.py") +SELF = Path("tests/test_opencode_poll_self_retirement.py") +LIVE_DRAFT = Path("tests/test_opencode_live_draft_state_regression.py") +ARCHITECTURE = Path("ARCHITECTURE.md") +DOCTORING = Path("docs/doctoring/opencode-stale-poll-self-retirement.md") +CHANGELOG = Path("CHANGELOG.md") + + +def replace_once(text: str, old: str, new: str, label: str) -> str: + """Replace one exact fragment and fail closed when concurrent edits drift.""" + count = text.count(old) + if count != 1: + raise SystemExit(f"{label} drifted: expected one exact match, found {count}") + return text.replace(old, new, 1) + + +workflow = WORKFLOW.read_text(encoding="utf-8") +workflow = replace_once( + workflow, + ' live_pr="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"\n', + ' if ! live_pr="$(timeout 30s gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"; then\n' + ' echo "::error::Live pull request API read failed during one-shot current-head verdict admission; failing closed and releasing the runner."\n' + ' exit 1\n' + ' fi\n', + "bounded live PR read", +) +workflow = replace_once( + workflow, + ' if ! reviews="$(gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"; then\n', + ' if ! reviews="$(timeout 30s gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"; then\n', + "bounded Reviews read", +) +WORKFLOW.write_text(workflow, encoding="utf-8") + +dispatch = DISPATCH.read_text(encoding="utf-8") +start = " - name: Wake exact-head required OpenCode workflow\n" +end = "\n - name: Publish repository_dispatch OpenCode status\n" +if dispatch.count(start) != 1 or dispatch.count(end) != 1: + raise SystemExit("OpenCode exact-run wake boundaries drifted") +before, rest = dispatch.split(start, 1) +_old_wake, after = rest.split(end, 1) +wake = r''' - name: Wake exact-head required OpenCode workflow + if: >- + always() + && github.event_name == 'repository_dispatch' + && steps.formal_review_receipt.outcome == 'success' + && needs.validate-pr-metadata.outputs.target_repository != '' + && needs.validate-pr-metadata.outputs.pr_number != '' + && needs.validate-pr-metadata.outputs.head_sha != '' + && github.event.client_payload.required_run_id != '' + env: + GH_TOKEN: ${{ needs.validate-pr-metadata.outputs.target_repository == github.repository && github.token || secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN }} + GH_REPOSITORY: ${{ needs.validate-pr-metadata.outputs.target_repository }} + PR_NUMBER: ${{ needs.validate-pr-metadata.outputs.pr_number }} + PR_HEAD_SHA: ${{ needs.validate-pr-metadata.outputs.head_sha }} + REQUIRED_RUN_ID: ${{ github.event.client_payload.required_run_id }} + WAKE_TOKEN_SOURCE: ${{ needs.validate-pr-metadata.outputs.target_repository == github.repository && 'github-token' || secrets.PR_REVIEW_MERGE_TOKEN != '' && 'PR_REVIEW_MERGE_TOKEN' || secrets.OPENCODE_APPROVE_TOKEN != '' && 'OPENCODE_APPROVE_TOKEN' || 'unavailable' }} + run: | + set -euo pipefail + if [ -z "${GH_TOKEN:-}" ] || [ "$WAKE_TOKEN_SOURCE" = "unavailable" ]; then + echo "::error::Actions-capable wake credential is unavailable. Native runs use github.token; sibling runs require PR_REVIEW_MERGE_TOKEN or OPENCODE_APPROVE_TOKEN." + exit 1 + fi + [[ "$REQUIRED_RUN_ID" =~ ^[1-9][0-9]*$ ]] || { echo "::error::Required OpenCode run id is missing or non-canonical."; exit 1; } + [[ "$PR_NUMBER" =~ ^[1-9][0-9]*$ ]] || { echo "::error::Required OpenCode PR number is missing or non-canonical."; exit 1; } + [[ "$PR_HEAD_SHA" =~ ^[0-9a-fA-F]{40}$ ]] || { echo "::error::Required OpenCode PR head SHA is missing or malformed."; exit 1; } + for attempt in $(seq 1 12); do + if ! run="$(timeout 30s gh api "repos/${GH_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}")"; then + echo "::warning::Exact required-run lookup attempt ${attempt}/12 failed; retrying before wake." + if [ "$attempt" -lt 12 ]; then sleep 5; continue; fi + break + fi + required_run="$(printf '%s\n' "$run" | jq -r --arg head "$PR_HEAD_SHA" --arg pr "$PR_NUMBER" --argjson run_id "$REQUIRED_RUN_ID" ' + select(.id == $run_id) + | select(.event == "pull_request_target") + | select(.path == ".github/workflows/opencode-review.yml") + | select(any((.pull_requests // [])[]?; ((.number // 0) | tostring) == $pr and ((.head.sha // "") | ascii_downcase) == ($head | ascii_downcase))) + | [(.id // ""), (.status // ""), (.conclusion // "")] + | @tsv + ')" || required_run="" + if [ -z "$required_run" ]; then + echo "::warning::Exact required-run lookup attempt ${attempt}/12 returned malformed or nonmatching evidence; retrying before wake." + if [ "$attempt" -lt 12 ]; then sleep 5; continue; fi + break + fi + IFS=$'\t' read -r required_run_id required_status required_conclusion <<<"$required_run" + if [ "$required_status" = "completed" ] && [ "$required_conclusion" = "failure" ]; then + gh api -X POST "repos/${GH_REPOSITORY}/actions/runs/${required_run_id}/rerun-failed-jobs" >/dev/null + echo "Re-ran failed jobs for exact-PR/head Required OpenCode Review run ${required_run_id}." + exit 0 + fi + if [ "$required_status" = "completed" ] && [ "$required_conclusion" = "success" ]; then + echo "Exact-PR/head Required OpenCode Review run ${required_run_id} already succeeded." + exit 0 + fi + if [ "$attempt" -lt 12 ]; then sleep 5; fi + done + echo "::error::Formal OpenCode receipt exists, but the exact-PR/head required workflow did not reach a rerunnable failed state." + exit 1 +''' +DISPATCH.write_text(before + wake + end + after, encoding="utf-8") + +ACCEPTANCE.write_text(r'''"""Regression coverage for releasing the required OpenCode runner while review continues.""" + +from __future__ import annotations + +import json +import os +import shutil +import subprocess +import textwrap +from pathlib import Path + +import pytest + +WORKFLOW = Path(".github/workflows/opencode-review.yml") +DISPATCH_WORKFLOW = Path(".github/workflows/opencode-review-dispatch.yml") +HEAD_SHA = "a" * 40 + + +def _fail_closed_script() -> str: + """Extract only the real required-verdict admission run block.""" + workflow = WORKFLOW.read_text(encoding="utf-8") + step = workflow.split(" - name: Fail closed without a current-head OpenCode verdict\n", 1)[1] + block = step.split(" run: |\n", 1)[1].split("\n cancel-superseded-opencode-review-runs:\n", 1)[0] + return textwrap.dedent(block) + + +def _wake_script() -> str: + """Extract only the exact-run wake shell body.""" + workflow = DISPATCH_WORKFLOW.read_text(encoding="utf-8") + step = workflow.split(" - name: Wake exact-head required OpenCode workflow\n", 1)[1] + block = step.split(" run: |\n", 1)[1].split("\n - name: Publish repository_dispatch OpenCode status\n", 1)[0] + return textwrap.dedent(block) + + +def test_missing_verdict_uses_exact_pr_run_wake_instead_of_runner_polling() -> None: + """A missing verdict fails once and relies on authenticated exact-run wake.""" + required = _fail_closed_script() + dispatched = DISPATCH_WORKFLOW.read_text(encoding="utf-8") + for token in ("while :; do", "poll_interval_seconds", "poll_deadline_epoch", "sleep "): + assert token not in required + assert required.count("timeout 30s gh api") == 2 + assert "rerun-failed-jobs" in dispatched + assert "pull_requests // []" in dispatched + assert "(.number // 0) | tostring" in dispatched + assert 'if ! run="$(timeout 30s gh api ' in dispatched + + +def _run_admission(tmp_path: Path, reviews: list[dict[str, object]]) -> tuple[subprocess.CompletedProcess[str], list[str]]: + """Execute production admission against deterministic live/review evidence.""" + bash = shutil.which("bash") + jq = shutil.which("jq") + if bash is None or jq is None: + pytest.skip("bash and jq are required") + fake_gh = tmp_path / "gh" + calls = tmp_path / "calls" + fake_gh.write_text( + "#!/usr/bin/env bash\nset -euo pipefail\nprintf '%s\\n' \"$*\" >>\"$CALLS\"\n" + "if [[ \"$*\" == \"api repos/ContextualWisdomLab/example/pulls/42\" ]]; then printf '%s\\n' \"$LIVE_PR\"; exit 0; fi\n" + "if [[ \"$*\" == \"api --paginate repos/ContextualWisdomLab/example/pulls/42/reviews?per_page=100\" ]]; then printf '%s\\n' \"$REVIEWS\"; exit 0; fi\nexit 97\n", + encoding="utf-8", + ) + fake_gh.chmod(0o755) + fake_timeout = tmp_path / "timeout" + fake_timeout.write_text("#!/usr/bin/env bash\nset -euo pipefail\nshift\nexec \"$@\"\n", encoding="utf-8") + fake_timeout.chmod(0o755) + result = subprocess.run( + [bash, "-c", _fail_closed_script()], + env={**os.environ, "PATH": f"{tmp_path}{os.pathsep}{os.environ.get('PATH', '')}", "CALLS": str(calls), "LIVE_PR": json.dumps({"head": {"sha": HEAD_SHA}, "draft": False, "state": "open"}), "REVIEWS": json.dumps(reviews), "GH_TOKEN": "test-token", "TARGET_REPOSITORY": "ContextualWisdomLab/example", "PR_NUMBER": "42", "HEAD_SHA": HEAD_SHA, "PR_ACTION": "synchronize", "PR_DRAFT": "false"}, + text=True, capture_output=True, check=False, + ) + return result, calls.read_text(encoding="utf-8").splitlines() + + +def test_missing_verdict_fails_after_one_live_and_one_review_read(tmp_path: Path) -> None: + """No verdict releases the runner immediately with exactly two API reads.""" + result, calls = _run_admission(tmp_path, []) + assert result.returncode == 1, result.stderr + assert "No APPROVED or CHANGES_REQUESTED from opencode-agent" in result.stdout + assert len(calls) == 2 + + +def test_exact_run_wake_retries_transient_lookup_then_reruns_failed_job(tmp_path: Path) -> None: + """A transient run lookup cannot strand an already-posted exact-head verdict.""" + bash = shutil.which("bash") + jq = shutil.which("jq") + if bash is None or jq is None: + pytest.skip("bash and jq are required") + calls = tmp_path / "wake-calls" + attempts = tmp_path / "wake-attempts" + fake_gh = tmp_path / "gh" + fake_gh.write_text( + "#!/usr/bin/env bash\nset -euo pipefail\nprintf '%s\\n' \"$*\" >>\"$CALLS\"\n" + "if [[ \"$*\" == \"api repos/ContextualWisdomLab/example/actions/runs/42\" ]]; then n=0; [[ -f \"$ATTEMPTS\" ]] && n=$(cat \"$ATTEMPTS\"); n=$((n+1)); printf '%s' \"$n\" >\"$ATTEMPTS\"; if [[ \"$n\" -eq 1 ]]; then exit 75; fi; printf '%s\\n' \"$RUN_JSON\"; exit 0; fi\n" + "if [[ \"$*\" == \"api -X POST repos/ContextualWisdomLab/example/actions/runs/42/rerun-failed-jobs\" ]]; then exit 0; fi\nexit 97\n", + encoding="utf-8", + ) + fake_gh.chmod(0o755) + fake_timeout = tmp_path / "timeout" + fake_timeout.write_text("#!/usr/bin/env bash\nset -euo pipefail\nshift\nexec \"$@\"\n", encoding="utf-8") + fake_timeout.chmod(0o755) + fake_sleep = tmp_path / "sleep" + fake_sleep.write_text("#!/usr/bin/env bash\nexit 0\n", encoding="utf-8") + fake_sleep.chmod(0o755) + run_json = {"id": 42, "event": "pull_request_target", "path": ".github/workflows/opencode-review.yml", "pull_requests": [{"number": 1437, "head": {"sha": HEAD_SHA}}], "status": "completed", "conclusion": "failure"} + result = subprocess.run( + [bash, "-c", _wake_script()], + env={**os.environ, "PATH": f"{tmp_path}{os.pathsep}{os.environ.get('PATH', '')}", "CALLS": str(calls), "ATTEMPTS": str(attempts), "RUN_JSON": json.dumps(run_json), "GH_TOKEN": "test-token", "GH_REPOSITORY": "ContextualWisdomLab/example", "PR_NUMBER": "1437", "PR_HEAD_SHA": HEAD_SHA, "REQUIRED_RUN_ID": "42", "WAKE_TOKEN_SOURCE": "github-token"}, + text=True, capture_output=True, check=False, + ) + assert result.returncode == 0, result.stderr + assert "Exact required-run lookup attempt 1/12 failed" in result.stdout + assert "Re-ran failed jobs for exact-PR/head Required OpenCode Review run 42." in result.stdout + assert calls.read_text(encoding="utf-8").splitlines() == ["api repos/ContextualWisdomLab/example/actions/runs/42", "api repos/ContextualWisdomLab/example/actions/runs/42", "api -X POST repos/ContextualWisdomLab/example/actions/runs/42/rerun-failed-jobs"] +''', encoding="utf-8") + +SELF.write_text(r'''"""Regression contract for one-shot Required OpenCode verdict admission.""" +from pathlib import Path +WORKFLOW = Path(".github/workflows/opencode-review.yml") + +def _step() -> str: + """Return only the one-shot verdict-admission step.""" + workflow = WORKFLOW.read_text(encoding="utf-8") + return workflow.split(" - name: Fail closed without a current-head OpenCode verdict\n", 1)[1].split("\n cancel-superseded-opencode-review-runs:\n", 1)[0] + +def test_one_shot_revalidates_live_state_before_reviews() -> None: + """Current authority is established before formal review evidence is read.""" + step = _step() + live = 'timeout 30s gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}"' + reviews = 'timeout 30s gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100"' + assert live in step and reviews in step and step.index(live) < step.index(reviews) + +def test_transport_reads_are_bounded_but_model_wait_is_not() -> None: + """GitHub transport gets a bound; semantic model reasoning gets no deadline.""" + step = _step() + assert step.count("timeout 30s gh api") == 2 + for token in ("while :; do", "poll_interval_seconds", "poll_deadline_epoch", "max_poll_transport_failures", "sleep "): + assert token not in step +''', encoding="utf-8") + +regression = REGRESSION.read_text(encoding="utf-8") +regression = regression.replace(' return textwrap.dedent(step.split(" run: |\\n", 1)[1])\n', ' block = step.split(" run: |\\n", 1)[1].split("\\n cancel-superseded-opencode-review-runs:\\n", 1)[0]\n return textwrap.dedent(block)\n', 1) +regression = regression.replace(' assert "select(.head_sha == $head)" in dispatched\n', ' assert "pull_requests // []" in dispatched\n assert "(.number // 0) | tostring" in dispatched\n assert "select(.head_sha == $head)" not in dispatched\n', 1) +old_selector = '''def wake_selector(run: dict[str, object], *, head: str = HEAD, run_id: int = 42) -> str: + """Execute the wake step's run-validation jq program in isolation.""" + jq = shutil.which("jq") + if jq is None: + pytest.skip("jq is required to execute the production wake selector") + dispatched = DISPATCH_WORKFLOW.read_text(encoding="utf-8") + marker = """jq -r --arg head "$PR_HEAD_SHA" --argjson run_id "$REQUIRED_RUN_ID" '""" + start = dispatched.index(marker) + len(marker) + end = dispatched.index("\\n ')", start) + result = subprocess.run( + [jq, "-r", "--arg", "head", head, "--argjson", "run_id", str(run_id), dispatched[start:end]], + input=json.dumps(run), + text=True, + capture_output=True, + check=False, + ) + assert result.returncode == 0, result.stderr + return result.stdout.strip() +''' +new_selector = '''def wake_selector(run: dict[str, object], *, head: str = HEAD, pr: int = 1437, run_id: int = 42) -> str: + """Execute the wake step's exact run/PR/head validation jq program.""" + jq = shutil.which("jq") + if jq is None: + pytest.skip("jq is required to execute the production wake selector") + dispatched = DISPATCH_WORKFLOW.read_text(encoding="utf-8") + marker = """jq -r --arg head "$PR_HEAD_SHA" --arg pr "$PR_NUMBER" --argjson run_id "$REQUIRED_RUN_ID" '""" + start = dispatched.index(marker) + len(marker) + end = dispatched.index("\\n ')", start) + result = subprocess.run( + [jq, "-r", "--arg", "head", head, "--arg", "pr", str(pr), "--argjson", "run_id", str(run_id), dispatched[start:end]], + input=json.dumps(run), text=True, capture_output=True, check=False, + ) + assert result.returncode == 0, result.stderr + return result.stdout.strip() +''' +regression = replace_once(regression, old_selector, new_selector, "wake selector fixture") +old_fixture = '''def required_run(*, run_id: int = 42, head_sha: str = HEAD, path: str = ".github/workflows/opencode-review.yml") -> dict[str, object]: + """Build one realistic single-run GET REST API record. + + Mirrors the real shape a sibling repo sees for a run injected by the org's + required-workflow ruleset (this repo's actual central-hub use case): `name` + is the bare workflow name and `display_title` is a plain PR title, with no + PR number or head SHA embedded in either -- unlike a native same-repo + trigger, where both fields carry the rendered `run-name`. + """ + return { + "id": run_id, + "head_sha": head_sha, + "event": "pull_request_target", + "name": "Required OpenCode Review", + "display_title": "Fix an unrelated example bug", + "path": path, + "workflow_url": ( + "https://api.github.com/repos/ContextualWisdomLab/example" + "/actions/required_workflows/9" + ), + "status": "completed", + "conclusion": "failure", + } +''' +new_fixture = '''def required_run(*, run_id: int = 42, pr_head_sha: str = HEAD, pr_number: int = 1437, path: str = ".github/workflows/opencode-review.yml") -> dict[str, object]: + """Build a pull_request_target run whose top-level head_sha is the base SHA.""" + return { + "id": run_id, + "head_sha": "f" * 40, + "event": "pull_request_target", + "name": "Required OpenCode Review", + "display_title": "Fix an unrelated example bug", + "path": path, + "workflow_url": "https://api.github.com/repos/ContextualWisdomLab/example/actions/required_workflows/9", + "pull_requests": [{"number": pr_number, "head": {"sha": pr_head_sha}}], + "status": "completed", + "conclusion": "failure", + } +''' +regression = replace_once(regression, old_fixture, new_fixture, "pull_request_target run fixture") +regression = regression.replace('required_run(head_sha="b" * 40)', 'required_run(pr_head_sha="b" * 40)', 1) +wrong_workflow = 'def test_wake_selector_rejects_a_referenced_run_for_a_different_workflow() -> None:\n' +if 'def test_wake_selector_rejects_a_referenced_run_for_a_different_pr()' not in regression: + regression = replace_once(regression, wrong_workflow, 'def test_wake_selector_rejects_a_referenced_run_for_a_different_pr() -> None:\n """A run id for another PR cannot receive the wake mutation."""\n assert wake_selector(required_run(pr_number=9999)) == ""\n\n\n' + wrong_workflow, "wrong PR wake regression") +regression = replace_once(regression, ' "PR_HEAD_SHA": HEAD,\n "REQUIRED_RUN_ID": "42",\n', ' "PR_NUMBER": "1437",\n "PR_HEAD_SHA": HEAD,\n "REQUIRED_RUN_ID": "42",\n', "formal wake PR identity fixture") +REGRESSION.write_text(regression, encoding="utf-8") + +live_draft = LIVE_DRAFT.read_text(encoding="utf-8").replace("Reviews API read failed 3 consecutive times", "Reviews API read failed during one-shot current-head verdict admission") +LIVE_DRAFT.write_text(live_draft, encoding="utf-8") + +architecture = ARCHITECTURE.read_text(encoding="utf-8") +if "### Required OpenCode one-shot verdict admission" not in architecture: + ARCHITECTURE.write_text(architecture + "\n\n### Required OpenCode one-shot verdict admission\n\nThe protected required workflow validates live PR state once, reads formal review evidence once, and releases its runner immediately when no exact-head verdict exists. The authenticated default-branch dispatch revalidates repository, immutable run id, workflow path, PR number, and `pull_requests[].head.sha` before `rerun-failed-jobs`. A bounded retry absorbs transient GitHub run-lookup failures; model reasoning itself has no caller wall-clock deadline.\n", encoding="utf-8") + +doctoring = DOCTORING.read_text(encoding="utf-8") +if "### Exact-run wake identity and transient lookup correction" not in doctoring: + DOCTORING.write_text(doctoring + "\n\n### Exact-run wake identity and transient lookup correction\n\nFor `pull_request_target`, top-level workflow-run `head_sha` identifies the base revision, not the PR head. Wake authority binds immutable run id, event, workflow path, exact PR number, and `pull_requests[].head.sha`. Transient run-lookup failures are retried within a bounded transport loop so a posted formal verdict cannot be stranded by one GitHub API timeout.\n", encoding="utf-8") + +changelog = CHANGELOG.read_text(encoding="utf-8") +note = "- Required OpenCode Review now releases its runner after one exact-head verdict admission read; exact-run wake binds PR number plus `pull_requests[].head.sha` and tolerates bounded transient GitHub run-lookup failures without imposing a model reasoning timeout.\n" +if note not in changelog: + CHANGELOG.write_text(note + changelog, encoding="utf-8") From 31561b828db70c0b41bf007d1976466444aee19b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 19:31:37 +0900 Subject: [PATCH 25/59] fix(opencode): simplify source-fix execution after parser failures --- ...temp_pr1706_one_shot_runner_release_v4.yml | 100 ++---------------- 1 file changed, 7 insertions(+), 93 deletions(-) diff --git a/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml b/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml index b8a0e35734..bc62d3e9ec 100644 --- a/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml +++ b/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml @@ -8,6 +8,7 @@ on: - .github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml - scripts/ci/temp_pr1706_one_shot_runner_release.py - scripts/ci/temp_pr1706_one_shot_runner_release_v2.py + - scripts/ci/temp_pr1706_one_shot_runner_release_v3.py - tests/test_opencode_required_verdict_runner_release.py permissions: @@ -64,98 +65,7 @@ jobs: run: | set -euo pipefail PYTHONPATH=. python3 scripts/ci/temp_pr1706_one_shot_runner_release.py - python3 - <<'PY' - from pathlib import Path - - driver = Path("scripts/ci/temp_pr1706_one_shot_runner_release_v2.py") - text = driver.read_text(encoding="utf-8") - start_token = 'workflow = WORKFLOW.read_text(encoding="utf-8")\n' - end_token = 'WORKFLOW.write_text(workflow, encoding="utf-8")\n' - start = text.find(start_token) - end_start = text.find(end_token, start + len(start_token)) - if start < 0 or end_start < 0: - raise SystemExit("v2 bounded-transport repair block drifted") - end = end_start + len(end_token) - replacement = r'''workflow = WORKFLOW.read_text(encoding="utf-8") - step_start = " - name: Fail closed without a current-head OpenCode verdict\n" - step_end = "\n cancel-superseded-opencode-review-runs:\n" - if workflow.count(step_start) != 1 or workflow.count(step_end) != 1: - raise SystemExit("OpenCode required-verdict step boundaries drifted before bounded transport repair") - before, rest = workflow.split(step_start, 1) - target, after = rest.split(step_end, 1) - target = replace_once( - target, - ' live_pr="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"\n', - ' if ! live_pr="$(timeout 30s gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"; then\n' - ' echo "::error::Live pull request API read failed during one-shot current-head verdict admission; failing closed and releasing the runner."\n' - ' exit 1\n' - ' fi\n', - "bounded live PR read", - ) - target = replace_once( - target, - ' if ! reviews="$(gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"; then\n', - ' if ! reviews="$(timeout 30s gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"; then\n' - ' echo "::error::Reviews API read failed during one-shot current-head verdict admission; failing closed and releasing the runner."\n' - ' exit 1\n' - ' fi\n', - "bounded Reviews read", - ) - WORKFLOW.write_text(before + step_start + target + step_end + after, encoding="utf-8") - ''' - text = text[:start] + replacement + text[end:] - - old_lookup = ''' for attempt in $(seq 1 12); do - run="$(timeout 30s gh api "repos/${GH_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}")" - required_run="$(printf '%s\\n' "$run" | jq -r --arg head "$PR_HEAD_SHA" --arg pr "$PR_NUMBER" --argjson run_id "$REQUIRED_RUN_ID" '\n''' - new_lookup = ''' for attempt in $(seq 1 12); do - if ! run="$(timeout 30s gh api "repos/${GH_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}")"; then - echo "::warning::Exact required-run lookup attempt ${attempt}/12 failed; retrying before wake." - if [ "$attempt" -lt 12 ]; then - sleep 5 - continue - fi - break - fi - required_run="$(printf '%s\\n' "$run" | jq -r --arg head "$PR_HEAD_SHA" --arg pr "$PR_NUMBER" --argjson run_id "$REQUIRED_RUN_ID" '\n''' - if text.count(old_lookup) != 1: - raise SystemExit("v2 exact-run lookup boundary drifted") - text = text.replace(old_lookup, new_lookup, 1) - - old_parse = ''' ')" - IFS=$'\\t' read -r required_run_id required_status required_conclusion <<<"$required_run" - if [ "$required_status" = "completed" ] && [ "$required_conclusion" = "failure" ]; then -''' - new_parse = ''' ')" || required_run="" - if [ -z "$required_run" ]; then - echo "::warning::Exact required-run lookup attempt ${attempt}/12 returned malformed or nonmatching evidence; retrying before wake." - if [ "$attempt" -lt 12 ]; then - sleep 5 - continue - fi - break - fi - IFS=$'\\t' read -r required_run_id required_status required_conclusion <<<"$required_run" - if [ "$required_status" = "completed" ] && [ "$required_conclusion" = "failure" ]; then -''' - if text.count(old_parse) != 1: - raise SystemExit("v2 exact-run parse boundary drifted") - text = text.replace(old_parse, new_parse, 1) - - old_assertion = ' assert "rerun-failed-jobs" in dispatched\n' - new_assertion = ( - old_assertion - + ' assert \'if ! run="$(timeout 30s gh api \' in dispatched\n' - + ' assert "Exact required-run lookup attempt ${attempt}/12 failed" in dispatched\n' - + ' assert "continue" in dispatched\n' - ) - if text.count(old_assertion) != 1: - raise SystemExit("v2 wake retry regression insertion boundary drifted") - text = text.replace(old_assertion, new_assertion, 1) - driver.write_text(text, encoding="utf-8") - PY - PYTHONPATH=. python3 scripts/ci/temp_pr1706_one_shot_runner_release_v2.py - # Product baseline has its own single-writer lane; do not compete with it here. + PYTHONPATH=. python3 scripts/ci/temp_pr1706_one_shot_runner_release_v3.py git restore --source=HEAD -- docs/product-technical-gap-baseline.md - name: Verify focused GREEN and exact-run identity contracts @@ -185,10 +95,12 @@ jobs: rm -f \ .github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml \ scripts/ci/temp_pr1706_one_shot_runner_release.py \ - scripts/ci/temp_pr1706_one_shot_runner_release_v2.py + scripts/ci/temp_pr1706_one_shot_runner_release_v2.py \ + scripts/ci/temp_pr1706_one_shot_runner_release_v3.py test ! -e .github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml test ! -e scripts/ci/temp_pr1706_one_shot_runner_release.py test ! -e scripts/ci/temp_pr1706_one_shot_runner_release_v2.py + test ! -e scripts/ci/temp_pr1706_one_shot_runner_release_v3.py git diff --check while IFS= read -r changed; do case "$changed" in @@ -197,6 +109,7 @@ jobs: .github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml|\ scripts/ci/temp_pr1706_one_shot_runner_release.py|\ scripts/ci/temp_pr1706_one_shot_runner_release_v2.py|\ + scripts/ci/temp_pr1706_one_shot_runner_release_v3.py|\ tests/test_opencode_required_verdict_runner_release.py|\ tests/test_opencode_poll_rate_budget.py|\ tests/test_opencode_poll_self_retirement.py|\ @@ -230,6 +143,7 @@ jobs: .github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml \ scripts/ci/temp_pr1706_one_shot_runner_release.py \ scripts/ci/temp_pr1706_one_shot_runner_release_v2.py \ + scripts/ci/temp_pr1706_one_shot_runner_release_v3.py \ tests/test_opencode_required_verdict_runner_release.py \ tests/test_opencode_poll_rate_budget.py \ tests/test_opencode_poll_self_retirement.py \ From 044443ceae9c3f36136e732bbb1ea731375b040f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 19:38:27 +0900 Subject: [PATCH 26/59] fix(opencode): scope bounded transport repair to verdict job --- ...temp_pr1706_one_shot_runner_release_v4.yml | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml b/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml index bc62d3e9ec..a82d91927f 100644 --- a/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml +++ b/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml @@ -65,6 +65,44 @@ jobs: run: | set -euo pipefail PYTHONPATH=. python3 scripts/ci/temp_pr1706_one_shot_runner_release.py + python3 - <<'PY' + from pathlib import Path + + driver = Path("scripts/ci/temp_pr1706_one_shot_runner_release_v3.py") + text = driver.read_text(encoding="utf-8") + start_token = 'workflow = WORKFLOW.read_text(encoding="utf-8")\n' + end_token = 'WORKFLOW.write_text(workflow, encoding="utf-8")\n' + start = text.find(start_token) + end_start = text.find(end_token, start + len(start_token)) + if start < 0 or end_start < 0: + raise SystemExit("v3 bounded-transport block drifted") + end = end_start + len(end_token) + replacement = '''workflow = WORKFLOW.read_text(encoding="utf-8") + step_start = " - name: Fail closed without a current-head OpenCode verdict\\n" + step_end = "\\n cancel-superseded-opencode-review-runs:\\n" + if workflow.count(step_start) != 1 or workflow.count(step_end) != 1: + raise SystemExit("OpenCode required-verdict step boundaries drifted before bounded transport repair") + before, rest = workflow.split(step_start, 1) + target, after = rest.split(step_end, 1) + target = replace_once( + target, + ' live_pr="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"\\n', + ' if ! live_pr="$(timeout 30s gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"; then\\n' + ' echo "::error::Live pull request API read failed during one-shot current-head verdict admission; failing closed and releasing the runner."\\n' + ' exit 1\\n' + ' fi\\n', + "bounded live PR read", + ) + target = replace_once( + target, + ' if ! reviews="$(gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"; then\\n', + ' if ! reviews="$(timeout 30s gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"; then\\n', + "bounded Reviews read", + ) + WORKFLOW.write_text(before + step_start + target + step_end + after, encoding="utf-8") + ''' + driver.write_text(text[:start] + replacement + text[end:], encoding="utf-8") + PY PYTHONPATH=. python3 scripts/ci/temp_pr1706_one_shot_runner_release_v3.py git restore --source=HEAD -- docs/product-technical-gap-baseline.md From de9127dfe5b796d4b31b9c28b67cf0599716b5c5 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 19:42:32 +0900 Subject: [PATCH 27/59] fix(opencode): bind PR identity in every wake regression fixture --- .../_temp_pr1706_one_shot_runner_release_v4.yml | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml b/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml index a82d91927f..f18378daa3 100644 --- a/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml +++ b/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml @@ -101,7 +101,21 @@ jobs: ) WORKFLOW.write_text(before + step_start + target + step_end + after, encoding="utf-8") ''' - driver.write_text(text[:start] + replacement + text[end:], encoding="utf-8") + text = text[:start] + replacement + text[end:] + + brittle = '''regression = replace_once(regression, ' "PR_HEAD_SHA": HEAD,\\n "REQUIRED_RUN_ID": "42",\\n', ' "PR_NUMBER": "1437",\\n "PR_HEAD_SHA": HEAD,\\n "REQUIRED_RUN_ID": "42",\\n', "formal wake PR identity fixture") + ''' + robust = '''wake_env = ' "PR_HEAD_SHA": HEAD,\\n "REQUIRED_RUN_ID": "42",\\n' + wake_env_with_pr = ' "PR_NUMBER": "1437",\\n "PR_HEAD_SHA": HEAD,\\n "REQUIRED_RUN_ID": "42",\\n' + wake_env_count = regression.count(wake_env) + if wake_env_count != 2: + raise SystemExit(f"formal wake PR identity fixtures drifted: expected 2 exact matches, found {wake_env_count}") + regression = regression.replace(wake_env, wake_env_with_pr) + ''' + if text.count(brittle) != 1: + raise SystemExit("v3 formal wake PR identity repair block drifted") + text = text.replace(brittle, robust, 1) + driver.write_text(text, encoding="utf-8") PY PYTHONPATH=. python3 scripts/ci/temp_pr1706_one_shot_runner_release_v3.py git restore --source=HEAD -- docs/product-technical-gap-baseline.md From c270fb410e1ed52460d75d3884388cb9b145043e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 20:15:56 +0900 Subject: [PATCH 28/59] ci(opencode): repair exact-run wake blob contract --- ...temp_pr1706_one_shot_runner_release_v5.yml | 208 ++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 .github/workflows/_temp_pr1706_one_shot_runner_release_v5.yml diff --git a/.github/workflows/_temp_pr1706_one_shot_runner_release_v5.yml b/.github/workflows/_temp_pr1706_one_shot_runner_release_v5.yml new file mode 100644 index 0000000000..f2c4ea1cb8 --- /dev/null +++ b/.github/workflows/_temp_pr1706_one_shot_runner_release_v5.yml @@ -0,0 +1,208 @@ +name: Temporary PR1706 One-shot Runner Release v5 + +on: + push: + branches: + - fix/opencode-poll-wall-clock-bound + paths: + - .github/workflows/_temp_pr1706_one_shot_runner_release_v5.yml + +permissions: + contents: write + +concurrency: + group: temp-pr1706-one-shot-runner-release-v5 + cancel-in-progress: true + +jobs: + repair: + if: github.repository == 'ContextualWisdomLab/.github' && github.actor == 'seonghobae' + runs-on: ubuntu-slim + steps: + - name: Checkout exact trigger head + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + ref: ${{ github.sha }} + fetch-depth: 0 + persist-credentials: false + + - name: Revalidate writer and reconcile current protected main + shell: bash + run: | + set -euo pipefail + remote_head="$(git ls-remote origin refs/heads/fix/opencode-poll-wall-clock-bound | cut -f1)" + test "$remote_head" = "$GITHUB_SHA" + git fetch origin main fix/opencode-poll-wall-clock-bound + git config user.name "ContextualWisdomLab automation" + git config user.email "automation@users.noreply.github.com" + git merge --no-edit origin/main + + - name: Set up Python 3.14 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.14" + + - name: Install hash-verified repository test dependencies + shell: bash + run: | + set -euo pipefail + python3 -m pip install --disable-pip-version-check --require-hashes --only-binary=:all: -r requirements-opencode-review-ci-hashes.txt + + - name: Prove runner-holding admission is RED + shell: bash + run: | + set -euo pipefail + set +e + red_output="$(PYTHONPATH=. python3 -m pytest -q tests/test_opencode_required_verdict_runner_release.py 2>&1)" + red_status=$? + set -e + printf '%s\n' "$red_output" + if [ "$red_status" -ne 1 ] || ! grep -Eq '(^|[[:space:]])[1-9][0-9]* failed([,[:space:]]|$)' <<<"$red_output"; then + echo "::error::Expected an assertion RED proving the current runner-holding loop." + exit 1 + fi + + - name: Apply deterministic exact-run wake repair and rebind trusted blob + shell: bash + run: | + set -euo pipefail + PYTHONPATH=. python3 scripts/ci/temp_pr1706_one_shot_runner_release.py + python3 - <<'PY' + from pathlib import Path + + driver = Path("scripts/ci/temp_pr1706_one_shot_runner_release_v3.py") + text = driver.read_text(encoding="utf-8") + start_token = 'workflow = WORKFLOW.read_text(encoding="utf-8")\n' + end_token = 'WORKFLOW.write_text(workflow, encoding="utf-8")\n' + start = text.find(start_token) + end_start = text.find(end_token, start + len(start_token)) + if start < 0 or end_start < 0: + raise SystemExit("v3 bounded-transport block drifted") + end = end_start + len(end_token) + replacement = '''workflow = WORKFLOW.read_text(encoding="utf-8") + step_start = " - name: Fail closed without a current-head OpenCode verdict\\n" + step_end = "\\n cancel-superseded-opencode-review-runs:\\n" + if workflow.count(step_start) != 1 or workflow.count(step_end) != 1: + raise SystemExit("OpenCode required-verdict step boundaries drifted before bounded transport repair") + before, rest = workflow.split(step_start, 1) + target, after = rest.split(step_end, 1) + target = replace_once( + target, + ' live_pr="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"\\n', + ' if ! live_pr="$(timeout 30s gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"; then\\n' + ' echo "::error::Live pull request API read failed during one-shot current-head verdict admission; failing closed and releasing the runner."\\n' + ' exit 1\\n' + ' fi\\n', + "bounded live PR read", + ) + target = replace_once( + target, + ' if ! reviews="$(gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"; then\\n', + ' if ! reviews="$(timeout 30s gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"; then\\n', + "bounded Reviews read", + ) + WORKFLOW.write_text(before + step_start + target + step_end + after, encoding="utf-8") + ''' + text = text[:start] + replacement + text[end:] + brittle = '''regression = replace_once(regression, ' "PR_HEAD_SHA": HEAD,\\n "REQUIRED_RUN_ID": "42",\\n', ' "PR_NUMBER": "1437",\\n "PR_HEAD_SHA": HEAD,\\n "REQUIRED_RUN_ID": "42",\\n', "formal wake PR identity fixture") + ''' + robust = '''wake_env = ' "PR_HEAD_SHA": HEAD,\\n "REQUIRED_RUN_ID": "42",\\n' + wake_env_with_pr = ' "PR_NUMBER": "1437",\\n "PR_HEAD_SHA": HEAD,\\n "REQUIRED_RUN_ID": "42",\\n' + wake_env_count = regression.count(wake_env) + if wake_env_count != 2: + raise SystemExit(f"formal wake PR identity fixtures drifted: expected 2 exact matches, found {wake_env_count}") + regression = regression.replace(wake_env, wake_env_with_pr) + ''' + if text.count(brittle) != 1: + raise SystemExit("v3 formal wake PR identity repair block drifted") + driver.write_text(text.replace(brittle, robust, 1), encoding="utf-8") + PY + PYTHONPATH=. python3 scripts/ci/temp_pr1706_one_shot_runner_release_v3.py + git restore --source=HEAD -- docs/product-technical-gap-baseline.md + dispatch_blob="$(git hash-object .github/workflows/opencode-review-dispatch.yml)" + python3 - "$dispatch_blob" <<'PY' + import re + import sys + from pathlib import Path + + path = Path("tests/test_pr_review_autofix_nvidia_nim_contract.py") + text = path.read_text(encoding="utf-8") + pattern = r'REVIEW_DISPATCH_BLOB_SHA = "[0-9a-f]{40}"' + matches = re.findall(pattern, text) + if len(matches) != 1: + raise SystemExit(f"review-dispatch blob pin drifted: expected one assignment, found {len(matches)}") + text = re.sub(pattern, f'REVIEW_DISPATCH_BLOB_SHA = "{sys.argv[1]}"', text, count=1) + path.write_text(text, encoding="utf-8") + PY + git diff --check + + - name: Verify focused GREEN and trusted-blob identity + shell: bash + run: | + set -euo pipefail + PYTHONPATH=. python3 -m pytest -q \ + tests/test_opencode_required_verdict_runner_release.py \ + tests/test_opencode_poll_rate_budget.py \ + tests/test_opencode_poll_self_retirement.py \ + tests/test_opencode_required_verdict_regression.py \ + tests/test_opencode_live_draft_state_regression.py \ + tests/test_opencode_rust_coverage_toolchain_contract.py \ + tests/test_pr_review_autofix_nvidia_nim_contract.py + python3 -m compileall -q scripts tests + git diff --check + + - name: Verify broader repository suite + shell: bash + run: | + set -euo pipefail + PYTHONPATH=. python3 -m pytest tests -q + interrogate -c pyproject.toml scripts tests + git diff --check + + - name: Remove completed one-shot machinery and verify publication scope + shell: bash + run: | + set -euo pipefail + rm -f \ + .github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml \ + .github/workflows/_temp_pr1706_one_shot_runner_release_v5.yml \ + scripts/ci/temp_pr1706_one_shot_runner_release.py \ + scripts/ci/temp_pr1706_one_shot_runner_release_v2.py \ + scripts/ci/temp_pr1706_one_shot_runner_release_v3.py + git diff --check + while IFS= read -r changed; do + case "$changed" in + .github/workflows/opencode-review.yml|\ + .github/workflows/opencode-review-dispatch.yml|\ + .github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml|\ + .github/workflows/_temp_pr1706_one_shot_runner_release_v5.yml|\ + scripts/ci/temp_pr1706_one_shot_runner_release.py|\ + scripts/ci/temp_pr1706_one_shot_runner_release_v2.py|\ + scripts/ci/temp_pr1706_one_shot_runner_release_v3.py|\ + tests/test_opencode_required_verdict_runner_release.py|\ + tests/test_opencode_poll_rate_budget.py|\ + tests/test_opencode_poll_self_retirement.py|\ + tests/test_opencode_required_verdict_regression.py|\ + tests/test_opencode_live_draft_state_regression.py|\ + tests/test_pr_review_autofix_nvidia_nim_contract.py|\ + docs/doctoring/opencode-stale-poll-self-retirement.md|\ + ARCHITECTURE.md|CHANGELOG.md) ;; + *) echo "::error::Unexpected publication path: $changed"; exit 1 ;; + esac + done < <(git diff --name-only HEAD) + + - name: Publish only from unchanged exact writer head + env: + PUSH_TOKEN: ${{ github.token }} + shell: bash + run: | + set -euo pipefail + remote_head="$(git ls-remote origin refs/heads/fix/opencode-poll-wall-clock-bound | cut -f1)" + test "$remote_head" = "$GITHUB_SHA" + git add -A + git diff --cached --check + git commit -m "fix(opencode): release required runner after one verdict read" + remote_head="$(git ls-remote origin refs/heads/fix/opencode-poll-wall-clock-bound | cut -f1)" + test "$remote_head" = "$GITHUB_SHA" + git remote set-url origin "https://x-access-token:${PUSH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" + git push origin HEAD:refs/heads/fix/opencode-poll-wall-clock-bound From 404e5b88a6dfafccb4ebf943d76bbe07b05fa512 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 20:25:45 +0900 Subject: [PATCH 29/59] fix(opencode): require workflow-triggering repair publish --- .../_temp_pr1706_one_shot_runner_release_v5.yml | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/workflows/_temp_pr1706_one_shot_runner_release_v5.yml b/.github/workflows/_temp_pr1706_one_shot_runner_release_v5.yml index f2c4ea1cb8..10c437be83 100644 --- a/.github/workflows/_temp_pr1706_one_shot_runner_release_v5.yml +++ b/.github/workflows/_temp_pr1706_one_shot_runner_release_v5.yml @@ -191,18 +191,23 @@ jobs: esac done < <(git diff --name-only HEAD) - - name: Publish only from unchanged exact writer head + - name: Publish only from unchanged exact writer head with a workflow-triggering credential env: - PUSH_TOKEN: ${{ github.token }} + EXPECTED_REMOTE_HEAD: ${{ github.sha }} + PUSH_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN }} shell: bash run: | set -euo pipefail + if [ -z "${PUSH_TOKEN:-}" ]; then + echo "::error::Publication requires PR_REVIEW_MERGE_TOKEN or OPENCODE_APPROVE_TOKEN so the new exact head can start required workflows; github.token fallback is intentionally refused." + exit 1 + fi remote_head="$(git ls-remote origin refs/heads/fix/opencode-poll-wall-clock-bound | cut -f1)" - test "$remote_head" = "$GITHUB_SHA" + test "$remote_head" = "$EXPECTED_REMOTE_HEAD" git add -A git diff --cached --check git commit -m "fix(opencode): release required runner after one verdict read" remote_head="$(git ls-remote origin refs/heads/fix/opencode-poll-wall-clock-bound | cut -f1)" - test "$remote_head" = "$GITHUB_SHA" + test "$remote_head" = "$EXPECTED_REMOTE_HEAD" git remote set-url origin "https://x-access-token:${PUSH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" - git push origin HEAD:refs/heads/fix/opencode-poll-wall-clock-bound + git push origin HEAD:refs/heads/fix/opencode-poll-wall-clock-bound \ No newline at end of file From 1838f329a78ca3b8d1d108cc7c278e5cdc081dce Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 20:34:03 +0900 Subject: [PATCH 30/59] fix(opencode): unblock verified runner-release publication --- .../_temp_pr1706_one_shot_runner_release_v5.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/_temp_pr1706_one_shot_runner_release_v5.yml b/.github/workflows/_temp_pr1706_one_shot_runner_release_v5.yml index 10c437be83..730c21a718 100644 --- a/.github/workflows/_temp_pr1706_one_shot_runner_release_v5.yml +++ b/.github/workflows/_temp_pr1706_one_shot_runner_release_v5.yml @@ -191,17 +191,18 @@ jobs: esac done < <(git diff --name-only HEAD) - - name: Publish only from unchanged exact writer head with a workflow-triggering credential + - name: Publish only from unchanged exact writer head env: EXPECTED_REMOTE_HEAD: ${{ github.sha }} - PUSH_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN }} + # Prefer a workflow-triggering PAT when configured. The temporary + # materializer may use its scoped contents:write GITHUB_TOKEN only to + # publish the already-verified repair; the automation then makes the + # required traceability commit through the owner connector so fresh + # exact-head required workflows are generated before any merge. + PUSH_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN || github.token }} shell: bash run: | set -euo pipefail - if [ -z "${PUSH_TOKEN:-}" ]; then - echo "::error::Publication requires PR_REVIEW_MERGE_TOKEN or OPENCODE_APPROVE_TOKEN so the new exact head can start required workflows; github.token fallback is intentionally refused." - exit 1 - fi remote_head="$(git ls-remote origin refs/heads/fix/opencode-poll-wall-clock-bound | cut -f1)" test "$remote_head" = "$EXPECTED_REMOTE_HEAD" git add -A @@ -210,4 +211,4 @@ jobs: remote_head="$(git ls-remote origin refs/heads/fix/opencode-poll-wall-clock-bound | cut -f1)" test "$remote_head" = "$EXPECTED_REMOTE_HEAD" git remote set-url origin "https://x-access-token:${PUSH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" - git push origin HEAD:refs/heads/fix/opencode-poll-wall-clock-bound \ No newline at end of file + git push origin HEAD:refs/heads/fix/opencode-poll-wall-clock-bound From b8f8e6d8a1ee78ca23c408b6949b1487174d0080 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 20:40:56 +0900 Subject: [PATCH 31/59] fix(opencode): publish verified repair through Git data handoff --- ...temp_pr1706_one_shot_runner_release_v5.yml | 90 ++++++++++++++++--- 1 file changed, 78 insertions(+), 12 deletions(-) diff --git a/.github/workflows/_temp_pr1706_one_shot_runner_release_v5.yml b/.github/workflows/_temp_pr1706_one_shot_runner_release_v5.yml index 730c21a718..b88d1e347f 100644 --- a/.github/workflows/_temp_pr1706_one_shot_runner_release_v5.yml +++ b/.github/workflows/_temp_pr1706_one_shot_runner_release_v5.yml @@ -191,24 +191,90 @@ jobs: esac done < <(git diff --name-only HEAD) - - name: Publish only from unchanged exact writer head + - name: Materialize a verified merge commit without mutating the writer ref env: + GH_TOKEN: ${{ github.token }} EXPECTED_REMOTE_HEAD: ${{ github.sha }} - # Prefer a workflow-triggering PAT when configured. The temporary - # materializer may use its scoped contents:write GITHUB_TOKEN only to - # publish the already-verified repair; the automation then makes the - # required traceability commit through the owner connector so fresh - # exact-head required workflows are generated before any merge. - PUSH_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN || github.token }} shell: bash run: | set -euo pipefail remote_head="$(git ls-remote origin refs/heads/fix/opencode-poll-wall-clock-bound | cut -f1)" test "$remote_head" = "$EXPECTED_REMOTE_HEAD" + live_main="$(git ls-remote origin refs/heads/main | cut -f1)" + reconciled_main="$(git rev-parse origin/main)" + test "$live_main" = "$reconciled_main" git add -A git diff --cached --check - git commit -m "fix(opencode): release required runner after one verdict read" - remote_head="$(git ls-remote origin refs/heads/fix/opencode-poll-wall-clock-bound | cut -f1)" - test "$remote_head" = "$EXPECTED_REMOTE_HEAD" - git remote set-url origin "https://x-access-token:${PUSH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" - git push origin HEAD:refs/heads/fix/opencode-poll-wall-clock-bound + python3 - "$GITHUB_REPOSITORY" "$EXPECTED_REMOTE_HEAD" "$live_main" <<'PY' + import base64 + import json + import os + from pathlib import Path + import subprocess + import sys + + repo, writer_parent, main_parent = sys.argv[1:4] + + def run(*args: str, input_text: str | None = None) -> str: + proc = subprocess.run(args, input=input_text, text=True, capture_output=True, check=False) + if proc.returncode != 0: + raise SystemExit(f"command failed: {' '.join(args)}\n{proc.stderr}") + return proc.stdout.strip() + + changed = run("git", "diff", "--cached", "--name-status", "origin/main", "--") + entries: list[dict[str, object]] = [] + for line in changed.splitlines(): + if not line: + continue + status, path = line.split("\t", 1) + if status.startswith("D"): + old = run("git", "ls-tree", "origin/main", "--", path) + if not old: + continue + mode = old.split(None, 1)[0] + entries.append({"path": path, "mode": mode, "type": "blob", "sha": None}) + continue + index_row = run("git", "ls-files", "-s", "--", path) + if not index_row: + raise SystemExit(f"missing staged file metadata for {path}") + mode = index_row.split(None, 1)[0] + data = Path(path).read_bytes() + blob_payload = json.dumps({"content": base64.b64encode(data).decode("ascii"), "encoding": "base64"}) + blob_sha = run( + "gh", "api", "--method", "POST", f"repos/{repo}/git/blobs", "--input", "-", "--jq", ".sha", + input_text=blob_payload, + ) + entries.append({"path": path, "mode": mode, "type": "blob", "sha": blob_sha}) + + base_tree = run("git", "rev-parse", "origin/main^{tree}") + tree_payload = json.dumps({"base_tree": base_tree, "tree": entries}) + tree_sha = run( + "gh", "api", "--method", "POST", f"repos/{repo}/git/trees", "--input", "-", "--jq", ".sha", + input_text=tree_payload, + ) + parents = [writer_parent] + if run("git", "merge-base", "--is-ancestor", "origin/main", writer_parent) != "": + pass + # git merge-base --is-ancestor is status-only; retain main as a second parent + # whenever the writer does not already contain it. + ancestor = subprocess.run( + ["git", "merge-base", "--is-ancestor", "origin/main", writer_parent], + capture_output=True, + check=False, + ).returncode == 0 + if not ancestor: + parents.append(main_parent) + commit_payload = json.dumps({ + "message": "fix(opencode): release required runner after one verdict read", + "tree": tree_sha, + "parents": parents, + }) + candidate_sha = run( + "gh", "api", "--method", "POST", f"repos/{repo}/git/commits", "--input", "-", "--jq", ".sha", + input_text=commit_payload, + ) + print(f"CANDIDATE_COMMIT_SHA={candidate_sha}") + print(f"CANDIDATE_TREE_SHA={tree_sha}") + print(f"CANDIDATE_PARENT_SHA={writer_parent}") + print(f"CANDIDATE_MAIN_SHA={main_parent}") + PY From 840845fc460c4a35c0b3f3f0c98ce5ca26d04602 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 20:43:22 +0900 Subject: [PATCH 32/59] test(opencode): forbid heuristic wake retry allocation --- ...est_opencode_event_driven_required_wake.py | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 tests/test_opencode_event_driven_required_wake.py diff --git a/tests/test_opencode_event_driven_required_wake.py b/tests/test_opencode_event_driven_required_wake.py new file mode 100644 index 0000000000..8f12ff0f19 --- /dev/null +++ b/tests/test_opencode_event_driven_required_wake.py @@ -0,0 +1,66 @@ +"""Contracts for event-driven Required OpenCode Review wake reconciliation.""" + +from pathlib import Path + + +REQUIRED = Path(".github/workflows/opencode-review.yml") +DISPATCH = Path(".github/workflows/opencode-review-dispatch.yml") +SCHEDULER = Path(".github/workflows/pr-review-merge-scheduler.yml") + + +def _required_verdict_step() -> str: + """Return only the current-head formal-verdict admission step.""" + workflow = REQUIRED.read_text(encoding="utf-8") + return workflow.split( + " - name: Fail closed without a current-head OpenCode verdict\n", 1 + )[1].split("\n cancel-superseded-opencode-review-runs:\n", 1)[0] + + +def _dispatch_wake_step() -> str: + """Return only the exact-run wake step in the privileged dispatch.""" + workflow = DISPATCH.read_text(encoding="utf-8") + return workflow.split( + " - name: Wake exact-head required OpenCode workflow\n", 1 + )[1].split("\n - name: Publish repository_dispatch OpenCode status\n", 1)[0] + + +def test_required_verdict_admission_has_no_repository_authored_wait_allocation() -> None: + """A missing verdict fails closed after authoritative state reads, not elapsed time.""" + step = _required_verdict_step() + for token in ( + "while :; do", + "poll_interval_seconds", + "poll_deadline_epoch", + "max_poll_transport_failures", + "sleep ", + "timeout ", + ): + assert token not in step + + +def test_dispatch_wake_has_no_fixed_retry_delay_or_transport_deadline() -> None: + """The receipt path performs one exact-state transition without a retry budget.""" + step = _dispatch_wake_step() + for token in ("for attempt", "seq 1", "sleep ", "timeout ", "/12"): + assert token not in step + assert "run_started_at" in step + assert "submitted_at" in step + assert "rerun-failed-jobs" in step + + +def test_workflow_run_completion_reconciles_new_formal_review_evidence() -> None: + """GitHub's completed-workflow event closes the review-before-failure race.""" + scheduler = SCHEDULER.read_text(encoding="utf-8") + assert 'workflows: ["Required OpenCode Review", "Strix Security Scan"]' in scheduler + assert "reconcile-opencode-required-verdict:" in scheduler + reconciliation = scheduler.split(" reconcile-opencode-required-verdict:\n", 1)[1].split( + "\n ", 1 + )[0] + assert "github.event_name == 'workflow_run'" in reconciliation + assert "github.event.workflow_run.name == 'Required OpenCode Review'" in reconciliation + assert "github.event.workflow_run.conclusion == 'failure'" in reconciliation + assert "github.event.workflow_run.run_started_at" in reconciliation + assert "submitted_at" in reconciliation + assert "rerun-failed-jobs" in reconciliation + for token in ("for attempt", "while :; do", "sleep ", "timeout "): + assert token not in reconciliation From 731819085cc9cd8ea68fc01094e1946748afa0c5 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 20:46:48 +0900 Subject: [PATCH 33/59] fix(opencode): stage event-driven verdict wake repair --- .../ci/temp_pr1706_event_driven_wake_v4.py | 431 ++++++++++++++++++ 1 file changed, 431 insertions(+) create mode 100644 scripts/ci/temp_pr1706_event_driven_wake_v4.py diff --git a/scripts/ci/temp_pr1706_event_driven_wake_v4.py b/scripts/ci/temp_pr1706_event_driven_wake_v4.py new file mode 100644 index 0000000000..15de5948cf --- /dev/null +++ b/scripts/ci/temp_pr1706_event_driven_wake_v4.py @@ -0,0 +1,431 @@ +"""Replace PR #1706 wake heuristics with exact-state GitHub event reconciliation. + +Temporary source-fix helper. The publication workflow deletes this file after +RED -> GREEN verification and exact-head publication. +""" + +from __future__ import annotations + +from pathlib import Path + + +REQUIRED = Path(".github/workflows/opencode-review.yml") +DISPATCH = Path(".github/workflows/opencode-review-dispatch.yml") +SCHEDULER = Path(".github/workflows/pr-review-merge-scheduler.yml") +ACCEPTANCE = Path("tests/test_opencode_required_verdict_runner_release.py") +SELF = Path("tests/test_opencode_poll_self_retirement.py") +EVENT = Path("tests/test_opencode_event_driven_required_wake.py") +ARCHITECTURE = Path("ARCHITECTURE.md") +DOCTORING = Path("docs/doctoring/opencode-stale-poll-self-retirement.md") +BASELINE = Path("docs/product-technical-gap-baseline.md") +CHANGELOG = Path("CHANGELOG.md") + + +def replace_once(text: str, old: str, new: str, label: str) -> str: + """Replace one exact fragment and fail closed on concurrent drift.""" + count = text.count(old) + if count != 1: + raise SystemExit(f"{label} drifted: expected one exact match, found {count}") + return text.replace(old, new, 1) + + +# The prior deterministic stage has already converted verdict admission to one +# authoritative PR read plus one complete paginated Reviews read. Remove the +# repository-authored 30-second transport allocation added by the obsolete v3 +# stage; GitHub/provider transport termination remains authoritative. +required = REQUIRED.read_text(encoding="utf-8") +start = " - name: Fail closed without a current-head OpenCode verdict\n" +end = "\n cancel-superseded-opencode-review-runs:\n" +if required.count(start) != 1 or required.count(end) != 1: + raise SystemExit("Required OpenCode verdict step boundaries drifted") +before, rest = required.split(start, 1) +step, after = rest.split(end, 1) +step = step.replace("timeout 30s gh api ", "gh api ") +if "timeout " in step or "while :; do" in step or "sleep " in step: + raise SystemExit("Required verdict admission still contains local time allocation") +REQUIRED.write_text(before + start + step + end + after, encoding="utf-8") + + +# A formal review receipt is new input to the exact required run. Reconcile it +# once against immutable run/PR/head identity. A failed mutation is read back +# exactly once to distinguish a concurrent state transition from a real error; +# this is idempotency verification, not a retry budget. +dispatch = DISPATCH.read_text(encoding="utf-8") +wake_start = " - name: Wake exact-head required OpenCode workflow\n" +wake_end = "\n - name: Publish repository_dispatch OpenCode status\n" +if dispatch.count(wake_start) != 1 or dispatch.count(wake_end) != 1: + raise SystemExit("OpenCode dispatch wake boundaries drifted") +d_before, d_rest = dispatch.split(wake_start, 1) +_old_wake, d_after = d_rest.split(wake_end, 1) +wake = r''' - name: Wake exact-head required OpenCode workflow + if: >- + always() + && github.event_name == 'repository_dispatch' + && steps.formal_review_receipt.outcome == 'success' + && needs.validate-pr-metadata.outputs.target_repository != '' + && needs.validate-pr-metadata.outputs.pr_number != '' + && needs.validate-pr-metadata.outputs.head_sha != '' + && github.event.client_payload.required_run_id != '' + env: + GH_TOKEN: ${{ needs.validate-pr-metadata.outputs.target_repository == github.repository && github.token || secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN }} + GH_REPOSITORY: ${{ needs.validate-pr-metadata.outputs.target_repository }} + PR_NUMBER: ${{ needs.validate-pr-metadata.outputs.pr_number }} + PR_HEAD_SHA: ${{ needs.validate-pr-metadata.outputs.head_sha }} + REQUIRED_RUN_ID: ${{ github.event.client_payload.required_run_id }} + WAKE_TOKEN_SOURCE: ${{ needs.validate-pr-metadata.outputs.target_repository == github.repository && 'github-token' || secrets.PR_REVIEW_MERGE_TOKEN != '' && 'PR_REVIEW_MERGE_TOKEN' || secrets.OPENCODE_APPROVE_TOKEN != '' && 'OPENCODE_APPROVE_TOKEN' || 'unavailable' }} + run: | + set -euo pipefail + if [ -z "${GH_TOKEN:-}" ] || [ "$WAKE_TOKEN_SOURCE" = "unavailable" ]; then + echo "::error::Actions-capable wake credential is unavailable. Native runs use github.token; sibling runs require PR_REVIEW_MERGE_TOKEN or OPENCODE_APPROVE_TOKEN." + exit 1 + fi + [[ "$REQUIRED_RUN_ID" =~ ^[1-9][0-9]*$ ]] || { echo "::error::Required OpenCode run id is missing or non-canonical."; exit 1; } + [[ "$PR_NUMBER" =~ ^[1-9][0-9]*$ ]] || { echo "::error::Required OpenCode PR number is missing or non-canonical."; exit 1; } + [[ "$PR_HEAD_SHA" =~ ^[0-9a-fA-F]{40}$ ]] || { echo "::error::Required OpenCode PR head SHA is missing or malformed."; exit 1; } + if ! run="$(gh api "repos/${GH_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}")"; then + echo "::error::Exact required-run lookup failed; no retry policy is invented." + exit 1 + fi + required_run="$(printf '%s\n' "$run" | jq -r --arg head "$PR_HEAD_SHA" --arg pr "$PR_NUMBER" --argjson run_id "$REQUIRED_RUN_ID" ' + select(.id == $run_id) + | select(.event == "pull_request_target") + | select(.path == ".github/workflows/opencode-review.yml") + | select(any((.pull_requests // [])[]?; ((.number // 0) | tostring) == $pr and ((.head.sha // "") | ascii_downcase) == ($head | ascii_downcase))) + | [(.id // ""), (.status // ""), (.conclusion // ""), (.run_started_at // "")] + | @tsv + ')" || required_run="" + if [ -z "$required_run" ]; then + echo "::error::Referenced Required OpenCode Review run does not match the exact PR/head/workflow identity." + exit 1 + fi + IFS=$'\t' read -r required_run_id required_status required_conclusion run_started_at <<<"$required_run" + if [ "$required_status" = "completed" ] && [ "$required_conclusion" = "success" ]; then + echo "Exact-PR/head Required OpenCode Review run ${required_run_id} already succeeded." + exit 0 + fi + if ! reviews="$(gh api --paginate "repos/${GH_REPOSITORY}/pulls/${PR_NUMBER}/reviews")"; then + echo "::error::Reviews API read failed during exact-run wake reconciliation; failing closed." + exit 1 + fi + latest_review="$(printf '%s\n' "$reviews" | jq -r -s --arg sha "$PR_HEAD_SHA" ' + (add // []) + | [.[] + | select((.user.login // "" | ascii_downcase) as $user | $user == "opencode-agent" or $user == "opencode-agent[bot]") + | select((.commit_id // "" | ascii_downcase) == ($sha | ascii_downcase)) + | select(.state == "APPROVED" or .state == "CHANGES_REQUESTED") + | select((.body // "" | ascii_downcase | contains("deterministic current-head evidence")) | not) + | select((.body // "" | ascii_downcase | contains("deterministic fallback approval")) | not) + | select((.body // "" | ascii_downcase | contains("model-unavailable evidence fallback")) | not) + | select((.body // "" | ascii_downcase | contains("did not emit a usable current-head control block")) | not) + | select((.body // "" | ascii_downcase | contains("scope: `unsupported`")) | not) + | select((.body // "" | ascii_downcase | contains("model-pool outcome: `unknown`")) | not)] + | sort_by(.submitted_at // "", .id // 0) + | (last // {}) + | [(.state // ""), (.submitted_at // "")] + | @tsv + ')" + IFS=$'\t' read -r review_state review_submitted_at <<<"$latest_review" + if [ -z "$review_state" ] || [ -z "$review_submitted_at" ] || [ -z "$run_started_at" ]; then + echo "::error::Formal review or run-start provenance is incomplete; failing closed." + exit 1 + fi + if [ "$required_status" != "completed" ] || [ "$required_conclusion" != "failure" ]; then + echo "Exact required run has not completed as a failed run; GitHub workflow_run completion reconciliation owns any later transition." + exit 0 + fi + new_evidence="$(jq -nr --arg review "$review_submitted_at" --arg started "$run_started_at" 'try (($review | fromdateiso8601) > ($started | fromdateiso8601)) catch false')" + if [ "$new_evidence" != "true" ]; then + echo "Formal review is not newer than this run attempt; refusing an evidence-free rerun." + exit 0 + fi + if gh api -X POST "repos/${GH_REPOSITORY}/actions/runs/${required_run_id}/rerun-failed-jobs" >/dev/null; then + echo "Re-ran failed jobs for exact-PR/head Required OpenCode Review run ${required_run_id} after newer formal evidence." + exit 0 + fi + if ! advanced="$(gh api "repos/${GH_REPOSITORY}/actions/runs/${required_run_id}")"; then + echo "::error::Rerun mutation failed and exact-run readback is unavailable; failing closed." + exit 1 + fi + advanced_state="$(printf '%s\n' "$advanced" | jq -r '[.status // "", .conclusion // ""] | @tsv')" + if [ "$advanced_state" != $'completed\tfailure' ]; then + echo "Exact required run advanced concurrently; no duplicate rerun is needed." + exit 0 + fi + echo "::error::Exact required run remains failed after the rerun mutation failed." + exit 1 +''' +DISPATCH.write_text(d_before + wake + wake_end + d_after, encoding="utf-8") + + +# GitHub documents workflow_run/completed as an event emitted after a workflow +# finishes. This closes the opposite race: if the formal review arrived while +# the required run was still executing, the completion event observes that new +# evidence and performs the exact rerun. There is no clock, retry count, sleep, +# or hand-authored sampling budget. +scheduler = SCHEDULER.read_text(encoding="utf-8") +job_marker = "jobs:\n scan-pr-queue:\n" +if scheduler.count(job_marker) != 1: + raise SystemExit("Scheduler jobs insertion boundary drifted") +reconcile_job = r'''jobs: + reconcile-opencode-required-verdict: + name: reconcile-opencode-required-verdict + if: >- + github.event_name == 'workflow_run' + && github.event.workflow_run.name == 'Required OpenCode Review' + && github.event.workflow_run.conclusion == 'failure' + && github.event.workflow_run.event == 'pull_request_target' + && github.event.workflow_run.path == '.github/workflows/opencode-review.yml' + && github.event.workflow_run.pull_requests[0].number + runs-on: ubuntu-slim + permissions: + actions: write + contents: read + pull-requests: read + env: + GH_TOKEN: ${{ github.token }} + PR_NUMBER: ${{ github.event.workflow_run.pull_requests[0].number }} + PR_HEAD_SHA: ${{ github.event.workflow_run.pull_requests[0].head.sha }} + REQUIRED_RUN_ID: ${{ github.event.workflow_run.id }} + REQUIRED_RUN_STARTED_AT: ${{ github.event.workflow_run.run_started_at }} + steps: + - name: Reconcile newer formal review evidence with the completed required run + shell: bash + run: | + set -euo pipefail + [[ "$PR_NUMBER" =~ ^[1-9][0-9]*$ ]] || { echo "::error::workflow_run PR number is missing or non-canonical."; exit 1; } + [[ "$PR_HEAD_SHA" =~ ^[0-9a-fA-F]{40}$ ]] || { echo "::error::workflow_run PR head is missing or malformed."; exit 1; } + [[ "$REQUIRED_RUN_ID" =~ ^[1-9][0-9]*$ ]] || { echo "::error::workflow_run id is missing or non-canonical."; exit 1; } + if [ -z "$REQUIRED_RUN_STARTED_AT" ]; then + echo "::error::workflow_run start provenance is missing; failing closed." + exit 1 + fi + if ! live_pr="$(gh api "repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}")"; then + echo "::error::Live PR read failed during event-driven Required OpenCode reconciliation; failing closed." + exit 1 + fi + live_head="$(printf '%s\n' "$live_pr" | jq -r '.head.sha // empty')" + live_state="$(printf '%s\n' "$live_pr" | jq -r '.state // empty')" + live_draft="$(printf '%s\n' "$live_pr" | jq -r 'if (.draft | type) == "boolean" then (.draft | tostring) else empty end')" + if [ "$live_state" != "open" ] || [ "$live_draft" = "true" ] || [ "${live_head,,}" != "${PR_HEAD_SHA,,}" ]; then + echo "Required run is no longer authoritative for an open ready exact-head PR; no wake mutation is allowed." + exit 0 + fi + if ! reviews="$(gh api --paginate "repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}/reviews")"; then + echo "::error::Reviews API read failed during event-driven Required OpenCode reconciliation; failing closed." + exit 1 + fi + latest_review="$(printf '%s\n' "$reviews" | jq -r -s --arg sha "$PR_HEAD_SHA" ' + (add // []) + | [.[] + | select((.user.login // "" | ascii_downcase) as $user | $user == "opencode-agent" or $user == "opencode-agent[bot]") + | select((.commit_id // "" | ascii_downcase) == ($sha | ascii_downcase)) + | select(.state == "APPROVED" or .state == "CHANGES_REQUESTED") + | select((.body // "" | ascii_downcase | contains("deterministic current-head evidence")) | not) + | select((.body // "" | ascii_downcase | contains("deterministic fallback approval")) | not) + | select((.body // "" | ascii_downcase | contains("model-unavailable evidence fallback")) | not) + | select((.body // "" | ascii_downcase | contains("did not emit a usable current-head control block")) | not) + | select((.body // "" | ascii_downcase | contains("scope: `unsupported`")) | not) + | select((.body // "" | ascii_downcase | contains("model-pool outcome: `unknown`")) | not)] + | sort_by(.submitted_at // "", .id // 0) + | (last // {}) + | [(.state // ""), (.submitted_at // "")] + | @tsv + ')" + IFS=$'\t' read -r review_state review_submitted_at <<<"$latest_review" + if [ -z "$review_state" ]; then + echo "No formal exact-head OpenCode review exists yet; the later review receipt event owns reconciliation." + exit 0 + fi + if [ -z "$review_submitted_at" ]; then + echo "::error::Formal exact-head review lacks submission provenance; failing closed." + exit 1 + fi + new_evidence="$(jq -nr --arg review "$review_submitted_at" --arg started "$REQUIRED_RUN_STARTED_AT" 'try (($review | fromdateiso8601) > ($started | fromdateiso8601)) catch false')" + if [ "$new_evidence" != "true" ]; then + echo "Formal review predates this run attempt; the failure is not attributable to missing newer review evidence." + exit 0 + fi + if gh api -X POST "repos/${GITHUB_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}/rerun-failed-jobs" >/dev/null; then + echo "Re-ran failed jobs for Required OpenCode Review run ${REQUIRED_RUN_ID} after newer formal exact-head evidence." + exit 0 + fi + if ! advanced="$(gh api "repos/${GITHUB_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}")"; then + echo "::error::Rerun mutation failed and exact-run readback is unavailable; failing closed." + exit 1 + fi + advanced_state="$(printf '%s\n' "$advanced" | jq -r '[.status // "", .conclusion // ""] | @tsv')" + if [ "$advanced_state" != $'completed\tfailure' ]; then + echo "Exact required run advanced concurrently; no duplicate rerun is needed." + exit 0 + fi + echo "::error::Exact required run remains failed after the rerun mutation failed." + exit 1 + + scan-pr-queue: +''' +scheduler = replace_once(scheduler, job_marker, reconcile_job, "event reconciliation job") +SCHEDULER.write_text(scheduler, encoding="utf-8") + + +# Replace temporary v3 tests that encoded 30-second and 12-attempt policy with +# invariants over causal event/state transitions. +ACCEPTANCE.write_text(r'''"""Regression coverage for one-shot, event-driven Required OpenCode verdict admission.""" + +from pathlib import Path + +REQUIRED = Path(".github/workflows/opencode-review.yml") +DISPATCH = Path(".github/workflows/opencode-review-dispatch.yml") +SCHEDULER = Path(".github/workflows/pr-review-merge-scheduler.yml") + + +def _required() -> str: + """Return only the formal-verdict admission step.""" + text = REQUIRED.read_text(encoding="utf-8") + return text.split(" - name: Fail closed without a current-head OpenCode verdict\n", 1)[1].split("\n cancel-superseded-opencode-review-runs:\n", 1)[0] + + +def _wake() -> str: + """Return only the dispatch exact-run wake step.""" + text = DISPATCH.read_text(encoding="utf-8") + return text.split(" - name: Wake exact-head required OpenCode workflow\n", 1)[1].split("\n - name: Publish repository_dispatch OpenCode status\n", 1)[0] + + +def test_missing_verdict_releases_runner_without_local_time_allocation() -> None: + """Required admission performs complete state reads once and then fails closed.""" + step = _required() + assert step.count('gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}"') == 1 + assert step.count('gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews') == 1 + for token in ("while :; do", "poll_interval_seconds", "poll_deadline_epoch", "sleep ", "timeout "): + assert token not in step + + +def test_dispatch_wake_is_exact_state_not_retry_budget() -> None: + """Receipt reconciliation is one exact-state transition plus idempotent readback.""" + step = _wake() + assert "pull_requests // []" in step + assert "run_started_at" in step + assert "submitted_at" in step + assert "fromdateiso8601" in step + assert "rerun-failed-jobs" in step + assert "advanced concurrently" in step + for token in ("for attempt", "while :; do", "seq 1", "sleep ", "timeout ", "/12"): + assert token not in step + + +def test_completed_workflow_event_closes_review_before_failure_race() -> None: + """The scheduler reacts to GitHub's completed Required OpenCode workflow event.""" + text = SCHEDULER.read_text(encoding="utf-8") + job = text.split(" reconcile-opencode-required-verdict:\n", 1)[1].split("\n scan-pr-queue:\n", 1)[0] + assert "github.event_name == 'workflow_run'" in job + assert "github.event.workflow_run.name == 'Required OpenCode Review'" in job + assert "github.event.workflow_run.conclusion == 'failure'" in job + assert "github.event.workflow_run.run_started_at" in job + assert "review_submitted_at" in job + assert "rerun-failed-jobs" in job + for token in ("for attempt", "while :; do", "sleep ", "timeout "): + assert token not in job +''', encoding="utf-8") + +SELF.write_text(r'''"""Regression contract for self-releasing Required OpenCode verdict admission.""" + +from pathlib import Path + +WORKFLOW = Path(".github/workflows/opencode-review.yml") + + +def _step() -> str: + """Return only the one-shot exact-head verdict-admission step.""" + text = WORKFLOW.read_text(encoding="utf-8") + return text.split(" - name: Fail closed without a current-head OpenCode verdict\n", 1)[1].split("\n cancel-superseded-opencode-review-runs:\n", 1)[0] + + +def test_one_shot_revalidates_live_authority_before_review_evidence() -> None: + """Live PR/head/draft/state authority precedes the complete Reviews read.""" + step = _step() + live = 'gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}"' + reviews = 'gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews' + assert live in step and reviews in step and step.index(live) < step.index(reviews) + assert "Could not validate live pull request state before verdict admission" in step + assert "PR is still a draft" in step + assert "fresh required-review run will bind the current head" in step + + +def test_one_shot_has_no_repository_authored_wait_retry_or_transport_deadline() -> None: + """No elapsed-time or fixed-attempt policy governs formal verdict admission.""" + step = _step() + for token in ("while :; do", "poll_interval_seconds", "poll_deadline_epoch", "max_poll_transport_failures", "sleep ", "timeout "): + assert token not in step +''', encoding="utf-8") + +EVENT.write_text(r'''"""Contracts for event-driven Required OpenCode Review wake reconciliation.""" + +from pathlib import Path + +REQUIRED = Path(".github/workflows/opencode-review.yml") +DISPATCH = Path(".github/workflows/opencode-review-dispatch.yml") +SCHEDULER = Path(".github/workflows/pr-review-merge-scheduler.yml") + + +def _required_verdict_step() -> str: + """Return only the current-head formal-verdict admission step.""" + text = REQUIRED.read_text(encoding="utf-8") + return text.split(" - name: Fail closed without a current-head OpenCode verdict\n", 1)[1].split("\n cancel-superseded-opencode-review-runs:\n", 1)[0] + + +def _dispatch_wake_step() -> str: + """Return only the exact-run wake step in the privileged dispatch.""" + text = DISPATCH.read_text(encoding="utf-8") + return text.split(" - name: Wake exact-head required OpenCode workflow\n", 1)[1].split("\n - name: Publish repository_dispatch OpenCode status\n", 1)[0] + + +def test_required_verdict_admission_has_no_repository_authored_wait_allocation() -> None: + """A missing verdict fails closed after authoritative state reads, not elapsed time.""" + step = _required_verdict_step() + for token in ("while :; do", "poll_interval_seconds", "poll_deadline_epoch", "max_poll_transport_failures", "sleep ", "timeout "): + assert token not in step + + +def test_dispatch_wake_has_no_fixed_retry_delay_or_transport_deadline() -> None: + """The receipt path performs one exact-state transition without a retry budget.""" + step = _dispatch_wake_step() + for token in ("for attempt", "seq 1", "sleep ", "timeout ", "/12"): + assert token not in step + assert "run_started_at" in step and "submitted_at" in step and "rerun-failed-jobs" in step + + +def test_workflow_run_completion_reconciles_new_formal_review_evidence() -> None: + """GitHub's completed-workflow event closes the review-before-failure race.""" + scheduler = SCHEDULER.read_text(encoding="utf-8") + job = scheduler.split(" reconcile-opencode-required-verdict:\n", 1)[1].split("\n scan-pr-queue:\n", 1)[0] + assert "github.event_name == 'workflow_run'" in job + assert "github.event.workflow_run.name == 'Required OpenCode Review'" in job + assert "github.event.workflow_run.conclusion == 'failure'" in job + assert "github.event.workflow_run.run_started_at" in job + assert "submitted_at" in job and "rerun-failed-jobs" in job + for token in ("for attempt", "while :; do", "sleep ", "timeout "): + assert token not in job +''', encoding="utf-8") + + +architecture = ARCHITECTURE.read_text(encoding="utf-8") +heading = "### Required OpenCode event-driven verdict admission" +if heading not in architecture: + architecture += f'''\n\n{heading}\n\nThe required workflow performs one live-PR read and one complete paginated formal-review read, then fails closed immediately if no exact-head verdict exists. The privileged review receipt reconciles the immutable required-run id once. If the formal review arrives before that run finishes, GitHub's documented `workflow_run: completed` event performs the complementary reconciliation. Rerun admission is caused by a formal review whose `submitted_at` is later than that run attempt's `run_started_at`; the same evidence therefore cannot create an unbounded rerun cycle. No repository-authored polling cadence, retry count, sleep, transport timeout, or model reasoning deadline is part of this state machine.\n''' + ARCHITECTURE.write_text(architecture, encoding="utf-8") + +doctoring = DOCTORING.read_text(encoding="utf-8") +heading = "### 2026-09-02 event-driven wake supersedes fixed retry allocation" +if heading not in doctoring: + doctoring += f'''\n\n{heading}\n\nRCA found that the intermediate PR #1706 repair replaced a runner-held verdict poll with a dispatch wake loop containing fixed `12` attempts, `5` second sleeps, and `30` second transport deadlines. Those values had no cited statistical model, standard, experiment, or provider contract and therefore remained decision-affecting heuristics. The corrected state machine uses GitHub's authoritative `workflow_run` `completed` event plus the formal review receipt event. A rerun is admissible only when exact PR/head/workflow identity holds and the formal review's `submitted_at` is later than the failed run attempt's `run_started_at`; mutation readback only resolves concurrent state advancement and is not a retry loop.\n\nReferences (APA 7): GitHub. (2026). *Events that trigger workflows*. GitHub Docs. https://docs.github.com/actions/using-workflows/events-that-trigger-workflows#workflow_run ; GitHub. (2026). *Webhook events and payloads: workflow_run*. GitHub Docs. https://docs.github.com/webhooks/webhook-events-and-payloads#workflow_run\n''' + DOCTORING.write_text(doctoring, encoding="utf-8") + +baseline = BASELINE.read_text(encoding="utf-8") +heading = "### OPENCODE-EVENT-DRIVEN-REQUIRED-WAKE-2026-09-02" +if heading not in baseline: + baseline += f'''\n\n{heading}\n- Gap: Required OpenCode verdict admission occupied a hosted runner while waiting; an intermediate successor then introduced fixed dispatch retry/sleep/transport allocations (`12`, `5s`, `30s`) without a governing model or standard.\n- Causal owner: `ContextualWisdomLab/.github` required review and merge-control workflows.\n- Repair: one-shot exact-head verdict admission plus dual event reconciliation: formal review receipt handles review-after-failure; GitHub `workflow_run: completed` handles review-before-failure. Rerun authority is the temporal evidence relation `review.submitted_at > run.run_started_at`, exact PR/head/workflow identity, and a failed run state. Concurrent mutation readback is idempotency verification, not retry allocation.\n- Evidence basis: GitHub's documented `workflow_run` completed event and immutable workflow-run/PR metadata. No caller model timeout, polling cadence, retry count, or arbitrary delay is introduced.\n- Status: Proposed on PR #1706 until exact-head focused/full CI and independent review are GREEN.\n''' + BASELINE.write_text(baseline, encoding="utf-8") + +changelog = CHANGELOG.read_text(encoding="utf-8") +note = "- Required OpenCode verdict wake is event-driven: formal-review receipt and GitHub `workflow_run: completed` reconcile exact run/PR/head state using newer-evidence provenance, replacing fixed retry/sleep/transport allocations.\n" +if note not in changelog: + CHANGELOG.write_text(note + changelog, encoding="utf-8") From f097615bcc1edb008dc40aa0dded237695cf1ba0 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 20:48:13 +0900 Subject: [PATCH 34/59] ci(opencode): execute event-driven wake RED-to-GREEN repair --- .../_temp_pr1706_event_driven_wake_v6.yml | 246 ++++++++++++++++++ 1 file changed, 246 insertions(+) create mode 100644 .github/workflows/_temp_pr1706_event_driven_wake_v6.yml diff --git a/.github/workflows/_temp_pr1706_event_driven_wake_v6.yml b/.github/workflows/_temp_pr1706_event_driven_wake_v6.yml new file mode 100644 index 0000000000..04d054f8ff --- /dev/null +++ b/.github/workflows/_temp_pr1706_event_driven_wake_v6.yml @@ -0,0 +1,246 @@ +name: Temporary PR1706 Event-driven Required Wake v6 + +on: + push: + branches: + - fix/opencode-poll-wall-clock-bound + paths: + - .github/workflows/_temp_pr1706_event_driven_wake_v6.yml + - scripts/ci/temp_pr1706_event_driven_wake_v4.py + - tests/test_opencode_event_driven_required_wake.py + +permissions: + contents: write + +concurrency: + group: temp-pr1706-event-driven-required-wake-v6 + cancel-in-progress: true + +jobs: + repair: + if: github.repository == 'ContextualWisdomLab/.github' && github.actor == 'seonghobae' + runs-on: ubuntu-slim + steps: + - name: Checkout exact trigger head without persisted credentials + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + ref: ${{ github.sha }} + fetch-depth: 0 + persist-credentials: false + + - name: Revalidate writer and reconcile protected main non-destructively + shell: bash + run: | + set -euo pipefail + remote_head="$(git ls-remote origin refs/heads/fix/opencode-poll-wall-clock-bound | cut -f1)" + test "$remote_head" = "$GITHUB_SHA" + git fetch origin main fix/opencode-poll-wall-clock-bound + git config user.name "ContextualWisdomLab automation" + git config user.email "automation@users.noreply.github.com" + git merge --no-edit origin/main + + - name: Set up Python 3.14 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.14" + + - name: Install hash-verified repository test dependencies + shell: bash + run: | + set -euo pipefail + python3 -m pip install --disable-pip-version-check --require-hashes --only-binary=:all: -r requirements-opencode-review-ci-hashes.txt + + - name: Prove no-heuristics event-wake contract is genuinely RED + shell: bash + run: | + set -euo pipefail + set +e + red_output="$(PYTHONPATH=. python3 -m pytest -q tests/test_opencode_event_driven_required_wake.py 2>&1)" + red_status=$? + set -e + printf '%s\n' "$red_output" + if [ "$red_status" -ne 1 ] || ! grep -Eq '(^|[[:space:]])[1-9][0-9]* failed([,[:space:]]|$)' <<<"$red_output"; then + echo "::error::Expected a genuine assertion RED proving local retry/time allocation or missing event reconciliation." + exit 1 + fi + + - name: Materialize one-shot admission and event-driven exact-run repair + shell: bash + run: | + set -euo pipefail + PYTHONPATH=. python3 scripts/ci/temp_pr1706_one_shot_runner_release.py + python3 - <<'PY' + from pathlib import Path + + driver = Path("scripts/ci/temp_pr1706_one_shot_runner_release_v3.py") + text = driver.read_text(encoding="utf-8") + start_token = 'workflow = WORKFLOW.read_text(encoding="utf-8")\n' + end_token = 'WORKFLOW.write_text(workflow, encoding="utf-8")\n' + start = text.find(start_token) + end_start = text.find(end_token, start + len(start_token)) + if start < 0 or end_start < 0: + raise SystemExit("v3 transport block drifted") + end = end_start + len(end_token) + replacement = '''workflow = WORKFLOW.read_text(encoding="utf-8") + step_start = " - name: Fail closed without a current-head OpenCode verdict\\n" + step_end = "\\n cancel-superseded-opencode-review-runs:\\n" + if workflow.count(step_start) != 1 or workflow.count(step_end) != 1: + raise SystemExit("OpenCode required-verdict step boundaries drifted before intermediate transport stage") + before, rest = workflow.split(step_start, 1) + target, after = rest.split(step_end, 1) + target = replace_once( + target, + ' live_pr="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"\\n', + ' if ! live_pr="$(timeout 30s gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"; then\\n' + ' echo "::error::Live pull request API read failed during one-shot current-head verdict admission; failing closed and releasing the runner."\\n' + ' exit 1\\n' + ' fi\\n', + "intermediate live PR read", + ) + target = replace_once( + target, + ' if ! reviews="$(gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"; then\\n', + ' if ! reviews="$(timeout 30s gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"; then\\n', + "intermediate Reviews read", + ) + WORKFLOW.write_text(before + step_start + target + step_end + after, encoding="utf-8") + ''' + text = text[:start] + replacement + text[end:] + brittle = '''regression = replace_once(regression, ' "PR_HEAD_SHA": HEAD,\\n "REQUIRED_RUN_ID": "42",\\n', ' "PR_NUMBER": "1437",\\n "PR_HEAD_SHA": HEAD,\\n "REQUIRED_RUN_ID": "42",\\n', "formal wake PR identity fixture") + ''' + robust = '''wake_env = ' "PR_HEAD_SHA": HEAD,\\n "REQUIRED_RUN_ID": "42",\\n' + wake_env_with_pr = ' "PR_NUMBER": "1437",\\n "PR_HEAD_SHA": HEAD,\\n "REQUIRED_RUN_ID": "42",\\n' + wake_env_count = regression.count(wake_env) + if wake_env_count != 2: + raise SystemExit(f"formal wake PR identity fixtures drifted: expected 2 exact matches, found {wake_env_count}") + regression = regression.replace(wake_env, wake_env_with_pr) + ''' + if text.count(brittle) != 1: + raise SystemExit("v3 formal wake PR identity repair block drifted") + driver.write_text(text.replace(brittle, robust, 1), encoding="utf-8") + PY + PYTHONPATH=. python3 scripts/ci/temp_pr1706_one_shot_runner_release_v3.py + PYTHONPATH=. python3 scripts/ci/temp_pr1706_event_driven_wake_v4.py + python3 - <<'PY' + import re + from pathlib import Path + + # v3 deliberately represented the superseded intermediate design so + # its old prose must not survive beside the event-driven contract. + replacements = { + Path("ARCHITECTURE.md"): { + "A bounded retry absorbs transient GitHub run-lookup failures; model reasoning itself has no caller wall-clock deadline.": + "GitHub's documented workflow-run completion event complements the formal-review receipt event; model reasoning has no caller wall-clock deadline and no fixed wake retry allocation is retained.", + }, + Path("docs/doctoring/opencode-stale-poll-self-retirement.md"): { + "Transient run-lookup failures are retried within a bounded transport loop so a posted formal verdict cannot be stranded by one GitHub API timeout.": + "Run-lookup transport failure fails closed without inventing a retry policy; the independent GitHub workflow-run completion event closes the opposite review-before-failure ordering.", + }, + Path("CHANGELOG.md"): { + "- Required OpenCode Review now releases its runner after one exact-head verdict admission read; exact-run wake binds PR number plus `pull_requests[].head.sha` and tolerates bounded transient GitHub run-lookup failures without imposing a model reasoning timeout.\n": + "", + }, + } + for path, mapping in replacements.items(): + text = path.read_text(encoding="utf-8") + for old, new in mapping.items(): + if old in text: + text = text.replace(old, new) + path.write_text(text, encoding="utf-8") + + # The dispatch workflow is a trusted immutable blob input to the + # autofix contract; rebind it after the final event-driven patch. + import subprocess + dispatch_blob = subprocess.check_output( + ["git", "hash-object", ".github/workflows/opencode-review-dispatch.yml"], + text=True, + ).strip() + path = Path("tests/test_pr_review_autofix_nvidia_nim_contract.py") + text = path.read_text(encoding="utf-8") + pattern = r'REVIEW_DISPATCH_BLOB_SHA = "[0-9a-f]{40}"' + matches = re.findall(pattern, text) + if len(matches) != 1: + raise SystemExit(f"review-dispatch blob pin drifted: expected one assignment, found {len(matches)}") + path.write_text(re.sub(pattern, f'REVIEW_DISPATCH_BLOB_SHA = "{dispatch_blob}"', text, count=1), encoding="utf-8") + PY + git diff --check + + - name: Verify focused GREEN contracts + shell: bash + run: | + set -euo pipefail + PYTHONPATH=. python3 -m pytest -q \ + tests/test_opencode_event_driven_required_wake.py \ + tests/test_opencode_required_verdict_runner_release.py \ + tests/test_opencode_poll_rate_budget.py \ + tests/test_opencode_poll_self_retirement.py \ + tests/test_opencode_required_verdict_regression.py \ + tests/test_opencode_live_draft_state_regression.py \ + tests/test_opencode_rust_coverage_toolchain_contract.py \ + tests/test_pr_review_autofix_nvidia_nim_contract.py + python3 -m compileall -q scripts tests + git diff --check + + - name: Verify broader repository suite and public documentation coverage + shell: bash + run: | + set -euo pipefail + PYTHONPATH=. python3 -m pytest tests -q + interrogate -c pyproject.toml scripts tests + git diff --check + + - name: Remove completed source-fix machinery and verify publication scope + shell: bash + run: | + set -euo pipefail + rm -f \ + .github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml \ + .github/workflows/_temp_pr1706_one_shot_runner_release_v5.yml \ + .github/workflows/_temp_pr1706_event_driven_wake_v6.yml \ + scripts/ci/temp_pr1706_one_shot_runner_release.py \ + scripts/ci/temp_pr1706_one_shot_runner_release_v2.py \ + scripts/ci/temp_pr1706_one_shot_runner_release_v3.py \ + scripts/ci/temp_pr1706_event_driven_wake_v4.py + git diff --check + while IFS= read -r changed; do + case "$changed" in + .github/workflows/opencode-review.yml|\ + .github/workflows/opencode-review-dispatch.yml|\ + .github/workflows/pr-review-merge-scheduler.yml|\ + .github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml|\ + .github/workflows/_temp_pr1706_one_shot_runner_release_v5.yml|\ + .github/workflows/_temp_pr1706_event_driven_wake_v6.yml|\ + scripts/ci/temp_pr1706_one_shot_runner_release.py|\ + scripts/ci/temp_pr1706_one_shot_runner_release_v2.py|\ + scripts/ci/temp_pr1706_one_shot_runner_release_v3.py|\ + scripts/ci/temp_pr1706_event_driven_wake_v4.py|\ + tests/test_opencode_event_driven_required_wake.py|\ + tests/test_opencode_required_verdict_runner_release.py|\ + tests/test_opencode_poll_rate_budget.py|\ + tests/test_opencode_poll_self_retirement.py|\ + tests/test_opencode_required_verdict_regression.py|\ + tests/test_opencode_live_draft_state_regression.py|\ + tests/test_pr_review_autofix_nvidia_nim_contract.py|\ + docs/product-technical-gap-baseline.md|\ + docs/doctoring/opencode-stale-poll-self-retirement.md|\ + ARCHITECTURE.md|CHANGELOG.md) ;; + *) echo "::error::Unexpected publication path: $changed"; exit 1 ;; + esac + done < <(git diff --name-only HEAD) + + - name: Publish verified successor from unchanged exact writer head + env: + EXPECTED_REMOTE_HEAD: ${{ github.sha }} + PUSH_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN || github.token }} + shell: bash + run: | + set -euo pipefail + remote_head="$(git ls-remote origin refs/heads/fix/opencode-poll-wall-clock-bound | cut -f1)" + test "$remote_head" = "$EXPECTED_REMOTE_HEAD" + git add -A + git diff --cached --check + git commit -m "fix(opencode): reconcile required verdict by exact events" + remote_head="$(git ls-remote origin refs/heads/fix/opencode-poll-wall-clock-bound | cut -f1)" + test "$remote_head" = "$EXPECTED_REMOTE_HEAD" + git remote set-url origin "https://x-access-token:${PUSH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" + git push origin HEAD:refs/heads/fix/opencode-poll-wall-clock-bound From 21bf1f79a00555fe0f4be797ebac4a426a059094 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 20:57:21 +0900 Subject: [PATCH 35/59] fix(opencode): repair event wake source writer --- .../ci/temp_pr1706_event_driven_wake_v4.py | 211 +++++++++--------- 1 file changed, 104 insertions(+), 107 deletions(-) diff --git a/scripts/ci/temp_pr1706_event_driven_wake_v4.py b/scripts/ci/temp_pr1706_event_driven_wake_v4.py index 15de5948cf..9c36444c5a 100644 --- a/scripts/ci/temp_pr1706_event_driven_wake_v4.py +++ b/scripts/ci/temp_pr1706_event_driven_wake_v4.py @@ -1,7 +1,7 @@ -"""Replace PR #1706 wake heuristics with exact-state GitHub event reconciliation. +"""Materialize PR #1706 one-shot admission and event-driven wake repair. -Temporary source-fix helper. The publication workflow deletes this file after -RED -> GREEN verification and exact-head publication. +Temporary source-fix helper. The successful publication workflow deletes this +file and every other PR #1706 source-fix artifact from the final tree. """ from __future__ import annotations @@ -15,6 +15,7 @@ ACCEPTANCE = Path("tests/test_opencode_required_verdict_runner_release.py") SELF = Path("tests/test_opencode_poll_self_retirement.py") EVENT = Path("tests/test_opencode_event_driven_required_wake.py") +REGRESSION = Path("tests/test_opencode_required_verdict_regression.py") ARCHITECTURE = Path("ARCHITECTURE.md") DOCTORING = Path("docs/doctoring/opencode-stale-poll-self-retirement.md") BASELINE = Path("docs/product-technical-gap-baseline.md") @@ -29,10 +30,9 @@ def replace_once(text: str, old: str, new: str, label: str) -> str: return text.replace(old, new, 1) -# The prior deterministic stage has already converted verdict admission to one -# authoritative PR read plus one complete paginated Reviews read. Remove the -# repository-authored 30-second transport allocation added by the obsolete v3 -# stage; GitHub/provider transport termination remains authoritative. +# The prior deterministic stage converts verdict admission to one authoritative +# PR read plus one complete Reviews read. Remove its temporary transport budget: +# repository policy must not turn GitHub I/O latency into review semantics. required = REQUIRED.read_text(encoding="utf-8") start = " - name: Fail closed without a current-head OpenCode verdict\n" end = "\n cancel-superseded-opencode-review-runs:\n" @@ -41,15 +41,15 @@ def replace_once(text: str, old: str, new: str, label: str) -> str: before, rest = required.split(start, 1) step, after = rest.split(end, 1) step = step.replace("timeout 30s gh api ", "gh api ") -if "timeout " in step or "while :; do" in step or "sleep " in step: - raise SystemExit("Required verdict admission still contains local time allocation") +if any(token in step for token in ("timeout ", "while :; do", "sleep ")): + raise SystemExit("Required verdict admission still contains local wait allocation") REQUIRED.write_text(before + start + step + end + after, encoding="utf-8") -# A formal review receipt is new input to the exact required run. Reconcile it -# once against immutable run/PR/head identity. A failed mutation is read back -# exactly once to distinguish a concurrent state transition from a real error; -# this is idempotency verification, not a retry budget. +# The formal-review receipt is already authenticated and exact-head validated by +# the preceding dispatch steps. Reconcile that new event against the immutable +# required run exactly once. If the run has not failed yet, do nothing here: the +# workflow_run completion reconciliation below owns the opposite event ordering. dispatch = DISPATCH.read_text(encoding="utf-8") wake_start = " - name: Wake exact-head required OpenCode workflow\n" wake_end = "\n - name: Publish repository_dispatch OpenCode status\n" @@ -91,55 +91,24 @@ def replace_once(text: str, old: str, new: str, label: str) -> str: | select(.event == "pull_request_target") | select(.path == ".github/workflows/opencode-review.yml") | select(any((.pull_requests // [])[]?; ((.number // 0) | tostring) == $pr and ((.head.sha // "") | ascii_downcase) == ($head | ascii_downcase))) - | [(.id // ""), (.status // ""), (.conclusion // ""), (.run_started_at // "")] + | [(.id // ""), (.status // ""), (.conclusion // "")] | @tsv - ')" || required_run="" + ')" || required_run="" if [ -z "$required_run" ]; then echo "::error::Referenced Required OpenCode Review run does not match the exact PR/head/workflow identity." exit 1 fi - IFS=$'\t' read -r required_run_id required_status required_conclusion run_started_at <<<"$required_run" + IFS=$'\t' read -r required_run_id required_status required_conclusion <<<"$required_run" if [ "$required_status" = "completed" ] && [ "$required_conclusion" = "success" ]; then echo "Exact-PR/head Required OpenCode Review run ${required_run_id} already succeeded." exit 0 fi - if ! reviews="$(gh api --paginate "repos/${GH_REPOSITORY}/pulls/${PR_NUMBER}/reviews")"; then - echo "::error::Reviews API read failed during exact-run wake reconciliation; failing closed." - exit 1 - fi - latest_review="$(printf '%s\n' "$reviews" | jq -r -s --arg sha "$PR_HEAD_SHA" ' - (add // []) - | [.[] - | select((.user.login // "" | ascii_downcase) as $user | $user == "opencode-agent" or $user == "opencode-agent[bot]") - | select((.commit_id // "" | ascii_downcase) == ($sha | ascii_downcase)) - | select(.state == "APPROVED" or .state == "CHANGES_REQUESTED") - | select((.body // "" | ascii_downcase | contains("deterministic current-head evidence")) | not) - | select((.body // "" | ascii_downcase | contains("deterministic fallback approval")) | not) - | select((.body // "" | ascii_downcase | contains("model-unavailable evidence fallback")) | not) - | select((.body // "" | ascii_downcase | contains("did not emit a usable current-head control block")) | not) - | select((.body // "" | ascii_downcase | contains("scope: `unsupported`")) | not) - | select((.body // "" | ascii_downcase | contains("model-pool outcome: `unknown`")) | not)] - | sort_by(.submitted_at // "", .id // 0) - | (last // {}) - | [(.state // ""), (.submitted_at // "")] - | @tsv - ')" - IFS=$'\t' read -r review_state review_submitted_at <<<"$latest_review" - if [ -z "$review_state" ] || [ -z "$review_submitted_at" ] || [ -z "$run_started_at" ]; then - echo "::error::Formal review or run-start provenance is incomplete; failing closed." - exit 1 - fi if [ "$required_status" != "completed" ] || [ "$required_conclusion" != "failure" ]; then - echo "Exact required run has not completed as a failed run; GitHub workflow_run completion reconciliation owns any later transition." - exit 0 - fi - new_evidence="$(jq -nr --arg review "$review_submitted_at" --arg started "$run_started_at" 'try (($review | fromdateiso8601) > ($started | fromdateiso8601)) catch false')" - if [ "$new_evidence" != "true" ]; then - echo "Formal review is not newer than this run attempt; refusing an evidence-free rerun." + echo "Exact required run is not a completed failure; workflow_run completion reconciliation owns any later transition." exit 0 fi if gh api -X POST "repos/${GH_REPOSITORY}/actions/runs/${required_run_id}/rerun-failed-jobs" >/dev/null; then - echo "Re-ran failed jobs for exact-PR/head Required OpenCode Review run ${required_run_id} after newer formal evidence." + echo "Re-ran failed jobs for exact-PR/head Required OpenCode Review run ${required_run_id} after formal exact-head evidence." exit 0 fi if ! advanced="$(gh api "repos/${GH_REPOSITORY}/actions/runs/${required_run_id}")"; then @@ -157,16 +126,13 @@ def replace_once(text: str, old: str, new: str, label: str) -> str: DISPATCH.write_text(d_before + wake + wake_end + d_after, encoding="utf-8") -# GitHub documents workflow_run/completed as an event emitted after a workflow -# finishes. This closes the opposite race: if the formal review arrived while -# the required run was still executing, the completion event observes that new -# evidence and performs the exact rerun. There is no clock, retry count, sleep, -# or hand-authored sampling budget. +# workflow_run/completed closes review-before-failure. This path independently +# revalidates live PR/head authority and requires review.submitted_at to be newer +# than run.run_started_at, preventing an old verdict from driving a rerun cycle. scheduler = SCHEDULER.read_text(encoding="utf-8") job_marker = "jobs:\n scan-pr-queue:\n" -if scheduler.count(job_marker) != 1: - raise SystemExit("Scheduler jobs insertion boundary drifted") -reconcile_job = r'''jobs: +if " reconcile-opencode-required-verdict:\n" not in scheduler: + reconcile_job = r'''jobs: reconcile-opencode-required-verdict: name: reconcile-opencode-required-verdict if: >- @@ -263,67 +229,88 @@ def replace_once(text: str, old: str, new: str, label: str) -> str: scan-pr-queue: ''' -scheduler = replace_once(scheduler, job_marker, reconcile_job, "event reconciliation job") + scheduler = replace_once(scheduler, job_marker, reconcile_job, "event reconciliation job") SCHEDULER.write_text(scheduler, encoding="utf-8") -# Replace temporary v3 tests that encoded 30-second and 12-attempt policy with -# invariants over causal event/state transitions. -ACCEPTANCE.write_text(r'''"""Regression coverage for one-shot, event-driven Required OpenCode verdict admission.""" +# Replace transitional tests with causal state-machine contracts. These tests +# deliberately avoid brittle indentation parsing and execute the relevant shell +# paths where useful. +ACCEPTANCE.write_text(r'''"""Regression coverage for one-shot Required OpenCode verdict admission.""" +from __future__ import annotations + +import json +import os +import shutil +import subprocess +import textwrap from pathlib import Path +import pytest + REQUIRED = Path(".github/workflows/opencode-review.yml") DISPATCH = Path(".github/workflows/opencode-review-dispatch.yml") -SCHEDULER = Path(".github/workflows/pr-review-merge-scheduler.yml") +HEAD = "a" * 40 -def _required() -> str: - """Return only the formal-verdict admission step.""" +def _required_script() -> str: + """Return the production exact-head verdict-admission shell body.""" text = REQUIRED.read_text(encoding="utf-8") - return text.split(" - name: Fail closed without a current-head OpenCode verdict\n", 1)[1].split("\n cancel-superseded-opencode-review-runs:\n", 1)[0] + step = text.split(" - name: Fail closed without a current-head OpenCode verdict\n", 1)[1] + return textwrap.dedent(step.split(" run: |\n", 1)[1].split("\n cancel-superseded-opencode-review-runs:\n", 1)[0]) def _wake() -> str: - """Return only the dispatch exact-run wake step.""" + """Return the production formal-receipt exact-run wake step.""" text = DISPATCH.read_text(encoding="utf-8") return text.split(" - name: Wake exact-head required OpenCode workflow\n", 1)[1].split("\n - name: Publish repository_dispatch OpenCode status\n", 1)[0] -def test_missing_verdict_releases_runner_without_local_time_allocation() -> None: - """Required admission performs complete state reads once and then fails closed.""" - step = _required() +def test_missing_verdict_releases_runner_without_local_wait_allocation() -> None: + """Admission performs complete state reads once and never polls or sleeps.""" + step = _required_script() assert step.count('gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}"') == 1 assert step.count('gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews') == 1 - for token in ("while :; do", "poll_interval_seconds", "poll_deadline_epoch", "sleep ", "timeout "): + for token in ("while :; do", "poll_interval_seconds", "poll_deadline_epoch", "max_poll_transport_failures", "sleep ", "timeout "): assert token not in step -def test_dispatch_wake_is_exact_state_not_retry_budget() -> None: - """Receipt reconciliation is one exact-state transition plus idempotent readback.""" +def test_receipt_wake_binds_exact_pr_head_and_run_without_polling() -> None: + """Authenticated receipt wake is one exact-state transition.""" step = _wake() + for token in ("for attempt", "while :; do", "seq 1", "sleep ", "timeout ", "/12", "--paginate"): + assert token not in step assert "pull_requests // []" in step - assert "run_started_at" in step - assert "submitted_at" in step - assert "fromdateiso8601" in step assert "rerun-failed-jobs" in step assert "advanced concurrently" in step - for token in ("for attempt", "while :; do", "seq 1", "sleep ", "timeout ", "/12"): - assert token not in step -def test_completed_workflow_event_closes_review_before_failure_race() -> None: - """The scheduler reacts to GitHub's completed Required OpenCode workflow event.""" - text = SCHEDULER.read_text(encoding="utf-8") - job = text.split(" reconcile-opencode-required-verdict:\n", 1)[1].split("\n scan-pr-queue:\n", 1)[0] - assert "github.event_name == 'workflow_run'" in job - assert "github.event.workflow_run.name == 'Required OpenCode Review'" in job - assert "github.event.workflow_run.conclusion == 'failure'" in job - assert "github.event.workflow_run.run_started_at" in job - assert "review_submitted_at" in job - assert "rerun-failed-jobs" in job - for token in ("for attempt", "while :; do", "sleep ", "timeout "): - assert token not in job +def test_missing_verdict_fails_after_one_live_and_one_reviews_read(tmp_path: Path) -> None: + """No formal verdict causes exactly two GitHub reads and an immediate failure.""" + bash = shutil.which("bash") + jq = shutil.which("jq") + if bash is None or jq is None: + pytest.skip("bash and jq are required") + calls = tmp_path / "calls" + gh = tmp_path / "gh" + gh.write_text( + "#!/usr/bin/env bash\nset -euo pipefail\nprintf '%s\\n' \"$*\" >>\"$CALLS\"\n" + "if [[ \"$*\" == \"api repos/ContextualWisdomLab/example/pulls/42\" ]]; then printf '%s\\n' \"$LIVE_PR\"; exit 0; fi\n" + "if [[ \"$*\" == \"api --paginate repos/ContextualWisdomLab/example/pulls/42/reviews?per_page=100\" ]]; then printf '[]\\n'; exit 0; fi\n" + "exit 97\n", + encoding="utf-8", + ) + gh.chmod(0o755) + result = subprocess.run( + [bash, "-c", _required_script()], + env={**os.environ, "PATH": f"{tmp_path}{os.pathsep}{os.environ.get('PATH', '')}", "CALLS": str(calls), "LIVE_PR": json.dumps({"head": {"sha": HEAD}, "draft": False, "state": "open"}), "GH_TOKEN": "token", "TARGET_REPOSITORY": "ContextualWisdomLab/example", "PR_NUMBER": "42", "HEAD_SHA": HEAD, "PR_ACTION": "synchronize", "PR_DRAFT": "false"}, + text=True, + capture_output=True, + check=False, + ) + assert result.returncode == 1, result.stderr + assert len(calls.read_text(encoding="utf-8").splitlines()) == 2 ''', encoding="utf-8") SELF.write_text(r'''"""Regression contract for self-releasing Required OpenCode verdict admission.""" @@ -366,66 +353,76 @@ def test_one_shot_has_no_repository_authored_wait_retry_or_transport_deadline() SCHEDULER = Path(".github/workflows/pr-review-merge-scheduler.yml") -def _required_verdict_step() -> str: - """Return only the current-head formal-verdict admission step.""" +def _required() -> str: + """Return only current-head formal-verdict admission.""" text = REQUIRED.read_text(encoding="utf-8") return text.split(" - name: Fail closed without a current-head OpenCode verdict\n", 1)[1].split("\n cancel-superseded-opencode-review-runs:\n", 1)[0] -def _dispatch_wake_step() -> str: - """Return only the exact-run wake step in the privileged dispatch.""" +def _wake() -> str: + """Return only authenticated formal-receipt wake.""" text = DISPATCH.read_text(encoding="utf-8") return text.split(" - name: Wake exact-head required OpenCode workflow\n", 1)[1].split("\n - name: Publish repository_dispatch OpenCode status\n", 1)[0] def test_required_verdict_admission_has_no_repository_authored_wait_allocation() -> None: - """A missing verdict fails closed after authoritative state reads, not elapsed time.""" - step = _required_verdict_step() + """Missing verdict fails closed after authoritative reads, not elapsed time.""" + step = _required() for token in ("while :; do", "poll_interval_seconds", "poll_deadline_epoch", "max_poll_transport_failures", "sleep ", "timeout "): assert token not in step -def test_dispatch_wake_has_no_fixed_retry_delay_or_transport_deadline() -> None: - """The receipt path performs one exact-state transition without a retry budget.""" - step = _dispatch_wake_step() - for token in ("for attempt", "seq 1", "sleep ", "timeout ", "/12"): +def test_dispatch_receipt_wake_is_one_exact_state_transition() -> None: + """A formal receipt never introduces a retry, sleep, transport, or review-read loop.""" + step = _wake() + for token in ("for attempt", "seq 1", "sleep ", "timeout ", "/12", "--paginate"): assert token not in step - assert "run_started_at" in step and "submitted_at" in step and "rerun-failed-jobs" in step + assert "pull_requests // []" in step + assert "rerun-failed-jobs" in step -def test_workflow_run_completion_reconciles_new_formal_review_evidence() -> None: - """GitHub's completed-workflow event closes the review-before-failure race.""" +def test_workflow_run_completion_closes_review_before_failure_race() -> None: + """Failed completion reruns only when newer formal exact-head evidence exists.""" scheduler = SCHEDULER.read_text(encoding="utf-8") job = scheduler.split(" reconcile-opencode-required-verdict:\n", 1)[1].split("\n scan-pr-queue:\n", 1)[0] assert "github.event_name == 'workflow_run'" in job assert "github.event.workflow_run.name == 'Required OpenCode Review'" in job assert "github.event.workflow_run.conclusion == 'failure'" in job assert "github.event.workflow_run.run_started_at" in job - assert "submitted_at" in job and "rerun-failed-jobs" in job + assert "review_submitted_at" in job and "fromdateiso8601" in job + assert "rerun-failed-jobs" in job for token in ("for attempt", "while :; do", "sleep ", "timeout "): assert token not in job ''', encoding="utf-8") +# Repair legacy regression assertions without weakening exact PR/head/run +# selection. v3 already migrates the selector to pull_requests[].head.sha. +regression = REGRESSION.read_text(encoding="utf-8") +regression = regression.replace(' assert "while :; do" in required\n', ' assert "while :; do" not in required\n', 1) +regression = regression.replace(' """The receipt wake path coexists with the unbounded required review wait."""\n', ' """The receipt wake path coexists with one-shot required verdict admission."""\n', 1) +REGRESSION.write_text(regression, encoding="utf-8") + + architecture = ARCHITECTURE.read_text(encoding="utf-8") heading = "### Required OpenCode event-driven verdict admission" if heading not in architecture: - architecture += f'''\n\n{heading}\n\nThe required workflow performs one live-PR read and one complete paginated formal-review read, then fails closed immediately if no exact-head verdict exists. The privileged review receipt reconciles the immutable required-run id once. If the formal review arrives before that run finishes, GitHub's documented `workflow_run: completed` event performs the complementary reconciliation. Rerun admission is caused by a formal review whose `submitted_at` is later than that run attempt's `run_started_at`; the same evidence therefore cannot create an unbounded rerun cycle. No repository-authored polling cadence, retry count, sleep, transport timeout, or model reasoning deadline is part of this state machine.\n''' + architecture += f'''\n\n{heading}\n\nThe required workflow performs one live-PR read and one complete paginated formal-review read, then fails closed immediately if no exact-head verdict exists. The privileged formal-review receipt reconciles the immutable required-run id once. If that review arrives before the run finishes, GitHub's `workflow_run: completed` event performs the complementary reconciliation. The completion path admits a rerun only when exact PR/head/workflow identity holds and `review.submitted_at > run.run_started_at`; the same old evidence therefore cannot create an unbounded rerun cycle. No repository-authored polling cadence, retry count, sleep, transport timeout, or model reasoning deadline is part of this verdict-wake state machine.\n''' ARCHITECTURE.write_text(architecture, encoding="utf-8") doctoring = DOCTORING.read_text(encoding="utf-8") heading = "### 2026-09-02 event-driven wake supersedes fixed retry allocation" if heading not in doctoring: - doctoring += f'''\n\n{heading}\n\nRCA found that the intermediate PR #1706 repair replaced a runner-held verdict poll with a dispatch wake loop containing fixed `12` attempts, `5` second sleeps, and `30` second transport deadlines. Those values had no cited statistical model, standard, experiment, or provider contract and therefore remained decision-affecting heuristics. The corrected state machine uses GitHub's authoritative `workflow_run` `completed` event plus the formal review receipt event. A rerun is admissible only when exact PR/head/workflow identity holds and the formal review's `submitted_at` is later than the failed run attempt's `run_started_at`; mutation readback only resolves concurrent state advancement and is not a retry loop.\n\nReferences (APA 7): GitHub. (2026). *Events that trigger workflows*. GitHub Docs. https://docs.github.com/actions/using-workflows/events-that-trigger-workflows#workflow_run ; GitHub. (2026). *Webhook events and payloads: workflow_run*. GitHub Docs. https://docs.github.com/webhooks/webhook-events-and-payloads#workflow_run\n''' + doctoring += f'''\n\n{heading}\n\nRCA found that the intermediate PR #1706 repair replaced a runner-held verdict poll with a dispatch wake loop containing fixed `12` attempts, `5` second sleeps, and `30` second transport deadlines. Those values had no governing model, standard, experiment, or provider contract. The corrected state machine uses the authenticated formal-review receipt event plus GitHub's `workflow_run` `completed` event. Receipt-after-failure performs one exact-run transition; review-before-failure is reconciled on completion and requires `review.submitted_at > run.run_started_at`. Mutation readback only resolves concurrent state advancement and is not a retry loop.\n\nReference (APA 7): GitHub. (2026). *Events that trigger workflows*. GitHub Docs. https://docs.github.com/actions/using-workflows/events-that-trigger-workflows#workflow_run\n''' DOCTORING.write_text(doctoring, encoding="utf-8") baseline = BASELINE.read_text(encoding="utf-8") heading = "### OPENCODE-EVENT-DRIVEN-REQUIRED-WAKE-2026-09-02" if heading not in baseline: - baseline += f'''\n\n{heading}\n- Gap: Required OpenCode verdict admission occupied a hosted runner while waiting; an intermediate successor then introduced fixed dispatch retry/sleep/transport allocations (`12`, `5s`, `30s`) without a governing model or standard.\n- Causal owner: `ContextualWisdomLab/.github` required review and merge-control workflows.\n- Repair: one-shot exact-head verdict admission plus dual event reconciliation: formal review receipt handles review-after-failure; GitHub `workflow_run: completed` handles review-before-failure. Rerun authority is the temporal evidence relation `review.submitted_at > run.run_started_at`, exact PR/head/workflow identity, and a failed run state. Concurrent mutation readback is idempotency verification, not retry allocation.\n- Evidence basis: GitHub's documented `workflow_run` completed event and immutable workflow-run/PR metadata. No caller model timeout, polling cadence, retry count, or arbitrary delay is introduced.\n- Status: Proposed on PR #1706 until exact-head focused/full CI and independent review are GREEN.\n''' + baseline += f'''\n\n{heading}\n- Gap: Required OpenCode verdict admission occupied a hosted runner while waiting; an intermediate repair then introduced fixed dispatch retry/sleep/transport allocations (`12`, `5s`, `30s`) without a governing model or standard.\n- Causal owner: `ContextualWisdomLab/.github` required review and merge-control workflows.\n- Repair: one-shot exact-head verdict admission plus dual event reconciliation. A formal-review receipt handles review-after-failure; GitHub `workflow_run: completed` handles review-before-failure with exact PR/head/workflow identity and `review.submitted_at > run.run_started_at`.\n- Verification: executable regressions cover one-shot admission, `pull_request_target` PR-head identity rather than base `head_sha`, opposite event orderings, stale evidence, and absence of repository-authored retry/sleep/transport budgets.\n- Status: Proposed on PR #1706 until exact-head focused/full CI and independent review are GREEN.\n''' BASELINE.write_text(baseline, encoding="utf-8") changelog = CHANGELOG.read_text(encoding="utf-8") -note = "- Required OpenCode verdict wake is event-driven: formal-review receipt and GitHub `workflow_run: completed` reconcile exact run/PR/head state using newer-evidence provenance, replacing fixed retry/sleep/transport allocations.\n" +note = "- Required OpenCode verdict wake is event-driven: formal-review receipt and GitHub `workflow_run: completed` reconcile exact run/PR/head state, replacing runner polling and fixed wake retry/sleep/transport allocations.\n" if note not in changelog: CHANGELOG.write_text(note + changelog, encoding="utf-8") From ec1f3397f0cdad94cc2a6acf287640801ce8d3c3 Mon Sep 17 00:00:00 2001 From: ContextualWisdomLab automation Date: Wed, 2 Sep 2026 12:00:32 +0000 Subject: [PATCH 36/59] fix(opencode): reconcile required verdict by exact events --- .../_temp_pr1706_event_driven_wake_v6.yml | 246 --------- ...temp_pr1706_one_shot_runner_release_v4.yml | 209 -------- ...temp_pr1706_one_shot_runner_release_v5.yml | 280 ----------- .../workflows/opencode-review-dispatch.yml | 75 +-- .github/workflows/opencode-review.yml | 111 +---- .../workflows/pr-review-merge-scheduler.yml | 94 ++++ ARCHITECTURE.md | 10 + CHANGELOG.md | 2 + .../opencode-stale-poll-self-retirement.md | 17 + docs/product-technical-gap-baseline.md | 15 + .../ci/temp_pr1706_event_driven_wake_v4.py | 428 ---------------- .../ci/temp_pr1706_one_shot_runner_release.py | 138 ------ .../temp_pr1706_one_shot_runner_release_v2.py | 361 -------------- .../temp_pr1706_one_shot_runner_release_v3.py | 356 ------------- ...est_opencode_event_driven_required_wake.py | 69 +-- ...st_opencode_live_draft_state_regression.py | 2 +- tests/test_opencode_poll_rate_budget.py | 54 +- tests/test_opencode_poll_self_retirement.py | 469 +----------------- ...st_opencode_required_verdict_regression.py | 65 +-- ...pencode_required_verdict_runner_release.py | 141 ++---- ...t_pr_review_autofix_nvidia_nim_contract.py | 2 +- 21 files changed, 335 insertions(+), 2809 deletions(-) delete mode 100644 .github/workflows/_temp_pr1706_event_driven_wake_v6.yml delete mode 100644 .github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml delete mode 100644 .github/workflows/_temp_pr1706_one_shot_runner_release_v5.yml delete mode 100644 scripts/ci/temp_pr1706_event_driven_wake_v4.py delete mode 100644 scripts/ci/temp_pr1706_one_shot_runner_release.py delete mode 100644 scripts/ci/temp_pr1706_one_shot_runner_release_v2.py delete mode 100644 scripts/ci/temp_pr1706_one_shot_runner_release_v3.py diff --git a/.github/workflows/_temp_pr1706_event_driven_wake_v6.yml b/.github/workflows/_temp_pr1706_event_driven_wake_v6.yml deleted file mode 100644 index 04d054f8ff..0000000000 --- a/.github/workflows/_temp_pr1706_event_driven_wake_v6.yml +++ /dev/null @@ -1,246 +0,0 @@ -name: Temporary PR1706 Event-driven Required Wake v6 - -on: - push: - branches: - - fix/opencode-poll-wall-clock-bound - paths: - - .github/workflows/_temp_pr1706_event_driven_wake_v6.yml - - scripts/ci/temp_pr1706_event_driven_wake_v4.py - - tests/test_opencode_event_driven_required_wake.py - -permissions: - contents: write - -concurrency: - group: temp-pr1706-event-driven-required-wake-v6 - cancel-in-progress: true - -jobs: - repair: - if: github.repository == 'ContextualWisdomLab/.github' && github.actor == 'seonghobae' - runs-on: ubuntu-slim - steps: - - name: Checkout exact trigger head without persisted credentials - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - with: - ref: ${{ github.sha }} - fetch-depth: 0 - persist-credentials: false - - - name: Revalidate writer and reconcile protected main non-destructively - shell: bash - run: | - set -euo pipefail - remote_head="$(git ls-remote origin refs/heads/fix/opencode-poll-wall-clock-bound | cut -f1)" - test "$remote_head" = "$GITHUB_SHA" - git fetch origin main fix/opencode-poll-wall-clock-bound - git config user.name "ContextualWisdomLab automation" - git config user.email "automation@users.noreply.github.com" - git merge --no-edit origin/main - - - name: Set up Python 3.14 - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 - with: - python-version: "3.14" - - - name: Install hash-verified repository test dependencies - shell: bash - run: | - set -euo pipefail - python3 -m pip install --disable-pip-version-check --require-hashes --only-binary=:all: -r requirements-opencode-review-ci-hashes.txt - - - name: Prove no-heuristics event-wake contract is genuinely RED - shell: bash - run: | - set -euo pipefail - set +e - red_output="$(PYTHONPATH=. python3 -m pytest -q tests/test_opencode_event_driven_required_wake.py 2>&1)" - red_status=$? - set -e - printf '%s\n' "$red_output" - if [ "$red_status" -ne 1 ] || ! grep -Eq '(^|[[:space:]])[1-9][0-9]* failed([,[:space:]]|$)' <<<"$red_output"; then - echo "::error::Expected a genuine assertion RED proving local retry/time allocation or missing event reconciliation." - exit 1 - fi - - - name: Materialize one-shot admission and event-driven exact-run repair - shell: bash - run: | - set -euo pipefail - PYTHONPATH=. python3 scripts/ci/temp_pr1706_one_shot_runner_release.py - python3 - <<'PY' - from pathlib import Path - - driver = Path("scripts/ci/temp_pr1706_one_shot_runner_release_v3.py") - text = driver.read_text(encoding="utf-8") - start_token = 'workflow = WORKFLOW.read_text(encoding="utf-8")\n' - end_token = 'WORKFLOW.write_text(workflow, encoding="utf-8")\n' - start = text.find(start_token) - end_start = text.find(end_token, start + len(start_token)) - if start < 0 or end_start < 0: - raise SystemExit("v3 transport block drifted") - end = end_start + len(end_token) - replacement = '''workflow = WORKFLOW.read_text(encoding="utf-8") - step_start = " - name: Fail closed without a current-head OpenCode verdict\\n" - step_end = "\\n cancel-superseded-opencode-review-runs:\\n" - if workflow.count(step_start) != 1 or workflow.count(step_end) != 1: - raise SystemExit("OpenCode required-verdict step boundaries drifted before intermediate transport stage") - before, rest = workflow.split(step_start, 1) - target, after = rest.split(step_end, 1) - target = replace_once( - target, - ' live_pr="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"\\n', - ' if ! live_pr="$(timeout 30s gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"; then\\n' - ' echo "::error::Live pull request API read failed during one-shot current-head verdict admission; failing closed and releasing the runner."\\n' - ' exit 1\\n' - ' fi\\n', - "intermediate live PR read", - ) - target = replace_once( - target, - ' if ! reviews="$(gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"; then\\n', - ' if ! reviews="$(timeout 30s gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"; then\\n', - "intermediate Reviews read", - ) - WORKFLOW.write_text(before + step_start + target + step_end + after, encoding="utf-8") - ''' - text = text[:start] + replacement + text[end:] - brittle = '''regression = replace_once(regression, ' "PR_HEAD_SHA": HEAD,\\n "REQUIRED_RUN_ID": "42",\\n', ' "PR_NUMBER": "1437",\\n "PR_HEAD_SHA": HEAD,\\n "REQUIRED_RUN_ID": "42",\\n', "formal wake PR identity fixture") - ''' - robust = '''wake_env = ' "PR_HEAD_SHA": HEAD,\\n "REQUIRED_RUN_ID": "42",\\n' - wake_env_with_pr = ' "PR_NUMBER": "1437",\\n "PR_HEAD_SHA": HEAD,\\n "REQUIRED_RUN_ID": "42",\\n' - wake_env_count = regression.count(wake_env) - if wake_env_count != 2: - raise SystemExit(f"formal wake PR identity fixtures drifted: expected 2 exact matches, found {wake_env_count}") - regression = regression.replace(wake_env, wake_env_with_pr) - ''' - if text.count(brittle) != 1: - raise SystemExit("v3 formal wake PR identity repair block drifted") - driver.write_text(text.replace(brittle, robust, 1), encoding="utf-8") - PY - PYTHONPATH=. python3 scripts/ci/temp_pr1706_one_shot_runner_release_v3.py - PYTHONPATH=. python3 scripts/ci/temp_pr1706_event_driven_wake_v4.py - python3 - <<'PY' - import re - from pathlib import Path - - # v3 deliberately represented the superseded intermediate design so - # its old prose must not survive beside the event-driven contract. - replacements = { - Path("ARCHITECTURE.md"): { - "A bounded retry absorbs transient GitHub run-lookup failures; model reasoning itself has no caller wall-clock deadline.": - "GitHub's documented workflow-run completion event complements the formal-review receipt event; model reasoning has no caller wall-clock deadline and no fixed wake retry allocation is retained.", - }, - Path("docs/doctoring/opencode-stale-poll-self-retirement.md"): { - "Transient run-lookup failures are retried within a bounded transport loop so a posted formal verdict cannot be stranded by one GitHub API timeout.": - "Run-lookup transport failure fails closed without inventing a retry policy; the independent GitHub workflow-run completion event closes the opposite review-before-failure ordering.", - }, - Path("CHANGELOG.md"): { - "- Required OpenCode Review now releases its runner after one exact-head verdict admission read; exact-run wake binds PR number plus `pull_requests[].head.sha` and tolerates bounded transient GitHub run-lookup failures without imposing a model reasoning timeout.\n": - "", - }, - } - for path, mapping in replacements.items(): - text = path.read_text(encoding="utf-8") - for old, new in mapping.items(): - if old in text: - text = text.replace(old, new) - path.write_text(text, encoding="utf-8") - - # The dispatch workflow is a trusted immutable blob input to the - # autofix contract; rebind it after the final event-driven patch. - import subprocess - dispatch_blob = subprocess.check_output( - ["git", "hash-object", ".github/workflows/opencode-review-dispatch.yml"], - text=True, - ).strip() - path = Path("tests/test_pr_review_autofix_nvidia_nim_contract.py") - text = path.read_text(encoding="utf-8") - pattern = r'REVIEW_DISPATCH_BLOB_SHA = "[0-9a-f]{40}"' - matches = re.findall(pattern, text) - if len(matches) != 1: - raise SystemExit(f"review-dispatch blob pin drifted: expected one assignment, found {len(matches)}") - path.write_text(re.sub(pattern, f'REVIEW_DISPATCH_BLOB_SHA = "{dispatch_blob}"', text, count=1), encoding="utf-8") - PY - git diff --check - - - name: Verify focused GREEN contracts - shell: bash - run: | - set -euo pipefail - PYTHONPATH=. python3 -m pytest -q \ - tests/test_opencode_event_driven_required_wake.py \ - tests/test_opencode_required_verdict_runner_release.py \ - tests/test_opencode_poll_rate_budget.py \ - tests/test_opencode_poll_self_retirement.py \ - tests/test_opencode_required_verdict_regression.py \ - tests/test_opencode_live_draft_state_regression.py \ - tests/test_opencode_rust_coverage_toolchain_contract.py \ - tests/test_pr_review_autofix_nvidia_nim_contract.py - python3 -m compileall -q scripts tests - git diff --check - - - name: Verify broader repository suite and public documentation coverage - shell: bash - run: | - set -euo pipefail - PYTHONPATH=. python3 -m pytest tests -q - interrogate -c pyproject.toml scripts tests - git diff --check - - - name: Remove completed source-fix machinery and verify publication scope - shell: bash - run: | - set -euo pipefail - rm -f \ - .github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml \ - .github/workflows/_temp_pr1706_one_shot_runner_release_v5.yml \ - .github/workflows/_temp_pr1706_event_driven_wake_v6.yml \ - scripts/ci/temp_pr1706_one_shot_runner_release.py \ - scripts/ci/temp_pr1706_one_shot_runner_release_v2.py \ - scripts/ci/temp_pr1706_one_shot_runner_release_v3.py \ - scripts/ci/temp_pr1706_event_driven_wake_v4.py - git diff --check - while IFS= read -r changed; do - case "$changed" in - .github/workflows/opencode-review.yml|\ - .github/workflows/opencode-review-dispatch.yml|\ - .github/workflows/pr-review-merge-scheduler.yml|\ - .github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml|\ - .github/workflows/_temp_pr1706_one_shot_runner_release_v5.yml|\ - .github/workflows/_temp_pr1706_event_driven_wake_v6.yml|\ - scripts/ci/temp_pr1706_one_shot_runner_release.py|\ - scripts/ci/temp_pr1706_one_shot_runner_release_v2.py|\ - scripts/ci/temp_pr1706_one_shot_runner_release_v3.py|\ - scripts/ci/temp_pr1706_event_driven_wake_v4.py|\ - tests/test_opencode_event_driven_required_wake.py|\ - tests/test_opencode_required_verdict_runner_release.py|\ - tests/test_opencode_poll_rate_budget.py|\ - tests/test_opencode_poll_self_retirement.py|\ - tests/test_opencode_required_verdict_regression.py|\ - tests/test_opencode_live_draft_state_regression.py|\ - tests/test_pr_review_autofix_nvidia_nim_contract.py|\ - docs/product-technical-gap-baseline.md|\ - docs/doctoring/opencode-stale-poll-self-retirement.md|\ - ARCHITECTURE.md|CHANGELOG.md) ;; - *) echo "::error::Unexpected publication path: $changed"; exit 1 ;; - esac - done < <(git diff --name-only HEAD) - - - name: Publish verified successor from unchanged exact writer head - env: - EXPECTED_REMOTE_HEAD: ${{ github.sha }} - PUSH_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN || github.token }} - shell: bash - run: | - set -euo pipefail - remote_head="$(git ls-remote origin refs/heads/fix/opencode-poll-wall-clock-bound | cut -f1)" - test "$remote_head" = "$EXPECTED_REMOTE_HEAD" - git add -A - git diff --cached --check - git commit -m "fix(opencode): reconcile required verdict by exact events" - remote_head="$(git ls-remote origin refs/heads/fix/opencode-poll-wall-clock-bound | cut -f1)" - test "$remote_head" = "$EXPECTED_REMOTE_HEAD" - git remote set-url origin "https://x-access-token:${PUSH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" - git push origin HEAD:refs/heads/fix/opencode-poll-wall-clock-bound diff --git a/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml b/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml deleted file mode 100644 index f18378daa3..0000000000 --- a/.github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml +++ /dev/null @@ -1,209 +0,0 @@ -name: Temporary PR1706 One-shot Runner Release v4 - -on: - push: - branches: - - fix/opencode-poll-wall-clock-bound - paths: - - .github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml - - scripts/ci/temp_pr1706_one_shot_runner_release.py - - scripts/ci/temp_pr1706_one_shot_runner_release_v2.py - - scripts/ci/temp_pr1706_one_shot_runner_release_v3.py - - tests/test_opencode_required_verdict_runner_release.py - -permissions: - contents: read - -concurrency: - group: temp-pr1706-one-shot-runner-release-v4 - cancel-in-progress: true - -jobs: - repair: - if: github.repository == 'ContextualWisdomLab/.github' && github.actor == 'seonghobae' - runs-on: ubuntu-slim - steps: - - name: Checkout exact trigger head without persisted mutation credentials - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - with: - ref: ${{ github.sha }} - fetch-depth: 0 - persist-credentials: false - - - name: Reconcile current protected main without rewriting writer history - run: | - set -euo pipefail - git fetch origin main fix/opencode-poll-wall-clock-bound - git config user.name "ContextualWisdomLab automation" - git config user.email "automation@users.noreply.github.com" - git merge --no-edit origin/main - - - name: Set up Python - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 - with: - python-version: "3.14" - - - name: Install locked repository test dependencies - run: | - set -euo pipefail - python3 -m pip install --disable-pip-version-check --require-hashes --only-binary=:all: -r requirements-opencode-review-ci-hashes.txt - - - name: Prove prerequisite regression is genuinely RED - run: | - set -euo pipefail - set +e - red_output="$(PYTHONPATH=. python3 -m pytest -q tests/test_opencode_required_verdict_runner_release.py 2>&1)" - red_status=$? - set -e - printf '%s\n' "$red_output" - if [ "$red_status" -ne 1 ] || ! grep -Eq '(^|[[:space:]])[1-9][0-9]* failed([,[:space:]]|$)' <<<"$red_output"; then - echo "::error::Expected a genuine pytest assertion RED (exit 1 with failed tests), not collection/setup failure or an already-GREEN prerequisite." - exit 1 - fi - - - name: Apply deterministic causal repair - run: | - set -euo pipefail - PYTHONPATH=. python3 scripts/ci/temp_pr1706_one_shot_runner_release.py - python3 - <<'PY' - from pathlib import Path - - driver = Path("scripts/ci/temp_pr1706_one_shot_runner_release_v3.py") - text = driver.read_text(encoding="utf-8") - start_token = 'workflow = WORKFLOW.read_text(encoding="utf-8")\n' - end_token = 'WORKFLOW.write_text(workflow, encoding="utf-8")\n' - start = text.find(start_token) - end_start = text.find(end_token, start + len(start_token)) - if start < 0 or end_start < 0: - raise SystemExit("v3 bounded-transport block drifted") - end = end_start + len(end_token) - replacement = '''workflow = WORKFLOW.read_text(encoding="utf-8") - step_start = " - name: Fail closed without a current-head OpenCode verdict\\n" - step_end = "\\n cancel-superseded-opencode-review-runs:\\n" - if workflow.count(step_start) != 1 or workflow.count(step_end) != 1: - raise SystemExit("OpenCode required-verdict step boundaries drifted before bounded transport repair") - before, rest = workflow.split(step_start, 1) - target, after = rest.split(step_end, 1) - target = replace_once( - target, - ' live_pr="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"\\n', - ' if ! live_pr="$(timeout 30s gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"; then\\n' - ' echo "::error::Live pull request API read failed during one-shot current-head verdict admission; failing closed and releasing the runner."\\n' - ' exit 1\\n' - ' fi\\n', - "bounded live PR read", - ) - target = replace_once( - target, - ' if ! reviews="$(gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"; then\\n', - ' if ! reviews="$(timeout 30s gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"; then\\n', - "bounded Reviews read", - ) - WORKFLOW.write_text(before + step_start + target + step_end + after, encoding="utf-8") - ''' - text = text[:start] + replacement + text[end:] - - brittle = '''regression = replace_once(regression, ' "PR_HEAD_SHA": HEAD,\\n "REQUIRED_RUN_ID": "42",\\n', ' "PR_NUMBER": "1437",\\n "PR_HEAD_SHA": HEAD,\\n "REQUIRED_RUN_ID": "42",\\n', "formal wake PR identity fixture") - ''' - robust = '''wake_env = ' "PR_HEAD_SHA": HEAD,\\n "REQUIRED_RUN_ID": "42",\\n' - wake_env_with_pr = ' "PR_NUMBER": "1437",\\n "PR_HEAD_SHA": HEAD,\\n "REQUIRED_RUN_ID": "42",\\n' - wake_env_count = regression.count(wake_env) - if wake_env_count != 2: - raise SystemExit(f"formal wake PR identity fixtures drifted: expected 2 exact matches, found {wake_env_count}") - regression = regression.replace(wake_env, wake_env_with_pr) - ''' - if text.count(brittle) != 1: - raise SystemExit("v3 formal wake PR identity repair block drifted") - text = text.replace(brittle, robust, 1) - driver.write_text(text, encoding="utf-8") - PY - PYTHONPATH=. python3 scripts/ci/temp_pr1706_one_shot_runner_release_v3.py - git restore --source=HEAD -- docs/product-technical-gap-baseline.md - - - name: Verify focused GREEN and exact-run identity contracts - run: | - set -euo pipefail - PYTHONPATH=. python3 -m pytest -q \ - tests/test_opencode_required_verdict_runner_release.py \ - tests/test_opencode_poll_rate_budget.py \ - tests/test_opencode_poll_self_retirement.py \ - tests/test_opencode_required_verdict_regression.py \ - tests/test_opencode_live_draft_state_regression.py - python3 -m compileall -q scripts tests - git diff --check - - - name: Verify broader repository suite and documentation coverage - run: | - set -euo pipefail - PYTHONPATH=. python3 -m pytest tests -q - if command -v interrogate >/dev/null 2>&1; then - interrogate -c pyproject.toml scripts tests - fi - git diff --check - - - name: Remove completed one-shot machinery and verify publication scope - run: | - set -euo pipefail - rm -f \ - .github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml \ - scripts/ci/temp_pr1706_one_shot_runner_release.py \ - scripts/ci/temp_pr1706_one_shot_runner_release_v2.py \ - scripts/ci/temp_pr1706_one_shot_runner_release_v3.py - test ! -e .github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml - test ! -e scripts/ci/temp_pr1706_one_shot_runner_release.py - test ! -e scripts/ci/temp_pr1706_one_shot_runner_release_v2.py - test ! -e scripts/ci/temp_pr1706_one_shot_runner_release_v3.py - git diff --check - while IFS= read -r changed; do - case "$changed" in - .github/workflows/opencode-review.yml|\ - .github/workflows/opencode-review-dispatch.yml|\ - .github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml|\ - scripts/ci/temp_pr1706_one_shot_runner_release.py|\ - scripts/ci/temp_pr1706_one_shot_runner_release_v2.py|\ - scripts/ci/temp_pr1706_one_shot_runner_release_v3.py|\ - tests/test_opencode_required_verdict_runner_release.py|\ - tests/test_opencode_poll_rate_budget.py|\ - tests/test_opencode_poll_self_retirement.py|\ - tests/test_opencode_required_verdict_regression.py|\ - tests/test_opencode_live_draft_state_regression.py|\ - docs/doctoring/opencode-stale-poll-self-retirement.md|\ - ARCHITECTURE.md|CHANGELOG.md) ;; - *) echo "::error::Unexpected publication path: $changed"; exit 1 ;; - esac - done < <(git diff --name-only HEAD) - - - name: Publish only from unchanged exact writer head with a workflow-triggering credential - env: - EXPECTED_REMOTE_HEAD: ${{ github.sha }} - PUSH_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN }} - run: | - set -euo pipefail - if [ -z "${PUSH_TOKEN:-}" ]; then - echo "::error::Publication requires PR_REVIEW_MERGE_TOKEN or OPENCODE_APPROVE_TOKEN so the new exact head can start required workflows; github.token fallback is intentionally refused." - exit 1 - fi - git fetch origin fix/opencode-poll-wall-clock-bound - live_head="$(git rev-parse origin/fix/opencode-poll-wall-clock-bound)" - if [ "$live_head" != "$EXPECTED_REMOTE_HEAD" ]; then - echo "::error::Writer branch advanced to $live_head; refusing stale publication." - exit 1 - fi - git add -A \ - .github/workflows/opencode-review.yml \ - .github/workflows/opencode-review-dispatch.yml \ - .github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml \ - scripts/ci/temp_pr1706_one_shot_runner_release.py \ - scripts/ci/temp_pr1706_one_shot_runner_release_v2.py \ - scripts/ci/temp_pr1706_one_shot_runner_release_v3.py \ - tests/test_opencode_required_verdict_runner_release.py \ - tests/test_opencode_poll_rate_budget.py \ - tests/test_opencode_poll_self_retirement.py \ - tests/test_opencode_required_verdict_regression.py \ - tests/test_opencode_live_draft_state_regression.py \ - docs/doctoring/opencode-stale-poll-self-retirement.md \ - ARCHITECTURE.md CHANGELOG.md - git diff --cached --check - git commit -m "fix(opencode): release required runner after one verdict read" - git remote set-url origin "https://x-access-token:${PUSH_TOKEN}@github.com/ContextualWisdomLab/.github.git" - git push origin HEAD:fix/opencode-poll-wall-clock-bound \ No newline at end of file diff --git a/.github/workflows/_temp_pr1706_one_shot_runner_release_v5.yml b/.github/workflows/_temp_pr1706_one_shot_runner_release_v5.yml deleted file mode 100644 index b88d1e347f..0000000000 --- a/.github/workflows/_temp_pr1706_one_shot_runner_release_v5.yml +++ /dev/null @@ -1,280 +0,0 @@ -name: Temporary PR1706 One-shot Runner Release v5 - -on: - push: - branches: - - fix/opencode-poll-wall-clock-bound - paths: - - .github/workflows/_temp_pr1706_one_shot_runner_release_v5.yml - -permissions: - contents: write - -concurrency: - group: temp-pr1706-one-shot-runner-release-v5 - cancel-in-progress: true - -jobs: - repair: - if: github.repository == 'ContextualWisdomLab/.github' && github.actor == 'seonghobae' - runs-on: ubuntu-slim - steps: - - name: Checkout exact trigger head - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - with: - ref: ${{ github.sha }} - fetch-depth: 0 - persist-credentials: false - - - name: Revalidate writer and reconcile current protected main - shell: bash - run: | - set -euo pipefail - remote_head="$(git ls-remote origin refs/heads/fix/opencode-poll-wall-clock-bound | cut -f1)" - test "$remote_head" = "$GITHUB_SHA" - git fetch origin main fix/opencode-poll-wall-clock-bound - git config user.name "ContextualWisdomLab automation" - git config user.email "automation@users.noreply.github.com" - git merge --no-edit origin/main - - - name: Set up Python 3.14 - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 - with: - python-version: "3.14" - - - name: Install hash-verified repository test dependencies - shell: bash - run: | - set -euo pipefail - python3 -m pip install --disable-pip-version-check --require-hashes --only-binary=:all: -r requirements-opencode-review-ci-hashes.txt - - - name: Prove runner-holding admission is RED - shell: bash - run: | - set -euo pipefail - set +e - red_output="$(PYTHONPATH=. python3 -m pytest -q tests/test_opencode_required_verdict_runner_release.py 2>&1)" - red_status=$? - set -e - printf '%s\n' "$red_output" - if [ "$red_status" -ne 1 ] || ! grep -Eq '(^|[[:space:]])[1-9][0-9]* failed([,[:space:]]|$)' <<<"$red_output"; then - echo "::error::Expected an assertion RED proving the current runner-holding loop." - exit 1 - fi - - - name: Apply deterministic exact-run wake repair and rebind trusted blob - shell: bash - run: | - set -euo pipefail - PYTHONPATH=. python3 scripts/ci/temp_pr1706_one_shot_runner_release.py - python3 - <<'PY' - from pathlib import Path - - driver = Path("scripts/ci/temp_pr1706_one_shot_runner_release_v3.py") - text = driver.read_text(encoding="utf-8") - start_token = 'workflow = WORKFLOW.read_text(encoding="utf-8")\n' - end_token = 'WORKFLOW.write_text(workflow, encoding="utf-8")\n' - start = text.find(start_token) - end_start = text.find(end_token, start + len(start_token)) - if start < 0 or end_start < 0: - raise SystemExit("v3 bounded-transport block drifted") - end = end_start + len(end_token) - replacement = '''workflow = WORKFLOW.read_text(encoding="utf-8") - step_start = " - name: Fail closed without a current-head OpenCode verdict\\n" - step_end = "\\n cancel-superseded-opencode-review-runs:\\n" - if workflow.count(step_start) != 1 or workflow.count(step_end) != 1: - raise SystemExit("OpenCode required-verdict step boundaries drifted before bounded transport repair") - before, rest = workflow.split(step_start, 1) - target, after = rest.split(step_end, 1) - target = replace_once( - target, - ' live_pr="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"\\n', - ' if ! live_pr="$(timeout 30s gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"; then\\n' - ' echo "::error::Live pull request API read failed during one-shot current-head verdict admission; failing closed and releasing the runner."\\n' - ' exit 1\\n' - ' fi\\n', - "bounded live PR read", - ) - target = replace_once( - target, - ' if ! reviews="$(gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"; then\\n', - ' if ! reviews="$(timeout 30s gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"; then\\n', - "bounded Reviews read", - ) - WORKFLOW.write_text(before + step_start + target + step_end + after, encoding="utf-8") - ''' - text = text[:start] + replacement + text[end:] - brittle = '''regression = replace_once(regression, ' "PR_HEAD_SHA": HEAD,\\n "REQUIRED_RUN_ID": "42",\\n', ' "PR_NUMBER": "1437",\\n "PR_HEAD_SHA": HEAD,\\n "REQUIRED_RUN_ID": "42",\\n', "formal wake PR identity fixture") - ''' - robust = '''wake_env = ' "PR_HEAD_SHA": HEAD,\\n "REQUIRED_RUN_ID": "42",\\n' - wake_env_with_pr = ' "PR_NUMBER": "1437",\\n "PR_HEAD_SHA": HEAD,\\n "REQUIRED_RUN_ID": "42",\\n' - wake_env_count = regression.count(wake_env) - if wake_env_count != 2: - raise SystemExit(f"formal wake PR identity fixtures drifted: expected 2 exact matches, found {wake_env_count}") - regression = regression.replace(wake_env, wake_env_with_pr) - ''' - if text.count(brittle) != 1: - raise SystemExit("v3 formal wake PR identity repair block drifted") - driver.write_text(text.replace(brittle, robust, 1), encoding="utf-8") - PY - PYTHONPATH=. python3 scripts/ci/temp_pr1706_one_shot_runner_release_v3.py - git restore --source=HEAD -- docs/product-technical-gap-baseline.md - dispatch_blob="$(git hash-object .github/workflows/opencode-review-dispatch.yml)" - python3 - "$dispatch_blob" <<'PY' - import re - import sys - from pathlib import Path - - path = Path("tests/test_pr_review_autofix_nvidia_nim_contract.py") - text = path.read_text(encoding="utf-8") - pattern = r'REVIEW_DISPATCH_BLOB_SHA = "[0-9a-f]{40}"' - matches = re.findall(pattern, text) - if len(matches) != 1: - raise SystemExit(f"review-dispatch blob pin drifted: expected one assignment, found {len(matches)}") - text = re.sub(pattern, f'REVIEW_DISPATCH_BLOB_SHA = "{sys.argv[1]}"', text, count=1) - path.write_text(text, encoding="utf-8") - PY - git diff --check - - - name: Verify focused GREEN and trusted-blob identity - shell: bash - run: | - set -euo pipefail - PYTHONPATH=. python3 -m pytest -q \ - tests/test_opencode_required_verdict_runner_release.py \ - tests/test_opencode_poll_rate_budget.py \ - tests/test_opencode_poll_self_retirement.py \ - tests/test_opencode_required_verdict_regression.py \ - tests/test_opencode_live_draft_state_regression.py \ - tests/test_opencode_rust_coverage_toolchain_contract.py \ - tests/test_pr_review_autofix_nvidia_nim_contract.py - python3 -m compileall -q scripts tests - git diff --check - - - name: Verify broader repository suite - shell: bash - run: | - set -euo pipefail - PYTHONPATH=. python3 -m pytest tests -q - interrogate -c pyproject.toml scripts tests - git diff --check - - - name: Remove completed one-shot machinery and verify publication scope - shell: bash - run: | - set -euo pipefail - rm -f \ - .github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml \ - .github/workflows/_temp_pr1706_one_shot_runner_release_v5.yml \ - scripts/ci/temp_pr1706_one_shot_runner_release.py \ - scripts/ci/temp_pr1706_one_shot_runner_release_v2.py \ - scripts/ci/temp_pr1706_one_shot_runner_release_v3.py - git diff --check - while IFS= read -r changed; do - case "$changed" in - .github/workflows/opencode-review.yml|\ - .github/workflows/opencode-review-dispatch.yml|\ - .github/workflows/_temp_pr1706_one_shot_runner_release_v4.yml|\ - .github/workflows/_temp_pr1706_one_shot_runner_release_v5.yml|\ - scripts/ci/temp_pr1706_one_shot_runner_release.py|\ - scripts/ci/temp_pr1706_one_shot_runner_release_v2.py|\ - scripts/ci/temp_pr1706_one_shot_runner_release_v3.py|\ - tests/test_opencode_required_verdict_runner_release.py|\ - tests/test_opencode_poll_rate_budget.py|\ - tests/test_opencode_poll_self_retirement.py|\ - tests/test_opencode_required_verdict_regression.py|\ - tests/test_opencode_live_draft_state_regression.py|\ - tests/test_pr_review_autofix_nvidia_nim_contract.py|\ - docs/doctoring/opencode-stale-poll-self-retirement.md|\ - ARCHITECTURE.md|CHANGELOG.md) ;; - *) echo "::error::Unexpected publication path: $changed"; exit 1 ;; - esac - done < <(git diff --name-only HEAD) - - - name: Materialize a verified merge commit without mutating the writer ref - env: - GH_TOKEN: ${{ github.token }} - EXPECTED_REMOTE_HEAD: ${{ github.sha }} - shell: bash - run: | - set -euo pipefail - remote_head="$(git ls-remote origin refs/heads/fix/opencode-poll-wall-clock-bound | cut -f1)" - test "$remote_head" = "$EXPECTED_REMOTE_HEAD" - live_main="$(git ls-remote origin refs/heads/main | cut -f1)" - reconciled_main="$(git rev-parse origin/main)" - test "$live_main" = "$reconciled_main" - git add -A - git diff --cached --check - python3 - "$GITHUB_REPOSITORY" "$EXPECTED_REMOTE_HEAD" "$live_main" <<'PY' - import base64 - import json - import os - from pathlib import Path - import subprocess - import sys - - repo, writer_parent, main_parent = sys.argv[1:4] - - def run(*args: str, input_text: str | None = None) -> str: - proc = subprocess.run(args, input=input_text, text=True, capture_output=True, check=False) - if proc.returncode != 0: - raise SystemExit(f"command failed: {' '.join(args)}\n{proc.stderr}") - return proc.stdout.strip() - - changed = run("git", "diff", "--cached", "--name-status", "origin/main", "--") - entries: list[dict[str, object]] = [] - for line in changed.splitlines(): - if not line: - continue - status, path = line.split("\t", 1) - if status.startswith("D"): - old = run("git", "ls-tree", "origin/main", "--", path) - if not old: - continue - mode = old.split(None, 1)[0] - entries.append({"path": path, "mode": mode, "type": "blob", "sha": None}) - continue - index_row = run("git", "ls-files", "-s", "--", path) - if not index_row: - raise SystemExit(f"missing staged file metadata for {path}") - mode = index_row.split(None, 1)[0] - data = Path(path).read_bytes() - blob_payload = json.dumps({"content": base64.b64encode(data).decode("ascii"), "encoding": "base64"}) - blob_sha = run( - "gh", "api", "--method", "POST", f"repos/{repo}/git/blobs", "--input", "-", "--jq", ".sha", - input_text=blob_payload, - ) - entries.append({"path": path, "mode": mode, "type": "blob", "sha": blob_sha}) - - base_tree = run("git", "rev-parse", "origin/main^{tree}") - tree_payload = json.dumps({"base_tree": base_tree, "tree": entries}) - tree_sha = run( - "gh", "api", "--method", "POST", f"repos/{repo}/git/trees", "--input", "-", "--jq", ".sha", - input_text=tree_payload, - ) - parents = [writer_parent] - if run("git", "merge-base", "--is-ancestor", "origin/main", writer_parent) != "": - pass - # git merge-base --is-ancestor is status-only; retain main as a second parent - # whenever the writer does not already contain it. - ancestor = subprocess.run( - ["git", "merge-base", "--is-ancestor", "origin/main", writer_parent], - capture_output=True, - check=False, - ).returncode == 0 - if not ancestor: - parents.append(main_parent) - commit_payload = json.dumps({ - "message": "fix(opencode): release required runner after one verdict read", - "tree": tree_sha, - "parents": parents, - }) - candidate_sha = run( - "gh", "api", "--method", "POST", f"repos/{repo}/git/commits", "--input", "-", "--jq", ".sha", - input_text=commit_payload, - ) - print(f"CANDIDATE_COMMIT_SHA={candidate_sha}") - print(f"CANDIDATE_TREE_SHA={tree_sha}") - print(f"CANDIDATE_PARENT_SHA={writer_parent}") - print(f"CANDIDATE_MAIN_SHA={main_parent}") - PY diff --git a/.github/workflows/opencode-review-dispatch.yml b/.github/workflows/opencode-review-dispatch.yml index bb5d439c3f..78e920e698 100644 --- a/.github/workflows/opencode-review-dispatch.yml +++ b/.github/workflows/opencode-review-dispatch.yml @@ -7538,11 +7538,13 @@ jobs: && github.event_name == 'repository_dispatch' && steps.formal_review_receipt.outcome == 'success' && needs.validate-pr-metadata.outputs.target_repository != '' + && needs.validate-pr-metadata.outputs.pr_number != '' && needs.validate-pr-metadata.outputs.head_sha != '' && github.event.client_payload.required_run_id != '' env: GH_TOKEN: ${{ needs.validate-pr-metadata.outputs.target_repository == github.repository && github.token || secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN }} GH_REPOSITORY: ${{ needs.validate-pr-metadata.outputs.target_repository }} + PR_NUMBER: ${{ needs.validate-pr-metadata.outputs.pr_number }} PR_HEAD_SHA: ${{ needs.validate-pr-metadata.outputs.head_sha }} REQUIRED_RUN_ID: ${{ github.event.client_payload.required_run_id }} WAKE_TOKEN_SOURCE: ${{ needs.validate-pr-metadata.outputs.target_repository == github.repository && 'github-token' || secrets.PR_REVIEW_MERGE_TOKEN != '' && 'PR_REVIEW_MERGE_TOKEN' || secrets.OPENCODE_APPROVE_TOKEN != '' && 'OPENCODE_APPROVE_TOKEN' || 'unavailable' }} @@ -7552,39 +7554,48 @@ jobs: echo "::error::Actions-capable wake credential is unavailable. Native runs use github.token; sibling runs require PR_REVIEW_MERGE_TOKEN or OPENCODE_APPROVE_TOKEN." exit 1 fi - [[ "$REQUIRED_RUN_ID" =~ ^[1-9][0-9]*$ ]] || { - echo "::error::Required OpenCode run id is missing or non-canonical." + [[ "$REQUIRED_RUN_ID" =~ ^[1-9][0-9]*$ ]] || { echo "::error::Required OpenCode run id is missing or non-canonical."; exit 1; } + [[ "$PR_NUMBER" =~ ^[1-9][0-9]*$ ]] || { echo "::error::Required OpenCode PR number is missing or non-canonical."; exit 1; } + [[ "$PR_HEAD_SHA" =~ ^[0-9a-fA-F]{40}$ ]] || { echo "::error::Required OpenCode PR head SHA is missing or malformed."; exit 1; } + if ! run="$(gh api "repos/${GH_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}")"; then + echo "::error::Exact required-run lookup failed; no retry policy is invented." exit 1 - } - # The immutable run id is scoped to GH_REPOSITORY. Revalidate its - # event, central workflow path, and live PR head before rerunning it; - # rendered titles and workflow_url differ between native and - # organization-required workflow contexts. - for attempt in $(seq 1 12); do - run="$(gh api "repos/${GH_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}")" - required_run="$(printf '%s\n' "$run" | jq -r --arg head "$PR_HEAD_SHA" --argjson run_id "$REQUIRED_RUN_ID" ' - select(.id == $run_id) - | select(.event == "pull_request_target") - | select(.path == ".github/workflows/opencode-review.yml") - | select(.head_sha == $head) - | [(.id // ""), (.status // ""), (.conclusion // "")] - | @tsv - ')" - IFS=$'\t' read -r required_run_id required_status required_conclusion <<<"$required_run" - if [ "$required_status" = "completed" ] && [ "$required_conclusion" = "failure" ]; then - gh api -X POST "repos/${GH_REPOSITORY}/actions/runs/${required_run_id}/rerun-failed-jobs" >/dev/null - echo "Re-ran failed jobs for exact-head Required OpenCode Review run ${required_run_id}." - exit 0 - fi - if [ "$required_status" = "completed" ] && [ "$required_conclusion" = "success" ]; then - echo "Exact-head Required OpenCode Review run ${required_run_id} already succeeded." - exit 0 - fi - if [ "$attempt" -lt 12 ]; then - sleep 5 - fi - done - echo "::error::Formal OpenCode receipt exists, but the exact-head required workflow did not reach a rerunnable failed state." + fi + required_run="$(printf '%s\n' "$run" | jq -r --arg head "$PR_HEAD_SHA" --arg pr "$PR_NUMBER" --argjson run_id "$REQUIRED_RUN_ID" ' + select(.id == $run_id) + | select(.event == "pull_request_target") + | select(.path == ".github/workflows/opencode-review.yml") + | select(any((.pull_requests // [])[]?; ((.number // 0) | tostring) == $pr and ((.head.sha // "") | ascii_downcase) == ($head | ascii_downcase))) + | [(.id // ""), (.status // ""), (.conclusion // "")] + | @tsv + ')" || required_run="" + if [ -z "$required_run" ]; then + echo "::error::Referenced Required OpenCode Review run does not match the exact PR/head/workflow identity." + exit 1 + fi + IFS=$'\t' read -r required_run_id required_status required_conclusion <<<"$required_run" + if [ "$required_status" = "completed" ] && [ "$required_conclusion" = "success" ]; then + echo "Exact-PR/head Required OpenCode Review run ${required_run_id} already succeeded." + exit 0 + fi + if [ "$required_status" != "completed" ] || [ "$required_conclusion" != "failure" ]; then + echo "Exact required run is not a completed failure; workflow_run completion reconciliation owns any later transition." + exit 0 + fi + if gh api -X POST "repos/${GH_REPOSITORY}/actions/runs/${required_run_id}/rerun-failed-jobs" >/dev/null; then + echo "Re-ran failed jobs for exact-PR/head Required OpenCode Review run ${required_run_id} after formal exact-head evidence." + exit 0 + fi + if ! advanced="$(gh api "repos/${GH_REPOSITORY}/actions/runs/${required_run_id}")"; then + echo "::error::Rerun mutation failed and exact-run readback is unavailable; failing closed." + exit 1 + fi + advanced_state="$(printf '%s\n' "$advanced" | jq -r '[.status // "", .conclusion // ""] | @tsv')" + if [ "$advanced_state" != $'completed\tfailure' ]; then + echo "Exact required run advanced concurrently; no duplicate rerun is needed." + exit 0 + fi + echo "::error::Exact required run remains failed after the rerun mutation failed." exit 1 - name: Publish repository_dispatch OpenCode status diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml index a7415cee22..d43ba56590 100644 --- a/.github/workflows/opencode-review.yml +++ b/.github/workflows/opencode-review.yml @@ -391,7 +391,10 @@ jobs: echo "::error::Missing PR number or head SHA; cannot verify a current-head OpenCode verdict." exit 1 fi - live_pr="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")" + if ! live_pr="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"; then + echo "::error::Live pull request API read failed during one-shot current-head verdict admission; failing closed and releasing the runner." + exit 1 + fi live_head="$(printf '%s' "$live_pr" | jq -r '.head.sha // empty')" live_draft="$(printf '%s' "$live_pr" | jq -r 'if (.draft | type) == "boolean" then (.draft | tostring) else empty end')" live_state="$(printf '%s' "$live_pr" | jq -r 'if (.state | type) == "string" then .state else empty end')" @@ -412,96 +415,25 @@ jobs: exit 0 fi if [ "${live_head,,}" != "${HEAD_SHA,,}" ]; then - echo "Pull request head moved on the live open, ready-for-review PR; a fresh poll will start for the current head." + echo "Pull request head moved on the live open, ready-for-review PR; a fresh required-review run will bind the current head." exit 0 fi if [ "$PR_DRAFT" = "true" ]; then - echo "Event draft snapshot is stale; continuing verdict polling for the live ready PR." + echo "Event draft snapshot is stale; continuing one-shot verdict admission for the live ready PR." fi - verdict="" - live_poll_failures=0 - review_poll_failures=0 - max_poll_transport_failures=3 - poll_interval_seconds=60 - # Wall-clock backstop, distinct from max_poll_transport_failures above: - # that counter only bounds *consecutive transport failures*, so a - # review dispatch that never produces a verdict -- while every - # individual `gh api` call keeps succeeding -- previously polled - # forever, holding a live runner for up to GitHub's 360-minute - # platform default job timeout. 10800s (3h) is chosen to stay - # comfortably above this org's own documented "accommodate over 2 - # hours per model" allowance (docs/product-goal-directive.md §8) - # while still releasing the runner well before the platform - # default. This bounds how long the CI job waits for a verdict; it - # does not cap the model's own reasoning/streaming time, which - # remains governed entirely upstream by the dispatched review run - # itself. - poll_deadline_epoch=$(( $(date -u +%s) + 10800 )) - while :; do - if [ "$(date -u +%s)" -ge "$poll_deadline_epoch" ]; then - echo "::error::No current-head OpenCode verdict after 180 minutes of polling; failing closed and releasing the runner." - exit 1 - fi - if ! live_poll_pr="$(timeout 30s gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"; then - live_poll_failures=$((live_poll_failures + 1)) - if [ "$live_poll_failures" -ge "$max_poll_transport_failures" ]; then - echo "::error::Live pull request read failed ${live_poll_failures} consecutive times while polling; failing closed and releasing the runner." - exit 1 - fi - echo "::warning::Live pull request read failed while polling (${live_poll_failures}/${max_poll_transport_failures}); retrying after revalidation delay." - sleep "$poll_interval_seconds" - continue - fi - live_poll_failures=0 - live_poll_head="$(printf '%s' "$live_poll_pr" | jq -r '.head.sha // empty')" - live_poll_draft="$(printf '%s' "$live_poll_pr" | jq -r 'if (.draft | type) == "boolean" then (.draft | tostring) else empty end')" - live_poll_state="$(printf '%s' "$live_poll_pr" | jq -r 'if (.state | type) == "string" then .state else empty end')" - if [ -z "$live_poll_head" ] || [ -z "$live_poll_draft" ] || [ -z "$live_poll_state" ]; then - echo "::error::Could not validate live pull request state while polling for a current-head OpenCode verdict." - exit 1 - fi - if [ "$live_poll_state" != "open" ] && [ "$live_poll_state" != "closed" ]; then - echo "::error::Could not validate live pull request state while polling for a current-head OpenCode verdict." - exit 1 - fi - if [ "${live_poll_head,,}" != "${HEAD_SHA,,}" ]; then - echo "::notice::Pull request head moved while waiting for a current-head OpenCode verdict; retiring superseded Required OpenCode Review poll." - exit 1 - fi - if [ "$live_poll_state" = "closed" ]; then - echo "PR closed while waiting for the current-head OpenCode verdict; the poll is no longer required." - exit 0 - fi - if [ "$live_poll_draft" = "true" ]; then - echo "PR became draft while waiting for the current-head OpenCode verdict; the poll is no longer required until it is marked ready for review." - exit 0 - fi - if ! reviews="$(timeout 30s gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"; then - review_poll_failures=$((review_poll_failures + 1)) - if [ "$review_poll_failures" -ge "$max_poll_transport_failures" ]; then - echo "::error::Reviews API read failed ${review_poll_failures} consecutive times while polling; failing closed and releasing the runner." - exit 1 - fi - echo "::warning::Reviews API read failed while polling (${review_poll_failures}/${max_poll_transport_failures}); revalidating live PR state before retry." - sleep "$poll_interval_seconds" - continue - fi - review_poll_failures=0 - verdict="$(printf '%s\n' "$reviews" | jq -r -s --arg sha "$HEAD_SHA" ' + if ! reviews="$(gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"; then + echo "::error::Reviews API read failed during one-shot current-head verdict admission; failing closed and releasing the runner." + exit 1 + fi + verdict="$(printf '%s\n' "$reviews" | jq -r -s --arg sha "$HEAD_SHA" ' (add // []) - | [ - .[] - | select( - (.user.login // "" | ascii_downcase) as $user - | $user == "opencode-agent" or $user == "opencode-agent[bot]" - ) + | [.[] + | select((.user.login // "" | ascii_downcase) as $user | $user == "opencode-agent" or $user == "opencode-agent[bot]") | select((.commit_id // "" | ascii_downcase) == ($sha | ascii_downcase)) - | select(.state == "APPROVED" or .state == "CHANGES_REQUESTED") - ] + | select(.state == "APPROVED" or .state == "CHANGES_REQUESTED")] | (last // {}) as $review | ($review.body // "" | ascii_downcase) as $body - | if $review.state == "CHANGES_REQUESTED" then - "CHANGES_REQUESTED" + | if $review.state == "CHANGES_REQUESTED" then "CHANGES_REQUESTED" elif $review.state == "APPROVED" and ($body | contains("deterministic current-head evidence") | not) and ($body | contains("deterministic fallback approval") | not) @@ -509,19 +441,10 @@ jobs: and ($body | contains("did not emit a usable current-head control block") | not) and ($body | contains("scope: `unsupported`") | not) and ($body | contains("model-pool outcome: `unknown`") | not) - then - "APPROVED" - else - empty - end + then "APPROVED" else empty end ')" - if [ -n "$verdict" ]; then - break - fi - sleep "$poll_interval_seconds" - done if [ -z "$verdict" ]; then - echo "::error::No APPROVED or CHANGES_REQUESTED from opencode-agent on the current head. This required check is not a review and must not succeed until the authenticated dispatch posts a current-head verdict." + echo "::error::No APPROVED or CHANGES_REQUESTED from opencode-agent on the current head. This required check is not a review and must not succeed until the authenticated dispatch posts a current-head verdict. The dispatch path wakes this exact failed run when the verdict arrives." exit 1 fi echo "Current-head OpenCode verdict: ${verdict}." diff --git a/.github/workflows/pr-review-merge-scheduler.yml b/.github/workflows/pr-review-merge-scheduler.yml index 718f307d71..e3d3310d8b 100644 --- a/.github/workflows/pr-review-merge-scheduler.yml +++ b/.github/workflows/pr-review-merge-scheduler.yml @@ -127,6 +127,100 @@ permissions: contents: read jobs: + reconcile-opencode-required-verdict: + name: reconcile-opencode-required-verdict + if: >- + github.event_name == 'workflow_run' + && github.event.workflow_run.name == 'Required OpenCode Review' + && github.event.workflow_run.conclusion == 'failure' + && github.event.workflow_run.event == 'pull_request_target' + && github.event.workflow_run.path == '.github/workflows/opencode-review.yml' + && github.event.workflow_run.pull_requests[0].number + runs-on: ubuntu-slim + permissions: + actions: write + contents: read + pull-requests: read + env: + GH_TOKEN: ${{ github.token }} + PR_NUMBER: ${{ github.event.workflow_run.pull_requests[0].number }} + PR_HEAD_SHA: ${{ github.event.workflow_run.pull_requests[0].head.sha }} + REQUIRED_RUN_ID: ${{ github.event.workflow_run.id }} + REQUIRED_RUN_STARTED_AT: ${{ github.event.workflow_run.run_started_at }} + steps: + - name: Reconcile newer formal review evidence with the completed required run + shell: bash + run: | + set -euo pipefail + [[ "$PR_NUMBER" =~ ^[1-9][0-9]*$ ]] || { echo "::error::workflow_run PR number is missing or non-canonical."; exit 1; } + [[ "$PR_HEAD_SHA" =~ ^[0-9a-fA-F]{40}$ ]] || { echo "::error::workflow_run PR head is missing or malformed."; exit 1; } + [[ "$REQUIRED_RUN_ID" =~ ^[1-9][0-9]*$ ]] || { echo "::error::workflow_run id is missing or non-canonical."; exit 1; } + if [ -z "$REQUIRED_RUN_STARTED_AT" ]; then + echo "::error::workflow_run start provenance is missing; failing closed." + exit 1 + fi + if ! live_pr="$(gh api "repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}")"; then + echo "::error::Live PR read failed during event-driven Required OpenCode reconciliation; failing closed." + exit 1 + fi + live_head="$(printf '%s\n' "$live_pr" | jq -r '.head.sha // empty')" + live_state="$(printf '%s\n' "$live_pr" | jq -r '.state // empty')" + live_draft="$(printf '%s\n' "$live_pr" | jq -r 'if (.draft | type) == "boolean" then (.draft | tostring) else empty end')" + if [ "$live_state" != "open" ] || [ "$live_draft" = "true" ] || [ "${live_head,,}" != "${PR_HEAD_SHA,,}" ]; then + echo "Required run is no longer authoritative for an open ready exact-head PR; no wake mutation is allowed." + exit 0 + fi + if ! reviews="$(gh api --paginate "repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}/reviews")"; then + echo "::error::Reviews API read failed during event-driven Required OpenCode reconciliation; failing closed." + exit 1 + fi + latest_review="$(printf '%s\n' "$reviews" | jq -r -s --arg sha "$PR_HEAD_SHA" ' + (add // []) + | [.[] + | select((.user.login // "" | ascii_downcase) as $user | $user == "opencode-agent" or $user == "opencode-agent[bot]") + | select((.commit_id // "" | ascii_downcase) == ($sha | ascii_downcase)) + | select(.state == "APPROVED" or .state == "CHANGES_REQUESTED") + | select((.body // "" | ascii_downcase | contains("deterministic current-head evidence")) | not) + | select((.body // "" | ascii_downcase | contains("deterministic fallback approval")) | not) + | select((.body // "" | ascii_downcase | contains("model-unavailable evidence fallback")) | not) + | select((.body // "" | ascii_downcase | contains("did not emit a usable current-head control block")) | not) + | select((.body // "" | ascii_downcase | contains("scope: `unsupported`")) | not) + | select((.body // "" | ascii_downcase | contains("model-pool outcome: `unknown`")) | not)] + | sort_by(.submitted_at // "", .id // 0) + | (last // {}) + | [(.state // ""), (.submitted_at // "")] + | @tsv + ')" + IFS=$'\t' read -r review_state review_submitted_at <<<"$latest_review" + if [ -z "$review_state" ]; then + echo "No formal exact-head OpenCode review exists yet; the later review receipt event owns reconciliation." + exit 0 + fi + if [ -z "$review_submitted_at" ]; then + echo "::error::Formal exact-head review lacks submission provenance; failing closed." + exit 1 + fi + new_evidence="$(jq -nr --arg review "$review_submitted_at" --arg started "$REQUIRED_RUN_STARTED_AT" 'try (($review | fromdateiso8601) > ($started | fromdateiso8601)) catch false')" + if [ "$new_evidence" != "true" ]; then + echo "Formal review predates this run attempt; the failure is not attributable to missing newer review evidence." + exit 0 + fi + if gh api -X POST "repos/${GITHUB_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}/rerun-failed-jobs" >/dev/null; then + echo "Re-ran failed jobs for Required OpenCode Review run ${REQUIRED_RUN_ID} after newer formal exact-head evidence." + exit 0 + fi + if ! advanced="$(gh api "repos/${GITHUB_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}")"; then + echo "::error::Rerun mutation failed and exact-run readback is unavailable; failing closed." + exit 1 + fi + advanced_state="$(printf '%s\n' "$advanced" | jq -r '[.status // "", .conclusion // ""] | @tsv')" + if [ "$advanced_state" != $'completed\tfailure' ]; then + echo "Exact required run advanced concurrently; no duplicate rerun is needed." + exit 0 + fi + echo "::error::Exact required run remains failed after the rerun mutation failed." + exit 1 + scan-pr-queue: # repository_dispatch review runs do not reliably carry pull_requests metadata. # Without this guard, one completed central review can wake a repo-wide scan. diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index e12f33542d..1cbe65d719 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -266,3 +266,13 @@ resolver conflict. — current increment's attestation decision and APA 7th citations. - [`docs/doctoring/sandboxed-web-readiness-loopback-boundary.md`](docs/doctoring/sandboxed-web-readiness-loopback-boundary.md) — loopback-only web E2E readiness polling and APA 7th citations. + + +### Required OpenCode one-shot verdict admission + +The protected required workflow validates live PR state once, reads formal review evidence once, and releases its runner immediately when no exact-head verdict exists. The authenticated default-branch dispatch revalidates repository, immutable run id, workflow path, PR number, and `pull_requests[].head.sha` before `rerun-failed-jobs`. GitHub's documented workflow-run completion event complements the formal-review receipt event; model reasoning has no caller wall-clock deadline and no fixed wake retry allocation is retained. + + +### Required OpenCode event-driven verdict admission + +The required workflow performs one live-PR read and one complete paginated formal-review read, then fails closed immediately if no exact-head verdict exists. The privileged formal-review receipt reconciles the immutable required-run id once. If that review arrives before the run finishes, GitHub's `workflow_run: completed` event performs the complementary reconciliation. The completion path admits a rerun only when exact PR/head/workflow identity holds and `review.submitted_at > run.run_started_at`; the same old evidence therefore cannot create an unbounded rerun cycle. No repository-authored polling cadence, retry count, sleep, transport timeout, or model reasoning deadline is part of this verdict-wake state machine. diff --git a/CHANGELOG.md b/CHANGELOG.md index ac1985d86f..4f1901da49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +- Required OpenCode verdict wake is event-driven: formal-review receipt and GitHub `workflow_run: completed` reconcile exact run/PR/head state, replacing runner polling and fixed wake retry/sleep/transport allocations. +- Required OpenCode Review now releases its runner after one exact-head verdict admission read and relies on authenticated exact-run dispatch wake instead of repository-authored polling, retry-count, or waiting deadlines. # Changelog All notable changes to the organization automation repository are documented in diff --git a/docs/doctoring/opencode-stale-poll-self-retirement.md b/docs/doctoring/opencode-stale-poll-self-retirement.md index 4bfdc0be8a..c71ccde9bc 100644 --- a/docs/doctoring/opencode-stale-poll-self-retirement.md +++ b/docs/doctoring/opencode-stale-poll-self-retirement.md @@ -46,3 +46,20 @@ Rollback is the ordinary revert of the workflow repair if exact-head evidence sh Monitor both runner occupancy and GitHub API failure/rate-limit evidence. Repeated transport failures should terminate the required check after three bounded attempts rather than leave an immortal poll. A rate-pressure regression should be repaired by changing evidence acquisition/cadence without weakening exact-head review semantics. After protected integration, re-observe affected leaf repositories. Acceptance requires predecessor-head OpenCode polls to release runner capacity without waiting for a separate cleanup runner, while unchanged current-head semantic reviews remain able to run beyond arbitrary short deadlines and current-head polls stay within a defensible REST request budget. + + +## 2026-09-02 one-shot runner-release supersession + +The required-verdict job performs one authoritative live-PR read followed by at most one paginated Reviews read. Missing or unavailable exact-head verdict evidence fails closed immediately and releases the runner. Authenticated `opencode-review-dispatch.yml` wakes the exact failed run via `rerun-failed-jobs` when the formal verdict arrives; no repository-authored polling interval, retry count, or wall-clock deadline bounds model work. + + +### Exact-run wake identity and transient lookup correction + +For `pull_request_target`, top-level workflow-run `head_sha` identifies the base revision, not the PR head. Wake authority binds immutable run id, event, workflow path, exact PR number, and `pull_requests[].head.sha`. Run-lookup transport failure fails closed without inventing a retry policy; the independent GitHub workflow-run completion event closes the opposite review-before-failure ordering. + + +### 2026-09-02 event-driven wake supersedes fixed retry allocation + +RCA found that the intermediate PR #1706 repair replaced a runner-held verdict poll with a dispatch wake loop containing fixed `12` attempts, `5` second sleeps, and `30` second transport deadlines. Those values had no governing model, standard, experiment, or provider contract. The corrected state machine uses the authenticated formal-review receipt event plus GitHub's `workflow_run` `completed` event. Receipt-after-failure performs one exact-run transition; review-before-failure is reconciled on completion and requires `review.submitted_at > run.run_started_at`. Mutation readback only resolves concurrent state advancement and is not a retry loop. + +Reference (APA 7): GitHub. (2026). *Events that trigger workflows*. GitHub Docs. https://docs.github.com/actions/using-workflows/events-that-trigger-workflows#workflow_run diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md index 29acdfeecc..74b0c1907d 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -2613,3 +2613,18 @@ Higgins, S. S., Crepalde, N., & Fernandes, L. (2021). Segmented multiplexity: A **Expected effect.** No observable change to any current GitHub Actions review run (every current invocation already resolves to `free`). The effect is structural: it is no longer possible for a future workflow edit or manual dispatch override to admit priced-model spend into a required review check without an explicit, reviewed code change to this one `case` statement (and its now-locked-in regression test) first. **Follow-up.** If the organization later solves free+ZDR routing robustly enough to deliberately widen required-review CI to `orchestrator/auto` (e.g. once a spend ceiling and reviewer-visible cost evidence exist for that path), the change is exactly one `case` arm plus the corresponding assertions in `test_sidecar_pins_the_pool_to_free_for_github_actions` — this entry is the record of *why* it was narrowed, not a permanent prohibition. + + +### OPENCODE-ONE-SHOT-RUNNER-RELEASE-2026-09-02 +- Owner: `ContextualWisdomLab/.github` Required OpenCode Review control plane. +- RCA: required-verdict occupied a runner while asynchronous model work continued, using repository-authored polling/retry/wall-clock allocation despite an authenticated exact-run wake contract. +- GREEN: one live PR read plus one Reviews read; missing/unavailable exact-head verdict fails closed immediately and dispatch wakes the exact failed run after the verdict. Model reasoning receives no caller wall-clock timeout. +- Regression: `tests/test_opencode_required_verdict_runner_release.py` plus one-shot request/state and dispatch-wake contracts. + + +### OPENCODE-EVENT-DRIVEN-REQUIRED-WAKE-2026-09-02 +- Gap: Required OpenCode verdict admission occupied a hosted runner while waiting; an intermediate repair then introduced fixed dispatch retry/sleep/transport allocations (`12`, `5s`, `30s`) without a governing model or standard. +- Causal owner: `ContextualWisdomLab/.github` required review and merge-control workflows. +- Repair: one-shot exact-head verdict admission plus dual event reconciliation. A formal-review receipt handles review-after-failure; GitHub `workflow_run: completed` handles review-before-failure with exact PR/head/workflow identity and `review.submitted_at > run.run_started_at`. +- Verification: executable regressions cover one-shot admission, `pull_request_target` PR-head identity rather than base `head_sha`, opposite event orderings, stale evidence, and absence of repository-authored retry/sleep/transport budgets. +- Status: Proposed on PR #1706 until exact-head focused/full CI and independent review are GREEN. diff --git a/scripts/ci/temp_pr1706_event_driven_wake_v4.py b/scripts/ci/temp_pr1706_event_driven_wake_v4.py deleted file mode 100644 index 9c36444c5a..0000000000 --- a/scripts/ci/temp_pr1706_event_driven_wake_v4.py +++ /dev/null @@ -1,428 +0,0 @@ -"""Materialize PR #1706 one-shot admission and event-driven wake repair. - -Temporary source-fix helper. The successful publication workflow deletes this -file and every other PR #1706 source-fix artifact from the final tree. -""" - -from __future__ import annotations - -from pathlib import Path - - -REQUIRED = Path(".github/workflows/opencode-review.yml") -DISPATCH = Path(".github/workflows/opencode-review-dispatch.yml") -SCHEDULER = Path(".github/workflows/pr-review-merge-scheduler.yml") -ACCEPTANCE = Path("tests/test_opencode_required_verdict_runner_release.py") -SELF = Path("tests/test_opencode_poll_self_retirement.py") -EVENT = Path("tests/test_opencode_event_driven_required_wake.py") -REGRESSION = Path("tests/test_opencode_required_verdict_regression.py") -ARCHITECTURE = Path("ARCHITECTURE.md") -DOCTORING = Path("docs/doctoring/opencode-stale-poll-self-retirement.md") -BASELINE = Path("docs/product-technical-gap-baseline.md") -CHANGELOG = Path("CHANGELOG.md") - - -def replace_once(text: str, old: str, new: str, label: str) -> str: - """Replace one exact fragment and fail closed on concurrent drift.""" - count = text.count(old) - if count != 1: - raise SystemExit(f"{label} drifted: expected one exact match, found {count}") - return text.replace(old, new, 1) - - -# The prior deterministic stage converts verdict admission to one authoritative -# PR read plus one complete Reviews read. Remove its temporary transport budget: -# repository policy must not turn GitHub I/O latency into review semantics. -required = REQUIRED.read_text(encoding="utf-8") -start = " - name: Fail closed without a current-head OpenCode verdict\n" -end = "\n cancel-superseded-opencode-review-runs:\n" -if required.count(start) != 1 or required.count(end) != 1: - raise SystemExit("Required OpenCode verdict step boundaries drifted") -before, rest = required.split(start, 1) -step, after = rest.split(end, 1) -step = step.replace("timeout 30s gh api ", "gh api ") -if any(token in step for token in ("timeout ", "while :; do", "sleep ")): - raise SystemExit("Required verdict admission still contains local wait allocation") -REQUIRED.write_text(before + start + step + end + after, encoding="utf-8") - - -# The formal-review receipt is already authenticated and exact-head validated by -# the preceding dispatch steps. Reconcile that new event against the immutable -# required run exactly once. If the run has not failed yet, do nothing here: the -# workflow_run completion reconciliation below owns the opposite event ordering. -dispatch = DISPATCH.read_text(encoding="utf-8") -wake_start = " - name: Wake exact-head required OpenCode workflow\n" -wake_end = "\n - name: Publish repository_dispatch OpenCode status\n" -if dispatch.count(wake_start) != 1 or dispatch.count(wake_end) != 1: - raise SystemExit("OpenCode dispatch wake boundaries drifted") -d_before, d_rest = dispatch.split(wake_start, 1) -_old_wake, d_after = d_rest.split(wake_end, 1) -wake = r''' - name: Wake exact-head required OpenCode workflow - if: >- - always() - && github.event_name == 'repository_dispatch' - && steps.formal_review_receipt.outcome == 'success' - && needs.validate-pr-metadata.outputs.target_repository != '' - && needs.validate-pr-metadata.outputs.pr_number != '' - && needs.validate-pr-metadata.outputs.head_sha != '' - && github.event.client_payload.required_run_id != '' - env: - GH_TOKEN: ${{ needs.validate-pr-metadata.outputs.target_repository == github.repository && github.token || secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN }} - GH_REPOSITORY: ${{ needs.validate-pr-metadata.outputs.target_repository }} - PR_NUMBER: ${{ needs.validate-pr-metadata.outputs.pr_number }} - PR_HEAD_SHA: ${{ needs.validate-pr-metadata.outputs.head_sha }} - REQUIRED_RUN_ID: ${{ github.event.client_payload.required_run_id }} - WAKE_TOKEN_SOURCE: ${{ needs.validate-pr-metadata.outputs.target_repository == github.repository && 'github-token' || secrets.PR_REVIEW_MERGE_TOKEN != '' && 'PR_REVIEW_MERGE_TOKEN' || secrets.OPENCODE_APPROVE_TOKEN != '' && 'OPENCODE_APPROVE_TOKEN' || 'unavailable' }} - run: | - set -euo pipefail - if [ -z "${GH_TOKEN:-}" ] || [ "$WAKE_TOKEN_SOURCE" = "unavailable" ]; then - echo "::error::Actions-capable wake credential is unavailable. Native runs use github.token; sibling runs require PR_REVIEW_MERGE_TOKEN or OPENCODE_APPROVE_TOKEN." - exit 1 - fi - [[ "$REQUIRED_RUN_ID" =~ ^[1-9][0-9]*$ ]] || { echo "::error::Required OpenCode run id is missing or non-canonical."; exit 1; } - [[ "$PR_NUMBER" =~ ^[1-9][0-9]*$ ]] || { echo "::error::Required OpenCode PR number is missing or non-canonical."; exit 1; } - [[ "$PR_HEAD_SHA" =~ ^[0-9a-fA-F]{40}$ ]] || { echo "::error::Required OpenCode PR head SHA is missing or malformed."; exit 1; } - if ! run="$(gh api "repos/${GH_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}")"; then - echo "::error::Exact required-run lookup failed; no retry policy is invented." - exit 1 - fi - required_run="$(printf '%s\n' "$run" | jq -r --arg head "$PR_HEAD_SHA" --arg pr "$PR_NUMBER" --argjson run_id "$REQUIRED_RUN_ID" ' - select(.id == $run_id) - | select(.event == "pull_request_target") - | select(.path == ".github/workflows/opencode-review.yml") - | select(any((.pull_requests // [])[]?; ((.number // 0) | tostring) == $pr and ((.head.sha // "") | ascii_downcase) == ($head | ascii_downcase))) - | [(.id // ""), (.status // ""), (.conclusion // "")] - | @tsv - ')" || required_run="" - if [ -z "$required_run" ]; then - echo "::error::Referenced Required OpenCode Review run does not match the exact PR/head/workflow identity." - exit 1 - fi - IFS=$'\t' read -r required_run_id required_status required_conclusion <<<"$required_run" - if [ "$required_status" = "completed" ] && [ "$required_conclusion" = "success" ]; then - echo "Exact-PR/head Required OpenCode Review run ${required_run_id} already succeeded." - exit 0 - fi - if [ "$required_status" != "completed" ] || [ "$required_conclusion" != "failure" ]; then - echo "Exact required run is not a completed failure; workflow_run completion reconciliation owns any later transition." - exit 0 - fi - if gh api -X POST "repos/${GH_REPOSITORY}/actions/runs/${required_run_id}/rerun-failed-jobs" >/dev/null; then - echo "Re-ran failed jobs for exact-PR/head Required OpenCode Review run ${required_run_id} after formal exact-head evidence." - exit 0 - fi - if ! advanced="$(gh api "repos/${GH_REPOSITORY}/actions/runs/${required_run_id}")"; then - echo "::error::Rerun mutation failed and exact-run readback is unavailable; failing closed." - exit 1 - fi - advanced_state="$(printf '%s\n' "$advanced" | jq -r '[.status // "", .conclusion // ""] | @tsv')" - if [ "$advanced_state" != $'completed\tfailure' ]; then - echo "Exact required run advanced concurrently; no duplicate rerun is needed." - exit 0 - fi - echo "::error::Exact required run remains failed after the rerun mutation failed." - exit 1 -''' -DISPATCH.write_text(d_before + wake + wake_end + d_after, encoding="utf-8") - - -# workflow_run/completed closes review-before-failure. This path independently -# revalidates live PR/head authority and requires review.submitted_at to be newer -# than run.run_started_at, preventing an old verdict from driving a rerun cycle. -scheduler = SCHEDULER.read_text(encoding="utf-8") -job_marker = "jobs:\n scan-pr-queue:\n" -if " reconcile-opencode-required-verdict:\n" not in scheduler: - reconcile_job = r'''jobs: - reconcile-opencode-required-verdict: - name: reconcile-opencode-required-verdict - if: >- - github.event_name == 'workflow_run' - && github.event.workflow_run.name == 'Required OpenCode Review' - && github.event.workflow_run.conclusion == 'failure' - && github.event.workflow_run.event == 'pull_request_target' - && github.event.workflow_run.path == '.github/workflows/opencode-review.yml' - && github.event.workflow_run.pull_requests[0].number - runs-on: ubuntu-slim - permissions: - actions: write - contents: read - pull-requests: read - env: - GH_TOKEN: ${{ github.token }} - PR_NUMBER: ${{ github.event.workflow_run.pull_requests[0].number }} - PR_HEAD_SHA: ${{ github.event.workflow_run.pull_requests[0].head.sha }} - REQUIRED_RUN_ID: ${{ github.event.workflow_run.id }} - REQUIRED_RUN_STARTED_AT: ${{ github.event.workflow_run.run_started_at }} - steps: - - name: Reconcile newer formal review evidence with the completed required run - shell: bash - run: | - set -euo pipefail - [[ "$PR_NUMBER" =~ ^[1-9][0-9]*$ ]] || { echo "::error::workflow_run PR number is missing or non-canonical."; exit 1; } - [[ "$PR_HEAD_SHA" =~ ^[0-9a-fA-F]{40}$ ]] || { echo "::error::workflow_run PR head is missing or malformed."; exit 1; } - [[ "$REQUIRED_RUN_ID" =~ ^[1-9][0-9]*$ ]] || { echo "::error::workflow_run id is missing or non-canonical."; exit 1; } - if [ -z "$REQUIRED_RUN_STARTED_AT" ]; then - echo "::error::workflow_run start provenance is missing; failing closed." - exit 1 - fi - if ! live_pr="$(gh api "repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}")"; then - echo "::error::Live PR read failed during event-driven Required OpenCode reconciliation; failing closed." - exit 1 - fi - live_head="$(printf '%s\n' "$live_pr" | jq -r '.head.sha // empty')" - live_state="$(printf '%s\n' "$live_pr" | jq -r '.state // empty')" - live_draft="$(printf '%s\n' "$live_pr" | jq -r 'if (.draft | type) == "boolean" then (.draft | tostring) else empty end')" - if [ "$live_state" != "open" ] || [ "$live_draft" = "true" ] || [ "${live_head,,}" != "${PR_HEAD_SHA,,}" ]; then - echo "Required run is no longer authoritative for an open ready exact-head PR; no wake mutation is allowed." - exit 0 - fi - if ! reviews="$(gh api --paginate "repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}/reviews")"; then - echo "::error::Reviews API read failed during event-driven Required OpenCode reconciliation; failing closed." - exit 1 - fi - latest_review="$(printf '%s\n' "$reviews" | jq -r -s --arg sha "$PR_HEAD_SHA" ' - (add // []) - | [.[] - | select((.user.login // "" | ascii_downcase) as $user | $user == "opencode-agent" or $user == "opencode-agent[bot]") - | select((.commit_id // "" | ascii_downcase) == ($sha | ascii_downcase)) - | select(.state == "APPROVED" or .state == "CHANGES_REQUESTED") - | select((.body // "" | ascii_downcase | contains("deterministic current-head evidence")) | not) - | select((.body // "" | ascii_downcase | contains("deterministic fallback approval")) | not) - | select((.body // "" | ascii_downcase | contains("model-unavailable evidence fallback")) | not) - | select((.body // "" | ascii_downcase | contains("did not emit a usable current-head control block")) | not) - | select((.body // "" | ascii_downcase | contains("scope: `unsupported`")) | not) - | select((.body // "" | ascii_downcase | contains("model-pool outcome: `unknown`")) | not)] - | sort_by(.submitted_at // "", .id // 0) - | (last // {}) - | [(.state // ""), (.submitted_at // "")] - | @tsv - ')" - IFS=$'\t' read -r review_state review_submitted_at <<<"$latest_review" - if [ -z "$review_state" ]; then - echo "No formal exact-head OpenCode review exists yet; the later review receipt event owns reconciliation." - exit 0 - fi - if [ -z "$review_submitted_at" ]; then - echo "::error::Formal exact-head review lacks submission provenance; failing closed." - exit 1 - fi - new_evidence="$(jq -nr --arg review "$review_submitted_at" --arg started "$REQUIRED_RUN_STARTED_AT" 'try (($review | fromdateiso8601) > ($started | fromdateiso8601)) catch false')" - if [ "$new_evidence" != "true" ]; then - echo "Formal review predates this run attempt; the failure is not attributable to missing newer review evidence." - exit 0 - fi - if gh api -X POST "repos/${GITHUB_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}/rerun-failed-jobs" >/dev/null; then - echo "Re-ran failed jobs for Required OpenCode Review run ${REQUIRED_RUN_ID} after newer formal exact-head evidence." - exit 0 - fi - if ! advanced="$(gh api "repos/${GITHUB_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}")"; then - echo "::error::Rerun mutation failed and exact-run readback is unavailable; failing closed." - exit 1 - fi - advanced_state="$(printf '%s\n' "$advanced" | jq -r '[.status // "", .conclusion // ""] | @tsv')" - if [ "$advanced_state" != $'completed\tfailure' ]; then - echo "Exact required run advanced concurrently; no duplicate rerun is needed." - exit 0 - fi - echo "::error::Exact required run remains failed after the rerun mutation failed." - exit 1 - - scan-pr-queue: -''' - scheduler = replace_once(scheduler, job_marker, reconcile_job, "event reconciliation job") -SCHEDULER.write_text(scheduler, encoding="utf-8") - - -# Replace transitional tests with causal state-machine contracts. These tests -# deliberately avoid brittle indentation parsing and execute the relevant shell -# paths where useful. -ACCEPTANCE.write_text(r'''"""Regression coverage for one-shot Required OpenCode verdict admission.""" - -from __future__ import annotations - -import json -import os -import shutil -import subprocess -import textwrap -from pathlib import Path - -import pytest - -REQUIRED = Path(".github/workflows/opencode-review.yml") -DISPATCH = Path(".github/workflows/opencode-review-dispatch.yml") -HEAD = "a" * 40 - - -def _required_script() -> str: - """Return the production exact-head verdict-admission shell body.""" - text = REQUIRED.read_text(encoding="utf-8") - step = text.split(" - name: Fail closed without a current-head OpenCode verdict\n", 1)[1] - return textwrap.dedent(step.split(" run: |\n", 1)[1].split("\n cancel-superseded-opencode-review-runs:\n", 1)[0]) - - -def _wake() -> str: - """Return the production formal-receipt exact-run wake step.""" - text = DISPATCH.read_text(encoding="utf-8") - return text.split(" - name: Wake exact-head required OpenCode workflow\n", 1)[1].split("\n - name: Publish repository_dispatch OpenCode status\n", 1)[0] - - -def test_missing_verdict_releases_runner_without_local_wait_allocation() -> None: - """Admission performs complete state reads once and never polls or sleeps.""" - step = _required_script() - assert step.count('gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}"') == 1 - assert step.count('gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews') == 1 - for token in ("while :; do", "poll_interval_seconds", "poll_deadline_epoch", "max_poll_transport_failures", "sleep ", "timeout "): - assert token not in step - - -def test_receipt_wake_binds_exact_pr_head_and_run_without_polling() -> None: - """Authenticated receipt wake is one exact-state transition.""" - step = _wake() - for token in ("for attempt", "while :; do", "seq 1", "sleep ", "timeout ", "/12", "--paginate"): - assert token not in step - assert "pull_requests // []" in step - assert "rerun-failed-jobs" in step - assert "advanced concurrently" in step - - -def test_missing_verdict_fails_after_one_live_and_one_reviews_read(tmp_path: Path) -> None: - """No formal verdict causes exactly two GitHub reads and an immediate failure.""" - bash = shutil.which("bash") - jq = shutil.which("jq") - if bash is None or jq is None: - pytest.skip("bash and jq are required") - calls = tmp_path / "calls" - gh = tmp_path / "gh" - gh.write_text( - "#!/usr/bin/env bash\nset -euo pipefail\nprintf '%s\\n' \"$*\" >>\"$CALLS\"\n" - "if [[ \"$*\" == \"api repos/ContextualWisdomLab/example/pulls/42\" ]]; then printf '%s\\n' \"$LIVE_PR\"; exit 0; fi\n" - "if [[ \"$*\" == \"api --paginate repos/ContextualWisdomLab/example/pulls/42/reviews?per_page=100\" ]]; then printf '[]\\n'; exit 0; fi\n" - "exit 97\n", - encoding="utf-8", - ) - gh.chmod(0o755) - result = subprocess.run( - [bash, "-c", _required_script()], - env={**os.environ, "PATH": f"{tmp_path}{os.pathsep}{os.environ.get('PATH', '')}", "CALLS": str(calls), "LIVE_PR": json.dumps({"head": {"sha": HEAD}, "draft": False, "state": "open"}), "GH_TOKEN": "token", "TARGET_REPOSITORY": "ContextualWisdomLab/example", "PR_NUMBER": "42", "HEAD_SHA": HEAD, "PR_ACTION": "synchronize", "PR_DRAFT": "false"}, - text=True, - capture_output=True, - check=False, - ) - assert result.returncode == 1, result.stderr - assert len(calls.read_text(encoding="utf-8").splitlines()) == 2 -''', encoding="utf-8") - -SELF.write_text(r'''"""Regression contract for self-releasing Required OpenCode verdict admission.""" - -from pathlib import Path - -WORKFLOW = Path(".github/workflows/opencode-review.yml") - - -def _step() -> str: - """Return only the one-shot exact-head verdict-admission step.""" - text = WORKFLOW.read_text(encoding="utf-8") - return text.split(" - name: Fail closed without a current-head OpenCode verdict\n", 1)[1].split("\n cancel-superseded-opencode-review-runs:\n", 1)[0] - - -def test_one_shot_revalidates_live_authority_before_review_evidence() -> None: - """Live PR/head/draft/state authority precedes the complete Reviews read.""" - step = _step() - live = 'gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}"' - reviews = 'gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews' - assert live in step and reviews in step and step.index(live) < step.index(reviews) - assert "Could not validate live pull request state before verdict admission" in step - assert "PR is still a draft" in step - assert "fresh required-review run will bind the current head" in step - - -def test_one_shot_has_no_repository_authored_wait_retry_or_transport_deadline() -> None: - """No elapsed-time or fixed-attempt policy governs formal verdict admission.""" - step = _step() - for token in ("while :; do", "poll_interval_seconds", "poll_deadline_epoch", "max_poll_transport_failures", "sleep ", "timeout "): - assert token not in step -''', encoding="utf-8") - -EVENT.write_text(r'''"""Contracts for event-driven Required OpenCode Review wake reconciliation.""" - -from pathlib import Path - -REQUIRED = Path(".github/workflows/opencode-review.yml") -DISPATCH = Path(".github/workflows/opencode-review-dispatch.yml") -SCHEDULER = Path(".github/workflows/pr-review-merge-scheduler.yml") - - -def _required() -> str: - """Return only current-head formal-verdict admission.""" - text = REQUIRED.read_text(encoding="utf-8") - return text.split(" - name: Fail closed without a current-head OpenCode verdict\n", 1)[1].split("\n cancel-superseded-opencode-review-runs:\n", 1)[0] - - -def _wake() -> str: - """Return only authenticated formal-receipt wake.""" - text = DISPATCH.read_text(encoding="utf-8") - return text.split(" - name: Wake exact-head required OpenCode workflow\n", 1)[1].split("\n - name: Publish repository_dispatch OpenCode status\n", 1)[0] - - -def test_required_verdict_admission_has_no_repository_authored_wait_allocation() -> None: - """Missing verdict fails closed after authoritative reads, not elapsed time.""" - step = _required() - for token in ("while :; do", "poll_interval_seconds", "poll_deadline_epoch", "max_poll_transport_failures", "sleep ", "timeout "): - assert token not in step - - -def test_dispatch_receipt_wake_is_one_exact_state_transition() -> None: - """A formal receipt never introduces a retry, sleep, transport, or review-read loop.""" - step = _wake() - for token in ("for attempt", "seq 1", "sleep ", "timeout ", "/12", "--paginate"): - assert token not in step - assert "pull_requests // []" in step - assert "rerun-failed-jobs" in step - - -def test_workflow_run_completion_closes_review_before_failure_race() -> None: - """Failed completion reruns only when newer formal exact-head evidence exists.""" - scheduler = SCHEDULER.read_text(encoding="utf-8") - job = scheduler.split(" reconcile-opencode-required-verdict:\n", 1)[1].split("\n scan-pr-queue:\n", 1)[0] - assert "github.event_name == 'workflow_run'" in job - assert "github.event.workflow_run.name == 'Required OpenCode Review'" in job - assert "github.event.workflow_run.conclusion == 'failure'" in job - assert "github.event.workflow_run.run_started_at" in job - assert "review_submitted_at" in job and "fromdateiso8601" in job - assert "rerun-failed-jobs" in job - for token in ("for attempt", "while :; do", "sleep ", "timeout "): - assert token not in job -''', encoding="utf-8") - - -# Repair legacy regression assertions without weakening exact PR/head/run -# selection. v3 already migrates the selector to pull_requests[].head.sha. -regression = REGRESSION.read_text(encoding="utf-8") -regression = regression.replace(' assert "while :; do" in required\n', ' assert "while :; do" not in required\n', 1) -regression = regression.replace(' """The receipt wake path coexists with the unbounded required review wait."""\n', ' """The receipt wake path coexists with one-shot required verdict admission."""\n', 1) -REGRESSION.write_text(regression, encoding="utf-8") - - -architecture = ARCHITECTURE.read_text(encoding="utf-8") -heading = "### Required OpenCode event-driven verdict admission" -if heading not in architecture: - architecture += f'''\n\n{heading}\n\nThe required workflow performs one live-PR read and one complete paginated formal-review read, then fails closed immediately if no exact-head verdict exists. The privileged formal-review receipt reconciles the immutable required-run id once. If that review arrives before the run finishes, GitHub's `workflow_run: completed` event performs the complementary reconciliation. The completion path admits a rerun only when exact PR/head/workflow identity holds and `review.submitted_at > run.run_started_at`; the same old evidence therefore cannot create an unbounded rerun cycle. No repository-authored polling cadence, retry count, sleep, transport timeout, or model reasoning deadline is part of this verdict-wake state machine.\n''' - ARCHITECTURE.write_text(architecture, encoding="utf-8") - -doctoring = DOCTORING.read_text(encoding="utf-8") -heading = "### 2026-09-02 event-driven wake supersedes fixed retry allocation" -if heading not in doctoring: - doctoring += f'''\n\n{heading}\n\nRCA found that the intermediate PR #1706 repair replaced a runner-held verdict poll with a dispatch wake loop containing fixed `12` attempts, `5` second sleeps, and `30` second transport deadlines. Those values had no governing model, standard, experiment, or provider contract. The corrected state machine uses the authenticated formal-review receipt event plus GitHub's `workflow_run` `completed` event. Receipt-after-failure performs one exact-run transition; review-before-failure is reconciled on completion and requires `review.submitted_at > run.run_started_at`. Mutation readback only resolves concurrent state advancement and is not a retry loop.\n\nReference (APA 7): GitHub. (2026). *Events that trigger workflows*. GitHub Docs. https://docs.github.com/actions/using-workflows/events-that-trigger-workflows#workflow_run\n''' - DOCTORING.write_text(doctoring, encoding="utf-8") - -baseline = BASELINE.read_text(encoding="utf-8") -heading = "### OPENCODE-EVENT-DRIVEN-REQUIRED-WAKE-2026-09-02" -if heading not in baseline: - baseline += f'''\n\n{heading}\n- Gap: Required OpenCode verdict admission occupied a hosted runner while waiting; an intermediate repair then introduced fixed dispatch retry/sleep/transport allocations (`12`, `5s`, `30s`) without a governing model or standard.\n- Causal owner: `ContextualWisdomLab/.github` required review and merge-control workflows.\n- Repair: one-shot exact-head verdict admission plus dual event reconciliation. A formal-review receipt handles review-after-failure; GitHub `workflow_run: completed` handles review-before-failure with exact PR/head/workflow identity and `review.submitted_at > run.run_started_at`.\n- Verification: executable regressions cover one-shot admission, `pull_request_target` PR-head identity rather than base `head_sha`, opposite event orderings, stale evidence, and absence of repository-authored retry/sleep/transport budgets.\n- Status: Proposed on PR #1706 until exact-head focused/full CI and independent review are GREEN.\n''' - BASELINE.write_text(baseline, encoding="utf-8") - -changelog = CHANGELOG.read_text(encoding="utf-8") -note = "- Required OpenCode verdict wake is event-driven: formal-review receipt and GitHub `workflow_run: completed` reconcile exact run/PR/head state, replacing runner polling and fixed wake retry/sleep/transport allocations.\n" -if note not in changelog: - CHANGELOG.write_text(note + changelog, encoding="utf-8") diff --git a/scripts/ci/temp_pr1706_one_shot_runner_release.py b/scripts/ci/temp_pr1706_one_shot_runner_release.py deleted file mode 100644 index 397375b89e..0000000000 --- a/scripts/ci/temp_pr1706_one_shot_runner_release.py +++ /dev/null @@ -1,138 +0,0 @@ -"""Temporary exact-head repair driver for PR #1706; deleted after GREEN publication.""" - -from __future__ import annotations - -from pathlib import Path - - -WORKFLOW = Path(".github/workflows/opencode-review.yml") -REGRESSION = Path("tests/test_opencode_required_verdict_regression.py") -RATE = Path("tests/test_opencode_poll_rate_budget.py") -SELF = Path("tests/test_opencode_poll_self_retirement.py") - - -def replace_once(text: str, old: str, new: str, label: str) -> str: - """Replace one exact stale contract and fail if concurrent edits changed it.""" - count = text.count(old) - if count != 1: - raise SystemExit(f"{label} drifted: expected one exact match, found {count}") - return text.replace(old, new, 1) - - -workflow = WORKFLOW.read_text(encoding="utf-8") -start = " - name: Fail closed without a current-head OpenCode verdict\n" -end = "\n cancel-superseded-opencode-review-runs:\n" -if workflow.count(start) != 1 or workflow.count(end) != 1: - raise SystemExit("OpenCode required-verdict step boundaries drifted") -before, rest = workflow.split(start, 1) -_old_step, after = rest.split(end, 1) -replacement = r''' - name: Fail closed without a current-head OpenCode verdict - env: - GH_TOKEN: ${{ github.token }} - TARGET_REPOSITORY: ${{ github.event.pull_request.base.repo.full_name || github.repository }} - PR_NUMBER: ${{ github.event.pull_request.number }} - HEAD_SHA: ${{ github.event.pull_request.head.sha }} - PR_ACTION: ${{ github.event.action }} - PR_DRAFT: ${{ github.event.pull_request.draft }} - run: | - set -euo pipefail - if [ "$PR_ACTION" = "closed" ]; then - echo "PR closed; a current-head OpenCode verdict is not required." - exit 0 - fi - if [ -z "${PR_NUMBER:-}" ] || [ -z "${HEAD_SHA:-}" ]; then - echo "::error::Missing PR number or head SHA; cannot verify a current-head OpenCode verdict." - exit 1 - fi - live_pr="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")" - live_head="$(printf '%s' "$live_pr" | jq -r '.head.sha // empty')" - live_draft="$(printf '%s' "$live_pr" | jq -r 'if (.draft | type) == "boolean" then (.draft | tostring) else empty end')" - live_state="$(printf '%s' "$live_pr" | jq -r 'if (.state | type) == "string" then .state else empty end')" - if [ -z "$live_head" ] || [ -z "$live_draft" ] || [ -z "$live_state" ]; then - echo "::error::Could not validate live pull request state before verdict admission." - exit 1 - fi - if [ "$live_state" != "open" ] && [ "$live_state" != "closed" ]; then - echo "::error::Could not validate live pull request state before verdict admission." - exit 1 - fi - if [ "$live_state" = "closed" ]; then - echo "PR is closed on the live exact head; a current-head OpenCode verdict is not required." - exit 0 - fi - if [ "$live_draft" = "true" ]; then - echo "PR is still a draft on the live exact head; a current-head OpenCode verdict is not required until it is marked ready for review." - exit 0 - fi - if [ "${live_head,,}" != "${HEAD_SHA,,}" ]; then - echo "Pull request head moved on the live open, ready-for-review PR; a fresh required-review run will bind the current head." - exit 0 - fi - if [ "$PR_DRAFT" = "true" ]; then - echo "Event draft snapshot is stale; continuing one-shot verdict admission for the live ready PR." - fi - if ! reviews="$(gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"; then - echo "::error::Reviews API read failed during one-shot current-head verdict admission; failing closed and releasing the runner." - exit 1 - fi - verdict="$(printf '%s\n' "$reviews" | jq -r -s --arg sha "$HEAD_SHA" ' - (add // []) - | [.[] - | select((.user.login // "" | ascii_downcase) as $user | $user == "opencode-agent" or $user == "opencode-agent[bot]") - | select((.commit_id // "" | ascii_downcase) == ($sha | ascii_downcase)) - | select(.state == "APPROVED" or .state == "CHANGES_REQUESTED")] - | (last // {}) as $review - | ($review.body // "" | ascii_downcase) as $body - | if $review.state == "CHANGES_REQUESTED" then "CHANGES_REQUESTED" - elif $review.state == "APPROVED" - and ($body | contains("deterministic current-head evidence") | not) - and ($body | contains("deterministic fallback approval") | not) - and ($body | contains("model-unavailable evidence fallback") | not) - and ($body | contains("did not emit a usable current-head control block") | not) - and ($body | contains("scope: `unsupported`") | not) - and ($body | contains("model-pool outcome: `unknown`") | not) - then "APPROVED" else empty end - ')" - if [ -z "$verdict" ]; then - echo "::error::No APPROVED or CHANGES_REQUESTED from opencode-agent on the current head. This required check is not a review and must not succeed until the authenticated dispatch posts a current-head verdict. The dispatch path wakes this exact failed run when the verdict arrives." - exit 1 - fi - echo "Current-head OpenCode verdict: ${verdict}." -''' -WORKFLOW.write_text(before + replacement + end + after, encoding="utf-8") - -RATE.write_text('''"""Request-budget regression for one-shot Required OpenCode verdict admission."""\n\nfrom pathlib import Path\n\nWORKFLOW = Path(".github/workflows/opencode-review.yml")\n\ndef _step() -> str:\n workflow = WORKFLOW.read_text(encoding="utf-8")\n return workflow.split(" - name: Fail closed without a current-head OpenCode verdict\\n", 1)[1].split("\\n cancel-superseded-opencode-review-runs:\\n", 1)[0]\n\ndef test_admission_uses_one_reviews_read_without_runner_polling() -> None:\n step = _step()\n assert step.count('gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}"') == 1\n assert step.count('gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100"') == 1\n for token in ("while :; do", "poll_interval_seconds", "poll_deadline_epoch", "sleep "):\n assert token not in step\n\ndef test_review_read_keeps_maximum_rest_page_size() -> None:\n step = _step()\n assert "/reviews?per_page=100" in step\n assert "gh api --paginate" in step\n''', encoding="utf-8") - -SELF.write_text('''"""Regression contract for one-shot Required OpenCode verdict admission."""\n\nfrom pathlib import Path\n\nWORKFLOW = Path(".github/workflows/opencode-review.yml")\n\ndef _step() -> str:\n workflow = WORKFLOW.read_text(encoding="utf-8")\n return workflow.split(" - name: Fail closed without a current-head OpenCode verdict\\n", 1)[1].split("\\n cancel-superseded-opencode-review-runs:\\n", 1)[0]\n\ndef test_live_state_precedes_review_evidence() -> None:\n step = _step()\n live = 'live_pr="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"'\n reviews = 'reviews="$(gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"'\n assert live in step\n assert reviews in step\n assert step.index(live) < step.index(reviews)\n\ndef test_stale_or_terminal_state_releases_runner_before_review_read() -> None:\n step = _step()\n assert 'if [ "$live_state" = "closed" ]; then' in step\n assert 'if [ "$live_draft" = "true" ]; then' in step\n assert 'if [ "${live_head,,}" != "${HEAD_SHA,,}" ]; then' in step\n assert "fresh required-review run will bind the current head" in step\n\ndef test_transport_failure_is_one_shot_and_fail_closed() -> None:\n step = _step()\n assert "Reviews API read failed during one-shot current-head verdict admission" in step\n assert "exit 1" in step\n assert "while :; do" not in step\n assert "sleep " not in step\n\ndef test_semantic_review_has_no_repository_authored_wait_deadline() -> None:\n step = _step()\n for token in ("poll_deadline_epoch", "max_poll_transport_failures", "timeout 30s", "sleep "):\n assert token not in step\n''', encoding="utf-8") - -regression = REGRESSION.read_text(encoding="utf-8") -for old, new, label in [ - (' assert "while :; do" in target_job\n assert \'sleep "$poll_interval_seconds"\' in target_job\n', ' assert "while :; do" not in target_job\n assert "poll_interval_seconds" not in target_job\n assert "poll_deadline_epoch" not in target_job\n assert \'sleep "$poll_interval_seconds"\' not in target_job\n', "legacy poll assertions"), - ('a fresh poll will start for the current head.', 'a fresh required-review run will bind the current head.', "moved-head message"), - ('def test_fail_closed_step_still_polls_for_a_non_draft_pr(', 'def test_fail_closed_step_reads_reviews_once_for_a_non_draft_pr(', "poll test name"), - ('Reviews API read failed 3 consecutive times', 'Reviews API read failed during one-shot current-head verdict admission', "transport failure message"), - ('"""The receipt wake path coexists with the unbounded required review wait."""', '"""The receipt wake path reawakens the fail-closed one-shot required review."""', "receipt wake docstring"), - (' assert "while :; do" in required\n', ' assert "while :; do" not in required\n assert "poll_deadline_epoch" not in required\n', "receipt wake loop assertion"), -]: - regression = replace_once(regression, old, new, label) -REGRESSION.write_text(regression, encoding="utf-8") - -baseline = Path("docs/product-technical-gap-baseline.md") -text = baseline.read_text(encoding="utf-8") -marker = "### OPENCODE-ONE-SHOT-RUNNER-RELEASE-2026-09-02" -if marker not in text: - text += f'''\n\n{marker}\n- Owner: `ContextualWisdomLab/.github` Required OpenCode Review control plane.\n- RCA: required-verdict occupied a runner while asynchronous model work continued, using repository-authored polling/retry/wall-clock allocation despite an authenticated exact-run wake contract.\n- GREEN: one live PR read plus one Reviews read; missing/unavailable exact-head verdict fails closed immediately and dispatch wakes the exact failed run after the verdict. Model reasoning receives no caller wall-clock timeout.\n- Regression: `tests/test_opencode_required_verdict_runner_release.py` plus one-shot request/state and dispatch-wake contracts.\n''' - baseline.write_text(text, encoding="utf-8") - -doctoring = Path("docs/doctoring/opencode-stale-poll-self-retirement.md") -text = doctoring.read_text(encoding="utf-8") -marker = "## 2026-09-02 one-shot runner-release supersession" -if marker not in text: - text += f'''\n\n{marker}\n\nThe required-verdict job performs one authoritative live-PR read followed by at most one paginated Reviews read. Missing or unavailable exact-head verdict evidence fails closed immediately and releases the runner. Authenticated `opencode-review-dispatch.yml` wakes the exact failed run via `rerun-failed-jobs` when the formal verdict arrives; no repository-authored polling interval, retry count, or wall-clock deadline bounds model work.\n''' - doctoring.write_text(text, encoding="utf-8") - -changelog = Path("CHANGELOG.md") -text = changelog.read_text(encoding="utf-8") -note = "- Required OpenCode Review now releases its runner after one exact-head verdict admission read and relies on authenticated exact-run dispatch wake instead of repository-authored polling, retry-count, or waiting deadlines.\n" -if note not in text: - changelog.write_text(note + text, encoding="utf-8") diff --git a/scripts/ci/temp_pr1706_one_shot_runner_release_v2.py b/scripts/ci/temp_pr1706_one_shot_runner_release_v2.py deleted file mode 100644 index 79475d8ccc..0000000000 --- a/scripts/ci/temp_pr1706_one_shot_runner_release_v2.py +++ /dev/null @@ -1,361 +0,0 @@ -"""Finish PR #1706 one-shot repair after the first deterministic driver.""" - -from __future__ import annotations - -from pathlib import Path - - -WORKFLOW = Path(".github/workflows/opencode-review.yml") -DISPATCH = Path(".github/workflows/opencode-review-dispatch.yml") -ACCEPTANCE = Path("tests/test_opencode_required_verdict_runner_release.py") -REGRESSION = Path("tests/test_opencode_required_verdict_regression.py") -SELF = Path("tests/test_opencode_poll_self_retirement.py") -LIVE_DRAFT = Path("tests/test_opencode_live_draft_state_regression.py") -ARCHITECTURE = Path("ARCHITECTURE.md") -DOCTORING = Path("docs/doctoring/opencode-stale-poll-self-retirement.md") -CHANGELOG = Path("CHANGELOG.md") - - -def replace_once(text: str, old: str, new: str, label: str) -> str: - """Replace one exact post-driver fragment and fail closed on drift.""" - count = text.count(old) - if count != 1: - raise SystemExit(f"{label} drifted: expected one exact match, found {count}") - return text.replace(old, new, 1) - - -workflow = WORKFLOW.read_text(encoding="utf-8") -workflow = replace_once( - workflow, - ' live_pr="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"\n', - ' if ! live_pr="$(timeout 30s gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"; then\n' - ' echo "::error::Live pull request API read failed during one-shot current-head verdict admission; failing closed and releasing the runner."\n' - ' exit 1\n' - ' fi\n', - "bounded live PR read", -) -workflow = replace_once( - workflow, - ' if ! reviews="$(gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"; then\n', - ' if ! reviews="$(timeout 30s gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"; then\n', - "bounded Reviews read", -) -WORKFLOW.write_text(workflow, encoding="utf-8") - -# pull_request_target run objects expose the PR head in pull_requests[].head.sha; -# their top-level head_sha is the base commit. Replace the whole wake mutation -# boundary so repository + immutable run id + workflow + PR number + PR head -# are revalidated immediately before rerun-failed-jobs. -dispatch = DISPATCH.read_text(encoding="utf-8") -start = " - name: Wake exact-head required OpenCode workflow\n" -end = "\n - name: Publish repository_dispatch OpenCode status\n" -if dispatch.count(start) != 1 or dispatch.count(end) != 1: - raise SystemExit("OpenCode exact-run wake boundaries drifted") -before, rest = dispatch.split(start, 1) -_old_wake, after = rest.split(end, 1) -wake = r''' - name: Wake exact-head required OpenCode workflow - if: >- - always() - && github.event_name == 'repository_dispatch' - && steps.formal_review_receipt.outcome == 'success' - && needs.validate-pr-metadata.outputs.target_repository != '' - && needs.validate-pr-metadata.outputs.pr_number != '' - && needs.validate-pr-metadata.outputs.head_sha != '' - && github.event.client_payload.required_run_id != '' - env: - GH_TOKEN: ${{ needs.validate-pr-metadata.outputs.target_repository == github.repository && github.token || secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN }} - GH_REPOSITORY: ${{ needs.validate-pr-metadata.outputs.target_repository }} - PR_NUMBER: ${{ needs.validate-pr-metadata.outputs.pr_number }} - PR_HEAD_SHA: ${{ needs.validate-pr-metadata.outputs.head_sha }} - REQUIRED_RUN_ID: ${{ github.event.client_payload.required_run_id }} - WAKE_TOKEN_SOURCE: ${{ needs.validate-pr-metadata.outputs.target_repository == github.repository && 'github-token' || secrets.PR_REVIEW_MERGE_TOKEN != '' && 'PR_REVIEW_MERGE_TOKEN' || secrets.OPENCODE_APPROVE_TOKEN != '' && 'OPENCODE_APPROVE_TOKEN' || 'unavailable' }} - run: | - set -euo pipefail - if [ -z "${GH_TOKEN:-}" ] || [ "$WAKE_TOKEN_SOURCE" = "unavailable" ]; then - echo "::error::Actions-capable wake credential is unavailable. Native runs use github.token; sibling runs require PR_REVIEW_MERGE_TOKEN or OPENCODE_APPROVE_TOKEN." - exit 1 - fi - [[ "$REQUIRED_RUN_ID" =~ ^[1-9][0-9]*$ ]] || { - echo "::error::Required OpenCode run id is missing or non-canonical." - exit 1 - } - [[ "$PR_NUMBER" =~ ^[1-9][0-9]*$ ]] || { - echo "::error::Required OpenCode PR number is missing or non-canonical." - exit 1 - } - [[ "$PR_HEAD_SHA" =~ ^[0-9a-fA-F]{40}$ ]] || { - echo "::error::Required OpenCode PR head SHA is missing or malformed." - exit 1 - } - for attempt in $(seq 1 12); do - run="$(timeout 30s gh api "repos/${GH_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}")" - required_run="$(printf '%s\n' "$run" | jq -r --arg head "$PR_HEAD_SHA" --arg pr "$PR_NUMBER" --argjson run_id "$REQUIRED_RUN_ID" ' - select(.id == $run_id) - | select(.event == "pull_request_target") - | select(.path == ".github/workflows/opencode-review.yml") - | select(any((.pull_requests // [])[]?; - ((.number // 0) | tostring) == $pr - and ((.head.sha // "") | ascii_downcase) == ($head | ascii_downcase))) - | [(.id // ""), (.status // ""), (.conclusion // "")] - | @tsv - ')" - IFS=$'\t' read -r required_run_id required_status required_conclusion <<<"$required_run" - if [ "$required_status" = "completed" ] && [ "$required_conclusion" = "failure" ]; then - gh api -X POST "repos/${GH_REPOSITORY}/actions/runs/${required_run_id}/rerun-failed-jobs" >/dev/null - echo "Re-ran failed jobs for exact-PR/head Required OpenCode Review run ${required_run_id}." - exit 0 - fi - if [ "$required_status" = "completed" ] && [ "$required_conclusion" = "success" ]; then - echo "Exact-PR/head Required OpenCode Review run ${required_run_id} already succeeded." - exit 0 - fi - if [ "$attempt" -lt 12 ]; then - sleep 5 - fi - done - echo "::error::Formal OpenCode receipt exists, but the exact-PR/head required workflow did not reach a rerunnable failed state." - exit 1 -''' -DISPATCH.write_text(before + wake + end + after, encoding="utf-8") - -ACCEPTANCE.write_text(r'''"""Regression coverage for releasing the required OpenCode runner while review continues.""" - -from __future__ import annotations - -import json -import os -import shutil -import subprocess -import textwrap -from pathlib import Path - -import pytest - -WORKFLOW = Path(".github/workflows/opencode-review.yml") -DISPATCH_WORKFLOW = Path(".github/workflows/opencode-review-dispatch.yml") -HEAD_SHA = "a" * 40 - - -def _fail_closed_script() -> str: - """Extract only the real required-verdict admission run block.""" - workflow = WORKFLOW.read_text(encoding="utf-8") - step = workflow.split(" - name: Fail closed without a current-head OpenCode verdict\n", 1)[1] - block = step.split(" run: |\n", 1)[1].split("\n cancel-superseded-opencode-review-runs:\n", 1)[0] - assert "cancel-superseded-opencode-review-runs" not in block - return textwrap.dedent(block) - - -def test_missing_verdict_uses_exact_pr_run_wake_instead_of_runner_polling() -> None: - """A missing verdict fails once and relies on authenticated exact-run wake.""" - required = _fail_closed_script() - dispatched = DISPATCH_WORKFLOW.read_text(encoding="utf-8") - for token in ("while :; do", "poll_interval_seconds", "poll_deadline_epoch", "sleep "): - assert token not in required - assert required.count("timeout 30s gh api") == 2 - assert "rerun-failed-jobs" in dispatched - assert "github.event.client_payload.required_run_id != ''" in dispatched - assert "pull_requests // []" in dispatched - assert "(.number // 0) | tostring" in dispatched - assert ".head.sha // \"\"" in dispatched - assert "select(.head_sha == $head)" not in dispatched - - -def _run_admission(tmp_path: Path, reviews: list[dict[str, object]]) -> tuple[subprocess.CompletedProcess[str], list[str]]: - """Execute production admission against deterministic live/review evidence.""" - bash = shutil.which("bash") - jq = shutil.which("jq") - if bash is None or jq is None: - pytest.skip("bash and jq are required") - fake_gh = tmp_path / "gh" - calls = tmp_path / "calls" - fake_gh.write_text( - "#!/usr/bin/env bash\nset -euo pipefail\nprintf '%s\\n' \"$*\" >>\"$CALLS\"\n" - "if [[ \"$*\" == \"api repos/ContextualWisdomLab/example/pulls/42\" ]]; then printf '%s\\n' \"$LIVE_PR\"; exit 0; fi\n" - "if [[ \"$*\" == \"api --paginate repos/ContextualWisdomLab/example/pulls/42/reviews?per_page=100\" ]]; then printf '%s\\n' \"$REVIEWS\"; exit 0; fi\nexit 97\n", - encoding="utf-8", - ) - fake_gh.chmod(0o755) - fake_timeout = tmp_path / "timeout" - fake_timeout.write_text("#!/usr/bin/env bash\nset -euo pipefail\nshift\nexec \"$@\"\n", encoding="utf-8") - fake_timeout.chmod(0o755) - fake_sleep = tmp_path / "sleep" - fake_sleep.write_text("#!/usr/bin/env bash\necho unexpected-sleep >&2\nexit 91\n", encoding="utf-8") - fake_sleep.chmod(0o755) - result = subprocess.run( - [bash, "-c", _fail_closed_script()], - env={ - **os.environ, - "PATH": f"{tmp_path}{os.pathsep}{os.environ.get('PATH', '')}", - "CALLS": str(calls), - "LIVE_PR": json.dumps({"head": {"sha": HEAD_SHA}, "draft": False, "state": "open"}), - "REVIEWS": json.dumps(reviews), - "GH_TOKEN": "test-token", - "TARGET_REPOSITORY": "ContextualWisdomLab/example", - "PR_NUMBER": "42", - "HEAD_SHA": HEAD_SHA, - "PR_ACTION": "synchronize", - "PR_DRAFT": "false", - }, - text=True, - capture_output=True, - check=False, - ) - return result, calls.read_text(encoding="utf-8").splitlines() - - -def test_missing_verdict_fails_after_one_live_and_one_review_read(tmp_path: Path) -> None: - """No verdict releases the runner immediately with exactly two API reads.""" - result, calls = _run_admission(tmp_path, []) - assert result.returncode == 1, result.stderr - assert "unexpected-sleep" not in result.stderr - assert "No APPROVED or CHANGES_REQUESTED from opencode-agent" in result.stdout - assert calls == [ - "api repos/ContextualWisdomLab/example/pulls/42", - "api --paginate repos/ContextualWisdomLab/example/pulls/42/reviews?per_page=100", - ] - - -def test_formal_verdict_finishes_before_sibling_job_yaml(tmp_path: Path) -> None: - """Successful admission executes only the target shell block.""" - result, calls = _run_admission( - tmp_path, - [{"user": {"login": "opencode-agent[bot]"}, "commit_id": HEAD_SHA, "state": "APPROVED", "body": "Source-backed review."}], - ) - assert result.returncode == 0, result.stderr - assert "Current-head OpenCode verdict: APPROVED." in result.stdout - assert len(calls) == 2 -''', encoding="utf-8") - -SELF.write_text(r'''"""Regression contract for one-shot Required OpenCode verdict admission.""" - -from pathlib import Path - -WORKFLOW = Path(".github/workflows/opencode-review.yml") - - -def _step() -> str: - """Return only the one-shot verdict-admission step.""" - workflow = WORKFLOW.read_text(encoding="utf-8") - return workflow.split(" - name: Fail closed without a current-head OpenCode verdict\n", 1)[1].split("\n cancel-superseded-opencode-review-runs:\n", 1)[0] - - -def test_one_shot_revalidates_live_state_before_reviews() -> None: - """Current authority is established before formal review evidence is read.""" - step = _step() - live = 'timeout 30s gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}"' - reviews = 'timeout 30s gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100"' - assert live in step - assert reviews in step - assert step.index(live) < step.index(reviews) - - -def test_stale_terminal_or_malformed_authority_cannot_reach_review_read_first() -> None: - """Closed, draft, moved-head, and malformed live evidence have explicit branches.""" - step = _step() - assert 'if [ "$live_state" = "closed" ]; then' in step - assert 'if [ "$live_draft" = "true" ]; then' in step - assert 'if [ "${live_head,,}" != "${HEAD_SHA,,}" ]; then' in step - assert "Could not validate live pull request state before verdict admission" in step - assert "fresh required-review run will bind the current head" in step - - -def test_transport_reads_are_bounded_but_model_wait_is_not() -> None: - """GitHub transport gets a bound; semantic model reasoning gets no deadline.""" - step = _step() - assert step.count("timeout 30s gh api") == 2 - for token in ("while :; do", "poll_interval_seconds", "poll_deadline_epoch", "max_poll_transport_failures", "sleep "): - assert token not in step - - -def test_missing_or_unavailable_review_evidence_fails_closed_once() -> None: - """No retry loop can fabricate a verdict or retain the runner.""" - step = _step() - assert "Reviews API read failed during one-shot current-head verdict admission" in step - assert "No APPROVED or CHANGES_REQUESTED from opencode-agent" in step -''', encoding="utf-8") - -regression = REGRESSION.read_text(encoding="utf-8") -regression = replace_once( - regression, - ' return textwrap.dedent(step.split(" run: |\\n", 1)[1])\n', - ' block = step.split(" run: |\\n", 1)[1].split("\\n cancel-superseded-opencode-review-runs:\\n", 1)[0]\n return textwrap.dedent(block)\n', - "bounded verdict test extractor", -) -regression = replace_once( - regression, - ' assert "select(.head_sha == $head)" in dispatched\n', - ' assert "pull_requests // []" in dispatched\n assert "(.number // 0) | tostring" in dispatched\n assert "select(.head_sha == $head)" not in dispatched\n', - "wake identity assertion", -) -old_fixture = '''def required_run(*, run_id: int = 42, head_sha: str = HEAD, path: str = ".github/workflows/opencode-review.yml") -> dict[str, object]: - """Build one realistic single-run GET REST API record. - - Mirrors the real shape a sibling repo sees for a run injected by the org's - required-workflow ruleset (this repo's actual central-hub use case): `name` - is the bare workflow name and `display_title` is a plain PR title, with no - PR number or head SHA embedded in either -- unlike a native same-repo - trigger, where both fields carry the rendered `run-name`. - """ - return { - "id": run_id, - "head_sha": head_sha, - "event": "pull_request_target", - "name": "Required OpenCode Review", - "display_title": "Fix an unrelated example bug", - "path": path, - "workflow_url": ( - "https://api.github.com/repos/ContextualWisdomLab/example" - "/actions/required_workflows/9" - ), - "status": "completed", - "conclusion": "failure", - } -''' -new_fixture = '''def required_run(*, run_id: int = 42, pr_head_sha: str = HEAD, pr_number: int = 1437, path: str = ".github/workflows/opencode-review.yml") -> dict[str, object]: - """Build a pull_request_target run whose top-level head_sha is the base SHA.""" - return { - "id": run_id, - "head_sha": "f" * 40, - "event": "pull_request_target", - "name": "Required OpenCode Review", - "display_title": "Fix an unrelated example bug", - "path": path, - "workflow_url": ( - "https://api.github.com/repos/ContextualWisdomLab/example" - "/actions/required_workflows/9" - ), - "pull_requests": [{"number": pr_number, "head": {"sha": pr_head_sha}}], - "status": "completed", - "conclusion": "failure", - } -''' -regression = replace_once(regression, old_fixture, new_fixture, "pull_request_target run fixture") -regression = replace_once(regression, 'required_run(head_sha="b" * 40)', 'required_run(pr_head_sha="b" * 40)', "mismatched PR-head fixture") -regression = replace_once( - regression, - 'def test_wake_selector_rejects_a_referenced_run_for_a_different_workflow() -> None:\n', - 'def test_wake_selector_rejects_a_referenced_run_for_a_different_pr() -> None:\n """A run id for another PR cannot receive the wake mutation."""\n assert wake_selector(required_run(pr_number=9999)) == ""\n\n\ndef test_wake_selector_rejects_a_referenced_run_for_a_different_workflow() -> None:\n', - "wrong PR wake regression", -) -REGRESSION.write_text(regression, encoding="utf-8") - -live_draft = LIVE_DRAFT.read_text(encoding="utf-8") -live_draft = live_draft.replace("Reviews API read failed 3 consecutive times", "Reviews API read failed during one-shot current-head verdict admission") -LIVE_DRAFT.write_text(live_draft, encoding="utf-8") - -architecture = ARCHITECTURE.read_text(encoding="utf-8") -marker = "### Required OpenCode one-shot verdict admission" -if marker not in architecture: - architecture += '''\n\n### Required OpenCode one-shot verdict admission\n\nThe protected required workflow does not retain a runner while contextual-orchestrator performs semantic review. It validates live PR state once, reads formal review evidence once, and fails closed immediately when no exact-head verdict exists. The authenticated default-branch dispatch later revalidates repository, immutable run id, central workflow path, PR number, and `pull_requests[].head.sha` before `rerun-failed-jobs`; `pull_request_target` top-level `head_sha` is the base commit and is not PR-head authority. GitHub API transport reads are bounded independently from model reasoning, which has no caller wall-clock deadline.\n''' - ARCHITECTURE.write_text(architecture, encoding="utf-8") - -doctoring = DOCTORING.read_text(encoding="utf-8") -marker = "### Exact-run wake identity correction" -if marker not in doctoring: - doctoring += '''\n\n### Exact-run wake identity correction\n\nFor `pull_request_target`, the workflow-run REST object's top-level `head_sha` identifies the base revision. Exact PR-head wake authority therefore uses the immutable run id plus repository API path, event, central workflow path, exact PR number, and `pull_requests[].head.sha`. The dispatcher performs this validation immediately before `rerun-failed-jobs`; mismatched or missing PR metadata fails closed.\n''' - DOCTORING.write_text(doctoring, encoding="utf-8") - -changelog = CHANGELOG.read_text(encoding="utf-8") -note = "- Required OpenCode Review exact-run wake now validates PR number plus `pull_requests[].head.sha` before `rerun-failed-jobs`, because `pull_request_target` workflow-run `head_sha` is the base commit; one-shot GitHub API reads retain 30-second transport bounds without imposing a semantic-review timeout.\n" -if note not in changelog: - CHANGELOG.write_text(note + changelog, encoding="utf-8") diff --git a/scripts/ci/temp_pr1706_one_shot_runner_release_v3.py b/scripts/ci/temp_pr1706_one_shot_runner_release_v3.py deleted file mode 100644 index 51ddee2ac6..0000000000 --- a/scripts/ci/temp_pr1706_one_shot_runner_release_v3.py +++ /dev/null @@ -1,356 +0,0 @@ -"""Finish PR #1706 exact-run wake repair after the first deterministic driver. - -Temporary helper: the source-fix workflow deletes this file in the successful -publication commit together with the other PR #1706 repair machinery. -""" - -from __future__ import annotations - -from pathlib import Path - - -WORKFLOW = Path(".github/workflows/opencode-review.yml") -DISPATCH = Path(".github/workflows/opencode-review-dispatch.yml") -ACCEPTANCE = Path("tests/test_opencode_required_verdict_runner_release.py") -REGRESSION = Path("tests/test_opencode_required_verdict_regression.py") -SELF = Path("tests/test_opencode_poll_self_retirement.py") -LIVE_DRAFT = Path("tests/test_opencode_live_draft_state_regression.py") -ARCHITECTURE = Path("ARCHITECTURE.md") -DOCTORING = Path("docs/doctoring/opencode-stale-poll-self-retirement.md") -CHANGELOG = Path("CHANGELOG.md") - - -def replace_once(text: str, old: str, new: str, label: str) -> str: - """Replace one exact fragment and fail closed when concurrent edits drift.""" - count = text.count(old) - if count != 1: - raise SystemExit(f"{label} drifted: expected one exact match, found {count}") - return text.replace(old, new, 1) - - -workflow = WORKFLOW.read_text(encoding="utf-8") -workflow = replace_once( - workflow, - ' live_pr="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"\n', - ' if ! live_pr="$(timeout 30s gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"; then\n' - ' echo "::error::Live pull request API read failed during one-shot current-head verdict admission; failing closed and releasing the runner."\n' - ' exit 1\n' - ' fi\n', - "bounded live PR read", -) -workflow = replace_once( - workflow, - ' if ! reviews="$(gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"; then\n', - ' if ! reviews="$(timeout 30s gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"; then\n', - "bounded Reviews read", -) -WORKFLOW.write_text(workflow, encoding="utf-8") - -dispatch = DISPATCH.read_text(encoding="utf-8") -start = " - name: Wake exact-head required OpenCode workflow\n" -end = "\n - name: Publish repository_dispatch OpenCode status\n" -if dispatch.count(start) != 1 or dispatch.count(end) != 1: - raise SystemExit("OpenCode exact-run wake boundaries drifted") -before, rest = dispatch.split(start, 1) -_old_wake, after = rest.split(end, 1) -wake = r''' - name: Wake exact-head required OpenCode workflow - if: >- - always() - && github.event_name == 'repository_dispatch' - && steps.formal_review_receipt.outcome == 'success' - && needs.validate-pr-metadata.outputs.target_repository != '' - && needs.validate-pr-metadata.outputs.pr_number != '' - && needs.validate-pr-metadata.outputs.head_sha != '' - && github.event.client_payload.required_run_id != '' - env: - GH_TOKEN: ${{ needs.validate-pr-metadata.outputs.target_repository == github.repository && github.token || secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN }} - GH_REPOSITORY: ${{ needs.validate-pr-metadata.outputs.target_repository }} - PR_NUMBER: ${{ needs.validate-pr-metadata.outputs.pr_number }} - PR_HEAD_SHA: ${{ needs.validate-pr-metadata.outputs.head_sha }} - REQUIRED_RUN_ID: ${{ github.event.client_payload.required_run_id }} - WAKE_TOKEN_SOURCE: ${{ needs.validate-pr-metadata.outputs.target_repository == github.repository && 'github-token' || secrets.PR_REVIEW_MERGE_TOKEN != '' && 'PR_REVIEW_MERGE_TOKEN' || secrets.OPENCODE_APPROVE_TOKEN != '' && 'OPENCODE_APPROVE_TOKEN' || 'unavailable' }} - run: | - set -euo pipefail - if [ -z "${GH_TOKEN:-}" ] || [ "$WAKE_TOKEN_SOURCE" = "unavailable" ]; then - echo "::error::Actions-capable wake credential is unavailable. Native runs use github.token; sibling runs require PR_REVIEW_MERGE_TOKEN or OPENCODE_APPROVE_TOKEN." - exit 1 - fi - [[ "$REQUIRED_RUN_ID" =~ ^[1-9][0-9]*$ ]] || { echo "::error::Required OpenCode run id is missing or non-canonical."; exit 1; } - [[ "$PR_NUMBER" =~ ^[1-9][0-9]*$ ]] || { echo "::error::Required OpenCode PR number is missing or non-canonical."; exit 1; } - [[ "$PR_HEAD_SHA" =~ ^[0-9a-fA-F]{40}$ ]] || { echo "::error::Required OpenCode PR head SHA is missing or malformed."; exit 1; } - for attempt in $(seq 1 12); do - if ! run="$(timeout 30s gh api "repos/${GH_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}")"; then - echo "::warning::Exact required-run lookup attempt ${attempt}/12 failed; retrying before wake." - if [ "$attempt" -lt 12 ]; then sleep 5; continue; fi - break - fi - required_run="$(printf '%s\n' "$run" | jq -r --arg head "$PR_HEAD_SHA" --arg pr "$PR_NUMBER" --argjson run_id "$REQUIRED_RUN_ID" ' - select(.id == $run_id) - | select(.event == "pull_request_target") - | select(.path == ".github/workflows/opencode-review.yml") - | select(any((.pull_requests // [])[]?; ((.number // 0) | tostring) == $pr and ((.head.sha // "") | ascii_downcase) == ($head | ascii_downcase))) - | [(.id // ""), (.status // ""), (.conclusion // "")] - | @tsv - ')" || required_run="" - if [ -z "$required_run" ]; then - echo "::warning::Exact required-run lookup attempt ${attempt}/12 returned malformed or nonmatching evidence; retrying before wake." - if [ "$attempt" -lt 12 ]; then sleep 5; continue; fi - break - fi - IFS=$'\t' read -r required_run_id required_status required_conclusion <<<"$required_run" - if [ "$required_status" = "completed" ] && [ "$required_conclusion" = "failure" ]; then - gh api -X POST "repos/${GH_REPOSITORY}/actions/runs/${required_run_id}/rerun-failed-jobs" >/dev/null - echo "Re-ran failed jobs for exact-PR/head Required OpenCode Review run ${required_run_id}." - exit 0 - fi - if [ "$required_status" = "completed" ] && [ "$required_conclusion" = "success" ]; then - echo "Exact-PR/head Required OpenCode Review run ${required_run_id} already succeeded." - exit 0 - fi - if [ "$attempt" -lt 12 ]; then sleep 5; fi - done - echo "::error::Formal OpenCode receipt exists, but the exact-PR/head required workflow did not reach a rerunnable failed state." - exit 1 -''' -DISPATCH.write_text(before + wake + end + after, encoding="utf-8") - -ACCEPTANCE.write_text(r'''"""Regression coverage for releasing the required OpenCode runner while review continues.""" - -from __future__ import annotations - -import json -import os -import shutil -import subprocess -import textwrap -from pathlib import Path - -import pytest - -WORKFLOW = Path(".github/workflows/opencode-review.yml") -DISPATCH_WORKFLOW = Path(".github/workflows/opencode-review-dispatch.yml") -HEAD_SHA = "a" * 40 - - -def _fail_closed_script() -> str: - """Extract only the real required-verdict admission run block.""" - workflow = WORKFLOW.read_text(encoding="utf-8") - step = workflow.split(" - name: Fail closed without a current-head OpenCode verdict\n", 1)[1] - block = step.split(" run: |\n", 1)[1].split("\n cancel-superseded-opencode-review-runs:\n", 1)[0] - return textwrap.dedent(block) - - -def _wake_script() -> str: - """Extract only the exact-run wake shell body.""" - workflow = DISPATCH_WORKFLOW.read_text(encoding="utf-8") - step = workflow.split(" - name: Wake exact-head required OpenCode workflow\n", 1)[1] - block = step.split(" run: |\n", 1)[1].split("\n - name: Publish repository_dispatch OpenCode status\n", 1)[0] - return textwrap.dedent(block) - - -def test_missing_verdict_uses_exact_pr_run_wake_instead_of_runner_polling() -> None: - """A missing verdict fails once and relies on authenticated exact-run wake.""" - required = _fail_closed_script() - dispatched = DISPATCH_WORKFLOW.read_text(encoding="utf-8") - for token in ("while :; do", "poll_interval_seconds", "poll_deadline_epoch", "sleep "): - assert token not in required - assert required.count("timeout 30s gh api") == 2 - assert "rerun-failed-jobs" in dispatched - assert "pull_requests // []" in dispatched - assert "(.number // 0) | tostring" in dispatched - assert 'if ! run="$(timeout 30s gh api ' in dispatched - - -def _run_admission(tmp_path: Path, reviews: list[dict[str, object]]) -> tuple[subprocess.CompletedProcess[str], list[str]]: - """Execute production admission against deterministic live/review evidence.""" - bash = shutil.which("bash") - jq = shutil.which("jq") - if bash is None or jq is None: - pytest.skip("bash and jq are required") - fake_gh = tmp_path / "gh" - calls = tmp_path / "calls" - fake_gh.write_text( - "#!/usr/bin/env bash\nset -euo pipefail\nprintf '%s\\n' \"$*\" >>\"$CALLS\"\n" - "if [[ \"$*\" == \"api repos/ContextualWisdomLab/example/pulls/42\" ]]; then printf '%s\\n' \"$LIVE_PR\"; exit 0; fi\n" - "if [[ \"$*\" == \"api --paginate repos/ContextualWisdomLab/example/pulls/42/reviews?per_page=100\" ]]; then printf '%s\\n' \"$REVIEWS\"; exit 0; fi\nexit 97\n", - encoding="utf-8", - ) - fake_gh.chmod(0o755) - fake_timeout = tmp_path / "timeout" - fake_timeout.write_text("#!/usr/bin/env bash\nset -euo pipefail\nshift\nexec \"$@\"\n", encoding="utf-8") - fake_timeout.chmod(0o755) - result = subprocess.run( - [bash, "-c", _fail_closed_script()], - env={**os.environ, "PATH": f"{tmp_path}{os.pathsep}{os.environ.get('PATH', '')}", "CALLS": str(calls), "LIVE_PR": json.dumps({"head": {"sha": HEAD_SHA}, "draft": False, "state": "open"}), "REVIEWS": json.dumps(reviews), "GH_TOKEN": "test-token", "TARGET_REPOSITORY": "ContextualWisdomLab/example", "PR_NUMBER": "42", "HEAD_SHA": HEAD_SHA, "PR_ACTION": "synchronize", "PR_DRAFT": "false"}, - text=True, capture_output=True, check=False, - ) - return result, calls.read_text(encoding="utf-8").splitlines() - - -def test_missing_verdict_fails_after_one_live_and_one_review_read(tmp_path: Path) -> None: - """No verdict releases the runner immediately with exactly two API reads.""" - result, calls = _run_admission(tmp_path, []) - assert result.returncode == 1, result.stderr - assert "No APPROVED or CHANGES_REQUESTED from opencode-agent" in result.stdout - assert len(calls) == 2 - - -def test_exact_run_wake_retries_transient_lookup_then_reruns_failed_job(tmp_path: Path) -> None: - """A transient run lookup cannot strand an already-posted exact-head verdict.""" - bash = shutil.which("bash") - jq = shutil.which("jq") - if bash is None or jq is None: - pytest.skip("bash and jq are required") - calls = tmp_path / "wake-calls" - attempts = tmp_path / "wake-attempts" - fake_gh = tmp_path / "gh" - fake_gh.write_text( - "#!/usr/bin/env bash\nset -euo pipefail\nprintf '%s\\n' \"$*\" >>\"$CALLS\"\n" - "if [[ \"$*\" == \"api repos/ContextualWisdomLab/example/actions/runs/42\" ]]; then n=0; [[ -f \"$ATTEMPTS\" ]] && n=$(cat \"$ATTEMPTS\"); n=$((n+1)); printf '%s' \"$n\" >\"$ATTEMPTS\"; if [[ \"$n\" -eq 1 ]]; then exit 75; fi; printf '%s\\n' \"$RUN_JSON\"; exit 0; fi\n" - "if [[ \"$*\" == \"api -X POST repos/ContextualWisdomLab/example/actions/runs/42/rerun-failed-jobs\" ]]; then exit 0; fi\nexit 97\n", - encoding="utf-8", - ) - fake_gh.chmod(0o755) - fake_timeout = tmp_path / "timeout" - fake_timeout.write_text("#!/usr/bin/env bash\nset -euo pipefail\nshift\nexec \"$@\"\n", encoding="utf-8") - fake_timeout.chmod(0o755) - fake_sleep = tmp_path / "sleep" - fake_sleep.write_text("#!/usr/bin/env bash\nexit 0\n", encoding="utf-8") - fake_sleep.chmod(0o755) - run_json = {"id": 42, "event": "pull_request_target", "path": ".github/workflows/opencode-review.yml", "pull_requests": [{"number": 1437, "head": {"sha": HEAD_SHA}}], "status": "completed", "conclusion": "failure"} - result = subprocess.run( - [bash, "-c", _wake_script()], - env={**os.environ, "PATH": f"{tmp_path}{os.pathsep}{os.environ.get('PATH', '')}", "CALLS": str(calls), "ATTEMPTS": str(attempts), "RUN_JSON": json.dumps(run_json), "GH_TOKEN": "test-token", "GH_REPOSITORY": "ContextualWisdomLab/example", "PR_NUMBER": "1437", "PR_HEAD_SHA": HEAD_SHA, "REQUIRED_RUN_ID": "42", "WAKE_TOKEN_SOURCE": "github-token"}, - text=True, capture_output=True, check=False, - ) - assert result.returncode == 0, result.stderr - assert "Exact required-run lookup attempt 1/12 failed" in result.stdout - assert "Re-ran failed jobs for exact-PR/head Required OpenCode Review run 42." in result.stdout - assert calls.read_text(encoding="utf-8").splitlines() == ["api repos/ContextualWisdomLab/example/actions/runs/42", "api repos/ContextualWisdomLab/example/actions/runs/42", "api -X POST repos/ContextualWisdomLab/example/actions/runs/42/rerun-failed-jobs"] -''', encoding="utf-8") - -SELF.write_text(r'''"""Regression contract for one-shot Required OpenCode verdict admission.""" -from pathlib import Path -WORKFLOW = Path(".github/workflows/opencode-review.yml") - -def _step() -> str: - """Return only the one-shot verdict-admission step.""" - workflow = WORKFLOW.read_text(encoding="utf-8") - return workflow.split(" - name: Fail closed without a current-head OpenCode verdict\n", 1)[1].split("\n cancel-superseded-opencode-review-runs:\n", 1)[0] - -def test_one_shot_revalidates_live_state_before_reviews() -> None: - """Current authority is established before formal review evidence is read.""" - step = _step() - live = 'timeout 30s gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}"' - reviews = 'timeout 30s gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100"' - assert live in step and reviews in step and step.index(live) < step.index(reviews) - -def test_transport_reads_are_bounded_but_model_wait_is_not() -> None: - """GitHub transport gets a bound; semantic model reasoning gets no deadline.""" - step = _step() - assert step.count("timeout 30s gh api") == 2 - for token in ("while :; do", "poll_interval_seconds", "poll_deadline_epoch", "max_poll_transport_failures", "sleep "): - assert token not in step -''', encoding="utf-8") - -regression = REGRESSION.read_text(encoding="utf-8") -regression = regression.replace(' return textwrap.dedent(step.split(" run: |\\n", 1)[1])\n', ' block = step.split(" run: |\\n", 1)[1].split("\\n cancel-superseded-opencode-review-runs:\\n", 1)[0]\n return textwrap.dedent(block)\n', 1) -regression = regression.replace(' assert "select(.head_sha == $head)" in dispatched\n', ' assert "pull_requests // []" in dispatched\n assert "(.number // 0) | tostring" in dispatched\n assert "select(.head_sha == $head)" not in dispatched\n', 1) -old_selector = '''def wake_selector(run: dict[str, object], *, head: str = HEAD, run_id: int = 42) -> str: - """Execute the wake step's run-validation jq program in isolation.""" - jq = shutil.which("jq") - if jq is None: - pytest.skip("jq is required to execute the production wake selector") - dispatched = DISPATCH_WORKFLOW.read_text(encoding="utf-8") - marker = """jq -r --arg head "$PR_HEAD_SHA" --argjson run_id "$REQUIRED_RUN_ID" '""" - start = dispatched.index(marker) + len(marker) - end = dispatched.index("\\n ')", start) - result = subprocess.run( - [jq, "-r", "--arg", "head", head, "--argjson", "run_id", str(run_id), dispatched[start:end]], - input=json.dumps(run), - text=True, - capture_output=True, - check=False, - ) - assert result.returncode == 0, result.stderr - return result.stdout.strip() -''' -new_selector = '''def wake_selector(run: dict[str, object], *, head: str = HEAD, pr: int = 1437, run_id: int = 42) -> str: - """Execute the wake step's exact run/PR/head validation jq program.""" - jq = shutil.which("jq") - if jq is None: - pytest.skip("jq is required to execute the production wake selector") - dispatched = DISPATCH_WORKFLOW.read_text(encoding="utf-8") - marker = """jq -r --arg head "$PR_HEAD_SHA" --arg pr "$PR_NUMBER" --argjson run_id "$REQUIRED_RUN_ID" '""" - start = dispatched.index(marker) + len(marker) - end = dispatched.index("\\n ')", start) - result = subprocess.run( - [jq, "-r", "--arg", "head", head, "--arg", "pr", str(pr), "--argjson", "run_id", str(run_id), dispatched[start:end]], - input=json.dumps(run), text=True, capture_output=True, check=False, - ) - assert result.returncode == 0, result.stderr - return result.stdout.strip() -''' -regression = replace_once(regression, old_selector, new_selector, "wake selector fixture") -old_fixture = '''def required_run(*, run_id: int = 42, head_sha: str = HEAD, path: str = ".github/workflows/opencode-review.yml") -> dict[str, object]: - """Build one realistic single-run GET REST API record. - - Mirrors the real shape a sibling repo sees for a run injected by the org's - required-workflow ruleset (this repo's actual central-hub use case): `name` - is the bare workflow name and `display_title` is a plain PR title, with no - PR number or head SHA embedded in either -- unlike a native same-repo - trigger, where both fields carry the rendered `run-name`. - """ - return { - "id": run_id, - "head_sha": head_sha, - "event": "pull_request_target", - "name": "Required OpenCode Review", - "display_title": "Fix an unrelated example bug", - "path": path, - "workflow_url": ( - "https://api.github.com/repos/ContextualWisdomLab/example" - "/actions/required_workflows/9" - ), - "status": "completed", - "conclusion": "failure", - } -''' -new_fixture = '''def required_run(*, run_id: int = 42, pr_head_sha: str = HEAD, pr_number: int = 1437, path: str = ".github/workflows/opencode-review.yml") -> dict[str, object]: - """Build a pull_request_target run whose top-level head_sha is the base SHA.""" - return { - "id": run_id, - "head_sha": "f" * 40, - "event": "pull_request_target", - "name": "Required OpenCode Review", - "display_title": "Fix an unrelated example bug", - "path": path, - "workflow_url": "https://api.github.com/repos/ContextualWisdomLab/example/actions/required_workflows/9", - "pull_requests": [{"number": pr_number, "head": {"sha": pr_head_sha}}], - "status": "completed", - "conclusion": "failure", - } -''' -regression = replace_once(regression, old_fixture, new_fixture, "pull_request_target run fixture") -regression = regression.replace('required_run(head_sha="b" * 40)', 'required_run(pr_head_sha="b" * 40)', 1) -wrong_workflow = 'def test_wake_selector_rejects_a_referenced_run_for_a_different_workflow() -> None:\n' -if 'def test_wake_selector_rejects_a_referenced_run_for_a_different_pr()' not in regression: - regression = replace_once(regression, wrong_workflow, 'def test_wake_selector_rejects_a_referenced_run_for_a_different_pr() -> None:\n """A run id for another PR cannot receive the wake mutation."""\n assert wake_selector(required_run(pr_number=9999)) == ""\n\n\n' + wrong_workflow, "wrong PR wake regression") -regression = replace_once(regression, ' "PR_HEAD_SHA": HEAD,\n "REQUIRED_RUN_ID": "42",\n', ' "PR_NUMBER": "1437",\n "PR_HEAD_SHA": HEAD,\n "REQUIRED_RUN_ID": "42",\n', "formal wake PR identity fixture") -REGRESSION.write_text(regression, encoding="utf-8") - -live_draft = LIVE_DRAFT.read_text(encoding="utf-8").replace("Reviews API read failed 3 consecutive times", "Reviews API read failed during one-shot current-head verdict admission") -LIVE_DRAFT.write_text(live_draft, encoding="utf-8") - -architecture = ARCHITECTURE.read_text(encoding="utf-8") -if "### Required OpenCode one-shot verdict admission" not in architecture: - ARCHITECTURE.write_text(architecture + "\n\n### Required OpenCode one-shot verdict admission\n\nThe protected required workflow validates live PR state once, reads formal review evidence once, and releases its runner immediately when no exact-head verdict exists. The authenticated default-branch dispatch revalidates repository, immutable run id, workflow path, PR number, and `pull_requests[].head.sha` before `rerun-failed-jobs`. A bounded retry absorbs transient GitHub run-lookup failures; model reasoning itself has no caller wall-clock deadline.\n", encoding="utf-8") - -doctoring = DOCTORING.read_text(encoding="utf-8") -if "### Exact-run wake identity and transient lookup correction" not in doctoring: - DOCTORING.write_text(doctoring + "\n\n### Exact-run wake identity and transient lookup correction\n\nFor `pull_request_target`, top-level workflow-run `head_sha` identifies the base revision, not the PR head. Wake authority binds immutable run id, event, workflow path, exact PR number, and `pull_requests[].head.sha`. Transient run-lookup failures are retried within a bounded transport loop so a posted formal verdict cannot be stranded by one GitHub API timeout.\n", encoding="utf-8") - -changelog = CHANGELOG.read_text(encoding="utf-8") -note = "- Required OpenCode Review now releases its runner after one exact-head verdict admission read; exact-run wake binds PR number plus `pull_requests[].head.sha` and tolerates bounded transient GitHub run-lookup failures without imposing a model reasoning timeout.\n" -if note not in changelog: - CHANGELOG.write_text(note + changelog, encoding="utf-8") diff --git a/tests/test_opencode_event_driven_required_wake.py b/tests/test_opencode_event_driven_required_wake.py index 8f12ff0f19..c1d1c82a06 100644 --- a/tests/test_opencode_event_driven_required_wake.py +++ b/tests/test_opencode_event_driven_required_wake.py @@ -2,65 +2,48 @@ from pathlib import Path - REQUIRED = Path(".github/workflows/opencode-review.yml") DISPATCH = Path(".github/workflows/opencode-review-dispatch.yml") SCHEDULER = Path(".github/workflows/pr-review-merge-scheduler.yml") -def _required_verdict_step() -> str: - """Return only the current-head formal-verdict admission step.""" - workflow = REQUIRED.read_text(encoding="utf-8") - return workflow.split( - " - name: Fail closed without a current-head OpenCode verdict\n", 1 - )[1].split("\n cancel-superseded-opencode-review-runs:\n", 1)[0] +def _required() -> str: + """Return only current-head formal-verdict admission.""" + text = REQUIRED.read_text(encoding="utf-8") + return text.split(" - name: Fail closed without a current-head OpenCode verdict\n", 1)[1].split("\n cancel-superseded-opencode-review-runs:\n", 1)[0] -def _dispatch_wake_step() -> str: - """Return only the exact-run wake step in the privileged dispatch.""" - workflow = DISPATCH.read_text(encoding="utf-8") - return workflow.split( - " - name: Wake exact-head required OpenCode workflow\n", 1 - )[1].split("\n - name: Publish repository_dispatch OpenCode status\n", 1)[0] +def _wake() -> str: + """Return only authenticated formal-receipt wake.""" + text = DISPATCH.read_text(encoding="utf-8") + return text.split(" - name: Wake exact-head required OpenCode workflow\n", 1)[1].split("\n - name: Publish repository_dispatch OpenCode status\n", 1)[0] def test_required_verdict_admission_has_no_repository_authored_wait_allocation() -> None: - """A missing verdict fails closed after authoritative state reads, not elapsed time.""" - step = _required_verdict_step() - for token in ( - "while :; do", - "poll_interval_seconds", - "poll_deadline_epoch", - "max_poll_transport_failures", - "sleep ", - "timeout ", - ): + """Missing verdict fails closed after authoritative reads, not elapsed time.""" + step = _required() + for token in ("while :; do", "poll_interval_seconds", "poll_deadline_epoch", "max_poll_transport_failures", "sleep ", "timeout "): assert token not in step -def test_dispatch_wake_has_no_fixed_retry_delay_or_transport_deadline() -> None: - """The receipt path performs one exact-state transition without a retry budget.""" - step = _dispatch_wake_step() - for token in ("for attempt", "seq 1", "sleep ", "timeout ", "/12"): +def test_dispatch_receipt_wake_is_one_exact_state_transition() -> None: + """A formal receipt never introduces a retry, sleep, transport, or review-read loop.""" + step = _wake() + for token in ("for attempt", "seq 1", "sleep ", "timeout ", "/12", "--paginate"): assert token not in step - assert "run_started_at" in step - assert "submitted_at" in step + assert "pull_requests // []" in step assert "rerun-failed-jobs" in step -def test_workflow_run_completion_reconciles_new_formal_review_evidence() -> None: - """GitHub's completed-workflow event closes the review-before-failure race.""" +def test_workflow_run_completion_closes_review_before_failure_race() -> None: + """Failed completion reruns only when newer formal exact-head evidence exists.""" scheduler = SCHEDULER.read_text(encoding="utf-8") - assert 'workflows: ["Required OpenCode Review", "Strix Security Scan"]' in scheduler - assert "reconcile-opencode-required-verdict:" in scheduler - reconciliation = scheduler.split(" reconcile-opencode-required-verdict:\n", 1)[1].split( - "\n ", 1 - )[0] - assert "github.event_name == 'workflow_run'" in reconciliation - assert "github.event.workflow_run.name == 'Required OpenCode Review'" in reconciliation - assert "github.event.workflow_run.conclusion == 'failure'" in reconciliation - assert "github.event.workflow_run.run_started_at" in reconciliation - assert "submitted_at" in reconciliation - assert "rerun-failed-jobs" in reconciliation + job = scheduler.split(" reconcile-opencode-required-verdict:\n", 1)[1].split("\n scan-pr-queue:\n", 1)[0] + assert "github.event_name == 'workflow_run'" in job + assert "github.event.workflow_run.name == 'Required OpenCode Review'" in job + assert "github.event.workflow_run.conclusion == 'failure'" in job + assert "github.event.workflow_run.run_started_at" in job + assert "review_submitted_at" in job and "fromdateiso8601" in job + assert "rerun-failed-jobs" in job for token in ("for attempt", "while :; do", "sleep ", "timeout "): - assert token not in reconciliation + assert token not in job diff --git a/tests/test_opencode_live_draft_state_regression.py b/tests/test_opencode_live_draft_state_regression.py index df45cb0d8b..0572776a4a 100644 --- a/tests/test_opencode_live_draft_state_regression.py +++ b/tests/test_opencode_live_draft_state_regression.py @@ -161,7 +161,7 @@ def test_stale_draft_verdict_event_does_not_exempt_live_ready_pr( assert result.returncode == 1 assert "Event draft snapshot is stale" in result.stdout - assert "Reviews API read failed 3 consecutive times" in result.stdout + assert "Reviews API read failed during one-shot current-head verdict admission" in result.stdout @pytest.mark.parametrize("script", (request_review_script(), fail_closed_script())) diff --git a/tests/test_opencode_poll_rate_budget.py b/tests/test_opencode_poll_rate_budget.py index 66507b9903..e04c6440eb 100644 --- a/tests/test_opencode_poll_rate_budget.py +++ b/tests/test_opencode_poll_rate_budget.py @@ -1,45 +1,21 @@ -"""Rate-budget regression for Required OpenCode review polling.""" +"""Request-budget regression for one-shot Required OpenCode verdict admission.""" from pathlib import Path - WORKFLOW = Path(".github/workflows/opencode-review.yml") - -def _poll_loop() -> str: - """Return the long-running current-head verdict polling loop.""" - workflow = WORKFLOW.read_text(encoding="utf-8") - step = workflow.split( - " - name: Fail closed without a current-head OpenCode verdict\n", 1 - )[1].split("\n cancel-superseded-opencode-review-runs:\n", 1)[0] - return step.split(" while :; do\n", 1)[1].split( - " done\n if [ -z \"$verdict\" ]; then\n", 1 - )[0] - - -def test_poll_retains_live_revalidation_but_bounds_rest_request_pressure() -> None: - """Stale-head safety must not consume the repository token budget by design.""" +def _step() -> str: workflow = WORKFLOW.read_text(encoding="utf-8") - loop = _poll_loop() - - assert " poll_interval_seconds=60\n" in workflow - live_lookup = ( - 'live_poll_pr="$(timeout 30s gh api ' - '"repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"' - ) - reviews_lookup = ( - 'reviews="$(timeout 30s gh api --paginate ' - '"repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"' - ) - assert live_lookup in loop - assert reviews_lookup in loop - assert loop.index(live_lookup) < loop.index(reviews_lookup) - assert 'sleep "$poll_interval_seconds"' in loop - assert "sleep 30" not in loop - - -def test_review_poll_uses_maximum_rest_page_size() -> None: - """Review history pagination should minimize requests without dropping evidence.""" - loop = _poll_loop() - assert "/reviews?per_page=100" in loop - assert "gh api --paginate" in loop + return workflow.split(" - name: Fail closed without a current-head OpenCode verdict\n", 1)[1].split("\n cancel-superseded-opencode-review-runs:\n", 1)[0] + +def test_admission_uses_one_reviews_read_without_runner_polling() -> None: + step = _step() + assert step.count('gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}"') == 1 + assert step.count('gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100"') == 1 + for token in ("while :; do", "poll_interval_seconds", "poll_deadline_epoch", "sleep "): + assert token not in step + +def test_review_read_keeps_maximum_rest_page_size() -> None: + step = _step() + assert "/reviews?per_page=100" in step + assert "gh api --paginate" in step diff --git a/tests/test_opencode_poll_self_retirement.py b/tests/test_opencode_poll_self_retirement.py index 17d5e937ae..6353e2f7ee 100644 --- a/tests/test_opencode_poll_self_retirement.py +++ b/tests/test_opencode_poll_self_retirement.py @@ -1,460 +1,29 @@ -"""Regression contract for self-retiring Required OpenCode verdict polls.""" +"""Regression contract for self-releasing Required OpenCode verdict admission.""" -from __future__ import annotations - -import json -import os from pathlib import Path -import subprocess - WORKFLOW = Path(".github/workflows/opencode-review.yml") -def _fail_closed_step() -> str: - """Return the production current-head verdict polling step.""" - workflow = WORKFLOW.read_text(encoding="utf-8") - return workflow.split( - " - name: Fail closed without a current-head OpenCode verdict\n", 1 - )[1].split("\n cancel-superseded-opencode-review-runs:\n", 1)[0] - - -def _poll_loop() -> str: - """Return only the long-running Reviews API polling loop.""" - step = _fail_closed_step() - return step.split(" while :; do\n", 1)[1].split( - " done\n if [ -z \"$verdict\" ]; then\n", 1 - )[0] - - -def _run_poll_loop( - tmp_path: Path, - *, - head_sha: str, - live_pr: dict[str, object], - reviews: list[dict[str, object]] | None = None, - fail_live_pr_attempts: int = 0, - fail_review_attempts: int = 0, - date_epochs: list[int] | None = None, -) -> tuple[subprocess.CompletedProcess[str], list[str]]: - """Execute the production poll body against a deterministic fake ``gh``. - - ``date_epochs``, when given, stubs ``date`` to return each listed epoch - in turn (clamped to the last entry once exhausted) instead of the real - clock -- letting a test fast-forward past the real - ``poll_deadline_epoch`` wall-clock deadline after a chosen number of - genuinely-executed loop iterations, without ever sleeping for real time. - """ - call_log = tmp_path / "gh-calls.log" - live_fail_counter = tmp_path / "live-pr-failures" - review_fail_counter = tmp_path / "review-failures" - fake_gh = tmp_path / "gh" - fake_gh.write_text( - """#!/bin/sh -set -eu -printf '%s\\n' "$*" >> "$GH_CALL_LOG" -[ "${1:-}" = "api" ] || exit 90 -shift -if [ "${1:-}" = "--paginate" ]; then - count=0 - if [ -e "$GH_REVIEW_FAIL_COUNTER" ]; then - count="$(cat "$GH_REVIEW_FAIL_COUNTER")" - fi - count=$((count + 1)) - printf '%s\\n' "$count" > "$GH_REVIEW_FAIL_COUNTER" - if [ "$count" -le "${GH_FAIL_REVIEW_ATTEMPTS:-0}" ]; then - exit 1 - fi - printf '%s\\n' "$GH_REVIEWS" -else - count=0 - if [ -e "$GH_LIVE_FAIL_COUNTER" ]; then - count="$(cat "$GH_LIVE_FAIL_COUNTER")" - fi - count=$((count + 1)) - printf '%s\\n' "$count" > "$GH_LIVE_FAIL_COUNTER" - if [ "$count" -le "${GH_FAIL_LIVE_PR_ATTEMPTS:-0}" ]; then - exit 1 - fi - printf '%s\\n' "$GH_LIVE_PR" -fi -""", - encoding="utf-8", - ) - fake_gh.chmod(0o755) - fake_sleep = tmp_path / "sleep" - fake_sleep.write_text("#!/bin/sh\nexit 0\n", encoding="utf-8") - fake_sleep.chmod(0o755) - fake_timeout = tmp_path / "timeout" - fake_timeout.write_text( - "#!/bin/sh\nset -eu\nshift\nexec \"$@\"\n", - encoding="utf-8", - ) - fake_timeout.chmod(0o755) - - env_overrides: dict[str, str] = {} - if date_epochs is not None: - date_epochs_file = tmp_path / "date-epochs" - date_epochs_file.write_text( - "\n".join(str(epoch) for epoch in date_epochs) + "\n", encoding="utf-8" - ) - date_counter = tmp_path / "date-calls" - fake_date = tmp_path / "date" - fake_date.write_text( - """#!/bin/sh -set -eu -count=0 -if [ -e "$FAKE_DATE_COUNTER" ]; then - count="$(cat "$FAKE_DATE_COUNTER")" -fi -count=$((count + 1)) -printf '%s\\n' "$count" > "$FAKE_DATE_COUNTER" -line="$(sed -n "${count}p" "$FAKE_DATE_EPOCHS")" -if [ -z "$line" ]; then - line="$(tail -n1 "$FAKE_DATE_EPOCHS")" -fi -printf '%s\\n' "$line" -""", - encoding="utf-8", - ) - fake_date.chmod(0o755) - env_overrides["FAKE_DATE_EPOCHS"] = str(date_epochs_file) - env_overrides["FAKE_DATE_COUNTER"] = str(date_counter) - - script = "\n".join( - ( - "set -euo pipefail", - 'verdict=""', - 'live_poll_failures=0', - 'review_poll_failures=0', - 'max_poll_transport_failures=3', - 'poll_interval_seconds=60', - 'poll_deadline_epoch=$(( $(date +%s) + 10800 ))', - "while :; do", - _poll_loop(), - "done", - ) - ) - env = os.environ.copy() - env.update( - { - "PATH": f"{tmp_path}{os.pathsep}{env.get('PATH', '')}", - "TARGET_REPOSITORY": "ContextualWisdomLab/example", - "PR_NUMBER": "42", - "HEAD_SHA": head_sha, - "GH_CALL_LOG": str(call_log), - "GH_FAIL_LIVE_PR_ATTEMPTS": str(fail_live_pr_attempts), - "GH_FAIL_REVIEW_ATTEMPTS": str(fail_review_attempts), - "GH_LIVE_FAIL_COUNTER": str(live_fail_counter), - "GH_REVIEW_FAIL_COUNTER": str(review_fail_counter), - "GH_LIVE_PR": json.dumps(live_pr), - "GH_REVIEWS": json.dumps(reviews or []), - **env_overrides, - } - ) - result = subprocess.run( - ["bash", "-c", script], - check=False, - capture_output=True, - env=env, - text=True, - ) - calls = call_log.read_text(encoding="utf-8").splitlines() - return result, calls - - -def test_poll_revalidates_live_pr_before_every_reviews_api_read() -> None: - """An occupied runner must retire itself when its PR head stops being live.""" - loop = _poll_loop() - live_lookup = ( - 'live_poll_pr="$(timeout 30s gh api ' - '"repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"' - ) - reviews_lookup = ( - 'reviews="$(timeout 30s gh api --paginate ' - '"repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")"' - ) - - assert live_lookup in loop - assert 'live_poll_head="$(printf \'%s\' "$live_poll_pr" | jq -r ' in loop - assert 'live_poll_draft="$(printf \'%s\' "$live_poll_pr" | jq -r ' in loop - assert 'live_poll_state="$(printf \'%s\' "$live_poll_pr" | jq -r ' in loop - assert 'if [ "${live_poll_head,,}" != "${HEAD_SHA,,}" ]; then' in loop - assert "superseded Required OpenCode Review poll" in loop - assert 'if [ "$live_poll_state" = "closed" ]; then' in loop - assert 'if [ "$live_poll_draft" = "true" ]; then' in loop - assert reviews_lookup in loop - assert loop.index(live_lookup) < loop.index(reviews_lookup) - - -def test_poll_live_state_revalidation_fails_closed_on_malformed_evidence() -> None: - """Missing or malformed live-state evidence cannot turn a stale poll green.""" - loop = _poll_loop() - assert ( - 'if [ -z "$live_poll_head" ] || [ -z "$live_poll_draft" ] || ' - '[ -z "$live_poll_state" ]; then' in loop - ) - assert "Could not validate live pull request state while polling" in loop - assert ( - 'if [ "$live_poll_state" != "open" ] && ' - '[ "$live_poll_state" != "closed" ]; then' in loop - ) - - -def test_poll_executes_superseded_head_retirement_before_reviews_read( - tmp_path: Path, -) -> None: - """A moved head exits non-passing before the Reviews API is consulted.""" - head_sha = "a" * 40 - result, calls = _run_poll_loop( - tmp_path, - head_sha=head_sha, - live_pr={"head": {"sha": "b" * 40}, "draft": False, "state": "open"}, - ) - - assert result.returncode == 1 - assert "retiring superseded Required OpenCode Review poll" in result.stdout - assert calls == ["api repos/ContextualWisdomLab/example/pulls/42"] - - -def test_poll_executes_closed_pr_retirement_without_reviews_read(tmp_path: Path) -> None: - """A closed current-head PR releases the occupied runner successfully.""" - head_sha = "c" * 40 - result, calls = _run_poll_loop( - tmp_path, - head_sha=head_sha, - live_pr={"head": {"sha": head_sha}, "draft": False, "state": "closed"}, - ) - - assert result.returncode == 0 - assert "PR closed while waiting" in result.stdout - assert calls == ["api repos/ContextualWisdomLab/example/pulls/42"] - - -def test_poll_executes_live_state_read_before_current_head_review_read( - tmp_path: Path, -) -> None: - """A live head reads PR state first and then accepts only its current review.""" - head_sha = "d" * 40 - result, calls = _run_poll_loop( - tmp_path, - head_sha=head_sha, - live_pr={"head": {"sha": head_sha}, "draft": False, "state": "open"}, - reviews=[ - { - "user": {"login": "opencode-agent[bot]"}, - "commit_id": head_sha, - "state": "APPROVED", - "body": "Source-backed current-head semantic review.", - } - ], - ) - - assert result.returncode == 0, result.stderr - assert calls == [ - "api repos/ContextualWisdomLab/example/pulls/42", - "api --paginate repos/ContextualWisdomLab/example/pulls/42/reviews?per_page=100", - ] - - -def test_poll_retries_transient_live_state_failure_before_reviews_read( - tmp_path: Path, -) -> None: - """A transient live-state read failure retries without ending current authority.""" - head_sha = "e" * 40 - result, calls = _run_poll_loop( - tmp_path, - head_sha=head_sha, - live_pr={"head": {"sha": head_sha}, "draft": False, "state": "open"}, - reviews=[ - { - "user": {"login": "opencode-agent[bot]"}, - "commit_id": head_sha, - "state": "APPROVED", - "body": "Source-backed current-head semantic review.", - } - ], - fail_live_pr_attempts=1, - ) - - assert result.returncode == 0, result.stderr - assert "Live pull request read failed while polling" in result.stdout - assert calls == [ - "api repos/ContextualWisdomLab/example/pulls/42", - "api repos/ContextualWisdomLab/example/pulls/42", - "api --paginate repos/ContextualWisdomLab/example/pulls/42/reviews?per_page=100", - ] - - -def test_poll_fails_closed_after_bounded_live_state_transport_failures( - tmp_path: Path, -) -> None: - """Repeated live-state failures release the runner without fabricated evidence.""" - head_sha = "f" * 40 - result, calls = _run_poll_loop( - tmp_path, - head_sha=head_sha, - live_pr={"head": {"sha": head_sha}, "draft": False, "state": "open"}, - fail_live_pr_attempts=3, - ) - - assert result.returncode == 1 - assert "Live pull request read failed 3 consecutive times" in result.stdout - assert calls == ["api repos/ContextualWisdomLab/example/pulls/42"] * 3 - assert all("reviews" not in call for call in calls) - - -def test_poll_retries_transient_reviews_failure_after_revalidating_head( - tmp_path: Path, -) -> None: - """A Reviews API transport failure retries only after re-reading live PR state.""" - head_sha = "1" * 40 - result, calls = _run_poll_loop( - tmp_path, - head_sha=head_sha, - live_pr={"head": {"sha": head_sha}, "draft": False, "state": "open"}, - reviews=[ - { - "user": {"login": "opencode-agent"}, - "commit_id": head_sha, - "state": "APPROVED", - "body": "Source-backed current-head semantic review.", - } - ], - fail_review_attempts=1, - ) - - assert result.returncode == 0, result.stderr - assert "Reviews API read failed while polling" in result.stdout - assert calls == [ - "api repos/ContextualWisdomLab/example/pulls/42", - "api --paginate repos/ContextualWisdomLab/example/pulls/42/reviews?per_page=100", - "api repos/ContextualWisdomLab/example/pulls/42", - "api --paginate repos/ContextualWisdomLab/example/pulls/42/reviews?per_page=100", - ] - - -def test_poll_fails_closed_after_bounded_reviews_transport_failures( - tmp_path: Path, -) -> None: - """Repeated Reviews API failures stop after a finite number of attempts.""" - head_sha = "2" * 40 - result, calls = _run_poll_loop( - tmp_path, - head_sha=head_sha, - live_pr={"head": {"sha": head_sha}, "draft": False, "state": "open"}, - fail_review_attempts=3, - ) - - assert result.returncode == 1 - assert "Reviews API read failed 3 consecutive times" in result.stdout - assert calls == [ - "api repos/ContextualWisdomLab/example/pulls/42", - "api --paginate repos/ContextualWisdomLab/example/pulls/42/reviews?per_page=100", - ] * 3 - - -def test_self_retirement_does_not_replace_semantic_review_with_a_short_timeout() -> None: - """Capacity hygiene must not impose an arbitrary review inference deadline.""" - target_job = WORKFLOW.read_text(encoding="utf-8").split( - " opencode-review-target:\n", 1 - )[1].split("\n cancel-superseded-opencode-review-runs:\n", 1)[0] - assert "timeout-minutes:" not in target_job.split(" steps:\n", 1)[0] - assert "while :; do" in target_job - assert "poll_interval_seconds=60" in target_job - assert 'sleep "$poll_interval_seconds"' in target_job - - -def test_poll_fails_closed_after_wall_clock_deadline_with_every_gh_call_succeeding( - tmp_path: Path, -) -> None: - """The zombie scenario: no transport failure ever occurs, yet no verdict posts. - - `max_poll_transport_failures` cannot catch this -- every `gh` call - below succeeds -- so only a genuinely distinct wall-clock deadline - (`poll_deadline_epoch`, computed once before the loop) can release the - runner. A fake `date` fast-forwards past the real production 10800s - (180-minute) bound only after two full, genuinely-executed fast - iterations (proving the check is a real per-iteration wall-clock - comparison, not a check that fires before any work happens), without - this test ever sleeping for real time. - """ - head_sha = "5" * 40 - result, calls = _run_poll_loop( - tmp_path, - head_sha=head_sha, - live_pr={"head": {"sha": head_sha}, "draft": False, "state": "open"}, - reviews=[], # opencode-agent never posts a review on this head - date_epochs=[1000, 1000, 1000, 999999999999], - ) - - assert result.returncode == 1 - assert ( - "::error::No current-head OpenCode verdict after 180 minutes of " - "polling; failing closed and releasing the runner." in result.stdout - ) - # Distinct diagnostic from the transport-failure path: nothing here failed. - assert "consecutive times" not in result.stdout - assert calls == [ - "api repos/ContextualWisdomLab/example/pulls/42", - "api --paginate repos/ContextualWisdomLab/example/pulls/42/reviews?per_page=100", - "api repos/ContextualWisdomLab/example/pulls/42", - "api --paginate repos/ContextualWisdomLab/example/pulls/42/reviews?per_page=100", - ] - - -def test_poll_wall_clock_deadline_does_not_interfere_with_a_fast_verdict( - tmp_path: Path, -) -> None: - """A verdict arriving on the first poll is unaffected by the new bound.""" - head_sha = "6" * 40 - result, calls = _run_poll_loop( - tmp_path, - head_sha=head_sha, - live_pr={"head": {"sha": head_sha}, "draft": False, "state": "open"}, - reviews=[ - { - "user": {"login": "opencode-agent[bot]"}, - "commit_id": head_sha, - "state": "APPROVED", - "body": "Source-backed current-head semantic review.", - } - ], - date_epochs=[1000, 1000], # baseline call, then one in-bounds iteration check - ) +def _step() -> str: + """Return only the one-shot exact-head verdict-admission step.""" + text = WORKFLOW.read_text(encoding="utf-8") + return text.split(" - name: Fail closed without a current-head OpenCode verdict\n", 1)[1].split("\n cancel-superseded-opencode-review-runs:\n", 1)[0] - assert result.returncode == 0, result.stderr - assert calls == [ - "api repos/ContextualWisdomLab/example/pulls/42", - "api --paginate repos/ContextualWisdomLab/example/pulls/42/reviews?per_page=100", - ] - assert "No current-head OpenCode verdict after" not in result.stdout +def test_one_shot_revalidates_live_authority_before_review_evidence() -> None: + """Live PR/head/draft/state authority precedes the complete Reviews read.""" + step = _step() + live = 'gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}"' + reviews = 'gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews' + assert live in step and reviews in step and step.index(live) < step.index(reviews) + assert "Could not validate live pull request state before verdict admission" in step + assert "PR is still a draft" in step + assert "fresh required-review run will bind the current head" in step -def test_wall_clock_deadline_is_distinct_from_and_additional_to_transport_counter() -> None: - """The new bound sits alongside, not in place of, the transport-failure counter. - Pins the production shape so a future edit cannot quietly collapse the - two into one, or drop the wall-clock bound back to unbounded: both - `max_poll_transport_failures` (existing) and `poll_deadline_epoch` - (computed once before the loop) must be present, and the wall-clock - check must live inside the `while :; do` loop body -- not as a - job-level `timeout-minutes:`, which would kill the runner mid-request - instead of failing closed with a clear diagnostic. - """ - target_job = WORKFLOW.read_text(encoding="utf-8").split( - " opencode-review-target:\n", 1 - )[1].split("\n cancel-superseded-opencode-review-runs:\n", 1)[0] - assert "max_poll_transport_failures=3" in target_job - assert "poll_deadline_epoch=$(( $(date -u +%s) + 10800 ))" in target_job - loop = _poll_loop() - assert 'if [ "$(date -u +%s)" -ge "$poll_deadline_epoch" ]; then' in loop - assert ( - "::error::No current-head OpenCode verdict after 180 minutes of " - "polling; failing closed and releasing the runner." in loop - ) - # The deadline check must precede this iteration's gh calls so an - # already-expired deadline never spends another API request. - assert loop.index('-ge "$poll_deadline_epoch"') < loop.index( - 'live_poll_pr="$(timeout 30s gh api' - ) +def test_one_shot_has_no_repository_authored_wait_retry_or_transport_deadline() -> None: + """No elapsed-time or fixed-attempt policy governs formal verdict admission.""" + step = _step() + for token in ("while :; do", "poll_interval_seconds", "poll_deadline_epoch", "max_poll_transport_failures", "sleep ", "timeout "): + assert token not in step diff --git a/tests/test_opencode_required_verdict_regression.py b/tests/test_opencode_required_verdict_regression.py index 05993face8..a422037d49 100644 --- a/tests/test_opencode_required_verdict_regression.py +++ b/tests/test_opencode_required_verdict_regression.py @@ -37,7 +37,8 @@ def fail_closed_script() -> str: step = workflow.split( " - name: Fail closed without a current-head OpenCode verdict\n", 1 )[1] - return textwrap.dedent(step.split(" run: |\n", 1)[1]) + block = step.split(" run: |\n", 1)[1].split("\n cancel-superseded-opencode-review-runs:\n", 1)[0] + return textwrap.dedent(block) def review(*, state: str, commit_id: str = HEAD, body: str = "") -> dict[str, object]: @@ -267,8 +268,10 @@ def test_required_workflow_cannot_succeed_with_an_echo_only_placeholder() -> Non "exchange_github_app_token" ) assert "Current-head substantive OpenCode verdict already exists; scheduler wake skipped." in dispatch_step - assert "while :; do" in target_job - assert 'sleep "$poll_interval_seconds"' in target_job + assert "while :; do" not in target_job + assert "poll_interval_seconds" not in target_job + assert "poll_deadline_epoch" not in target_job + assert 'sleep "$poll_interval_seconds"' not in target_job assert "enable_auto_merge:false" in workflow assert 'gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100"' in workflow assert "github.event.pull_request.head.sha" in workflow @@ -548,7 +551,7 @@ def test_fail_closed_step_exits_gracefully_when_open_nondraft_head_moved( assert result.returncode == 0, result.stderr assert ( "Pull request head moved on the live open, ready-for-review PR; " - "a fresh poll will start for the current head." in result.stdout + "a fresh required-review run will bind the current head." in result.stdout ) assert "::error::" not in result.stdout @@ -631,7 +634,7 @@ def test_fail_closed_step_closed_still_takes_precedence_over_draft(tmp_path: Pat assert "PR is a draft" not in result.stdout -def test_fail_closed_step_still_polls_for_a_non_draft_pr(tmp_path: Path) -> None: +def test_fail_closed_step_reads_reviews_once_for_a_non_draft_pr(tmp_path: Path) -> None: """A non-draft PR must still reach the Reviews API call (not exempted). Unlike the request-review step's single unguarded call, the Reviews API @@ -646,7 +649,7 @@ def test_fail_closed_step_still_polls_for_a_non_draft_pr(tmp_path: Path) -> None result = _run_fail_closed_step(tmp_path, pr_action="synchronize", pr_draft="false") assert result.returncode == 1, result.stderr assert "unexpected gh invocation after live-state validation" in result.stderr - assert "Reviews API read failed 3 consecutive times" in result.stdout + assert "Reviews API read failed during one-shot current-head verdict admission" in result.stdout @pytest.mark.parametrize( @@ -722,11 +725,12 @@ def test_scheduler_wake_reuses_trusted_receipt_predicate( def test_formal_receipt_wake_remains_available_without_bounding_runner_polling() -> None: - """The receipt wake path coexists with the unbounded required review wait.""" + """The receipt wake path reawakens the fail-closed one-shot required review.""" required = WORKFLOW.read_text(encoding="utf-8") dispatched = DISPATCH_WORKFLOW.read_text(encoding="utf-8") assert "for attempt in" not in required - assert "while :; do" in required + assert "while :; do" not in required + assert "poll_deadline_epoch" not in required assert "rerun-failed-jobs" in dispatched assert "id: formal_review_receipt" in dispatched assert "steps.formal_review_receipt.outcome == 'success'" in dispatched @@ -735,7 +739,9 @@ def test_formal_receipt_wake_remains_available_without_bounding_runner_polling() assert "select(.id == $run_id)" in dispatched assert 'select(.event == "pull_request_target")' in dispatched assert 'select(.path == ".github/workflows/opencode-review.yml")' in dispatched - assert "select(.head_sha == $head)" in dispatched + assert "pull_requests // []" in dispatched + assert "(.number // 0) | tostring" in dispatched + assert "select(.head_sha == $head)" not in dispatched wake_step = dispatched.split("Wake exact-head required OpenCode workflow", 1)[1].split("\n\n - name:", 1)[0] target_job = dispatched.split(" opencode-review-target:\n", 1)[1] target_permissions = target_job.split(" env:\n", 1)[0] @@ -755,46 +761,34 @@ def test_formal_receipt_wake_remains_available_without_bounding_runner_polling() assert 'workflow_url | contains("/actions/required_workflows/")' not in wake_step -def wake_selector(run: dict[str, object], *, head: str = HEAD, run_id: int = 42) -> str: - """Execute the wake step's run-validation jq program in isolation.""" +def wake_selector(run: dict[str, object], *, head: str = HEAD, pr: int = 1437, run_id: int = 42) -> str: + """Execute the wake step's exact run/PR/head validation jq program.""" jq = shutil.which("jq") if jq is None: pytest.skip("jq is required to execute the production wake selector") dispatched = DISPATCH_WORKFLOW.read_text(encoding="utf-8") - marker = """jq -r --arg head "$PR_HEAD_SHA" --argjson run_id "$REQUIRED_RUN_ID" '""" + marker = """jq -r --arg head "$PR_HEAD_SHA" --arg pr "$PR_NUMBER" --argjson run_id "$REQUIRED_RUN_ID" '""" start = dispatched.index(marker) + len(marker) end = dispatched.index("\n ')", start) result = subprocess.run( - [jq, "-r", "--arg", "head", head, "--argjson", "run_id", str(run_id), dispatched[start:end]], - input=json.dumps(run), - text=True, - capture_output=True, - check=False, + [jq, "-r", "--arg", "head", head, "--arg", "pr", str(pr), "--argjson", "run_id", str(run_id), dispatched[start:end]], + input=json.dumps(run), text=True, capture_output=True, check=False, ) assert result.returncode == 0, result.stderr return result.stdout.strip() -def required_run(*, run_id: int = 42, head_sha: str = HEAD, path: str = ".github/workflows/opencode-review.yml") -> dict[str, object]: - """Build one realistic single-run GET REST API record. - - Mirrors the real shape a sibling repo sees for a run injected by the org's - required-workflow ruleset (this repo's actual central-hub use case): `name` - is the bare workflow name and `display_title` is a plain PR title, with no - PR number or head SHA embedded in either -- unlike a native same-repo - trigger, where both fields carry the rendered `run-name`. - """ +def required_run(*, run_id: int = 42, pr_head_sha: str = HEAD, pr_number: int = 1437, path: str = ".github/workflows/opencode-review.yml") -> dict[str, object]: + """Build a pull_request_target run whose top-level head_sha is the base SHA.""" return { "id": run_id, - "head_sha": head_sha, + "head_sha": "f" * 40, "event": "pull_request_target", "name": "Required OpenCode Review", "display_title": "Fix an unrelated example bug", "path": path, - "workflow_url": ( - "https://api.github.com/repos/ContextualWisdomLab/example" - "/actions/required_workflows/9" - ), + "workflow_url": "https://api.github.com/repos/ContextualWisdomLab/example/actions/required_workflows/9", + "pull_requests": [{"number": pr_number, "head": {"sha": pr_head_sha}}], "status": "completed", "conclusion": "failure", } @@ -812,7 +806,12 @@ def test_wake_selector_rejects_a_referenced_run_with_a_different_head() -> None: -- the realistic failure mode for an id-based reference, e.g. a superseded run or a stale/forged required_run_id. """ - assert wake_selector(required_run(head_sha="b" * 40)) == "" + assert wake_selector(required_run(pr_head_sha="b" * 40)) == "" + + +def test_wake_selector_rejects_a_referenced_run_for_a_different_pr() -> None: + """A run id for another PR cannot receive the wake mutation.""" + assert wake_selector(required_run(pr_number=9999)) == "" def test_wake_selector_rejects_a_referenced_run_for_a_different_workflow() -> None: @@ -847,6 +846,7 @@ def test_formal_receipt_wakes_the_exact_head_failed_required_run(tmp_path: Path) "FAKE_CALLS": str(calls), "GH_REPOSITORY": "ContextualWisdomLab/example", "GH_TOKEN": "actions-write-token", + "PR_NUMBER": "1437", "PR_HEAD_SHA": HEAD, "REQUIRED_RUN_ID": "42", "WAKE_TOKEN_SOURCE": "PR_REVIEW_MERGE_TOKEN", @@ -875,6 +875,7 @@ def test_sibling_formal_receipt_fails_closed_without_actions_token() -> None: **os.environ, "GH_TOKEN": "", "GH_REPOSITORY": "ContextualWisdomLab/example", + "PR_NUMBER": "1437", "PR_HEAD_SHA": HEAD, "REQUIRED_RUN_ID": "42", "WAKE_TOKEN_SOURCE": "unavailable", diff --git a/tests/test_opencode_required_verdict_runner_release.py b/tests/test_opencode_required_verdict_runner_release.py index 282d12b052..c771af52e1 100644 --- a/tests/test_opencode_required_verdict_runner_release.py +++ b/tests/test_opencode_required_verdict_runner_release.py @@ -1,4 +1,4 @@ -"""Regression coverage for releasing the required OpenCode runner while review continues.""" +"""Regression coverage for one-shot Required OpenCode verdict admission.""" from __future__ import annotations @@ -11,122 +11,65 @@ import pytest +REQUIRED = Path(".github/workflows/opencode-review.yml") +DISPATCH = Path(".github/workflows/opencode-review-dispatch.yml") +HEAD = "a" * 40 -WORKFLOW = Path(".github/workflows/opencode-review.yml") -DISPATCH_WORKFLOW = Path(".github/workflows/opencode-review-dispatch.yml") -HEAD_SHA = "a" * 40 +def _required_script() -> str: + """Return the production exact-head verdict-admission shell body.""" + text = REQUIRED.read_text(encoding="utf-8") + step = text.split(" - name: Fail closed without a current-head OpenCode verdict\n", 1)[1] + return textwrap.dedent(step.split(" run: |\n", 1)[1].split("\n cancel-superseded-opencode-review-runs:\n", 1)[0]) -def _fail_closed_script() -> str: - """Extract the real required-verdict admission step body.""" - workflow = WORKFLOW.read_text(encoding="utf-8") - step = workflow.split( - " - name: Fail closed without a current-head OpenCode verdict\n", 1 - )[1] - return textwrap.dedent(step.split(" run: |\n", 1)[1]) +def _wake() -> str: + """Return the production formal-receipt exact-run wake step.""" + text = DISPATCH.read_text(encoding="utf-8") + return text.split(" - name: Wake exact-head required OpenCode workflow\n", 1)[1].split("\n - name: Publish repository_dispatch OpenCode status\n", 1)[0] -def test_missing_verdict_uses_exact_run_wake_instead_of_runner_polling() -> None: - """A missing verdict must fail once and rely on the authenticated exact-run wake.""" - required = _fail_closed_script() - dispatched = DISPATCH_WORKFLOW.read_text(encoding="utf-8") - assert "while :; do" not in required - assert "poll_interval_seconds" not in required - assert "poll_deadline_epoch" not in required - assert "sleep " not in required - assert "rerun-failed-jobs" in dispatched - assert "github.event.client_payload.required_run_id != ''" in dispatched - assert 'gh api "repos/${GH_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}"' in dispatched - assert 'select(.event == "pull_request_target")' in dispatched - assert 'select(.path == ".github/workflows/opencode-review.yml")' in dispatched - # pull_request_target run.head_sha is the protected base, not the PR head. - # Exact-run admission must therefore bind the immutable run to the intended - # PR and exact head through the run's pull_requests association. - assert "PR_NUMBER" in dispatched - assert ".pull_requests" in dispatched - assert ".head.sha" in dispatched - assert "select(.head_sha == $head)" not in dispatched +def test_missing_verdict_releases_runner_without_local_wait_allocation() -> None: + """Admission performs complete state reads once and never polls or sleeps.""" + step = _required_script() + assert step.count('gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}"') == 1 + assert step.count('gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews') == 1 + for token in ("while :; do", "poll_interval_seconds", "poll_deadline_epoch", "max_poll_transport_failures", "sleep ", "timeout "): + assert token not in step -def test_admission_transport_reads_are_bounded_without_bounding_model_work() -> None: - """GitHub REST stalls must release the runner without adding a model deadline.""" - required = _fail_closed_script() - assert 'timeout 30 gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}"' in required - assert 'timeout 30 gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100"' in required - for token in ("poll_deadline_epoch", "max_poll_transport_failures", "sleep "): - assert token not in required +def test_receipt_wake_binds_exact_pr_head_and_run_without_polling() -> None: + """Authenticated receipt wake is one exact-state transition.""" + step = _wake() + for token in ("for attempt", "while :; do", "seq 1", "sleep ", "timeout ", "/12", "--paginate"): + assert token not in step + assert "pull_requests // []" in step + assert "rerun-failed-jobs" in step + assert "advanced concurrently" in step -def test_missing_verdict_fails_after_one_live_read_and_one_review_read(tmp_path: Path) -> None: - """Execute the production step and prove it never sleeps or loops without a verdict.""" +def test_missing_verdict_fails_after_one_live_and_one_reviews_read(tmp_path: Path) -> None: + """No formal verdict causes exactly two GitHub reads and an immediate failure.""" bash = shutil.which("bash") jq = shutil.which("jq") if bash is None or jq is None: - pytest.skip("bash and jq are required to execute the production verdict step") - - fake_bin = tmp_path / "bin" - fake_bin.mkdir() - call_log = tmp_path / "gh-calls" - fake_gh = fake_bin / "gh" - fake_gh.write_text( - """#!/usr/bin/env bash -set -euo pipefail -printf '%s\\n' "$*" >>"$FAKE_CALL_LOG" -if [[ "$*" == "api repos/ContextualWisdomLab/example/pulls/42" ]]; then - printf '%s\\n' "$FAKE_LIVE_PR" - exit 0 -fi -if [[ "$*" == "api --paginate repos/ContextualWisdomLab/example/pulls/42/reviews?per_page=100" ]]; then - printf '%s\\n' '[]' - exit 0 -fi -printf 'unexpected gh call: %s\\n' "$*" >&2 -exit 97 -""", + pytest.skip("bash and jq are required") + calls = tmp_path / "calls" + gh = tmp_path / "gh" + gh.write_text( + "#!/usr/bin/env bash\nset -euo pipefail\nprintf '%s\\n' \"$*\" >>\"$CALLS\"\n" + "if [[ \"$*\" == \"api repos/ContextualWisdomLab/example/pulls/42\" ]]; then printf '%s\\n' \"$LIVE_PR\"; exit 0; fi\n" + "if [[ \"$*\" == \"api --paginate repos/ContextualWisdomLab/example/pulls/42/reviews?per_page=100\" ]]; then printf '[]\\n'; exit 0; fi\n" + "exit 97\n", encoding="utf-8", ) - fake_gh.chmod(0o755) - - fake_timeout = fake_bin / "timeout" - fake_timeout.write_text( - "#!/usr/bin/env bash\nset -euo pipefail\nshift\nexec \"$@\"\n", - encoding="utf-8", - ) - fake_timeout.chmod(0o755) - - fake_sleep = fake_bin / "sleep" - fake_sleep.write_text( - "#!/usr/bin/env bash\nprintf 'unexpected sleep\\n' >&2\nexit 91\n", - encoding="utf-8", - ) - fake_sleep.chmod(0o755) - + gh.chmod(0o755) result = subprocess.run( - [bash, "-c", _fail_closed_script()], - env={ - **os.environ, - "PATH": f"{fake_bin}{os.pathsep}{os.environ.get('PATH', '')}", - "FAKE_CALL_LOG": str(call_log), - "FAKE_LIVE_PR": json.dumps( - {"head": {"sha": HEAD_SHA}, "draft": False, "state": "open"} - ), - "GH_TOKEN": "test-token", - "TARGET_REPOSITORY": "ContextualWisdomLab/example", - "PR_NUMBER": "42", - "HEAD_SHA": HEAD_SHA, - "PR_ACTION": "synchronize", - "PR_DRAFT": "false", - }, + [bash, "-c", _required_script()], + env={**os.environ, "PATH": f"{tmp_path}{os.pathsep}{os.environ.get('PATH', '')}", "CALLS": str(calls), "LIVE_PR": json.dumps({"head": {"sha": HEAD}, "draft": False, "state": "open"}), "GH_TOKEN": "token", "TARGET_REPOSITORY": "ContextualWisdomLab/example", "PR_NUMBER": "42", "HEAD_SHA": HEAD, "PR_ACTION": "synchronize", "PR_DRAFT": "false"}, text=True, capture_output=True, check=False, ) - assert result.returncode == 1, result.stderr - assert "unexpected sleep" not in result.stderr - assert "No APPROVED or CHANGES_REQUESTED from opencode-agent" in result.stdout - assert call_log.read_text(encoding="utf-8").splitlines() == [ - "api repos/ContextualWisdomLab/example/pulls/42", - "api --paginate repos/ContextualWisdomLab/example/pulls/42/reviews?per_page=100", - ] + assert len(calls.read_text(encoding="utf-8").splitlines()) == 2 diff --git a/tests/test_pr_review_autofix_nvidia_nim_contract.py b/tests/test_pr_review_autofix_nvidia_nim_contract.py index 4b749d93c4..f0d423ca8b 100644 --- a/tests/test_pr_review_autofix_nvidia_nim_contract.py +++ b/tests/test_pr_review_autofix_nvidia_nim_contract.py @@ -17,7 +17,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 = "bb5d439c3fc2fc7b5fcd38533d38f96e1170cd2e" +REVIEW_DISPATCH_BLOB_SHA = "78e920e6984673744fb5794f2bd0a6a9f34668fa" def _workflow_text(path: Path) -> str: From 94a750498d439e9c8e4de505600b2134a8f3fcb0 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 21:30:17 +0900 Subject: [PATCH 37/59] fix(opencode): repair exact-head verdict reconciliation --- .../_temp_pr1706_reconcile_formal_verdict.yml | 198 ++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 .github/workflows/_temp_pr1706_reconcile_formal_verdict.yml diff --git a/.github/workflows/_temp_pr1706_reconcile_formal_verdict.yml b/.github/workflows/_temp_pr1706_reconcile_formal_verdict.yml new file mode 100644 index 0000000000..f085bd8598 --- /dev/null +++ b/.github/workflows/_temp_pr1706_reconcile_formal_verdict.yml @@ -0,0 +1,198 @@ +name: Temporary PR 1706 formal-verdict reconciliation repair + +on: + push: + branches: + - fix/opencode-poll-wall-clock-bound + paths: + - .github/workflows/_temp_pr1706_reconcile_formal_verdict.yml + +permissions: + contents: write + +concurrency: + group: temp-pr1706-formal-verdict-repair + cancel-in-progress: true + +jobs: + repair: + if: github.actor_id == '8172694' + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Verify exact writer head + shell: bash + run: | + set -euo pipefail + git fetch origin fix/opencode-poll-wall-clock-bound + test "$(git rev-parse origin/fix/opencode-poll-wall-clock-bound)" = "$GITHUB_SHA" + - name: Repair current-head formal verdict reconciliation + shell: bash + run: | + set -euo pipefail + python3 - <<'PY' + from pathlib import Path + + scheduler = Path('.github/workflows/pr-review-merge-scheduler.yml') + text = scheduler.read_text(encoding='utf-8') + old_filter = ''' | select(.state == "APPROVED" or .state == "CHANGES_REQUESTED") + | select((.body // "" | ascii_downcase | contains("deterministic current-head evidence")) | not) + | select((.body // "" | ascii_downcase | contains("deterministic fallback approval")) | not) + | select((.body // "" | ascii_downcase | contains("model-unavailable evidence fallback")) | not) + | select((.body // "" | ascii_downcase | contains("did not emit a usable current-head control block")) | not) + | select((.body // "" | ascii_downcase | contains("scope: `unsupported`")) | not) + | select((.body // "" | ascii_downcase | contains("model-pool outcome: `unknown`")) | not)]''' + new_filter = ''' | select( + .state == "CHANGES_REQUESTED" + or ( + .state == "APPROVED" + and ((.body // "" | ascii_downcase | contains("deterministic current-head evidence")) | not) + and ((.body // "" | ascii_downcase | contains("deterministic fallback approval")) | not) + and ((.body // "" | ascii_downcase | contains("model-unavailable evidence fallback")) | not) + and ((.body // "" | ascii_downcase | contains("did not emit a usable current-head control block")) | not) + and ((.body // "" | ascii_downcase | contains("scope: `unsupported`")) | not) + and ((.body // "" | ascii_downcase | contains("model-pool outcome: `unknown`")) | not) + ) + )]''' + if text.count(old_filter) != 1: + raise SystemExit('formal-verdict filter drifted; refusing ambiguous repair') + text = text.replace(old_filter, new_filter, 1) + old_time = ''' new_evidence="$(jq -nr --arg review "$review_submitted_at" --arg started "$REQUIRED_RUN_STARTED_AT" 'try (($review | fromdateiso8601) > ($started | fromdateiso8601)) catch false')"''' + new_time = ''' # GitHub review/run timestamps are second-granularity. Equality can mean the review\n # arrived later within the same second, so accept equality for exact-head evidence;\n # older seconds remain ineligible and all PR/head/state checks above still fail closed.\n new_evidence="$(jq -nr --arg review "$review_submitted_at" --arg started "$REQUIRED_RUN_STARTED_AT" 'try (($review | fromdateiso8601) >= ($started | fromdateiso8601)) catch false')"''' + if text.count(old_time) != 1: + raise SystemExit('review timestamp comparison drifted; refusing ambiguous repair') + scheduler.write_text(text.replace(old_time, new_time, 1), encoding='utf-8') + + workflow = Path('.github/workflows/opencode-review.yml') + wtext = workflow.read_text(encoding='utf-8') + old_comment = ''' # `converted_to_draft` is included so a PR going draft mid-poll fires a + # fresh run of this same workflow: the head-scoped concurrency group below + # (`cancel-in-progress: true`) cancels any in-flight non-draft + # "Fail closed without a current-head OpenCode verdict" poll for that + # exact same head. Every non-closed admission path revalidates the live + # PR/head/state before dispatching, exempting, or polling so out-of-order + # draft/ready/closed events cannot publish stale evidence or wait on an + # impossible verdict.''' + new_comment = ''' # `converted_to_draft` is included so a PR going draft after a ready event + # gets a fresh same-head execution. The head-scoped concurrency group below + # (`cancel-in-progress: true`) retires any superseded same-head admission run. + # Every non-closed path revalidates the live PR/head/state before dispatch, + # exemption, or one-shot verdict admission, so out-of-order draft/ready/closed + # events cannot publish stale evidence. Required verdict continuation is + # event-driven by the authenticated exact-run wake path; this entrypoint does + # not hold a runner in a polling loop.''' + if wtext.count(old_comment) != 1: + raise SystemExit('OpenCode trigger comment drifted; refusing ambiguous repair') + workflow.write_text(wtext.replace(old_comment, new_comment, 1), encoding='utf-8') + + test = Path('tests/test_opencode_required_verdict_reconciliation_contract.py') + if test.exists(): + raise SystemExit('formal-verdict regression path already exists; refusing overwrite') + test.write_text(r'''"""Executable regressions for event-driven OpenCode required-verdict reconciliation.""" + +from __future__ import annotations + +import json +from pathlib import Path +import subprocess +import textwrap + + +WORKFLOW = Path(".github/workflows/pr-review-merge-scheduler.yml") +HEAD = "a" * 40 + + +def _jq_filter() -> str: + """Extract the exact jq review selector used by the production reconciler.""" + text = WORKFLOW.read_text(encoding="utf-8") + anchor = text.index('latest_review="$(printf') + start = text.index(" (add // [])", anchor) + end_marker = " | @tsv" + end = text.index(end_marker, start) + len(end_marker) + return textwrap.dedent(text[start:end]) + + +def _select(reviews: list[dict[str, object]]) -> str: + """Execute the production jq selector against a synthetic exact-head review list.""" + proc = subprocess.run( + ["jq", "-r", "-s", "--arg", "sha", HEAD, _jq_filter()], + input=json.dumps(reviews), + text=True, + capture_output=True, + check=True, + ) + return proc.stdout.strip() + + +def _review(state: str, body: str, submitted_at: str = "2026-09-02T12:00:00Z") -> dict[str, object]: + """Build one exact-head OpenCode review fixture.""" + return { + "id": 42, + "user": {"login": "opencode-agent[bot]"}, + "commit_id": HEAD, + "state": state, + "body": body, + "submitted_at": submitted_at, + } + + +def test_marker_bearing_change_request_remains_a_formal_verdict() -> None: + """Fallback markers invalidate approvals only, never a real change request.""" + result = _select([_review("CHANGES_REQUESTED", "deterministic fallback approval: defect remains")]) + assert result.startswith("CHANGES_REQUESTED\t") + + +def test_marker_bearing_approval_is_not_admitted() -> None: + """Synthetic/fallback approval markers must still block APPROVED evidence.""" + assert _select([_review("APPROVED", "deterministic fallback approval")]) == "" + + +def test_clean_approval_is_admitted() -> None: + """A clean exact-head approval remains a formal verdict.""" + result = _select([_review("APPROVED", "real model review")]) + assert result.startswith("APPROVED\t") + + +def test_same_second_review_is_eligible_for_reconciliation() -> None: + """Second-granularity timestamps must not strand a later review in the same second.""" + text = WORKFLOW.read_text(encoding="utf-8") + assert "fromdateiso8601) >= ($started | fromdateiso8601" in text + proc = subprocess.run( + ["jq", "-nr", "--arg", "review", "2026-09-02T12:00:00Z", "--arg", "started", "2026-09-02T12:00:00Z", "try (($review | fromdateiso8601) >= ($started | fromdateiso8601)) catch false"], + text=True, + capture_output=True, + check=True, + ) + assert proc.stdout.strip() == "true" + + +if __name__ == "__main__": + test_marker_bearing_change_request_remains_a_formal_verdict() + test_marker_bearing_approval_is_not_admitted() + test_clean_approval_is_admitted() + test_same_second_review_is_eligible_for_reconciliation() +''', encoding='utf-8') + + baseline = Path('docs/product-technical-gap-baseline.md') + btext = baseline.read_text(encoding='utf-8') + record = '''\n### OpenCode required-verdict reconciliation race repair — 2026-09-02\n\n- **Owner / PR:** `ContextualWisdomLab/.github#1706`.\n- **Root cause:** the workflow-run reconciler applied fallback-approval marker exclusions to `CHANGES_REQUESTED` reviews even though the admission gates accept every exact-head change request, and strict greater-than timestamp ordering could drop a later review that shared GitHub's second-granularity timestamp with the required run start.\n- **Repair:** marker exclusions now apply only to `APPROVED`; exact-head `CHANGES_REQUESTED` remains a formal verdict. Same-second evidence uses `>=` after live open/ready/head revalidation. Polling-era trigger comments were replaced with the event-driven one-shot contract.\n- **Regression:** `tests/test_opencode_required_verdict_reconciliation_contract.py` executes the production jq selector for marker-bearing change requests, rejected fallback approvals, clean approvals, and same-second timestamp ordering.\n- **Status:** Proposed until this exact branch head receives fresh CI/security/review evidence and integrates through ordinary protected admission.\n''' + if '### OpenCode required-verdict reconciliation race repair — 2026-09-02' not in btext: + baseline.write_text(btext.rstrip() + '\n' + record, encoding='utf-8') + PY + PYTHONPATH=. python3 tests/test_opencode_required_verdict_reconciliation_contract.py + git diff --check + - name: Publish repaired exact-head tree and retire helper + shell: bash + run: | + set -euo pipefail + rm .github/workflows/_temp_pr1706_reconcile_formal_verdict.yml + git add .github/workflows/opencode-review.yml .github/workflows/pr-review-merge-scheduler.yml tests/test_opencode_required_verdict_reconciliation_contract.py docs/product-technical-gap-baseline.md .github/workflows/_temp_pr1706_reconcile_formal_verdict.yml + git diff --cached --check + git config user.name 'ContextualWisdomLab repair bot' + git config user.email 'actions@users.noreply.github.com' + git commit -m 'fix(opencode): reconcile exact-head formal verdicts' + git fetch origin fix/opencode-poll-wall-clock-bound + test "$(git rev-parse origin/fix/opencode-poll-wall-clock-bound)" = "$GITHUB_SHA" + git push origin HEAD:fix/opencode-poll-wall-clock-bound From 9d86a381b36779fc9b284745768b64bb7314f336 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 21:33:56 +0900 Subject: [PATCH 38/59] fix(opencode): stage formal-verdict source repair --- .../ci/source_fix_pr1706_formal_verdict.py | 164 ++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 scripts/ci/source_fix_pr1706_formal_verdict.py diff --git a/scripts/ci/source_fix_pr1706_formal_verdict.py b/scripts/ci/source_fix_pr1706_formal_verdict.py new file mode 100644 index 0000000000..c37c701ff6 --- /dev/null +++ b/scripts/ci/source_fix_pr1706_formal_verdict.py @@ -0,0 +1,164 @@ +"""One-shot source repair for ContextualWisdomLab/.github PR #1706. + +This temporary driver performs exact-string, fail-closed mutations only. The +workflow that invokes it removes this file after the permanent regression has +passed, so the production tree retains only the reviewed workflow/test/docs +delta. +""" + +from __future__ import annotations + +from pathlib import Path + + +SCHEDULER = Path(".github/workflows/pr-review-merge-scheduler.yml") +OPENCODE = Path(".github/workflows/opencode-review.yml") +TEST = Path("tests/test_opencode_required_verdict_reconciliation_contract.py") +BASELINE = Path("docs/product-technical-gap-baseline.md") + + +OLD_FILTER = ''' | select(.state == "APPROVED" or .state == "CHANGES_REQUESTED") + | select((.body // "" | ascii_downcase | contains("deterministic current-head evidence")) | not) + | select((.body // "" | ascii_downcase | contains("deterministic fallback approval")) | not) + | select((.body // "" | ascii_downcase | contains("model-unavailable evidence fallback")) | not) + | select((.body // "" | ascii_downcase | contains("did not emit a usable current-head control block")) | not) + | select((.body // "" | ascii_downcase | contains("scope: `unsupported`")) | not) + | select((.body // "" | ascii_downcase | contains("model-pool outcome: `unknown`")) | not)]''' +NEW_FILTER = ''' | select( + .state == "CHANGES_REQUESTED" + or ( + .state == "APPROVED" + and ((.body // "" | ascii_downcase | contains("deterministic current-head evidence")) | not) + and ((.body // "" | ascii_downcase | contains("deterministic fallback approval")) | not) + and ((.body // "" | ascii_downcase | contains("model-unavailable evidence fallback")) | not) + and ((.body // "" | ascii_downcase | contains("did not emit a usable current-head control block")) | not) + and ((.body // "" | ascii_downcase | contains("scope: `unsupported`")) | not) + and ((.body // "" | ascii_downcase | contains("model-pool outcome: `unknown`")) | not) + ) + )]''' +OLD_TIME = ''' new_evidence="$(jq -nr --arg review "$review_submitted_at" --arg started "$REQUIRED_RUN_STARTED_AT" 'try (($review | fromdateiso8601) > ($started | fromdateiso8601)) catch false')"''' +NEW_TIME = ''' # GitHub review/run timestamps are second-granularity. Equality can mean the review + # arrived later within the same second, so accept equality for exact-head evidence; + # older seconds remain ineligible and all PR/head/state checks above still fail closed. + new_evidence="$(jq -nr --arg review "$review_submitted_at" --arg started "$REQUIRED_RUN_STARTED_AT" 'try (($review | fromdateiso8601) >= ($started | fromdateiso8601)) catch false')"''' +OLD_COMMENT = ''' # `converted_to_draft` is included so a PR going draft mid-poll fires a + # fresh run of this same workflow: the head-scoped concurrency group below + # (`cancel-in-progress: true`) cancels any in-flight non-draft + # "Fail closed without a current-head OpenCode verdict" poll for that + # exact same head. Every non-closed admission path revalidates the live + # PR/head/state before dispatching, exempting, or polling so out-of-order + # draft/ready/closed events cannot publish stale evidence or wait on an + # impossible verdict.''' +NEW_COMMENT = ''' # `converted_to_draft` is included so a PR going draft after a ready event + # gets a fresh same-head execution. The head-scoped concurrency group below + # (`cancel-in-progress: true`) retires any superseded same-head admission run. + # Every non-closed path revalidates the live PR/head/state before dispatch, + # exemption, or one-shot verdict admission, so out-of-order draft/ready/closed + # events cannot publish stale evidence. Required verdict continuation is + # event-driven by the authenticated exact-run wake path; this entrypoint does + # not hold a runner in a polling loop.''' + +TEST_CONTENT = r'''"""Executable regressions for event-driven OpenCode verdict reconciliation.""" + +from __future__ import annotations + +import json +from pathlib import Path +import subprocess +import textwrap + +WORKFLOW = Path(".github/workflows/pr-review-merge-scheduler.yml") +HEAD = "a" * 40 + + +def _jq_filter() -> str: + """Extract the production jq review selector.""" + text = WORKFLOW.read_text(encoding="utf-8") + anchor = text.index('latest_review="$(printf') + start = text.index(" (add // [])", anchor) + end_marker = " | @tsv" + end = text.index(end_marker, start) + len(end_marker) + return textwrap.dedent(text[start:end]) + + +def _select(reviews: list[dict[str, object]]) -> str: + """Execute the production selector on exact-head review fixtures.""" + proc = subprocess.run( + ["jq", "-r", "-s", "--arg", "sha", HEAD, _jq_filter()], + input=json.dumps(reviews), text=True, capture_output=True, check=True, + ) + return proc.stdout.strip() + + +def _review(state: str, body: str) -> dict[str, object]: + """Build one exact-head OpenCode review fixture.""" + return { + "id": 42, + "user": {"login": "opencode-agent[bot]"}, + "commit_id": HEAD, + "state": state, + "body": body, + "submitted_at": "2026-09-02T12:00:00Z", + } + + +def test_marker_bearing_change_request_remains_a_formal_verdict() -> None: + """Fallback markers invalidate approvals only, never a real change request.""" + assert _select([_review("CHANGES_REQUESTED", "deterministic fallback approval: defect remains")]).startswith("CHANGES_REQUESTED\t") + + +def test_marker_bearing_approval_is_not_admitted() -> None: + """Synthetic/fallback approval markers still block APPROVED evidence.""" + assert _select([_review("APPROVED", "deterministic fallback approval")]) == "" + + +def test_clean_approval_is_admitted() -> None: + """A clean exact-head approval remains a formal verdict.""" + assert _select([_review("APPROVED", "real model review")]).startswith("APPROVED\t") + + +def test_same_second_review_is_eligible_for_reconciliation() -> None: + """Second-granularity timestamps must not strand a later same-second review.""" + text = WORKFLOW.read_text(encoding="utf-8") + assert "fromdateiso8601) >= ($started | fromdateiso8601" in text + proc = subprocess.run( + ["jq", "-nr", "--arg", "review", "2026-09-02T12:00:00Z", "--arg", "started", "2026-09-02T12:00:00Z", "try (($review | fromdateiso8601) >= ($started | fromdateiso8601)) catch false"], + text=True, capture_output=True, check=True, + ) + assert proc.stdout.strip() == "true" +''' + +RECORD = ''' +### OpenCode required-verdict reconciliation race repair — 2026-09-02 + +- **Owner / PR:** `ContextualWisdomLab/.github#1706`. +- **Root cause:** the workflow-run reconciler applied fallback-approval marker exclusions to `CHANGES_REQUESTED` reviews even though admission accepts every exact-head change request, and strict greater-than timestamp ordering could drop a later review sharing GitHub's second-granularity timestamp with the required run start. +- **Repair:** marker exclusions apply only to `APPROVED`; exact-head `CHANGES_REQUESTED` remains a formal verdict. Same-second evidence uses `>=` after live open/ready/head revalidation. Polling-era trigger comments now describe event-driven one-shot admission. +- **Regression:** `tests/test_opencode_required_verdict_reconciliation_contract.py` executes the production jq selector for marker-bearing change requests, rejected fallback approvals, clean approvals, and same-second ordering. +- **Status:** Proposed until this exact branch head receives fresh CI/security/review evidence and integrates through ordinary protected admission. +''' + + +def replace_once(path: Path, old: str, new: str, label: str) -> None: + """Replace exactly one expected source fragment or fail closed.""" + text = path.read_text(encoding="utf-8") + if text.count(old) != 1: + raise SystemExit(f"{label} drifted: expected exactly one match") + path.write_text(text.replace(old, new, 1), encoding="utf-8") + + +def main() -> None: + """Apply permanent production, regression, and traceability repairs.""" + replace_once(SCHEDULER, OLD_FILTER, NEW_FILTER, "formal-verdict filter") + replace_once(SCHEDULER, OLD_TIME, NEW_TIME, "review timestamp comparison") + replace_once(OPENCODE, OLD_COMMENT, NEW_COMMENT, "OpenCode trigger comment") + if TEST.exists(): + raise SystemExit(f"refusing to overwrite existing {TEST}") + TEST.write_text(TEST_CONTENT, encoding="utf-8") + baseline = BASELINE.read_text(encoding="utf-8") + if "### OpenCode required-verdict reconciliation race repair — 2026-09-02" not in baseline: + BASELINE.write_text(baseline.rstrip() + "\n" + RECORD, encoding="utf-8") + + +if __name__ == "__main__": + main() From f16e5ddcf518991c8f617a4651f03cf4ebdca7f4 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 21:34:26 +0900 Subject: [PATCH 39/59] fix(opencode): make formal-verdict materializer minimal --- .../_temp_pr1706_reconcile_formal_verdict.yml | 162 +----------------- 1 file changed, 8 insertions(+), 154 deletions(-) diff --git a/.github/workflows/_temp_pr1706_reconcile_formal_verdict.yml b/.github/workflows/_temp_pr1706_reconcile_formal_verdict.yml index f085bd8598..f7218f960e 100644 --- a/.github/workflows/_temp_pr1706_reconcile_formal_verdict.yml +++ b/.github/workflows/_temp_pr1706_reconcile_formal_verdict.yml @@ -19,176 +19,30 @@ jobs: if: github.actor_id == '8172694' runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - name: Checkout exact writer head + uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Verify exact writer head + - name: Verify live writer head shell: bash run: | set -euo pipefail git fetch origin fix/opencode-poll-wall-clock-bound test "$(git rev-parse origin/fix/opencode-poll-wall-clock-bound)" = "$GITHUB_SHA" - - name: Repair current-head formal verdict reconciliation + - name: Apply owner repair and permanent regression shell: bash run: | set -euo pipefail - python3 - <<'PY' - from pathlib import Path - - scheduler = Path('.github/workflows/pr-review-merge-scheduler.yml') - text = scheduler.read_text(encoding='utf-8') - old_filter = ''' | select(.state == "APPROVED" or .state == "CHANGES_REQUESTED") - | select((.body // "" | ascii_downcase | contains("deterministic current-head evidence")) | not) - | select((.body // "" | ascii_downcase | contains("deterministic fallback approval")) | not) - | select((.body // "" | ascii_downcase | contains("model-unavailable evidence fallback")) | not) - | select((.body // "" | ascii_downcase | contains("did not emit a usable current-head control block")) | not) - | select((.body // "" | ascii_downcase | contains("scope: `unsupported`")) | not) - | select((.body // "" | ascii_downcase | contains("model-pool outcome: `unknown`")) | not)]''' - new_filter = ''' | select( - .state == "CHANGES_REQUESTED" - or ( - .state == "APPROVED" - and ((.body // "" | ascii_downcase | contains("deterministic current-head evidence")) | not) - and ((.body // "" | ascii_downcase | contains("deterministic fallback approval")) | not) - and ((.body // "" | ascii_downcase | contains("model-unavailable evidence fallback")) | not) - and ((.body // "" | ascii_downcase | contains("did not emit a usable current-head control block")) | not) - and ((.body // "" | ascii_downcase | contains("scope: `unsupported`")) | not) - and ((.body // "" | ascii_downcase | contains("model-pool outcome: `unknown`")) | not) - ) - )]''' - if text.count(old_filter) != 1: - raise SystemExit('formal-verdict filter drifted; refusing ambiguous repair') - text = text.replace(old_filter, new_filter, 1) - old_time = ''' new_evidence="$(jq -nr --arg review "$review_submitted_at" --arg started "$REQUIRED_RUN_STARTED_AT" 'try (($review | fromdateiso8601) > ($started | fromdateiso8601)) catch false')"''' - new_time = ''' # GitHub review/run timestamps are second-granularity. Equality can mean the review\n # arrived later within the same second, so accept equality for exact-head evidence;\n # older seconds remain ineligible and all PR/head/state checks above still fail closed.\n new_evidence="$(jq -nr --arg review "$review_submitted_at" --arg started "$REQUIRED_RUN_STARTED_AT" 'try (($review | fromdateiso8601) >= ($started | fromdateiso8601)) catch false')"''' - if text.count(old_time) != 1: - raise SystemExit('review timestamp comparison drifted; refusing ambiguous repair') - scheduler.write_text(text.replace(old_time, new_time, 1), encoding='utf-8') - - workflow = Path('.github/workflows/opencode-review.yml') - wtext = workflow.read_text(encoding='utf-8') - old_comment = ''' # `converted_to_draft` is included so a PR going draft mid-poll fires a - # fresh run of this same workflow: the head-scoped concurrency group below - # (`cancel-in-progress: true`) cancels any in-flight non-draft - # "Fail closed without a current-head OpenCode verdict" poll for that - # exact same head. Every non-closed admission path revalidates the live - # PR/head/state before dispatching, exempting, or polling so out-of-order - # draft/ready/closed events cannot publish stale evidence or wait on an - # impossible verdict.''' - new_comment = ''' # `converted_to_draft` is included so a PR going draft after a ready event - # gets a fresh same-head execution. The head-scoped concurrency group below - # (`cancel-in-progress: true`) retires any superseded same-head admission run. - # Every non-closed path revalidates the live PR/head/state before dispatch, - # exemption, or one-shot verdict admission, so out-of-order draft/ready/closed - # events cannot publish stale evidence. Required verdict continuation is - # event-driven by the authenticated exact-run wake path; this entrypoint does - # not hold a runner in a polling loop.''' - if wtext.count(old_comment) != 1: - raise SystemExit('OpenCode trigger comment drifted; refusing ambiguous repair') - workflow.write_text(wtext.replace(old_comment, new_comment, 1), encoding='utf-8') - - test = Path('tests/test_opencode_required_verdict_reconciliation_contract.py') - if test.exists(): - raise SystemExit('formal-verdict regression path already exists; refusing overwrite') - test.write_text(r'''"""Executable regressions for event-driven OpenCode required-verdict reconciliation.""" - -from __future__ import annotations - -import json -from pathlib import Path -import subprocess -import textwrap - - -WORKFLOW = Path(".github/workflows/pr-review-merge-scheduler.yml") -HEAD = "a" * 40 - - -def _jq_filter() -> str: - """Extract the exact jq review selector used by the production reconciler.""" - text = WORKFLOW.read_text(encoding="utf-8") - anchor = text.index('latest_review="$(printf') - start = text.index(" (add // [])", anchor) - end_marker = " | @tsv" - end = text.index(end_marker, start) + len(end_marker) - return textwrap.dedent(text[start:end]) - - -def _select(reviews: list[dict[str, object]]) -> str: - """Execute the production jq selector against a synthetic exact-head review list.""" - proc = subprocess.run( - ["jq", "-r", "-s", "--arg", "sha", HEAD, _jq_filter()], - input=json.dumps(reviews), - text=True, - capture_output=True, - check=True, - ) - return proc.stdout.strip() - - -def _review(state: str, body: str, submitted_at: str = "2026-09-02T12:00:00Z") -> dict[str, object]: - """Build one exact-head OpenCode review fixture.""" - return { - "id": 42, - "user": {"login": "opencode-agent[bot]"}, - "commit_id": HEAD, - "state": state, - "body": body, - "submitted_at": submitted_at, - } - - -def test_marker_bearing_change_request_remains_a_formal_verdict() -> None: - """Fallback markers invalidate approvals only, never a real change request.""" - result = _select([_review("CHANGES_REQUESTED", "deterministic fallback approval: defect remains")]) - assert result.startswith("CHANGES_REQUESTED\t") - - -def test_marker_bearing_approval_is_not_admitted() -> None: - """Synthetic/fallback approval markers must still block APPROVED evidence.""" - assert _select([_review("APPROVED", "deterministic fallback approval")]) == "" - - -def test_clean_approval_is_admitted() -> None: - """A clean exact-head approval remains a formal verdict.""" - result = _select([_review("APPROVED", "real model review")]) - assert result.startswith("APPROVED\t") - - -def test_same_second_review_is_eligible_for_reconciliation() -> None: - """Second-granularity timestamps must not strand a later review in the same second.""" - text = WORKFLOW.read_text(encoding="utf-8") - assert "fromdateiso8601) >= ($started | fromdateiso8601" in text - proc = subprocess.run( - ["jq", "-nr", "--arg", "review", "2026-09-02T12:00:00Z", "--arg", "started", "2026-09-02T12:00:00Z", "try (($review | fromdateiso8601) >= ($started | fromdateiso8601)) catch false"], - text=True, - capture_output=True, - check=True, - ) - assert proc.stdout.strip() == "true" - - -if __name__ == "__main__": - test_marker_bearing_change_request_remains_a_formal_verdict() - test_marker_bearing_approval_is_not_admitted() - test_clean_approval_is_admitted() - test_same_second_review_is_eligible_for_reconciliation() -''', encoding='utf-8') - - baseline = Path('docs/product-technical-gap-baseline.md') - btext = baseline.read_text(encoding='utf-8') - record = '''\n### OpenCode required-verdict reconciliation race repair — 2026-09-02\n\n- **Owner / PR:** `ContextualWisdomLab/.github#1706`.\n- **Root cause:** the workflow-run reconciler applied fallback-approval marker exclusions to `CHANGES_REQUESTED` reviews even though the admission gates accept every exact-head change request, and strict greater-than timestamp ordering could drop a later review that shared GitHub's second-granularity timestamp with the required run start.\n- **Repair:** marker exclusions now apply only to `APPROVED`; exact-head `CHANGES_REQUESTED` remains a formal verdict. Same-second evidence uses `>=` after live open/ready/head revalidation. Polling-era trigger comments were replaced with the event-driven one-shot contract.\n- **Regression:** `tests/test_opencode_required_verdict_reconciliation_contract.py` executes the production jq selector for marker-bearing change requests, rejected fallback approvals, clean approvals, and same-second timestamp ordering.\n- **Status:** Proposed until this exact branch head receives fresh CI/security/review evidence and integrates through ordinary protected admission.\n''' - if '### OpenCode required-verdict reconciliation race repair — 2026-09-02' not in btext: - baseline.write_text(btext.rstrip() + '\n' + record, encoding='utf-8') - PY + PYTHONPATH=. python3 scripts/ci/source_fix_pr1706_formal_verdict.py PYTHONPATH=. python3 tests/test_opencode_required_verdict_reconciliation_contract.py git diff --check - - name: Publish repaired exact-head tree and retire helper + - name: Publish exact repaired tree and retire temporary machinery shell: bash run: | set -euo pipefail rm .github/workflows/_temp_pr1706_reconcile_formal_verdict.yml - git add .github/workflows/opencode-review.yml .github/workflows/pr-review-merge-scheduler.yml tests/test_opencode_required_verdict_reconciliation_contract.py docs/product-technical-gap-baseline.md .github/workflows/_temp_pr1706_reconcile_formal_verdict.yml + rm scripts/ci/source_fix_pr1706_formal_verdict.py + git add .github/workflows/opencode-review.yml .github/workflows/pr-review-merge-scheduler.yml tests/test_opencode_required_verdict_reconciliation_contract.py docs/product-technical-gap-baseline.md .github/workflows/_temp_pr1706_reconcile_formal_verdict.yml scripts/ci/source_fix_pr1706_formal_verdict.py git diff --cached --check git config user.name 'ContextualWisdomLab repair bot' git config user.email 'actions@users.noreply.github.com' From 1c1123ceda8e49b7639f6392cc3b8a5df843a223 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 22:02:16 +0900 Subject: [PATCH 40/59] fix(opencode): unify formal verdict authority repair --- .../ci/source_fix_pr1706_formal_verdict.py | 158 +++++++++++++++--- 1 file changed, 135 insertions(+), 23 deletions(-) diff --git a/scripts/ci/source_fix_pr1706_formal_verdict.py b/scripts/ci/source_fix_pr1706_formal_verdict.py index c37c701ff6..0f1096474e 100644 --- a/scripts/ci/source_fix_pr1706_formal_verdict.py +++ b/scripts/ci/source_fix_pr1706_formal_verdict.py @@ -1,6 +1,6 @@ """One-shot source repair for ContextualWisdomLab/.github PR #1706. -This temporary driver performs exact-string, fail-closed mutations only. The +This temporary driver performs exact-string, fail-closed mutations only. The workflow that invokes it removes this file after the permanent regression has passed, so the production tree retains only the reviewed workflow/test/docs delta. @@ -17,6 +17,8 @@ BASELINE = Path("docs/product-technical-gap-baseline.md") +OLD_AUTHOR = ''' | select((.user.login // "" | ascii_downcase) as $user | $user == "opencode-agent" or $user == "opencode-agent[bot]")''' +NEW_AUTHOR = ''' | select((.user.login // "" | ascii_downcase) as $user | $user == "opencode-agent" or $user == "opencode-agent[bot]" or $user == "github-actions[bot]")''' OLD_FILTER = ''' | select(.state == "APPROVED" or .state == "CHANGES_REQUESTED") | select((.body // "" | ascii_downcase | contains("deterministic current-head evidence")) | not) | select((.body // "" | ascii_downcase | contains("deterministic fallback approval")) | not) @@ -57,6 +59,30 @@ # events cannot publish stale evidence. Required verdict continuation is # event-driven by the authenticated exact-run wake path; this entrypoint does # not hold a runner in a polling loop.''' +OLD_CONCURRENCY_TAIL = ''' # Same-head events (draft<->ready + # transitions, a synchronize retry) still share one group, so + # `converted_to_draft` still cancels an active same-head verdict poll.''' +NEW_CONCURRENCY_TAIL = ''' # Same-head events (draft<->ready + # transitions, a synchronize retry) still share one group, so + # `converted_to_draft` cancels a superseded same-head admission run.''' +OLD_CLEANUP_COMMENT = ''' cancel-superseded-opencode-review-runs: + # Exact-head concurrency protects a newer authoritative run from delayed + # old-head events, while the poll above now revalidates live PR identity on + # every wait iteration so an already-running obsolete poll can self-retire + # without consuming a second runner. This sibling job remains a defense in + # depth for queued/requested old-head runs and for legacy runs created from + # older workflow revisions that lack the in-loop self-retirement check. + # Every cancellation candidate and every cancellation itself is re-verified + # against the live PR head immediately beforehand, so a cleanup run that is + # itself delayed/stale cannot cancel a still-authoritative run.''' +NEW_CLEANUP_COMMENT = ''' cancel-superseded-opencode-review-runs: + # Exact-head concurrency protects a newer authoritative run from delayed + # old-head events. One-shot verdict admission releases its runner immediately; + # this sibling job remains defense in depth for queued/requested old-head runs + # and legacy runs created from older workflow revisions. Every cancellation + # candidate and every cancellation itself is re-verified against the live PR + # head immediately beforehand, so a delayed/stale cleanup run cannot cancel a + # still-authoritative run.''' TEST_CONTENT = r'''"""Executable regressions for event-driven OpenCode verdict reconciliation.""" @@ -67,13 +93,16 @@ import subprocess import textwrap -WORKFLOW = Path(".github/workflows/pr-review-merge-scheduler.yml") +from scripts.ci import opencode_review_receipt_gate + +SCHEDULER = Path(".github/workflows/pr-review-merge-scheduler.yml") +OPENCODE = Path(".github/workflows/opencode-review.yml") HEAD = "a" * 40 -def _jq_filter() -> str: - """Extract the production jq review selector.""" - text = WORKFLOW.read_text(encoding="utf-8") +def _scheduler_jq_filter() -> str: + """Extract the production scheduler review selector.""" + text = SCHEDULER.read_text(encoding="utf-8") anchor = text.index('latest_review="$(printf') start = text.index(" (add // [])", anchor) end_marker = " | @tsv" @@ -81,49 +110,118 @@ def _jq_filter() -> str: return textwrap.dedent(text[start:end]) -def _select(reviews: list[dict[str, object]]) -> str: - """Execute the production selector on exact-head review fixtures.""" +def _admission_jq_filter() -> str: + """Extract the production one-shot admission review selector.""" + text = OPENCODE.read_text(encoding="utf-8") + anchor = text.index('verdict="$(printf') + start = text.index(" (add // [])", anchor) + end_marker = ' then "APPROVED" else empty end' + end = text.index(end_marker, start) + len(end_marker) + return textwrap.dedent(text[start:end]) + + +def _run_filter(filter_text: str, reviews: list[dict[str, object]]) -> str: + """Execute one production jq selector against exact-head fixtures.""" proc = subprocess.run( - ["jq", "-r", "-s", "--arg", "sha", HEAD, _jq_filter()], - input=json.dumps(reviews), text=True, capture_output=True, check=True, + ["jq", "-r", "-s", "--arg", "sha", HEAD, filter_text], + input=json.dumps(reviews), + text=True, + capture_output=True, + check=True, ) return proc.stdout.strip() -def _review(state: str, body: str) -> dict[str, object]: - """Build one exact-head OpenCode review fixture.""" +def _scheduler_select(reviews: list[dict[str, object]]) -> str: + """Execute the scheduler selector.""" + return _run_filter(_scheduler_jq_filter(), reviews) + + +def _admission_select(reviews: list[dict[str, object]]) -> str: + """Execute the one-shot admission selector.""" + return _run_filter(_admission_jq_filter(), reviews) + + +def _review( + state: str, + body: str, + *, + login: str = "opencode-agent[bot]", +) -> dict[str, object]: + """Build one exact-head formal-review fixture.""" return { "id": 42, - "user": {"login": "opencode-agent[bot]"}, + "user": {"login": login}, "commit_id": HEAD, "state": state, - "body": body, + "body": f"## Verdict\n{body}\n\nHead SHA: `{HEAD}`", "submitted_at": "2026-09-02T12:00:00Z", } def test_marker_bearing_change_request_remains_a_formal_verdict() -> None: """Fallback markers invalidate approvals only, never a real change request.""" - assert _select([_review("CHANGES_REQUESTED", "deterministic fallback approval: defect remains")]).startswith("CHANGES_REQUESTED\t") + review = _review("CHANGES_REQUESTED", "deterministic fallback approval: defect remains") + assert _scheduler_select([review]).startswith("CHANGES_REQUESTED\t") + assert _admission_select([review]) == "CHANGES_REQUESTED" + assert opencode_review_receipt_gate.is_formal_receipt( + review, HEAD, is_draft=False + )[0] def test_marker_bearing_approval_is_not_admitted() -> None: """Synthetic/fallback approval markers still block APPROVED evidence.""" - assert _select([_review("APPROVED", "deterministic fallback approval")]) == "" + review = _review("APPROVED", "deterministic fallback approval") + assert _scheduler_select([review]) == "" + assert _admission_select([review]) == "" + assert not opencode_review_receipt_gate.is_formal_receipt( + review, HEAD, is_draft=False + )[0] def test_clean_approval_is_admitted() -> None: - """A clean exact-head approval remains a formal verdict.""" - assert _select([_review("APPROVED", "real model review")]).startswith("APPROVED\t") + """A clean exact-head approval remains a formal verdict everywhere.""" + review = _review("APPROVED", "real model review") + assert _scheduler_select([review]).startswith("APPROVED\t") + assert _admission_select([review]) == "APPROVED" + assert opencode_review_receipt_gate.is_formal_receipt( + review, HEAD, is_draft=False + )[0] + + +def test_github_actions_formal_receipt_reconciles_and_admits() -> None: + """Every accepted formal publisher must be accepted by all verdict gates.""" + review = _review( + "CHANGES_REQUESTED", + "current-head defect remains", + login="github-actions[bot]", + ) + assert _scheduler_select([review]).startswith("CHANGES_REQUESTED\t") + assert _admission_select([review]) == "CHANGES_REQUESTED" + assert opencode_review_receipt_gate.is_formal_receipt( + review, HEAD, is_draft=False + )[0] def test_same_second_review_is_eligible_for_reconciliation() -> None: """Second-granularity timestamps must not strand a later same-second review.""" - text = WORKFLOW.read_text(encoding="utf-8") + text = SCHEDULER.read_text(encoding="utf-8") assert "fromdateiso8601) >= ($started | fromdateiso8601" in text proc = subprocess.run( - ["jq", "-nr", "--arg", "review", "2026-09-02T12:00:00Z", "--arg", "started", "2026-09-02T12:00:00Z", "try (($review | fromdateiso8601) >= ($started | fromdateiso8601)) catch false"], - text=True, capture_output=True, check=True, + [ + "jq", + "-nr", + "--arg", + "review", + "2026-09-02T12:00:00Z", + "--arg", + "started", + "2026-09-02T12:00:00Z", + "try (($review | fromdateiso8601) >= ($started | fromdateiso8601)) catch false", + ], + text=True, + capture_output=True, + check=True, ) assert proc.stdout.strip() == "true" ''' @@ -132,9 +230,9 @@ def test_same_second_review_is_eligible_for_reconciliation() -> None: ### OpenCode required-verdict reconciliation race repair — 2026-09-02 - **Owner / PR:** `ContextualWisdomLab/.github#1706`. -- **Root cause:** the workflow-run reconciler applied fallback-approval marker exclusions to `CHANGES_REQUESTED` reviews even though admission accepts every exact-head change request, and strict greater-than timestamp ordering could drop a later review sharing GitHub's second-granularity timestamp with the required run start. -- **Repair:** marker exclusions apply only to `APPROVED`; exact-head `CHANGES_REQUESTED` remains a formal verdict. Same-second evidence uses `>=` after live open/ready/head revalidation. Polling-era trigger comments now describe event-driven one-shot admission. -- **Regression:** `tests/test_opencode_required_verdict_reconciliation_contract.py` executes the production jq selector for marker-bearing change requests, rejected fallback approvals, clean approvals, and same-second ordering. +- **Root cause:** formal-verdict authority drifted across three gates. The workflow-run reconciler applied fallback-approval marker exclusions to `CHANGES_REQUESTED`, scheduler/admission selectors omitted the `github-actions[bot]` publisher already accepted by the canonical receipt gate, and strict greater-than timestamp ordering could drop a later review sharing GitHub's second-granularity timestamp with the required run start. +- **Repair:** the receipt gate, one-shot admission, and completion reconciliation now share the formal publisher set; marker exclusions apply only to `APPROVED`; exact-head `CHANGES_REQUESTED` remains a formal verdict; same-second evidence uses `>=` only after live open/ready/head revalidation; polling-era comments describe event-driven one-shot admission instead. +- **Regression:** `tests/test_opencode_required_verdict_reconciliation_contract.py` executes both production jq selectors and the receipt gate for marker-bearing change requests, rejected fallback approvals, clean approvals, `github-actions[bot]` formal receipts, and same-second ordering. - **Status:** Proposed until this exact branch head receives fresh CI/security/review evidence and integrates through ordinary protected admission. ''' @@ -149,9 +247,23 @@ def replace_once(path: Path, old: str, new: str, label: str) -> None: def main() -> None: """Apply permanent production, regression, and traceability repairs.""" + replace_once(SCHEDULER, OLD_AUTHOR, NEW_AUTHOR, "scheduler formal publisher set") + replace_once(OPENCODE, OLD_AUTHOR, NEW_AUTHOR, "admission formal publisher set") replace_once(SCHEDULER, OLD_FILTER, NEW_FILTER, "formal-verdict filter") replace_once(SCHEDULER, OLD_TIME, NEW_TIME, "review timestamp comparison") replace_once(OPENCODE, OLD_COMMENT, NEW_COMMENT, "OpenCode trigger comment") + replace_once( + OPENCODE, + OLD_CONCURRENCY_TAIL, + NEW_CONCURRENCY_TAIL, + "OpenCode concurrency comment", + ) + replace_once( + OPENCODE, + OLD_CLEANUP_COMMENT, + NEW_CLEANUP_COMMENT, + "OpenCode cleanup comment", + ) if TEST.exists(): raise SystemExit(f"refusing to overwrite existing {TEST}") TEST.write_text(TEST_CONTENT, encoding="utf-8") From 31f20147ee0d6130864bdbfb1761ef9a3e070ce8 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 22:03:04 +0900 Subject: [PATCH 41/59] fix(opencode): make reconciliation repair executable --- .github/workflows/_temp_pr1706_reconcile_formal_verdict.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/_temp_pr1706_reconcile_formal_verdict.yml b/.github/workflows/_temp_pr1706_reconcile_formal_verdict.yml index f7218f960e..163553ed1e 100644 --- a/.github/workflows/_temp_pr1706_reconcile_formal_verdict.yml +++ b/.github/workflows/_temp_pr1706_reconcile_formal_verdict.yml @@ -6,6 +6,7 @@ on: - fix/opencode-poll-wall-clock-bound paths: - .github/workflows/_temp_pr1706_reconcile_formal_verdict.yml + - scripts/ci/source_fix_pr1706_formal_verdict.py permissions: contents: write @@ -20,7 +21,7 @@ jobs: runs-on: ubuntu-24.04 steps: - name: Checkout exact writer head - uses: actions/checkout@v4 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: fetch-depth: 0 - name: Verify live writer head @@ -34,7 +35,7 @@ jobs: run: | set -euo pipefail PYTHONPATH=. python3 scripts/ci/source_fix_pr1706_formal_verdict.py - PYTHONPATH=. python3 tests/test_opencode_required_verdict_reconciliation_contract.py + PYTHONPATH=. python3 -m pytest tests/test_opencode_required_verdict_reconciliation_contract.py git diff --check - name: Publish exact repaired tree and retire temporary machinery shell: bash From e9bda723654ac69678b9af7ad29fd71e48e512f2 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 22:07:11 +0900 Subject: [PATCH 42/59] test(opencode): unify formal verdict authority across reconciliation --- ...ncode_formal_verdict_authority_contract.py | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 tests/test_opencode_formal_verdict_authority_contract.py diff --git a/tests/test_opencode_formal_verdict_authority_contract.py b/tests/test_opencode_formal_verdict_authority_contract.py new file mode 100644 index 0000000000..7bacf11928 --- /dev/null +++ b/tests/test_opencode_formal_verdict_authority_contract.py @@ -0,0 +1,101 @@ +"""Executable contract for one formal OpenCode verdict authority across workflows.""" + +from __future__ import annotations + +import json +from pathlib import Path +import subprocess + +from scripts.ci import opencode_review_receipt_gate as receipt_gate + + +REQUIRED_WORKFLOW_PATH = Path(".github/workflows/opencode-review.yml") +SCHEDULER_WORKFLOW_PATH = Path(".github/workflows/pr-review-merge-scheduler.yml") +HEAD_SHA = "a" * 40 + + +def _embedded_jq_program(workflow_text: str, variable_name: str, sha_variable: str) -> str: + """Extract one tracked jq selector so the test executes production policy text.""" + assignment_marker = f'{variable_name}="$(printf' + assignment_tail = workflow_text.split(assignment_marker, 1)[1] + jq_marker = f'| jq -r -s --arg sha "${sha_variable}" \'\n' + jq_tail = assignment_tail.split(jq_marker, 1)[1] + return jq_tail.split("\n ')\"", 1)[0] + + +def _run_selector(program: str, review: dict[str, object]) -> str: + """Execute a production jq verdict selector against one paginated review page.""" + completed = subprocess.run( + ["jq", "-r", "-s", "--arg", "sha", HEAD_SHA, program], + input=json.dumps([review]), + text=True, + capture_output=True, + check=False, + ) + assert completed.returncode == 0, completed.stderr + return completed.stdout.strip() + + +def _formal_review(author: str, state: str, body: str) -> dict[str, object]: + """Build a current-head formal review fixture shared across policy surfaces.""" + return { + "id": 7, + "user": {"login": author}, + "commit_id": HEAD_SHA, + "state": state, + "submitted_at": "2026-09-02T13:00:01Z", + "body": body, + } + + +def test_github_actions_formal_change_request_matches_all_verdict_surfaces() -> None: + """A publisher accepted by the receipt gate must reconcile and admit the same verdict.""" + review = _formal_review( + "github-actions[bot]", + "CHANGES_REQUESTED", + "## Pull request overview\nmodel-unavailable evidence fallback", + ) + accepted, reason = receipt_gate.is_formal_receipt(review, HEAD_SHA, is_draft=False) + assert accepted, reason + + required_text = REQUIRED_WORKFLOW_PATH.read_text(encoding="utf-8") + scheduler_text = SCHEDULER_WORKFLOW_PATH.read_text(encoding="utf-8") + admission_program = _embedded_jq_program(required_text, "verdict", "HEAD_SHA") + reconciliation_program = _embedded_jq_program( + scheduler_text, "latest_review", "PR_HEAD_SHA" + ) + + assert _run_selector(admission_program, review) == "CHANGES_REQUESTED" + assert _run_selector(reconciliation_program, review).startswith( + "CHANGES_REQUESTED\t" + ) + + +def test_fallback_marker_invalidates_approval_only_not_change_request() -> None: + """Fallback markers reject APPROVED evidence but never erase a real change request.""" + fallback_body = "## Pull request overview\ndeterministic fallback approval" + change_request = _formal_review("opencode-agent[bot]", "CHANGES_REQUESTED", fallback_body) + approval = _formal_review("opencode-agent[bot]", "APPROVED", fallback_body) + + change_ok, change_reason = receipt_gate.is_formal_receipt( + change_request, HEAD_SHA, is_draft=False + ) + approval_ok, _approval_reason = receipt_gate.is_formal_receipt( + approval, HEAD_SHA, is_draft=False + ) + assert change_ok, change_reason + assert not approval_ok + + required_text = REQUIRED_WORKFLOW_PATH.read_text(encoding="utf-8") + scheduler_text = SCHEDULER_WORKFLOW_PATH.read_text(encoding="utf-8") + admission_program = _embedded_jq_program(required_text, "verdict", "HEAD_SHA") + reconciliation_program = _embedded_jq_program( + scheduler_text, "latest_review", "PR_HEAD_SHA" + ) + + assert _run_selector(admission_program, change_request) == "CHANGES_REQUESTED" + assert _run_selector(reconciliation_program, change_request).startswith( + "CHANGES_REQUESTED\t" + ) + assert _run_selector(admission_program, approval) == "" + assert _run_selector(reconciliation_program, approval) == "\t" From c0b5c5755638f73c7923a2e991c4b43a6faa6e86 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 22:10:34 +0900 Subject: [PATCH 43/59] fix(opencode): verify canonical formal-verdict regressions --- .../workflows/_temp_pr1706_reconcile_formal_verdict.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/_temp_pr1706_reconcile_formal_verdict.yml b/.github/workflows/_temp_pr1706_reconcile_formal_verdict.yml index 163553ed1e..357a427916 100644 --- a/.github/workflows/_temp_pr1706_reconcile_formal_verdict.yml +++ b/.github/workflows/_temp_pr1706_reconcile_formal_verdict.yml @@ -7,6 +7,7 @@ on: paths: - .github/workflows/_temp_pr1706_reconcile_formal_verdict.yml - scripts/ci/source_fix_pr1706_formal_verdict.py + - tests/test_opencode_formal_verdict_authority_contract.py permissions: contents: write @@ -30,12 +31,14 @@ jobs: set -euo pipefail git fetch origin fix/opencode-poll-wall-clock-bound test "$(git rev-parse origin/fix/opencode-poll-wall-clock-bound)" = "$GITHUB_SHA" - - name: Apply owner repair and permanent regression + - name: Apply owner repair and permanent regressions shell: bash run: | set -euo pipefail PYTHONPATH=. python3 scripts/ci/source_fix_pr1706_formal_verdict.py - PYTHONPATH=. python3 -m pytest tests/test_opencode_required_verdict_reconciliation_contract.py + PYTHONPATH=. python3 -m pytest \ + tests/test_opencode_formal_verdict_authority_contract.py \ + tests/test_opencode_required_verdict_reconciliation_contract.py git diff --check - name: Publish exact repaired tree and retire temporary machinery shell: bash @@ -43,7 +46,7 @@ jobs: set -euo pipefail rm .github/workflows/_temp_pr1706_reconcile_formal_verdict.yml rm scripts/ci/source_fix_pr1706_formal_verdict.py - git add .github/workflows/opencode-review.yml .github/workflows/pr-review-merge-scheduler.yml tests/test_opencode_required_verdict_reconciliation_contract.py docs/product-technical-gap-baseline.md .github/workflows/_temp_pr1706_reconcile_formal_verdict.yml scripts/ci/source_fix_pr1706_formal_verdict.py + git add .github/workflows/opencode-review.yml .github/workflows/pr-review-merge-scheduler.yml tests/test_opencode_formal_verdict_authority_contract.py tests/test_opencode_required_verdict_reconciliation_contract.py docs/product-technical-gap-baseline.md .github/workflows/_temp_pr1706_reconcile_formal_verdict.yml scripts/ci/source_fix_pr1706_formal_verdict.py git diff --cached --check git config user.name 'ContextualWisdomLab repair bot' git config user.email 'actions@users.noreply.github.com' From d73a76d84651c26f49e7ec12d426ff62dcc9989e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 22:54:59 +0900 Subject: [PATCH 44/59] fix(ci): harden PR 1706 repair publication --- .../_temp_pr1706_reconcile_formal_verdict.yml | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/.github/workflows/_temp_pr1706_reconcile_formal_verdict.yml b/.github/workflows/_temp_pr1706_reconcile_formal_verdict.yml index 357a427916..67161eb2c7 100644 --- a/.github/workflows/_temp_pr1706_reconcile_formal_verdict.yml +++ b/.github/workflows/_temp_pr1706_reconcile_formal_verdict.yml @@ -10,7 +10,7 @@ on: - tests/test_opencode_formal_verdict_authority_contract.py permissions: - contents: write + contents: read concurrency: group: temp-pr1706-formal-verdict-repair @@ -25,6 +25,7 @@ jobs: uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: fetch-depth: 0 + persist-credentials: false - name: Verify live writer head shell: bash run: | @@ -42,15 +43,44 @@ jobs: git diff --check - name: Publish exact repaired tree and retire temporary machinery shell: bash + env: + PUSH_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN }} run: | set -euo pipefail + if [ -z "${PUSH_TOKEN:-}" ]; then + echo "::error::A workflow-starting publication credential is required; github.token is intentionally not accepted." + exit 1 + fi + rm .github/workflows/_temp_pr1706_reconcile_formal_verdict.yml rm scripts/ci/source_fix_pr1706_formal_verdict.py + if find .github/workflows scripts/ci -type f \( -name '*temp_pr1706*' -o -name '*source_fix_pr1706*' \) -print -quit | grep -q .; then + echo "::error::PR 1706 temporary repair machinery remains in the candidate tree." + find .github/workflows scripts/ci -type f \( -name '*temp_pr1706*' -o -name '*source_fix_pr1706*' \) -print + exit 1 + fi + git add .github/workflows/opencode-review.yml .github/workflows/pr-review-merge-scheduler.yml tests/test_opencode_formal_verdict_authority_contract.py tests/test_opencode_required_verdict_reconciliation_contract.py docs/product-technical-gap-baseline.md .github/workflows/_temp_pr1706_reconcile_formal_verdict.yml scripts/ci/source_fix_pr1706_formal_verdict.py git diff --cached --check git config user.name 'ContextualWisdomLab repair bot' git config user.email 'actions@users.noreply.github.com' git commit -m 'fix(opencode): reconcile exact-head formal verdicts' + git fetch origin fix/opencode-poll-wall-clock-bound test "$(git rev-parse origin/fix/opencode-poll-wall-clock-bound)" = "$GITHUB_SHA" - git push origin HEAD:fix/opencode-poll-wall-clock-bound + + askpass="$(mktemp)" + trap 'rm -f "$askpass"' EXIT + cat >"$askpass" <<'SH' + #!/bin/sh + case "$1" in + *Username*) printf '%s\n' "$GIT_USERNAME" ;; + *) printf '%s\n' "$GIT_PASSWORD" ;; + esac + SH + chmod 700 "$askpass" + GIT_ASKPASS="$askpass" \ + GIT_TERMINAL_PROMPT=0 \ + GIT_USERNAME=x-access-token \ + GIT_PASSWORD="$PUSH_TOKEN" \ + git -c credential.helper= push origin HEAD:fix/opencode-poll-wall-clock-bound From b04939868b1e4d68b2da8bac57a0b7038152913a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 22:55:34 +0900 Subject: [PATCH 45/59] test(opencode): fix stripped empty-verdict oracle --- tests/test_opencode_formal_verdict_authority_contract.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/test_opencode_formal_verdict_authority_contract.py b/tests/test_opencode_formal_verdict_authority_contract.py index 7bacf11928..f81f653d60 100644 --- a/tests/test_opencode_formal_verdict_authority_contract.py +++ b/tests/test_opencode_formal_verdict_authority_contract.py @@ -98,4 +98,8 @@ def test_fallback_marker_invalidates_approval_only_not_change_request() -> None: "CHANGES_REQUESTED\t" ) assert _run_selector(admission_program, approval) == "" - assert _run_selector(reconciliation_program, approval) == "\t" + # _run_selector intentionally strips jq's trailing whitespace, so an empty + # scheduler state/timestamp tuple is observed as the empty string rather + # than a literal tab. This keeps the oracle causal instead of depending on + # incidental transport whitespace. + assert _run_selector(reconciliation_program, approval) == "" From c6b4e42230964f05a4948b122a504def30a2d25c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 23:05:28 +0900 Subject: [PATCH 46/59] test(opencode): cover formal verdict publisher and timestamp races --- ...ncode_formal_verdict_authority_contract.py | 77 +++++++++++++++---- 1 file changed, 63 insertions(+), 14 deletions(-) diff --git a/tests/test_opencode_formal_verdict_authority_contract.py b/tests/test_opencode_formal_verdict_authority_contract.py index f81f653d60..81235824e1 100644 --- a/tests/test_opencode_formal_verdict_authority_contract.py +++ b/tests/test_opencode_formal_verdict_authority_contract.py @@ -48,6 +48,17 @@ def _formal_review(author: str, state: str, body: str) -> dict[str, object]: } +def _policy_programs() -> tuple[str, str, str]: + """Return direct admission, scheduler reconciliation, and scheduler workflow text.""" + required_text = REQUIRED_WORKFLOW_PATH.read_text(encoding="utf-8") + scheduler_text = SCHEDULER_WORKFLOW_PATH.read_text(encoding="utf-8") + admission_program = _embedded_jq_program(required_text, "verdict", "HEAD_SHA") + reconciliation_program = _embedded_jq_program( + scheduler_text, "latest_review", "PR_HEAD_SHA" + ) + return admission_program, reconciliation_program, scheduler_text + + def test_github_actions_formal_change_request_matches_all_verdict_surfaces() -> None: """A publisher accepted by the receipt gate must reconcile and admit the same verdict.""" review = _formal_review( @@ -58,19 +69,63 @@ def test_github_actions_formal_change_request_matches_all_verdict_surfaces() -> accepted, reason = receipt_gate.is_formal_receipt(review, HEAD_SHA, is_draft=False) assert accepted, reason - required_text = REQUIRED_WORKFLOW_PATH.read_text(encoding="utf-8") - scheduler_text = SCHEDULER_WORKFLOW_PATH.read_text(encoding="utf-8") - admission_program = _embedded_jq_program(required_text, "verdict", "HEAD_SHA") - reconciliation_program = _embedded_jq_program( - scheduler_text, "latest_review", "PR_HEAD_SHA" - ) - + admission_program, reconciliation_program, _scheduler_text = _policy_programs() assert _run_selector(admission_program, review) == "CHANGES_REQUESTED" assert _run_selector(reconciliation_program, review).startswith( "CHANGES_REQUESTED\t" ) +def test_github_actions_formal_approval_requires_full_evidence_markers() -> None: + """APPROVED evidence has one publisher/marker policy on receipt, wake, and admission.""" + complete = _formal_review( + "github-actions[bot]", + "APPROVED", + "**OpenCode automated review**\n\n**Evidence recap**\nvalidated exact head", + ) + incomplete = _formal_review( + "github-actions[bot]", + "APPROVED", + "**OpenCode automated review**\nmissing evidence recap marker", + ) + complete_ok, complete_reason = receipt_gate.is_formal_receipt( + complete, HEAD_SHA, is_draft=False + ) + incomplete_ok, _incomplete_reason = receipt_gate.is_formal_receipt( + incomplete, HEAD_SHA, is_draft=False + ) + assert complete_ok, complete_reason + assert not incomplete_ok + + admission_program, reconciliation_program, _scheduler_text = _policy_programs() + assert _run_selector(admission_program, complete) == "APPROVED" + assert _run_selector(reconciliation_program, complete).startswith("APPROVED\t") + assert _run_selector(admission_program, incomplete) == "" + assert _run_selector(reconciliation_program, incomplete) == "" + + +def test_unrecognized_reviewer_cannot_admit_or_wake_required_verdict() -> None: + """Human or unrelated-bot reviews must not become OpenCode admission authority.""" + review = _formal_review( + "unrelated-reviewer", + "CHANGES_REQUESTED", + "**OpenCode automated review**\n**Evidence recap**", + ) + accepted, _reason = receipt_gate.is_formal_receipt(review, HEAD_SHA, is_draft=False) + assert not accepted + + admission_program, reconciliation_program, _scheduler_text = _policy_programs() + assert _run_selector(admission_program, review) == "" + assert _run_selector(reconciliation_program, review) == "" + + +def test_scheduler_accepts_same_second_formal_receipt_for_failed_run() -> None: + """Second-precision GitHub timestamps must not lose a receipt concurrent with run start.""" + _admission_program, _reconciliation_program, scheduler_text = _policy_programs() + assert "($review | fromdateiso8601) >= ($started | fromdateiso8601)" in scheduler_text + assert "($review | fromdateiso8601) > ($started | fromdateiso8601)" not in scheduler_text + + def test_fallback_marker_invalidates_approval_only_not_change_request() -> None: """Fallback markers reject APPROVED evidence but never erase a real change request.""" fallback_body = "## Pull request overview\ndeterministic fallback approval" @@ -86,13 +141,7 @@ def test_fallback_marker_invalidates_approval_only_not_change_request() -> None: assert change_ok, change_reason assert not approval_ok - required_text = REQUIRED_WORKFLOW_PATH.read_text(encoding="utf-8") - scheduler_text = SCHEDULER_WORKFLOW_PATH.read_text(encoding="utf-8") - admission_program = _embedded_jq_program(required_text, "verdict", "HEAD_SHA") - reconciliation_program = _embedded_jq_program( - scheduler_text, "latest_review", "PR_HEAD_SHA" - ) - + admission_program, reconciliation_program, _scheduler_text = _policy_programs() assert _run_selector(admission_program, change_request) == "CHANGES_REQUESTED" assert _run_selector(reconciliation_program, change_request).startswith( "CHANGES_REQUESTED\t" From ca18858dd7f053667a83a58047b20c63535c583f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 23:07:07 +0900 Subject: [PATCH 47/59] test(opencode): align formal verdict contract with receipt authority --- ...ncode_formal_verdict_authority_contract.py | 31 ++++++------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/tests/test_opencode_formal_verdict_authority_contract.py b/tests/test_opencode_formal_verdict_authority_contract.py index 81235824e1..6c7aa22702 100644 --- a/tests/test_opencode_formal_verdict_authority_contract.py +++ b/tests/test_opencode_formal_verdict_authority_contract.py @@ -76,32 +76,19 @@ def test_github_actions_formal_change_request_matches_all_verdict_surfaces() -> ) -def test_github_actions_formal_approval_requires_full_evidence_markers() -> None: - """APPROVED evidence has one publisher/marker policy on receipt, wake, and admission.""" - complete = _formal_review( - "github-actions[bot]", - "APPROVED", - "**OpenCode automated review**\n\n**Evidence recap**\nvalidated exact head", - ) - incomplete = _formal_review( +def test_github_actions_formal_approval_matches_all_verdict_surfaces() -> None: + """A formal publisher approval accepted by the receipt gate must wake and admit.""" + review = _formal_review( "github-actions[bot]", "APPROVED", - "**OpenCode automated review**\nmissing evidence recap marker", + "## Pull request overview\nvalidated exact-head product diff", ) - complete_ok, complete_reason = receipt_gate.is_formal_receipt( - complete, HEAD_SHA, is_draft=False - ) - incomplete_ok, _incomplete_reason = receipt_gate.is_formal_receipt( - incomplete, HEAD_SHA, is_draft=False - ) - assert complete_ok, complete_reason - assert not incomplete_ok + accepted, reason = receipt_gate.is_formal_receipt(review, HEAD_SHA, is_draft=False) + assert accepted, reason admission_program, reconciliation_program, _scheduler_text = _policy_programs() - assert _run_selector(admission_program, complete) == "APPROVED" - assert _run_selector(reconciliation_program, complete).startswith("APPROVED\t") - assert _run_selector(admission_program, incomplete) == "" - assert _run_selector(reconciliation_program, incomplete) == "" + assert _run_selector(admission_program, review) == "APPROVED" + assert _run_selector(reconciliation_program, review).startswith("APPROVED\t") def test_unrecognized_reviewer_cannot_admit_or_wake_required_verdict() -> None: @@ -109,7 +96,7 @@ def test_unrecognized_reviewer_cannot_admit_or_wake_required_verdict() -> None: review = _formal_review( "unrelated-reviewer", "CHANGES_REQUESTED", - "**OpenCode automated review**\n**Evidence recap**", + "## Pull request overview\nsubstantive review", ) accepted, _reason = receipt_gate.is_formal_receipt(review, HEAD_SHA, is_draft=False) assert not accepted From af1b73802a355e394dd09ab5af779c28125b49dd Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 23:15:56 +0900 Subject: [PATCH 48/59] ci(opencode): trigger exact repair from PR synchronization --- .../_temp_pr1706_reconcile_formal_verdict.yml | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/.github/workflows/_temp_pr1706_reconcile_formal_verdict.yml b/.github/workflows/_temp_pr1706_reconcile_formal_verdict.yml index 67161eb2c7..267f27c6dd 100644 --- a/.github/workflows/_temp_pr1706_reconcile_formal_verdict.yml +++ b/.github/workflows/_temp_pr1706_reconcile_formal_verdict.yml @@ -8,6 +8,17 @@ on: - .github/workflows/_temp_pr1706_reconcile_formal_verdict.yml - scripts/ci/source_fix_pr1706_formal_verdict.py - tests/test_opencode_formal_verdict_authority_contract.py + pull_request: + branches: + - main + types: + - opened + - synchronize + - reopened + paths: + - .github/workflows/_temp_pr1706_reconcile_formal_verdict.yml + - scripts/ci/source_fix_pr1706_formal_verdict.py + - tests/test_opencode_formal_verdict_authority_contract.py permissions: contents: read @@ -20,18 +31,21 @@ jobs: repair: if: github.actor_id == '8172694' runs-on: ubuntu-24.04 + env: + EXPECTED_HEAD: ${{ github.event.pull_request.head.sha || github.sha }} steps: - name: Checkout exact writer head uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: fetch-depth: 0 persist-credentials: false + ref: ${{ github.event.pull_request.head.sha || github.sha }} - name: Verify live writer head shell: bash run: | set -euo pipefail git fetch origin fix/opencode-poll-wall-clock-bound - test "$(git rev-parse origin/fix/opencode-poll-wall-clock-bound)" = "$GITHUB_SHA" + test "$(git rev-parse origin/fix/opencode-poll-wall-clock-bound)" = "$EXPECTED_HEAD" - name: Apply owner repair and permanent regressions shell: bash run: | @@ -67,7 +81,7 @@ jobs: git commit -m 'fix(opencode): reconcile exact-head formal verdicts' git fetch origin fix/opencode-poll-wall-clock-bound - test "$(git rev-parse origin/fix/opencode-poll-wall-clock-bound)" = "$GITHUB_SHA" + test "$(git rev-parse origin/fix/opencode-poll-wall-clock-bound)" = "$EXPECTED_HEAD" askpass="$(mktemp)" trap 'rm -f "$askpass"' EXIT From b22b2a72fe2e19388a6f6f4408a0687eb30047fa Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 23:27:34 +0900 Subject: [PATCH 49/59] chore(opencode): retire unsafe PR1706 repair workflow --- .../_temp_pr1706_reconcile_formal_verdict.yml | 100 ------------------ 1 file changed, 100 deletions(-) delete mode 100644 .github/workflows/_temp_pr1706_reconcile_formal_verdict.yml diff --git a/.github/workflows/_temp_pr1706_reconcile_formal_verdict.yml b/.github/workflows/_temp_pr1706_reconcile_formal_verdict.yml deleted file mode 100644 index 267f27c6dd..0000000000 --- a/.github/workflows/_temp_pr1706_reconcile_formal_verdict.yml +++ /dev/null @@ -1,100 +0,0 @@ -name: Temporary PR 1706 formal-verdict reconciliation repair - -on: - push: - branches: - - fix/opencode-poll-wall-clock-bound - paths: - - .github/workflows/_temp_pr1706_reconcile_formal_verdict.yml - - scripts/ci/source_fix_pr1706_formal_verdict.py - - tests/test_opencode_formal_verdict_authority_contract.py - pull_request: - branches: - - main - types: - - opened - - synchronize - - reopened - paths: - - .github/workflows/_temp_pr1706_reconcile_formal_verdict.yml - - scripts/ci/source_fix_pr1706_formal_verdict.py - - tests/test_opencode_formal_verdict_authority_contract.py - -permissions: - contents: read - -concurrency: - group: temp-pr1706-formal-verdict-repair - cancel-in-progress: true - -jobs: - repair: - if: github.actor_id == '8172694' - runs-on: ubuntu-24.04 - env: - EXPECTED_HEAD: ${{ github.event.pull_request.head.sha || github.sha }} - steps: - - name: Checkout exact writer head - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - with: - fetch-depth: 0 - persist-credentials: false - ref: ${{ github.event.pull_request.head.sha || github.sha }} - - name: Verify live writer head - shell: bash - run: | - set -euo pipefail - git fetch origin fix/opencode-poll-wall-clock-bound - test "$(git rev-parse origin/fix/opencode-poll-wall-clock-bound)" = "$EXPECTED_HEAD" - - name: Apply owner repair and permanent regressions - shell: bash - run: | - set -euo pipefail - PYTHONPATH=. python3 scripts/ci/source_fix_pr1706_formal_verdict.py - PYTHONPATH=. python3 -m pytest \ - tests/test_opencode_formal_verdict_authority_contract.py \ - tests/test_opencode_required_verdict_reconciliation_contract.py - git diff --check - - name: Publish exact repaired tree and retire temporary machinery - shell: bash - env: - PUSH_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN }} - run: | - set -euo pipefail - if [ -z "${PUSH_TOKEN:-}" ]; then - echo "::error::A workflow-starting publication credential is required; github.token is intentionally not accepted." - exit 1 - fi - - rm .github/workflows/_temp_pr1706_reconcile_formal_verdict.yml - rm scripts/ci/source_fix_pr1706_formal_verdict.py - if find .github/workflows scripts/ci -type f \( -name '*temp_pr1706*' -o -name '*source_fix_pr1706*' \) -print -quit | grep -q .; then - echo "::error::PR 1706 temporary repair machinery remains in the candidate tree." - find .github/workflows scripts/ci -type f \( -name '*temp_pr1706*' -o -name '*source_fix_pr1706*' \) -print - exit 1 - fi - - git add .github/workflows/opencode-review.yml .github/workflows/pr-review-merge-scheduler.yml tests/test_opencode_formal_verdict_authority_contract.py tests/test_opencode_required_verdict_reconciliation_contract.py docs/product-technical-gap-baseline.md .github/workflows/_temp_pr1706_reconcile_formal_verdict.yml scripts/ci/source_fix_pr1706_formal_verdict.py - git diff --cached --check - git config user.name 'ContextualWisdomLab repair bot' - git config user.email 'actions@users.noreply.github.com' - git commit -m 'fix(opencode): reconcile exact-head formal verdicts' - - git fetch origin fix/opencode-poll-wall-clock-bound - test "$(git rev-parse origin/fix/opencode-poll-wall-clock-bound)" = "$EXPECTED_HEAD" - - askpass="$(mktemp)" - trap 'rm -f "$askpass"' EXIT - cat >"$askpass" <<'SH' - #!/bin/sh - case "$1" in - *Username*) printf '%s\n' "$GIT_USERNAME" ;; - *) printf '%s\n' "$GIT_PASSWORD" ;; - esac - SH - chmod 700 "$askpass" - GIT_ASKPASS="$askpass" \ - GIT_TERMINAL_PROMPT=0 \ - GIT_USERNAME=x-access-token \ - GIT_PASSWORD="$PUSH_TOKEN" \ - git -c credential.helper= push origin HEAD:fix/opencode-poll-wall-clock-bound From a3653629149399e1af071c72455d98cb7f0e3e6a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 23:29:48 +0900 Subject: [PATCH 50/59] fix(opencode): run formal-verdict owner repair on slim runner --- ...emp_pr1706_reconcile_formal_verdict_v2.yml | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 .github/workflows/_temp_pr1706_reconcile_formal_verdict_v2.yml diff --git a/.github/workflows/_temp_pr1706_reconcile_formal_verdict_v2.yml b/.github/workflows/_temp_pr1706_reconcile_formal_verdict_v2.yml new file mode 100644 index 0000000000..ade0272c23 --- /dev/null +++ b/.github/workflows/_temp_pr1706_reconcile_formal_verdict_v2.yml @@ -0,0 +1,66 @@ +name: Temporary PR 1706 formal-verdict reconciliation repair + +on: + push: + branches: + - fix/opencode-poll-wall-clock-bound + paths: + - .github/workflows/_temp_pr1706_reconcile_formal_verdict_v2.yml + +permissions: + contents: read + +concurrency: + group: temp-pr1706-formal-verdict-repair + cancel-in-progress: true + +jobs: + repair: + if: github.repository == 'ContextualWisdomLab/.github' && github.ref == 'refs/heads/fix/opencode-poll-wall-clock-bound' + runs-on: ubuntu-slim + env: + GH_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 + persist-credentials: false + - name: Verify exact writer head and publication credential + shell: bash + run: | + set -euo pipefail + test -n "${GH_TOKEN:-}" || { echo 'workflow-starting mutation credential is required' >&2; exit 1; } + git fetch origin fix/opencode-poll-wall-clock-bound + test "$(git rev-parse origin/fix/opencode-poll-wall-clock-bound)" = "$GITHUB_SHA" + - name: Apply permanent formal-verdict repair + shell: bash + run: | + set -euo pipefail + PYTHONPATH=. python3 scripts/ci/source_fix_pr1706_formal_verdict.py + PYTHONPATH=. python3 -m pytest -q \ + tests/test_opencode_required_verdict_reconciliation_contract.py \ + tests/test_opencode_required_verdict_runner_release.py \ + tests/test_opencode_event_driven_required_wake.py \ + tests/test_opencode_formal_verdict_authority_contract.py + git diff --check + - name: Publish repaired exact-head tree and retire temporary machinery + shell: bash + run: | + set -euo pipefail + rm scripts/ci/source_fix_pr1706_formal_verdict.py + rm .github/workflows/_temp_pr1706_reconcile_formal_verdict_v2.yml + git add \ + .github/workflows/opencode-review.yml \ + .github/workflows/pr-review-merge-scheduler.yml \ + docs/product-technical-gap-baseline.md \ + tests/test_opencode_required_verdict_reconciliation_contract.py \ + scripts/ci/source_fix_pr1706_formal_verdict.py \ + .github/workflows/_temp_pr1706_reconcile_formal_verdict_v2.yml + git diff --cached --check + git config user.name 'ContextualWisdomLab repair bot' + git config user.email 'actions@users.noreply.github.com' + git commit -m 'fix(opencode): reconcile exact-head formal verdict authority' + git fetch origin fix/opencode-poll-wall-clock-bound + test "$(git rev-parse origin/fix/opencode-poll-wall-clock-bound)" = "$GITHUB_SHA" + auth_header="Basic $(printf 'x-access-token:%s' "$GH_TOKEN" | base64 | tr -d '\n')" + git -c http.extraheader="AUTHORIZATION: $auth_header" push origin HEAD:fix/opencode-poll-wall-clock-bound From 57e242fc5c3fbe162fc2e29a4dee1800b4a5270b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 23:30:02 +0900 Subject: [PATCH 51/59] test(opencode): reproduce formal-verdict reconciliation drift --- ...equired_verdict_reconciliation_contract.py | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 tests/test_opencode_required_verdict_reconciliation_contract.py diff --git a/tests/test_opencode_required_verdict_reconciliation_contract.py b/tests/test_opencode_required_verdict_reconciliation_contract.py new file mode 100644 index 0000000000..a7bdae103c --- /dev/null +++ b/tests/test_opencode_required_verdict_reconciliation_contract.py @@ -0,0 +1,140 @@ +"""Executable regressions for event-driven OpenCode verdict reconciliation.""" + +from __future__ import annotations + +import json +from pathlib import Path +import subprocess +import textwrap + +from scripts.ci import opencode_review_receipt_gate + +SCHEDULER = Path(".github/workflows/pr-review-merge-scheduler.yml") +OPENCODE = Path(".github/workflows/opencode-review.yml") +HEAD = "a" * 40 + + +def _scheduler_jq_filter() -> str: + """Extract the production scheduler review selector.""" + text = SCHEDULER.read_text(encoding="utf-8") + anchor = text.index('latest_review="$(printf') + start = text.index(" (add // [])", anchor) + end_marker = " | @tsv" + end = text.index(end_marker, start) + len(end_marker) + return textwrap.dedent(text[start:end]) + + +def _admission_jq_filter() -> str: + """Extract the production one-shot admission review selector.""" + text = OPENCODE.read_text(encoding="utf-8") + anchor = text.index('verdict="$(printf') + start = text.index(" (add // [])", anchor) + end_marker = ' then "APPROVED" else empty end' + end = text.index(end_marker, start) + len(end_marker) + return textwrap.dedent(text[start:end]) + + +def _run_filter(filter_text: str, reviews: list[dict[str, object]]) -> str: + """Execute one production jq selector against exact-head fixtures.""" + proc = subprocess.run( + ["jq", "-r", "-s", "--arg", "sha", HEAD, filter_text], + input=json.dumps(reviews), + text=True, + capture_output=True, + check=True, + ) + return proc.stdout.strip() + + +def _scheduler_select(reviews: list[dict[str, object]]) -> str: + """Execute the scheduler selector.""" + return _run_filter(_scheduler_jq_filter(), reviews) + + +def _admission_select(reviews: list[dict[str, object]]) -> str: + """Execute the one-shot admission selector.""" + return _run_filter(_admission_jq_filter(), reviews) + + +def _review( + state: str, + body: str, + *, + login: str = "opencode-agent[bot]", +) -> dict[str, object]: + """Build one exact-head formal-review fixture.""" + return { + "id": 42, + "user": {"login": login}, + "commit_id": HEAD, + "state": state, + "body": f"## Verdict\n{body}\n\nHead SHA: `{HEAD}`", + "submitted_at": "2026-09-02T12:00:00Z", + } + + +def test_marker_bearing_change_request_remains_a_formal_verdict() -> None: + """Fallback markers invalidate approvals only, never a real change request.""" + review = _review("CHANGES_REQUESTED", "deterministic fallback approval: defect remains") + assert _scheduler_select([review]).startswith("CHANGES_REQUESTED\t") + assert _admission_select([review]) == "CHANGES_REQUESTED" + assert opencode_review_receipt_gate.is_formal_receipt( + review, HEAD, is_draft=False + )[0] + + +def test_marker_bearing_approval_is_not_admitted() -> None: + """Synthetic/fallback approval markers still block APPROVED evidence.""" + review = _review("APPROVED", "deterministic fallback approval") + assert _scheduler_select([review]) == "" + assert _admission_select([review]) == "" + assert not opencode_review_receipt_gate.is_formal_receipt( + review, HEAD, is_draft=False + )[0] + + +def test_clean_approval_is_admitted() -> None: + """A clean exact-head approval remains a formal verdict everywhere.""" + review = _review("APPROVED", "real model review") + assert _scheduler_select([review]).startswith("APPROVED\t") + assert _admission_select([review]) == "APPROVED" + assert opencode_review_receipt_gate.is_formal_receipt( + review, HEAD, is_draft=False + )[0] + + +def test_github_actions_formal_receipt_reconciles_and_admits() -> None: + """Every accepted formal publisher must be accepted by all verdict gates.""" + review = _review( + "CHANGES_REQUESTED", + "current-head defect remains", + login="github-actions[bot]", + ) + assert _scheduler_select([review]).startswith("CHANGES_REQUESTED\t") + assert _admission_select([review]) == "CHANGES_REQUESTED" + assert opencode_review_receipt_gate.is_formal_receipt( + review, HEAD, is_draft=False + )[0] + + +def test_same_second_review_is_eligible_for_reconciliation() -> None: + """Second-granularity timestamps must not strand a later same-second review.""" + text = SCHEDULER.read_text(encoding="utf-8") + assert "fromdateiso8601) >= ($started | fromdateiso8601" in text + proc = subprocess.run( + [ + "jq", + "-nr", + "--arg", + "review", + "2026-09-02T12:00:00Z", + "--arg", + "started", + "2026-09-02T12:00:00Z", + "try (($review | fromdateiso8601) >= ($started | fromdateiso8601)) catch false", + ], + text=True, + capture_output=True, + check=True, + ) + assert proc.stdout.strip() == "true" From 5c8e08825058ac28e41fa0cb75e21d16f2d2971f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 23:34:08 +0900 Subject: [PATCH 52/59] fix(temp): admit pre-existing PR1706 RED regression --- ..._temp_pr1706_reconcile_formal_verdict_v2.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/.github/workflows/_temp_pr1706_reconcile_formal_verdict_v2.yml b/.github/workflows/_temp_pr1706_reconcile_formal_verdict_v2.yml index ade0272c23..34038d8288 100644 --- a/.github/workflows/_temp_pr1706_reconcile_formal_verdict_v2.yml +++ b/.github/workflows/_temp_pr1706_reconcile_formal_verdict_v2.yml @@ -36,6 +36,23 @@ jobs: shell: bash run: | set -euo pipefail + python3 - <<'PY' + from pathlib import Path + + driver = Path("scripts/ci/source_fix_pr1706_formal_verdict.py") + text = driver.read_text(encoding="utf-8") + old = ''' if TEST.exists(): + raise SystemExit(f"refusing to overwrite existing {TEST}") + TEST.write_text(TEST_CONTENT, encoding="utf-8")''' + new = ''' if TEST.exists(): + if TEST.read_text(encoding="utf-8") != TEST_CONTENT: + raise SystemExit(f"existing {TEST} drifted from the expected RED regression") + else: + TEST.write_text(TEST_CONTENT, encoding="utf-8")''' + if text.count(old) != 1: + raise SystemExit("transition driver test-existence guard drifted; refusing to patch") + driver.write_text(text.replace(old, new, 1), encoding="utf-8") + PY PYTHONPATH=. python3 scripts/ci/source_fix_pr1706_formal_verdict.py PYTHONPATH=. python3 -m pytest -q \ tests/test_opencode_required_verdict_reconciliation_contract.py \ From 54b6f6a1cedc7a3733df14ee0402b772b98b6059 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 23:35:35 +0900 Subject: [PATCH 53/59] fix(temp): make PR1706 transition guard exact --- ...emp_pr1706_reconcile_formal_verdict_v2.yml | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/.github/workflows/_temp_pr1706_reconcile_formal_verdict_v2.yml b/.github/workflows/_temp_pr1706_reconcile_formal_verdict_v2.yml index 34038d8288..ca10a4faff 100644 --- a/.github/workflows/_temp_pr1706_reconcile_formal_verdict_v2.yml +++ b/.github/workflows/_temp_pr1706_reconcile_formal_verdict_v2.yml @@ -41,14 +41,18 @@ jobs: driver = Path("scripts/ci/source_fix_pr1706_formal_verdict.py") text = driver.read_text(encoding="utf-8") - old = ''' if TEST.exists(): - raise SystemExit(f"refusing to overwrite existing {TEST}") - TEST.write_text(TEST_CONTENT, encoding="utf-8")''' - new = ''' if TEST.exists(): - if TEST.read_text(encoding="utf-8") != TEST_CONTENT: - raise SystemExit(f"existing {TEST} drifted from the expected RED regression") - else: - TEST.write_text(TEST_CONTENT, encoding="utf-8")''' + old = ( + " if TEST.exists():\n" + " raise SystemExit(f\"refusing to overwrite existing {TEST}\")\n" + " TEST.write_text(TEST_CONTENT, encoding=\"utf-8\")\n" + ) + new = ( + " if TEST.exists():\n" + " if TEST.read_text(encoding=\"utf-8\") != TEST_CONTENT:\n" + " raise SystemExit(f\"existing {TEST} drifted from the expected RED regression\")\n" + " else:\n" + " TEST.write_text(TEST_CONTENT, encoding=\"utf-8\")\n" + ) if text.count(old) != 1: raise SystemExit("transition driver test-existence guard drifted; refusing to patch") driver.write_text(text.replace(old, new, 1), encoding="utf-8") From aa1cd79c4be153e6d282908011c9dace287945d2 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 23:37:13 +0900 Subject: [PATCH 54/59] chore(temp): retire unregistered PR1706 repair workflow --- ...emp_pr1706_reconcile_formal_verdict_v2.yml | 87 ------------------- 1 file changed, 87 deletions(-) delete mode 100644 .github/workflows/_temp_pr1706_reconcile_formal_verdict_v2.yml diff --git a/.github/workflows/_temp_pr1706_reconcile_formal_verdict_v2.yml b/.github/workflows/_temp_pr1706_reconcile_formal_verdict_v2.yml deleted file mode 100644 index ca10a4faff..0000000000 --- a/.github/workflows/_temp_pr1706_reconcile_formal_verdict_v2.yml +++ /dev/null @@ -1,87 +0,0 @@ -name: Temporary PR 1706 formal-verdict reconciliation repair - -on: - push: - branches: - - fix/opencode-poll-wall-clock-bound - paths: - - .github/workflows/_temp_pr1706_reconcile_formal_verdict_v2.yml - -permissions: - contents: read - -concurrency: - group: temp-pr1706-formal-verdict-repair - cancel-in-progress: true - -jobs: - repair: - if: github.repository == 'ContextualWisdomLab/.github' && github.ref == 'refs/heads/fix/opencode-poll-wall-clock-bound' - runs-on: ubuntu-slim - env: - GH_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN }} - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - fetch-depth: 0 - persist-credentials: false - - name: Verify exact writer head and publication credential - shell: bash - run: | - set -euo pipefail - test -n "${GH_TOKEN:-}" || { echo 'workflow-starting mutation credential is required' >&2; exit 1; } - git fetch origin fix/opencode-poll-wall-clock-bound - test "$(git rev-parse origin/fix/opencode-poll-wall-clock-bound)" = "$GITHUB_SHA" - - name: Apply permanent formal-verdict repair - shell: bash - run: | - set -euo pipefail - python3 - <<'PY' - from pathlib import Path - - driver = Path("scripts/ci/source_fix_pr1706_formal_verdict.py") - text = driver.read_text(encoding="utf-8") - old = ( - " if TEST.exists():\n" - " raise SystemExit(f\"refusing to overwrite existing {TEST}\")\n" - " TEST.write_text(TEST_CONTENT, encoding=\"utf-8\")\n" - ) - new = ( - " if TEST.exists():\n" - " if TEST.read_text(encoding=\"utf-8\") != TEST_CONTENT:\n" - " raise SystemExit(f\"existing {TEST} drifted from the expected RED regression\")\n" - " else:\n" - " TEST.write_text(TEST_CONTENT, encoding=\"utf-8\")\n" - ) - if text.count(old) != 1: - raise SystemExit("transition driver test-existence guard drifted; refusing to patch") - driver.write_text(text.replace(old, new, 1), encoding="utf-8") - PY - PYTHONPATH=. python3 scripts/ci/source_fix_pr1706_formal_verdict.py - PYTHONPATH=. python3 -m pytest -q \ - tests/test_opencode_required_verdict_reconciliation_contract.py \ - tests/test_opencode_required_verdict_runner_release.py \ - tests/test_opencode_event_driven_required_wake.py \ - tests/test_opencode_formal_verdict_authority_contract.py - git diff --check - - name: Publish repaired exact-head tree and retire temporary machinery - shell: bash - run: | - set -euo pipefail - rm scripts/ci/source_fix_pr1706_formal_verdict.py - rm .github/workflows/_temp_pr1706_reconcile_formal_verdict_v2.yml - git add \ - .github/workflows/opencode-review.yml \ - .github/workflows/pr-review-merge-scheduler.yml \ - docs/product-technical-gap-baseline.md \ - tests/test_opencode_required_verdict_reconciliation_contract.py \ - scripts/ci/source_fix_pr1706_formal_verdict.py \ - .github/workflows/_temp_pr1706_reconcile_formal_verdict_v2.yml - git diff --cached --check - git config user.name 'ContextualWisdomLab repair bot' - git config user.email 'actions@users.noreply.github.com' - git commit -m 'fix(opencode): reconcile exact-head formal verdict authority' - git fetch origin fix/opencode-poll-wall-clock-bound - test "$(git rev-parse origin/fix/opencode-poll-wall-clock-bound)" = "$GITHUB_SHA" - auth_header="Basic $(printf 'x-access-token:%s' "$GH_TOKEN" | base64 | tr -d '\n')" - git -c http.extraheader="AUTHORIZATION: $auth_header" push origin HEAD:fix/opencode-poll-wall-clock-bound From 69a1a0386d39e0805d0df85c2f116bcd0f4bec84 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 23:50:03 +0900 Subject: [PATCH 55/59] fix(temp): admit committed PR1706 RED contract --- scripts/ci/source_fix_pr1706_formal_verdict.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/ci/source_fix_pr1706_formal_verdict.py b/scripts/ci/source_fix_pr1706_formal_verdict.py index 0f1096474e..9dcb24da8e 100644 --- a/scripts/ci/source_fix_pr1706_formal_verdict.py +++ b/scripts/ci/source_fix_pr1706_formal_verdict.py @@ -265,8 +265,10 @@ def main() -> None: "OpenCode cleanup comment", ) if TEST.exists(): - raise SystemExit(f"refusing to overwrite existing {TEST}") - TEST.write_text(TEST_CONTENT, encoding="utf-8") + if TEST.read_text(encoding="utf-8") != TEST_CONTENT: + raise SystemExit(f"existing {TEST} drifted from expected RED regression") + else: + TEST.write_text(TEST_CONTENT, encoding="utf-8") baseline = BASELINE.read_text(encoding="utf-8") if "### OpenCode required-verdict reconciliation race repair — 2026-09-02" not in baseline: BASELINE.write_text(baseline.rstrip() + "\n" + RECORD, encoding="utf-8") From 39d542d351460ab9e1868878834735ab41318a27 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 23:50:37 +0900 Subject: [PATCH 56/59] fix(opencode): reconcile exact-head formal verdict authority --- .github/workflows/opencode-review.yml | 36 ++- .../workflows/pr-review-merge-scheduler.yml | 36 ++- .../ci/source_fix_pr1706_formal_verdict.py | 278 ------------------ 3 files changed, 39 insertions(+), 311 deletions(-) delete mode 100644 scripts/ci/source_fix_pr1706_formal_verdict.py diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml index d43ba56590..db31b521ba 100644 --- a/.github/workflows/opencode-review.yml +++ b/.github/workflows/opencode-review.yml @@ -9,14 +9,14 @@ on: # content and never binds repository secrets. Privileged review execution is # isolated in opencode-review-dispatch.yml on repository_dispatch only. pull_request_target: - # `converted_to_draft` is included so a PR going draft mid-poll fires a - # fresh run of this same workflow: the head-scoped concurrency group below - # (`cancel-in-progress: true`) cancels any in-flight non-draft - # "Fail closed without a current-head OpenCode verdict" poll for that - # exact same head. Every non-closed admission path revalidates the live - # PR/head/state before dispatching, exempting, or polling so out-of-order - # draft/ready/closed events cannot publish stale evidence or wait on an - # impossible verdict. + # `converted_to_draft` is included so a PR going draft after a ready event + # gets a fresh same-head execution. The head-scoped concurrency group below + # (`cancel-in-progress: true`) retires any superseded same-head admission run. + # Every non-closed path revalidates the live PR/head/state before dispatch, + # exemption, or one-shot verdict admission, so out-of-order draft/ready/closed + # events cannot publish stale evidence. Required verdict continuation is + # event-driven by the authenticated exact-run wake path; this entrypoint does + # not hold a runner in a polling loop. types: [opened, synchronize, reopened, ready_for_review, converted_to_draft, closed] concurrency: @@ -28,7 +28,7 @@ concurrency: # current head's still-valid run before its own live-head check could ever # reject it (Devin Review on `#1568`). Same-head events (draft<->ready # transitions, a synchronize retry) still share one group, so - # `converted_to_draft` still cancels an active same-head verdict poll. + # `converted_to_draft` cancels a superseded same-head admission run. group: >- opencode-review-bootstrap-${{ github.event.pull_request.base.repo.full_name || github.repository }}-${{ @@ -428,7 +428,7 @@ jobs: verdict="$(printf '%s\n' "$reviews" | jq -r -s --arg sha "$HEAD_SHA" ' (add // []) | [.[] - | select((.user.login // "" | ascii_downcase) as $user | $user == "opencode-agent" or $user == "opencode-agent[bot]") + | select((.user.login // "" | ascii_downcase) as $user | $user == "opencode-agent" or $user == "opencode-agent[bot]" or $user == "github-actions[bot]") | select((.commit_id // "" | ascii_downcase) == ($sha | ascii_downcase)) | select(.state == "APPROVED" or .state == "CHANGES_REQUESTED")] | (last // {}) as $review @@ -444,21 +444,19 @@ jobs: then "APPROVED" else empty end ')" if [ -z "$verdict" ]; then - echo "::error::No APPROVED or CHANGES_REQUESTED from opencode-agent on the current head. This required check is not a review and must not succeed until the authenticated dispatch posts a current-head verdict. The dispatch path wakes this exact failed run when the verdict arrives." + echo "::error::No APPROVED or CHANGES_REQUESTED from an authorized OpenCode formal-review publisher on the current head. This required check is not a review and must not succeed until the authenticated dispatch posts a current-head verdict. The dispatch path wakes this exact failed run when the verdict arrives." exit 1 fi echo "Current-head OpenCode verdict: ${verdict}." cancel-superseded-opencode-review-runs: # Exact-head concurrency protects a newer authoritative run from delayed - # old-head events, while the poll above now revalidates live PR identity on - # every wait iteration so an already-running obsolete poll can self-retire - # without consuming a second runner. This sibling job remains a defense in - # depth for queued/requested old-head runs and for legacy runs created from - # older workflow revisions that lack the in-loop self-retirement check. - # Every cancellation candidate and every cancellation itself is re-verified - # against the live PR head immediately beforehand, so a cleanup run that is - # itself delayed/stale cannot cancel a still-authoritative run. + # old-head events. One-shot verdict admission releases its runner immediately; + # this sibling job remains defense in depth for queued/requested old-head runs + # and legacy runs created from older workflow revisions. Every cancellation + # candidate and every cancellation itself is re-verified against the live PR + # head immediately beforehand, so a delayed/stale cleanup run cannot cancel a + # still-authoritative run. if: github.event_name == 'pull_request_target' && github.event.action == 'synchronize' runs-on: ubuntu-24.04 permissions: diff --git a/.github/workflows/pr-review-merge-scheduler.yml b/.github/workflows/pr-review-merge-scheduler.yml index e3d3310d8b..c7a120b9da 100644 --- a/.github/workflows/pr-review-merge-scheduler.yml +++ b/.github/workflows/pr-review-merge-scheduler.yml @@ -93,11 +93,11 @@ on: # Hourly org-wide sweep cadence for the org-queue-sweep job below. Target # repositories only receive scheduler runs on PR events, review/security # workflow completion, and protected-branch pushes; a PR whose approval or - # required checks land AFTER its last event has no later trigger and sits - # approved-but-unmerged until a human pushes something. The sweep closes - # that gap on a fixed heartbeat. Runs hourly so an approval or - # required check that lands after a PR's last event is auto-updated/merged - # within about an hour without adding quarter-hourly runner pressure. + # required checks land AFTER its last event has no event-driven re-wake at + # all. The sweep closes that gap on a fixed heartbeat. Runs hourly so an + # approval or required check that lands after a PR's last event is + # auto-updated/merged within about an hour without adding quarter-hourly + # runner pressure. - cron: "0 * * * *" repository_dispatch: types: [merge-scheduler] @@ -177,15 +177,20 @@ jobs: latest_review="$(printf '%s\n' "$reviews" | jq -r -s --arg sha "$PR_HEAD_SHA" ' (add // []) | [.[] - | select((.user.login // "" | ascii_downcase) as $user | $user == "opencode-agent" or $user == "opencode-agent[bot]") + | select((.user.login // "" | ascii_downcase) as $user | $user == "opencode-agent" or $user == "opencode-agent[bot]" or $user == "github-actions[bot]") | select((.commit_id // "" | ascii_downcase) == ($sha | ascii_downcase)) - | select(.state == "APPROVED" or .state == "CHANGES_REQUESTED") - | select((.body // "" | ascii_downcase | contains("deterministic current-head evidence")) | not) - | select((.body // "" | ascii_downcase | contains("deterministic fallback approval")) | not) - | select((.body // "" | ascii_downcase | contains("model-unavailable evidence fallback")) | not) - | select((.body // "" | ascii_downcase | contains("did not emit a usable current-head control block")) | not) - | select((.body // "" | ascii_downcase | contains("scope: `unsupported`")) | not) - | select((.body // "" | ascii_downcase | contains("model-pool outcome: `unknown`")) | not)] + | select( + .state == "CHANGES_REQUESTED" + or ( + .state == "APPROVED" + and ((.body // "" | ascii_downcase | contains("deterministic current-head evidence")) | not) + and ((.body // "" | ascii_downcase | contains("deterministic fallback approval")) | not) + and ((.body // "" | ascii_downcase | contains("model-unavailable evidence fallback")) | not) + and ((.body // "" | ascii_downcase | contains("did not emit a usable current-head control block")) | not) + and ((.body // "" | ascii_downcase | contains("scope: `unsupported`")) | not) + and ((.body // "" | ascii_downcase | contains("model-pool outcome: `unknown`")) | not) + ) + )] | sort_by(.submitted_at // "", .id // 0) | (last // {}) | [(.state // ""), (.submitted_at // "")] @@ -200,7 +205,10 @@ jobs: echo "::error::Formal exact-head review lacks submission provenance; failing closed." exit 1 fi - new_evidence="$(jq -nr --arg review "$review_submitted_at" --arg started "$REQUIRED_RUN_STARTED_AT" 'try (($review | fromdateiso8601) > ($started | fromdateiso8601)) catch false')" + # GitHub review/run timestamps are second-granularity. Equality can mean the review + # arrived later within the same second, so accept equality for exact-head evidence; + # older seconds remain ineligible and all PR/head/state checks above still fail closed. + new_evidence="$(jq -nr --arg review "$review_submitted_at" --arg started "$REQUIRED_RUN_STARTED_AT" 'try (($review | fromdateiso8601) >= ($started | fromdateiso8601)) catch false')" if [ "$new_evidence" != "true" ]; then echo "Formal review predates this run attempt; the failure is not attributable to missing newer review evidence." exit 0 diff --git a/scripts/ci/source_fix_pr1706_formal_verdict.py b/scripts/ci/source_fix_pr1706_formal_verdict.py deleted file mode 100644 index 9dcb24da8e..0000000000 --- a/scripts/ci/source_fix_pr1706_formal_verdict.py +++ /dev/null @@ -1,278 +0,0 @@ -"""One-shot source repair for ContextualWisdomLab/.github PR #1706. - -This temporary driver performs exact-string, fail-closed mutations only. The -workflow that invokes it removes this file after the permanent regression has -passed, so the production tree retains only the reviewed workflow/test/docs -delta. -""" - -from __future__ import annotations - -from pathlib import Path - - -SCHEDULER = Path(".github/workflows/pr-review-merge-scheduler.yml") -OPENCODE = Path(".github/workflows/opencode-review.yml") -TEST = Path("tests/test_opencode_required_verdict_reconciliation_contract.py") -BASELINE = Path("docs/product-technical-gap-baseline.md") - - -OLD_AUTHOR = ''' | select((.user.login // "" | ascii_downcase) as $user | $user == "opencode-agent" or $user == "opencode-agent[bot]")''' -NEW_AUTHOR = ''' | select((.user.login // "" | ascii_downcase) as $user | $user == "opencode-agent" or $user == "opencode-agent[bot]" or $user == "github-actions[bot]")''' -OLD_FILTER = ''' | select(.state == "APPROVED" or .state == "CHANGES_REQUESTED") - | select((.body // "" | ascii_downcase | contains("deterministic current-head evidence")) | not) - | select((.body // "" | ascii_downcase | contains("deterministic fallback approval")) | not) - | select((.body // "" | ascii_downcase | contains("model-unavailable evidence fallback")) | not) - | select((.body // "" | ascii_downcase | contains("did not emit a usable current-head control block")) | not) - | select((.body // "" | ascii_downcase | contains("scope: `unsupported`")) | not) - | select((.body // "" | ascii_downcase | contains("model-pool outcome: `unknown`")) | not)]''' -NEW_FILTER = ''' | select( - .state == "CHANGES_REQUESTED" - or ( - .state == "APPROVED" - and ((.body // "" | ascii_downcase | contains("deterministic current-head evidence")) | not) - and ((.body // "" | ascii_downcase | contains("deterministic fallback approval")) | not) - and ((.body // "" | ascii_downcase | contains("model-unavailable evidence fallback")) | not) - and ((.body // "" | ascii_downcase | contains("did not emit a usable current-head control block")) | not) - and ((.body // "" | ascii_downcase | contains("scope: `unsupported`")) | not) - and ((.body // "" | ascii_downcase | contains("model-pool outcome: `unknown`")) | not) - ) - )]''' -OLD_TIME = ''' new_evidence="$(jq -nr --arg review "$review_submitted_at" --arg started "$REQUIRED_RUN_STARTED_AT" 'try (($review | fromdateiso8601) > ($started | fromdateiso8601)) catch false')"''' -NEW_TIME = ''' # GitHub review/run timestamps are second-granularity. Equality can mean the review - # arrived later within the same second, so accept equality for exact-head evidence; - # older seconds remain ineligible and all PR/head/state checks above still fail closed. - new_evidence="$(jq -nr --arg review "$review_submitted_at" --arg started "$REQUIRED_RUN_STARTED_AT" 'try (($review | fromdateiso8601) >= ($started | fromdateiso8601)) catch false')"''' -OLD_COMMENT = ''' # `converted_to_draft` is included so a PR going draft mid-poll fires a - # fresh run of this same workflow: the head-scoped concurrency group below - # (`cancel-in-progress: true`) cancels any in-flight non-draft - # "Fail closed without a current-head OpenCode verdict" poll for that - # exact same head. Every non-closed admission path revalidates the live - # PR/head/state before dispatching, exempting, or polling so out-of-order - # draft/ready/closed events cannot publish stale evidence or wait on an - # impossible verdict.''' -NEW_COMMENT = ''' # `converted_to_draft` is included so a PR going draft after a ready event - # gets a fresh same-head execution. The head-scoped concurrency group below - # (`cancel-in-progress: true`) retires any superseded same-head admission run. - # Every non-closed path revalidates the live PR/head/state before dispatch, - # exemption, or one-shot verdict admission, so out-of-order draft/ready/closed - # events cannot publish stale evidence. Required verdict continuation is - # event-driven by the authenticated exact-run wake path; this entrypoint does - # not hold a runner in a polling loop.''' -OLD_CONCURRENCY_TAIL = ''' # Same-head events (draft<->ready - # transitions, a synchronize retry) still share one group, so - # `converted_to_draft` still cancels an active same-head verdict poll.''' -NEW_CONCURRENCY_TAIL = ''' # Same-head events (draft<->ready - # transitions, a synchronize retry) still share one group, so - # `converted_to_draft` cancels a superseded same-head admission run.''' -OLD_CLEANUP_COMMENT = ''' cancel-superseded-opencode-review-runs: - # Exact-head concurrency protects a newer authoritative run from delayed - # old-head events, while the poll above now revalidates live PR identity on - # every wait iteration so an already-running obsolete poll can self-retire - # without consuming a second runner. This sibling job remains a defense in - # depth for queued/requested old-head runs and for legacy runs created from - # older workflow revisions that lack the in-loop self-retirement check. - # Every cancellation candidate and every cancellation itself is re-verified - # against the live PR head immediately beforehand, so a cleanup run that is - # itself delayed/stale cannot cancel a still-authoritative run.''' -NEW_CLEANUP_COMMENT = ''' cancel-superseded-opencode-review-runs: - # Exact-head concurrency protects a newer authoritative run from delayed - # old-head events. One-shot verdict admission releases its runner immediately; - # this sibling job remains defense in depth for queued/requested old-head runs - # and legacy runs created from older workflow revisions. Every cancellation - # candidate and every cancellation itself is re-verified against the live PR - # head immediately beforehand, so a delayed/stale cleanup run cannot cancel a - # still-authoritative run.''' - -TEST_CONTENT = r'''"""Executable regressions for event-driven OpenCode verdict reconciliation.""" - -from __future__ import annotations - -import json -from pathlib import Path -import subprocess -import textwrap - -from scripts.ci import opencode_review_receipt_gate - -SCHEDULER = Path(".github/workflows/pr-review-merge-scheduler.yml") -OPENCODE = Path(".github/workflows/opencode-review.yml") -HEAD = "a" * 40 - - -def _scheduler_jq_filter() -> str: - """Extract the production scheduler review selector.""" - text = SCHEDULER.read_text(encoding="utf-8") - anchor = text.index('latest_review="$(printf') - start = text.index(" (add // [])", anchor) - end_marker = " | @tsv" - end = text.index(end_marker, start) + len(end_marker) - return textwrap.dedent(text[start:end]) - - -def _admission_jq_filter() -> str: - """Extract the production one-shot admission review selector.""" - text = OPENCODE.read_text(encoding="utf-8") - anchor = text.index('verdict="$(printf') - start = text.index(" (add // [])", anchor) - end_marker = ' then "APPROVED" else empty end' - end = text.index(end_marker, start) + len(end_marker) - return textwrap.dedent(text[start:end]) - - -def _run_filter(filter_text: str, reviews: list[dict[str, object]]) -> str: - """Execute one production jq selector against exact-head fixtures.""" - proc = subprocess.run( - ["jq", "-r", "-s", "--arg", "sha", HEAD, filter_text], - input=json.dumps(reviews), - text=True, - capture_output=True, - check=True, - ) - return proc.stdout.strip() - - -def _scheduler_select(reviews: list[dict[str, object]]) -> str: - """Execute the scheduler selector.""" - return _run_filter(_scheduler_jq_filter(), reviews) - - -def _admission_select(reviews: list[dict[str, object]]) -> str: - """Execute the one-shot admission selector.""" - return _run_filter(_admission_jq_filter(), reviews) - - -def _review( - state: str, - body: str, - *, - login: str = "opencode-agent[bot]", -) -> dict[str, object]: - """Build one exact-head formal-review fixture.""" - return { - "id": 42, - "user": {"login": login}, - "commit_id": HEAD, - "state": state, - "body": f"## Verdict\n{body}\n\nHead SHA: `{HEAD}`", - "submitted_at": "2026-09-02T12:00:00Z", - } - - -def test_marker_bearing_change_request_remains_a_formal_verdict() -> None: - """Fallback markers invalidate approvals only, never a real change request.""" - review = _review("CHANGES_REQUESTED", "deterministic fallback approval: defect remains") - assert _scheduler_select([review]).startswith("CHANGES_REQUESTED\t") - assert _admission_select([review]) == "CHANGES_REQUESTED" - assert opencode_review_receipt_gate.is_formal_receipt( - review, HEAD, is_draft=False - )[0] - - -def test_marker_bearing_approval_is_not_admitted() -> None: - """Synthetic/fallback approval markers still block APPROVED evidence.""" - review = _review("APPROVED", "deterministic fallback approval") - assert _scheduler_select([review]) == "" - assert _admission_select([review]) == "" - assert not opencode_review_receipt_gate.is_formal_receipt( - review, HEAD, is_draft=False - )[0] - - -def test_clean_approval_is_admitted() -> None: - """A clean exact-head approval remains a formal verdict everywhere.""" - review = _review("APPROVED", "real model review") - assert _scheduler_select([review]).startswith("APPROVED\t") - assert _admission_select([review]) == "APPROVED" - assert opencode_review_receipt_gate.is_formal_receipt( - review, HEAD, is_draft=False - )[0] - - -def test_github_actions_formal_receipt_reconciles_and_admits() -> None: - """Every accepted formal publisher must be accepted by all verdict gates.""" - review = _review( - "CHANGES_REQUESTED", - "current-head defect remains", - login="github-actions[bot]", - ) - assert _scheduler_select([review]).startswith("CHANGES_REQUESTED\t") - assert _admission_select([review]) == "CHANGES_REQUESTED" - assert opencode_review_receipt_gate.is_formal_receipt( - review, HEAD, is_draft=False - )[0] - - -def test_same_second_review_is_eligible_for_reconciliation() -> None: - """Second-granularity timestamps must not strand a later same-second review.""" - text = SCHEDULER.read_text(encoding="utf-8") - assert "fromdateiso8601) >= ($started | fromdateiso8601" in text - proc = subprocess.run( - [ - "jq", - "-nr", - "--arg", - "review", - "2026-09-02T12:00:00Z", - "--arg", - "started", - "2026-09-02T12:00:00Z", - "try (($review | fromdateiso8601) >= ($started | fromdateiso8601)) catch false", - ], - text=True, - capture_output=True, - check=True, - ) - assert proc.stdout.strip() == "true" -''' - -RECORD = ''' -### OpenCode required-verdict reconciliation race repair — 2026-09-02 - -- **Owner / PR:** `ContextualWisdomLab/.github#1706`. -- **Root cause:** formal-verdict authority drifted across three gates. The workflow-run reconciler applied fallback-approval marker exclusions to `CHANGES_REQUESTED`, scheduler/admission selectors omitted the `github-actions[bot]` publisher already accepted by the canonical receipt gate, and strict greater-than timestamp ordering could drop a later review sharing GitHub's second-granularity timestamp with the required run start. -- **Repair:** the receipt gate, one-shot admission, and completion reconciliation now share the formal publisher set; marker exclusions apply only to `APPROVED`; exact-head `CHANGES_REQUESTED` remains a formal verdict; same-second evidence uses `>=` only after live open/ready/head revalidation; polling-era comments describe event-driven one-shot admission instead. -- **Regression:** `tests/test_opencode_required_verdict_reconciliation_contract.py` executes both production jq selectors and the receipt gate for marker-bearing change requests, rejected fallback approvals, clean approvals, `github-actions[bot]` formal receipts, and same-second ordering. -- **Status:** Proposed until this exact branch head receives fresh CI/security/review evidence and integrates through ordinary protected admission. -''' - - -def replace_once(path: Path, old: str, new: str, label: str) -> None: - """Replace exactly one expected source fragment or fail closed.""" - text = path.read_text(encoding="utf-8") - if text.count(old) != 1: - raise SystemExit(f"{label} drifted: expected exactly one match") - path.write_text(text.replace(old, new, 1), encoding="utf-8") - - -def main() -> None: - """Apply permanent production, regression, and traceability repairs.""" - replace_once(SCHEDULER, OLD_AUTHOR, NEW_AUTHOR, "scheduler formal publisher set") - replace_once(OPENCODE, OLD_AUTHOR, NEW_AUTHOR, "admission formal publisher set") - replace_once(SCHEDULER, OLD_FILTER, NEW_FILTER, "formal-verdict filter") - replace_once(SCHEDULER, OLD_TIME, NEW_TIME, "review timestamp comparison") - replace_once(OPENCODE, OLD_COMMENT, NEW_COMMENT, "OpenCode trigger comment") - replace_once( - OPENCODE, - OLD_CONCURRENCY_TAIL, - NEW_CONCURRENCY_TAIL, - "OpenCode concurrency comment", - ) - replace_once( - OPENCODE, - OLD_CLEANUP_COMMENT, - NEW_CLEANUP_COMMENT, - "OpenCode cleanup comment", - ) - if TEST.exists(): - if TEST.read_text(encoding="utf-8") != TEST_CONTENT: - raise SystemExit(f"existing {TEST} drifted from expected RED regression") - else: - TEST.write_text(TEST_CONTENT, encoding="utf-8") - baseline = BASELINE.read_text(encoding="utf-8") - if "### OpenCode required-verdict reconciliation race repair — 2026-09-02" not in baseline: - BASELINE.write_text(baseline.rstrip() + "\n" + RECORD, encoding="utf-8") - - -if __name__ == "__main__": - main() From 90a2803d0d66e68f610972c606dd31ed564e9eb3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 07:53:34 +0900 Subject: [PATCH 57/59] fix(review): narrow this PR to only the still-real allowlist gap Re-investigation after the initial merge surfaced 2 more full-suite regressions this PR's original scope caused, missed by the earlier supersession check against #1830 alone: - The proposed opencode-review-dispatch.yml wake-step redesign (pull_requests[]/PR-number matching, single lookup) contradicts #1830's OWN test (test_opencode_required_rerun_capacity.py), which still expects the original 12-attempt loop and .head_sha matching -- a live design disagreement with already-shipped, already-tested behavior, not a stale assumption to just override. - The proposed reconcile-opencode-required-verdict workflow_run: completed handler in pr-review-merge-scheduler.yml directly reintroduces the exact workflow_run-triggered fanout mechanism #1840 ("stop required-check completion fanout", merged 2026-09-04) deliberately removed in favor of native auto-merge. Reverted both files to main's current content, removed the 4 test files and 2 doc additions that only made sense under the reverted design, and recomputed the now-restored opencode-review-dispatch.yml blob-SHA pin. What survives, verified real and uncontested: adding github-actions[bot] to opencode-review.yml's required-check verdict lookup, matching scripts/ci/opencode_review_receipt_gate.py's own FORMAL_AUTHORS allowlist. Rewrote the CHANGELOG and gap-baseline entries to describe only this narrower, actually-landing scope. Full suite: only the 1 pre-existing failure tracked by .github#1874 (unrelated stale hourly-cron test oracle) remains. Co-Authored-By: Claude Sonnet 5 --- .../workflows/opencode-review-dispatch.yml | 75 ++++------ .../workflows/pr-review-merge-scheduler.yml | 102 ------------- ARCHITECTURE.md | 10 -- CHANGELOG.md | 2 +- .../opencode-stale-poll-self-retirement.md | 17 --- docs/product-technical-gap-baseline.md | 12 +- ...est_opencode_event_driven_required_wake.py | 49 ------ ...ncode_formal_verdict_authority_contract.py | 141 ------------------ ...equired_verdict_reconciliation_contract.py | 140 ----------------- ...st_opencode_required_verdict_regression.py | 51 ++++--- ...pencode_required_verdict_runner_release.py | 75 ---------- ...t_pr_review_autofix_nvidia_nim_contract.py | 2 +- 12 files changed, 65 insertions(+), 611 deletions(-) delete mode 100644 tests/test_opencode_event_driven_required_wake.py delete mode 100644 tests/test_opencode_formal_verdict_authority_contract.py delete mode 100644 tests/test_opencode_required_verdict_reconciliation_contract.py delete mode 100644 tests/test_opencode_required_verdict_runner_release.py diff --git a/.github/workflows/opencode-review-dispatch.yml b/.github/workflows/opencode-review-dispatch.yml index 4c1a917920..0823eac0d2 100644 --- a/.github/workflows/opencode-review-dispatch.yml +++ b/.github/workflows/opencode-review-dispatch.yml @@ -7535,13 +7535,11 @@ jobs: && github.event_name == 'repository_dispatch' && steps.formal_review_receipt.outcome == 'success' && needs.validate-pr-metadata.outputs.target_repository != '' - && needs.validate-pr-metadata.outputs.pr_number != '' && needs.validate-pr-metadata.outputs.head_sha != '' && github.event.client_payload.required_run_id != '' env: GH_TOKEN: ${{ needs.validate-pr-metadata.outputs.target_repository == github.repository && github.token || secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN }} GH_REPOSITORY: ${{ needs.validate-pr-metadata.outputs.target_repository }} - PR_NUMBER: ${{ needs.validate-pr-metadata.outputs.pr_number }} PR_HEAD_SHA: ${{ needs.validate-pr-metadata.outputs.head_sha }} REQUIRED_RUN_ID: ${{ github.event.client_payload.required_run_id }} WAKE_TOKEN_SOURCE: ${{ needs.validate-pr-metadata.outputs.target_repository == github.repository && 'github-token' || secrets.PR_REVIEW_MERGE_TOKEN != '' && 'PR_REVIEW_MERGE_TOKEN' || secrets.OPENCODE_APPROVE_TOKEN != '' && 'OPENCODE_APPROVE_TOKEN' || 'unavailable' }} @@ -7551,48 +7549,39 @@ jobs: echo "::error::Actions-capable wake credential is unavailable. Native runs use github.token; sibling runs require PR_REVIEW_MERGE_TOKEN or OPENCODE_APPROVE_TOKEN." exit 1 fi - [[ "$REQUIRED_RUN_ID" =~ ^[1-9][0-9]*$ ]] || { echo "::error::Required OpenCode run id is missing or non-canonical."; exit 1; } - [[ "$PR_NUMBER" =~ ^[1-9][0-9]*$ ]] || { echo "::error::Required OpenCode PR number is missing or non-canonical."; exit 1; } - [[ "$PR_HEAD_SHA" =~ ^[0-9a-fA-F]{40}$ ]] || { echo "::error::Required OpenCode PR head SHA is missing or malformed."; exit 1; } - if ! run="$(gh api "repos/${GH_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}")"; then - echo "::error::Exact required-run lookup failed; no retry policy is invented." - exit 1 - fi - required_run="$(printf '%s\n' "$run" | jq -r --arg head "$PR_HEAD_SHA" --arg pr "$PR_NUMBER" --argjson run_id "$REQUIRED_RUN_ID" ' - select(.id == $run_id) - | select(.event == "pull_request_target") - | select(.path == ".github/workflows/opencode-review.yml") - | select(any((.pull_requests // [])[]?; ((.number // 0) | tostring) == $pr and ((.head.sha // "") | ascii_downcase) == ($head | ascii_downcase))) - | [(.id // ""), (.status // ""), (.conclusion // "")] - | @tsv - ')" || required_run="" - if [ -z "$required_run" ]; then - echo "::error::Referenced Required OpenCode Review run does not match the exact PR/head/workflow identity." - exit 1 - fi - IFS=$'\t' read -r required_run_id required_status required_conclusion <<<"$required_run" - if [ "$required_status" = "completed" ] && [ "$required_conclusion" = "success" ]; then - echo "Exact-PR/head Required OpenCode Review run ${required_run_id} already succeeded." - exit 0 - fi - if [ "$required_status" != "completed" ] || [ "$required_conclusion" != "failure" ]; then - echo "Exact required run is not a completed failure; workflow_run completion reconciliation owns any later transition." - exit 0 - fi - if gh api -X POST "repos/${GH_REPOSITORY}/actions/runs/${required_run_id}/rerun-failed-jobs" >/dev/null; then - echo "Re-ran failed jobs for exact-PR/head Required OpenCode Review run ${required_run_id} after formal exact-head evidence." - exit 0 - fi - if ! advanced="$(gh api "repos/${GH_REPOSITORY}/actions/runs/${required_run_id}")"; then - echo "::error::Rerun mutation failed and exact-run readback is unavailable; failing closed." + [[ "$REQUIRED_RUN_ID" =~ ^[1-9][0-9]*$ ]] || { + echo "::error::Required OpenCode run id is missing or non-canonical." exit 1 - fi - advanced_state="$(printf '%s\n' "$advanced" | jq -r '[.status // "", .conclusion // ""] | @tsv')" - if [ "$advanced_state" != $'completed\tfailure' ]; then - echo "Exact required run advanced concurrently; no duplicate rerun is needed." - exit 0 - fi - echo "::error::Exact required run remains failed after the rerun mutation failed." + } + # The immutable run id is scoped to GH_REPOSITORY. Revalidate its + # event, central workflow path, and live PR head before rerunning it; + # rendered titles and workflow_url differ between native and + # organization-required workflow contexts. + for attempt in $(seq 1 12); do + run="$(gh api "repos/${GH_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}")" + required_run="$(printf '%s\n' "$run" | jq -r --arg head "$PR_HEAD_SHA" --argjson run_id "$REQUIRED_RUN_ID" ' + select(.id == $run_id) + | select(.event == "pull_request_target") + | select(.path == ".github/workflows/opencode-review.yml") + | select(.head_sha == $head) + | [(.id // ""), (.status // ""), (.conclusion // "")] + | @tsv + ')" + IFS=$'\t' read -r required_run_id required_status required_conclusion <<<"$required_run" + if [ "$required_status" = "completed" ] && [ "$required_conclusion" = "failure" ]; then + gh api -X POST "repos/${GH_REPOSITORY}/actions/runs/${required_run_id}/rerun-failed-jobs" >/dev/null + echo "Re-ran failed jobs for exact-head Required OpenCode Review run ${required_run_id}." + exit 0 + fi + if [ "$required_status" = "completed" ] && [ "$required_conclusion" = "success" ]; then + echo "Exact-head Required OpenCode Review run ${required_run_id} already succeeded." + exit 0 + fi + if [ "$attempt" -lt 12 ]; then + sleep 5 + fi + done + echo "::error::Formal OpenCode receipt exists, but the exact-head required workflow did not reach a rerunnable failed state." exit 1 - name: Publish repository_dispatch OpenCode status diff --git a/.github/workflows/pr-review-merge-scheduler.yml b/.github/workflows/pr-review-merge-scheduler.yml index 420315c241..939087bff5 100644 --- a/.github/workflows/pr-review-merge-scheduler.yml +++ b/.github/workflows/pr-review-merge-scheduler.yml @@ -99,108 +99,6 @@ permissions: contents: read jobs: - reconcile-opencode-required-verdict: - name: reconcile-opencode-required-verdict - if: >- - github.event_name == 'workflow_run' - && github.event.workflow_run.name == 'Required OpenCode Review' - && github.event.workflow_run.conclusion == 'failure' - && github.event.workflow_run.event == 'pull_request_target' - && github.event.workflow_run.path == '.github/workflows/opencode-review.yml' - && github.event.workflow_run.pull_requests[0].number - runs-on: ubuntu-slim - permissions: - actions: write - contents: read - pull-requests: read - env: - GH_TOKEN: ${{ github.token }} - PR_NUMBER: ${{ github.event.workflow_run.pull_requests[0].number }} - PR_HEAD_SHA: ${{ github.event.workflow_run.pull_requests[0].head.sha }} - REQUIRED_RUN_ID: ${{ github.event.workflow_run.id }} - REQUIRED_RUN_STARTED_AT: ${{ github.event.workflow_run.run_started_at }} - steps: - - name: Reconcile newer formal review evidence with the completed required run - shell: bash - run: | - set -euo pipefail - [[ "$PR_NUMBER" =~ ^[1-9][0-9]*$ ]] || { echo "::error::workflow_run PR number is missing or non-canonical."; exit 1; } - [[ "$PR_HEAD_SHA" =~ ^[0-9a-fA-F]{40}$ ]] || { echo "::error::workflow_run PR head is missing or malformed."; exit 1; } - [[ "$REQUIRED_RUN_ID" =~ ^[1-9][0-9]*$ ]] || { echo "::error::workflow_run id is missing or non-canonical."; exit 1; } - if [ -z "$REQUIRED_RUN_STARTED_AT" ]; then - echo "::error::workflow_run start provenance is missing; failing closed." - exit 1 - fi - if ! live_pr="$(gh api "repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}")"; then - echo "::error::Live PR read failed during event-driven Required OpenCode reconciliation; failing closed." - exit 1 - fi - live_head="$(printf '%s\n' "$live_pr" | jq -r '.head.sha // empty')" - live_state="$(printf '%s\n' "$live_pr" | jq -r '.state // empty')" - live_draft="$(printf '%s\n' "$live_pr" | jq -r 'if (.draft | type) == "boolean" then (.draft | tostring) else empty end')" - if [ "$live_state" != "open" ] || [ "$live_draft" = "true" ] || [ "${live_head,,}" != "${PR_HEAD_SHA,,}" ]; then - echo "Required run is no longer authoritative for an open ready exact-head PR; no wake mutation is allowed." - exit 0 - fi - if ! reviews="$(gh api --paginate "repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}/reviews")"; then - echo "::error::Reviews API read failed during event-driven Required OpenCode reconciliation; failing closed." - exit 1 - fi - latest_review="$(printf '%s\n' "$reviews" | jq -r -s --arg sha "$PR_HEAD_SHA" ' - (add // []) - | [.[] - | select((.user.login // "" | ascii_downcase) as $user | $user == "opencode-agent" or $user == "opencode-agent[bot]" or $user == "github-actions[bot]") - | select((.commit_id // "" | ascii_downcase) == ($sha | ascii_downcase)) - | select( - .state == "CHANGES_REQUESTED" - or ( - .state == "APPROVED" - and ((.body // "" | ascii_downcase | contains("deterministic current-head evidence")) | not) - and ((.body // "" | ascii_downcase | contains("deterministic fallback approval")) | not) - and ((.body // "" | ascii_downcase | contains("model-unavailable evidence fallback")) | not) - and ((.body // "" | ascii_downcase | contains("did not emit a usable current-head control block")) | not) - and ((.body // "" | ascii_downcase | contains("scope: `unsupported`")) | not) - and ((.body // "" | ascii_downcase | contains("model-pool outcome: `unknown`")) | not) - ) - )] - | sort_by(.submitted_at // "", .id // 0) - | (last // {}) - | [(.state // ""), (.submitted_at // "")] - | @tsv - ')" - IFS=$'\t' read -r review_state review_submitted_at <<<"$latest_review" - if [ -z "$review_state" ]; then - echo "No formal exact-head OpenCode review exists yet; the later review receipt event owns reconciliation." - exit 0 - fi - if [ -z "$review_submitted_at" ]; then - echo "::error::Formal exact-head review lacks submission provenance; failing closed." - exit 1 - fi - # GitHub review/run timestamps are second-granularity. Equality can mean the review - # arrived later within the same second, so accept equality for exact-head evidence; - # older seconds remain ineligible and all PR/head/state checks above still fail closed. - new_evidence="$(jq -nr --arg review "$review_submitted_at" --arg started "$REQUIRED_RUN_STARTED_AT" 'try (($review | fromdateiso8601) >= ($started | fromdateiso8601)) catch false')" - if [ "$new_evidence" != "true" ]; then - echo "Formal review predates this run attempt; the failure is not attributable to missing newer review evidence." - exit 0 - fi - if gh api -X POST "repos/${GITHUB_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}/rerun-failed-jobs" >/dev/null; then - echo "Re-ran failed jobs for Required OpenCode Review run ${REQUIRED_RUN_ID} after newer formal exact-head evidence." - exit 0 - fi - if ! advanced="$(gh api "repos/${GITHUB_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}")"; then - echo "::error::Rerun mutation failed and exact-run readback is unavailable; failing closed." - exit 1 - fi - advanced_state="$(printf '%s\n' "$advanced" | jq -r '[.status // "", .conclusion // ""] | @tsv')" - if [ "$advanced_state" != $'completed\tfailure' ]; then - echo "Exact required run advanced concurrently; no duplicate rerun is needed." - exit 0 - fi - echo "::error::Exact required run remains failed after the rerun mutation failed." - exit 1 - scan-pr-queue: # repository_dispatch review runs do not reliably carry pull_requests metadata. # Without this guard, one completed central review can wake a repo-wide scan. diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 1cbe65d719..e12f33542d 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -266,13 +266,3 @@ resolver conflict. — current increment's attestation decision and APA 7th citations. - [`docs/doctoring/sandboxed-web-readiness-loopback-boundary.md`](docs/doctoring/sandboxed-web-readiness-loopback-boundary.md) — loopback-only web E2E readiness polling and APA 7th citations. - - -### Required OpenCode one-shot verdict admission - -The protected required workflow validates live PR state once, reads formal review evidence once, and releases its runner immediately when no exact-head verdict exists. The authenticated default-branch dispatch revalidates repository, immutable run id, workflow path, PR number, and `pull_requests[].head.sha` before `rerun-failed-jobs`. GitHub's documented workflow-run completion event complements the formal-review receipt event; model reasoning has no caller wall-clock deadline and no fixed wake retry allocation is retained. - - -### Required OpenCode event-driven verdict admission - -The required workflow performs one live-PR read and one complete paginated formal-review read, then fails closed immediately if no exact-head verdict exists. The privileged formal-review receipt reconciles the immutable required-run id once. If that review arrives before the run finishes, GitHub's `workflow_run: completed` event performs the complementary reconciliation. The completion path admits a rerun only when exact PR/head/workflow identity holds and `review.submitted_at > run.run_started_at`; the same old evidence therefore cannot create an unbounded rerun cycle. No repository-authored polling cadence, retry count, sleep, transport timeout, or model reasoning deadline is part of this verdict-wake state machine. diff --git a/CHANGELOG.md b/CHANGELOG.md index 038400993c..11fa57697f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -- **Reconcile the remaining OpenCode required-verdict gaps `#1830`'s runner-release rework left open.** The required check's own verdict lookup now accepts `github-actions[bot]` alongside `opencode-agent`/`opencode-agent[bot]`, matching `scripts/ci/opencode_review_receipt_gate.py`'s existing `FORMAL_AUTHORS` allowlist (previously the receipt gate could wake a run for a `github-actions[bot]` review that the required job's own admission logic would not recognize). The dispatch's wake step replaces its fixed 12-attempt/5-second-sleep retry loop with a single exact repo/run-id/PR-number/head lookup plus readback-based idempotency on a failed rerun mutation. Added a new `reconcile-opencode-required-verdict` `workflow_run: completed` handler in `pr-review-merge-scheduler.yml` for the case where a formal review posts before the required run itself finishes. +- **Accept `github-actions[bot]` in the required OpenCode check's own verdict lookup.** `scripts/ci/opencode_review_receipt_gate.py`'s `FORMAL_AUTHORS` allowlist already accepted `github-actions[bot]` as a formal reviewer, but the required check's own jq verdict-matching in `opencode-review.yml` only recognized `opencode-agent`/`opencode-agent[bot]` — so the receipt gate could wake a run for a `github-actions[bot]` review that the required job's own admission logic would never actually recognize. Fixed the inconsistency; no other behavior change. ### Contextual-orchestrator pin refresh diff --git a/docs/doctoring/opencode-stale-poll-self-retirement.md b/docs/doctoring/opencode-stale-poll-self-retirement.md index c71ccde9bc..4bfdc0be8a 100644 --- a/docs/doctoring/opencode-stale-poll-self-retirement.md +++ b/docs/doctoring/opencode-stale-poll-self-retirement.md @@ -46,20 +46,3 @@ Rollback is the ordinary revert of the workflow repair if exact-head evidence sh Monitor both runner occupancy and GitHub API failure/rate-limit evidence. Repeated transport failures should terminate the required check after three bounded attempts rather than leave an immortal poll. A rate-pressure regression should be repaired by changing evidence acquisition/cadence without weakening exact-head review semantics. After protected integration, re-observe affected leaf repositories. Acceptance requires predecessor-head OpenCode polls to release runner capacity without waiting for a separate cleanup runner, while unchanged current-head semantic reviews remain able to run beyond arbitrary short deadlines and current-head polls stay within a defensible REST request budget. - - -## 2026-09-02 one-shot runner-release supersession - -The required-verdict job performs one authoritative live-PR read followed by at most one paginated Reviews read. Missing or unavailable exact-head verdict evidence fails closed immediately and releases the runner. Authenticated `opencode-review-dispatch.yml` wakes the exact failed run via `rerun-failed-jobs` when the formal verdict arrives; no repository-authored polling interval, retry count, or wall-clock deadline bounds model work. - - -### Exact-run wake identity and transient lookup correction - -For `pull_request_target`, top-level workflow-run `head_sha` identifies the base revision, not the PR head. Wake authority binds immutable run id, event, workflow path, exact PR number, and `pull_requests[].head.sha`. Run-lookup transport failure fails closed without inventing a retry policy; the independent GitHub workflow-run completion event closes the opposite review-before-failure ordering. - - -### 2026-09-02 event-driven wake supersedes fixed retry allocation - -RCA found that the intermediate PR #1706 repair replaced a runner-held verdict poll with a dispatch wake loop containing fixed `12` attempts, `5` second sleeps, and `30` second transport deadlines. Those values had no governing model, standard, experiment, or provider contract. The corrected state machine uses the authenticated formal-review receipt event plus GitHub's `workflow_run` `completed` event. Receipt-after-failure performs one exact-run transition; review-before-failure is reconciled on completion and requires `review.submitted_at > run.run_started_at`. Mutation readback only resolves concurrent state advancement and is not a retry loop. - -Reference (APA 7): GitHub. (2026). *Events that trigger workflows*. GitHub Docs. https://docs.github.com/actions/using-workflows/events-that-trigger-workflows#workflow_run diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md index 00ce4716f1..205fbba8ef 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -2650,13 +2650,13 @@ Higgins, S. S., Crepalde, N., & Fernandes, L. (2021). Segmented multiplexity: A **Follow-up.** If the organization later solves free+ZDR routing robustly enough to deliberately widen required-review CI to `orchestrator/auto` (e.g. once a spend ceiling and reviewer-visible cost evidence exist for that path), the change is exactly one `case` arm plus the corresponding assertions in `test_sidecar_pins_the_pool_to_free_for_github_actions` — this entry is the record of *why* it was narrowed, not a permanent prohibition. -### OPENCODE-EVENT-DRIVEN-REQUIRED-WAKE-2026-09-02 (updated 2026-09-05) -- Gap: Required OpenCode verdict admission occupied a hosted runner while waiting; an intermediate repair then introduced fixed dispatch retry/sleep/transport allocations (`12`, `5s`, `30s`) without a governing model or standard. +### OPENCODE-EVENT-DRIVEN-REQUIRED-WAKE-2026-09-02 (reconciled 2026-09-05, scope narrowed) +- Gap, as originally scoped: Required OpenCode verdict admission occupied a hosted runner while waiting; an intermediate repair then introduced fixed dispatch retry/sleep/transport allocations (`12`, `5s`, `30s`) without a governing model or standard; the required check's own verdict lookup was also inconsistent with `scripts/ci/opencode_review_receipt_gate.py`'s `FORMAL_AUTHORS` allowlist. - Causal owner: `ContextualWisdomLab/.github` required review and merge-control workflows. -- **Update (2026-09-05): the one-shot-runner-release half of this gap landed independently via `#1830`** ("release required runner after dispatch," merged 2026-09-04) before this PR's own branch reconciled with `main` — `#1830` rewrote `opencode-review.yml`'s fail-closed step to one live PR read plus one Reviews read with immediate fail-closed on a missing verdict, using its own regression suite (including the new `tests/test_opencode_required_rerun_capacity.py`), and deleted the now-obsolete `tests/test_opencode_poll_rate_budget.py`/`tests/test_opencode_poll_self_retirement.py`. This entry's own now-removed `OPENCODE-ONE-SHOT-RUNNER-RELEASE-2026-09-02` register row described that same goal from this PR's side; it is dropped here rather than kept as a second, redundant claim to the same already-landed change. -- Repair actually reconciled here: the required check's verdict lookup now accepts `github-actions[bot]` alongside `opencode-agent`/`opencode-agent[bot]` (matching `scripts/ci/opencode_review_receipt_gate.py`'s `FORMAL_AUTHORS`, previously inconsistent with it); the dispatch wake step replaces its fixed 12-attempt/5-second-sleep retry loop with a single exact repo/run-id/PR-number/head lookup and readback-based idempotency; a new `reconcile-opencode-required-verdict` `workflow_run: completed` handler in `pr-review-merge-scheduler.yml` covers the review-before-failure race with exact PR/head/workflow identity and `review.submitted_at > run.run_started_at`. -- Verification: executable regressions cover the allowlist fix, the single-lookup wake step, absence of repository-authored retry/sleep/transport budgets, and the new reconciliation job. -- Status: Merged into `main` via this PR after reconciling with `#1830`'s independent runner-release work. +- **2026-09-05 reconciliation: two of this PR's three proposed changes are now superseded by separately-merged, later work; only the allowlist fix survives.** `#1830` ("release required runner after dispatch," merged 2026-09-04) independently rewrote `opencode-review.yml`'s fail-closed step to one live PR read plus one Reviews read with immediate fail-closed, achieving the runner-release goal a different way, and its own `tests/test_opencode_required_rerun_capacity.py` still expects `opencode-review-dispatch.yml`'s wake step to keep its original 12-attempt loop and `.head_sha`-based run matching — this PR's proposed single-lookup/`pull_requests[]`-matching replacement for that step directly contradicts that already-tested, already-shipping design, so it was dropped rather than pushed through over a live test disagreement. Separately, `#1840` ("stop required-check completion fanout," merged 2026-09-04) deliberately removed every `workflow_run:` listener from `pr-review-merge-scheduler.yml` in favor of GitHub's native auto-merge; this PR's proposed `reconcile-opencode-required-verdict` `workflow_run: completed` handler would reintroduce exactly the mechanism `#1840` retired, so it was also dropped. Both reverted pieces' dedicated test files (`test_opencode_event_driven_required_wake.py`, `test_opencode_formal_verdict_authority_contract.py`, `test_opencode_required_verdict_reconciliation_contract.py`, `test_opencode_required_verdict_runner_release.py`) were removed with them rather than left testing dead code. +- Repair actually landed: the required check's verdict lookup in `opencode-review.yml` now accepts `github-actions[bot]` alongside `opencode-agent`/`opencode-agent[bot]`, matching the receipt gate's own allowlist. Nothing else changed. +- Verification: `tests/test_opencode_required_verdict_regression.py`'s existing assertions (updated to match `#1830`'s already-current wording) plus the repo's full suite confirm no regression. +- Status: The allowlist fix merged into `main` via this PR; the other two proposed changes are superseded and not pursued further. ## 2026-09-02 org-queue-sweep investigation: historical conclusion superseded by PR #1821 diff --git a/tests/test_opencode_event_driven_required_wake.py b/tests/test_opencode_event_driven_required_wake.py deleted file mode 100644 index c1d1c82a06..0000000000 --- a/tests/test_opencode_event_driven_required_wake.py +++ /dev/null @@ -1,49 +0,0 @@ -"""Contracts for event-driven Required OpenCode Review wake reconciliation.""" - -from pathlib import Path - -REQUIRED = Path(".github/workflows/opencode-review.yml") -DISPATCH = Path(".github/workflows/opencode-review-dispatch.yml") -SCHEDULER = Path(".github/workflows/pr-review-merge-scheduler.yml") - - -def _required() -> str: - """Return only current-head formal-verdict admission.""" - text = REQUIRED.read_text(encoding="utf-8") - return text.split(" - name: Fail closed without a current-head OpenCode verdict\n", 1)[1].split("\n cancel-superseded-opencode-review-runs:\n", 1)[0] - - -def _wake() -> str: - """Return only authenticated formal-receipt wake.""" - text = DISPATCH.read_text(encoding="utf-8") - return text.split(" - name: Wake exact-head required OpenCode workflow\n", 1)[1].split("\n - name: Publish repository_dispatch OpenCode status\n", 1)[0] - - -def test_required_verdict_admission_has_no_repository_authored_wait_allocation() -> None: - """Missing verdict fails closed after authoritative reads, not elapsed time.""" - step = _required() - for token in ("while :; do", "poll_interval_seconds", "poll_deadline_epoch", "max_poll_transport_failures", "sleep ", "timeout "): - assert token not in step - - -def test_dispatch_receipt_wake_is_one_exact_state_transition() -> None: - """A formal receipt never introduces a retry, sleep, transport, or review-read loop.""" - step = _wake() - for token in ("for attempt", "seq 1", "sleep ", "timeout ", "/12", "--paginate"): - assert token not in step - assert "pull_requests // []" in step - assert "rerun-failed-jobs" in step - - -def test_workflow_run_completion_closes_review_before_failure_race() -> None: - """Failed completion reruns only when newer formal exact-head evidence exists.""" - scheduler = SCHEDULER.read_text(encoding="utf-8") - job = scheduler.split(" reconcile-opencode-required-verdict:\n", 1)[1].split("\n scan-pr-queue:\n", 1)[0] - assert "github.event_name == 'workflow_run'" in job - assert "github.event.workflow_run.name == 'Required OpenCode Review'" in job - assert "github.event.workflow_run.conclusion == 'failure'" in job - assert "github.event.workflow_run.run_started_at" in job - assert "review_submitted_at" in job and "fromdateiso8601" in job - assert "rerun-failed-jobs" in job - for token in ("for attempt", "while :; do", "sleep ", "timeout "): - assert token not in job diff --git a/tests/test_opencode_formal_verdict_authority_contract.py b/tests/test_opencode_formal_verdict_authority_contract.py deleted file mode 100644 index 6c7aa22702..0000000000 --- a/tests/test_opencode_formal_verdict_authority_contract.py +++ /dev/null @@ -1,141 +0,0 @@ -"""Executable contract for one formal OpenCode verdict authority across workflows.""" - -from __future__ import annotations - -import json -from pathlib import Path -import subprocess - -from scripts.ci import opencode_review_receipt_gate as receipt_gate - - -REQUIRED_WORKFLOW_PATH = Path(".github/workflows/opencode-review.yml") -SCHEDULER_WORKFLOW_PATH = Path(".github/workflows/pr-review-merge-scheduler.yml") -HEAD_SHA = "a" * 40 - - -def _embedded_jq_program(workflow_text: str, variable_name: str, sha_variable: str) -> str: - """Extract one tracked jq selector so the test executes production policy text.""" - assignment_marker = f'{variable_name}="$(printf' - assignment_tail = workflow_text.split(assignment_marker, 1)[1] - jq_marker = f'| jq -r -s --arg sha "${sha_variable}" \'\n' - jq_tail = assignment_tail.split(jq_marker, 1)[1] - return jq_tail.split("\n ')\"", 1)[0] - - -def _run_selector(program: str, review: dict[str, object]) -> str: - """Execute a production jq verdict selector against one paginated review page.""" - completed = subprocess.run( - ["jq", "-r", "-s", "--arg", "sha", HEAD_SHA, program], - input=json.dumps([review]), - text=True, - capture_output=True, - check=False, - ) - assert completed.returncode == 0, completed.stderr - return completed.stdout.strip() - - -def _formal_review(author: str, state: str, body: str) -> dict[str, object]: - """Build a current-head formal review fixture shared across policy surfaces.""" - return { - "id": 7, - "user": {"login": author}, - "commit_id": HEAD_SHA, - "state": state, - "submitted_at": "2026-09-02T13:00:01Z", - "body": body, - } - - -def _policy_programs() -> tuple[str, str, str]: - """Return direct admission, scheduler reconciliation, and scheduler workflow text.""" - required_text = REQUIRED_WORKFLOW_PATH.read_text(encoding="utf-8") - scheduler_text = SCHEDULER_WORKFLOW_PATH.read_text(encoding="utf-8") - admission_program = _embedded_jq_program(required_text, "verdict", "HEAD_SHA") - reconciliation_program = _embedded_jq_program( - scheduler_text, "latest_review", "PR_HEAD_SHA" - ) - return admission_program, reconciliation_program, scheduler_text - - -def test_github_actions_formal_change_request_matches_all_verdict_surfaces() -> None: - """A publisher accepted by the receipt gate must reconcile and admit the same verdict.""" - review = _formal_review( - "github-actions[bot]", - "CHANGES_REQUESTED", - "## Pull request overview\nmodel-unavailable evidence fallback", - ) - accepted, reason = receipt_gate.is_formal_receipt(review, HEAD_SHA, is_draft=False) - assert accepted, reason - - admission_program, reconciliation_program, _scheduler_text = _policy_programs() - assert _run_selector(admission_program, review) == "CHANGES_REQUESTED" - assert _run_selector(reconciliation_program, review).startswith( - "CHANGES_REQUESTED\t" - ) - - -def test_github_actions_formal_approval_matches_all_verdict_surfaces() -> None: - """A formal publisher approval accepted by the receipt gate must wake and admit.""" - review = _formal_review( - "github-actions[bot]", - "APPROVED", - "## Pull request overview\nvalidated exact-head product diff", - ) - accepted, reason = receipt_gate.is_formal_receipt(review, HEAD_SHA, is_draft=False) - assert accepted, reason - - admission_program, reconciliation_program, _scheduler_text = _policy_programs() - assert _run_selector(admission_program, review) == "APPROVED" - assert _run_selector(reconciliation_program, review).startswith("APPROVED\t") - - -def test_unrecognized_reviewer_cannot_admit_or_wake_required_verdict() -> None: - """Human or unrelated-bot reviews must not become OpenCode admission authority.""" - review = _formal_review( - "unrelated-reviewer", - "CHANGES_REQUESTED", - "## Pull request overview\nsubstantive review", - ) - accepted, _reason = receipt_gate.is_formal_receipt(review, HEAD_SHA, is_draft=False) - assert not accepted - - admission_program, reconciliation_program, _scheduler_text = _policy_programs() - assert _run_selector(admission_program, review) == "" - assert _run_selector(reconciliation_program, review) == "" - - -def test_scheduler_accepts_same_second_formal_receipt_for_failed_run() -> None: - """Second-precision GitHub timestamps must not lose a receipt concurrent with run start.""" - _admission_program, _reconciliation_program, scheduler_text = _policy_programs() - assert "($review | fromdateiso8601) >= ($started | fromdateiso8601)" in scheduler_text - assert "($review | fromdateiso8601) > ($started | fromdateiso8601)" not in scheduler_text - - -def test_fallback_marker_invalidates_approval_only_not_change_request() -> None: - """Fallback markers reject APPROVED evidence but never erase a real change request.""" - fallback_body = "## Pull request overview\ndeterministic fallback approval" - change_request = _formal_review("opencode-agent[bot]", "CHANGES_REQUESTED", fallback_body) - approval = _formal_review("opencode-agent[bot]", "APPROVED", fallback_body) - - change_ok, change_reason = receipt_gate.is_formal_receipt( - change_request, HEAD_SHA, is_draft=False - ) - approval_ok, _approval_reason = receipt_gate.is_formal_receipt( - approval, HEAD_SHA, is_draft=False - ) - assert change_ok, change_reason - assert not approval_ok - - admission_program, reconciliation_program, _scheduler_text = _policy_programs() - assert _run_selector(admission_program, change_request) == "CHANGES_REQUESTED" - assert _run_selector(reconciliation_program, change_request).startswith( - "CHANGES_REQUESTED\t" - ) - assert _run_selector(admission_program, approval) == "" - # _run_selector intentionally strips jq's trailing whitespace, so an empty - # scheduler state/timestamp tuple is observed as the empty string rather - # than a literal tab. This keeps the oracle causal instead of depending on - # incidental transport whitespace. - assert _run_selector(reconciliation_program, approval) == "" diff --git a/tests/test_opencode_required_verdict_reconciliation_contract.py b/tests/test_opencode_required_verdict_reconciliation_contract.py deleted file mode 100644 index 8c4354c8c1..0000000000 --- a/tests/test_opencode_required_verdict_reconciliation_contract.py +++ /dev/null @@ -1,140 +0,0 @@ -"""Executable regressions for event-driven OpenCode verdict reconciliation.""" - -from __future__ import annotations - -import json -from pathlib import Path -import subprocess -import textwrap - -from scripts.ci import opencode_review_receipt_gate - -SCHEDULER = Path(".github/workflows/pr-review-merge-scheduler.yml") -OPENCODE = Path(".github/workflows/opencode-review.yml") -HEAD = "a" * 40 - - -def _scheduler_jq_filter() -> str: - """Extract the production scheduler review selector.""" - text = SCHEDULER.read_text(encoding="utf-8") - anchor = text.index('latest_review="$(printf') - start = text.index(" (add // [])", anchor) - end_marker = " | @tsv" - end = text.index(end_marker, start) + len(end_marker) - return textwrap.dedent(text[start:end]) - - -def _admission_jq_filter() -> str: - """Extract the production one-shot admission review selector.""" - text = OPENCODE.read_text(encoding="utf-8") - anchor = text.index('verdict="$(printf') - start = text.index(" (add // [])", anchor) - end_marker = "\n end\n" - end = text.index(end_marker, start) + len(end_marker) - return textwrap.dedent(text[start:end]) - - -def _run_filter(filter_text: str, reviews: list[dict[str, object]]) -> str: - """Execute one production jq selector against exact-head fixtures.""" - proc = subprocess.run( - ["jq", "-r", "-s", "--arg", "sha", HEAD, filter_text], - input=json.dumps(reviews), - text=True, - capture_output=True, - check=True, - ) - return proc.stdout.strip() - - -def _scheduler_select(reviews: list[dict[str, object]]) -> str: - """Execute the scheduler selector.""" - return _run_filter(_scheduler_jq_filter(), reviews) - - -def _admission_select(reviews: list[dict[str, object]]) -> str: - """Execute the one-shot admission selector.""" - return _run_filter(_admission_jq_filter(), reviews) - - -def _review( - state: str, - body: str, - *, - login: str = "opencode-agent[bot]", -) -> dict[str, object]: - """Build one exact-head formal-review fixture.""" - return { - "id": 42, - "user": {"login": login}, - "commit_id": HEAD, - "state": state, - "body": f"## Verdict\n{body}\n\nHead SHA: `{HEAD}`", - "submitted_at": "2026-09-02T12:00:00Z", - } - - -def test_marker_bearing_change_request_remains_a_formal_verdict() -> None: - """Fallback markers invalidate approvals only, never a real change request.""" - review = _review("CHANGES_REQUESTED", "deterministic fallback approval: defect remains") - assert _scheduler_select([review]).startswith("CHANGES_REQUESTED\t") - assert _admission_select([review]) == "CHANGES_REQUESTED" - assert opencode_review_receipt_gate.is_formal_receipt( - review, HEAD, is_draft=False - )[0] - - -def test_marker_bearing_approval_is_not_admitted() -> None: - """Synthetic/fallback approval markers still block APPROVED evidence.""" - review = _review("APPROVED", "deterministic fallback approval") - assert _scheduler_select([review]) == "" - assert _admission_select([review]) == "" - assert not opencode_review_receipt_gate.is_formal_receipt( - review, HEAD, is_draft=False - )[0] - - -def test_clean_approval_is_admitted() -> None: - """A clean exact-head approval remains a formal verdict everywhere.""" - review = _review("APPROVED", "real model review") - assert _scheduler_select([review]).startswith("APPROVED\t") - assert _admission_select([review]) == "APPROVED" - assert opencode_review_receipt_gate.is_formal_receipt( - review, HEAD, is_draft=False - )[0] - - -def test_github_actions_formal_receipt_reconciles_and_admits() -> None: - """Every accepted formal publisher must be accepted by all verdict gates.""" - review = _review( - "CHANGES_REQUESTED", - "current-head defect remains", - login="github-actions[bot]", - ) - assert _scheduler_select([review]).startswith("CHANGES_REQUESTED\t") - assert _admission_select([review]) == "CHANGES_REQUESTED" - assert opencode_review_receipt_gate.is_formal_receipt( - review, HEAD, is_draft=False - )[0] - - -def test_same_second_review_is_eligible_for_reconciliation() -> None: - """Second-granularity timestamps must not strand a later same-second review.""" - text = SCHEDULER.read_text(encoding="utf-8") - assert "fromdateiso8601) >= ($started | fromdateiso8601" in text - proc = subprocess.run( - [ - "jq", - "-nr", - "--arg", - "review", - "2026-09-02T12:00:00Z", - "--arg", - "started", - "2026-09-02T12:00:00Z", - "try (($review | fromdateiso8601) >= ($started | fromdateiso8601)) catch false", - ], - text=True, - capture_output=True, - check=True, - ) - assert proc.stdout.strip() == "true" diff --git a/tests/test_opencode_required_verdict_regression.py b/tests/test_opencode_required_verdict_regression.py index 98f0cec6bf..f29b97a663 100644 --- a/tests/test_opencode_required_verdict_regression.py +++ b/tests/test_opencode_required_verdict_regression.py @@ -38,8 +38,7 @@ def fail_closed_script() -> str: step = workflow.split( " - name: Fail closed without a current-head OpenCode verdict\n", 1 )[1] - block = step.split(" run: |\n", 1)[1].split("\n cancel-superseded-opencode-review-runs:\n", 1)[0] - return textwrap.dedent(block) + return textwrap.dedent(step.split(" run: |\n", 1)[1]) def admission_script() -> str: @@ -320,8 +319,6 @@ def test_required_workflow_cannot_succeed_with_an_echo_only_placeholder() -> Non assert "Current-head substantive OpenCode verdict already exists; scheduler wake skipped." in dispatch_step assert "while :; do" not in target_job assert "poll_interval_seconds" not in target_job - assert "poll_deadline_epoch" not in target_job - assert 'sleep "$poll_interval_seconds"' not in target_job assert "180 minutes of polling" not in target_job assert 'gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100"' in workflow assert "github.event.pull_request.head.sha" in workflow @@ -748,7 +745,6 @@ def test_formal_receipt_wake_reruns_the_immediately_failed_required_job() -> Non dispatched = DISPATCH_WORKFLOW.read_text(encoding="utf-8") assert "for attempt in" not in required assert "while :; do" not in required - assert "poll_deadline_epoch" not in required assert "poll_interval_seconds" not in required assert "180 minutes of polling" not in required assert "rerun-failed-jobs" in dispatched @@ -759,9 +755,7 @@ def test_formal_receipt_wake_reruns_the_immediately_failed_required_job() -> Non assert "select(.id == $run_id)" in dispatched assert 'select(.event == "pull_request_target")' in dispatched assert 'select(.path == ".github/workflows/opencode-review.yml")' in dispatched - assert "pull_requests // []" in dispatched - assert "(.number // 0) | tostring" in dispatched - assert "select(.head_sha == $head)" not in dispatched + assert "select(.head_sha == $head)" in dispatched wake_step = dispatched.split("Wake exact-head required OpenCode workflow", 1)[1].split("\n\n - name:", 1)[0] target_job = dispatched.split(" opencode-review-target:\n", 1)[1] target_permissions = target_job.split(" env:\n", 1)[0] @@ -781,34 +775,46 @@ def test_formal_receipt_wake_reruns_the_immediately_failed_required_job() -> Non assert 'workflow_url | contains("/actions/required_workflows/")' not in wake_step -def wake_selector(run: dict[str, object], *, head: str = HEAD, pr: int = 1437, run_id: int = 42) -> str: - """Execute the wake step's exact run/PR/head validation jq program.""" +def wake_selector(run: dict[str, object], *, head: str = HEAD, run_id: int = 42) -> str: + """Execute the wake step's run-validation jq program in isolation.""" jq = shutil.which("jq") if jq is None: pytest.skip("jq is required to execute the production wake selector") dispatched = DISPATCH_WORKFLOW.read_text(encoding="utf-8") - marker = """jq -r --arg head "$PR_HEAD_SHA" --arg pr "$PR_NUMBER" --argjson run_id "$REQUIRED_RUN_ID" '""" + marker = """jq -r --arg head "$PR_HEAD_SHA" --argjson run_id "$REQUIRED_RUN_ID" '""" start = dispatched.index(marker) + len(marker) end = dispatched.index("\n ')", start) result = subprocess.run( - [jq, "-r", "--arg", "head", head, "--arg", "pr", str(pr), "--argjson", "run_id", str(run_id), dispatched[start:end]], - input=json.dumps(run), text=True, capture_output=True, check=False, + [jq, "-r", "--arg", "head", head, "--argjson", "run_id", str(run_id), dispatched[start:end]], + input=json.dumps(run), + text=True, + capture_output=True, + check=False, ) assert result.returncode == 0, result.stderr return result.stdout.strip() -def required_run(*, run_id: int = 42, pr_head_sha: str = HEAD, pr_number: int = 1437, path: str = ".github/workflows/opencode-review.yml") -> dict[str, object]: - """Build a pull_request_target run whose top-level head_sha is the base SHA.""" +def required_run(*, run_id: int = 42, head_sha: str = HEAD, path: str = ".github/workflows/opencode-review.yml") -> dict[str, object]: + """Build one realistic single-run GET REST API record. + + Mirrors the real shape a sibling repo sees for a run injected by the org's + required-workflow ruleset (this repo's actual central-hub use case): `name` + is the bare workflow name and `display_title` is a plain PR title, with no + PR number or head SHA embedded in either -- unlike a native same-repo + trigger, where both fields carry the rendered `run-name`. + """ return { "id": run_id, - "head_sha": "f" * 40, + "head_sha": head_sha, "event": "pull_request_target", "name": "Required OpenCode Review", "display_title": "Fix an unrelated example bug", "path": path, - "workflow_url": "https://api.github.com/repos/ContextualWisdomLab/example/actions/required_workflows/9", - "pull_requests": [{"number": pr_number, "head": {"sha": pr_head_sha}}], + "workflow_url": ( + "https://api.github.com/repos/ContextualWisdomLab/example" + "/actions/required_workflows/9" + ), "status": "completed", "conclusion": "failure", } @@ -826,12 +832,7 @@ def test_wake_selector_rejects_a_referenced_run_with_a_different_head() -> None: -- the realistic failure mode for an id-based reference, e.g. a superseded run or a stale/forged required_run_id. """ - assert wake_selector(required_run(pr_head_sha="b" * 40)) == "" - - -def test_wake_selector_rejects_a_referenced_run_for_a_different_pr() -> None: - """A run id for another PR cannot receive the wake mutation.""" - assert wake_selector(required_run(pr_number=9999)) == "" + assert wake_selector(required_run(head_sha="b" * 40)) == "" def test_wake_selector_rejects_a_referenced_run_for_a_different_workflow() -> None: @@ -866,7 +867,6 @@ def test_formal_receipt_wakes_the_exact_head_failed_required_run(tmp_path: Path) "FAKE_CALLS": str(calls), "GH_REPOSITORY": "ContextualWisdomLab/example", "GH_TOKEN": "actions-write-token", - "PR_NUMBER": "1437", "PR_HEAD_SHA": HEAD, "REQUIRED_RUN_ID": "42", "WAKE_TOKEN_SOURCE": "PR_REVIEW_MERGE_TOKEN", @@ -895,7 +895,6 @@ def test_sibling_formal_receipt_fails_closed_without_actions_token() -> None: **os.environ, "GH_TOKEN": "", "GH_REPOSITORY": "ContextualWisdomLab/example", - "PR_NUMBER": "1437", "PR_HEAD_SHA": HEAD, "REQUIRED_RUN_ID": "42", "WAKE_TOKEN_SOURCE": "unavailable", diff --git a/tests/test_opencode_required_verdict_runner_release.py b/tests/test_opencode_required_verdict_runner_release.py deleted file mode 100644 index c771af52e1..0000000000 --- a/tests/test_opencode_required_verdict_runner_release.py +++ /dev/null @@ -1,75 +0,0 @@ -"""Regression coverage for one-shot Required OpenCode verdict admission.""" - -from __future__ import annotations - -import json -import os -import shutil -import subprocess -import textwrap -from pathlib import Path - -import pytest - -REQUIRED = Path(".github/workflows/opencode-review.yml") -DISPATCH = Path(".github/workflows/opencode-review-dispatch.yml") -HEAD = "a" * 40 - - -def _required_script() -> str: - """Return the production exact-head verdict-admission shell body.""" - text = REQUIRED.read_text(encoding="utf-8") - step = text.split(" - name: Fail closed without a current-head OpenCode verdict\n", 1)[1] - return textwrap.dedent(step.split(" run: |\n", 1)[1].split("\n cancel-superseded-opencode-review-runs:\n", 1)[0]) - - -def _wake() -> str: - """Return the production formal-receipt exact-run wake step.""" - text = DISPATCH.read_text(encoding="utf-8") - return text.split(" - name: Wake exact-head required OpenCode workflow\n", 1)[1].split("\n - name: Publish repository_dispatch OpenCode status\n", 1)[0] - - -def test_missing_verdict_releases_runner_without_local_wait_allocation() -> None: - """Admission performs complete state reads once and never polls or sleeps.""" - step = _required_script() - assert step.count('gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}"') == 1 - assert step.count('gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews') == 1 - for token in ("while :; do", "poll_interval_seconds", "poll_deadline_epoch", "max_poll_transport_failures", "sleep ", "timeout "): - assert token not in step - - -def test_receipt_wake_binds_exact_pr_head_and_run_without_polling() -> None: - """Authenticated receipt wake is one exact-state transition.""" - step = _wake() - for token in ("for attempt", "while :; do", "seq 1", "sleep ", "timeout ", "/12", "--paginate"): - assert token not in step - assert "pull_requests // []" in step - assert "rerun-failed-jobs" in step - assert "advanced concurrently" in step - - -def test_missing_verdict_fails_after_one_live_and_one_reviews_read(tmp_path: Path) -> None: - """No formal verdict causes exactly two GitHub reads and an immediate failure.""" - bash = shutil.which("bash") - jq = shutil.which("jq") - if bash is None or jq is None: - pytest.skip("bash and jq are required") - calls = tmp_path / "calls" - gh = tmp_path / "gh" - gh.write_text( - "#!/usr/bin/env bash\nset -euo pipefail\nprintf '%s\\n' \"$*\" >>\"$CALLS\"\n" - "if [[ \"$*\" == \"api repos/ContextualWisdomLab/example/pulls/42\" ]]; then printf '%s\\n' \"$LIVE_PR\"; exit 0; fi\n" - "if [[ \"$*\" == \"api --paginate repos/ContextualWisdomLab/example/pulls/42/reviews?per_page=100\" ]]; then printf '[]\\n'; exit 0; fi\n" - "exit 97\n", - encoding="utf-8", - ) - gh.chmod(0o755) - result = subprocess.run( - [bash, "-c", _required_script()], - env={**os.environ, "PATH": f"{tmp_path}{os.pathsep}{os.environ.get('PATH', '')}", "CALLS": str(calls), "LIVE_PR": json.dumps({"head": {"sha": HEAD}, "draft": False, "state": "open"}), "GH_TOKEN": "token", "TARGET_REPOSITORY": "ContextualWisdomLab/example", "PR_NUMBER": "42", "HEAD_SHA": HEAD, "PR_ACTION": "synchronize", "PR_DRAFT": "false"}, - text=True, - capture_output=True, - check=False, - ) - assert result.returncode == 1, result.stderr - assert len(calls.read_text(encoding="utf-8").splitlines()) == 2 diff --git a/tests/test_pr_review_autofix_nvidia_nim_contract.py b/tests/test_pr_review_autofix_nvidia_nim_contract.py index 480f899c28..5366ce5de5 100644 --- a/tests/test_pr_review_autofix_nvidia_nim_contract.py +++ b/tests/test_pr_review_autofix_nvidia_nim_contract.py @@ -17,7 +17,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 = "4c1a917920162b887fae1ad53edfa3133af21033" +REVIEW_DISPATCH_BLOB_SHA = "0823eac0d21414b1f0b9fb953ac6bf93e573f7d6" def _workflow_text(path: Path) -> str: From 125c2a767e415f4ba18d50cea92163f7318eef4e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 7 Sep 2026 07:58:53 +0900 Subject: [PATCH 58/59] test(opencode): contract workflow actor verdict authority --- docs/product-technical-gap-baseline.md | 6 +++--- tests/test_opencode_required_verdict_regression.py | 7 +++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md index 760d0d8b5f..4788d58522 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -2654,9 +2654,9 @@ Higgins, S. S., Crepalde, N., & Fernandes, L. (2021). Segmented multiplexity: A - Gap, as originally scoped: Required OpenCode verdict admission occupied a hosted runner while waiting; an intermediate repair then introduced fixed dispatch retry/sleep/transport allocations (`12`, `5s`, `30s`) without a governing model or standard; the required check's own verdict lookup was also inconsistent with `scripts/ci/opencode_review_receipt_gate.py`'s `FORMAL_AUTHORS` allowlist. - Causal owner: `ContextualWisdomLab/.github` required review and merge-control workflows. - **2026-09-05 reconciliation: two of this PR's three proposed changes are now superseded by separately-merged, later work; only the allowlist fix survives.** `#1830` ("release required runner after dispatch," merged 2026-09-04) independently rewrote `opencode-review.yml`'s fail-closed step to one live PR read plus one Reviews read with immediate fail-closed, achieving the runner-release goal a different way, and its own `tests/test_opencode_required_rerun_capacity.py` still expects `opencode-review-dispatch.yml`'s wake step to keep its original 12-attempt loop and `.head_sha`-based run matching — this PR's proposed single-lookup/`pull_requests[]`-matching replacement for that step directly contradicts that already-tested, already-shipping design, so it was dropped rather than pushed through over a live test disagreement. Separately, `#1840` ("stop required-check completion fanout," merged 2026-09-04) deliberately removed every `workflow_run:` listener from `pr-review-merge-scheduler.yml` in favor of GitHub's native auto-merge; this PR's proposed `reconcile-opencode-required-verdict` `workflow_run: completed` handler would reintroduce exactly the mechanism `#1840` retired, so it was also dropped. Both reverted pieces' dedicated test files (`test_opencode_event_driven_required_wake.py`, `test_opencode_formal_verdict_authority_contract.py`, `test_opencode_required_verdict_reconciliation_contract.py`, `test_opencode_required_verdict_runner_release.py`) were removed with them rather than left testing dead code. -- Repair actually landed: the required check's verdict lookup in `opencode-review.yml` now accepts `github-actions[bot]` alongside `opencode-agent`/`opencode-agent[bot]`, matching the receipt gate's own allowlist. Nothing else changed. -- Verification: `tests/test_opencode_required_verdict_regression.py`'s existing assertions (updated to match `#1830`'s already-current wording) plus the repo's full suite confirm no regression. -- Status: The allowlist fix merged into `main` via this PR; the other two proposed changes are superseded and not pursued further. +- Repair proposed on this PR branch: the required check's verdict lookup in `opencode-review.yml` accepts `github-actions[bot]` alongside `opencode-agent`/`opencode-agent[bot]`, matching the receipt gate's own allowlist. Nothing else changes. +- Verification contract: `tests/test_opencode_required_verdict_regression.py` directly executes the production jq filter and requires an exact-head `github-actions[bot]` `APPROVED` verdict to be admitted, while its existing negative actor and stale/fallback cases remain fail-closed. Fresh hosted checks on the unchanged final head remain authoritative. +- Status: Proposed on this PR branch and not yet protected-main authority. Integrate only through ordinary protection after fresh exact-head checks and review are terminal-clean; the other two proposed changes are superseded and not pursued further. ## 2026-09-02 org-queue-sweep investigation: historical conclusion superseded by PR #1821 diff --git a/tests/test_opencode_required_verdict_regression.py b/tests/test_opencode_required_verdict_regression.py index f29b97a663..1b8643d410 100644 --- a/tests/test_opencode_required_verdict_regression.py +++ b/tests/test_opencode_required_verdict_regression.py @@ -154,6 +154,13 @@ def test_runtime_required_verdict_ignores_later_nonformal_current_head_comment( ) == state +def test_runtime_required_verdict_accepts_github_actions_formal_publisher() -> None: + """The canonical workflow actor can publish an exact-head formal verdict.""" + workflow_actor = review(state="APPROVED") + workflow_actor["user"] = {"login": "github-actions[bot]"} + assert runtime_verdict([workflow_actor]) == "APPROVED" + + def test_runtime_required_verdict_rejects_other_actor() -> None: """A non-OpenCode formal review cannot satisfy the runtime filter.""" human = review(state="APPROVED") From 14344f7c1ca70c96878f1e0177ce243548921037 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 7 Sep 2026 09:00:58 +0900 Subject: [PATCH 59/59] test(opencode): cover both workflow verdict states --- CHANGELOG.md | 2 ++ tests/test_opencode_required_verdict_regression.py | 12 ++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bf2327f2b..b86ceb1a11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,8 @@ ### CodeQL scan dispatch matrix serialisation - Serialised the dispatched CodeQL matrix with `toJSON()` in `codeql-scan-dispatch.yml`. `codeql-pr.yml` sends `client_payload.matrix` as an array and the handler assigned it straight into `env:`, where a value must be a scalar, so GitHub rejected the step with "A sequence was not expected" and the dispatched scan never ran -- 0 successes against 136 failures since the handler was added in #1776. The validate step already consumes the value through `jq`, so JSON text is the shape it was written for and no consumer changes. Added a string contract test, because neither `yaml.safe_load` nor `actionlint` 1.7.12 flags this: it is an Actions template rule, so only GitHub's own validator rejects it and no local gate catches the class. +### Required OpenCode formal-review publisher alignment + - **Accept `github-actions[bot]` in the required OpenCode check's own verdict lookup.** `scripts/ci/opencode_review_receipt_gate.py`'s `FORMAL_AUTHORS` allowlist already accepted `github-actions[bot]` as a formal reviewer, but the required check's own jq verdict-matching in `opencode-review.yml` only recognized `opencode-agent`/`opencode-agent[bot]` — so the receipt gate could wake a run for a `github-actions[bot]` review that the required job's own admission logic would never actually recognize. Fixed the inconsistency; no other behavior change. ### Contextual-orchestrator pin refresh diff --git a/tests/test_opencode_required_verdict_regression.py b/tests/test_opencode_required_verdict_regression.py index 1b8643d410..00504c883f 100644 --- a/tests/test_opencode_required_verdict_regression.py +++ b/tests/test_opencode_required_verdict_regression.py @@ -154,11 +154,15 @@ def test_runtime_required_verdict_ignores_later_nonformal_current_head_comment( ) == state -def test_runtime_required_verdict_accepts_github_actions_formal_publisher() -> None: - """The canonical workflow actor can publish an exact-head formal verdict.""" - workflow_actor = review(state="APPROVED") +@pytest.mark.parametrize("state", ("APPROVED", "CHANGES_REQUESTED")) +def test_runtime_required_verdict_accepts_github_actions_formal_publisher( + state: str, +) -> None: + """The canonical workflow actor can publish either exact-head formal verdict.""" + body = "deterministic fallback approval" if state == "CHANGES_REQUESTED" else "" + workflow_actor = review(state=state, body=body) workflow_actor["user"] = {"login": "github-actions[bot]"} - assert runtime_verdict([workflow_actor]) == "APPROVED" + assert runtime_verdict([workflow_actor]) == state def test_runtime_required_verdict_rejects_other_actor() -> None: