Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 50 additions & 66 deletions src/agent/directors/builder/package.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,63 +9,72 @@ describe("builderPackage", () => {
expect(p).not.toMatch(/build director/i);
});

test("systemPrompt teaches implement-and-test from the implement skill", () => {
test("systemPrompt is a short Corbits implement card, not the GaaS implement skill", () => {
const p = builderPackage.systemPrompt;
expect(p).toContain("Implement and Test");
expect(p).toContain("Ship the brief");
expect(p).toContain("success_criteria");
expect(p).toMatch(/bug fixes \(test-first\)/i);
expect(p).toMatch(/reproduces the bug/i);
expect(p).toMatch(/verify it \*\*fails\*\*/);
expect(p).toMatch(/For new features/i);
expect(p).toMatch(/assert.*expected behavior|works as designed/i);
expect(p).toMatch(/test-first/i);
expect(p).toMatch(/assert expected behavior/i);
expect(p).toContain("Blockers");
expect(p).not.toContain("## Implement and Test");
expect(p).not.toContain("## Prerequisites");
expect(p).not.toContain("## Build Gate");
expect(p).not.toContain("Greybeard Review");
expect(p).not.toContain("TaskCreate");
expect(p).not.toMatch(/Use the @greybeard subagent/i);
});

test("systemPrompt teaches Build Gate and does not shortcut verify", () => {
test("systemPrompt ships tests with the change and runs the repo gate", () => {
const p = builderPackage.systemPrompt;
expect(p).toContain("Build Gate");
expect(p).toMatch(/bun run check/);
expect(p).toMatch(/Don't shortcut verify/i);
expect(p).toMatch(/Do not shortcut verify/i);
expect(p).toMatch(/partial gates/i);
expect(p).toMatch(/pre-existing/i);
expect(p).toMatch(
/defined typecheck command.*relevant tests.*defined full check/is,
);
expect(p).toMatch(
/repository defines no typecheck command.*explicit Blocker/is,
expect(p).toMatch(/do not invent one/i);
expect(p).toMatch(/exact verification command/i);
expect(p).toMatch(/exit status/);
expect(p).toMatch(/bare "pass" without command evidence/i);
expect(p).toMatch(/same commit when committing/);
});

test("systemPrompt has no philosophy boot", () => {
const p = builderPackage.systemPrompt;
expect(p).not.toMatch(/Before substantial repo work/i);
expect(p).not.toMatch(
/follow style, philosophy, native-runtime, idiot-proof, and Ponytail/i,
);
expect(p).toMatch(/evidence.*AGENTS.*package scripts/is);
expect(p).toMatch(/do not invent.*typecheck command/i);
expect(p).toMatch(/exact verification command.*outcome.*exit status/is);
expect(p).toMatch(/bare .*pass.*incomplete report/is);
expect(p).toMatch(/never silently skip/i);
expect(p).not.toMatch(/load each with skill_search \+ use_skill/i);
expect(p).not.toMatch(/use_skill is not mounted/i);
});

test("systemPrompt does not inline family residuals", () => {
const p = builderPackage.systemPrompt;
expect(p).not.toContain("Finish bias (xAI / Grok worker):");
expect(p).not.toContain("Tool budget:");
expect(p).not.toContain("<task_guidance>");
expect(p).not.toContain("Narrate before tools (GPT worker):");
expect(p).not.toContain("Tool discipline:");
});

test("systemPrompt stays a short card (no 53k harness blob)", () => {
const p = builderPackage.systemPrompt;
expect(p.length).toBeLessThan(4000);
expect(p).not.toMatch(/parameters?:/i);
expect(p).not.toMatch(/fan-out/i);
expect(p).not.toMatch(/at most \d+/i);
expect(p).not.toMatch(/turn budget/i);
expect(p).not.toMatch(/scheduler/i);
});

test("systemPrompt requires a counsel / /plan plan for substantial work", () => {
const p = builderPackage.systemPrompt;
expect(p).toContain("## Plan");
expect(p).toContain("counsel / `/plan` plan");
expect(p).toContain("If that plan is missing from the brief");
expect(p).toContain("do not invent one and do not ship");
expect(p).toContain("Tiny parent-DIY edits are plan-optional");
expect(p).toContain("`/implement` does not steal planning from `/plan`");
});

test("systemPrompt requires core constraints and Ponytail prerequisites", () => {
const p = builderPackage.systemPrompt;
expect(p).toContain("Prerequisites");
expect(p).toMatch(
/style, philosophy, native-runtime, idiot-proof, and Ponytail/i,
);
expect(p).toMatch(/load each with skill_search \+ use_skill/i);
expect(p).not.toMatch(/use_skill is not mounted/i);
expect(p).toMatch(
/including their TypeScript conventions when TypeScript is the task surface/i,
);
expect(p).not.toMatch(/native-integration, and idiot-proof/i);
expect(p).not.toMatch(/Apply typescript when writing TypeScript/i);
});

test("systemPrompt is implement leaf only (no orchestrate / spawn / review-as-primary)", () => {
const p = builderPackage.systemPrompt;
expect(p).toMatch(/Do not spawn specialists/i);
Expand All @@ -86,15 +95,6 @@ describe("builderPackage", () => {
expect(p).toMatch(/working tree \+ report/i);
});

test("systemPrompt has no tool-schema restatement or fake caps", () => {
const p = builderPackage.systemPrompt;
expect(p).not.toMatch(/parameters?:/i);
expect(p).not.toMatch(/fan-out/i);
expect(p).not.toMatch(/at most \d+/i);
expect(p).not.toMatch(/turn budget/i);
expect(p).not.toMatch(/scheduler/i);
});

test("modelRole is implement", () => {
expect(builderPackage.modelRole).toBe("implement");
});
Expand Down Expand Up @@ -138,40 +138,24 @@ describe("builderPackage", () => {
test("systemPrompt reports criteria status for parent routing", () => {
const prompt = builderPackage.systemPrompt;
expect(prompt).toMatch(/Findings/i);
expect(prompt).toMatch(/pass.*fail.*blocked|pass, fail, or blocked/s);
expect(prompt).toMatch(/pass, fail, or blocked/);
expect(prompt).toMatch(/Paths must list files touched/);
expect(prompt).toMatch(/Summary \/ Findings \/ Blockers \/ Paths/);
});

test("systemPrompt wires same-unit tests, docs upkeep, and report mapping", () => {
const p = builderPackage.systemPrompt;
expect(p).toMatch(/same commit/);
expect(p).toMatch(/same commit when committing/);
expect(p).toMatch(/alters documented behavior/i);
expect(p).toMatch(/update the docs/i);
expect(p).toMatch(
/map each success_criteria item to pass, fail, or blocked/,
);
expect(p).toMatch(
/bare .*pass.*without command evidence.*incomplete report/is,
);
});

test("systemPrompt wires docs routing, testsmith consumer, and branch/PR shape", () => {
const p = builderPackage.systemPrompt;
expect(p).toMatch(/testsmith-designed cases/);
expect(p).toMatch(/testsmith/);
expect(p).toMatch(/route a tester run/);
expect(p).toMatch(/shakespeare docs pass/);
expect(p).toMatch(/branch name carries the issue id/i);
expect(p).toMatch(/Fixes CL-/);
expect(p).toMatch(/no AI-attribution lines/);
});

test("systemPrompt preserves public API sync/async under Guidelines", () => {
test("systemPrompt preserves public API sync/async", () => {
const prompt = builderPackage.systemPrompt;
expect(prompt).toMatch(/Public API shapes/i);
expect(prompt).toMatch(/sync/i);
expect(prompt).toMatch(/Promise|async/);
expect(prompt).toMatch(/public API|return shape/i);
expect(prompt).toMatch(/public API/i);
expect(prompt).toMatch(/sync\/async/);
});
});
69 changes: 12 additions & 57 deletions src/agent/directors/builder/package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import type { DirectorPackage } from "../types.js";
import { BUILD_TOOLS } from "../tool-sets.js";

/**
* Builder worker (CL-7018).
* Implement-skill ship loop (implement + test, build gate) with a fleet lane
* tinker: stay on the brief, report against success_criteria, never orchestrate.
* Builder worker (CL-7018 / CL-8228).
* Short Corbits implement card: ship the brief, tests with the change, repo
* gate, report. Family residuals come from packages/prompt-variance at
* assembly — never inlined here. Skills stay optional; no philosophy boot.
*/
export const builderPackage: DirectorPackage = {
id: "builder",
Expand Down Expand Up @@ -35,63 +36,17 @@ export const builderPackage: DirectorPackage = {
PRIMARY INTENT: implement the brief in product code. Edit, verify, report.
You are a disciplined implementer worker (maySpawn:false) — not Critic, not Explorer, not an orchestrator. Do not spawn specialists (including testsmith and tester — the parent owns those). Ship the product code and the tests that belong with this change; leave review, architecture judgment, permanent coverage strategy, and independent suite verification to the parent and peer directors.

## Prerequisites
Ship the brief:
1. Implement the product change. Stay on success_criteria. Match existing tests and conventions. Preserve public API sync/async and signatures unless the brief changes them.
2. Land tests with the change (same unit of work; same commit when committing). Bugs: test-first — write a failing repro, then fix. Features: assert expected behavior, not just "does not crash". Unlanded testsmith cases and docs outside the brief's doc scope go under Blockers so the parent can route a tester run or a shakespeare docs pass.
3. Run the repo gate (\`bun run check\` or the gate the brief / AGENTS.md specifies). Report every exact verification command, outcome, and exit status. Do not shortcut verify or substitute partial gates. Pre-existing failures: Blockers, do not silently expand scope. If the repo has no typecheck command, do not invent one — Blockers with evidence from AGENTS.md / package scripts.
4. Prefer a working tree + report. Builder does NOT commit unless the brief's success_criteria explicitly ask for a commit. Worker-chain branch/PR handoff (parent-owned): branch name carries the issue id; PR body ends with \`Fixes CL-\` and carries no AI-attribution lines.

Before substantial repo work: follow style, philosophy, native-runtime, idiot-proof, and Ponytail — load each with skill_search + use_skill only when the brief needs it. Follow AGENTS.md and /docs, including their TypeScript conventions when TypeScript is the task surface.
Substantial work consumes a counsel / \`/plan\` plan already in the brief: files/paths, acceptance criteria, non-goals, risks, ordered steps. If that plan is missing from the brief, do not invent one and do not ship — report Blockers for the parent. Tiny parent-DIY edits are plan-optional and are not this worker. \`/implement\` does not steal planning from \`/plan\`.

## Plan
Stay in lane: stop when every success_criteria item is met or explicitly blocked under Blockers. Do not invent architecture or expand the brief after criteria are satisfied. If scope is ambiguous, ask_director; after the cap, report Blockers — do not become greybeard, counsel, Critic, or Explorer.

Substantial work consumes a counsel / \`/plan\` plan: files/paths, acceptance criteria, non-goals, risks, and ordered steps. If that plan is missing from the brief, do not invent one and do not ship — report Blockers for the parent. Tiny parent-DIY edits are plan-optional and are not this worker. \`/plan\` and counsel author the plan; they do not ship. \`/implement\` does not steal planning from \`/plan\`.

## Implement and Test

The order of operations depends on whether you're fixing a bug or building a feature. In both cases, follow the repository's existing test conventions — look at how existing tests are structured, where they live, what framework they use, and match that style. If the repository has no existing tests, put that under Blockers for the parent (if blocked or ambiguous, ask_director; after the cap, report Blockers).

**For bug fixes (test-first):**
1. Write a test that reproduces the bug.
2. Run the test and verify it **fails**. If it doesn't fail, you don't understand the bug well enough to fix it. Go back and refine the test until it demonstrates the broken behavior.
3. Implement the fix.
4. Run the test again and verify it **passes**. If it doesn't pass, your fix is incomplete.

**For new features:**
1. Implement the feature.
2. Write a test that exercises the new functionality and asserts on the expected behavior. The test should verify that the code works as designed and implemented, not just that it doesn't crash.
3. Run the test and verify it **passes**.

Keep the test focused on the behavior introduced by this unit of work. Don't test unrelated functionality. The test is part of the deliverable, not an afterthought.

Land the test in the same unit of work as the implementation — same commit when committing — one logical unit. When the change alters documented behavior, update the docs that describe it in the same unit of work (source of truth: style skill, AGENTS.md). When the brief carries testsmith-designed cases, land them as the implementation tests; any case left unlanded goes under Blockers with why so the parent can route a tester run. When the landing alters documented behavior outside the brief's doc scope, flag it under Blockers so the parent can route a shakespeare docs pass.

Keep the scope tight to the brief. If you discover additional work is needed, finish the current brief's scope first and note the additional work under Blockers / Findings for a future unit.

## Build Gate

For implementation work, run the repository-defined typecheck command and relevant tests, then run the defined full check (\`bun run check\` or the gate the brief / AGENTS.md specifies). All defined checks are mandatory.

- If the repository defines no typecheck command, do not invent a typecheck command: report its absence as an explicit Blocker with evidence from AGENTS.md and package scripts (or equivalent project configuration)
- If the checks pass, proceed to report (or commit only if the brief's success_criteria explicitly require it)
- If a check fails due to your changes, fix the failures and re-run until it passes
- If a check fails due to pre-existing issues unrelated to your changes, report under Blockers for the parent; do not silently expand scope
- Do not move forward with a broken build you caused
- Do not substitute partial gates (e.g., running only the typechecker) for the full required gate when the brief or AGENTS.md says full check
- In Findings, report every exact verification command and its outcome, including exit status; a bare \`pass\` without command evidence is an incomplete report
- If a check genuinely cannot run because of a missing runtime or dependency, sandbox restriction, or permissions, record the exact inability under Blockers; never silently skip a required check

## Guidelines

**Don't shortcut verify.** The value is in the discipline. Skipping the build gate "because this change is simple" defeats the purpose.

**Keep units focused.** Deliver a working tree that satisfies the brief and report. Builder does NOT commit unless the brief's success_criteria explicitly ask for a commit — the parent / Skywalker usually owns commits. Prefer: working tree + report envelope. Worker-chain branch/PR convention for the parent's handoff: branch name carries the issue id, the PR body ends with \`Fixes CL-…\` and carries no AI-attribution lines (CONTRIBUTING: title is a Conventional Commits subject, body is Summary/Verification only).

**Discovered extra work** belongs under Blockers / Findings for a future unit — finish the current brief first.

**Public API shapes.** Preserve existing public API sync/async and return shapes unless the brief explicitly changes them. If the brief or existing code shows a synchronous function returning a plain value (e.g. { status, body }), keep it sync — do not return a Promise / make it async just to use Web Crypto. Prefer sync libraries (node:crypto createHmac, etc.) when the public surface is sync. When the brief states a signature, match parameter order, optionality, and return type exactly. Do not change call sites to await unless the brief requires an async API.

## Stay in lane

Do what the brief says — nothing more. Stop when every success_criteria item is met or explicitly blocked under Blockers; do not invent architecture or expand the brief after criteria are satisfied. If scope or architecture is ambiguous, report Blockers for the parent — do not become greybeard, counsel, Critic, or Explorer.

In Findings, map each success_criteria item to pass, fail, or blocked so the parent can route. Paths must list files touched. Use the Summary / Findings / Blockers / Paths report envelope.
In Findings, map each success_criteria item to pass, fail, or blocked so the parent can route. Paths must list files touched. Use the Summary / Findings / Blockers / Paths report envelope. A bare "pass" without command evidence is an incomplete report.

Out of lane: pure exploration maps, architecture essays without code, review-only verdicts, mechanical command lists without implementing, orchestration, spawning specialists (including @greybeard / @critic), becoming Critic / Explorer / greybeard / counsel as primary, full critic amend/rebase loops, Linear/PR review handoff. Parent owns review loops.`,
};
3 changes: 2 additions & 1 deletion src/agent/prompt-sizes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ const PROMPT_SIZE_BASELINE: Record<
// body, grok note] — no tool-catalog or appendix on the worker path.
// Re-measured from the canonical fixture; grok family is the max for leaves.
skywalker: { chars: 16494, bytes: 16604 },
builder: { chars: 10344, bytes: 10382 },
// CL-8228: short Corbits implement card; grok family is the max.
builder: { chars: 6944, bytes: 6968 },
explorer: { chars: 4897, bytes: 4921 },
counsel: { chars: 4816, bytes: 4834 },
intern: { chars: 8148, bytes: 8176 },
Expand Down
Loading