Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
85558f1
fix(opencode): skip duplicate same-head review dispatch while in flight
seonghobae Sep 19, 2026
5d1ab11
test(opencode): cover exact saturated dispatch identity
seonghobae Sep 19, 2026
e8b6570
fix(opencode): preserve exact saturated dispatch identity
seonghobae Sep 19, 2026
9535e7a
docs(opencode): record saturated exact-run contract
seonghobae Sep 19, 2026
b6c5a19
test(opencode): reproduce dispatch admission races
seonghobae Sep 19, 2026
f70922f
fix(opencode): bind in-flight state to canonical workflow
seonghobae Sep 19, 2026
e0b9702
fix(opencode): serialize same-head review dispatch
seonghobae Sep 19, 2026
54b20d8
fix(opencode): enforce serialized dispatch ownership
seonghobae Sep 19, 2026
91424c2
test(opencode): align serialized owner contract
seonghobae Sep 19, 2026
ee56022
test(opencode): isolate stale state to one active status
seonghobae Sep 19, 2026
017b563
docs(opencode): record serialized owner repair
seonghobae Sep 19, 2026
3f1360e
test(opencode): preserve queued dispatch owners
seonghobae Sep 19, 2026
dfff8b3
fix(opencode): retain pending dispatch owners
seonghobae Sep 19, 2026
24b2452
fix(opencode): remove dynamic dispatch cancellation
seonghobae Sep 19, 2026
a33c0b4
test(opencode): align queued-owner regression
seonghobae Sep 19, 2026
f3f6cc2
docs(opencode): record pending-run replacement RCA
seonghobae Sep 19, 2026
1cf2d42
test(opencode): require cross-head dispatch admission
seonghobae Sep 19, 2026
81f15bf
fix(opencode): admit newer heads past dispatch queue
seonghobae Sep 19, 2026
115f88f
docs(opencode): record cross-head retirement boundary
seonghobae Sep 19, 2026
b01c701
test(opencode): bind exact-head workflow group
seonghobae Sep 19, 2026
054f695
fix(opencode): reject empty paginated run envelope
seonghobae Sep 19, 2026
16de378
fix(opencode): admit only explicit dispatch states
seonghobae Sep 19, 2026
1f40455
test(opencode): align explicit missing-state contract
seonghobae Sep 19, 2026
e6113e1
test(opencode): repin reviewed dispatch workflow
seonghobae Sep 19, 2026
f610478
test(opencode): align cross-head queue contract
seonghobae Sep 19, 2026
9da1d3f
docs(opencode): clarify layered concurrency contract
seonghobae Sep 19, 2026
70df6a1
docs(opencode): align public dispatch contract
seonghobae Sep 19, 2026
91c19fd
fix(opencode): reject malformed run pages
seonghobae Sep 19, 2026
5fdce9b
fix(opencode): fail closed on malformed runs; list once
seonghobae Sep 20, 2026
368b87c
fix(opencode): reject missing/unknown workflow run status
seonghobae Sep 20, 2026
0d472b4
test(opencode): model queued in-flight dispatch status
seonghobae Sep 21, 2026
928fc27
chore(test): restore trailing newline
seonghobae Sep 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 64 additions & 17 deletions .github/workflows/opencode-review-dispatch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,18 @@ on:
types: [opencode-review]

concurrency:
# Workflow-level admission, for the same reason strix.yml, noema-review.yml and
# opencode-review.yml carry theirs at this level: a job-level group is never
# evaluated while the whole run waits behind the organization job ceiling, so
# superseded dispatches for one pull request coalesce only after each of them
# has already been allocated a runner. Measured on 2026-09-06: of the five
# dispatch runs that passed `validate-pr-metadata`, four were rejected hours
# later by `opencode-review`'s privileged metadata check because the head had
# moved while they queued (runs 34002473295, 34010256951, 34015973300,
# 34016922761) -- each after `coverage-source-tree` and `coverage-evidence`
# had run. Cancelling the superseded run at creation returns that slot instead
# of spending it to discover the review's subject no longer exists.
#
# The key is the target pull request, matching the job-level group below and
# codeql-scan-dispatch.yml's workflow-level group; `github.run_id` keeps runs
# without a payload in their own groups rather than colliding.
# Workflow-level admission serializes dispatches for one exact pull-request
# head before a runner is allocated. GitHub's default single-pending policy
# replaces an existing pending run, so queue every same-head event instead.
# A newer head uses a different workflow group and can reach the downstream
# PR-scoped cancel-in-progress boundary to retire stale semantic work.
# `github.run_id` keeps malformed payloads in separate groups.
group: >-
opencode-review-dispatch-${{
github.event.client_payload.target_repository || github.repository }}-${{
github.event.client_payload.pr_number || github.run_id }}
cancel-in-progress: true
github.event.client_payload.pr_number || github.run_id }}-${{
github.event.client_payload.pr_head_sha || github.run_id }}
queue: max

permissions:
contents: read
Expand Down Expand Up @@ -69,6 +61,7 @@ jobs:
head_ref: ${{ steps.validate.outputs.head_ref }}
head_sha: ${{ steps.validate.outputs.head_sha }}
is_private: ${{ steps.validate.outputs.is_private }}
needs_review: ${{ steps.existing_receipt.outputs.needs_review }}
steps:
- name: Exchange OpenCode app token for target repository metadata reads
id: metadata_read_app_token
Expand Down Expand Up @@ -256,12 +249,62 @@ jobs:
} >>"$GITHUB_OUTPUT"
printf 'Validated current live metadata for %s#%s: base=%s/%s head=%s/%s.\n' "$TARGET_REPOSITORY" "$PR_NUMBER" "$live_base_ref" "$live_base_sha" "$live_head_ref" "$live_head_sha"

- name: Retire serialized same-head duplicate with formal receipt
id: existing_receipt
env:
CENTRAL_GH_TOKEN: ${{ github.token }}
GH_TOKEN: ${{ steps.metadata_read_app_token.outputs.token || secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN || github.token }}
TARGET_REPOSITORY: ${{ steps.validate.outputs.target_repository }}
PR_NUMBER: ${{ steps.validate.outputs.pr_number }}
HEAD_SHA: ${{ steps.validate.outputs.head_sha }}
run: |
set -euo pipefail
helper="$(mktemp)"
trap 'rm -f "$helper"' EXIT
GH_TOKEN="$CENTRAL_GH_TOKEN" gh api \
"repos/ContextualWisdomLab/.github/contents/scripts/ci/opencode_review_receipt_gate.py?ref=${GITHUB_SHA}" \
--jq .content | base64 --decode >"$helper"
receipt_state="$(
python3 - "$helper" "$TARGET_REPOSITORY" "$PR_NUMBER" "$HEAD_SHA" <<'PY'
import importlib.machinery
import importlib.util
import sys

helper_path, repository, number, head_sha = sys.argv[1:]
loader = importlib.machinery.SourceFileLoader(
"trusted_opencode_receipt_gate", helper_path
)
spec = importlib.util.spec_from_loader(loader.name, loader)
if spec is None or spec.loader is None:
raise RuntimeError("trusted OpenCode receipt helper could not be loaded")
gate = importlib.util.module_from_spec(spec)
spec.loader.exec_module(gate)
reviews = gate.fetch_reviews(repository, int(number))
receipt, _reason = gate.evaluate_receipts(reviews, head_sha, is_draft=False)
print("present" if receipt is not None else "missing")
PY
)"
case "$receipt_state" in
present)
echo "needs_review=false" >>"$GITHUB_OUTPUT"
echo "Serialized same-head duplicate retired: a formal exact-head receipt already exists."
;;
missing)
echo "needs_review=true" >>"$GITHUB_OUTPUT"
;;
*)
echo "::error::Trusted OpenCode receipt helper returned an invalid state."
exit 1
;;
esac

- name: Exchange OpenCode app token for target repository coverage reads
id: coverage_read_app_token
if: >-
github.event_name == 'repository_dispatch'
&& steps.validate.outputs.target_repository != ''
&& steps.validate.outputs.target_repository != github.repository
&& steps.existing_receipt.outputs.needs_review == 'true'
env:
OIDC_AUDIENCE: opencode-github-action
OPENCODE_API_BASE_URL: https://api.opencode.ai
Expand Down Expand Up @@ -327,6 +370,7 @@ jobs:
} >>"$GITHUB_OUTPUT"

- name: Materialize pull request merge tree for coverage measurement
if: steps.existing_receipt.outputs.needs_review == 'true'
env:
GH_TOKEN: ${{ steps.coverage_read_app_token.outputs.token || secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN || github.token }}
TARGET_REPOSITORY: ${{ steps.validate.outputs.target_repository }}
Expand Down Expand Up @@ -381,6 +425,7 @@ jobs:
tar -cf "$COVERAGE_SOURCE_ARCHIVE" -C "$COVERAGE_SOURCE_WORKDIR" .

- name: Upload materialized pull request merge tree
if: steps.existing_receipt.outputs.needs_review == 'true'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: opencode-coverage-source
Expand All @@ -393,6 +438,7 @@ jobs:
needs: [validate-pr-metadata]
if: >-
needs.validate-pr-metadata.result == 'success'
&& needs.validate-pr-metadata.outputs.needs_review == 'true'
&& github.event_name == 'repository_dispatch'
runs-on: ubuntu-24.04
timeout-minutes: 300
Expand Down Expand Up @@ -2332,6 +2378,7 @@ jobs:
if: >-
always()
&& needs.validate-pr-metadata.result == 'success'
&& needs.validate-pr-metadata.outputs.needs_review == 'true'
&& needs.coverage-evidence.result != 'cancelled'
&& github.event_name == 'repository_dispatch'
concurrency:
Expand Down
48 changes: 38 additions & 10 deletions .github/workflows/opencode-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -497,16 +497,44 @@ jobs:
exit 1
fi
echo "::add-mask::$app_token"
jq -cn \
--arg target_repository "$TARGET_REPOSITORY" \
--arg pr_number "$PR_NUMBER" \
--arg pr_base_ref "$BASE_BRANCH" \
--arg pr_base_sha "$BASE_SHA" \
--arg pr_head_ref "$HEAD_REF" \
--arg pr_head_sha "$HEAD_SHA" \
--arg required_run_id "$GITHUB_RUN_ID" \
'{event_type:"opencode-review",client_payload:{target_repository:$target_repository,pr_number:$pr_number,pr_base_ref:$pr_base_ref,pr_base_sha:$pr_base_sha,pr_head_ref:$pr_head_ref,pr_head_sha:$pr_head_sha,required_run_id:$required_run_id}}' |
GH_TOKEN="$app_token" gh api -X POST repos/ContextualWisdomLab/.github/dispatches --input -
# Same-head in-flight dedupe (pg-erd-cloud#1183 / appguardrail#1247):
# a second repository_dispatch for an unchanged head cancels the
# already-queued/running central review via dispatch concurrency and
# restarts the multi-hour queue→9s-fail handshake. Skip the POST when
# an exact-head OpenCode Review Dispatch is already queued or running;
# still fall through to fail-closed without a formal verdict.
inflight_helper="$(mktemp)"
trap 'rm -f "$helper" "$inflight_helper"' EXIT
gh api "repos/ContextualWisdomLab/.github/contents/scripts/ci/opencode_inflight_dispatch_gate.py?ref=${WORKFLOW_SHA}" \
--jq .content | base64 --decode >"$inflight_helper"
inflight_state="$(
GH_TOKEN="$app_token" python3 "$inflight_helper" \
--target-repository "$TARGET_REPOSITORY" \
--pr-number "$PR_NUMBER" \
--head-sha "$HEAD_SHA"
)"
if [ "$inflight_state" = "present" ]; then
echo "Exact-head OpenCode Review Dispatch already queued or running; duplicate repository_dispatch skipped."
else
if [ "$inflight_state" = "stale" ]; then
echo "Prior-head OpenCode dispatch observed; the new head will reach PR-scoped stale-work retirement."
elif [ "$inflight_state" = "missing" ]; then
:
else
echo "::error::OpenCode in-flight dispatch gate returned an invalid state."
exit 1
fi
jq -cn \
--arg target_repository "$TARGET_REPOSITORY" \
--arg pr_number "$PR_NUMBER" \
--arg pr_base_ref "$BASE_BRANCH" \
--arg pr_base_sha "$BASE_SHA" \
--arg pr_head_ref "$HEAD_REF" \
--arg pr_head_sha "$HEAD_SHA" \
--arg required_run_id "$GITHUB_RUN_ID" \
'{event_type:"opencode-review",client_payload:{target_repository:$target_repository,pr_number:$pr_number,pr_base_ref:$pr_base_ref,pr_base_sha:$pr_base_sha,pr_head_ref:$pr_head_ref,pr_head_sha:$pr_head_sha,required_run_id:$required_run_id}}' |
GH_TOKEN="$app_token" gh api -X POST repos/ContextualWisdomLab/.github/dispatches --input -
fi

- name: Fail closed without a current-head OpenCode verdict
env:
Expand Down
Loading
Loading