Skip to content

Commit e3a769d

Browse files
committed
test(prompt-variance): red — failing tests for family rows, assemble, per-row sizes
CL-8269: versioned prompt-variance package (default/muse/grok rows, assemble, per-model overrides, per-row sizes) plus three-family director prompt-size coverage. All fail until the package lands.
1 parent 9961dbb commit e3a769d

5 files changed

Lines changed: 231 additions & 3 deletions

File tree

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { describe, expect, test } from "bun:test";
2+
import { assemble } from "./assemble.js";
3+
import { defaultRow, grokRow, museRow } from "./rows.js";
4+
5+
// CL-8269 RED: assemble does not exist yet — every test below fails until
6+
// the GREEN lands packages/prompt-variance.
7+
8+
const SECTIONS = ["contract", "Tools (names only): read_file", "context"];
9+
const TOOLS = ["read_file", "skill_search", "use_skill", "run_shell"];
10+
11+
describe("assemble", () => {
12+
test("joins sections with the residual as the last section", () => {
13+
const out = assemble(SECTIONS, grokRow, TOOLS);
14+
expect(out.systemPrompt).toContain("contract");
15+
expect(out.systemPrompt.trimEnd().endsWith(grokRow.residual)).toBe(true);
16+
});
17+
18+
test("a row without residual leaves the sections unchanged", () => {
19+
const out = assemble(SECTIONS, defaultRow, TOOLS);
20+
expect(out.systemPrompt).toBe(SECTIONS.join("\n\n"));
21+
});
22+
23+
test("muse residual closes the prompt like the shipped tail append", () => {
24+
const out = assemble(SECTIONS, museRow, TOOLS);
25+
expect(out.systemPrompt.trimEnd().endsWith(museRow.residual)).toBe(true);
26+
});
27+
28+
test("filters advertisedToolDeny from the tool names, preserving order", () => {
29+
const out = assemble(SECTIONS, grokRow, TOOLS);
30+
expect([...out.toolNames]).toEqual(["read_file", "use_skill", "run_shell"]);
31+
});
32+
33+
test("variance cannot grant tools: output is always a subset of the input", () => {
34+
for (const row of [defaultRow, museRow, grokRow]) {
35+
const out = assemble(SECTIONS, row, TOOLS);
36+
for (const name of out.toolNames) {
37+
expect(TOOLS).toContain(name);
38+
}
39+
expect(out.toolNames.length).toBeLessThanOrEqual(TOOLS.length);
40+
}
41+
});
42+
43+
test("variance cannot grant tools: unknown mounted names pass through untouched", () => {
44+
const out = assemble(SECTIONS, defaultRow, ["alpha", "beta"]);
45+
expect([...out.toolNames]).toEqual(["alpha", "beta"]);
46+
});
47+
48+
test("refuses a row that denies use_skill", () => {
49+
const bad = { ...grokRow, advertisedToolDeny: ["use_skill"] };
50+
expect(() => assemble(SECTIONS, bad, TOOLS)).toThrow(/use_skill/);
51+
});
52+
});
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { describe, expect, test } from "bun:test";
2+
import { applyRowOverride, resolvePromptVariance } from "./resolve.js";
3+
import { grokRow } from "./rows.js";
4+
5+
// CL-8269 RED: the resolver does not exist yet — every test below fails
6+
// until the GREEN lands packages/prompt-variance.
7+
8+
describe("resolvePromptVariance", () => {
9+
test("resolves each family to its row", () => {
10+
expect(resolvePromptVariance({ family: "default" }).id).toBe("default");
11+
expect(resolvePromptVariance({ family: "muse" }).id).toBe("muse");
12+
expect(resolvePromptVariance({ family: "grok" }).id).toBe("grok");
13+
});
14+
15+
test("grok keeps its finish-bias residual and skill_search deny on leaves", () => {
16+
const row = resolvePromptVariance({ family: "grok", orchestrator: false });
17+
expect(row.residual).toBe(grokRow.residual);
18+
expect(row.advertisedToolDeny).toContain("skill_search");
19+
});
20+
21+
test("grok on orchestrators falls back to the default shape", () => {
22+
const row = resolvePromptVariance({ family: "grok", orchestrator: true });
23+
expect(row.residual).toBe("");
24+
expect([...row.advertisedToolDeny]).toEqual([]);
25+
});
26+
27+
test("muse keeps its residual on leaves and orchestrators alike", () => {
28+
expect(
29+
resolvePromptVariance({ family: "muse", orchestrator: false }).residual,
30+
).not.toBe("");
31+
expect(
32+
resolvePromptVariance({ family: "muse", orchestrator: true }).residual,
33+
).not.toBe("");
34+
});
35+
36+
test("applies per-model-id overrides from the row", () => {
37+
const row = applyRowOverride(
38+
{
39+
...grokRow,
40+
overrides: {
41+
"grok-4-special": { advertisedToolDeny: [] },
42+
},
43+
},
44+
"GROK-4-SPECIAL",
45+
);
46+
expect([...row.advertisedToolDeny]).toEqual([]);
47+
expect(row.residual).toBe(grokRow.residual);
48+
});
49+
50+
test("an unknown model id resolves to the base row", () => {
51+
const row = resolvePromptVariance({
52+
family: "grok",
53+
orchestrator: false,
54+
model: "grok-9-unknown",
55+
});
56+
expect(row.residual).toBe(grokRow.residual);
57+
expect([...row.advertisedToolDeny]).toEqual(["skill_search"]);
58+
});
59+
});
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { describe, expect, test } from "bun:test";
2+
import {
3+
defaultRow,
4+
FAMILY_IDS,
5+
grokRow,
6+
museRow,
7+
type PromptVarianceFamily,
8+
} from "./rows.js";
9+
10+
// CL-8269 RED: the versioned prompt-variance package does not exist yet, so
11+
// every test below fails at import time until the GREEN lands it.
12+
13+
describe("prompt-variance family rows", () => {
14+
test("ships exactly the default/muse/grok families", () => {
15+
expect([...FAMILY_IDS]).toEqual(["default", "muse", "grok"]);
16+
});
17+
18+
test("has no glm/claude/gpt rows until their evals land (CL-8265/7772/7775)", () => {
19+
for (const id of FAMILY_IDS) {
20+
expect(["glm", "claude", "gpt"]).not.toContain(id);
21+
}
22+
});
23+
24+
test("every row carries the render/residual/deny/omit shape", () => {
25+
for (const row of [defaultRow, museRow, grokRow]) {
26+
expect(row.render).toBe("tail");
27+
expect(typeof row.residual).toBe("string");
28+
expect(Array.isArray([...row.advertisedToolDeny])).toBe(true);
29+
expect(Array.isArray([...row.sectionOmit])).toBe(true);
30+
}
31+
});
32+
33+
test("row ids match their family", () => {
34+
const ids: PromptVarianceFamily[] = [
35+
defaultRow.id,
36+
museRow.id,
37+
grokRow.id,
38+
];
39+
expect(ids).toEqual(["default", "muse", "grok"]);
40+
});
41+
42+
test("muse row is the shipped CL-7869 tool-discipline text", () => {
43+
expect(museRow.residual).toContain("Tool discipline:");
44+
expect(museRow.residual).toContain("Batch independent tool calls");
45+
expect(museRow.residual).toContain("Never re-read a file");
46+
expect(museRow.residual).toContain("Do not narrate; act.");
47+
});
48+
49+
test("grok row is the existing finish-bias residual", () => {
50+
expect(grokRow.residual).toContain("Finish bias (xAI / Grok worker):");
51+
expect(grokRow.residual).toContain("prefer the structured report");
52+
expect(grokRow.residual).toContain("re-open paths you already read");
53+
expect(grokRow.residual).toContain("done-definition is met");
54+
expect(grokRow.residual).toContain("never run_shell");
55+
});
56+
57+
test("default row carries no residual", () => {
58+
expect(defaultRow.residual).toBe("");
59+
});
60+
61+
test("grok leaves deny skill_search only; other rows deny nothing", () => {
62+
expect([...grokRow.advertisedToolDeny]).toEqual(["skill_search"]);
63+
expect([...defaultRow.advertisedToolDeny]).toEqual([]);
64+
expect([...museRow.advertisedToolDeny]).toEqual([]);
65+
});
66+
67+
test("no row denies use_skill — brief-named skills always load directly", () => {
68+
for (const row of [defaultRow, museRow, grokRow]) {
69+
expect(row.advertisedToolDeny).not.toContain("use_skill");
70+
}
71+
});
72+
73+
test("sectionOmit starts empty on every row", () => {
74+
for (const row of [defaultRow, museRow, grokRow]) {
75+
expect([...row.sectionOmit]).toEqual([]);
76+
}
77+
});
78+
});
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { describe, expect, test } from "bun:test";
2+
import { defaultRow, grokRow, museRow } from "./rows.js";
3+
4+
// CL-8269 RED: per-family-row prompt sizes. Residuals are tail appends, so
5+
// each must stay a small fraction of a worker prompt: non-empty for the
6+
// tuned families, empty for default, and bounded well under a kilobyte each.
7+
8+
describe("prompt-variance prompt sizes per family row", () => {
9+
test("default residual is empty", () => {
10+
expect(defaultRow.residual.length).toBe(0);
11+
});
12+
13+
test("every tuned residual stays under 2000 chars / 3000 bytes", () => {
14+
for (const row of [museRow, grokRow]) {
15+
expect(row.residual.length).toBeGreaterThan(0);
16+
expect(row.residual.length).toBeLessThan(2000);
17+
expect(Buffer.byteLength(row.residual, "utf8")).toBeLessThan(3000);
18+
}
19+
});
20+
21+
test("the grok finish-bias residual is larger than the muse discipline rules", () => {
22+
expect(grokRow.residual.length).toBeGreaterThan(museRow.residual.length);
23+
});
24+
});

‎src/agent/prompt-sizes.test.ts‎

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,10 @@ function budgetMessage(
127127
describe("director prompt size budget", () => {
128128
const rows = directorPromptSizeTable();
129129

130-
test("covers every director in both families", () => {
131-
expect(rows.length).toBe(DIRECTOR_IDS.length * 2);
130+
test("covers every director in all three variance families", () => {
131+
expect(rows.length).toBe(DIRECTOR_IDS.length * 3);
132132
for (const directorId of DIRECTOR_IDS) {
133-
for (const family of ["default", "grok"] as const) {
133+
for (const family of ["default", "muse", "grok"] as const) {
134134
expect(
135135
rows.some((r) => r.directorId === directorId && r.family === family),
136136
).toBe(true);
@@ -177,6 +177,21 @@ describe("director prompt size budget", () => {
177177
}
178178
});
179179

180+
test("muse family appends the shipped tool-discipline rules", () => {
181+
for (const directorId of DIRECTOR_IDS) {
182+
const base = rows.find(
183+
(r) => r.directorId === directorId && r.family === "default",
184+
);
185+
const muse = rows.find(
186+
(r) => r.directorId === directorId && r.family === "muse",
187+
);
188+
expect(muse?.chars ?? 0).toBeGreaterThan(base?.chars ?? 0);
189+
expect(assembleDirectorPrompt(directorId, "muse")).toContain(
190+
"Tool discipline:",
191+
);
192+
}
193+
});
194+
180195
test("measurement is deterministic", () => {
181196
const again = directorPromptSizeTable();
182197
expect(again.map((r) => r.chars)).toEqual(rows.map((r) => r.chars));

0 commit comments

Comments
 (0)