From 07e7706d5503ad699be48d08e34ed650e799a532 Mon Sep 17 00:00:00 2001 From: Dylan Moore <60218243+dymoo@users.noreply.github.com> Date: Wed, 9 Sep 2026 10:13:08 +0100 Subject: [PATCH 1/2] Use isolated GLM high reviewer credentials --- .github/workflows/shipyard-reviewer.yml | 16 ++++++++-------- AGENTS.md | 8 ++++++-- README.md | 13 +++++++++++++ test/cloud-coder-action.test.js | 3 +-- test/reviewer-workflow.test.js | 22 ++++++++++++++++++++++ 5 files changed, 50 insertions(+), 12 deletions(-) create mode 100644 test/reviewer-workflow.test.js diff --git a/.github/workflows/shipyard-reviewer.yml b/.github/workflows/shipyard-reviewer.yml index ddb4bbc..0b66175 100644 --- a/.github/workflows/shipyard-reviewer.yml +++ b/.github/workflows/shipyard-reviewer.yml @@ -1,5 +1,5 @@ -# Shipyard Cloud Reviewer reviewing itself. It uses the last released major rather -# than executing code from the pull request under review. +# Shipyard Cloud Reviewer reviewing itself. Use an audited immutable revision, +# never code from the pull request under review. name: Shipyard Cloud Reviewer on: @@ -24,19 +24,19 @@ concurrency: jobs: review: if: >- - vars.LLM_BASE_URL != '' && - vars.LLM_MODEL != '' && + vars.OPENROUTER_REVIEWER_ENABLED == 'true' && ((github.event_name == 'pull_request_target' && !startsWith(github.event.pull_request.head.ref, 'shipyard/issue-')) || (github.event_name == 'repository_dispatch' && github.event.action == 'shipyard-review') || (github.event.issue.pull_request && contains(github.event.comment.body, '@shipyard'))) runs-on: shipyard-runners steps: - - uses: dymoo/shipyard@v3 + - uses: dymoo/shipyard@efec252b39c55bc3d902d14d59286660046f9776 with: - api-key: ${{ secrets.LLM_API_KEY }} - base-url: ${{ vars.LLM_BASE_URL }} - model: ${{ vars.LLM_MODEL }} + api-key: ${{ secrets.OPENROUTER_REVIEWER_API_KEY }} + base-url: https://openrouter.ai/api/v1 + model: z-ai/glm-5.3-flash + reasoning-effort: high handoff-token: ${{ secrets.SHIPYARD_HANDOFF_TOKEN }} instructions: | This action has no runtime dependencies. Any import that is not diff --git a/AGENTS.md b/AGENTS.md index 0172fb6..607366b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -66,8 +66,12 @@ The review action's public inputs are exactly `api-key`, `base-url`, `model`, When omitted, reasoning effort uses the provider default; an explicit validated value is sent unchanged. The released `dymoo/shipyard@v3` reference does not accept this new input; consumers must pin a supporting immutable revision until -it is released. The self-review workflow remains on `@v3` and omits it. When -its base URL is exactly OpenRouter, the shared client adds the fixed public app +it is released. The self-review workflow pins a supporting immutable revision +and selects `z-ai/glm-5.3-flash` with explicit `high` effort. Its dedicated +`OPENROUTER_REVIEWER_API_KEY` is separate from the Coder's `LLM_API_KEY`; +`OPENROUTER_REVIEWER_ENABLED` must be `true` only after that key and the existing +hand-off secret are configured. The generic `@v3` examples remain unchanged. +When its base URL is exactly OpenRouter, the shared client adds the fixed public app attribution headers `HTTP-Referer: https://github.com/dymoo/shipyard` and `X-OpenRouter-Title: Shipyard`, plus one `session_id` derived from the opaque GitHub workflow-run identifier to keep provider prompt caches sticky. It never diff --git a/README.md b/README.md index 75d2e63..236d70c 100644 --- a/README.md +++ b/README.md @@ -182,6 +182,19 @@ image and `npm test`, because that command uses only Node's built-in test runner. A consumer whose Agent Brief declares another test command must publish an image containing that toolchain and use its immutable digest. +This repository's own reviewer pilot uses `z-ai/glm-5.3-flash` through OpenRouter +with explicit `high` reasoning and a supporting immutable action revision. It +uses a dedicated `OPENROUTER_REVIEWER_API_KEY`, not the Coder's `LLM_API_KEY`. +Keep its `OPENROUTER_REVIEWER_ENABLED` Variable absent or `false` until the new +key and existing `SHIPYARD_HANDOFF_TOKEN` are configured; set it to `true` to +activate, and clear it before removing or rotating either secret. The reviewer +key must allow only this model and retain strict ZDR/no-training routing with +both OpenRouter input/output logging options off and an owner-selected spending +cap. Account/member guardrails can further restrict it; key creation does not +prove model eligibility. Verify an actual review after activation. Coder model +variables and credentials remain unchanged. These pilot settings do not change +the configurable, released-version workflow examples above. + Use `dymoo/shipyard/cloud-coder@v4` in the copied workflow. Both actions must receive the same `SHIPYARD_HANDOFF_TOKEN`; Shipyard uses it only to sign and verify context-bound HMAC hand-offs, never stores it in the dispatch payload, diff --git a/test/cloud-coder-action.test.js b/test/cloud-coder-action.test.js index 920434a..21cd257 100644 --- a/test/cloud-coder-action.test.js +++ b/test/cloud-coder-action.test.js @@ -66,8 +66,7 @@ test('the Shipyard reviewer pilot targets the dedicated ARC scale set', () => { const workflow = fs.readFileSync(new URL('../.github/workflows/shipyard-reviewer.yml', import.meta.url), 'utf8'); assert.match(workflow, /^name: Shipyard Cloud Reviewer$/m); assert.match(workflow, /runs-on: shipyard-runners/); - assert.match(workflow, /vars\.LLM_BASE_URL != ''/); - assert.match(workflow, /vars\.LLM_MODEL != ''/); + assert.match(workflow, /vars\.OPENROUTER_REVIEWER_ENABLED == 'true'/); const example = fs.readFileSync(new URL('../examples/workflows/shipyard-reviewer.yml', import.meta.url), 'utf8'); assert.match(example, /vars\.LLM_BASE_URL != ''/); diff --git a/test/reviewer-workflow.test.js b/test/reviewer-workflow.test.js new file mode 100644 index 0000000..e6c5942 --- /dev/null +++ b/test/reviewer-workflow.test.js @@ -0,0 +1,22 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import fs from 'node:fs'; + +const workflow = fs.readFileSync(new URL('../.github/workflows/shipyard-reviewer.yml', import.meta.url), 'utf8'); + +test('the reviewer pilot uses isolated GLM high credentials and an effort-supporting pin', () => { + assert.match(workflow, /uses: dymoo\/shipyard@efec252b39c55bc3d902d14d59286660046f9776/); + assert.match(workflow, /model: z-ai\/glm-5\.3-flash/); + assert.match(workflow, /reasoning-effort: high/); + assert.match(workflow, /base-url: https:\/\/openrouter\.ai\/api\/v1/); + assert.match(workflow, /secrets\.OPENROUTER_REVIEWER_API_KEY/); + assert.doesNotMatch(workflow, /secrets\.LLM_API_KEY|vars\.LLM_MODEL|vars\.LLM_BASE_URL/); +}); + +test('the reviewer pilot retains readiness and trusted hand-off boundaries without checkout', () => { + assert.match(workflow, /vars\.OPENROUTER_REVIEWER_ENABLED == 'true'/); + assert.match(workflow, /handoff-token: \$\{\{ secrets\.SHIPYARD_HANDOFF_TOKEN \}\}/); + assert.match(workflow, /github\.event\.action == 'shipyard-review'/); + assert.match(workflow, /!startsWith\(github\.event\.pull_request\.head\.ref, 'shipyard\/issue-'\)/); + assert.doesNotMatch(workflow, /uses: actions\/checkout|run:/); +}); From 1a05ff5bb7592dacabd53e33cc6184ecdca02e95 Mon Sep 17 00:00:00 2001 From: Dylan Moore <60218243+dymoo@users.noreply.github.com> Date: Thu, 10 Sep 2026 16:28:32 +0100 Subject: [PATCH 2/2] Use DeepSeek V4.1 Flash max for review --- .github/workflows/shipyard-reviewer.yml | 4 ++-- AGENTS.md | 2 +- README.md | 5 +++-- test/cloud-coder-action.test.js | 16 ---------------- test/reviewer-workflow.test.js | 22 ---------------------- 5 files changed, 6 insertions(+), 43 deletions(-) delete mode 100644 test/reviewer-workflow.test.js diff --git a/.github/workflows/shipyard-reviewer.yml b/.github/workflows/shipyard-reviewer.yml index 0b66175..87e6550 100644 --- a/.github/workflows/shipyard-reviewer.yml +++ b/.github/workflows/shipyard-reviewer.yml @@ -35,8 +35,8 @@ jobs: with: api-key: ${{ secrets.OPENROUTER_REVIEWER_API_KEY }} base-url: https://openrouter.ai/api/v1 - model: z-ai/glm-5.3-flash - reasoning-effort: high + model: deepseek/deepseek-v4.1-flash + reasoning-effort: max handoff-token: ${{ secrets.SHIPYARD_HANDOFF_TOKEN }} instructions: | This action has no runtime dependencies. Any import that is not diff --git a/AGENTS.md b/AGENTS.md index 607366b..a998fab 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -67,7 +67,7 @@ When omitted, reasoning effort uses the provider default; an explicit validated value is sent unchanged. The released `dymoo/shipyard@v3` reference does not accept this new input; consumers must pin a supporting immutable revision until it is released. The self-review workflow pins a supporting immutable revision -and selects `z-ai/glm-5.3-flash` with explicit `high` effort. Its dedicated +and selects `deepseek/deepseek-v4.1-flash` with explicit `max` effort. Its dedicated `OPENROUTER_REVIEWER_API_KEY` is separate from the Coder's `LLM_API_KEY`; `OPENROUTER_REVIEWER_ENABLED` must be `true` only after that key and the existing hand-off secret are configured. The generic `@v3` examples remain unchanged. diff --git a/README.md b/README.md index 236d70c..4325d21 100644 --- a/README.md +++ b/README.md @@ -182,8 +182,9 @@ image and `npm test`, because that command uses only Node's built-in test runner. A consumer whose Agent Brief declares another test command must publish an image containing that toolchain and use its immutable digest. -This repository's own reviewer pilot uses `z-ai/glm-5.3-flash` through OpenRouter -with explicit `high` reasoning and a supporting immutable action revision. It +This repository's own reviewer pilot uses `deepseek/deepseek-v4.1-flash` +through OpenRouter with explicit `max` reasoning and a supporting immutable +action revision. It uses a dedicated `OPENROUTER_REVIEWER_API_KEY`, not the Coder's `LLM_API_KEY`. Keep its `OPENROUTER_REVIEWER_ENABLED` Variable absent or `false` until the new key and existing `SHIPYARD_HANDOFF_TOKEN` are configured; set it to `true` to diff --git a/test/cloud-coder-action.test.js b/test/cloud-coder-action.test.js index 21cd257..2027cc3 100644 --- a/test/cloud-coder-action.test.js +++ b/test/cloud-coder-action.test.js @@ -62,22 +62,6 @@ test('the Shipyard pilot workflow routes only ready Issues through a pinned Node assert.match(example, /github\.event\.issue\.state == 'open'/); }); -test('the Shipyard reviewer pilot targets the dedicated ARC scale set', () => { - const workflow = fs.readFileSync(new URL('../.github/workflows/shipyard-reviewer.yml', import.meta.url), 'utf8'); - assert.match(workflow, /^name: Shipyard Cloud Reviewer$/m); - assert.match(workflow, /runs-on: shipyard-runners/); - assert.match(workflow, /vars\.OPENROUTER_REVIEWER_ENABLED == 'true'/); - - const example = fs.readFileSync(new URL('../examples/workflows/shipyard-reviewer.yml', import.meta.url), 'utf8'); - assert.match(example, /vars\.LLM_BASE_URL != ''/); - assert.match(example, /vars\.LLM_MODEL != ''/); - - const readme = fs.readFileSync(new URL('../README.md', import.meta.url), 'utf8'); - assert.match(readme, /Do \*\*not\*\* create a reviewer-only workflow from this README/); - assert.match(readme, /`setup-shipyard`/); - assert.doesNotMatch(readme, /^## Add Shipyard Cloud Reviewer$/m); -}); - test('dispatches only when ready-for-agent labels an Issue', (t) => { const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'shipyard-coder-event-')); const eventPath = path.join(dir, 'event.json'); diff --git a/test/reviewer-workflow.test.js b/test/reviewer-workflow.test.js deleted file mode 100644 index e6c5942..0000000 --- a/test/reviewer-workflow.test.js +++ /dev/null @@ -1,22 +0,0 @@ -import test from 'node:test'; -import assert from 'node:assert/strict'; -import fs from 'node:fs'; - -const workflow = fs.readFileSync(new URL('../.github/workflows/shipyard-reviewer.yml', import.meta.url), 'utf8'); - -test('the reviewer pilot uses isolated GLM high credentials and an effort-supporting pin', () => { - assert.match(workflow, /uses: dymoo\/shipyard@efec252b39c55bc3d902d14d59286660046f9776/); - assert.match(workflow, /model: z-ai\/glm-5\.3-flash/); - assert.match(workflow, /reasoning-effort: high/); - assert.match(workflow, /base-url: https:\/\/openrouter\.ai\/api\/v1/); - assert.match(workflow, /secrets\.OPENROUTER_REVIEWER_API_KEY/); - assert.doesNotMatch(workflow, /secrets\.LLM_API_KEY|vars\.LLM_MODEL|vars\.LLM_BASE_URL/); -}); - -test('the reviewer pilot retains readiness and trusted hand-off boundaries without checkout', () => { - assert.match(workflow, /vars\.OPENROUTER_REVIEWER_ENABLED == 'true'/); - assert.match(workflow, /handoff-token: \$\{\{ secrets\.SHIPYARD_HANDOFF_TOKEN \}\}/); - assert.match(workflow, /github\.event\.action == 'shipyard-review'/); - assert.match(workflow, /!startsWith\(github\.event\.pull_request\.head\.ref, 'shipyard\/issue-'\)/); - assert.doesNotMatch(workflow, /uses: actions\/checkout|run:/); -});