Parent: #204 | Phase 1: Single Rig, Single Polecat
Revised: This was previously "tRPC Routes — Town & Rig Management." The tRPC routes have been moved to a new issue. This issue is now about the Rig DO alarm — the core scheduler that drives the system.
Goal
The Rig DO becomes the active scheduler. Alarms periodically scan state and signal the container to start/stop agent processes. This is the "DO is the brain, container is the muscle" model.
Alarm Handler
async alarm(): Promise<void> {
await this.schedulePendingWork();
await this.witnessPatrol();
await this.processReviewQueue();
// Re-arm: 30s while active, 5 min when idle
const hasActiveWork = this.hasActiveAgentsOrPendingBeads();
const nextAlarm = hasActiveWork ? 30_000 : 300_000;
this.ctx.storage.setAlarm(Date.now() + nextAlarm);
}
schedulePendingWork() — Dispatch beads to agents
Find agents that have hooked beads but are idle (not yet started in container), and signal the container to start them:
async schedulePendingWork(): Promise<void> {
const pendingAgents = this.ctx.storage.sql.exec(
`SELECT a.*, b.id as bead_id, b.title as bead_title
FROM agents a
JOIN beads b ON b.assignee_agent_id = a.id
WHERE a.status = 'idle'
AND b.status = 'in_progress'
AND a.current_hook_bead_id IS NOT NULL`
).toArray();
for (const agent of pendingAgents) {
await this.startAgentInContainer(agent);
}
}
witnessPatrol() — Health monitoring
The existing witnessPatrol() method is now called by the alarm. Updated to check container process health instead of cloud-agent-next session health:
async witnessPatrol(): Promise<void> {
const workingAgents = /* SELECT agents WHERE status IN ('working', 'blocked') */;
for (const agent of workingAgents) {
// Check if agent process is alive in container
const container = this.env.TOWN_CONTAINER.get(
this.env.TOWN_CONTAINER.idFromName(this.townId)
);
const statusRes = await container.fetch(`http://container/agents/${agent.id}/status`);
const { status } = await statusRes.json();
if (status === 'not_found' || status === 'exited') {
if (agent.current_hook_bead_id) {
await this.restartAgent(agent); // Re-dispatch with checkpoint
} else {
this.updateAgentStatus(agent.id, 'idle');
}
continue;
}
// GUPP violation check (30 min no progress)
if (agent.last_activity_at) {
const staleMs = Date.now() - new Date(agent.last_activity_at).getTime();
if (staleMs > 30 * 60 * 1000) {
await this.sendMail({
from_agent_id: 'witness',
to_agent_id: agent.id,
subject: 'GUPP_CHECK',
body: 'You have had work hooked for 30+ minutes with no activity. Are you stuck? If so, call gt_escalate.',
});
}
}
}
}
processReviewQueue() — Trigger merge/refinery
async processReviewQueue(): Promise<void> {
const pendingEntry = this.popReviewQueue();
if (!pendingEntry) return;
// Phase 1: deterministic git merge via container
// Phase 2: start refinery agent in container
await this.startMergeInContainer(pendingEntry);
}
Alarm Activation
The alarm is armed when:
- A new bead is assigned to an agent (
hookBead)
- An agent calls
agentDone (to process review queue)
- Container reports an agent process has exited
- Health check endpoint is called
private armAlarmIfNeeded() {
const currentAlarm = this.ctx.storage.getAlarm();
if (!currentAlarm) {
this.ctx.storage.setAlarm(Date.now() + 5_000);
}
}
Note: This issue subsumes the witness alarm functionality from the old #217. The witness patrol is now part of the Rig DO alarm handler rather than a separate PR.
Dependencies
- PR 1 (Rig DO)
- PR 4 (Town Container — for
fetch() communication)
Acceptance Criteria
Parent: #204 | Phase 1: Single Rig, Single Polecat
Goal
The Rig DO becomes the active scheduler. Alarms periodically scan state and signal the container to start/stop agent processes. This is the "DO is the brain, container is the muscle" model.
Alarm Handler
schedulePendingWork()— Dispatch beads to agentsFind agents that have hooked beads but are idle (not yet started in container), and signal the container to start them:
witnessPatrol()— Health monitoringThe existing
witnessPatrol()method is now called by the alarm. Updated to check container process health instead of cloud-agent-next session health:processReviewQueue()— Trigger merge/refineryAlarm Activation
The alarm is armed when:
hookBead)agentDone(to process review queue)Dependencies
fetch()communication)Acceptance Criteria
alarm()handler implemented in Rig DOschedulePendingWork()dispatches idle agents to containerwitnessPatrol()checks container process health, restarts dead agents, detects GUPP violationsprocessReviewQueue()triggers merge processing