Add patrol hygiene: inbox cleanup for Witness/Refinery/Deacon (gt-h5e0)
Each patrol formula now includes: - Explicit archive instructions after processing messages - New patrol-cleanup step for end-of-cycle inbox hygiene - Deacon: log rotation and state.json pruning Changes: - mol-witness-patrol v2: POLECAT_STARTED archive, patrol-cleanup step - mol-refinery-patrol v3: MERGE_READY archive after merge, patrol-cleanup step - mol-deacon-patrol v3: log-maintenance step, patrol-cleanup step 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -12,7 +12,7 @@ Witnesses detect it and escalate to the Mayor.
|
|||||||
The Deacon's agent bead last_activity timestamp is updated during each patrol
|
The Deacon's agent bead last_activity timestamp is updated during each patrol
|
||||||
cycle. Witnesses check this timestamp to verify health."""
|
cycle. Witnesses check this timestamp to verify health."""
|
||||||
formula = "mol-deacon-patrol"
|
formula = "mol-deacon-patrol"
|
||||||
version = 2
|
version = 3
|
||||||
|
|
||||||
[[steps]]
|
[[steps]]
|
||||||
id = "inbox-check"
|
id = "inbox-check"
|
||||||
@@ -35,16 +35,30 @@ gt mail read <id>
|
|||||||
|
|
||||||
**WITNESS_PING**:
|
**WITNESS_PING**:
|
||||||
Witnesses periodically ping to verify Deacon is alive. Simply acknowledge
|
Witnesses periodically ping to verify Deacon is alive. Simply acknowledge
|
||||||
and mark as read - the fact that you're processing mail proves you're running.
|
and archive - the fact that you're processing mail proves you're running.
|
||||||
Your agent bead last_activity is updated automatically during patrol.
|
Your agent bead last_activity is updated automatically during patrol.
|
||||||
|
```bash
|
||||||
|
gt mail archive <message-id>
|
||||||
|
```
|
||||||
|
|
||||||
**HELP / Escalation**:
|
**HELP / Escalation**:
|
||||||
Assess and handle or forward to Mayor.
|
Assess and handle or forward to Mayor.
|
||||||
|
Archive after handling:
|
||||||
|
```bash
|
||||||
|
gt mail archive <message-id>
|
||||||
|
```
|
||||||
|
|
||||||
**LIFECYCLE messages**:
|
**LIFECYCLE messages**:
|
||||||
Polecats reporting completion, refineries reporting merge results.
|
Polecats reporting completion, refineries reporting merge results.
|
||||||
|
Archive after processing:
|
||||||
|
```bash
|
||||||
|
gt mail archive <message-id>
|
||||||
|
```
|
||||||
|
|
||||||
Callbacks may spawn new polecats, update issue state, or trigger other actions."""
|
Callbacks may spawn new polecats, update issue state, or trigger other actions.
|
||||||
|
|
||||||
|
**Hygiene principle**: Archive messages after they're fully processed.
|
||||||
|
Keep inbox near-empty - only unprocessed items should remain."""
|
||||||
|
|
||||||
[[steps]]
|
[[steps]]
|
||||||
id = "trigger-pending-spawns"
|
id = "trigger-pending-spawns"
|
||||||
@@ -350,10 +364,86 @@ This handles:
|
|||||||
|
|
||||||
All cleanup is handled by doctor checks - no need to run separate commands."""
|
All cleanup is handled by doctor checks - no need to run separate commands."""
|
||||||
|
|
||||||
|
[[steps]]
|
||||||
|
id = "log-maintenance"
|
||||||
|
title = "Rotate logs and prune state"
|
||||||
|
needs = ["session-gc"]
|
||||||
|
description = """
|
||||||
|
Maintain daemon logs and state files.
|
||||||
|
|
||||||
|
**Step 1: Check daemon.log size**
|
||||||
|
```bash
|
||||||
|
# Get log file size
|
||||||
|
ls -la ~/.beads/daemon*.log 2>/dev/null || ls -la ~/gt/.beads/daemon*.log 2>/dev/null
|
||||||
|
```
|
||||||
|
|
||||||
|
If daemon.log exceeds 10MB:
|
||||||
|
```bash
|
||||||
|
# Rotate with date suffix and gzip
|
||||||
|
LOGFILE="$HOME/gt/.beads/daemon.log"
|
||||||
|
if [ -f "$LOGFILE" ] && [ $(stat -f%z "$LOGFILE" 2>/dev/null || stat -c%s "$LOGFILE") -gt 10485760 ]; then
|
||||||
|
DATE=$(date +%Y-%m-%dT%H-%M-%S)
|
||||||
|
mv "$LOGFILE" "${LOGFILE%.log}-${DATE}.log"
|
||||||
|
gzip "${LOGFILE%.log}-${DATE}.log"
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 2: Archive old daemon logs**
|
||||||
|
|
||||||
|
Clean up daemon logs older than 7 days:
|
||||||
|
```bash
|
||||||
|
find ~/gt/.beads/ -name "daemon-*.log.gz" -mtime +7 -delete
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 3: Prune state.json of dead sessions**
|
||||||
|
|
||||||
|
The state.json tracks active sessions. Prune entries for sessions that no longer exist:
|
||||||
|
```bash
|
||||||
|
# Check for stale session entries
|
||||||
|
gt daemon status --json 2>/dev/null
|
||||||
|
```
|
||||||
|
|
||||||
|
If state.json references sessions not in tmux:
|
||||||
|
- Remove the stale entries
|
||||||
|
- The daemon's internal cleanup should handle this, but verify
|
||||||
|
|
||||||
|
**Note**: Log rotation prevents disk bloat from long-running daemons.
|
||||||
|
State pruning keeps runtime state accurate."""
|
||||||
|
|
||||||
|
[[steps]]
|
||||||
|
id = "patrol-cleanup"
|
||||||
|
title = "End-of-cycle inbox hygiene"
|
||||||
|
needs = ["log-maintenance"]
|
||||||
|
description = """
|
||||||
|
Verify inbox hygiene before ending patrol cycle.
|
||||||
|
|
||||||
|
**Step 1: Check inbox state**
|
||||||
|
```bash
|
||||||
|
gt mail inbox
|
||||||
|
```
|
||||||
|
|
||||||
|
Inbox should be EMPTY or contain only just-arrived unprocessed messages.
|
||||||
|
|
||||||
|
**Step 2: Archive any remaining processed messages**
|
||||||
|
|
||||||
|
All message types should have been archived during inbox-check processing:
|
||||||
|
- WITNESS_PING → archived after acknowledging
|
||||||
|
- HELP/Escalation → archived after handling
|
||||||
|
- LIFECYCLE → archived after processing
|
||||||
|
|
||||||
|
If any were missed:
|
||||||
|
```bash
|
||||||
|
# For each stale message found:
|
||||||
|
gt mail archive <message-id>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Goal**: Inbox should have ≤2 active messages at end of cycle.
|
||||||
|
Deacon mail should flow through quickly - no accumulation."""
|
||||||
|
|
||||||
[[steps]]
|
[[steps]]
|
||||||
id = "context-check"
|
id = "context-check"
|
||||||
title = "Check own context limit"
|
title = "Check own context limit"
|
||||||
needs = ["session-gc"]
|
needs = ["patrol-cleanup"]
|
||||||
description = """
|
description = """
|
||||||
Check own context limit.
|
Check own context limit.
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ Witness Refinery Git
|
|||||||
After successful merge, Refinery sends MERGED mail back to Witness so it can
|
After successful merge, Refinery sends MERGED mail back to Witness so it can
|
||||||
complete cleanup (nuke the polecat worktree)."""
|
complete cleanup (nuke the polecat worktree)."""
|
||||||
formula = "mol-refinery-patrol"
|
formula = "mol-refinery-patrol"
|
||||||
version = 2
|
version = 3
|
||||||
|
|
||||||
[[steps]]
|
[[steps]]
|
||||||
id = "inbox-check"
|
id = "inbox-check"
|
||||||
@@ -65,19 +65,34 @@ A polecat's work is ready for merge. Extract details and track for processing.
|
|||||||
# - MR bead ID (REQUIRED for closing after merge)
|
# - MR bead ID (REQUIRED for closing after merge)
|
||||||
```
|
```
|
||||||
|
|
||||||
**IMPORTANT**: You MUST track the polecat name and MR bead ID - you will need them
|
**IMPORTANT**: You MUST track the polecat name, MR bead ID, AND message ID - you will need them
|
||||||
in merge-push step to send MERGED notification and close the MR bead.
|
in merge-push step to send MERGED notification, close the MR bead, and archive the mail.
|
||||||
|
|
||||||
Mark as read. The work will be processed in queue-scan/process-branch.
|
Mark as read. The work will be processed in queue-scan/process-branch.
|
||||||
|
**Do NOT archive yet** - archive after merge/reject decision in merge-push step.
|
||||||
|
|
||||||
**PATROL: Wake up**:
|
**PATROL: Wake up**:
|
||||||
Witness detected MRs waiting but refinery idle. Acknowledge and proceed.
|
Witness detected MRs waiting but refinery idle. Acknowledge and archive:
|
||||||
|
```bash
|
||||||
|
gt mail archive <message-id>
|
||||||
|
```
|
||||||
|
|
||||||
**HELP / Blocked**:
|
**HELP / Blocked**:
|
||||||
Assess and respond. If you can't help, escalate to Mayor.
|
Assess and respond. If you can't help, escalate to Mayor.
|
||||||
|
Archive after handling:
|
||||||
|
```bash
|
||||||
|
gt mail archive <message-id>
|
||||||
|
```
|
||||||
|
|
||||||
**HANDOFF**:
|
**HANDOFF**:
|
||||||
Read predecessor context. Check for in-flight merges."""
|
Read predecessor context. Check for in-flight merges.
|
||||||
|
Archive after absorbing context:
|
||||||
|
```bash
|
||||||
|
gt mail archive <message-id>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Hygiene principle**: Archive messages after they're fully processed.
|
||||||
|
Keep only: pending MRs in queue. Inbox should be near-empty."""
|
||||||
|
|
||||||
[[steps]]
|
[[steps]]
|
||||||
id = "queue-scan"
|
id = "queue-scan"
|
||||||
@@ -196,7 +211,13 @@ The MR bead ID was in the MERGE_READY message or find via:
|
|||||||
bd list --type=merge-request --status=open | grep <polecat-name>
|
bd list --type=merge-request --status=open | grep <polecat-name>
|
||||||
```
|
```
|
||||||
|
|
||||||
**Step 4: Cleanup (only after Steps 2-3 confirmed)**
|
**Step 4: Archive the MERGE_READY mail (REQUIRED)**
|
||||||
|
```bash
|
||||||
|
gt mail archive <merge-ready-message-id>
|
||||||
|
```
|
||||||
|
The message ID was tracked when you processed inbox-check.
|
||||||
|
|
||||||
|
**Step 5: Cleanup (only after Steps 2-4 confirmed)**
|
||||||
```bash
|
```bash
|
||||||
git branch -d temp
|
git branch -d temp
|
||||||
git push origin --delete <polecat-branch>
|
git push origin --delete <polecat-branch>
|
||||||
@@ -205,8 +226,9 @@ git push origin --delete <polecat-branch>
|
|||||||
**VERIFICATION GATE**: You CANNOT proceed to loop-check without:
|
**VERIFICATION GATE**: You CANNOT proceed to loop-check without:
|
||||||
- [x] MERGED mail sent to witness
|
- [x] MERGED mail sent to witness
|
||||||
- [x] MR bead closed
|
- [x] MR bead closed
|
||||||
|
- [x] MERGE_READY mail archived
|
||||||
|
|
||||||
If you skipped notifications, GO BACK AND SEND THEM NOW.
|
If you skipped notifications or archiving, GO BACK AND DO THEM NOW.
|
||||||
|
|
||||||
Main has moved. Any remaining branches need rebasing on new baseline."""
|
Main has moved. Any remaining branches need rebasing on new baseline."""
|
||||||
|
|
||||||
@@ -232,13 +254,15 @@ Summarize this patrol cycle.
|
|||||||
**VERIFICATION**: Before generating summary, confirm for each merged branch:
|
**VERIFICATION**: Before generating summary, confirm for each merged branch:
|
||||||
- [ ] MERGED mail was sent to witness
|
- [ ] MERGED mail was sent to witness
|
||||||
- [ ] MR bead was closed
|
- [ ] MR bead was closed
|
||||||
|
- [ ] MERGE_READY mail archived
|
||||||
|
|
||||||
If any MERGED notifications were missed, send them now!
|
If any notifications or archiving were missed, do them now!
|
||||||
|
|
||||||
Include in summary:
|
Include in summary:
|
||||||
- Branches processed (count, names)
|
- Branches processed (count, names)
|
||||||
- MERGED mails sent (count - should match branches processed)
|
- MERGED mails sent (count - should match branches processed)
|
||||||
- MR beads closed (count - should match branches processed)
|
- MR beads closed (count - should match branches processed)
|
||||||
|
- MERGE_READY mails archived (count - should match branches processed)
|
||||||
- Test results (pass/fail)
|
- Test results (pass/fail)
|
||||||
- Issues filed (if any)
|
- Issues filed (if any)
|
||||||
- Branches skipped (with reasons)
|
- Branches skipped (with reasons)
|
||||||
@@ -260,10 +284,41 @@ If context is HIGH (>80%):
|
|||||||
If context is LOW:
|
If context is LOW:
|
||||||
- Can continue processing"""
|
- Can continue processing"""
|
||||||
|
|
||||||
|
[[steps]]
|
||||||
|
id = "patrol-cleanup"
|
||||||
|
title = "End-of-cycle inbox hygiene"
|
||||||
|
needs = ["context-check"]
|
||||||
|
description = """
|
||||||
|
Verify inbox hygiene before ending patrol cycle.
|
||||||
|
|
||||||
|
**Step 1: Check inbox state**
|
||||||
|
```bash
|
||||||
|
gt mail inbox
|
||||||
|
```
|
||||||
|
|
||||||
|
Inbox should contain ONLY:
|
||||||
|
- Unprocessed MERGE_READY messages (will process next cycle)
|
||||||
|
- Active work items
|
||||||
|
|
||||||
|
**Step 2: Archive any stale messages**
|
||||||
|
|
||||||
|
Look for messages that were processed but not archived:
|
||||||
|
- PATROL: Wake up that was acknowledged → archive
|
||||||
|
- HELP/Blocked that was handled → archive
|
||||||
|
- MERGE_READY where merge completed but archive was missed → archive
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# For each stale message found:
|
||||||
|
gt mail archive <message-id>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Goal**: Inbox should have ≤3 active messages at end of cycle.
|
||||||
|
Keep only: pending MRs in queue."""
|
||||||
|
|
||||||
[[steps]]
|
[[steps]]
|
||||||
id = "burn-or-loop"
|
id = "burn-or-loop"
|
||||||
title = "Burn and respawn or loop"
|
title = "Burn and respawn or loop"
|
||||||
needs = ["context-check"]
|
needs = ["patrol-cleanup"]
|
||||||
description = """
|
description = """
|
||||||
End of patrol cycle decision.
|
End of patrol cycle decision.
|
||||||
|
|
||||||
|
|||||||
@@ -1,369 +1,56 @@
|
|||||||
description = """
|
description = "Per-rig worker monitor patrol loop.\n\nThe Witness is the Pit Boss for your rig. You watch polecats, nudge them toward\ncompletion, verify clean git state before kills, and escalate stuck workers.\n\n**You do NOT do implementation work.** Your job is oversight, not coding.\n\n## Design Philosophy\n\nThis patrol follows Gas Town principles:\n- **Discovery over tracking**: Observe reality each cycle, don't maintain state\n- **Events over state**: POLECAT_DONE mail triggers cleanup wisps\n- **Cleanup wisps as finalizers**: Pending cleanups are wisps, not queue entries\n- **Task tool for parallelism**: Subagents inspect polecats, not molecule arms\n\n## Patrol Shape (Linear, Deacon-style)\n\n```\ninbox-check ─► process-cleanups ─► check-refinery ─► survey-workers\n │\n ┌──────────────────────────────────────────────────┘\n ▼\n check-swarm ─► ping-deacon ─► patrol-cleanup ─► context-check ─► loop-or-exit\n```\n\nNo dynamic arms. No fanout gates. No persistent nudge counters.\nState is discovered each cycle from reality (tmux, beads, mail)."
|
||||||
Per-rig worker monitor patrol loop.
|
formula = 'mol-witness-patrol'
|
||||||
|
version = 2
|
||||||
The Witness is the Pit Boss for your rig. You watch polecats, nudge them toward
|
|
||||||
completion, verify clean git state before kills, and escalate stuck workers.
|
|
||||||
|
|
||||||
**You do NOT do implementation work.** Your job is oversight, not coding.
|
|
||||||
|
|
||||||
## Design Philosophy
|
|
||||||
|
|
||||||
This patrol follows Gas Town principles:
|
|
||||||
- **Discovery over tracking**: Observe reality each cycle, don't maintain state
|
|
||||||
- **Events over state**: POLECAT_DONE mail triggers cleanup wisps
|
|
||||||
- **Cleanup wisps as finalizers**: Pending cleanups are wisps, not queue entries
|
|
||||||
- **Task tool for parallelism**: Subagents inspect polecats, not molecule arms
|
|
||||||
|
|
||||||
## Patrol Shape (Linear, Deacon-style)
|
|
||||||
|
|
||||||
```
|
|
||||||
inbox-check ─► process-cleanups ─► check-refinery ─► survey-workers
|
|
||||||
│
|
|
||||||
┌──────────────────────────────────────────────────┘
|
|
||||||
▼
|
|
||||||
check-swarm ─► ping-deacon ─► context-check ─► loop-or-exit
|
|
||||||
```
|
|
||||||
|
|
||||||
No dynamic arms. No fanout gates. No persistent nudge counters.
|
|
||||||
State is discovered each cycle from reality (tmux, beads, mail)."""
|
|
||||||
formula = "mol-witness-patrol"
|
|
||||||
version = 1
|
|
||||||
|
|
||||||
[[steps]]
|
[[steps]]
|
||||||
id = "inbox-check"
|
description = "Check inbox and handle messages.\n\n```bash\ngt mail inbox\n```\n\nFor each message:\n\n**POLECAT_STARTED**:\nA new polecat has started working. Acknowledge and archive.\n```bash\n# Acknowledge startup (optional: log for activity tracking)\ngt mail archive <message-id>\n```\nNo action needed beyond acknowledgment - archive immediately.\n\n**POLECAT_DONE / LIFECYCLE:Shutdown**:\nCreate a cleanup wisp for this polecat:\n```bash\nbd create --wisp --title \"cleanup:<polecat>\" --description \"Verify and cleanup polecat <name>\" --labels cleanup,polecat:<name>,state:pending\n```\nThe wisp's existence IS the pending cleanup. Process in next step.\n**Do NOT archive yet** - archive after cleanup completes (in MERGED handling).\n\n**MERGED**:\nA branch was merged successfully. Complete the cleanup.\n```bash\n# Find the cleanup wisp for this polecat\nbd list --wisp --labels=polecat:<name>,state:merge-requested --status=open\n\n# If found, proceed with full polecat nuke:\n# - Kill Claude session\n# - Delete worktree\n# - Delete branch\n# - Remove agent bead\ngt polecat nuke <name>\n\n# Burn the cleanup wisp\nbd close <wisp-id>\n\n# NOW archive both the MERGED mail and the original POLECAT_DONE mail\n# (The POLECAT_DONE message ID should be tracked in the cleanup wisp or MR bead)\ngt mail archive <merged-message-id>\ngt mail archive <polecat-done-message-id> # If tracked\n```\nArchive after cleanup is complete.\n\n**HELP / Blocked**:\nAssess the request. Can you help? If not, escalate to Mayor:\n```bash\ngt mail send mayor/ -s \"Escalation: <polecat> needs help\" -m \"<details>\"\n```\nArchive after handling (escalated or resolved):\n```bash\ngt mail archive <message-id>\n```\n\n**HANDOFF**:\nRead predecessor context. Continue from where they left off.\nArchive after absorbing context:\n```bash\ngt mail archive <message-id>\n```\n\n**SWARM_START**:\nMayor initiating batch polecat work. Initialize swarm tracking.\n```bash\n# Parse swarm info from mail body: {\"swarm_id\": \"batch-123\", \"beads\": [\"bd-a\", \"bd-b\"]}\nbd create --wisp --title \"swarm:<swarm_id>\" --description \"Tracking batch: <swarm_id>\" --labels swarm,swarm_id:<swarm_id>,total:<N>,completed:0,start:<timestamp>\n```\nArchive after creating swarm tracking wisp:\n```bash\ngt mail archive <message-id>\n```\n\n**Hygiene principle**: Archive messages after they're fully processed.\nKeep only: active work, unprocessed requests. Inbox should be near-empty."
|
||||||
title = "Process witness mail"
|
id = 'inbox-check'
|
||||||
description = """
|
title = 'Process witness mail'
|
||||||
Check inbox and handle messages.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gt mail inbox
|
|
||||||
```
|
|
||||||
|
|
||||||
For each message:
|
|
||||||
|
|
||||||
**POLECAT_DONE / LIFECYCLE:Shutdown**:
|
|
||||||
Create a cleanup wisp for this polecat:
|
|
||||||
```bash
|
|
||||||
bd create --wisp --title "cleanup:<polecat>" \
|
|
||||||
--description "Verify and cleanup polecat <name>" \
|
|
||||||
--labels cleanup,polecat:<name>,state:pending
|
|
||||||
```
|
|
||||||
The wisp's existence IS the pending cleanup. Process in next step.
|
|
||||||
Mark mail as read.
|
|
||||||
|
|
||||||
**MERGED**:
|
|
||||||
A branch was merged successfully. Complete the cleanup.
|
|
||||||
```bash
|
|
||||||
# Find the cleanup wisp for this polecat
|
|
||||||
bd list --wisp --labels=polecat:<name>,state:merge-requested --status=open
|
|
||||||
|
|
||||||
# If found, proceed with full polecat nuke:
|
|
||||||
# - Kill Claude session
|
|
||||||
# - Delete worktree
|
|
||||||
# - Delete branch
|
|
||||||
# - Remove agent bead
|
|
||||||
gt polecat nuke <name>
|
|
||||||
|
|
||||||
# Burn the cleanup wisp
|
|
||||||
bd close <wisp-id>
|
|
||||||
```
|
|
||||||
Mark mail as read.
|
|
||||||
|
|
||||||
**HELP / Blocked**:
|
|
||||||
Assess the request. Can you help? If not, escalate to Mayor:
|
|
||||||
```bash
|
|
||||||
gt mail send mayor/ -s "Escalation: <polecat> needs help" -m "<details>"
|
|
||||||
```
|
|
||||||
|
|
||||||
**HANDOFF**:
|
|
||||||
Read predecessor context. Continue from where they left off.
|
|
||||||
|
|
||||||
**SWARM_START**:
|
|
||||||
Mayor initiating batch polecat work. Initialize swarm tracking.
|
|
||||||
```bash
|
|
||||||
# Parse swarm info from mail body: {"swarm_id": "batch-123", "beads": ["bd-a", "bd-b"]}
|
|
||||||
bd create --wisp --title "swarm:<swarm_id>" \
|
|
||||||
--description "Tracking batch: <swarm_id>" \
|
|
||||||
--labels swarm,swarm_id:<swarm_id>,total:<N>,completed:0,start:<timestamp>
|
|
||||||
```
|
|
||||||
Mark mail as read."""
|
|
||||||
|
|
||||||
[[steps]]
|
[[steps]]
|
||||||
id = "process-cleanups"
|
description = "Find and process cleanup wisps (the finalizer pattern).\n\n```bash\n# Find all cleanup wisps\nbd list --wisp --labels=cleanup --status=open\n```\n\nFor each cleanup wisp, check its state label:\n\n## State: pending (needs verification → MERGE_READY)\n\n1. **Extract polecat name** from wisp title/labels\n\n2. **Pre-kill verification**:\n```bash\ncd polecats/<name>\ngit status # Must be clean\ngit log origin/main..HEAD # Commits should be pushed\nbd show <assigned-issue> # Issue closed or deferred\n```\n\n3. **Get branch and issue info**:\n```bash\n# Get current branch\ngit rev-parse --abbrev-ref HEAD\n\n# Get the hook_bead from agent bead\nbd show <agent-bead> # Look for hook_bead field\n```\n\n4. **Verify productive work** (ZFC - you make the call):\n - Check git log for commits mentioning the issue\n - Legitimate exceptions: already fixed, duplicate, deferred\n - If closing as 'done' with no commits, flag for review\n\n5. **If clean**: Send MERGE_READY to refinery\n```bash\ngt mail send <rig>/refinery -s \"MERGE_READY <polecat>\" -m \"Branch: <branch>\nIssue: <issue-id>\nPolecat: <polecat>\nVerified: clean git state, issue closed\"\n```\nThen update the wisp to merge-requested state:\n```bash\nbd update <wisp-id> --labels cleanup,polecat:<name>,state:merge-requested\n```\n**Do NOT kill the polecat yet** - wait for MERGED confirmation from refinery.\n\n6. **If dirty**: Leave wisp open, log the issue, retry next cycle.\n\n## State: merge-requested (waiting for refinery)\n\nSkip - waiting for MERGED mail from refinery. The inbox-check step handles\nMERGED messages and completes these cleanup wisps.\n\n**Parallelism**: Use Task tool subagents to process multiple cleanups concurrently.\nEach cleanup is independent - perfect for parallel execution."
|
||||||
title = "Process pending cleanup wisps"
|
id = 'process-cleanups'
|
||||||
needs = ["inbox-check"]
|
needs = ['inbox-check']
|
||||||
description = """
|
title = 'Process pending cleanup wisps'
|
||||||
Find and process cleanup wisps (the finalizer pattern).
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Find all cleanup wisps
|
|
||||||
bd list --wisp --labels=cleanup --status=open
|
|
||||||
```
|
|
||||||
|
|
||||||
For each cleanup wisp, check its state label:
|
|
||||||
|
|
||||||
## State: pending (needs verification → MERGE_READY)
|
|
||||||
|
|
||||||
1. **Extract polecat name** from wisp title/labels
|
|
||||||
|
|
||||||
2. **Pre-kill verification**:
|
|
||||||
```bash
|
|
||||||
cd polecats/<name>
|
|
||||||
git status # Must be clean
|
|
||||||
git log origin/main..HEAD # Commits should be pushed
|
|
||||||
bd show <assigned-issue> # Issue closed or deferred
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Get branch and issue info**:
|
|
||||||
```bash
|
|
||||||
# Get current branch
|
|
||||||
git rev-parse --abbrev-ref HEAD
|
|
||||||
|
|
||||||
# Get the hook_bead from agent bead
|
|
||||||
bd show <agent-bead> # Look for hook_bead field
|
|
||||||
```
|
|
||||||
|
|
||||||
4. **Verify productive work** (ZFC - you make the call):
|
|
||||||
- Check git log for commits mentioning the issue
|
|
||||||
- Legitimate exceptions: already fixed, duplicate, deferred
|
|
||||||
- If closing as 'done' with no commits, flag for review
|
|
||||||
|
|
||||||
5. **If clean**: Send MERGE_READY to refinery
|
|
||||||
```bash
|
|
||||||
gt mail send <rig>/refinery -s "MERGE_READY <polecat>" -m "Branch: <branch>
|
|
||||||
Issue: <issue-id>
|
|
||||||
Polecat: <polecat>
|
|
||||||
Verified: clean git state, issue closed"
|
|
||||||
```
|
|
||||||
Then update the wisp to merge-requested state:
|
|
||||||
```bash
|
|
||||||
bd update <wisp-id> --labels cleanup,polecat:<name>,state:merge-requested
|
|
||||||
```
|
|
||||||
**Do NOT kill the polecat yet** - wait for MERGED confirmation from refinery.
|
|
||||||
|
|
||||||
6. **If dirty**: Leave wisp open, log the issue, retry next cycle.
|
|
||||||
|
|
||||||
## State: merge-requested (waiting for refinery)
|
|
||||||
|
|
||||||
Skip - waiting for MERGED mail from refinery. The inbox-check step handles
|
|
||||||
MERGED messages and completes these cleanup wisps.
|
|
||||||
|
|
||||||
**Parallelism**: Use Task tool subagents to process multiple cleanups concurrently.
|
|
||||||
Each cleanup is independent - perfect for parallel execution."""
|
|
||||||
|
|
||||||
[[steps]]
|
[[steps]]
|
||||||
id = "check-refinery"
|
description = "Ensure the refinery is alive and processing merge requests.\n\n```bash\n# Check if refinery session exists\ngt session status <rig>/refinery\n\n# Check for pending merge requests\nbd list --type=merge-request --status=open\n```\n\nIf MRs waiting AND refinery not running:\n```bash\ngt session start <rig>/refinery\ngt mail send <rig>/refinery -s \"PATROL: Wake up\" -m \"Merge requests in queue. Please process.\"\n```\n\nIf refinery running but queue stale (>30 min), send nudge."
|
||||||
title = "Ensure refinery is alive"
|
id = 'check-refinery'
|
||||||
needs = ["process-cleanups"]
|
needs = ['process-cleanups']
|
||||||
description = """
|
title = 'Ensure refinery is alive'
|
||||||
Ensure the refinery is alive and processing merge requests.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Check if refinery session exists
|
|
||||||
gt session status <rig>/refinery
|
|
||||||
|
|
||||||
# Check for pending merge requests
|
|
||||||
bd list --type=merge-request --status=open
|
|
||||||
```
|
|
||||||
|
|
||||||
If MRs waiting AND refinery not running:
|
|
||||||
```bash
|
|
||||||
gt session start <rig>/refinery
|
|
||||||
gt mail send <rig>/refinery -s "PATROL: Wake up" \
|
|
||||||
-m "Merge requests in queue. Please process."
|
|
||||||
```
|
|
||||||
|
|
||||||
If refinery running but queue stale (>30 min), send nudge."""
|
|
||||||
|
|
||||||
[[steps]]
|
[[steps]]
|
||||||
id = "survey-workers"
|
description = "Survey all polecats using agent beads (ZFC: trust what agents report).\n\n**Step 1: List polecat agent beads**\n\n```bash\nbd list --type=agent --json\n```\n\nFilter the JSON output for entries where description contains `role_type: polecat`.\nEach polecat agent bead has fields in its description:\n- `role_type: polecat`\n- `rig: <rig-name>`\n- `agent_state: running|idle|stuck|done`\n- `hook_bead: <current-work-id>`\n\n**Step 2: For each polecat, check agent_state**\n\n| agent_state | Meaning | Action |\n|-------------|---------|--------|\n| running | Actively working | Check progress (Step 3) |\n| idle | No work assigned | Skip (no action needed) |\n| stuck | Self-reported stuck | Handle stuck protocol |\n| done | Work complete | Verify cleanup triggered (see Step 4a) |\n\n**Step 3: For running polecats, assess progress**\n\nCheck the hook_bead field to see what they're working on:\n```bash\nbd show <hook_bead> # See current step/issue\n```\n\nYou can also verify they're responsive:\n```bash\ntmux capture-pane -t gt-<rig>-<name> -p | tail -20\n```\n\nLook for:\n- Recent tool activity → making progress\n- Idle at prompt → may need nudge\n- Error messages → may need help\n\n**Step 4: Decide action**\n\n| Observation | Action |\n|-------------|--------|\n| agent_state=running, recent activity | None |\n| agent_state=running, idle 5-15 min | Gentle nudge |\n| agent_state=running, idle 15+ min | Direct nudge with deadline |\n| agent_state=stuck | Assess and help or escalate |\n| agent_state=done | Verify cleanup triggered (see Step 4a) |\n\n**Step 4a: Handle agent_state=done**\n\nCheck if a cleanup wisp exists for this polecat:\n```bash\nbd list --wisp --labels=polecat:<name> --status=open\n```\n\nIf cleanup wisp exists:\n- state:pending → Will be processed in process-cleanups\n- state:merge-requested → Waiting for refinery MERGED response\n\nIf NO cleanup wisp exists (POLECAT_DONE mail missed):\nCreate one to trigger the cleanup flow:\n```bash\nbd create --wisp --title \"cleanup:<polecat>\" --description \"Discovered done polecat without cleanup wisp\" --labels cleanup,polecat:<name>,state:pending\n```\nThis ensures done polecats eventually get cleaned up even if mail was lost.\n\n**Step 5: Execute nudges**\n```bash\ngt nudge <rig>/polecats/<name> \"How's progress? Need help?\"\n```\n\n**Step 6: Escalate if needed**\n```bash\ngt mail send mayor/ -s \"Escalation: <polecat> stuck\" \\\n -m \"Polecat <name> reports stuck. Please intervene.\"\n```\n\n**Parallelism**: Use Task tool subagents to inspect multiple polecats concurrently.\n\n**ZFC Principle**: Trust agent_state from beads. Don't infer state from PID/tmux."
|
||||||
title = "Inspect all active polecats"
|
id = 'survey-workers'
|
||||||
needs = ["check-refinery"]
|
needs = ['check-refinery']
|
||||||
description = """
|
title = 'Inspect all active polecats'
|
||||||
Survey all polecats using agent beads (ZFC: trust what agents report).
|
|
||||||
|
|
||||||
**Step 1: List polecat agent beads**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
bd list --type=agent --json
|
|
||||||
```
|
|
||||||
|
|
||||||
Filter the JSON output for entries where description contains `role_type: polecat`.
|
|
||||||
Each polecat agent bead has fields in its description:
|
|
||||||
- `role_type: polecat`
|
|
||||||
- `rig: <rig-name>`
|
|
||||||
- `agent_state: running|idle|stuck|done`
|
|
||||||
- `hook_bead: <current-work-id>`
|
|
||||||
|
|
||||||
**Step 2: For each polecat, check agent_state**
|
|
||||||
|
|
||||||
| agent_state | Meaning | Action |
|
|
||||||
|-------------|---------|--------|
|
|
||||||
| running | Actively working | Check progress (Step 3) |
|
|
||||||
| idle | No work assigned | Skip (no action needed) |
|
|
||||||
| stuck | Self-reported stuck | Handle stuck protocol |
|
|
||||||
| done | Work complete | Verify cleanup triggered (see Step 4a) |
|
|
||||||
|
|
||||||
**Step 3: For running polecats, assess progress**
|
|
||||||
|
|
||||||
Check the hook_bead field to see what they're working on:
|
|
||||||
```bash
|
|
||||||
bd show <hook_bead> # See current step/issue
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also verify they're responsive:
|
|
||||||
```bash
|
|
||||||
tmux capture-pane -t gt-<rig>-<name> -p | tail -20
|
|
||||||
```
|
|
||||||
|
|
||||||
Look for:
|
|
||||||
- Recent tool activity → making progress
|
|
||||||
- Idle at prompt → may need nudge
|
|
||||||
- Error messages → may need help
|
|
||||||
|
|
||||||
**Step 4: Decide action**
|
|
||||||
|
|
||||||
| Observation | Action |
|
|
||||||
|-------------|--------|
|
|
||||||
| agent_state=running, recent activity | None |
|
|
||||||
| agent_state=running, idle 5-15 min | Gentle nudge |
|
|
||||||
| agent_state=running, idle 15+ min | Direct nudge with deadline |
|
|
||||||
| agent_state=stuck | Assess and help or escalate |
|
|
||||||
| agent_state=done | Verify cleanup triggered (see Step 4a) |
|
|
||||||
|
|
||||||
**Step 4a: Handle agent_state=done**
|
|
||||||
|
|
||||||
Check if a cleanup wisp exists for this polecat:
|
|
||||||
```bash
|
|
||||||
bd list --wisp --labels=polecat:<name> --status=open
|
|
||||||
```
|
|
||||||
|
|
||||||
If cleanup wisp exists:
|
|
||||||
- state:pending → Will be processed in process-cleanups
|
|
||||||
- state:merge-requested → Waiting for refinery MERGED response
|
|
||||||
|
|
||||||
If NO cleanup wisp exists (POLECAT_DONE mail missed):
|
|
||||||
Create one to trigger the cleanup flow:
|
|
||||||
```bash
|
|
||||||
bd create --wisp --title "cleanup:<polecat>" \
|
|
||||||
--description "Discovered done polecat without cleanup wisp" \
|
|
||||||
--labels cleanup,polecat:<name>,state:pending
|
|
||||||
```
|
|
||||||
This ensures done polecats eventually get cleaned up even if mail was lost.
|
|
||||||
|
|
||||||
**Step 5: Execute nudges**
|
|
||||||
```bash
|
|
||||||
gt nudge <rig>/polecats/<name> "How's progress? Need help?"
|
|
||||||
```
|
|
||||||
|
|
||||||
**Step 6: Escalate if needed**
|
|
||||||
```bash
|
|
||||||
gt mail send mayor/ -s "Escalation: <polecat> stuck" \\
|
|
||||||
-m "Polecat <name> reports stuck. Please intervene."
|
|
||||||
```
|
|
||||||
|
|
||||||
**Parallelism**: Use Task tool subagents to inspect multiple polecats concurrently.
|
|
||||||
|
|
||||||
**ZFC Principle**: Trust agent_state from beads. Don't infer state from PID/tmux."""
|
|
||||||
|
|
||||||
[[steps]]
|
[[steps]]
|
||||||
id = "check-swarm-completion"
|
description = "If Mayor started a batch (SWARM_START), check if all polecats have completed.\n\n**Step 1: Find active swarm tracking wisps**\n```bash\nbd list --wisp --labels=swarm --status=open\n```\nIf no active swarm, skip this step.\n\n**Step 2: Count completed polecats for this swarm**\n\nExtract from wisp labels: swarm_id, total, completed, start timestamp.\nCheck how many cleanup wisps have been closed for this swarm's polecats.\n\n**Step 3: If all complete, notify Mayor**\n```bash\ngt mail send mayor/ -s \"SWARM_COMPLETE: <swarm_id>\" -m \"All <total> polecats merged.\nDuration: <minutes> minutes\nSwarm: <swarm_id>\"\n\n# Close the swarm tracking wisp\nbd close <swarm-wisp-id> --reason \"All polecats merged\"\n```\n\nNote: Runs every patrol cycle. Notification sent exactly once when all complete."
|
||||||
title = "Check if active swarm is complete"
|
id = 'check-swarm-completion'
|
||||||
needs = ["survey-workers"]
|
needs = ['survey-workers']
|
||||||
description = """
|
title = 'Check if active swarm is complete'
|
||||||
If Mayor started a batch (SWARM_START), check if all polecats have completed.
|
|
||||||
|
|
||||||
**Step 1: Find active swarm tracking wisps**
|
|
||||||
```bash
|
|
||||||
bd list --wisp --labels=swarm --status=open
|
|
||||||
```
|
|
||||||
If no active swarm, skip this step.
|
|
||||||
|
|
||||||
**Step 2: Count completed polecats for this swarm**
|
|
||||||
|
|
||||||
Extract from wisp labels: swarm_id, total, completed, start timestamp.
|
|
||||||
Check how many cleanup wisps have been closed for this swarm's polecats.
|
|
||||||
|
|
||||||
**Step 3: If all complete, notify Mayor**
|
|
||||||
```bash
|
|
||||||
gt mail send mayor/ -s "SWARM_COMPLETE: <swarm_id>" -m "All <total> polecats merged.
|
|
||||||
Duration: <minutes> minutes
|
|
||||||
Swarm: <swarm_id>"
|
|
||||||
|
|
||||||
# Close the swarm tracking wisp
|
|
||||||
bd close <swarm-wisp-id> --reason "All polecats merged"
|
|
||||||
```
|
|
||||||
|
|
||||||
Note: Runs every patrol cycle. Notification sent exactly once when all complete."""
|
|
||||||
|
|
||||||
[[steps]]
|
[[steps]]
|
||||||
id = "ping-deacon"
|
description = "Send WITNESS_PING to Deacon for second-order monitoring.\n\nThe Witness fleet collectively monitors Deacon health - this prevents the\n\"who watches the watchers\" problem. If Deacon dies, Witnesses detect it.\n\n**Step 1: Send ping**\n```bash\ngt mail send deacon/ -s \"WITNESS_PING <rig>\" -m \"Rig: <rig>\nTimestamp: $(date -u +%Y-%m-%dT%H:%M:%SZ)\nPatrol: <cycle-number>\"\n```\n\n**Step 2: Check Deacon health**\n```bash\n# Check Deacon agent bead for last_activity\nbd list --type=agent --json | jq '.[] | select(.description | contains(\"deacon\"))'\n```\n\nLook at the `last_activity` timestamp. If stale (>5 minutes since last update):\n- Deacon may be dead or stuck\n\n**Step 3: Escalate if needed**\n```bash\n# If Deacon appears down\ngt mail send mayor/ -s \"ALERT: Deacon appears unresponsive\" -m \"No Deacon activity for >5 minutes.\nLast seen: <timestamp>\nWitness: <rig>/witness\"\n```\n\nNote: Multiple Witnesses may send this alert. Mayor should handle deduplication."
|
||||||
title = "Ping Deacon for health check"
|
id = 'ping-deacon'
|
||||||
needs = ["check-swarm-completion"]
|
needs = ['check-swarm-completion']
|
||||||
description = """
|
title = 'Ping Deacon for health check'
|
||||||
Send WITNESS_PING to Deacon for second-order monitoring.
|
|
||||||
|
|
||||||
The Witness fleet collectively monitors Deacon health - this prevents the
|
|
||||||
"who watches the watchers" problem. If Deacon dies, Witnesses detect it.
|
|
||||||
|
|
||||||
**Step 1: Send ping**
|
|
||||||
```bash
|
|
||||||
gt mail send deacon/ -s "WITNESS_PING <rig>" -m "Rig: <rig>
|
|
||||||
Timestamp: $(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
||||||
Patrol: <cycle-number>"
|
|
||||||
```
|
|
||||||
|
|
||||||
**Step 2: Check Deacon health**
|
|
||||||
```bash
|
|
||||||
# Check Deacon agent bead for last_activity
|
|
||||||
bd list --type=agent --json | jq '.[] | select(.description | contains("deacon"))'
|
|
||||||
```
|
|
||||||
|
|
||||||
Look at the `last_activity` timestamp. If stale (>5 minutes since last update):
|
|
||||||
- Deacon may be dead or stuck
|
|
||||||
|
|
||||||
**Step 3: Escalate if needed**
|
|
||||||
```bash
|
|
||||||
# If Deacon appears down
|
|
||||||
gt mail send mayor/ -s "ALERT: Deacon appears unresponsive" \
|
|
||||||
-m "No Deacon activity for >5 minutes.
|
|
||||||
Last seen: <timestamp>
|
|
||||||
Witness: <rig>/witness"
|
|
||||||
```
|
|
||||||
|
|
||||||
Note: Multiple Witnesses may send this alert. Mayor should handle deduplication."""
|
|
||||||
|
|
||||||
[[steps]]
|
[[steps]]
|
||||||
id = "context-check"
|
description = "Verify inbox hygiene before ending patrol cycle.\n\n**Step 1: Check inbox state**\n```bash\ngt mail inbox\n```\n\nInbox should contain ONLY:\n- Unprocessed messages (just arrived, will handle next cycle)\n- Active work markers (POLECAT_DONE waiting for MERGED confirmation)\n\n**Step 2: Archive any stale messages**\n\nLook for messages that were processed but not archived:\n- POLECAT_STARTED older than this cycle → archive\n- HELP/Blocked that was escalated → archive\n- SWARM_START that created tracking wisp → archive\n\n```bash\n# For each stale message found:\ngt mail archive <message-id>\n```\n\n**Step 3: Verify cleanup wisp hygiene**\n\nCheck that all cleanup wisps are in valid states:\n```bash\nbd list --wisp --labels=cleanup --status=open\n```\n\n- state:pending → Will be processed next cycle\n- state:merge-requested → Waiting for refinery\n\nIf any cleanup wisp is older than expected (>1 hour in merge-requested state),\nthe refinery may be stuck. This was checked in check-refinery step.\n\n**Goal**: Inbox should have ≤5 active messages at end of cycle."
|
||||||
title = "Check own context limit"
|
id = 'patrol-cleanup'
|
||||||
needs = ["ping-deacon"]
|
needs = ['ping-deacon']
|
||||||
description = """
|
title = 'End-of-cycle inbox hygiene'
|
||||||
Check own context usage.
|
|
||||||
|
|
||||||
If context is HIGH (>80%):
|
|
||||||
- Ensure any notes are written to handoff mail
|
|
||||||
- Prepare for session restart
|
|
||||||
|
|
||||||
If context is LOW:
|
|
||||||
- Can continue patrolling"""
|
|
||||||
|
|
||||||
[[steps]]
|
[[steps]]
|
||||||
id = "loop-or-exit"
|
description = "Check own context usage.\n\nIf context is HIGH (>80%):\n- Ensure any notes are written to handoff mail\n- Prepare for session restart\n\nIf context is LOW:\n- Can continue patrolling"
|
||||||
title = "Loop or exit for respawn"
|
id = 'context-check'
|
||||||
needs = ["context-check"]
|
needs = ['patrol-cleanup']
|
||||||
description = """
|
title = 'Check own context limit'
|
||||||
End of patrol cycle decision.
|
|
||||||
|
|
||||||
**If context LOW**:
|
[[steps]]
|
||||||
- Sleep briefly to avoid tight loop (30-60 seconds)
|
description = "End of patrol cycle decision.\n\n**If context LOW**:\n- Sleep briefly to avoid tight loop (30-60 seconds)\n- Return to inbox-check step\n- Continue patrolling\n\n**If context HIGH**:\n- Write handoff mail to self with any notable observations:\n```bash\ngt handoff -s \"Witness patrol handoff\" -m \"<observations>\"\n```\n- Exit cleanly (daemon respawns fresh Witness)\n\nThe daemon ensures Witness is always running."
|
||||||
- Return to inbox-check step
|
id = 'loop-or-exit'
|
||||||
- Continue patrolling
|
needs = ['context-check']
|
||||||
|
title = 'Loop or exit for respawn'
|
||||||
**If context HIGH**:
|
|
||||||
- Write handoff mail to self with any notable observations:
|
|
||||||
```bash
|
|
||||||
gt handoff -s "Witness patrol handoff" -m "<observations>"
|
|
||||||
```
|
|
||||||
- Exit cleanly (daemon respawns fresh Witness)
|
|
||||||
|
|
||||||
The daemon ensures Witness is always running."""
|
|
||||||
|
|||||||
Reference in New Issue
Block a user