mirror of
https://github.com/garrytan/gstack.git
synced 2026-08-23 22:42:31 +02:00
* feat: model taxonomy gains gpt-5.6-sol + per-host generation defaults
Adds 'gpt-5.6-sol' to the model taxonomy with exact-match-only resolution
(Terra/Luna/suffixed IDs deliberately fall back to generic gpt) and replaces
the hardcoded 'claude' generation default with a validated
HostConfig.defaultModel: codex renders the gpt profile when --model is
absent, every other host keeps claude. Codex ship golden regenerated
accordingly; ADDING_A_HOST documents the new field.
* feat: gpt-5.6-sol bounded-scope overlay + scope-aware resolvers
The Sol profile pins the explicit task as the lake: adjacent work is
report-only, investigation is bounded, runs terminate on one clean
verification pass, and the AskUserQuestion decision-brief format is never
trimmed. The overlay wrapper grants scope-interpretation precedence while
concrete workflow steps, gates, and skill-mandated re-verification loops
still win. Sol-specific Completeness Principle and first-run intro copy.
New SETUP_COMMAND resolver renders './setup --host <host>' for every
non-claude host so generated upgrade skills reinstall their own host.
* feat: setup reads the Codex model from config.toml
New resolve-codex-generation-model.ts reads the top-level model from
${CODEX_HOME:-~/.codex}/config.toml, validates against the model allowlist,
strips control characters from every config-derived string it surfaces,
guards against non-absolute config locations, and warns on Sol near-misses.
setup runs it on EVERY invocation (read-only TOML lookup) so a plain
./setup can never clobber a Sol user's rendered profile with the hardcoded
fallback; --model <id> overrides for one run and prints the persistence
hint. Kiro installs render the claude profile before copying (Kiro fronts
Claude-family models), rewrite the baked setup command to --host kiro, and
restore the resolved Codex profile after; the codex skills path honors
CODEX_HOME. Static pins cover the resolver wiring, fail-closed exit,
quoted argv, and the Kiro sandwich.
* feat: hermetic Codex runner hardening + Sol scope-termination E2E
The Codex E2E runner copies auth.json only (operator plugins, MCP servers,
rules, and skills no longer leak into hermetic evals), pins CODEX_HOME to
the temp dir, and supports per-run model, TOML overrides, and
--ignore-user-config. New periodic E2E installs the FULL generated
investigate skill on gpt-5.6-sol against a planted one-line bug with decoy
TODOs: the fix must land inside the boundary (untracked files counted via
git status --porcelain), decoys stay byte-identical, the regression oracle
survives unweakened, nothing gets committed, all within 30 tool calls.
The shared .agents tree is snapshotted and restored exactly in beforeAll;
fixture commits disable gpg signing. Wired into the periodic CI matrix,
paid-shard globs, eval scripts, touchfiles/E2E_TIERS
(codex-sol-scope-termination), and diff-based selection. Real-file
periodic-tier classification pins both codex E2Es out of the gate tier.
Free-tier test proves an explicit --model overrides the host default
through the real generation CLI.
* chore: bump version and changelog (v1.67.2.0)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs: post-ship documentation sync for v1.67.2.0
- README: Codex skills path is CODEX_HOME-aware; state that
--model overrides detection for one run only (persist via
the Codex config.toml model key)
- CONTRIBUTING: add the model-overlay axis to the per-host
config table (per-host defaultModel, override precedence)
- CLAUDE.md: eval results dir is ~/.gstack/projects/<slug>/evals/
(legacy fallback ~/.gstack-dev/evals/), matching eval-store.ts
and the eval:* CLI headers
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs: post-ship documentation sync (v1.67.2.0)
Sol exact-match and near-miss warning documented in README; CODEX_HOME-aware
uninstall and troubleshooting paths; hermetic auth.json-only detail and the
build-clobber gotcha in CLAUDE.md; eval-store location corrected in
ARCHITECTURE.md; defaultModel row in the ADDING_A_HOST field reference;
resolver test count corrected in the CHANGELOG entry.
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
87 lines
4.0 KiB
TypeScript
87 lines
4.0 KiB
TypeScript
import { describe, expect, test } from 'bun:test';
|
|
import { resolveModel } from '../scripts/models';
|
|
import { generateModelOverlay, readOverlay } from '../scripts/resolvers/model-overlay';
|
|
import { generateCompletenessSection } from '../scripts/resolvers/preamble/generate-completeness-section';
|
|
import { generateLakeIntro } from '../scripts/resolvers/preamble/generate-lake-intro';
|
|
import { generateSetupCommand } from '../scripts/resolvers/utility';
|
|
import type { TemplateContext } from '../scripts/resolvers/types';
|
|
|
|
function ctx(model: TemplateContext['model']): TemplateContext {
|
|
return {
|
|
skillName: 'investigate',
|
|
tmplPath: 'investigate/SKILL.md.tmpl',
|
|
host: 'codex',
|
|
paths: {
|
|
skillRoot: '$GSTACK_ROOT',
|
|
localSkillRoot: '.agents/skills/gstack',
|
|
binDir: '$GSTACK_BIN',
|
|
browseDir: '$GSTACK_BROWSE',
|
|
designDir: '$GSTACK_DESIGN',
|
|
makePdfDir: '$GSTACK_MAKE_PDF',
|
|
},
|
|
preambleTier: 3,
|
|
model,
|
|
};
|
|
}
|
|
|
|
describe('GPT-5.6 Sol model profile', () => {
|
|
test('only the exact Sol ID selects the Sol profile', () => {
|
|
expect(resolveModel('gpt-5.6-sol')).toBe('gpt-5.6-sol');
|
|
expect(resolveModel('gpt-5.6-terra')).toBe('gpt');
|
|
expect(resolveModel('gpt-5.6-luna')).toBe('gpt');
|
|
expect(resolveModel('gpt-5.6-sol-preview')).toBe('gpt');
|
|
expect(resolveModel('gpt-5.7')).toBe('gpt');
|
|
});
|
|
|
|
test('standalone overlay does not inherit generic GPT completion bias', () => {
|
|
const raw = readOverlay('gpt-5.6-sol');
|
|
expect(raw).toContain('The explicit task is the lake');
|
|
expect(raw).toContain('one clean relevant verification pass');
|
|
expect(raw).toContain('report-only');
|
|
expect(raw).not.toContain('{{INHERIT:gpt}}');
|
|
expect(raw).not.toContain('make your best judgment and proceed');
|
|
});
|
|
|
|
test('wrapper gives scope interpretation precedence but preserves concrete gates', () => {
|
|
const out = generateModelOverlay(ctx('gpt-5.6-sol'));
|
|
expect(out).toContain('disambiguate scope');
|
|
expect(out).toContain('Concrete skill workflow steps');
|
|
expect(out).toContain('Never use this patch to skip a concrete requirement');
|
|
});
|
|
|
|
test('completeness and first-run copy stay inside the explicit task boundary', () => {
|
|
const completeness = generateCompletenessSection(ctx('gpt-5.6-sol'));
|
|
const intro = generateLakeIntro(ctx('gpt-5.6-sol'));
|
|
expect(completeness).toContain("inside the user's explicit task boundary");
|
|
expect(completeness).toContain('report them, do not implement them');
|
|
expect(completeness).toContain('all relevant in-scope edge cases');
|
|
expect(intro).toContain("within the user's explicit task boundary");
|
|
expect(intro).toContain('Do not widen that boundary');
|
|
});
|
|
|
|
test('generic GPT copy remains unchanged', () => {
|
|
const generic = generateModelOverlay(ctx('gpt'));
|
|
const completeness = generateCompletenessSection(ctx('gpt'));
|
|
const intro = generateLakeIntro(ctx('gpt'));
|
|
expect(generic).toContain('make your best judgment and proceed');
|
|
expect(completeness).toContain('the complete thing is the goal');
|
|
expect(intro).toContain('do the complete thing when AI makes marginal cost near-zero');
|
|
expect(intro).not.toContain('Do not widen that boundary');
|
|
});
|
|
|
|
test('terse mode still suppresses the completeness section for Sol', () => {
|
|
// Terse short-circuits before the Sol branch — a check-order flip would
|
|
// ship Sol completeness prose to terse users (a token regression).
|
|
expect(generateCompletenessSection({ ...ctx('gpt-5.6-sol'), explainLevel: 'terse' })).toBe('');
|
|
});
|
|
});
|
|
|
|
describe('SETUP_COMMAND resolver', () => {
|
|
test('claude keeps bare ./setup; every other host reinstalls itself', () => {
|
|
expect(generateSetupCommand({ ...ctx('claude'), host: 'claude' })).toBe('./setup');
|
|
expect(generateSetupCommand({ ...ctx('gpt'), host: 'codex' })).toBe('./setup --host codex');
|
|
expect(generateSetupCommand({ ...ctx('claude'), host: 'kiro' })).toBe('./setup --host kiro');
|
|
expect(generateSetupCommand({ ...ctx('claude'), host: 'factory' })).toBe('./setup --host factory');
|
|
});
|
|
});
|