From 2600c6520be94f290d482e8b19f25d8ac1cef0db Mon Sep 17 00:00:00 2001 From: John Fawcett Date: Thu, 12 Mar 2026 16:56:29 -0500 Subject: [PATCH] feat(gastown): inject quality gates into polecat system prompt for pre-submission validation Adds gates parameter to buildPolecatSystemPrompt that injects the town's configured quality gates (from townConfig.refinery.gates) into a Pre-Submission Gates section of the polecat prompt. Polecats now self-validate by running lint/test/build before calling gt_done, catching failures in-session instead of wasting a refinery round-trip. Closes #958 --- .../src/dos/town/container-dispatch.ts | 3 +++ .../src/prompts/polecat-system.prompt.test.ts | 18 ++++++++++++++++ .../src/prompts/polecat-system.prompt.ts | 21 ++++++++++++++++++- 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/cloudflare-gastown/src/dos/town/container-dispatch.ts b/cloudflare-gastown/src/dos/town/container-dispatch.ts index 158e95b710..f10fc4e180 100644 --- a/cloudflare-gastown/src/dos/town/container-dispatch.ts +++ b/cloudflare-gastown/src/dos/town/container-dispatch.ts @@ -150,6 +150,7 @@ export function systemPromptForRole(params: { agentName: string; rigId: string; townId: string; + gates: string[]; }): string { switch (params.role) { case 'polecat': @@ -158,6 +159,7 @@ export function systemPromptForRole(params: { rigId: params.rigId, townId: params.townId, identity: params.identity, + gates: params.gates, }); case 'mayor': return buildMayorSystemPrompt({ @@ -333,6 +335,7 @@ export async function startAgentInContainer( agentName: params.agentName, rigId: params.rigId, townId: params.townId, + gates: params.townConfig.refinery?.gates ?? [], }), gitUrl: params.gitUrl, branch: params.convoyFeatureBranch diff --git a/cloudflare-gastown/src/prompts/polecat-system.prompt.test.ts b/cloudflare-gastown/src/prompts/polecat-system.prompt.test.ts index 17397a74dd..c62a4cd9cb 100644 --- a/cloudflare-gastown/src/prompts/polecat-system.prompt.test.ts +++ b/cloudflare-gastown/src/prompts/polecat-system.prompt.test.ts @@ -7,6 +7,7 @@ describe('buildPolecatSystemPrompt', () => { rigId: 'rig-123', townId: 'town-abc', identity: 'polecat-alpha', + gates: [], }; it('should include agent name and identity', () => { @@ -50,4 +51,21 @@ describe('buildPolecatSystemPrompt', () => { expect(prompt).toContain('gt_escalate'); expect(prompt).toContain('stuck'); }); + + it('should include Pre-Submission Gates section when gates are provided', () => { + const prompt = buildPolecatSystemPrompt({ + ...params, + gates: ['pnpm test', 'pnpm lint', 'pnpm build'], + }); + expect(prompt).toContain('## Pre-Submission Gates'); + expect(prompt).toContain('1. `pnpm test`'); + expect(prompt).toContain('2. `pnpm lint`'); + expect(prompt).toContain('3. `pnpm build`'); + expect(prompt).toContain('Do NOT call gt_done until all gates pass'); + }); + + it('should not include Pre-Submission Gates section when gates is empty', () => { + const prompt = buildPolecatSystemPrompt({ ...params, gates: [] }); + expect(prompt).not.toContain('## Pre-Submission Gates'); + }); }); diff --git a/cloudflare-gastown/src/prompts/polecat-system.prompt.ts b/cloudflare-gastown/src/prompts/polecat-system.prompt.ts index b47f755481..ea57e1077b 100644 --- a/cloudflare-gastown/src/prompts/polecat-system.prompt.ts +++ b/cloudflare-gastown/src/prompts/polecat-system.prompt.ts @@ -9,7 +9,26 @@ export function buildPolecatSystemPrompt(params: { rigId: string; townId: string; identity: string; + gates: string[]; }): string { + const gatesSection = + params.gates.length > 0 + ? ` +## Pre-Submission Gates + +Before calling gt_done, run ALL of the following quality gates to validate your work: + +${params.gates.map((g, i) => `${i + 1}. \`${g}\``).join('\n')} + +If any gate fails: +- Fix the issue and re-run the failing gate. +- Repeat until all gates pass. +- If you cannot fix a gate failure after a few attempts, call gt_escalate with the full failure output, then call gt_done anyway — let the refinery make the final call. + +Do NOT call gt_done until all gates pass (or you have escalated a failure you cannot fix). +` + : ''; + return `You are ${params.agentName}, a polecat agent in Gastown rig "${params.rigId}" (town "${params.townId}"). Your identity: ${params.identity} @@ -38,7 +57,7 @@ You have these tools available. Use them to coordinate with the Gastown orchestr 3. **Commit frequently**: Make small, focused commits. Push often. The container's disk is ephemeral — if it restarts, unpushed work is lost. 4. **Checkpoint**: After significant milestones, call gt_checkpoint with a summary of progress. 5. **Done**: When the bead is complete, push your branch and call gt_done with the branch name. The bead transitions to \`in_review\` and the refinery picks it up for merge. If the review fails (rework), you will be re-dispatched with the bead back in \`in_progress\`. - +${gatesSection} ## Commit & Push Hygiene - Commit after every meaningful unit of work (new function, passing test, config change).