Skip to content

Commit cd7fc55

Browse files
committed
test(cl-8310): failing gpt narrate-before-tools residual tests
1 parent 0d13595 commit cd7fc55

3 files changed

Lines changed: 159 additions & 0 deletions

File tree

‎src/agent/model-family-policy.test.ts‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,4 +178,24 @@ describe("resolveModelFamilyPolicy", () => {
178178
expect(grok.family).toBe("grok");
179179
expect(grok.promptResidual).toContain("Tool budget:");
180180
});
181+
test("gpt resolves its own family on permissive default thresholds (CL-8310)", () => {
182+
const gpt = resolveModelFamilyPolicy({
183+
providerName: "codex/default",
184+
model: "gpt-5.5",
185+
});
186+
const base = resolveModelFamilyPolicy({
187+
providerName: "anthropic",
188+
model: "claude-sonnet-4",
189+
});
190+
expect(gpt.family).toBe("gpt");
191+
// No eval characterization for gpt tool-only stretches yet: ship the
192+
// permissive default, no finish-bias, no discipline rules. The
193+
// narrate-before-tools residual is prompt-level (see prompts.ts), not a
194+
// threshold.
195+
expect(gpt.toolOnlyTurnNudgeAt).toBe(base.toolOnlyTurnNudgeAt);
196+
expect(gpt.subAgentStallTimeoutMs).toBe(base.subAgentStallTimeoutMs);
197+
expect(gpt.applyGrokFinishBias).toBe(false);
198+
expect(gpt.toolDisciplineRules).toBeUndefined();
199+
expect(gpt.advertisedToolDeny).toEqual([]);
200+
});
181201
});

‎src/agent/prompts.test.ts‎

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { describe, expect, it } from "bun:test";
22
import {
33
buildChatSystemPrompt,
44
buildClaudeTaskGuidanceNote,
5+
buildGptNarrateBeforeToolsNote,
56
buildGrokLeafAntiThrashNote,
67
buildGuidelines,
78
buildPromptDisciplineBlock,
@@ -372,6 +373,7 @@ describe("grok finish-bias residual gating (extends existing provider-family tes
372373
});
373374
});
374375

376+
<<<<<<< HEAD
375377
describe("promptResidual assembly (CL-8297)", () => {
376378
const TOOL_BUDGET =
377379
"Tool budget:\n" +
@@ -452,3 +454,79 @@ describe("claude XML task_guidance residual (provider residual, not a prompt for
452454
);
453455
});
454456
});
457+
458+
describe("gpt narrate-before-tools residual (CL-8310)", () => {
459+
it("is a 3-line narrate-before-tools note, not manage_tasks ceremony", () => {
460+
const note = buildGptNarrateBeforeToolsNote();
461+
expect(note).toContain("Narrate before tools (GPT worker):");
462+
expect(note).toMatch(/before.*tool call.*one short line/is);
463+
expect(note).toMatch(/no narration between them/i);
464+
expect(note).toContain("write the report envelope");
465+
expect(note.toLowerCase()).not.toContain("manage_tasks");
466+
});
467+
468+
it("appears exactly once on a gpt leaf prompt", () => {
469+
const prompt = buildSubAgentSystemPrompt(undefined, undefined, undefined, {
470+
orchestrator: false,
471+
gptNarrateBeforeTools: true,
472+
});
473+
const note = buildGptNarrateBeforeToolsNote();
474+
expect(countOccurrences(prompt, note)).toBe(1);
475+
expect(prompt.trimEnd().endsWith(note)).toBe(true);
476+
});
477+
478+
it("appears exactly once on a gpt primary prompt", () => {
479+
const prompt = buildChatSystemPrompt(
480+
undefined,
481+
undefined,
482+
undefined,
483+
[],
484+
"orchestrator",
485+
undefined,
486+
undefined,
487+
{ gptNarrateBeforeTools: true },
488+
);
489+
const note = buildGptNarrateBeforeToolsNote();
490+
expect(countOccurrences(prompt, note)).toBe(1);
491+
});
492+
493+
it("is absent by default on both primary and leaf", () => {
494+
const leaf = buildSubAgentSystemPrompt(undefined, undefined, undefined, {
495+
orchestrator: false,
496+
grokAntiThrash: false,
497+
});
498+
const primary = buildChatSystemPrompt(
499+
undefined,
500+
undefined,
501+
undefined,
502+
[],
503+
"orchestrator",
504+
);
505+
expect(leaf).not.toContain("Narrate before tools (GPT worker):");
506+
expect(primary).not.toContain("Narrate before tools (GPT worker):");
507+
});
508+
509+
it("is absent on grok and claude prompts", () => {
510+
const grokLeaf = buildSubAgentSystemPrompt(undefined, undefined, undefined, {
511+
orchestrator: false,
512+
grokAntiThrash: true,
513+
});
514+
const claudeLeaf = buildSubAgentSystemPrompt(undefined, undefined, undefined, {
515+
orchestrator: false,
516+
grokAntiThrash: false,
517+
});
518+
const claudePrimary = buildChatSystemPrompt(
519+
undefined,
520+
undefined,
521+
undefined,
522+
[],
523+
"orchestrator",
524+
);
525+
for (const prompt of [grokLeaf, claudeLeaf, claudePrimary]) {
526+
expect(prompt).not.toContain("Narrate before tools (GPT worker):");
527+
expect(prompt).not.toContain("Narrate before tools (GPT");
528+
}
529+
// The grok row keeps its own residual, untouched.
530+
expect(grokLeaf).toContain("Finish bias (xAI / Grok worker):");
531+
});
532+
});

‎src/subagent/provider-family.test.ts‎

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ import { describe, expect, test } from "bun:test";
22
import {
33
detectModelFamily,
44
isClaudeLeafProvider,
5+
isGptProvider,
56
isKimiLeafProvider,
67
isXaiGrokLeafProvider,
78
shouldApplyGrokAntiThrash,
89
} from "./provider-family.js";
10+
import { CODEX_DEFAULT_MODELS } from "../auth/codex/constants.js";
911

1012
describe("isXaiGrokLeafProvider", () => {
1113
test("matches xai/ OAuth provider names", () => {
@@ -194,3 +196,62 @@ describe("detectModelFamily claude row", () => {
194196
).toBe("claude");
195197
});
196198
});
199+
200+
describe("isGptProvider (CL-8310)", () => {
201+
test("matches codex OAuth provider names", () => {
202+
expect(isGptProvider({ providerName: "codex/default" })).toBe(true);
203+
expect(isGptProvider({ providerName: "codex/work" })).toBe(true);
204+
});
205+
206+
test("matches codex adapter ids and bare codex names", () => {
207+
expect(isGptProvider({ providerName: "codex-responses" })).toBe(true);
208+
expect(isGptProvider({ providerName: "codex" })).toBe(true);
209+
});
210+
211+
test("matches gpt-* model ids on any provider", () => {
212+
expect(
213+
isGptProvider({ providerName: "openai", model: "gpt-5.5" }),
214+
).toBe(true);
215+
expect(
216+
isGptProvider({ providerName: "opencode-go", model: "gpt-5.1" }),
217+
).toBe(true);
218+
expect(
219+
isGptProvider({ providerName: "openai-compat", model: "gpt-5.6-luna" }),
220+
).toBe(true);
221+
});
222+
223+
test("covers every in-tree codex catalog model without naming cells", () => {
224+
// No terra/sol/astra special-casing: every served codex id resolves via
225+
// the generic codex-provider / gpt-* match, so future cells ride along.
226+
for (const model of CODEX_DEFAULT_MODELS) {
227+
expect(isGptProvider({ providerName: "codex/default", model })).toBe(
228+
true,
229+
);
230+
expect(detectModelFamily({ providerName: "codex/default", model })).toBe(
231+
"gpt",
232+
);
233+
}
234+
});
235+
236+
test("rejects grok, kimi, muse, and claude", () => {
237+
expect(
238+
isGptProvider({ providerName: "xai/default", model: "grok-4.6" }),
239+
).toBe(false);
240+
expect(isGptProvider({ providerName: "moonshot", model: "kimi-k2" })).toBe(
241+
false,
242+
);
243+
expect(
244+
isGptProvider({
245+
providerName: "opencode-go",
246+
model: "muse-spark-1.3-contributor",
247+
}),
248+
).toBe(false);
249+
expect(
250+
isGptProvider({
251+
providerName: "anthropic",
252+
model: "claude-sonnet-4",
253+
}),
254+
).toBe(false);
255+
expect(isGptProvider({ providerName: "anthropic" })).toBe(false);
256+
});
257+
});

0 commit comments

Comments
 (0)