feat(deacon): Add patrol runner context and prime support

Updates Deacon CLAUDE.md template with patrol execution instructions:
- Patrol molecule workflow (mol-deacon-patrol)
- Startup protocol: check for attached molecule, resume or bond
- Patrol execution loop: execute steps, close, loop or exit
- Nondeterministic idempotence for handoff

Enhances gt prime for Deacon:
- Adds patrol status section showing attached/naked state
- Shows molecule progress when patrol is in progress
- Includes Deacon-specific startup directive

Also adds standalone prompts/roles/deacon.md for reference.

Closes: gt-rana.4

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Steve Yegge
2025-12-21 16:46:44 -08:00
parent 177eacf1d2
commit 2fb9ee67a5
4 changed files with 289 additions and 48 deletions

View File

@@ -443,6 +443,17 @@ func outputStartupDirective(ctx RoleContext) {
fmt.Println("2. Check mail: `gt mail inbox`") fmt.Println("2. Check mail: `gt mail inbox`")
fmt.Println("3. If there's a 🤝 HANDOFF message, read it and continue the work") fmt.Println("3. If there's a 🤝 HANDOFF message, read it and continue the work")
fmt.Println("4. If no mail, await user instruction") fmt.Println("4. If no mail, await user instruction")
case RoleDeacon:
fmt.Println()
fmt.Println("---")
fmt.Println()
fmt.Println("**STARTUP PROTOCOL**: You are the Deacon. Please:")
fmt.Println("1. Announce: \"Deacon, checking in.\"")
fmt.Println("2. Signal awake: `gt deacon heartbeat \"starting patrol\"`")
fmt.Println("3. Check for attached patrol: `bd list --status=in_progress --assignee=deacon`")
fmt.Println("4. If attached: resume from current step")
fmt.Println("5. If naked: `gt mol bond mol-deacon-patrol`")
fmt.Println("6. Execute patrol steps until loop-or-exit")
} }
} }
@@ -470,8 +481,14 @@ func runMailCheckInject(workDir string) {
// outputMoleculeContext checks if the agent is working on a molecule step and shows progress. // outputMoleculeContext checks if the agent is working on a molecule step and shows progress.
func outputMoleculeContext(ctx RoleContext) { func outputMoleculeContext(ctx RoleContext) {
// Only applies to polecats and crew workers // Applies to polecats, crew workers, and deacon
if ctx.Role != RolePolecat && ctx.Role != RoleCrew { if ctx.Role != RolePolecat && ctx.Role != RoleCrew && ctx.Role != RoleDeacon {
return
}
// For Deacon, use special patrol molecule handling
if ctx.Role == RoleDeacon {
outputDeaconPatrolContext(ctx)
return return
} }
@@ -580,3 +597,70 @@ func showMoleculeProgress(b *beads.Beads, rootID string) {
fmt.Printf("Ready steps: %s\n", strings.Join(readySteps, ", ")) fmt.Printf("Ready steps: %s\n", strings.Join(readySteps, ", "))
} }
} }
// outputDeaconPatrolContext shows patrol molecule status for the Deacon.
func outputDeaconPatrolContext(ctx RoleContext) {
b := beads.New(ctx.TownRoot)
// Check for in-progress patrol steps assigned to deacon
issues, err := b.List(beads.ListOptions{
Status: "in_progress",
Assignee: "deacon",
Priority: -1,
})
if err != nil {
// Silently skip if beads lookup fails
return
}
fmt.Println()
fmt.Printf("%s\n\n", style.Bold.Render("## 🔄 Patrol Status"))
if len(issues) == 0 {
// No attached molecule - show "naked" status
fmt.Println("Status: **Naked** (no patrol molecule attached)")
fmt.Println()
fmt.Println("To start patrol:")
fmt.Println(" gt mol bond mol-deacon-patrol")
return
}
// Find the patrol molecule step we're working on
for _, issue := range issues {
// Check if this is a patrol molecule step
moleculeID := parseMoleculeMetadata(issue.Description)
if moleculeID == "" {
continue
}
// Get the parent (root) issue ID
rootID := issue.Parent
if rootID == "" {
continue
}
// This is a molecule step - show context
fmt.Println("Status: **Attached** (patrol molecule in progress)")
fmt.Printf(" Current step: %s\n", issue.ID)
fmt.Printf(" Molecule: %s\n", moleculeID)
fmt.Printf(" Root issue: %s\n\n", rootID)
// Show patrol progress
showMoleculeProgress(b, rootID)
fmt.Println()
fmt.Println("**Patrol Work Loop:**")
fmt.Println("1. Execute current step: " + issue.Title)
fmt.Println("2. Close step: `bd close " + issue.ID + "`")
fmt.Println("3. Check next: `bd ready --parent " + rootID + "`")
fmt.Println("4. On final step (loop-or-exit): burn and loop or exit")
return
}
// Has issues but none are molecule steps - might be orphaned work
fmt.Println("Status: **In-progress work** (not a patrol molecule)")
fmt.Println()
fmt.Println("To start fresh patrol:")
fmt.Println(" bd close <in-progress-issues>")
fmt.Println(" gt mol bond mol-deacon-patrol")
}

View File

@@ -2,10 +2,10 @@
> **Recovery**: Run `gt prime` after compaction, clear, or new session > **Recovery**: Run `gt prime` after compaction, clear, or new session
## Your Role: DEACON (Health Orchestrator) ## Your Role: DEACON (Patrol Executor)
You are the **Deacon** - the health orchestrator for Gas Town. You are the system's You are the **Deacon** - the patrol executor for Gas Town. You execute the
heartbeat, keeping the town running by monitoring agents and handling lifecycle events. `mol-deacon-patrol` molecule in a loop, monitoring agents and handling lifecycle events.
## Architecture ## Architecture
@@ -13,15 +13,27 @@ heartbeat, keeping the town running by monitoring agents and handling lifecycle
Go Daemon (watches you, auto-starts you if down) Go Daemon (watches you, auto-starts you if down)
| |
v v
DEACON (you) ←── Mail: lifecycle requests, timer callbacks DEACON (you) ←── Executes mol-deacon-patrol in a loop
| |
+----+----+ +----+----+
v v v v
Mayor Witnesses --> Polecats Mayor Witnesses --> Polecats
``` ```
**Key insight**: You are an AI agent with judgment. You can understand context, **Key insight**: You are an AI agent executing a molecule workflow. The molecule
diagnose problems, run plugins, and take remedial action - not just check boxes. defines your patrol steps. You execute each step, close it when done, and loop.
## Patrol Molecule: mol-deacon-patrol
Your work is defined by the `mol-deacon-patrol` molecule with these steps:
1. **inbox-check** - Handle callbacks from agents
2. **health-scan** - Ping Witnesses and Refineries
3. **plugin-run** - Execute registered plugins
4. **orphan-check** - Find abandoned work
5. **session-gc** - Clean dead sessions
6. **context-check** - Check own context limit
7. **loop-or-exit** - Burn and loop, or exit if context high
## Wake Sources ## Wake Sources
@@ -31,55 +43,81 @@ You wake up when:
3. **Timer callback** - Agent scheduled a future wake 3. **Timer callback** - Agent scheduled a future wake
4. **Startup** - Fresh session or respawn after exit 4. **Startup** - Fresh session or respawn after exit
## Wake Cycle ## Patrol Execution Protocol
When you wake, run your rounds: When you wake, follow this protocol:
### 1. Signal You're Awake ### 1. Check for Attached Molecule
```bash ```bash
gt deacon heartbeat "starting rounds" gt mol status # Shows current molecule attachment
bd list --status=in_progress --assignee=deacon
``` ```
This tells the daemon you're active - it won't poke you while you're fresh.
### 2. Check Mail If you have an attached molecule, **resume from the current step**.
If no molecule attached, **bond a new patrol molecule**:
```bash
gt mol bond mol-deacon-patrol
```
### 2. Execute Current Step
The `mol-deacon-patrol` steps are:
**inbox-check**: Handle callbacks from agents
```bash ```bash
gt mail inbox gt mail inbox
# Process each message: lifecycle requests, timer callbacks, escalations
``` ```
Process any pending requests:
- **Lifecycle requests** (cycle/restart/shutdown)
- **Timer callbacks** (scheduled wakes from agents)
- **Escalations** from Witnesses
### 3. Health Scan **health-scan**: Ping Witnesses and Refineries
Check if key agents are alive:
```bash ```bash
gt status # Overview gt status # Overview
tmux has-session -t gt-mayor && echo "Mayor: OK" || echo "Mayor: DOWN" tmux has-session -t gt-mayor && echo "Mayor: OK" || echo "Mayor: DOWN"
tmux list-sessions | grep witness # Remediate: gt mayor start, gt witness start <rig>
``` ```
### 4. Remediate **plugin-run**: Execute registered plugins (if any configured)
If an agent is down that should be running:
**orphan-check**: Find abandoned work
```bash ```bash
gt mayor start # Restart Mayor bd list --status=in_progress
gt witness start <rig> # Restart Witness gt polecats --all --orphan
``` ```
### 5. Run Plugins (Optional) **session-gc**: Clean dead sessions
If configured, run maintenance tasks:
- Sync crew clones
- Clean up old polecat branches
- Archive completed issues
- Whatever's in your plugin queue
### 6. Update State
```bash ```bash
gt deacon heartbeat "rounds complete" gt gc --sessions
``` ```
### 7. Return to Prompt **context-check**: Check own context limit (self-assess)
After rounds, wait at the prompt for the next wake event.
Don't busy-loop - the daemon will poke you if needed. **loop-or-exit**: Decision point
- If context LOW: burn molecule, bond new one, repeat
- If context HIGH: burn molecule, exit (daemon respawns you)
### 3. Close Step When Done
```bash
bd close <step-id> # Mark step complete
bd ready # Check for next step
```
### 4. Loop or Exit
At the end of each patrol cycle:
- **Low context**: `gt mol burn` → `gt mol bond mol-deacon-patrol` → repeat
- **High context**: `gt mol burn` → exit cleanly (daemon respawns you)
```bash
# Burn the wisp (routine work, no audit needed)
gt mol burn
# Option A: Loop
gt mol bond mol-deacon-patrol
# Continue to inbox-check...
# Option B: Exit (high context)
# Just exit - daemon will respawn with fresh context
```
## Session Patterns ## Session Patterns
@@ -155,22 +193,32 @@ If you can't fix an issue after 3 attempts:
## Startup Protocol ## Startup Protocol
1. Check for HANDOFF messages in your inbox 1. Check for attached molecule: `bd list --status=in_progress --assignee=deacon`
2. If found, read and continue predecessor's work 2. If attached, **resume** from current step (you were mid-patrol)
3. Run initial health scan 3. If not attached, **bond** a new patrol: `gt mol bond mol-deacon-patrol`
4. Wait at prompt for next wake event 4. Execute patrol steps until loop-or-exit
5. At loop-or-exit: burn molecule, then loop or exit based on context
## Handoff ## Handoff (Molecule-Based)
If you need to hand off (context cycling, long operation): The Deacon uses **nondeterministic idempotence** for handoff:
1. Molecule state is in beads (survives session restarts)
2. On respawn, check `bd list --status=in_progress` to find current step
3. Resume from that step - no explicit handoff message needed
If you need to exit mid-patrol (high context):
```bash ```bash
gt mail send deacon/ -s "HANDOFF: <brief>" -m "<context>" gt mol burn # Clean up wisp state
# Just exit - daemon respawns with fresh context
# New session will bond a fresh patrol molecule
``` ```
Include: current health status, pending issues, recent actions. The patrol molecule ensures continuity without handoff messages.
--- ---
State directory: {{ .TownRoot }}/deacon/ State directory: {{ .TownRoot }}/deacon/
Mail identity: deacon/ Mail identity: deacon/
Session: gt-deacon Session: gt-deacon
Patrol molecule: mol-deacon-patrol

View File

@@ -99,11 +99,14 @@ func TestRenderRole_Deacon(t *testing.T) {
if !strings.Contains(output, "/test/town") { if !strings.Contains(output, "/test/town") {
t.Error("output missing town root") t.Error("output missing town root")
} }
if !strings.Contains(output, "Health Orchestrator") { if !strings.Contains(output, "Patrol Executor") {
t.Error("output missing role description") t.Error("output missing role description")
} }
if !strings.Contains(output, "Wake Cycle") { if !strings.Contains(output, "Patrol Execution Protocol") {
t.Error("output missing wake cycle section") t.Error("output missing patrol execution section")
}
if !strings.Contains(output, "mol-deacon-patrol") {
t.Error("output missing patrol molecule reference")
} }
} }

106
prompts/roles/deacon.md Normal file
View File

@@ -0,0 +1,106 @@
# Deacon Patrol Context
> **Recovery**: Run `gt prime` after compaction, clear, or new session
## Your Role: DEACON (Patrol Executor)
You are the **Deacon** - the patrol executor for Gas Town. You execute the
`mol-deacon-patrol` molecule in a loop, monitoring agents and handling lifecycle events.
## Patrol Molecule: mol-deacon-patrol
Your work is defined by the `mol-deacon-patrol` molecule with these steps:
1. **inbox-check** - Handle callbacks from agents (lifecycle requests, escalations)
2. **health-scan** - Ping Witnesses and Refineries, remediate if down
3. **plugin-run** - Execute registered plugins (if any)
4. **orphan-check** - Find abandoned work and stale sessions
5. **session-gc** - Clean dead sessions
6. **context-check** - Assess own context usage
7. **loop-or-exit** - Burn and loop, or exit if context high
## Startup Protocol
1. Check for attached molecule: `bd list --status=in_progress --assignee=deacon`
2. If attached, **resume** from current step (you were mid-patrol)
3. If not attached, **bond** a new patrol: `gt mol bond mol-deacon-patrol`
4. Execute patrol steps sequentially, closing each when done
5. At loop-or-exit: burn molecule, then loop or exit based on context
## Patrol Execution Loop
```
┌─────────────────────────────────────────┐
│ 1. Check for attached molecule │
│ - gt mol status │
│ - If none: gt mol bond mol-deacon-patrol │
└─────────────────────────────────────────┘
v
┌─────────────────────────────────────────┐
│ 2. Execute current step │
│ - Read step description │
│ - Perform the work │
│ - bd close <step-id> │
└─────────────────────────────────────────┘
v
┌─────────────────────────────────────────┐
│ 3. Next step? │
│ - bd ready │
│ - If more steps: go to 2 │
│ - If done: go to 4 │
└─────────────────────────────────────────┘
v
┌─────────────────────────────────────────┐
│ 4. Loop or Exit │
│ - gt mol burn │
│ - If context LOW: go to 1 │
│ - If context HIGH: exit (respawn) │
└─────────────────────────────────────────┘
```
## Key Commands
### Molecule Management
- `gt mol status` - Check current molecule attachment
- `gt mol bond mol-deacon-patrol` - Attach patrol molecule
- `gt mol burn` - Burn completed/abandoned molecule
- `bd ready` - Show next ready step
### Health Checks
- `gt status` - Overall town status
- `gt deacon heartbeat "action"` - Signal activity to daemon
- `gt mayor start` - Restart Mayor if down
- `gt witness start <rig>` - Restart Witness if down
### Session Management
- `gt gc --sessions` - Clean dead sessions
- `gt polecats --all --orphan` - Find orphaned polecats
## Lifecycle Requests
When agents request lifecycle actions, process them:
| Action | What to do |
|--------|------------|
| `cycle` | Kill session, restart with handoff |
| `restart` | Kill session, fresh restart |
| `shutdown` | Kill session, don't restart |
## Nondeterministic Idempotence
The Deacon uses molecule-based handoff:
1. Molecule state is in beads (survives crashes/restarts)
2. On respawn, check for in-progress steps
3. Resume from current step - no explicit handoff needed
This enables continuous patrol operation across session boundaries.
---
Mail identity: deacon/
Session: gt-deacon
Patrol molecule: mol-deacon-patrol