Skip to content
Merged
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
23 changes: 23 additions & 0 deletions services/gastown/src/dos/Town.do.ts
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,17 @@ export class TownDO extends DurableObject<Env> {
});
}

// Option B (defense-in-depth): If the reconciler re-dispatches an
// open triage batch bead (gt:triage, created_by='patrol') — e.g.
// because Option A's in_progress transition was somehow bypassed —
// inject the triage system prompt so the polecat gets the correct
// tools and instructions instead of the generic polecat prompt.
if (bead.labels.includes(patrol.TRIAGE_BATCH_LABEL) && bead.created_by === 'patrol') {
const pendingRequests = patrol.listPendingTriageRequests(this.sql);
const { buildTriageSystemPrompt } = await import('../prompts/triage-system.prompt');
systemPromptOverride = buildTriageSystemPrompt(pendingRequests);
}

return scheduling.dispatchAgent(schedulingCtx, agent, bead, {
systemPromptOverride,
});
Expand Down Expand Up @@ -4064,18 +4075,30 @@ export class TownDO extends DurableObject<Env> {
const systemPrompt = buildTriageSystemPrompt(pendingRequests);

// Only now create the synthetic bead — preconditions are verified.
// Set rig_id so that if Rule 3 resets this bead to 'open' after a
// dispatch timeout, Rule 1 of the reconciler can pick it up and
// re-dispatch it (with the correct triage system prompt via Option B).
const triageBead = beadOps.createBead(this.sql, {
type: 'issue',
title: `Triage batch: ${pendingCount} request(s)`,
body: 'Process all pending triage request beads and resolve each one.',
priority: 'high',
labels: [patrol.TRIAGE_BATCH_LABEL],
created_by: 'patrol',
rig_id: rigId,
});

const triageAgent = agents.getOrCreateAgent(this.sql, 'polecat', rigId, this.townId);
agents.hookBead(this.sql, triageAgent.id, triageBead.bead_id);

// Option A: Immediately mark the triage batch bead as in_progress so
// the reconciler's Rule 2 (idle agent + open hooked bead → dispatch_agent)
// does not re-fire on the next tick if the container start fails. Rule 3
// (stale in_progress bead + no working agent + 5-min timeout) will reset
// it back to open if the dispatch fails, allowing a clean retry via
// maybeDispatchTriageAgent with the correct triage system prompt.
beadOps.updateBeadStatus(this.sql, triageBead.bead_id, 'in_progress', triageAgent.id);
Comment thread
jrf0110 marked this conversation as resolved.

const started = await dispatch.startAgentInContainer(this.env, this.ctx.storage, {
townId: this.townId,
rigId,
Expand Down