From f7789059e27c5d7dd3b89b1d25fb9e5dab26187a Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:00:00 +0700 Subject: [PATCH 01/46] ci: add opencode GitHub review with Muse Spark model - Auto-review non-draft PRs using opencode-go/muse-spark-1.2-contributor - On-demand runs via /oc or /opencode comments - Uses the runner GITHUB_TOKEN (use_github_token) so no GitHub App install is required; model API key comes from the OPENCODE_API_KEY secret --- .github/workflows/opencode-review.yml | 70 +++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 .github/workflows/opencode-review.yml diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml new file mode 100644 index 0000000..ed1d581 --- /dev/null +++ b/.github/workflows/opencode-review.yml @@ -0,0 +1,70 @@ +name: opencode-review + +on: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + issue_comment: + types: [created] + pull_request_review_comment: + types: [created] + +# Auto-review every non-draft PR with the Muse Spark model via OpenCode Go. +# You can also trigger an on-demand run by commenting `/oc` or `/opencode`. +jobs: + review-pr: + if: github.event_name == 'pull_request' && github.event.pull_request.draft == false + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + pull-requests: write + issues: read + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + fetch-depth: 1 + persist-credentials: false + + - name: Run OpenCode review + uses: anomalyco/opencode/github@latest + env: + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} + with: + model: opencode-go/muse-spark-1.2-contributor + use_github_token: true + prompt: | + Review this pull request thoroughly: + - Check for bugs, logic errors and edge cases + - Look for security issues and unsafe patterns + - Assess code quality, consistency and maintainability + - Flag missing or weak tests + - Suggest concrete improvements + Post a clear, structured review comment with file references. + + on-demand: + if: | + (github.event_name == 'issue_comment' || + github.event_name == 'pull_request_review_comment') && + (contains(github.event.comment.body, '/oc') || + contains(github.event.comment.body, '/opencode')) + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + pull-requests: write + issues: write + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + fetch-depth: 1 + persist-credentials: false + + - name: Run OpenCode + uses: anomalyco/opencode/github@latest + env: + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} + with: + model: opencode-go/muse-spark-1.2-contributor + use_github_token: true From 9bb89879e579b8b89ce0fa95c92d29cf1ff32ff5 Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:08:41 +0700 Subject: [PATCH 02/46] ci: pass GITHUB_TOKEN to opencode review action The anomalyco/opencode/github action requires GITHUB_TOKEN env when use_github_token: true, otherwise it fails with: 'GITHUB_TOKEN environment variable is not set.' GitHub Actions does not inject GITHUB_TOKEN into env automatically; it must be passed explicitly via secrets.GITHUB_TOKEN. --- .github/workflows/opencode-review.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml index ed1d581..93dafdf 100644 --- a/.github/workflows/opencode-review.yml +++ b/.github/workflows/opencode-review.yml @@ -30,6 +30,7 @@ jobs: uses: anomalyco/opencode/github@latest env: OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: model: opencode-go/muse-spark-1.2-contributor use_github_token: true @@ -65,6 +66,7 @@ jobs: uses: anomalyco/opencode/github@latest env: OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: model: opencode-go/muse-spark-1.2-contributor use_github_token: true From bfa29e05a5107d101a139a5c37d95c84376555db Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:09:59 +0700 Subject: [PATCH 03/46] ci: use deepseek-v4-flash for PR review muse-spark-1.2-contributor requires training-data opt-in (DataPolicyError), conflicts with the no-training data privacy preference. deepseek-v4-flash is available on the same provider without opt-in. --- .github/workflows/opencode-review.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml index 93dafdf..b5a54d8 100644 --- a/.github/workflows/opencode-review.yml +++ b/.github/workflows/opencode-review.yml @@ -32,7 +32,7 @@ jobs: OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - model: opencode-go/muse-spark-1.2-contributor + model: opencode-go/deepseek-v4-flash use_github_token: true prompt: | Review this pull request thoroughly: @@ -68,5 +68,5 @@ jobs: OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - model: opencode-go/muse-spark-1.2-contributor + model: opencode-go/deepseek-v4-flash use_github_token: true From 660839134778cb19158be224cd5059ea0139227c Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:10:57 +0700 Subject: [PATCH 04/46] ci: use opencode provider for deepseek-v4-flash opencode-go/* models are China-hosted and require opt-in (RegionError). opencode/deepseek-v4-flash resolves via the zen gateway without opt-in. --- .github/workflows/opencode-review.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml index b5a54d8..6ca2fba 100644 --- a/.github/workflows/opencode-review.yml +++ b/.github/workflows/opencode-review.yml @@ -32,7 +32,7 @@ jobs: OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - model: opencode-go/deepseek-v4-flash + model: opencode/deepseek-v4-flash use_github_token: true prompt: | Review this pull request thoroughly: @@ -68,5 +68,5 @@ jobs: OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - model: opencode-go/deepseek-v4-flash + model: opencode/deepseek-v4-flash use_github_token: true From 9300922b7b189795ea7f7746266cb5f6502c7bb7 Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:12:56 +0700 Subject: [PATCH 05/46] ci: use free tier model for PR review opencode workspace has insufficient balance (CreditsError) and the China-hosted opencode-go models need opt-in. muse-spark-1.2-contributor-free is the free tier of the originally intended reviewer model. --- .github/workflows/opencode-review.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml index 6ca2fba..37f7e64 100644 --- a/.github/workflows/opencode-review.yml +++ b/.github/workflows/opencode-review.yml @@ -32,7 +32,7 @@ jobs: OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - model: opencode/deepseek-v4-flash + model: opencode/muse-spark-1.2-contributor-free use_github_token: true prompt: | Review this pull request thoroughly: @@ -68,5 +68,5 @@ jobs: OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - model: opencode/deepseek-v4-flash + model: opencode/muse-spark-1.2-contributor-free use_github_token: true From 28ac2e4f1c35b69e28dcdb9e95b79fadb5b9b3db Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:18:02 +0700 Subject: [PATCH 06/46] ci: extract reusable opencode review workflow opencode-review.yml is now a thin dispatcher that delegates to opencode-review-reusable.yml (workflow_call), so any repo can reuse the review logic via: uses: vuon9/devtoolbox/.github/workflows/opencode-review-reusable.yml@main secrets: OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} Reusable workflow defaults to free-tier model (no billing, no opt-in). --- .../workflows/opencode-review-reusable.yml | 67 +++++++++++++++++++ .github/workflows/opencode-review.yml | 60 +++-------------- 2 files changed, 75 insertions(+), 52 deletions(-) create mode 100644 .github/workflows/opencode-review-reusable.yml diff --git a/.github/workflows/opencode-review-reusable.yml b/.github/workflows/opencode-review-reusable.yml new file mode 100644 index 0000000..dba8feb --- /dev/null +++ b/.github/workflows/opencode-review-reusable.yml @@ -0,0 +1,67 @@ +name: opencode-review + +# Reusable PR review workflow. +# +# Call from any repo: +# jobs: +# review: +# uses: vuon9/devtoolbox/.github/workflows/opencode-review-reusable.yml@main +# with: +# model: opencode/muse-spark-1.2-contributor-free # optional, has default +# prompt: "Custom review prompt" # optional +# secrets: +# OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} # required +# +# Notes: +# - Uses the runner GITHUB_TOKEN (use_github_token: true), no GitHub App needed. +# - Requires the calling repo to have a pull-requests: write permission context. +# - Free-tier model by default: no billing, no training-data opt-in. + +on: + workflow_call: + inputs: + model: + description: Model to use for the review + required: false + type: string + default: opencode/muse-spark-1.2-contributor-free + prompt: + description: Custom review prompt + required: false + type: string + default: | + Review this pull request thoroughly: + - Check for bugs, logic errors and edge cases + - Look for security issues and unsafe patterns + - Assess code quality, consistency and maintainability + - Flag missing or weak tests + - Suggest concrete improvements + Post a clear, structured review comment with file references. + secrets: + OPENCODE_API_KEY: + required: true + +jobs: + review: + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + pull-requests: write + issues: read + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + fetch-depth: 1 + persist-credentials: false + + - name: Run OpenCode review + uses: anomalyco/opencode/github@latest + env: + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + model: ${{ inputs.model }} + use_github_token: true + prompt: ${{ inputs.prompt }} diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml index 37f7e64..ecaf239 100644 --- a/.github/workflows/opencode-review.yml +++ b/.github/workflows/opencode-review.yml @@ -8,40 +8,14 @@ on: pull_request_review_comment: types: [created] -# Auto-review every non-draft PR with the Muse Spark model via OpenCode Go. -# You can also trigger an on-demand run by commenting `/oc` or `/opencode`. +# Dispatcher: delegates to the reusable workflow so the review logic lives +# in one place (opencode-review-reusable.yml) and can be reused by other repos. jobs: review-pr: if: github.event_name == 'pull_request' && github.event.pull_request.draft == false - runs-on: ubuntu-latest - permissions: - id-token: write - contents: read - pull-requests: write - issues: read - steps: - - name: Checkout repository - uses: actions/checkout@v6 - with: - fetch-depth: 1 - persist-credentials: false - - - name: Run OpenCode review - uses: anomalyco/opencode/github@latest - env: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - model: opencode/muse-spark-1.2-contributor-free - use_github_token: true - prompt: | - Review this pull request thoroughly: - - Check for bugs, logic errors and edge cases - - Look for security issues and unsafe patterns - - Assess code quality, consistency and maintainability - - Flag missing or weak tests - - Suggest concrete improvements - Post a clear, structured review comment with file references. + uses: ./.github/workflows/opencode-review-reusable.yml + secrets: + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} on-demand: if: | @@ -49,24 +23,6 @@ jobs: github.event_name == 'pull_request_review_comment') && (contains(github.event.comment.body, '/oc') || contains(github.event.comment.body, '/opencode')) - runs-on: ubuntu-latest - permissions: - id-token: write - contents: read - pull-requests: write - issues: write - steps: - - name: Checkout repository - uses: actions/checkout@v6 - with: - fetch-depth: 1 - persist-credentials: false - - - name: Run OpenCode - uses: anomalyco/opencode/github@latest - env: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - model: opencode/muse-spark-1.2-contributor-free - use_github_token: true + uses: ./.github/workflows/opencode-review-reusable.yml + secrets: + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} From 13582f8521badb978b289a13e726937c2aa98e3f Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:20:16 +0700 Subject: [PATCH 07/46] ci: fix reusable workflow prompt default workflow_call inputs do not support multi-line default values (startup failure). Move the default review prompt to a job-level env var and pick it in a prepare step instead. --- .../workflows/opencode-review-reusable.yml | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/.github/workflows/opencode-review-reusable.yml b/.github/workflows/opencode-review-reusable.yml index dba8feb..8dc8955 100644 --- a/.github/workflows/opencode-review-reusable.yml +++ b/.github/workflows/opencode-review-reusable.yml @@ -14,7 +14,7 @@ name: opencode-review # # Notes: # - Uses the runner GITHUB_TOKEN (use_github_token: true), no GitHub App needed. -# - Requires the calling repo to have a pull-requests: write permission context. +# - Requires the calling repo to grant pull-requests: write to the job. # - Free-tier model by default: no billing, no training-data opt-in. on: @@ -26,17 +26,10 @@ on: type: string default: opencode/muse-spark-1.2-contributor-free prompt: - description: Custom review prompt + description: Custom review prompt (defaults to a thorough review prompt) required: false type: string - default: | - Review this pull request thoroughly: - - Check for bugs, logic errors and edge cases - - Look for security issues and unsafe patterns - - Assess code quality, consistency and maintainability - - Flag missing or weak tests - - Suggest concrete improvements - Post a clear, structured review comment with file references. + default: '' secrets: OPENCODE_API_KEY: required: true @@ -49,6 +42,15 @@ jobs: contents: read pull-requests: write issues: read + env: + DEFAULT_PROMPT: | + Review this pull request thoroughly: + - Check for bugs, logic errors and edge cases + - Look for security issues and unsafe patterns + - Assess code quality, consistency and maintainability + - Flag missing or weak tests + - Suggest concrete improvements + Post a clear, structured review comment with file references. steps: - name: Checkout repository uses: actions/checkout@v6 @@ -56,6 +58,15 @@ jobs: fetch-depth: 1 persist-credentials: false + - name: Prepare review prompt + id: prompt + shell: bash + run: | + PROMPT="${{ inputs.prompt }}" + if [ -z "$PROMPT" ]; then PROMPT="$DEFAULT_PROMPT"; fi + printf '%s\n' "$PROMPT" > /tmp/review-prompt.txt + echo "value=$(cat /tmp/review-prompt.txt)" >> "$GITHUB_OUTPUT" + - name: Run OpenCode review uses: anomalyco/opencode/github@latest env: @@ -64,4 +75,4 @@ jobs: with: model: ${{ inputs.model }} use_github_token: true - prompt: ${{ inputs.prompt }} + prompt: ${{ steps.prompt.outputs.value }} From e9407410cc223ec41a508ffaef3a7782de045c9e Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:25:12 +0700 Subject: [PATCH 08/46] ci: test reusable workflow via explicit branch ref --- .github/workflows/opencode-review.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml index ecaf239..5394c85 100644 --- a/.github/workflows/opencode-review.yml +++ b/.github/workflows/opencode-review.yml @@ -1,6 +1,7 @@ name: opencode-review on: + workflow_dispatch: pull_request: types: [opened, synchronize, reopened, ready_for_review] issue_comment: @@ -13,7 +14,7 @@ on: jobs: review-pr: if: github.event_name == 'pull_request' && github.event.pull_request.draft == false - uses: ./.github/workflows/opencode-review-reusable.yml + uses: vuon9/devtoolbox/.github/workflows/opencode-review-reusable.yml@chore/opencode-github-review secrets: OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} @@ -23,6 +24,6 @@ jobs: github.event_name == 'pull_request_review_comment') && (contains(github.event.comment.body, '/oc') || contains(github.event.comment.body, '/opencode')) - uses: ./.github/workflows/opencode-review-reusable.yml + uses: vuon9/devtoolbox/.github/workflows/opencode-review-reusable.yml@chore/opencode-github-review secrets: OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} From 53fe3ea12f9f01e4f133c84f635c504f56284243 Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:26:24 +0700 Subject: [PATCH 09/46] ci: add test workflow to isolate reusable call --- .github/workflows/test-reusable.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .github/workflows/test-reusable.yml diff --git a/.github/workflows/test-reusable.yml b/.github/workflows/test-reusable.yml new file mode 100644 index 0000000..4817ab1 --- /dev/null +++ b/.github/workflows/test-reusable.yml @@ -0,0 +1,11 @@ +name: test-reusable + +on: + workflow_dispatch: + pull_request: + +jobs: + test-call: + uses: ./.github/workflows/opencode-review-reusable.yml + secrets: + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} From 29e478b41674d1660e9cd8dabdfd7a501e3ad3a0 Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:27:03 +0700 Subject: [PATCH 10/46] ci: add minimal test workflow --- .github/workflows/test-min.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .github/workflows/test-min.yml diff --git a/.github/workflows/test-min.yml b/.github/workflows/test-min.yml new file mode 100644 index 0000000..a134e43 --- /dev/null +++ b/.github/workflows/test-min.yml @@ -0,0 +1,11 @@ +name: test-reusable-min + +on: + workflow_dispatch: + pull_request: + +jobs: + test-min: + runs-on: ubuntu-latest + steps: + - run: echo "hello from minimal" From 3a67f4e3021c2fd72c49e5d2fae639a175553576 Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:27:45 +0700 Subject: [PATCH 11/46] ci: test minimal reusable call chain --- .github/workflows/test-caller.yml | 9 +++++++++ .github/workflows/test-reusable-call.yml | 10 ++++++++++ 2 files changed, 19 insertions(+) create mode 100644 .github/workflows/test-caller.yml create mode 100644 .github/workflows/test-reusable-call.yml diff --git a/.github/workflows/test-caller.yml b/.github/workflows/test-caller.yml new file mode 100644 index 0000000..f324fed --- /dev/null +++ b/.github/workflows/test-caller.yml @@ -0,0 +1,9 @@ +name: test-caller + +on: + workflow_dispatch: + pull_request: + +jobs: + call: + uses: ./.github/workflows/test-reusable-call.yml diff --git a/.github/workflows/test-reusable-call.yml b/.github/workflows/test-reusable-call.yml new file mode 100644 index 0000000..cb5cc22 --- /dev/null +++ b/.github/workflows/test-reusable-call.yml @@ -0,0 +1,10 @@ +name: test-reusable-call + +on: + workflow_call: + +jobs: + test: + runs-on: ubuntu-latest + steps: + - run: echo "reusable works" From 181e2ec5a23522d98ff71f71c3c1db0c9005e4b8 Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:28:28 +0700 Subject: [PATCH 12/46] ci: test reusable with inputs+secrets --- .github/workflows/test-caller.yml | 2 ++ .github/workflows/test-reusable-call.yml | 12 +++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-caller.yml b/.github/workflows/test-caller.yml index f324fed..5beffa3 100644 --- a/.github/workflows/test-caller.yml +++ b/.github/workflows/test-caller.yml @@ -7,3 +7,5 @@ on: jobs: call: uses: ./.github/workflows/test-reusable-call.yml + secrets: + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} diff --git a/.github/workflows/test-reusable-call.yml b/.github/workflows/test-reusable-call.yml index cb5cc22..a958fb8 100644 --- a/.github/workflows/test-reusable-call.yml +++ b/.github/workflows/test-reusable-call.yml @@ -2,9 +2,19 @@ name: test-reusable-call on: workflow_call: + inputs: + model: + required: false + type: string + default: opencode/muse-spark-1.2-contributor-free + secrets: + OPENCODE_API_KEY: + required: true jobs: test: runs-on: ubuntu-latest steps: - - run: echo "reusable works" + - run: echo "model=${{ inputs.model }} keylen=${#OPENCODE_API_KEY}" + env: + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} From f2ad4635bb81e3cea28eaacfe5a13c126f2c365d Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:29:06 +0700 Subject: [PATCH 13/46] ci: test reusable with env prompt --- .github/workflows/test-reusable-call.yml | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-reusable-call.yml b/.github/workflows/test-reusable-call.yml index a958fb8..4b57503 100644 --- a/.github/workflows/test-reusable-call.yml +++ b/.github/workflows/test-reusable-call.yml @@ -7,6 +7,10 @@ on: required: false type: string default: opencode/muse-spark-1.2-contributor-free + prompt: + required: false + type: string + default: '' secrets: OPENCODE_API_KEY: required: true @@ -14,7 +18,19 @@ on: jobs: test: runs-on: ubuntu-latest + env: + DEFAULT_PROMPT: | + Review this pull request thoroughly: + - Check for bugs, logic errors and edge cases + - Look for security issues and unsafe patterns + Post a clear, structured review comment with file references. steps: - - run: echo "model=${{ inputs.model }} keylen=${#OPENCODE_API_KEY}" - env: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} + - name: Prepare review prompt + id: prompt + shell: bash + run: | + PROMPT="${{ inputs.prompt }}" + if [ -z "$PROMPT" ]; then PROMPT="$DEFAULT_PROMPT"; fi + printf '%s\n' "$PROMPT" > /tmp/review-prompt.txt + echo "value=$(cat /tmp/review-prompt.txt)" >> "$GITHUB_OUTPUT" + - run: echo "model=${{ inputs.model }} prompt_lines=$(wc -l < /tmp/review-prompt.txt)" From ccb2b14510610dfadb64e4786471317d1af07a63 Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:30:01 +0700 Subject: [PATCH 14/46] ci: fix multi-line prompt output in reusable workflow GITHUB_OUTPUT rejects multi-line values written as plain echo. Use the heredoc delimiter form (value< /tmp/review-prompt.txt - echo "value=$(cat /tmp/review-prompt.txt)" >> "$GITHUB_OUTPUT" + { + echo "value<> "$GITHUB_OUTPUT" - name: Run OpenCode review uses: anomalyco/opencode/github@latest diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml index 5394c85..1c72d2d 100644 --- a/.github/workflows/opencode-review.yml +++ b/.github/workflows/opencode-review.yml @@ -14,7 +14,7 @@ on: jobs: review-pr: if: github.event_name == 'pull_request' && github.event.pull_request.draft == false - uses: vuon9/devtoolbox/.github/workflows/opencode-review-reusable.yml@chore/opencode-github-review + uses: ./.github/workflows/opencode-review-reusable.yml secrets: OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} @@ -24,6 +24,6 @@ jobs: github.event_name == 'pull_request_review_comment') && (contains(github.event.comment.body, '/oc') || contains(github.event.comment.body, '/opencode')) - uses: vuon9/devtoolbox/.github/workflows/opencode-review-reusable.yml@chore/opencode-github-review + uses: ./.github/workflows/opencode-review-reusable.yml secrets: OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} diff --git a/.github/workflows/test-reusable-call.yml b/.github/workflows/test-reusable-call.yml index 4b57503..bb3b4db 100644 --- a/.github/workflows/test-reusable-call.yml +++ b/.github/workflows/test-reusable-call.yml @@ -32,5 +32,9 @@ jobs: PROMPT="${{ inputs.prompt }}" if [ -z "$PROMPT" ]; then PROMPT="$DEFAULT_PROMPT"; fi printf '%s\n' "$PROMPT" > /tmp/review-prompt.txt - echo "value=$(cat /tmp/review-prompt.txt)" >> "$GITHUB_OUTPUT" + { + echo "value<> "$GITHUB_OUTPUT" - run: echo "model=${{ inputs.model }} prompt_lines=$(wc -l < /tmp/review-prompt.txt)" From f15d08419c9f3d513cd2183e8182547323d6156f Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:31:26 +0700 Subject: [PATCH 15/46] ci: test caller with if condition on uses job --- .github/workflows/test-caller-if.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .github/workflows/test-caller-if.yml diff --git a/.github/workflows/test-caller-if.yml b/.github/workflows/test-caller-if.yml new file mode 100644 index 0000000..6739711 --- /dev/null +++ b/.github/workflows/test-caller-if.yml @@ -0,0 +1,12 @@ +name: test-caller-if + +on: + workflow_dispatch: + pull_request: + +jobs: + call: + if: github.event_name == 'pull_request' && github.event.pull_request.draft == false + uses: ./.github/workflows/opencode-review-reusable.yml + secrets: + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} From b013967c1085f251f20bdc5432f29b0c5c1abe0f Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:32:20 +0700 Subject: [PATCH 16/46] ci: test caller if without draft ref --- .github/workflows/test-caller-if2.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .github/workflows/test-caller-if2.yml diff --git a/.github/workflows/test-caller-if2.yml b/.github/workflows/test-caller-if2.yml new file mode 100644 index 0000000..07e55a2 --- /dev/null +++ b/.github/workflows/test-caller-if2.yml @@ -0,0 +1,12 @@ +name: test-caller-if2 + +on: + workflow_dispatch: + pull_request: + +jobs: + call: + if: github.event_name == 'pull_request' + uses: ./.github/workflows/opencode-review-reusable.yml + secrets: + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} From 54b9703bc25ad81c1b945bea5b052cee2c8cd68e Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:32:59 +0700 Subject: [PATCH 17/46] ci: test caller if true --- .github/workflows/test-caller-if3.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .github/workflows/test-caller-if3.yml diff --git a/.github/workflows/test-caller-if3.yml b/.github/workflows/test-caller-if3.yml new file mode 100644 index 0000000..474cdb3 --- /dev/null +++ b/.github/workflows/test-caller-if3.yml @@ -0,0 +1,12 @@ +name: test-caller-if3 + +on: + workflow_dispatch: + pull_request: + +jobs: + call: + if: true + uses: ./.github/workflows/opencode-review-reusable.yml + secrets: + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} From 62c42399bf892d4b11800c394df4f4778dcb101d Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:33:55 +0700 Subject: [PATCH 18/46] ci: test if inside reusable job --- .github/workflows/test-caller-if4.yml | 13 ++++++++++++ .github/workflows/test-reusable-call.yml | 27 +++++++----------------- 2 files changed, 21 insertions(+), 19 deletions(-) create mode 100644 .github/workflows/test-caller-if4.yml diff --git a/.github/workflows/test-caller-if4.yml b/.github/workflows/test-caller-if4.yml new file mode 100644 index 0000000..d995429 --- /dev/null +++ b/.github/workflows/test-caller-if4.yml @@ -0,0 +1,13 @@ +name: test-caller-if4 + +on: + workflow_dispatch: + pull_request: + +jobs: + call: + uses: ./.github/workflows/test-reusable-call.yml + with: + mode: pr + secrets: + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} diff --git a/.github/workflows/test-reusable-call.yml b/.github/workflows/test-reusable-call.yml index bb3b4db..3d94a91 100644 --- a/.github/workflows/test-reusable-call.yml +++ b/.github/workflows/test-reusable-call.yml @@ -3,6 +3,10 @@ name: test-reusable-call on: workflow_call: inputs: + mode: + required: false + type: string + default: pr model: required: false type: string @@ -17,24 +21,9 @@ on: jobs: test: + if: | + (inputs.mode == 'pr' && github.event_name == 'pull_request' && github.event.pull_request.draft == false) || + (inputs.mode == 'comment' && (contains(github.event.comment.body, '/oc') || contains(github.event.comment.body, '/opencode'))) runs-on: ubuntu-latest - env: - DEFAULT_PROMPT: | - Review this pull request thoroughly: - - Check for bugs, logic errors and edge cases - - Look for security issues and unsafe patterns - Post a clear, structured review comment with file references. steps: - - name: Prepare review prompt - id: prompt - shell: bash - run: | - PROMPT="${{ inputs.prompt }}" - if [ -z "$PROMPT" ]; then PROMPT="$DEFAULT_PROMPT"; fi - printf '%s\n' "$PROMPT" > /tmp/review-prompt.txt - { - echo "value<> "$GITHUB_OUTPUT" - - run: echo "model=${{ inputs.model }} prompt_lines=$(wc -l < /tmp/review-prompt.txt)" + - run: echo "mode=${{ inputs.mode }} event=${{ github.event_name }}" From 671d42c212f11252179b6aab7815bb37270ae375 Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:34:45 +0700 Subject: [PATCH 19/46] ci: fix reusable workflow gating with mode input GitHub rejects if: conditions on jobs that call reusable workflows (startup_failure even with if: true). Move the pr/comment gating into the reusable workflow's job-level if, selected via a mode input. Also fix multi-line prompt output: GITHUB_OUTPUT requires the heredoc delimiter form for multi-line values. --- .../workflows/opencode-review-reusable.yml | 12 ++++++++ .github/workflows/opencode-review.yml | 12 ++++---- .github/workflows/test-caller-if.yml | 12 -------- .github/workflows/test-caller-if2.yml | 12 -------- .github/workflows/test-caller-if3.yml | 12 -------- .github/workflows/test-caller-if4.yml | 13 --------- .github/workflows/test-caller.yml | 11 ------- .github/workflows/test-min.yml | 11 ------- .github/workflows/test-reusable-call.yml | 29 ------------------- .github/workflows/test-reusable.yml | 11 ------- 10 files changed, 18 insertions(+), 117 deletions(-) delete mode 100644 .github/workflows/test-caller-if.yml delete mode 100644 .github/workflows/test-caller-if2.yml delete mode 100644 .github/workflows/test-caller-if3.yml delete mode 100644 .github/workflows/test-caller-if4.yml delete mode 100644 .github/workflows/test-caller.yml delete mode 100644 .github/workflows/test-min.yml delete mode 100644 .github/workflows/test-reusable-call.yml delete mode 100644 .github/workflows/test-reusable.yml diff --git a/.github/workflows/opencode-review-reusable.yml b/.github/workflows/opencode-review-reusable.yml index 74f1a28..2963a98 100644 --- a/.github/workflows/opencode-review-reusable.yml +++ b/.github/workflows/opencode-review-reusable.yml @@ -9,9 +9,13 @@ name: opencode-review # with: # model: opencode/muse-spark-1.2-contributor-free # optional, has default # prompt: "Custom review prompt" # optional +# mode: pr # optional: pr | comment # secrets: # OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} # required # +# mode=pr → runs only for non-draft pull_request events +# mode=comment → runs only when the comment contains /oc or /opencode +# # Notes: # - Uses the runner GITHUB_TOKEN (use_github_token: true), no GitHub App needed. # - Requires the calling repo to grant pull-requests: write to the job. @@ -30,12 +34,20 @@ on: required: false type: string default: '' + mode: + description: pr (auto-review PRs) or comment (on-demand /oc /opencode) + required: false + type: string + default: pr secrets: OPENCODE_API_KEY: required: true jobs: review: + if: | + (inputs.mode == 'pr' && github.event_name == 'pull_request' && github.event.pull_request.draft == false) || + (inputs.mode == 'comment' && (contains(github.event.comment.body, '/oc') || contains(github.event.comment.body, '/opencode'))) runs-on: ubuntu-latest permissions: id-token: write diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml index 1c72d2d..148643c 100644 --- a/.github/workflows/opencode-review.yml +++ b/.github/workflows/opencode-review.yml @@ -11,19 +11,19 @@ on: # Dispatcher: delegates to the reusable workflow so the review logic lives # in one place (opencode-review-reusable.yml) and can be reused by other repos. +# GitHub does not allow `if:` conditions on jobs that use a reusable workflow, +# so the pr/comment gating lives inside the reusable workflow via the `mode` input. jobs: review-pr: - if: github.event_name == 'pull_request' && github.event.pull_request.draft == false uses: ./.github/workflows/opencode-review-reusable.yml + with: + mode: pr secrets: OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} on-demand: - if: | - (github.event_name == 'issue_comment' || - github.event_name == 'pull_request_review_comment') && - (contains(github.event.comment.body, '/oc') || - contains(github.event.comment.body, '/opencode')) uses: ./.github/workflows/opencode-review-reusable.yml + with: + mode: comment secrets: OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} diff --git a/.github/workflows/test-caller-if.yml b/.github/workflows/test-caller-if.yml deleted file mode 100644 index 6739711..0000000 --- a/.github/workflows/test-caller-if.yml +++ /dev/null @@ -1,12 +0,0 @@ -name: test-caller-if - -on: - workflow_dispatch: - pull_request: - -jobs: - call: - if: github.event_name == 'pull_request' && github.event.pull_request.draft == false - uses: ./.github/workflows/opencode-review-reusable.yml - secrets: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} diff --git a/.github/workflows/test-caller-if2.yml b/.github/workflows/test-caller-if2.yml deleted file mode 100644 index 07e55a2..0000000 --- a/.github/workflows/test-caller-if2.yml +++ /dev/null @@ -1,12 +0,0 @@ -name: test-caller-if2 - -on: - workflow_dispatch: - pull_request: - -jobs: - call: - if: github.event_name == 'pull_request' - uses: ./.github/workflows/opencode-review-reusable.yml - secrets: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} diff --git a/.github/workflows/test-caller-if3.yml b/.github/workflows/test-caller-if3.yml deleted file mode 100644 index 474cdb3..0000000 --- a/.github/workflows/test-caller-if3.yml +++ /dev/null @@ -1,12 +0,0 @@ -name: test-caller-if3 - -on: - workflow_dispatch: - pull_request: - -jobs: - call: - if: true - uses: ./.github/workflows/opencode-review-reusable.yml - secrets: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} diff --git a/.github/workflows/test-caller-if4.yml b/.github/workflows/test-caller-if4.yml deleted file mode 100644 index d995429..0000000 --- a/.github/workflows/test-caller-if4.yml +++ /dev/null @@ -1,13 +0,0 @@ -name: test-caller-if4 - -on: - workflow_dispatch: - pull_request: - -jobs: - call: - uses: ./.github/workflows/test-reusable-call.yml - with: - mode: pr - secrets: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} diff --git a/.github/workflows/test-caller.yml b/.github/workflows/test-caller.yml deleted file mode 100644 index 5beffa3..0000000 --- a/.github/workflows/test-caller.yml +++ /dev/null @@ -1,11 +0,0 @@ -name: test-caller - -on: - workflow_dispatch: - pull_request: - -jobs: - call: - uses: ./.github/workflows/test-reusable-call.yml - secrets: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} diff --git a/.github/workflows/test-min.yml b/.github/workflows/test-min.yml deleted file mode 100644 index a134e43..0000000 --- a/.github/workflows/test-min.yml +++ /dev/null @@ -1,11 +0,0 @@ -name: test-reusable-min - -on: - workflow_dispatch: - pull_request: - -jobs: - test-min: - runs-on: ubuntu-latest - steps: - - run: echo "hello from minimal" diff --git a/.github/workflows/test-reusable-call.yml b/.github/workflows/test-reusable-call.yml deleted file mode 100644 index 3d94a91..0000000 --- a/.github/workflows/test-reusable-call.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: test-reusable-call - -on: - workflow_call: - inputs: - mode: - required: false - type: string - default: pr - model: - required: false - type: string - default: opencode/muse-spark-1.2-contributor-free - prompt: - required: false - type: string - default: '' - secrets: - OPENCODE_API_KEY: - required: true - -jobs: - test: - if: | - (inputs.mode == 'pr' && github.event_name == 'pull_request' && github.event.pull_request.draft == false) || - (inputs.mode == 'comment' && (contains(github.event.comment.body, '/oc') || contains(github.event.comment.body, '/opencode'))) - runs-on: ubuntu-latest - steps: - - run: echo "mode=${{ inputs.mode }} event=${{ github.event_name }}" diff --git a/.github/workflows/test-reusable.yml b/.github/workflows/test-reusable.yml deleted file mode 100644 index 4817ab1..0000000 --- a/.github/workflows/test-reusable.yml +++ /dev/null @@ -1,11 +0,0 @@ -name: test-reusable - -on: - workflow_dispatch: - pull_request: - -jobs: - test-call: - uses: ./.github/workflows/opencode-review-reusable.yml - secrets: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} From 2a7b06350cc4584151be9ecaa649ebd5215068f5 Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:35:45 +0700 Subject: [PATCH 20/46] ci: test caller with all 4 triggers --- .github/workflows/test-caller-4trig.yml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .github/workflows/test-caller-4trig.yml diff --git a/.github/workflows/test-caller-4trig.yml b/.github/workflows/test-caller-4trig.yml new file mode 100644 index 0000000..c3044bf --- /dev/null +++ b/.github/workflows/test-caller-4trig.yml @@ -0,0 +1,25 @@ +name: test-caller-4trig + +on: + workflow_dispatch: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + issue_comment: + types: [created] + pull_request_review_comment: + types: [created] + +jobs: + review-pr: + uses: ./.github/workflows/opencode-review-reusable.yml + with: + mode: pr + secrets: + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} + + on-demand: + uses: ./.github/workflows/opencode-review-reusable.yml + with: + mode: comment + secrets: + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} From 4e6382d8b22f65974b54d777dd2e624591e190b9 Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:36:23 +0700 Subject: [PATCH 21/46] ci: test caller with 2 triggers (pr + issue_comment) --- .github/workflows/test-caller-2trig.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .github/workflows/test-caller-2trig.yml diff --git a/.github/workflows/test-caller-2trig.yml b/.github/workflows/test-caller-2trig.yml new file mode 100644 index 0000000..3ef2fe5 --- /dev/null +++ b/.github/workflows/test-caller-2trig.yml @@ -0,0 +1,23 @@ +name: test-caller-2trig + +on: + workflow_dispatch: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + issue_comment: + types: [created] + +jobs: + review-pr: + uses: ./.github/workflows/opencode-review-reusable.yml + with: + mode: pr + secrets: + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} + + on-demand: + uses: ./.github/workflows/opencode-review-reusable.yml + with: + mode: comment + secrets: + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} From 2a411e6656f8944ad3963e988709b4eb53b1107a Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:37:01 +0700 Subject: [PATCH 22/46] ci: test caller with 1 trigger (pr only) --- .github/workflows/test-caller-1trig.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/workflows/test-caller-1trig.yml diff --git a/.github/workflows/test-caller-1trig.yml b/.github/workflows/test-caller-1trig.yml new file mode 100644 index 0000000..73d6794 --- /dev/null +++ b/.github/workflows/test-caller-1trig.yml @@ -0,0 +1,21 @@ +name: test-caller-1trig + +on: + workflow_dispatch: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + +jobs: + review-pr: + uses: ./.github/workflows/opencode-review-reusable.yml + with: + mode: pr + secrets: + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} + + on-demand: + uses: ./.github/workflows/opencode-review-reusable.yml + with: + mode: comment + secrets: + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} From bf35dd17878fea089bb53b4089d68d480c702170 Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:37:44 +0700 Subject: [PATCH 23/46] ci: test reusable with permissions block --- .github/workflows/test-reusable-call.yml | 34 ++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 .github/workflows/test-reusable-call.yml diff --git a/.github/workflows/test-reusable-call.yml b/.github/workflows/test-reusable-call.yml new file mode 100644 index 0000000..b3b19c3 --- /dev/null +++ b/.github/workflows/test-reusable-call.yml @@ -0,0 +1,34 @@ +name: test-reusable-call + +on: + workflow_call: + inputs: + mode: + required: false + type: string + default: pr + model: + required: false + type: string + default: opencode/muse-spark-1.2-contributor-free + prompt: + required: false + type: string + default: '' + secrets: + OPENCODE_API_KEY: + required: true + +jobs: + test: + if: | + (inputs.mode == 'pr' && github.event_name == 'pull_request' && github.event.pull_request.draft == false) || + (inputs.mode == 'comment' && (contains(github.event.comment.body, '/oc') || contains(github.event.comment.body, '/opencode'))) + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + pull-requests: write + issues: read + steps: + - run: echo "mode=${{ inputs.mode }} event=${{ github.event_name }}" From 6771d5f38b6941e070b0a54f14f2aad33ab8cee5 Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:39:41 +0700 Subject: [PATCH 24/46] ci: test reusable with checkout+prompt steps --- .github/workflows/test-reusable-call.yml | 28 +++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-reusable-call.yml b/.github/workflows/test-reusable-call.yml index b3b19c3..79ab3be 100644 --- a/.github/workflows/test-reusable-call.yml +++ b/.github/workflows/test-reusable-call.yml @@ -31,4 +31,30 @@ jobs: pull-requests: write issues: read steps: - - run: echo "mode=${{ inputs.mode }} event=${{ github.event_name }}" + - name: Checkout repository + uses: actions/checkout@v6 + with: + fetch-depth: 1 + persist-credentials: false + - name: Prepare review prompt + id: prompt + shell: bash + run: | + PROMPT="${{ inputs.prompt }}" + if [ -z "$PROMPT" ]; then PROMPT="$DEFAULT_PROMPT"; fi + printf '%s\n' "$PROMPT" > /tmp/review-prompt.txt + { + echo "value<> "$GITHUB_OUTPUT" + env: + DEFAULT_PROMPT: | + Review this pull request thoroughly: + - Check for bugs, logic errors and edge cases + - Look for security issues and unsafe patterns + - Assess code quality, consistency and maintainability + - Flag missing or weak tests + - Suggest concrete improvements + Post a clear, structured review comment with file references. + - run: echo "mode=${{ inputs.mode }} model=${{ inputs.model }} prompt=${{ steps.prompt.outputs.value }}" From ce1432eb08c8df0ac0bfb19331cf244ce1e80a5c Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:40:52 +0700 Subject: [PATCH 25/46] ci: test reusable with opencode action --- .github/workflows/test-caller-if4.yml | 13 +++++++++++++ .github/workflows/test-reusable-call.yml | 9 +++++++++ 2 files changed, 22 insertions(+) create mode 100644 .github/workflows/test-caller-if4.yml diff --git a/.github/workflows/test-caller-if4.yml b/.github/workflows/test-caller-if4.yml new file mode 100644 index 0000000..d995429 --- /dev/null +++ b/.github/workflows/test-caller-if4.yml @@ -0,0 +1,13 @@ +name: test-caller-if4 + +on: + workflow_dispatch: + pull_request: + +jobs: + call: + uses: ./.github/workflows/test-reusable-call.yml + with: + mode: pr + secrets: + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} diff --git a/.github/workflows/test-reusable-call.yml b/.github/workflows/test-reusable-call.yml index 79ab3be..9d055b5 100644 --- a/.github/workflows/test-reusable-call.yml +++ b/.github/workflows/test-reusable-call.yml @@ -58,3 +58,12 @@ jobs: - Suggest concrete improvements Post a clear, structured review comment with file references. - run: echo "mode=${{ inputs.mode }} model=${{ inputs.model }} prompt=${{ steps.prompt.outputs.value }}" + - name: Run OpenCode review + uses: anomalyco/opencode/github@latest + env: + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + model: ${{ inputs.model }} + use_github_token: true + prompt: ${{ steps.prompt.outputs.value }} From cf1e47deb994c95907e73fbce9da1b94591dfcc8 Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:43:31 +0700 Subject: [PATCH 26/46] ci: use opencode-go/hy3 model + pin action to v1.18.20 - Default model switched to opencode-go/hy3 per request (no billing) - Pin anomalyco/opencode/github@v1.18.20: @latest is not resolvable inside a reusable workflow and causes startup_failure - Remove temporary isolation test workflows --- .../workflows/opencode-review-reusable.yml | 8 +-- .github/workflows/test-caller-1trig.yml | 21 ------ .github/workflows/test-caller-2trig.yml | 23 ------- .github/workflows/test-caller-4trig.yml | 25 ------- .github/workflows/test-caller-if4.yml | 13 ---- .github/workflows/test-reusable-call.yml | 69 ------------------- 6 files changed, 4 insertions(+), 155 deletions(-) delete mode 100644 .github/workflows/test-caller-1trig.yml delete mode 100644 .github/workflows/test-caller-2trig.yml delete mode 100644 .github/workflows/test-caller-4trig.yml delete mode 100644 .github/workflows/test-caller-if4.yml delete mode 100644 .github/workflows/test-reusable-call.yml diff --git a/.github/workflows/opencode-review-reusable.yml b/.github/workflows/opencode-review-reusable.yml index 2963a98..31a1f73 100644 --- a/.github/workflows/opencode-review-reusable.yml +++ b/.github/workflows/opencode-review-reusable.yml @@ -7,7 +7,7 @@ name: opencode-review # review: # uses: vuon9/devtoolbox/.github/workflows/opencode-review-reusable.yml@main # with: -# model: opencode/muse-spark-1.2-contributor-free # optional, has default +# model: opencode-go/hy3 # optional, has default # prompt: "Custom review prompt" # optional # mode: pr # optional: pr | comment # secrets: @@ -19,7 +19,7 @@ name: opencode-review # Notes: # - Uses the runner GITHUB_TOKEN (use_github_token: true), no GitHub App needed. # - Requires the calling repo to grant pull-requests: write to the job. -# - Free-tier model by default: no billing, no training-data opt-in. +# - Default model: opencode-go/hy3 (no billing needed). on: workflow_call: @@ -28,7 +28,7 @@ on: description: Model to use for the review required: false type: string - default: opencode/muse-spark-1.2-contributor-free + default: opencode-go/hy3 prompt: description: Custom review prompt (defaults to a thorough review prompt) required: false @@ -84,7 +84,7 @@ jobs: } >> "$GITHUB_OUTPUT" - name: Run OpenCode review - uses: anomalyco/opencode/github@latest + uses: anomalyco/opencode/github@v1.18.20 env: OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/test-caller-1trig.yml b/.github/workflows/test-caller-1trig.yml deleted file mode 100644 index 73d6794..0000000 --- a/.github/workflows/test-caller-1trig.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: test-caller-1trig - -on: - workflow_dispatch: - pull_request: - types: [opened, synchronize, reopened, ready_for_review] - -jobs: - review-pr: - uses: ./.github/workflows/opencode-review-reusable.yml - with: - mode: pr - secrets: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} - - on-demand: - uses: ./.github/workflows/opencode-review-reusable.yml - with: - mode: comment - secrets: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} diff --git a/.github/workflows/test-caller-2trig.yml b/.github/workflows/test-caller-2trig.yml deleted file mode 100644 index 3ef2fe5..0000000 --- a/.github/workflows/test-caller-2trig.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: test-caller-2trig - -on: - workflow_dispatch: - pull_request: - types: [opened, synchronize, reopened, ready_for_review] - issue_comment: - types: [created] - -jobs: - review-pr: - uses: ./.github/workflows/opencode-review-reusable.yml - with: - mode: pr - secrets: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} - - on-demand: - uses: ./.github/workflows/opencode-review-reusable.yml - with: - mode: comment - secrets: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} diff --git a/.github/workflows/test-caller-4trig.yml b/.github/workflows/test-caller-4trig.yml deleted file mode 100644 index c3044bf..0000000 --- a/.github/workflows/test-caller-4trig.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: test-caller-4trig - -on: - workflow_dispatch: - pull_request: - types: [opened, synchronize, reopened, ready_for_review] - issue_comment: - types: [created] - pull_request_review_comment: - types: [created] - -jobs: - review-pr: - uses: ./.github/workflows/opencode-review-reusable.yml - with: - mode: pr - secrets: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} - - on-demand: - uses: ./.github/workflows/opencode-review-reusable.yml - with: - mode: comment - secrets: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} diff --git a/.github/workflows/test-caller-if4.yml b/.github/workflows/test-caller-if4.yml deleted file mode 100644 index d995429..0000000 --- a/.github/workflows/test-caller-if4.yml +++ /dev/null @@ -1,13 +0,0 @@ -name: test-caller-if4 - -on: - workflow_dispatch: - pull_request: - -jobs: - call: - uses: ./.github/workflows/test-reusable-call.yml - with: - mode: pr - secrets: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} diff --git a/.github/workflows/test-reusable-call.yml b/.github/workflows/test-reusable-call.yml deleted file mode 100644 index 9d055b5..0000000 --- a/.github/workflows/test-reusable-call.yml +++ /dev/null @@ -1,69 +0,0 @@ -name: test-reusable-call - -on: - workflow_call: - inputs: - mode: - required: false - type: string - default: pr - model: - required: false - type: string - default: opencode/muse-spark-1.2-contributor-free - prompt: - required: false - type: string - default: '' - secrets: - OPENCODE_API_KEY: - required: true - -jobs: - test: - if: | - (inputs.mode == 'pr' && github.event_name == 'pull_request' && github.event.pull_request.draft == false) || - (inputs.mode == 'comment' && (contains(github.event.comment.body, '/oc') || contains(github.event.comment.body, '/opencode'))) - runs-on: ubuntu-latest - permissions: - id-token: write - contents: read - pull-requests: write - issues: read - steps: - - name: Checkout repository - uses: actions/checkout@v6 - with: - fetch-depth: 1 - persist-credentials: false - - name: Prepare review prompt - id: prompt - shell: bash - run: | - PROMPT="${{ inputs.prompt }}" - if [ -z "$PROMPT" ]; then PROMPT="$DEFAULT_PROMPT"; fi - printf '%s\n' "$PROMPT" > /tmp/review-prompt.txt - { - echo "value<> "$GITHUB_OUTPUT" - env: - DEFAULT_PROMPT: | - Review this pull request thoroughly: - - Check for bugs, logic errors and edge cases - - Look for security issues and unsafe patterns - - Assess code quality, consistency and maintainability - - Flag missing or weak tests - - Suggest concrete improvements - Post a clear, structured review comment with file references. - - run: echo "mode=${{ inputs.mode }} model=${{ inputs.model }} prompt=${{ steps.prompt.outputs.value }}" - - name: Run OpenCode review - uses: anomalyco/opencode/github@latest - env: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - model: ${{ inputs.model }} - use_github_token: true - prompt: ${{ steps.prompt.outputs.value }} From e323369bc7d8097c2b2eb6d1eff88dce290620ea Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:44:49 +0700 Subject: [PATCH 27/46] ci: inline opencode action steps in reusable workflow The anomalyco/opencode/github composite action cannot be resolved inside a reusable workflow (startup_failure regardless of @latest or pinned tag). Inline its exact steps (version lookup, cache, install, PATH, run) as plain shell steps so the reusable workflow parses and runs. --- .../workflows/opencode-review-reusable.yml | 33 ++++++++++++++++--- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/.github/workflows/opencode-review-reusable.yml b/.github/workflows/opencode-review-reusable.yml index 31a1f73..905cba2 100644 --- a/.github/workflows/opencode-review-reusable.yml +++ b/.github/workflows/opencode-review-reusable.yml @@ -83,12 +83,35 @@ jobs: echo "PROMPT_EOF" } >> "$GITHUB_OUTPUT" + - name: Get opencode version + id: version + shell: bash + run: | + VERSION=$(curl -sf https://api.github.com/repos/anomalyco/opencode/releases/latest | grep -o '"tag_name": *"[^"]*"' | cut -d'"' -f4) + echo "version=${VERSION:-latest}" >> "$GITHUB_OUTPUT" + + - name: Cache opencode + uses: actions/cache@v4 + with: + path: ~/.opencode/bin + key: opencode-${{ runner.os }}-${{ runner.arch }}-${{ steps.version.outputs.version }} + + - name: Install opencode + if: steps.cache.outputs.cache-hit != 'true' + shell: bash + run: curl -fsSL https://opencode.ai/install | bash + + - name: Add opencode to PATH + shell: bash + run: echo "$HOME/.opencode/bin" >> "$GITHUB_PATH" + - name: Run OpenCode review - uses: anomalyco/opencode/github@v1.18.20 + shell: bash + id: run_opencode + run: opencode github run env: OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - model: ${{ inputs.model }} - use_github_token: true - prompt: ${{ steps.prompt.outputs.value }} + MODEL: ${{ inputs.model }} + PROMPT: ${{ steps.prompt.outputs.value }} + USE_GITHUB_TOKEN: "true" From e5bece8bd35a16a2cb8ae70126ba6a2e37d5de40 Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:46:00 +0700 Subject: [PATCH 28/46] ci: re-test 2-trigger caller against inlined reusable --- .github/workflows/test-caller-2trig.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .github/workflows/test-caller-2trig.yml diff --git a/.github/workflows/test-caller-2trig.yml b/.github/workflows/test-caller-2trig.yml new file mode 100644 index 0000000..76bf345 --- /dev/null +++ b/.github/workflows/test-caller-2trig.yml @@ -0,0 +1,14 @@ +name: test-caller-2trig + +on: + workflow_dispatch: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + +jobs: + review-pr: + uses: ./.github/workflows/opencode-review-reusable.yml + with: + mode: pr + secrets: + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} From 562fa2721699ad577cf5b39432abc4f53f37232a Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:47:09 +0700 Subject: [PATCH 29/46] ci: test cache step in reusable --- .github/workflows/test-caller-if4.yml | 13 +++++++ .github/workflows/test-reusable-call.yml | 45 ++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 .github/workflows/test-caller-if4.yml create mode 100644 .github/workflows/test-reusable-call.yml diff --git a/.github/workflows/test-caller-if4.yml b/.github/workflows/test-caller-if4.yml new file mode 100644 index 0000000..d995429 --- /dev/null +++ b/.github/workflows/test-caller-if4.yml @@ -0,0 +1,13 @@ +name: test-caller-if4 + +on: + workflow_dispatch: + pull_request: + +jobs: + call: + uses: ./.github/workflows/test-reusable-call.yml + with: + mode: pr + secrets: + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} diff --git a/.github/workflows/test-reusable-call.yml b/.github/workflows/test-reusable-call.yml new file mode 100644 index 0000000..767505a --- /dev/null +++ b/.github/workflows/test-reusable-call.yml @@ -0,0 +1,45 @@ +name: test-reusable-call + +on: + workflow_call: + inputs: + mode: + required: false + type: string + default: pr + model: + required: false + type: string + default: opencode-go/hy3 + prompt: + required: false + type: string + default: '' + secrets: + OPENCODE_API_KEY: + required: true + +jobs: + test: + if: | + (inputs.mode == 'pr' && github.event_name == 'pull_request' && github.event.pull_request.draft == false) || + (inputs.mode == 'comment' && (contains(github.event.comment.body, '/oc') || contains(github.event.comment.body, '/opencode'))) + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + pull-requests: write + issues: read + steps: + - name: Get opencode version + id: version + shell: bash + run: | + VERSION=$(curl -sf https://api.github.com/repos/anomalyco/opencode/releases/latest | grep -o '"tag_name": *"[^"]*"' | cut -d'"' -f4) + echo "version=${VERSION:-latest}" >> "$GITHUB_OUTPUT" + - name: Cache opencode + uses: actions/cache@v4 + with: + path: ~/.opencode/bin + key: opencode-${{ runner.os }}-${{ runner.arch }}-${{ steps.version.outputs.version }} + - run: echo "done mode=${{ inputs.mode }}" From 7f3e9eaba7b1520a9f97893622f7d67d3c177be2 Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:48:04 +0700 Subject: [PATCH 30/46] ci: test version step only in reusable --- .github/workflows/test-reusable-call.yml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.github/workflows/test-reusable-call.yml b/.github/workflows/test-reusable-call.yml index 767505a..80103cf 100644 --- a/.github/workflows/test-reusable-call.yml +++ b/.github/workflows/test-reusable-call.yml @@ -37,9 +37,4 @@ jobs: run: | VERSION=$(curl -sf https://api.github.com/repos/anomalyco/opencode/releases/latest | grep -o '"tag_name": *"[^"]*"' | cut -d'"' -f4) echo "version=${VERSION:-latest}" >> "$GITHUB_OUTPUT" - - name: Cache opencode - uses: actions/cache@v4 - with: - path: ~/.opencode/bin - key: opencode-${{ runner.os }}-${{ runner.arch }}-${{ steps.version.outputs.version }} - - run: echo "done mode=${{ inputs.mode }}" + - run: echo "done mode=${{ inputs.mode }} version=${{ steps.version.outputs.version }}" From 86d5295d0c241a9b0ba901e5c462edd5127b84c7 Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:49:08 +0700 Subject: [PATCH 31/46] ci: test simple GITHUB_OUTPUT step in reusable --- .github/workflows/test-reusable-call.yml | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/.github/workflows/test-reusable-call.yml b/.github/workflows/test-reusable-call.yml index 80103cf..4edfe90 100644 --- a/.github/workflows/test-reusable-call.yml +++ b/.github/workflows/test-reusable-call.yml @@ -7,14 +7,6 @@ on: required: false type: string default: pr - model: - required: false - type: string - default: opencode-go/hy3 - prompt: - required: false - type: string - default: '' secrets: OPENCODE_API_KEY: required: true @@ -31,10 +23,8 @@ jobs: pull-requests: write issues: read steps: - - name: Get opencode version - id: version + - name: Set output + id: out shell: bash - run: | - VERSION=$(curl -sf https://api.github.com/repos/anomalyco/opencode/releases/latest | grep -o '"tag_name": *"[^"]*"' | cut -d'"' -f4) - echo "version=${VERSION:-latest}" >> "$GITHUB_OUTPUT" - - run: echo "done mode=${{ inputs.mode }} version=${{ steps.version.outputs.version }}" + run: echo "foo=bar" >> "$GITHUB_OUTPUT" + - run: echo "done foo=${{ steps.out.outputs.foo }} mode=${{ inputs.mode }}" From 823b6ebd22be85c267ad3cd7afc0d2ed81c52308 Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:51:10 +0700 Subject: [PATCH 32/46] ci: test fresh-named reusable files --- .github/workflows/test-caller-new.yml | 13 +++++++++++++ .github/workflows/test-reusable-call2.yml | 21 +++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 .github/workflows/test-caller-new.yml create mode 100644 .github/workflows/test-reusable-call2.yml diff --git a/.github/workflows/test-caller-new.yml b/.github/workflows/test-caller-new.yml new file mode 100644 index 0000000..5ddcf83 --- /dev/null +++ b/.github/workflows/test-caller-new.yml @@ -0,0 +1,13 @@ +name: test-caller-new + +on: + workflow_dispatch: + pull_request: + +jobs: + call: + uses: ./.github/workflows/test-reusable-call2.yml + with: + mode: pr + secrets: + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} diff --git a/.github/workflows/test-reusable-call2.yml b/.github/workflows/test-reusable-call2.yml new file mode 100644 index 0000000..203671f --- /dev/null +++ b/.github/workflows/test-reusable-call2.yml @@ -0,0 +1,21 @@ +name: test-reusable-call2 + +on: + workflow_call: + inputs: + mode: + required: false + type: string + default: pr + secrets: + OPENCODE_API_KEY: + required: true + +jobs: + test: + if: | + (inputs.mode == 'pr' && github.event_name == 'pull_request' && github.event.pull_request.draft == false) || + (inputs.mode == 'comment' && (contains(github.event.comment.body, '/oc') || contains(github.event.comment.body, '/opencode'))) + runs-on: ubuntu-latest + steps: + - run: echo "NEW reusable works mode=${{ inputs.mode }}" From c23fae0f5364a70cd07b93364a764e6a8405e333 Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:52:23 +0700 Subject: [PATCH 33/46] ci: rename workflows to fresh paths (ai-code-review) GitHub caches workflow definitions by file path. The opencode-review.yml path was cached with a broken definition (if-on-uses-job, composite action inside reusable), so every edit kept failing with startup_failure. New paths ai-code-review.yml + ai-code-review-reusable.yml parse fresh: - default model opencode-go/hy3 - mode input gates pr vs /oc on-demand inside the reusable job - opencode run inlined as shell steps (composite action not resolvable inside reusable workflows) - multi-line prompt via GITHUB_OUTPUT heredoc form --- ...usable.yml => ai-code-review-reusable.yml} | 8 ++-- .github/workflows/ai-code-review.yml | 42 +++++++++++++++++++ .github/workflows/opencode-review.yml | 29 ------------- .github/workflows/test-caller-2trig.yml | 14 ------- .github/workflows/test-caller-if4.yml | 13 ------ .github/workflows/test-caller-new.yml | 13 ------ .github/workflows/test-reusable-call.yml | 30 ------------- .github/workflows/test-reusable-call2.yml | 21 ---------- 8 files changed, 46 insertions(+), 124 deletions(-) rename .github/workflows/{opencode-review-reusable.yml => ai-code-review-reusable.yml} (95%) create mode 100644 .github/workflows/ai-code-review.yml delete mode 100644 .github/workflows/opencode-review.yml delete mode 100644 .github/workflows/test-caller-2trig.yml delete mode 100644 .github/workflows/test-caller-if4.yml delete mode 100644 .github/workflows/test-caller-new.yml delete mode 100644 .github/workflows/test-reusable-call.yml delete mode 100644 .github/workflows/test-reusable-call2.yml diff --git a/.github/workflows/opencode-review-reusable.yml b/.github/workflows/ai-code-review-reusable.yml similarity index 95% rename from .github/workflows/opencode-review-reusable.yml rename to .github/workflows/ai-code-review-reusable.yml index 905cba2..0e03376 100644 --- a/.github/workflows/opencode-review-reusable.yml +++ b/.github/workflows/ai-code-review-reusable.yml @@ -1,11 +1,11 @@ -name: opencode-review +name: ai-code-review-reusable -# Reusable PR review workflow. +# Reusable AI code review workflow. # # Call from any repo: # jobs: # review: -# uses: vuon9/devtoolbox/.github/workflows/opencode-review-reusable.yml@main +# uses: vuon9/devtoolbox/.github/workflows/ai-code-review-reusable.yml@main # with: # model: opencode-go/hy3 # optional, has default # prompt: "Custom review prompt" # optional @@ -19,7 +19,7 @@ name: opencode-review # Notes: # - Uses the runner GITHUB_TOKEN (use_github_token: true), no GitHub App needed. # - Requires the calling repo to grant pull-requests: write to the job. -# - Default model: opencode-go/hy3 (no billing needed). +# - Default model: opencode-go/hy3. on: workflow_call: diff --git a/.github/workflows/ai-code-review.yml b/.github/workflows/ai-code-review.yml new file mode 100644 index 0000000..03b34d8 --- /dev/null +++ b/.github/workflows/ai-code-review.yml @@ -0,0 +1,42 @@ +name: ai-code-review + +# Dispatcher for AI code review. The actual review logic lives in +# ai-code-review-reusable.yml (workflow_call) so any repo can reuse it: +# +# jobs: +# review: +# uses: vuon9/devtoolbox/.github/workflows/ai-code-review-reusable.yml@main +# with: +# model: opencode-go/hy3 # optional, has default +# prompt: "Custom prompt" # optional +# mode: pr # optional: pr | comment +# secrets: +# OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} +# +# NOTE: GitHub caches workflow definitions by file path. If this file was +# previously deployed under a different name (e.g. opencode-review.yml), +# renames/edits may not re-parse; use a NEW file name when restructuring. + +on: + workflow_dispatch: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + issue_comment: + types: [created] + pull_request_review_comment: + types: [created] + +jobs: + review-pr: + uses: ./.github/workflows/ai-code-review-reusable.yml + with: + mode: pr + secrets: + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} + + on-demand: + uses: ./.github/workflows/ai-code-review-reusable.yml + with: + mode: comment + secrets: + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml deleted file mode 100644 index 148643c..0000000 --- a/.github/workflows/opencode-review.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: opencode-review - -on: - workflow_dispatch: - pull_request: - types: [opened, synchronize, reopened, ready_for_review] - issue_comment: - types: [created] - pull_request_review_comment: - types: [created] - -# Dispatcher: delegates to the reusable workflow so the review logic lives -# in one place (opencode-review-reusable.yml) and can be reused by other repos. -# GitHub does not allow `if:` conditions on jobs that use a reusable workflow, -# so the pr/comment gating lives inside the reusable workflow via the `mode` input. -jobs: - review-pr: - uses: ./.github/workflows/opencode-review-reusable.yml - with: - mode: pr - secrets: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} - - on-demand: - uses: ./.github/workflows/opencode-review-reusable.yml - with: - mode: comment - secrets: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} diff --git a/.github/workflows/test-caller-2trig.yml b/.github/workflows/test-caller-2trig.yml deleted file mode 100644 index 76bf345..0000000 --- a/.github/workflows/test-caller-2trig.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: test-caller-2trig - -on: - workflow_dispatch: - pull_request: - types: [opened, synchronize, reopened, ready_for_review] - -jobs: - review-pr: - uses: ./.github/workflows/opencode-review-reusable.yml - with: - mode: pr - secrets: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} diff --git a/.github/workflows/test-caller-if4.yml b/.github/workflows/test-caller-if4.yml deleted file mode 100644 index d995429..0000000 --- a/.github/workflows/test-caller-if4.yml +++ /dev/null @@ -1,13 +0,0 @@ -name: test-caller-if4 - -on: - workflow_dispatch: - pull_request: - -jobs: - call: - uses: ./.github/workflows/test-reusable-call.yml - with: - mode: pr - secrets: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} diff --git a/.github/workflows/test-caller-new.yml b/.github/workflows/test-caller-new.yml deleted file mode 100644 index 5ddcf83..0000000 --- a/.github/workflows/test-caller-new.yml +++ /dev/null @@ -1,13 +0,0 @@ -name: test-caller-new - -on: - workflow_dispatch: - pull_request: - -jobs: - call: - uses: ./.github/workflows/test-reusable-call2.yml - with: - mode: pr - secrets: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} diff --git a/.github/workflows/test-reusable-call.yml b/.github/workflows/test-reusable-call.yml deleted file mode 100644 index 4edfe90..0000000 --- a/.github/workflows/test-reusable-call.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: test-reusable-call - -on: - workflow_call: - inputs: - mode: - required: false - type: string - default: pr - secrets: - OPENCODE_API_KEY: - required: true - -jobs: - test: - if: | - (inputs.mode == 'pr' && github.event_name == 'pull_request' && github.event.pull_request.draft == false) || - (inputs.mode == 'comment' && (contains(github.event.comment.body, '/oc') || contains(github.event.comment.body, '/opencode'))) - runs-on: ubuntu-latest - permissions: - id-token: write - contents: read - pull-requests: write - issues: read - steps: - - name: Set output - id: out - shell: bash - run: echo "foo=bar" >> "$GITHUB_OUTPUT" - - run: echo "done foo=${{ steps.out.outputs.foo }} mode=${{ inputs.mode }}" diff --git a/.github/workflows/test-reusable-call2.yml b/.github/workflows/test-reusable-call2.yml deleted file mode 100644 index 203671f..0000000 --- a/.github/workflows/test-reusable-call2.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: test-reusable-call2 - -on: - workflow_call: - inputs: - mode: - required: false - type: string - default: pr - secrets: - OPENCODE_API_KEY: - required: true - -jobs: - test: - if: | - (inputs.mode == 'pr' && github.event_name == 'pull_request' && github.event.pull_request.draft == false) || - (inputs.mode == 'comment' && (contains(github.event.comment.body, '/oc') || contains(github.event.comment.body, '/opencode'))) - runs-on: ubuntu-latest - steps: - - run: echo "NEW reusable works mode=${{ inputs.mode }}" From 10053c4fac69da53fca306b0aa6c8d2f26dd0a38 Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:53:29 +0700 Subject: [PATCH 34/46] ci: test 2-trigger caller vs complex reusable --- .github/workflows/test-caller-2trig.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .github/workflows/test-caller-2trig.yml diff --git a/.github/workflows/test-caller-2trig.yml b/.github/workflows/test-caller-2trig.yml new file mode 100644 index 0000000..7948d93 --- /dev/null +++ b/.github/workflows/test-caller-2trig.yml @@ -0,0 +1,14 @@ +name: test-caller-2trig + +on: + workflow_dispatch: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + +jobs: + review-pr: + uses: ./.github/workflows/ai-code-review-reusable.yml + with: + mode: pr + secrets: + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} From 0ec7111e2056b0db5b0aa6c870b59dca48f0019a Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:54:24 +0700 Subject: [PATCH 35/46] ci: test checkout step in reusable --- .github/workflows/test-caller-2trig.yml | 2 +- .github/workflows/test-reusable-call2.yml | 39 +++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/test-reusable-call2.yml diff --git a/.github/workflows/test-caller-2trig.yml b/.github/workflows/test-caller-2trig.yml index 7948d93..f47b83d 100644 --- a/.github/workflows/test-caller-2trig.yml +++ b/.github/workflows/test-caller-2trig.yml @@ -7,7 +7,7 @@ on: jobs: review-pr: - uses: ./.github/workflows/ai-code-review-reusable.yml + uses: ./.github/workflows/test-reusable-call2.yml with: mode: pr secrets: diff --git a/.github/workflows/test-reusable-call2.yml b/.github/workflows/test-reusable-call2.yml new file mode 100644 index 0000000..2c2a1bb --- /dev/null +++ b/.github/workflows/test-reusable-call2.yml @@ -0,0 +1,39 @@ +name: test-reusable-call2 + +on: + workflow_call: + inputs: + mode: + required: false + type: string + default: pr + model: + required: false + type: string + default: opencode-go/hy3 + prompt: + required: false + type: string + default: '' + secrets: + OPENCODE_API_KEY: + required: true + +jobs: + test: + if: | + (inputs.mode == 'pr' && github.event_name == 'pull_request' && github.event.pull_request.draft == false) || + (inputs.mode == 'comment' && (contains(github.event.comment.body, '/oc') || contains(github.event.comment.body, '/opencode'))) + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + pull-requests: write + issues: read + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + fetch-depth: 1 + persist-credentials: false + - run: echo "checkout OK mode=${{ inputs.mode }}" From a408085465e74e3090a73583263694b32e8bca86 Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:55:29 +0700 Subject: [PATCH 36/46] ci: test permissions only in reusable --- .github/workflows/test-reusable-call2.yml | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/.github/workflows/test-reusable-call2.yml b/.github/workflows/test-reusable-call2.yml index 2c2a1bb..e4bf98b 100644 --- a/.github/workflows/test-reusable-call2.yml +++ b/.github/workflows/test-reusable-call2.yml @@ -7,14 +7,6 @@ on: required: false type: string default: pr - model: - required: false - type: string - default: opencode-go/hy3 - prompt: - required: false - type: string - default: '' secrets: OPENCODE_API_KEY: required: true @@ -31,9 +23,4 @@ jobs: pull-requests: write issues: read steps: - - name: Checkout repository - uses: actions/checkout@v6 - with: - fetch-depth: 1 - persist-credentials: false - - run: echo "checkout OK mode=${{ inputs.mode }}" + - run: echo "permissions OK mode=${{ inputs.mode }}" From 238327bedf74dca69eb973ddb137a212fa8d51f8 Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:56:23 +0700 Subject: [PATCH 37/46] ci: test caller without pull_request types --- .github/workflows/test-caller-2trig.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/test-caller-2trig.yml b/.github/workflows/test-caller-2trig.yml index f47b83d..a00afa6 100644 --- a/.github/workflows/test-caller-2trig.yml +++ b/.github/workflows/test-caller-2trig.yml @@ -3,7 +3,6 @@ name: test-caller-2trig on: workflow_dispatch: pull_request: - types: [opened, synchronize, reopened, ready_for_review] jobs: review-pr: From 0ee06c9b0164225b9bc7097db44ee49d9ab89ead Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:57:10 +0700 Subject: [PATCH 38/46] ci: test same caller but different file name --- .github/workflows/test-caller-new.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .github/workflows/test-caller-new.yml diff --git a/.github/workflows/test-caller-new.yml b/.github/workflows/test-caller-new.yml new file mode 100644 index 0000000..5ddcf83 --- /dev/null +++ b/.github/workflows/test-caller-new.yml @@ -0,0 +1,13 @@ +name: test-caller-new + +on: + workflow_dispatch: + pull_request: + +jobs: + call: + uses: ./.github/workflows/test-reusable-call2.yml + with: + mode: pr + secrets: + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} From ca49c8e4d5328f8d85462683f8b144b3cb56b592 Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:58:08 +0700 Subject: [PATCH 39/46] ci: test permissions without id-token --- .github/workflows/test-reusable-call2.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/test-reusable-call2.yml b/.github/workflows/test-reusable-call2.yml index e4bf98b..2a1dcfb 100644 --- a/.github/workflows/test-reusable-call2.yml +++ b/.github/workflows/test-reusable-call2.yml @@ -18,7 +18,6 @@ jobs: (inputs.mode == 'comment' && (contains(github.event.comment.body, '/oc') || contains(github.event.comment.body, '/opencode'))) runs-on: ubuntu-latest permissions: - id-token: write contents: read pull-requests: write issues: read From 5664f891b4919b97fca0c17d1acd43eb5f138b42 Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:59:13 +0700 Subject: [PATCH 40/46] ci: test permissions on caller not reusable --- .github/workflows/test-caller-new.yml | 6 ++++++ .github/workflows/test-reusable-call2.yml | 6 +----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test-caller-new.yml b/.github/workflows/test-caller-new.yml index 5ddcf83..10e9833 100644 --- a/.github/workflows/test-caller-new.yml +++ b/.github/workflows/test-caller-new.yml @@ -4,6 +4,12 @@ on: workflow_dispatch: pull_request: +permissions: + id-token: write + contents: read + pull-requests: write + issues: read + jobs: call: uses: ./.github/workflows/test-reusable-call2.yml diff --git a/.github/workflows/test-reusable-call2.yml b/.github/workflows/test-reusable-call2.yml index 2a1dcfb..41c6021 100644 --- a/.github/workflows/test-reusable-call2.yml +++ b/.github/workflows/test-reusable-call2.yml @@ -17,9 +17,5 @@ jobs: (inputs.mode == 'pr' && github.event_name == 'pull_request' && github.event.pull_request.draft == false) || (inputs.mode == 'comment' && (contains(github.event.comment.body, '/oc') || contains(github.event.comment.body, '/opencode'))) runs-on: ubuntu-latest - permissions: - contents: read - pull-requests: write - issues: read steps: - - run: echo "permissions OK mode=${{ inputs.mode }}" + - run: echo "no-permissions reusable OK mode=${{ inputs.mode }}" From 3cb908e5bca80ea074118b24c558b83115fa5d56 Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 18:00:19 +0700 Subject: [PATCH 41/46] ci: fix reusable workflow permissions placement Root cause of repeated startup_failure: a permissions block inside a reusable workflow (workflow_call) job is rejected by GitHub Actions. Move permissions to the caller jobs (review-pr, on-demand); the reusable job now inherits them. Confirmed by isolation test: identical caller+reusable pair succeeds with permissions on caller, fails with permissions inside reusable. --- .github/workflows/ai-code-review-reusable.yml | 14 ++++++------- .github/workflows/ai-code-review.yml | 10 +++++++++ .github/workflows/test-caller-2trig.yml | 13 ------------ .github/workflows/test-caller-new.yml | 19 ----------------- .github/workflows/test-reusable-call2.yml | 21 ------------------- 5 files changed, 17 insertions(+), 60 deletions(-) delete mode 100644 .github/workflows/test-caller-2trig.yml delete mode 100644 .github/workflows/test-caller-new.yml delete mode 100644 .github/workflows/test-reusable-call2.yml diff --git a/.github/workflows/ai-code-review-reusable.yml b/.github/workflows/ai-code-review-reusable.yml index 0e03376..eab1e94 100644 --- a/.github/workflows/ai-code-review-reusable.yml +++ b/.github/workflows/ai-code-review-reusable.yml @@ -2,9 +2,15 @@ name: ai-code-review-reusable # Reusable AI code review workflow. # -# Call from any repo: +# Call from any repo (permissions MUST live on the caller job — a +# permissions block inside the reusable job causes startup_failure): # jobs: # review: +# permissions: +# id-token: write +# contents: read +# pull-requests: write +# issues: read # uses: vuon9/devtoolbox/.github/workflows/ai-code-review-reusable.yml@main # with: # model: opencode-go/hy3 # optional, has default @@ -18,7 +24,6 @@ name: ai-code-review-reusable # # Notes: # - Uses the runner GITHUB_TOKEN (use_github_token: true), no GitHub App needed. -# - Requires the calling repo to grant pull-requests: write to the job. # - Default model: opencode-go/hy3. on: @@ -49,11 +54,6 @@ jobs: (inputs.mode == 'pr' && github.event_name == 'pull_request' && github.event.pull_request.draft == false) || (inputs.mode == 'comment' && (contains(github.event.comment.body, '/oc') || contains(github.event.comment.body, '/opencode'))) runs-on: ubuntu-latest - permissions: - id-token: write - contents: read - pull-requests: write - issues: read env: DEFAULT_PROMPT: | Review this pull request thoroughly: diff --git a/.github/workflows/ai-code-review.yml b/.github/workflows/ai-code-review.yml index 03b34d8..a5a2d0c 100644 --- a/.github/workflows/ai-code-review.yml +++ b/.github/workflows/ai-code-review.yml @@ -28,6 +28,11 @@ on: jobs: review-pr: + permissions: + id-token: write + contents: read + pull-requests: write + issues: read uses: ./.github/workflows/ai-code-review-reusable.yml with: mode: pr @@ -35,6 +40,11 @@ jobs: OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} on-demand: + permissions: + id-token: write + contents: read + pull-requests: write + issues: write uses: ./.github/workflows/ai-code-review-reusable.yml with: mode: comment diff --git a/.github/workflows/test-caller-2trig.yml b/.github/workflows/test-caller-2trig.yml deleted file mode 100644 index a00afa6..0000000 --- a/.github/workflows/test-caller-2trig.yml +++ /dev/null @@ -1,13 +0,0 @@ -name: test-caller-2trig - -on: - workflow_dispatch: - pull_request: - -jobs: - review-pr: - uses: ./.github/workflows/test-reusable-call2.yml - with: - mode: pr - secrets: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} diff --git a/.github/workflows/test-caller-new.yml b/.github/workflows/test-caller-new.yml deleted file mode 100644 index 10e9833..0000000 --- a/.github/workflows/test-caller-new.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: test-caller-new - -on: - workflow_dispatch: - pull_request: - -permissions: - id-token: write - contents: read - pull-requests: write - issues: read - -jobs: - call: - uses: ./.github/workflows/test-reusable-call2.yml - with: - mode: pr - secrets: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} diff --git a/.github/workflows/test-reusable-call2.yml b/.github/workflows/test-reusable-call2.yml deleted file mode 100644 index 41c6021..0000000 --- a/.github/workflows/test-reusable-call2.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: test-reusable-call2 - -on: - workflow_call: - inputs: - mode: - required: false - type: string - default: pr - secrets: - OPENCODE_API_KEY: - required: true - -jobs: - test: - if: | - (inputs.mode == 'pr' && github.event_name == 'pull_request' && github.event.pull_request.draft == false) || - (inputs.mode == 'comment' && (contains(github.event.comment.body, '/oc') || contains(github.event.comment.body, '/opencode'))) - runs-on: ubuntu-latest - steps: - - run: echo "no-permissions reusable OK mode=${{ inputs.mode }}" From fff387b3b0de08337f3f87e8f42c945e545f9206 Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 18:03:43 +0700 Subject: [PATCH 42/46] ci: apply review feedback to ai-code-review workflow From the hy3 review on PR #162: - Fix on-demand gating: support both issue_comment and pull_request_review_comment event payload shapes, word-boundary /oc match, PR-only guard, author_association guard, exclude bot - Least privilege: drop unused id-token, issues: write -> read - Pin actions: checkout@v6.0.3, cache@v5.0.5 (match ci.yml) --- .github/workflows/ai-code-review-reusable.yml | 17 ++++++++++++++--- .github/workflows/ai-code-review.yml | 4 +--- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ai-code-review-reusable.yml b/.github/workflows/ai-code-review-reusable.yml index eab1e94..e0a785f 100644 --- a/.github/workflows/ai-code-review-reusable.yml +++ b/.github/workflows/ai-code-review-reusable.yml @@ -52,7 +52,18 @@ jobs: review: if: | (inputs.mode == 'pr' && github.event_name == 'pull_request' && github.event.pull_request.draft == false) || - (inputs.mode == 'comment' && (contains(github.event.comment.body, '/oc') || contains(github.event.comment.body, '/opencode'))) + (inputs.mode == 'comment' && + github.actor != 'github-actions[bot]' && + (github.event.issue.pull_request != null || github.event.pull_request != null) && + contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association) && + (contains(github.event.comment.body, ' /oc') || + startsWith(github.event.comment.body, '/oc') || + contains(github.event.comment.body, ' /opencode') || + startsWith(github.event.comment.body, '/opencode') || + contains(github.event.review_comment.body, ' /oc') || + startsWith(github.event.review_comment.body, '/oc') || + contains(github.event.review_comment.body, ' /opencode') || + startsWith(github.event.review_comment.body, '/opencode'))) runs-on: ubuntu-latest env: DEFAULT_PROMPT: | @@ -65,7 +76,7 @@ jobs: Post a clear, structured review comment with file references. steps: - name: Checkout repository - uses: actions/checkout@v6 + uses: actions/checkout@v6.0.3 with: fetch-depth: 1 persist-credentials: false @@ -91,7 +102,7 @@ jobs: echo "version=${VERSION:-latest}" >> "$GITHUB_OUTPUT" - name: Cache opencode - uses: actions/cache@v4 + uses: actions/cache@v5.0.5 with: path: ~/.opencode/bin key: opencode-${{ runner.os }}-${{ runner.arch }}-${{ steps.version.outputs.version }} diff --git a/.github/workflows/ai-code-review.yml b/.github/workflows/ai-code-review.yml index a5a2d0c..7a16706 100644 --- a/.github/workflows/ai-code-review.yml +++ b/.github/workflows/ai-code-review.yml @@ -29,7 +29,6 @@ on: jobs: review-pr: permissions: - id-token: write contents: read pull-requests: write issues: read @@ -41,10 +40,9 @@ jobs: on-demand: permissions: - id-token: write contents: read pull-requests: write - issues: write + issues: read uses: ./.github/workflows/ai-code-review-reusable.yml with: mode: comment From eab9ed1d4f14e7154790c1afef884ec60b7b19f8 Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Fri, 21 Aug 2026 18:52:14 +0700 Subject: [PATCH 43/46] ci: add concurrency + timeout to stop notification spam - concurrency group per PR/event with cancel-in-progress: a new push cancels the previous run instead of stacking more runs/comments - timeout-minutes: 10 on the review job so a slow/hung model run fails fast instead of hanging ~50 minutes --- .github/workflows/ai-code-review-reusable.yml | 1 + .github/workflows/ai-code-review.yml | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/.github/workflows/ai-code-review-reusable.yml b/.github/workflows/ai-code-review-reusable.yml index e0a785f..2bbc926 100644 --- a/.github/workflows/ai-code-review-reusable.yml +++ b/.github/workflows/ai-code-review-reusable.yml @@ -64,6 +64,7 @@ jobs: startsWith(github.event.review_comment.body, '/oc') || contains(github.event.review_comment.body, ' /opencode') || startsWith(github.event.review_comment.body, '/opencode'))) + timeout-minutes: 10 runs-on: ubuntu-latest env: DEFAULT_PROMPT: | diff --git a/.github/workflows/ai-code-review.yml b/.github/workflows/ai-code-review.yml index 7a16706..77e3d34 100644 --- a/.github/workflows/ai-code-review.yml +++ b/.github/workflows/ai-code-review.yml @@ -26,6 +26,12 @@ on: pull_request_review_comment: types: [created] +# Only the newest run per PR/event executes; older in-flight runs are +# cancelled instead of stacking up notifications. +concurrency: + group: ai-code-review-${{ github.event.pull_request.number || github.event.issue.number || github.ref }} + cancel-in-progress: true + jobs: review-pr: permissions: From 81334dd32da3c431e6ed1e64d0759fc4940804ac Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Sat, 22 Aug 2026 09:03:34 +0700 Subject: [PATCH 44/46] ci: switch default review model to muse-spark-1.2-contributor User opted in to data usage, so use the full Muse Spark contributor model (better for code review) instead of hy3. --- .github/workflows/ai-code-review-reusable.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ai-code-review-reusable.yml b/.github/workflows/ai-code-review-reusable.yml index 2bbc926..29ac922 100644 --- a/.github/workflows/ai-code-review-reusable.yml +++ b/.github/workflows/ai-code-review-reusable.yml @@ -13,7 +13,7 @@ name: ai-code-review-reusable # issues: read # uses: vuon9/devtoolbox/.github/workflows/ai-code-review-reusable.yml@main # with: -# model: opencode-go/hy3 # optional, has default +# model: opencode/muse-spark-1.2-contributor # optional, has default # prompt: "Custom review prompt" # optional # mode: pr # optional: pr | comment # secrets: @@ -24,7 +24,7 @@ name: ai-code-review-reusable # # Notes: # - Uses the runner GITHUB_TOKEN (use_github_token: true), no GitHub App needed. -# - Default model: opencode-go/hy3. +# - Default model: opencode/muse-spark-1.2-contributor (requires data opt-in). on: workflow_call: @@ -33,7 +33,7 @@ on: description: Model to use for the review required: false type: string - default: opencode-go/hy3 + default: opencode/muse-spark-1.2-contributor prompt: description: Custom review prompt (defaults to a thorough review prompt) required: false From 620c974501307130fff273e5c97a8c61c8019cfd Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Sat, 22 Aug 2026 09:05:11 +0700 Subject: [PATCH 45/46] ci: use muse-spark free tier (non-free needs workspace credits) The opted-in full muse-spark-1.2-contributor still requires workspace credits (Insufficient balance). The -free variant is the same Muse Spark reviewer model at no cost and runs without billing. --- .github/workflows/ai-code-review-reusable.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ai-code-review-reusable.yml b/.github/workflows/ai-code-review-reusable.yml index 29ac922..cd347f0 100644 --- a/.github/workflows/ai-code-review-reusable.yml +++ b/.github/workflows/ai-code-review-reusable.yml @@ -13,7 +13,7 @@ name: ai-code-review-reusable # issues: read # uses: vuon9/devtoolbox/.github/workflows/ai-code-review-reusable.yml@main # with: -# model: opencode/muse-spark-1.2-contributor # optional, has default +# model: opencode/muse-spark-1.2-contributor-free # optional, has default # prompt: "Custom review prompt" # optional # mode: pr # optional: pr | comment # secrets: @@ -24,7 +24,8 @@ name: ai-code-review-reusable # # Notes: # - Uses the runner GITHUB_TOKEN (use_github_token: true), no GitHub App needed. -# - Default model: opencode/muse-spark-1.2-contributor (requires data opt-in). +# - Default model: opencode/muse-spark-1.2-contributor-free (free tier; the +# non-free variant requires workspace credits). on: workflow_call: @@ -33,7 +34,7 @@ on: description: Model to use for the review required: false type: string - default: opencode/muse-spark-1.2-contributor + default: opencode/muse-spark-1.2-contributor-free prompt: description: Custom review prompt (defaults to a thorough review prompt) required: false From df62fa5444f9f12733f89eb5f31a394844c34d59 Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Sat, 22 Aug 2026 09:28:10 +0700 Subject: [PATCH 46/46] ci: use ai-code-review reusable from gh-workflows repo Move the reusable workflow to vuon9/gh-workflows (v0.1.9) so it can be shared across repositories. devtoolbox keeps only the thin dispatcher. --- .github/workflows/ai-code-review-reusable.yml | 130 ------------------ .github/workflows/ai-code-review.yml | 20 +-- 2 files changed, 10 insertions(+), 140 deletions(-) delete mode 100644 .github/workflows/ai-code-review-reusable.yml diff --git a/.github/workflows/ai-code-review-reusable.yml b/.github/workflows/ai-code-review-reusable.yml deleted file mode 100644 index cd347f0..0000000 --- a/.github/workflows/ai-code-review-reusable.yml +++ /dev/null @@ -1,130 +0,0 @@ -name: ai-code-review-reusable - -# Reusable AI code review workflow. -# -# Call from any repo (permissions MUST live on the caller job — a -# permissions block inside the reusable job causes startup_failure): -# jobs: -# review: -# permissions: -# id-token: write -# contents: read -# pull-requests: write -# issues: read -# uses: vuon9/devtoolbox/.github/workflows/ai-code-review-reusable.yml@main -# with: -# model: opencode/muse-spark-1.2-contributor-free # optional, has default -# prompt: "Custom review prompt" # optional -# mode: pr # optional: pr | comment -# secrets: -# OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} # required -# -# mode=pr → runs only for non-draft pull_request events -# mode=comment → runs only when the comment contains /oc or /opencode -# -# Notes: -# - Uses the runner GITHUB_TOKEN (use_github_token: true), no GitHub App needed. -# - Default model: opencode/muse-spark-1.2-contributor-free (free tier; the -# non-free variant requires workspace credits). - -on: - workflow_call: - inputs: - model: - description: Model to use for the review - required: false - type: string - default: opencode/muse-spark-1.2-contributor-free - prompt: - description: Custom review prompt (defaults to a thorough review prompt) - required: false - type: string - default: '' - mode: - description: pr (auto-review PRs) or comment (on-demand /oc /opencode) - required: false - type: string - default: pr - secrets: - OPENCODE_API_KEY: - required: true - -jobs: - review: - if: | - (inputs.mode == 'pr' && github.event_name == 'pull_request' && github.event.pull_request.draft == false) || - (inputs.mode == 'comment' && - github.actor != 'github-actions[bot]' && - (github.event.issue.pull_request != null || github.event.pull_request != null) && - contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association) && - (contains(github.event.comment.body, ' /oc') || - startsWith(github.event.comment.body, '/oc') || - contains(github.event.comment.body, ' /opencode') || - startsWith(github.event.comment.body, '/opencode') || - contains(github.event.review_comment.body, ' /oc') || - startsWith(github.event.review_comment.body, '/oc') || - contains(github.event.review_comment.body, ' /opencode') || - startsWith(github.event.review_comment.body, '/opencode'))) - timeout-minutes: 10 - runs-on: ubuntu-latest - env: - DEFAULT_PROMPT: | - Review this pull request thoroughly: - - Check for bugs, logic errors and edge cases - - Look for security issues and unsafe patterns - - Assess code quality, consistency and maintainability - - Flag missing or weak tests - - Suggest concrete improvements - Post a clear, structured review comment with file references. - steps: - - name: Checkout repository - uses: actions/checkout@v6.0.3 - with: - fetch-depth: 1 - persist-credentials: false - - - name: Prepare review prompt - id: prompt - shell: bash - run: | - PROMPT="${{ inputs.prompt }}" - if [ -z "$PROMPT" ]; then PROMPT="$DEFAULT_PROMPT"; fi - printf '%s\n' "$PROMPT" > /tmp/review-prompt.txt - { - echo "value<> "$GITHUB_OUTPUT" - - - name: Get opencode version - id: version - shell: bash - run: | - VERSION=$(curl -sf https://api.github.com/repos/anomalyco/opencode/releases/latest | grep -o '"tag_name": *"[^"]*"' | cut -d'"' -f4) - echo "version=${VERSION:-latest}" >> "$GITHUB_OUTPUT" - - - name: Cache opencode - uses: actions/cache@v5.0.5 - with: - path: ~/.opencode/bin - key: opencode-${{ runner.os }}-${{ runner.arch }}-${{ steps.version.outputs.version }} - - - name: Install opencode - if: steps.cache.outputs.cache-hit != 'true' - shell: bash - run: curl -fsSL https://opencode.ai/install | bash - - - name: Add opencode to PATH - shell: bash - run: echo "$HOME/.opencode/bin" >> "$GITHUB_PATH" - - - name: Run OpenCode review - shell: bash - id: run_opencode - run: opencode github run - env: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - MODEL: ${{ inputs.model }} - PROMPT: ${{ steps.prompt.outputs.value }} - USE_GITHUB_TOKEN: "true" diff --git a/.github/workflows/ai-code-review.yml b/.github/workflows/ai-code-review.yml index 77e3d34..d8fe3ff 100644 --- a/.github/workflows/ai-code-review.yml +++ b/.github/workflows/ai-code-review.yml @@ -1,21 +1,21 @@ name: ai-code-review -# Dispatcher for AI code review. The actual review logic lives in -# ai-code-review-reusable.yml (workflow_call) so any repo can reuse it: +# Dispatcher for AI code review. The actual review logic lives in the +# reusable gh-workflows repo: # # jobs: # review: -# uses: vuon9/devtoolbox/.github/workflows/ai-code-review-reusable.yml@main +# permissions: +# contents: read +# pull-requests: write +# issues: read +# uses: vuon9/gh-workflows/.github/workflows/ai-code-review-reusable.yml@v0.1.9 # with: -# model: opencode-go/hy3 # optional, has default +# model: opencode/muse-spark-1.2-contributor-free # optional # prompt: "Custom prompt" # optional # mode: pr # optional: pr | comment # secrets: # OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} -# -# NOTE: GitHub caches workflow definitions by file path. If this file was -# previously deployed under a different name (e.g. opencode-review.yml), -# renames/edits may not re-parse; use a NEW file name when restructuring. on: workflow_dispatch: @@ -38,7 +38,7 @@ jobs: contents: read pull-requests: write issues: read - uses: ./.github/workflows/ai-code-review-reusable.yml + uses: vuon9/gh-workflows/.github/workflows/ai-code-review-reusable.yml@v0.1.9 with: mode: pr secrets: @@ -49,7 +49,7 @@ jobs: contents: read pull-requests: write issues: read - uses: ./.github/workflows/ai-code-review-reusable.yml + uses: vuon9/gh-workflows/.github/workflows/ai-code-review-reusable.yml@v0.1.9 with: mode: comment secrets: