feat: Add swarm dispatch to Witness patrol (gt-kc7yj.2)
Add dispatch-swarm-work step to mol-witness-patrol formula that: - Queries bd ready --parent=<epic> to find ready swarm tasks - Finds idle polecats via agent beads - Dispatches work via gt sling <task> <rig>/<polecat> Also updated: - SWARM mail handling to store epic_id in labels - check-swarm-completion to use beads discovery model 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -78,14 +78,16 @@ gt mail send mayor/ -s "Escalation: <polecat> needs help" -m "<details>"
|
|||||||
**HANDOFF**:
|
**HANDOFF**:
|
||||||
Read predecessor context. Continue from where they left off.
|
Read predecessor context. Continue from where they left off.
|
||||||
|
|
||||||
**SWARM_START**:
|
**SWARM: <epic-id>** (subject pattern):
|
||||||
Mayor initiating batch polecat work. Initialize swarm tracking.
|
Mayor assigning swarm coordination. The epic IS the swarm - beads tracks everything.
|
||||||
```bash
|
```bash
|
||||||
# Parse swarm info from mail body: {"swarm_id": "batch-123", "beads": ["bd-a", "bd-b"]}
|
# Parse epic ID from subject: "SWARM: gt-epic-123"
|
||||||
bd create --wisp --title "swarm:<swarm_id>" \
|
# Create tracking wisp with epic_id in labels for dispatch step
|
||||||
--description "Tracking batch: <swarm_id>" \
|
bd create --wisp --title "swarm:<epic_id>" \
|
||||||
--labels swarm,swarm_id:<swarm_id>,total:<N>,completed:0,start:<timestamp>
|
--description "Coordinating swarm for epic: <epic_id>" \
|
||||||
|
--labels swarm,epic_id:<epic_id>,start:<timestamp>
|
||||||
```
|
```
|
||||||
|
The dispatch-swarm-work step will use epic_id to query `bd ready --parent=<epic_id>`.
|
||||||
Mark mail as read."""
|
Mark mail as read."""
|
||||||
|
|
||||||
[[steps]]
|
[[steps]]
|
||||||
@@ -267,11 +269,81 @@ gt mail send mayor/ -s "Escalation: <polecat> stuck" \\
|
|||||||
**ZFC Principle**: Trust agent_state from beads. Don't infer state from PID/tmux."""
|
**ZFC Principle**: Trust agent_state from beads. Don't infer state from PID/tmux."""
|
||||||
|
|
||||||
[[steps]]
|
[[steps]]
|
||||||
id = "check-swarm-completion"
|
id = "dispatch-swarm-work"
|
||||||
title = "Check if active swarm is complete"
|
title = "Dispatch ready swarm tasks to idle polecats"
|
||||||
needs = ["survey-workers"]
|
needs = ["survey-workers"]
|
||||||
description = """
|
description = """
|
||||||
If Mayor started a batch (SWARM_START), check if all polecats have completed.
|
If an active swarm exists, dispatch ready tasks to available polecats.
|
||||||
|
|
||||||
|
This is the core swarm coordination logic - the Witness keeps the swarm moving
|
||||||
|
by matching ready issues with idle workers.
|
||||||
|
|
||||||
|
**Step 1: Find active swarm tracking wisps**
|
||||||
|
```bash
|
||||||
|
bd list --wisp --labels=swarm --status=open
|
||||||
|
```
|
||||||
|
If no active swarm, skip this step entirely.
|
||||||
|
|
||||||
|
**Step 2: For each swarm, get ready tasks**
|
||||||
|
|
||||||
|
Extract the epic_id from the swarm wisp (stored in labels or description).
|
||||||
|
Then query the ready front:
|
||||||
|
```bash
|
||||||
|
bd ready --parent=<epic_id>
|
||||||
|
```
|
||||||
|
This returns issues that have no blockers and are ready to work.
|
||||||
|
|
||||||
|
**Step 3: Find idle polecats**
|
||||||
|
|
||||||
|
From the survey-workers step, you should have agent beads data.
|
||||||
|
Filter for polecats where:
|
||||||
|
- agent_state = idle (no work assigned)
|
||||||
|
- OR hook_bead is empty/none
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bd list --type=agent --json | jq '[.[] | select(.description | contains("role_type: polecat") and contains("agent_state: idle"))]'
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 4: Dispatch ready tasks to idle polecats**
|
||||||
|
|
||||||
|
For each ready task (up to number of idle polecats):
|
||||||
|
```bash
|
||||||
|
gt sling <task-id> <rig>/<polecat-name>
|
||||||
|
```
|
||||||
|
|
||||||
|
This will:
|
||||||
|
- Attach the task to the polecat's hook
|
||||||
|
- Spawn the polecat session if not running
|
||||||
|
- Inject work context
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```bash
|
||||||
|
# Ready task: gt-abc.3, Idle polecat: toast
|
||||||
|
gt sling gt-abc.3 gastown/toast
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 5: Log dispatch activity**
|
||||||
|
|
||||||
|
For observability, record what was dispatched:
|
||||||
|
```bash
|
||||||
|
# Update swarm wisp with dispatch info (optional)
|
||||||
|
echo "Dispatched gt-abc.3 -> toast at $(date)"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Parallelism**: Dispatch calls can be parallelized if multiple idle polecats
|
||||||
|
and multiple ready tasks exist.
|
||||||
|
|
||||||
|
**Rate limiting**: Don't dispatch more than max_active_polecats (typically 3-5)
|
||||||
|
to avoid overwhelming the rig/refinery."""
|
||||||
|
|
||||||
|
[[steps]]
|
||||||
|
id = "check-swarm-completion"
|
||||||
|
title = "Check if active swarm is complete"
|
||||||
|
needs = ["dispatch-swarm-work"]
|
||||||
|
description = """
|
||||||
|
Check if any active swarm has completed (all tasks closed).
|
||||||
|
|
||||||
|
**Discovery over tracking**: Don't count - QUERY. The beads state IS the source of truth.
|
||||||
|
|
||||||
**Step 1: Find active swarm tracking wisps**
|
**Step 1: Find active swarm tracking wisps**
|
||||||
```bash
|
```bash
|
||||||
@@ -279,22 +351,42 @@ bd list --wisp --labels=swarm --status=open
|
|||||||
```
|
```
|
||||||
If no active swarm, skip this step.
|
If no active swarm, skip this step.
|
||||||
|
|
||||||
**Step 2: Count completed polecats for this swarm**
|
**Step 2: For each swarm, check completion via beads**
|
||||||
|
|
||||||
Extract from wisp labels: swarm_id, total, completed, start timestamp.
|
Extract epic_id from wisp labels, then check if all children are closed:
|
||||||
Check how many cleanup wisps have been closed for this swarm's polecats.
|
|
||||||
|
|
||||||
**Step 3: If all complete, notify Mayor**
|
|
||||||
```bash
|
```bash
|
||||||
gt mail send mayor/ -s "SWARM_COMPLETE: <swarm_id>" -m "All <total> polecats merged.
|
# Get swarm status from beads (shows ready/active/blocked/completed)
|
||||||
Duration: <minutes> minutes
|
bd swarm status <epic_id> --json
|
||||||
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."""
|
Swarm is complete when:
|
||||||
|
- ready = [] (empty)
|
||||||
|
- active = [] (empty)
|
||||||
|
- blocked = [] (empty)
|
||||||
|
- All children are closed
|
||||||
|
|
||||||
|
Alternative direct check:
|
||||||
|
```bash
|
||||||
|
# If bd ready --parent returns nothing AND no active issues, swarm is done
|
||||||
|
bd ready --parent=<epic_id> # Should return nothing
|
||||||
|
bd list --parent=<epic_id> --status=in_progress # Should return nothing
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 3: If complete, notify Mayor and close swarm**
|
||||||
|
```bash
|
||||||
|
gt mail send mayor/ -s "SWARM_COMPLETE: <epic_id>" -m "Epic: <epic_id>
|
||||||
|
All tasks completed and merged.
|
||||||
|
Duration: <minutes> minutes"
|
||||||
|
|
||||||
|
# Close the swarm tracking wisp
|
||||||
|
bd close <swarm-wisp-id> --reason "All tasks completed"
|
||||||
|
|
||||||
|
# Optionally close the epic itself (or leave for Mayor to close)
|
||||||
|
# bd close <epic_id> --reason "Swarm complete"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key insight**: Notification sent exactly once because the wisp gets closed.
|
||||||
|
Next patrol cycle finds no open swarm wisps, so this step is skipped."""
|
||||||
|
|
||||||
[[steps]]
|
[[steps]]
|
||||||
id = "ping-deacon"
|
id = "ping-deacon"
|
||||||
|
|||||||
Reference in New Issue
Block a user