feat(validators): add fable to BASE_ALLOWED_MODELS with accept/reject coverage
This commit is contained in:
parent
937482067d
commit
357e17b176
2 changed files with 74 additions and 2 deletions
|
|
@ -21,7 +21,7 @@
|
|||
// PROFILE_READ_ERROR — file unreadable or parse-error
|
||||
// PROFILE_NOT_FOUND — file does not exist
|
||||
//
|
||||
// Allowed model values: ['sonnet', 'opus']. Haiku is allowed only when
|
||||
// Allowed model values: ['sonnet', 'opus', 'fable']. Haiku is allowed only when
|
||||
// VOYAGE_ALLOW_HAIKU=1 (per global CLAUDE.md modellvalg-prinsipp: Haiku skal
|
||||
// ikke brukes som default; eksplisitt opt-in for spesielle bruksmønstre).
|
||||
|
||||
|
|
@ -42,7 +42,7 @@ export const PROFILE_REQUIRED_PHASES = Object.freeze([
|
|||
'brief', 'research', 'plan', 'execute', 'review', 'continue',
|
||||
]);
|
||||
|
||||
export const BASE_ALLOWED_MODELS = Object.freeze(['sonnet', 'opus']);
|
||||
export const BASE_ALLOWED_MODELS = Object.freeze(['sonnet', 'opus', 'fable']);
|
||||
|
||||
function getAllowedModels(env = process.env) {
|
||||
if (env.VOYAGE_ALLOW_HAIKU === '1') {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import {
|
|||
validateProfileContent,
|
||||
PROFILE_REQUIRED_FIELDS,
|
||||
PROFILE_REQUIRED_PHASES,
|
||||
BASE_ALLOWED_MODELS,
|
||||
} from '../../lib/validators/profile-validator.mjs';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
|
@ -93,6 +94,77 @@ brief_reviewer_iter_cap: 1
|
|||
`expected valid with VOYAGE_ALLOW_HAIKU=1, got: ${JSON.stringify(allowed.errors)}`);
|
||||
});
|
||||
|
||||
// Fable tier (v5.9): fable accepted under DEFAULT env (no opt-in flag),
|
||||
// unknown models still rejected — the allowlist gate must demonstrably fire.
|
||||
|
||||
test('fable accepted in phase_models under default env (no env flag)', () => {
|
||||
const fableProfile = `---
|
||||
profile_version: "1.0"
|
||||
name: fable-inline
|
||||
phase_models:
|
||||
- phase: brief
|
||||
model: fable
|
||||
- phase: research
|
||||
model: fable
|
||||
- phase: plan
|
||||
model: fable
|
||||
- phase: execute
|
||||
model: fable
|
||||
- phase: review
|
||||
model: fable
|
||||
- phase: continue
|
||||
model: fable
|
||||
parallel_agents_min: 6
|
||||
parallel_agents_max: 8
|
||||
external_research_enabled: true
|
||||
brief_reviewer_iter_cap: 3
|
||||
---
|
||||
`;
|
||||
const r = validateProfileContent(fableProfile, { env: { /* default: no flags */ } });
|
||||
assert.equal(r.valid, true,
|
||||
`expected fable accepted under default env, got: ${JSON.stringify(r.errors)}`);
|
||||
assert.equal(r.errors.length, 0);
|
||||
});
|
||||
|
||||
test('unknown model gpt5 rejected with PROFILE_INVALID_MODEL under default env', () => {
|
||||
const gpt5Profile = `---
|
||||
profile_version: "1.0"
|
||||
name: gpt5-inline
|
||||
phase_models:
|
||||
- phase: brief
|
||||
model: gpt5
|
||||
- phase: research
|
||||
model: sonnet
|
||||
- phase: plan
|
||||
model: opus
|
||||
- phase: execute
|
||||
model: sonnet
|
||||
- phase: review
|
||||
model: opus
|
||||
- phase: continue
|
||||
model: sonnet
|
||||
parallel_agents_min: 2
|
||||
parallel_agents_max: 4
|
||||
external_research_enabled: false
|
||||
brief_reviewer_iter_cap: 1
|
||||
---
|
||||
`;
|
||||
const r = validateProfileContent(gpt5Profile, { env: { /* default: no flags */ } });
|
||||
assert.equal(r.valid, false);
|
||||
const found = r.errors.find(e => e.code === 'PROFILE_INVALID_MODEL' && /gpt5/.test(e.message));
|
||||
assert.ok(found, `expected PROFILE_INVALID_MODEL for gpt5, got: ${JSON.stringify(r.errors)}`);
|
||||
});
|
||||
|
||||
// BASE_ALLOWED_MODELS allowlist drift-pin (mirrors the PROFILE_REQUIRED_FIELDS pin)
|
||||
|
||||
test('BASE_ALLOWED_MODELS export drift-pin', () => {
|
||||
assert.deepEqual(
|
||||
[...BASE_ALLOWED_MODELS],
|
||||
['sonnet', 'opus', 'fable'],
|
||||
'BASE_ALLOWED_MODELS contract drift — pin contract',
|
||||
);
|
||||
});
|
||||
|
||||
// Required fields presence
|
||||
|
||||
test('PROFILE_MISSING_FIELD when name absent', () => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue