diff --git a/.github/workflows/shipyard-reviewer.yml b/.github/workflows/shipyard-reviewer.yml index ddb4bbc..87e6550 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: 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 0172fb6..a998fab 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 `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. +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..4325d21 100644 --- a/README.md +++ b/README.md @@ -182,6 +182,20 @@ 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 `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 +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..2027cc3 100644 --- a/test/cloud-coder-action.test.js +++ b/test/cloud-coder-action.test.js @@ -62,23 +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\.LLM_BASE_URL != ''/); - assert.match(workflow, /vars\.LLM_MODEL != ''/); - - 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');