From 4aa28ccac5980d57759b50d83785dbd39d858263 Mon Sep 17 00:00:00 2001 From: ethereal49 Date: Sun, 11 Oct 2026 20:11:55 -1000 Subject: [PATCH] fix(commands): honor Codex model configuration in Santa Loop (cherry picked from commit 2109d97b4005580f43b8d9dda92a126f8899d493) --- commands/santa-loop.md | 4 +- docs/ja-JP/commands/santa-loop.md | 5 +- docs/zh-CN/commands/santa-loop.md | 4 +- tests/commands/santa-loop.test.js | 85 +++++++++++++++++++++++++++++++ 4 files changed, 95 insertions(+), 3 deletions(-) create mode 100644 tests/commands/santa-loop.test.js diff --git a/commands/santa-loop.md b/commands/santa-loop.md index 2ea1ae896..e5813993c 100644 --- a/commands/santa-loop.md +++ b/commands/santa-loop.md @@ -72,6 +72,8 @@ Launch an Agent (subagent_type: `code-reviewer`, model: `opus`) with the full ru #### Reviewer B: External Model (Claude fallback if no external reviewer is ready) +Codex uses its configured model or CLI default. Set `model` in your Codex configuration to choose a reviewer model supported by your account. + Antigravity is optional and requires the external `ccg-workflow` runtime; it is not included in the base ECC install. An existing installation must provide both an executable `~/.claude/bin/codeagent-wrapper` and a readable regular reviewer role file at `~/.claude/.ccg/prompts/antigravity/reviewer.md`. Otherwise, retain the Claude fallback. Use the wrapper's maintained model-selection contract; do not add an Antigravity model pin. Run detection, prompt creation, and the selected external review together in this single Bash subshell. Replace the prompt placeholder with the same rubric, file contents, and review-only instructions given to Reviewer A. The order remains Codex, Gemini, Antigravity, then Claude. The Claude fallback creates no prompt file. @@ -116,7 +118,7 @@ EOF case "$REVIEWER_BACKEND" in codex) - codex exec --sandbox read-only -m gpt-5.4 -C "$(pwd)" - < "$PROMPT_FILE" + codex exec --sandbox read-only -C "$(pwd)" - < "$PROMPT_FILE" ;; gemini) REVIEWER_PROMPT=$(cat "$PROMPT_FILE") diff --git a/docs/ja-JP/commands/santa-loop.md b/docs/ja-JP/commands/santa-loop.md index bd024a75c..f875e1dad 100644 --- a/docs/ja-JP/commands/santa-loop.md +++ b/docs/ja-JP/commands/santa-loop.md @@ -89,8 +89,11 @@ EOF 最初に利用可能なCLIを使用します: **Codex CLI**(インストールされている場合) + +Codexは設定されたモデル、またはCLIのデフォルトを使用します。アカウントで利用できるレビュアーモデルを選ぶには、Codex設定の `model` を指定してください。 + ```bash -codex exec --sandbox read-only -m gpt-5.4 -C "$(pwd)" - < "$PROMPT_FILE" +codex exec --sandbox read-only -C "$(pwd)" - < "$PROMPT_FILE" rm -f "$PROMPT_FILE" ``` diff --git a/docs/zh-CN/commands/santa-loop.md b/docs/zh-CN/commands/santa-loop.md index adafef45d..ce7262f2a 100644 --- a/docs/zh-CN/commands/santa-loop.md +++ b/docs/zh-CN/commands/santa-loop.md @@ -93,8 +93,10 @@ EOF **Codex CLI**(如果已安装) +Codex 使用已配置的模型或 CLI 默认值。在 Codex 配置中设置 `model`,即可选择账号支持的评审模型。 + ```bash -codex exec --sandbox read-only -m gpt-5.4 -C "$(pwd)" - < "$PROMPT_FILE" +codex exec --sandbox read-only -C "$(pwd)" - < "$PROMPT_FILE" rm -f "$PROMPT_FILE" ``` diff --git a/tests/commands/santa-loop.test.js b/tests/commands/santa-loop.test.js new file mode 100644 index 000000000..1a56db5da --- /dev/null +++ b/tests/commands/santa-loop.test.js @@ -0,0 +1,85 @@ +'use strict'; + +const assert = require('assert'); +const fs = require('fs'); +const os = require('os'); +const path = require('path'); +const { spawnSync } = require('child_process'); + +const repoRoot = path.resolve(__dirname, '..', '..'); +const commands = [ + 'commands/santa-loop.md', + 'docs/ja-JP/commands/santa-loop.md', + 'docs/zh-CN/commands/santa-loop.md', +]; + +if (process.platform === 'win32') { + console.log('Skipped: Santa Loop examples require Bash'); + process.exit(0); +} + +let passed = 0; +let failed = 0; + +function test(name, fn) { + try { + fn(); + console.log(` ✓ ${name}`); + passed++; + } catch (error) { + console.log(` ✗ ${name}: ${error.message}`); + failed++; + } +} + +function runCodexExample(commandPath, configuredModel) { + const source = fs.readFileSync(path.join(repoRoot, commandPath), 'utf8'); + const invocation = source.match(/^\s*codex exec[^\n]+/m); + assert.ok(invocation, 'Expected a Codex reviewer invocation'); + const tempRoot = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'ecc-santa-model-'))); + try { + const promptFile = path.join(tempRoot, 'prompt.md'); + fs.writeFileSync(promptFile, 'Independent review only.'); + fs.writeFileSync(path.join(tempRoot, 'codex'), `#!${process.execPath} +const fs = require('fs'); +const args = process.argv.slice(2); +const modelFlag = args.findIndex(arg => arg === '-m' || arg === '--model'); +const model = modelFlag < 0 ? process.env.ECC_TEST_CONFIGURED_MODEL : args[modelFlag + 1]; +if (model !== process.env.ECC_TEST_CONFIGURED_MODEL) { + console.error('400: model is not supported by this account'); + process.exit(1); +} +console.log(JSON.stringify({ args, model, prompt: fs.readFileSync(0, 'utf8') })); +`, { mode: 0o755 }); + const result = spawnSync('bash', ['-c', invocation[0]], { + cwd: tempRoot, + env: { ...process.env, PATH: `${tempRoot}${path.delimiter}${process.env.PATH}`, + PROMPT_FILE: promptFile, ECC_TEST_CONFIGURED_MODEL: configuredModel }, + encoding: 'utf8', timeout: 10000, + }); + assert.strictEqual(result.status, 0, result.stderr); + return { ...JSON.parse(result.stdout), cwd: tempRoot }; + } finally { + fs.rmSync(tempRoot, { recursive: true, force: true }); + } +} + +console.log('\n=== Testing Santa Loop Codex model selection (stub CLI) ===\n'); + +for (const commandPath of commands) { + for (const model of ['account-default', 'configured-review-model']) { + test(`${commandPath} respects ${model} and preserves review inputs`, () => { + const result = runCodexExample(commandPath, model); + assert.strictEqual(result.model, model); + assert.strictEqual(result.args[0], 'exec'); + assert.strictEqual(result.args[result.args.indexOf('--sandbox') + 1], 'read-only'); + assert.strictEqual(result.args[result.args.indexOf('-C') + 1], result.cwd); + assert.strictEqual(result.args.at(-1), '-'); + assert.strictEqual(result.prompt, 'Independent review only.'); + }); + } +} + +console.log(`\nPassed: ${passed}`); +console.log(`Failed: ${failed}`); +process.exit(failed > 0 ? 1 : 0);