enh(#466): refresh opus-tier model IDs to current GA (Opus 4.8 / codex gpt-5.5) (#467)

* enh: bump opus-tier model IDs to current GA (Opus 4.8 / codex gpt-5.5)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore(#466): changeset for opus-tier model-ID refresh

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: CI Rebase Check <ci@gsd-redux>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-05-29 11:52:47 -04:00
committed by GitHub
parent b8c33647d8
commit c7e5a88353
8 changed files with 48 additions and 43 deletions

View File

@@ -0,0 +1,5 @@
---
type: Changed
pr: 467
---
**Opus-tier default model IDs refreshed to current GA** — `query resolve-model` / `resolve-execution` now resolve the opus tier to `claude-opus-4-8` (claude/copilot/opencode/hermes) and `gpt-5.5` (codex), replacing the previous `claude-opus-4-7` / `gpt-5.4`. The `opus`/`sonnet`/`haiku` aliases and all sonnet/haiku + codex coding/mini tiers are unchanged.

View File

@@ -1127,7 +1127,7 @@ The intent is the same as the Claude profile tiers -- use a stronger model for p
| Value | Behavior | Use When |
|-------|----------|----------|
| `false` (default) | Returns Claude aliases (`opus`, `sonnet`, `haiku`) | Claude Code with native Anthropic API |
| `true` | Maps aliases to full Claude model IDs (`claude-opus-4-7`) | Claude Code with API that requires full IDs |
| `true` | Maps aliases to full Claude model IDs (`claude-opus-4-8`) | Claude Code with API that requires full IDs |
| `"omit"` | Returns empty string (runtime picks its default) | Non-Claude runtimes (Codex, OpenCode, Gemini CLI, Kilo) |
### Runtime-Aware Profiles (#2517)
@@ -1140,13 +1140,13 @@ When `runtime` is set, profile tiers (`opus`/`sonnet`/`haiku`) resolve to runtim
| Runtime | `opus` | `sonnet` | `haiku` | reasoning_effort |
|---------|--------|----------|---------|------------------|
| `claude` | `claude-opus-4-7` | `claude-sonnet-4-6` | `claude-haiku-4-5` | (not used) |
| `codex` | `gpt-5.4` | `gpt-5.3-codex` | `gpt-5.4-mini` | `xhigh` / `medium` / `medium` |
| `claude` | `claude-opus-4-8` | `claude-sonnet-4-6` | `claude-haiku-4-5` | (not used) |
| `codex` | `gpt-5.5` | `gpt-5.3-codex` | `gpt-5.4-mini` | `xhigh` / `medium` / `medium` |
| `gemini` | `gemini-3-pro` | `gemini-3-flash` | `gemini-2.5-flash-lite` | (not used) |
| `qwen` | `qwen3-max-2026-01-23` | `qwen3-coder-plus` | `qwen3-coder-next` | (not used) |
| `opencode` | `anthropic/claude-opus-4-7` | `anthropic/claude-sonnet-4-6` | `anthropic/claude-haiku-4-5` | (not used) |
| `copilot` | `claude-opus-4-7` | `claude-sonnet-4-6` | `claude-haiku-4-5` | (not used) |
| `hermes` | `anthropic/claude-opus-4-7` | `anthropic/claude-sonnet-4-6` | `anthropic/claude-haiku-4-5` | (not used) |
| `opencode` | `anthropic/claude-opus-4-8` | `anthropic/claude-sonnet-4-6` | `anthropic/claude-haiku-4-5` | (not used) |
| `copilot` | `claude-opus-4-8` | `claude-sonnet-4-6` | `claude-haiku-4-5` | (not used) |
| `hermes` | `anthropic/claude-opus-4-8` | `anthropic/claude-sonnet-4-6` | `anthropic/claude-haiku-4-5` | (not used) |
| Group B (`kilo`, `cline`, `cursor`, `windsurf`, `augment`, `trae`, `codebuddy`, `antigravity`) | (no built-in default — your runtime handles model selection) | | | |
**Codex example** — one config, tiered models, no large `model_overrides` block:
@@ -1158,7 +1158,7 @@ When `runtime` is set, profile tiers (`opus`/`sonnet`/`haiku`) resolve to runtim
}
```
This resolves `gsd-planner` → `gpt-5.4` (xhigh), `gsd-executor` → `gpt-5.3-codex` (medium), `gsd-codebase-mapper` → `gpt-5.4-mini` (medium). The Codex installer embeds `model = "..."` and `model_reasoning_effort = "..."` in each generated agent TOML.
This resolves `gsd-planner` → `gpt-5.5` (xhigh), `gsd-executor` → `gpt-5.3-codex` (medium), `gsd-codebase-mapper` → `gpt-5.4-mini` (medium). The Codex installer embeds `model = "..."` and `model_reasoning_effort = "..."` in each generated agent TOML.
**Claude example** — explicit opt-in resolves to full Claude IDs (no `resolve_model_ids: true` needed):

View File

@@ -8,12 +8,12 @@
},
"runtimeTierDefaults": {
"claude": {
"opus": { "model": "claude-opus-4-7" },
"opus": { "model": "claude-opus-4-8" },
"sonnet": { "model": "claude-sonnet-4-6" },
"haiku": { "model": "claude-haiku-4-5" }
},
"codex": {
"opus": { "model": "gpt-5.4", "reasoning_effort": "xhigh" },
"opus": { "model": "gpt-5.5", "reasoning_effort": "xhigh" },
"sonnet": { "model": "gpt-5.3-codex", "reasoning_effort": "medium" },
"haiku": { "model": "gpt-5.4-mini", "reasoning_effort": "medium" }
},
@@ -28,17 +28,17 @@
"haiku": { "model": "qwen3-coder-next" }
},
"opencode": {
"opus": { "model": "anthropic/claude-opus-4-7" },
"opus": { "model": "anthropic/claude-opus-4-8" },
"sonnet": { "model": "anthropic/claude-sonnet-4-6" },
"haiku": { "model": "anthropic/claude-haiku-4-5" }
},
"copilot": {
"opus": { "model": "claude-opus-4-7" },
"opus": { "model": "claude-opus-4-8" },
"sonnet": { "model": "claude-sonnet-4-6" },
"haiku": { "model": "claude-haiku-4-5" }
},
"hermes": {
"opus": { "model": "anthropic/claude-opus-4-7" },
"opus": { "model": "anthropic/claude-opus-4-8" },
"sonnet": { "model": "anthropic/claude-sonnet-4-6" },
"haiku": { "model": "anthropic/claude-haiku-4-5" }
},

View File

@@ -344,13 +344,13 @@ Built-in tier defaults by runtime:
| Runtime | `opus` | `sonnet` | `haiku` |
|------------|-------------------------------|---------------------------------|-------------------------------|
| `claude` | `claude-opus-4-7` | `claude-sonnet-4-6` | `claude-haiku-4-5` |
| `codex` | `gpt-5.4` | `gpt-5.3-codex` | `gpt-5.4-mini` |
| `claude` | `claude-opus-4-8` | `claude-sonnet-4-6` | `claude-haiku-4-5` |
| `codex` | `gpt-5.5` | `gpt-5.3-codex` | `gpt-5.4-mini` |
| `gemini` | `gemini-3-pro` | `gemini-3-flash` | `gemini-2.5-flash-lite` |
| `qwen` | `qwen3-max-2026-01-23` | `qwen3-coder-plus` | `qwen3-coder-next` |
| `opencode` | `anthropic/claude-opus-4-7` | `anthropic/claude-sonnet-4-6` | `anthropic/claude-haiku-4-5` |
| `copilot` | `claude-opus-4-7` | `claude-sonnet-4-6` | `claude-haiku-4-5` |
| `hermes` | `anthropic/claude-opus-4-7` | `anthropic/claude-sonnet-4-6` | `anthropic/claude-haiku-4-5` |
| `opencode` | `anthropic/claude-opus-4-8` | `anthropic/claude-sonnet-4-6` | `anthropic/claude-haiku-4-5` |
| `copilot` | `claude-opus-4-8` | `claude-sonnet-4-6` | `claude-haiku-4-5` |
| `hermes` | `anthropic/claude-opus-4-8` | `anthropic/claude-sonnet-4-6` | `anthropic/claude-haiku-4-5` |
| Group B (`kilo`, `cline`, `cursor`, `windsurf`, `augment`, `trae`, `codebuddy`, `antigravity`) | (no built-in default — your runtime handles model selection) | | |
Display a table to the user showing the effective configuration:

View File

@@ -1148,7 +1148,7 @@ describe('resolve-model command', () => {
assert.ok(result.success, `Command failed: ${result.error}`);
const output = JSON.parse(result.output);
assert.strictEqual(output.model, 'gpt-5.4');
assert.strictEqual(output.model, 'gpt-5.5');
assert.strictEqual(output.profile, 'balanced');
// #443: effort is now the unified field (xhigh for gsd-planner heavy tier default)
assert.strictEqual(output.effort, 'xhigh');

View File

@@ -505,9 +505,9 @@ describe('resolveModelInternal', () => {
describe('resolve_model_ids: true', () => {
// Regression test for #2712: MODEL_ALIAS_MAP must track current model releases.
test('opus alias resolves to claude-opus-4-7', () => {
test('opus alias resolves to claude-opus-4-8', () => {
writeConfig({ resolve_model_ids: true, model_profile: 'quality' });
assert.strictEqual(resolveModelInternal(tmpDir, 'gsd-planner'), 'claude-opus-4-7');
assert.strictEqual(resolveModelInternal(tmpDir, 'gsd-planner'), 'claude-opus-4-8');
});
test('sonnet alias resolves to claude-sonnet-4-6', () => {

View File

@@ -91,7 +91,7 @@ describe('issue #2517: backwards compat — no runtime key set', () => {
test('resolve_model_ids:true still maps alias -> full Claude ID with no runtime', () => {
writeConfig(tmpDir, { model_profile: 'balanced', resolve_model_ids: true });
assert.strictEqual(resolveModelInternal(tmpDir, 'gsd-planner'), 'claude-opus-4-7');
assert.strictEqual(resolveModelInternal(tmpDir, 'gsd-planner'), 'claude-opus-4-8');
});
test('resolve_model_ids:"omit" still returns "" with no runtime', () => {
@@ -145,7 +145,7 @@ describe('issue #2517: runtime "claude" is a no-op for resolution (finding #4)',
model_profile: 'quality',
resolve_model_ids: true,
});
assert.strictEqual(resolveModelInternal(tmpDir, 'gsd-planner'), 'claude-opus-4-7');
assert.strictEqual(resolveModelInternal(tmpDir, 'gsd-planner'), 'claude-opus-4-8');
});
test('effort is first-class on Claude (emits output_config.effort)', () => {
@@ -165,10 +165,10 @@ describe('issue #2517: runtime "codex" — Codex tier resolution', () => {
beforeEach(() => { isolateHome(); tmpDir = createTempProject(); _resetRuntimeWarningCacheForTests(); });
afterEach(() => { cleanup(tmpDir); restoreHome(); });
test('opus tier -> gpt-5.4 model; heavy-tier agent -> xhigh effort on codex', () => {
test('opus tier -> gpt-5.5 model; heavy-tier agent -> xhigh effort on codex', () => {
writeConfig(tmpDir, { runtime: 'codex', model_profile: 'quality' });
// gsd-planner quality -> opus -> gpt-5.4 (model unchanged)
assert.strictEqual(resolveModelInternal(tmpDir, 'gsd-planner'), 'gpt-5.4');
// gsd-planner quality -> opus -> gpt-5.5 (model unchanged)
assert.strictEqual(resolveModelInternal(tmpDir, 'gsd-planner'), 'gpt-5.5');
// gsd-planner is heavy routing tier → effort 'xhigh' → rendered model_reasoning_effort
const eff = resolveEffortInternal(tmpDir, 'gsd-planner');
const rendered = renderEffortForRuntime('codex', eff);
@@ -198,8 +198,8 @@ describe('issue #2517: runtime "codex" — Codex tier resolution', () => {
test('adaptive profile resolves on Codex (no #1713/#1806 regression)', () => {
writeConfig(tmpDir, { runtime: 'codex', model_profile: 'adaptive' });
// gsd-planner adaptive -> opus -> gpt-5.4
assert.strictEqual(resolveModelInternal(tmpDir, 'gsd-planner'), 'gpt-5.4');
// gsd-planner adaptive -> opus -> gpt-5.5
assert.strictEqual(resolveModelInternal(tmpDir, 'gsd-planner'), 'gpt-5.5');
// gsd-codebase-mapper adaptive -> haiku -> gpt-5.4-mini
assert.strictEqual(resolveModelInternal(tmpDir, 'gsd-codebase-mapper'), 'gpt-5.4-mini');
});
@@ -221,7 +221,7 @@ describe('issue #2517: runtime "codex" — Codex tier resolution', () => {
model_profile: 'quality',
resolve_model_ids: 'omit',
});
assert.strictEqual(resolveModelInternal(tmpDir, 'gsd-planner'), 'gpt-5.4');
assert.strictEqual(resolveModelInternal(tmpDir, 'gsd-planner'), 'gpt-5.5');
});
});
@@ -311,7 +311,7 @@ describe('issue #2517: field-merge of overrides with built-in defaults (finding
model_profile: 'quality',
model_profile_overrides: { codex: { opus: { reasoning_effort: 'low' } } },
});
assert.strictEqual(resolveModelInternal(tmpDir, 'gsd-planner'), 'gpt-5.4');
assert.strictEqual(resolveModelInternal(tmpDir, 'gsd-planner'), 'gpt-5.5');
const eff = resolveEffortInternal(tmpDir, 'gsd-planner');
const rendered = renderEffortForRuntime('codex', eff);
assert.strictEqual(rendered.param, 'model_reasoning_effort');
@@ -350,7 +350,7 @@ describe('issue #2517: field-merge of overrides with built-in defaults (finding
tier: 'opus',
overrides: { codex: { opus: { reasoning_effort: 'low' } } },
});
assert.deepStrictEqual(entry, { model: 'gpt-5.4', reasoning_effort: 'low' });
assert.deepStrictEqual(entry, { model: 'gpt-5.5', reasoning_effort: 'low' });
});
test('resolveTierEntry helper: unknown runtime + no overrides -> null', () => {
@@ -427,7 +427,7 @@ describe('issue #2517: unknown runtime + safe fallback', () => {
test('runtime:"codex" but missing model_profile_overrides[codex] uses spec defaults', () => {
writeConfig(tmpDir, { runtime: 'codex', model_profile: 'quality' });
// No model_profile_overrides at all — built-in Codex defaults take over
assert.strictEqual(resolveModelInternal(tmpDir, 'gsd-planner'), 'gpt-5.4');
assert.strictEqual(resolveModelInternal(tmpDir, 'gsd-planner'), 'gpt-5.5');
});
});
@@ -551,7 +551,7 @@ describe('issue #2517: install end-to-end — per-project config reaches Codex T
assert.ok(resolver, 'expected a resolver from per-project config');
assert.strictEqual(resolver.runtime, 'codex');
const entry = resolver.resolve('gsd-planner');
assert.deepStrictEqual(entry, { model: 'gpt-5.4', reasoning_effort: 'xhigh' });
assert.deepStrictEqual(entry, { model: 'gpt-5.5', reasoning_effort: 'xhigh' });
});
test('per-project config wins over global ~/.gsd/defaults.json', () => {
@@ -564,7 +564,7 @@ describe('issue #2517: install end-to-end — per-project config reaches Codex T
const resolver = readGsdRuntimeProfileResolver(tmpDir);
assert.strictEqual(resolver.runtime, 'codex');
const entry = resolver.resolve('gsd-planner');
assert.strictEqual(entry.model, 'gpt-5.4');
assert.strictEqual(entry.model, 'gpt-5.5');
});
test('generated Codex TOML embeds model = and model_reasoning_effort = lines', () => {
@@ -576,7 +576,7 @@ describe('issue #2517: install end-to-end — per-project config reaches Codex T
null,
resolver
);
assert.match(toml, /^model = "gpt-5\.4"$/m);
assert.match(toml, /^model = "gpt-5\.5"$/m);
assert.match(toml, /^model_reasoning_effort = "xhigh"$/m);
});
@@ -631,9 +631,9 @@ describe('issue #2517: RUNTIME_PROFILE_MAP single source of truth (finding #16)'
// entries through `resolveTierEntry`, so any future drift between the two
// files would surface as a test failure here rather than a silent bug.
const codexOpus = RUNTIME_PROFILE_MAP.codex?.opus;
assert.deepStrictEqual(codexOpus, { model: 'gpt-5.4', reasoning_effort: 'xhigh' });
assert.deepStrictEqual(codexOpus, { model: 'gpt-5.5', reasoning_effort: 'xhigh' });
const claudeOpus = RUNTIME_PROFILE_MAP.claude?.opus;
assert.deepStrictEqual(claudeOpus, { model: 'claude-opus-4-7' });
assert.deepStrictEqual(claudeOpus, { model: 'claude-opus-4-8' });
});
});
@@ -699,9 +699,9 @@ describe('issue #2612: runtime "opencode" — OpenCode tier resolution', () => {
beforeEach(() => { isolateHome(); tmpDir = createTempProject(); _resetRuntimeWarningCacheForTests(); });
afterEach(() => { cleanup(tmpDir); restoreHome(); });
test('opus tier -> anthropic/claude-opus-4-7', () => {
test('opus tier -> anthropic/claude-opus-4-8', () => {
writeConfig(tmpDir, { runtime: 'opencode', model_profile: 'quality' });
assert.strictEqual(resolveModelInternal(tmpDir, 'gsd-planner'), 'anthropic/claude-opus-4-7');
assert.strictEqual(resolveModelInternal(tmpDir, 'gsd-planner'), 'anthropic/claude-opus-4-8');
});
test('sonnet tier -> anthropic/claude-sonnet-4-6', () => {
@@ -727,9 +727,9 @@ describe('issue #2612: runtime "copilot" — Copilot tier resolution', () => {
beforeEach(() => { isolateHome(); tmpDir = createTempProject(); _resetRuntimeWarningCacheForTests(); });
afterEach(() => { cleanup(tmpDir); restoreHome(); });
test('opus tier -> claude-opus-4-7', () => {
test('opus tier -> claude-opus-4-8', () => {
writeConfig(tmpDir, { runtime: 'copilot', model_profile: 'quality' });
assert.strictEqual(resolveModelInternal(tmpDir, 'gsd-planner'), 'claude-opus-4-7');
assert.strictEqual(resolveModelInternal(tmpDir, 'gsd-planner'), 'claude-opus-4-8');
});
test('sonnet tier -> claude-sonnet-4-6', () => {
@@ -843,7 +843,7 @@ describe('issue #2612: partial override merge for new Group A runtimes', () => {
},
});
// gsd-planner balanced -> opus -> built-in default
assert.strictEqual(resolveModelInternal(tmpDir, 'gsd-planner'), 'anthropic/claude-opus-4-7');
assert.strictEqual(resolveModelInternal(tmpDir, 'gsd-planner'), 'anthropic/claude-opus-4-8');
// gsd-roadmapper balanced -> sonnet -> overridden
assert.strictEqual(resolveModelInternal(tmpDir, 'gsd-roadmapper'), 'anthropic/claude-sonnet-4-7');
// gsd-codebase-mapper balanced -> haiku -> built-in default (haiku not overridden)

View File

@@ -13,8 +13,8 @@ const assert = require('node:assert/strict');
const { MODEL_ALIAS_MAP } = require('../get-shit-done/bin/lib/core.cjs');
describe('MODEL_ALIAS_MAP (#1690 regression)', () => {
test('opus maps to claude-opus-4-7', () => {
assert.equal(MODEL_ALIAS_MAP.opus, 'claude-opus-4-7');
test('opus maps to claude-opus-4-8', () => {
assert.equal(MODEL_ALIAS_MAP.opus, 'claude-opus-4-8');
});
test('sonnet maps to claude-sonnet-4-6', () => {