Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
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
30 changes: 27 additions & 3 deletions .github/workflows/opencode-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,13 @@ permissions:
jobs:
coverage-evidence:
name: coverage-evidence
if: github.event_name == 'workflow_dispatch'
if: >-
github.event_name == 'workflow_dispatch'
|| (
github.event_name == 'pull_request_target'
&& github.event.pull_request.draft != true
&& github.event.pull_request.head.repo.full_name == github.repository
)
runs-on: ubuntu-latest
permissions:
contents: read
Expand Down Expand Up @@ -328,6 +334,7 @@ jobs:
npx -y "$CODEGRAPH_PACKAGE" status

- name: Prepare bounded OpenCode review evidence
timeout-minutes: 8
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPOSITORY: ${{ github.repository }}
Expand Down Expand Up @@ -430,6 +437,7 @@ jobs:
fi

if [ "$attempt" -lt "$attempts" ]; then
printf 'Failed-check evidence attempt %s/%s found no completed peer-check failure while peer checks are still running; retrying in %ss.\n' "$attempt" "$attempts" "$sleep_seconds" >&2
sleep "$sleep_seconds"
fi
attempt=$((attempt + 1))
Expand Down Expand Up @@ -1847,6 +1855,7 @@ jobs:

- name: Approve PR if OpenCode review passed
if: always()
timeout-minutes: 45
env:
GH_TOKEN: ${{ steps.opencode_app_token.outputs.token || secrets.OPENCODE_APPROVE_TOKEN || secrets.GITHUB_TOKEN }}
GH_REPOSITORY: ${{ github.repository }}
Expand Down Expand Up @@ -1877,7 +1886,7 @@ jobs:
OPENCODE_O_SERIES_FALLBACK_OUTPUT_FILE: ${{ runner.temp }}/opencode-review-o-series-fallback.md
PR_BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.inputs.pr_base_sha }}
PR_HEAD_SHA: ${{ github.event.pull_request.head.sha || github.event.inputs.pr_head_sha }}
APPROVAL_CHECK_WAIT_ATTEMPTS: "241"
APPROVAL_CHECK_WAIT_ATTEMPTS: "81"
APPROVAL_CHECK_WAIT_SLEEP_SECONDS: "30"
CHECK_LOOKUP_RETRY_ATTEMPTS: "5"
CHECK_LOOKUP_RETRY_SLEEP_SECONDS: "5"
Expand Down Expand Up @@ -3030,10 +3039,25 @@ jobs:
local input_file="$1"
local output_file="$2"
local manual_strix_success_target
local manual_strix_success_run_id
local failed_strix_run_id

manual_strix_success_target="$(current_head_manual_strix_success_status || true)"
if [ -n "$manual_strix_success_target" ]; then
awk '$0 !~ /^- (Strix Security Scan\/strix|strix):/' "$input_file" >"$output_file"
manual_strix_success_run_id="$(printf '%s' "$manual_strix_success_target" | sed -n 's#.*/actions/runs/\([0-9][0-9]*\).*#\1#p')"
while IFS= read -r rollup_line; do
case "$rollup_line" in
"- Strix Security Scan/"*|"- strix:"*)
failed_strix_run_id="$(printf '%s' "$rollup_line" | sed -n 's#.*/actions/runs/\([0-9][0-9]*\).*#\1#p')"
if [ -z "$failed_strix_run_id" ] ||
[ -z "$manual_strix_success_run_id" ] ||
[ "$failed_strix_run_id" -lt "$manual_strix_success_run_id" ]; then
continue
fi
;;
esac
printf '%s\n' "$rollup_line"
done <"$input_file" >"$output_file"
else
cat "$input_file" >"$output_file"
fi
Expand Down
11 changes: 8 additions & 3 deletions .github/workflows/strix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,12 @@ jobs:
exit 1

- name: Self-test Strix gate script
timeout-minutes: 10
working-directory: ${{ runner.temp }}/trusted-workspace
run: bash "$TRUSTED_STRIX_GATE_TEST"
run: |
set -euo pipefail
printf 'Running Strix gate self-test with a 10-minute step timeout.\n'
bash "$TRUSTED_STRIX_GATE_TEST"

- name: Gate Strix secrets
id: gate
Expand Down Expand Up @@ -341,6 +345,7 @@ jobs:

- name: Run Strix (quick)
if: steps.gate.outputs.enabled == 'true'
timeout-minutes: 30
# Security invariant for pull_request_target: execute only from the
# trusted base checkout. The gate copies PR-head blobs into an isolated
# temporary scope with execute bits stripped, then scans that scope as
Expand Down Expand Up @@ -384,11 +389,11 @@ jobs:
IS_PR_EVIDENCE_RUN: ${{ (github.event_name == 'pull_request_target' || github.event.inputs.pr_number != '') && 'true' || 'false' }}
run: |
budget_suffix="TIME""OUT"
process_budget_seconds="3600"
process_budget_seconds="1500"
export "LLM_${budget_suffix}=120"
export "STRIX_MEMORY_COMPRESSOR_${budget_suffix}=10"
export "STRIX_PROCESS_${budget_suffix}_SECONDS=$process_budget_seconds"
export "STRIX_TOTAL_${budget_suffix}_SECONDS=7200"
export "STRIX_TOTAL_${budget_suffix}_SECONDS=1800"
bash "$TRUSTED_STRIX_GATE"

- name: Collect Strix reports for artifact upload
Expand Down
25 changes: 14 additions & 11 deletions PR_GOVERNANCE_AUDIT.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,16 @@ OpenCode decides; GitHub Actions mutates.
- Prefer `gh pr merge --auto --merge --match-head-commit <head>` when native auto-merge is enabled.
- Use direct `gh pr merge --merge --match-head-commit <head>` only when the repo policy already allows immediate merge.
- OpenCode app-token merges are deprecated; keep app tokens for review publication, not mechanical branch mutation.
- OpenCode approval publication must be bounded. Peer GitHub Checks can be awaited, but the approval step itself must time out instead of running for hours; the current central limit is a 45 minute approval step with 81 peer-check probes at 30 seconds.
- Tool failures are not source findings. Model failure, API transient, update-branch `422/403`, fork/write-permission failure, conflict, failed checks, and stale review state must be reported as distinct scheduler outcomes.

## Live Repository Inventory

Live generated: 2026-06-23 04:18 KST. PR #28 rechecked: 2026-06-23 11:43 KST.
Live generated: 2026-06-23 04:18 KST. PR #28 post-merge refresh: 2026-06-23 16:05 KST.

| Repo | Flow | Default | Auto | Rulesets | Required checks | Stale dismissal | Merge queue | Workflows | Recent merged actor |
|---|---:|---:|---:|---|---|---:|---:|---|---|
| `ContextualWisdomLab/.github` | GitHub Flow | `main` | on | `Lock default branch` | none | true | no | OpenCode Review; PR Review Merge Scheduler; Strix Security Scan | #18 `seonghobae`; #17 `seonghobae`; #2 `seonghobae` |
| `ContextualWisdomLab/.github` | GitHub Flow | `main` | on | `Lock default branch` | none | true | no | OpenCode Review; PR Review Merge Scheduler; Strix Security Scan | #28 `seonghobae` merge `a025be1`; #18 `seonghobae`; #17 `seonghobae` |
| `ContextualWisdomLab/bandscope` | Git Flow | `develop` | on | `Lock default branch` | `ci / build-and-test`, `dependency-review`, `security-audit`, `CodeQL`, `sbom`, `release-preflight`, `gate / build / windows`, `gate / build / macos`, `trivy-fs-scan` | false | no | OpenCode Review; PR Review Merge Scheduler; Strix Security Scan | #427 `github-actions`; #408 `seonghobae`; #405 `seonghobae` |
| `ContextualWisdomLab/clearfolio` | GitHub Flow | `main` | off | `PR` | none | false | no | OpenCode Review; Strix Security Scan | #9 `seonghobae`; #8 `seonghobae`; #7 `seonghobae` |
| `ContextualWisdomLab/codec-carver` | GitHub Flow | `main` | on | `Lock default branch` | none | true | no | OpenCode Review; Scheduled PR Review Merge; Strix Security Scan | #94 `opencode-agent`; #93 `seonghobae`; #90 `seonghobae` |
Expand All @@ -37,7 +38,7 @@ Live generated: 2026-06-23 04:18 KST. PR #28 rechecked: 2026-06-23 11:43 KST.

| Repo | Gap |
|---|---|
| `.github` | PR #28 head `60c821e` is blocked by current-head OpenCode `CHANGES_REQUESTED` and `strix` status failure. Same-head manual Strix run `27996904501` passed self-test but failed `Run Strix (quick)`, so it is not merge evidence. |
| `.github` | PR #28 is merged at `a025be1` after same-head manual Strix run `28007326148`, same-head OpenCode run `28008174977`, unresolved review threads `0`, and guarded merge against head `811446d`. Remaining open PRs #19-#27 and #29-#36 are still blocked by `CHANGES_REQUESTED` and/or `DIRTY`; the scheduler dry run inspected 17 PRs and made no mutation. |
| `bandscope` | Required checks are repo-specific and broad; keep GitHub native auto-merge as the check interpreter. |
| `clearfolio` | Auto-merge is off and the PR Review Merge Scheduler is missing. |
| `codec-carver` | Latest merged sample #94 still used `opencode-agent`; replace the legacy scheduler with the central GitHub Actions path. |
Expand All @@ -53,7 +54,7 @@ Live generated: 2026-06-23 04:18 KST. PR #28 rechecked: 2026-06-23 11:43 KST.
| Repo | Live evidence | Adopt | Reject |
|---|---|---|---|
| `naruon` | `develop`, strict required checks `opencode-review` and `strix`, stale review dismissal enabled. Open PRs show `BEHIND`, `DIRTY`, and `CHANGES_REQUESTED` cases. | Strict current-head evidence and stale-dismissal awareness. | Treating `BEHIND` as merge-ready. |
| `.github` | PR #28 is `MERGEABLE` but `BLOCKED`; required PR-target Strix failed on trusted-base self-test, and same-head manual Strix run `27996904501` also failed at `Run Strix (quick)` after publishing `strix` status failure. The latest OpenCode review still cited the stale PR-target Strix URL instead of the same-head manual Strix failure. | Same-head manual evidence for self-modifying trusted workflow changes, plus explicit handling for failed manual evidence. | Treating stale PR-target failure logs as the only current-head diagnosis after a same-head manual Strix rerun exists. |
| `.github` | PR #28 head `811446d` reached current-head approval after manual Strix run `28007326148` published a successful `strix` status and manual OpenCode run `28008174977` approved the same head; it was merged by `seonghobae` with merge commit `a025be1`. The earlier PR-target Strix failure remains useful only as the reason same-head manual evidence was required. | Same-head manual evidence for self-modifying trusted workflow changes, current-head OpenCode approval, unresolved thread check, and `--match-head-commit` guarded merge. | Treating stale PR-target failure logs as merge blockers after newer same-head evidence exists. |
| `pg-erd-cloud` | Recent PRs #236, #237, #239 were merged by `app/github-actions`. | GitHub Actions as mechanical merge actor with head guard. | Human-only queue draining. |
| `codec-carver` | Recent PR #94 was merged by `app/opencode-agent`, and the repo still has legacy `Scheduled PR Review Merge`. | Native auto-merge path for current-head approved PRs. | OpenCode app as merge actor. |
| `VibeSec` | PR #108 had native auto-merge enabled; #106 merged by `app/github-actions`; #109 merged by human. | Keep native auto-merge as preferred waiting path. | Repo-by-repo actor inconsistency. |
Expand All @@ -78,25 +79,24 @@ $ python3 scripts/ci/pr_review_merge_scheduler.py --self-test
self-test passed

$ python3 scripts/ci/pr_review_merge_scheduler.py --repo ContextualWisdomLab/.github --base-branch main --project-flow github-flow --dry-run --max-prs 40 --no-trigger-reviews
PR #19: block: current-head OpenCode review requested changes
PR #20: block: current-head OpenCode review requested changes
PR #19: block: merge conflict: DIRTY
PR #20: block: merge conflict: DIRTY
PR #21: block: current-head OpenCode review requested changes
PR #22: block: current-head OpenCode review requested changes
PR #22: block: merge conflict: DIRTY
PR #23: block: merge conflict: DIRTY
PR #24: block: current-head OpenCode review requested changes
PR #25: block: current-head OpenCode review requested changes
PR #26: block: current-head OpenCode review requested changes
PR #27: block: current-head OpenCode review requested changes
PR #28: block: current-head OpenCode review requested changes
PR #29: block: current-head OpenCode review requested changes
PR #30: block: current-head OpenCode review requested changes
PR #31: block: current-head OpenCode review requested changes
PR #32: block: current-head OpenCode review requested changes
PR #33: block: current-head OpenCode review requested changes
PR #34: block: current-head OpenCode review requested changes
PR #35: block: current-head OpenCode review requested changes
PR #36: block: current-head OpenCode review requested changes
{"base_branch": "main", "counts": {"block": 18}, "dry_run": true, "inspected": 18, "project_flow": "github-flow"}
PR #36: block: merge conflict: DIRTY
{"base_branch": "main", "counts": {"block": 17}, "dry_run": true, "inspected": 17, "project_flow": "github-flow"}
```

## Rollout List
Expand All @@ -109,7 +109,10 @@ PR #36: block: current-head OpenCode review requested changes

## Remaining Proof Gaps

- A live current-head review -> same-head manual Strix status bridge -> OpenCode approval -> guarded merge trace has been completed on `.github` PR #28.
- No live outdated -> update-branch -> new-head review -> merge/auto-merge trace has been completed yet.
- `update-branch` `422/403` behavior still needs a safe fixture or a real blocked case before claiming standardized handling.
- Required-check interpretation should stay delegated to GitHub native auto-merge until a repo needs immediate merge.
- PR #28 itself cannot prove adoption until the same-head manual Strix failure is diagnosed and OpenCode stops reusing stale PR-target Strix self-test logs as the only failed-check evidence.
- PR #28 proves the self-modifying trusted workflow bootstrap path after newer same-head evidence exists, but it does not prove update-branch behavior, stale approval dismissal after a head change, or cross-repository rollout.
- PR #37 adds a bounded OpenCode approval publication timeout after manual current-head OpenCode run `28011338113` reached the approval step and was observed waiting on peer checks instead of finishing promptly.
- PR #37 current-head run `28012303665` proved 100% coverage/docstring evidence and OpenCode completion for head `184be63`; Strix run `28012303876` proved same-head manual Strix success. The run also exposed that cancelled PR-target helper check `Strix Security Scan/publish-manual-pr-evidence-status` must be superseded by the newer same-head manual `strix` status, not treated as a source finding.
40 changes: 29 additions & 11 deletions scripts/ci/collect_failed_check_evidence.sh
Original file line number Diff line number Diff line change
Expand Up @@ -201,20 +201,38 @@ trap cleanup EXIT

manual_success_for_label() {
local label="$1"
local failed_run_id="${2:-}"
local key
local lower_label
local success_context
local success_url
local success_description
local success_run_id

key="${label##*/}"
key="$(printf '%s' "$key" | tr '[:upper:]' '[:lower:]')"
awk -F '\t' -v key="$key" '
tolower($1) == key {
print
found = 1
exit
}
END {
exit found ? 0 : 1
}
' "$manual_success_contexts"
lower_label="$(printf '%s' "$label" | tr '[:upper:]' '[:lower:]')"
case "$lower_label" in
"strix security scan/"*)
key="strix"
;;
esac

while IFS=$'\t' read -r success_context success_url success_description; do
if [ "$(printf '%s' "$success_context" | tr '[:upper:]' '[:lower:]')" != "$key" ]; then
continue
fi
success_run_id="$(printf '%s' "$success_url" | sed -n 's#.*/actions/runs/\([0-9][0-9]*\).*#\1#p')"
if [ -n "$failed_run_id" ] &&
[ -n "$success_run_id" ] &&
[ "$failed_run_id" -ge "$success_run_id" ]; then
continue
fi
printf '%s\t%s\t%s\n' "$success_context" "$success_url" "$success_description"
return 0
done <"$manual_success_contexts"

return 1
}

# shellcheck disable=SC2016
Expand Down Expand Up @@ -350,7 +368,7 @@ while IFS=$'\t' read -r kind label conclusion details_url run_id check_run_id; d
done <"$workflow_run_contexts"

while IFS=$'\t' read -r kind label conclusion details_url run_id check_run_id; do
if success_line="$(manual_success_for_label "$label")"; then
if success_line="$(manual_success_for_label "$label" "$run_id")"; then
IFS=$'\t' read -r success_context success_url success_description <<<"$success_line"
printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \
"$kind" \
Expand Down
Loading
Loading