Simplify Witness patrol: linear + Task tool, no Christmas Ornament (gt-p3v5n)
Design pivot: - Remove mol-polecat-arm and dynamic bonding pattern - Replace with linear patrol (Deacon-style) + Task tool parallelism - Cleanup wisps as finalizers (marker wisp = pending cleanup) - Discovery over tracking (no persistent nudge counts) New docs: - polecat-lifecycle.md: step-based restart model, evolution path - witness-patrol-design.md: simplified, terse Closed obsolete issues: gt-p3v5n.1 through gt-p3v5n.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:
@@ -1,50 +0,0 @@
|
||||
{
|
||||
"formula": "mol-polecat-arm",
|
||||
"description": "Single polecat inspection and action cycle.\n\nThis molecule is bonded dynamically by mol-witness-patrol's survey-workers step. Each polecat being monitored gets one arm that runs in parallel with other arms.\n\n## Variables\n\n| Variable | Required | Description |\n|----------|----------|-------------|\n| polecat_name | Yes | Name of the polecat to inspect |\n| rig | Yes | Rig containing the polecat |\n| nudge_text | No | Text to send when nudging (default: \"How's progress?...\") |",
|
||||
"version": 1,
|
||||
"vars": {
|
||||
"polecat_name": {
|
||||
"required": true,
|
||||
"description": "Name of the polecat to inspect"
|
||||
},
|
||||
"rig": {
|
||||
"required": true,
|
||||
"description": "Rig containing the polecat"
|
||||
},
|
||||
"nudge_text": {
|
||||
"default": "How's progress? Need any help?",
|
||||
"description": "Text to send when nudging the polecat"
|
||||
}
|
||||
},
|
||||
"steps": [
|
||||
{
|
||||
"id": "capture",
|
||||
"title": "Capture polecat state",
|
||||
"description": "Capture recent tmux output for {{polecat_name}}.\n\n```bash\ntmux capture-pane -t gt-{{rig}}-{{polecat_name}} -p | tail -50\n```\n\nRecord:\n- Last activity timestamp (when was last tool call?)\n- Visible errors or stack traces\n- Completion indicators (\"Done\", \"Finished\", etc.)"
|
||||
},
|
||||
{
|
||||
"id": "assess",
|
||||
"title": "Assess work status",
|
||||
"needs": ["capture"],
|
||||
"description": "Categorize polecat state based on captured output.\n\nStates:\n- **working**: Recent tool calls, active processing\n- **idle**: At prompt, no recent activity\n- **error**: Showing errors or stack traces\n- **requesting_shutdown**: Sent LIFECYCLE/Shutdown mail\n- **done**: Showing completion indicators\n\nCalculate: minutes since last activity."
|
||||
},
|
||||
{
|
||||
"id": "load-history",
|
||||
"title": "Load intervention history",
|
||||
"needs": ["assess"],
|
||||
"description": "Read nudge history for {{polecat_name}} from patrol state.\n\n```\nnudge_count = state.nudges[{{polecat_name}}].count\nlast_nudge_time = state.nudges[{{polecat_name}}].timestamp\n```\n\nThis data was loaded by the parent patrol's load-state step and passed to the arm via the bonding context."
|
||||
},
|
||||
{
|
||||
"id": "decide",
|
||||
"title": "Decide intervention action",
|
||||
"needs": ["load-history"],
|
||||
"description": "Apply the nudge matrix to determine action for {{polecat_name}}.\n\n| State | Idle Time | Nudge Count | Action |\n|-------|-----------|-------------|--------|\n| working | any | any | none |\n| idle | <10min | any | none |\n| idle | 10-15min | 0 | nudge-1 (gentle) |\n| idle | 15-20min | 1 | nudge-2 (direct) |\n| idle | 20+min | 2 | nudge-3 (final) |\n| idle | any | 3 | escalate |\n| error | any | any | assess-severity |\n| requesting_shutdown | any | any | pre-kill-verify |\n| done | any | any | pre-kill-verify |\n\nNudge text:\n1. \"How's progress? Need any help?\"\n2. \"Please wrap up soon. What's blocking you?\"\n3. \"Final check. Will escalate in 5 min if no response.\"\n\nRecord decision and rationale."
|
||||
},
|
||||
{
|
||||
"id": "execute",
|
||||
"title": "Execute intervention",
|
||||
"needs": ["decide"],
|
||||
"description": "Take the decided action for {{polecat_name}}.\n\n**nudge-N**:\n```bash\ntmux send-keys -t gt-{{rig}}-{{polecat_name}} \"{{nudge_text}}\" Enter\n```\n\n**pre-kill-verify**:\n```bash\ncd polecats/{{polecat_name}}\ngit status # Must be clean\ngit log origin/main..HEAD # Check for unpushed\nbd show <assigned-issue> # Verify closed/deferred\n\n# Verify productive work (for 'done' closures)\ngit log --oneline --grep='<issue-id>' | head -1\n```\n\n**Commit verification** (ZFC principle - agent makes the call):\n- If issue closed as 'done' but no commits reference it → flag for review\n- Legitimate exceptions: already fixed elsewhere, duplicate, deferred\n- Agent must provide justification if closing without commits\n\nIf clean: kill session, remove worktree, delete branch\nIf dirty: record failure, retry next cycle\n\n**escalate**:\n```bash\ngt mail send mayor/ -s \"Escalation: {{polecat_name}} stuck\" -m \"...\"\n```\n\n**none**: No action needed.\n\nRecord: action taken, result, updated nudge count.\n\n## Output\n\nThe arm completes with:\n- action_taken: none | nudge-1 | nudge-2 | nudge-3 | killed | escalated\n- result: success | failed | pending\n- updated_state: New nudge count and timestamp for {{polecat_name}}\n\nThis data feeds back to the parent patrol's aggregate step."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,145 +0,0 @@
|
||||
description = """
|
||||
Single polecat inspection and action cycle.
|
||||
|
||||
This molecule is bonded dynamically by mol-witness-patrol's survey-workers step. Each polecat being monitored gets one arm that runs in parallel with other arms.
|
||||
|
||||
## Variables
|
||||
|
||||
| Variable | Required | Description |
|
||||
|----------|----------|-------------|
|
||||
| polecat_name | Yes | Name of the polecat to inspect |
|
||||
| rig | Yes | Rig containing the polecat |
|
||||
| nudge_text | No | Text to send when nudging (default: \"How's progress?...\") |"""
|
||||
formula = "mol-polecat-arm"
|
||||
version = 1
|
||||
|
||||
[[steps]]
|
||||
description = """
|
||||
Capture recent tmux output for {{polecat_name}}.
|
||||
|
||||
```bash
|
||||
tmux capture-pane -t gt-{{rig}}-{{polecat_name}} -p | tail -50
|
||||
```
|
||||
|
||||
Record:
|
||||
- Last activity timestamp (when was last tool call?)
|
||||
- Visible errors or stack traces
|
||||
- Completion indicators (\"Done\", \"Finished\", etc.)"""
|
||||
id = "capture"
|
||||
title = "Capture polecat state"
|
||||
|
||||
[[steps]]
|
||||
description = """
|
||||
Categorize polecat state based on captured output.
|
||||
|
||||
States:
|
||||
- **working**: Recent tool calls, active processing
|
||||
- **idle**: At prompt, no recent activity
|
||||
- **error**: Showing errors or stack traces
|
||||
- **requesting_shutdown**: Sent LIFECYCLE/Shutdown mail
|
||||
- **done**: Showing completion indicators
|
||||
|
||||
Calculate: minutes since last activity."""
|
||||
id = "assess"
|
||||
needs = ["capture"]
|
||||
title = "Assess work status"
|
||||
|
||||
[[steps]]
|
||||
description = """
|
||||
Read nudge history for {{polecat_name}} from patrol state.
|
||||
|
||||
```
|
||||
nudge_count = state.nudges[{{polecat_name}}].count
|
||||
last_nudge_time = state.nudges[{{polecat_name}}].timestamp
|
||||
```
|
||||
|
||||
This data was loaded by the parent patrol's load-state step and passed to the arm via the bonding context."""
|
||||
id = "load-history"
|
||||
needs = ["assess"]
|
||||
title = "Load intervention history"
|
||||
|
||||
[[steps]]
|
||||
description = """
|
||||
Apply the nudge matrix to determine action for {{polecat_name}}.
|
||||
|
||||
| State | Idle Time | Nudge Count | Action |
|
||||
|-------|-----------|-------------|--------|
|
||||
| working | any | any | none |
|
||||
| idle | <10min | any | none |
|
||||
| idle | 10-15min | 0 | nudge-1 (gentle) |
|
||||
| idle | 15-20min | 1 | nudge-2 (direct) |
|
||||
| idle | 20+min | 2 | nudge-3 (final) |
|
||||
| idle | any | 3 | escalate |
|
||||
| error | any | any | assess-severity |
|
||||
| requesting_shutdown | any | any | pre-kill-verify |
|
||||
| done | any | any | pre-kill-verify |
|
||||
|
||||
Nudge text:
|
||||
1. \"How's progress? Need any help?\"
|
||||
2. \"Please wrap up soon. What's blocking you?\"
|
||||
3. \"Final check. Will escalate in 5 min if no response.\"
|
||||
|
||||
Record decision and rationale."""
|
||||
id = "decide"
|
||||
needs = ["load-history"]
|
||||
title = "Decide intervention action"
|
||||
|
||||
[[steps]]
|
||||
description = """
|
||||
Take the decided action for {{polecat_name}}.
|
||||
|
||||
**nudge-N**:
|
||||
```bash
|
||||
tmux send-keys -t gt-{{rig}}-{{polecat_name}} \"{{nudge_text}}\" Enter
|
||||
```
|
||||
|
||||
**pre-kill-verify**:
|
||||
```bash
|
||||
cd polecats/{{polecat_name}}
|
||||
git status # Must be clean
|
||||
git log origin/main..HEAD # Check for unpushed
|
||||
bd show <assigned-issue> # Verify closed/deferred
|
||||
|
||||
# Verify productive work (for 'done' closures)
|
||||
git log --oneline --grep='<issue-id>' | head -1
|
||||
```
|
||||
|
||||
**Commit verification** (ZFC principle - agent makes the call):
|
||||
- If issue closed as 'done' but no commits reference it → flag for review
|
||||
- Legitimate exceptions: already fixed elsewhere, duplicate, deferred
|
||||
- Agent must provide justification if closing without commits
|
||||
|
||||
If clean: kill session, remove worktree, delete branch
|
||||
If dirty: record failure, retry next cycle
|
||||
|
||||
**escalate**:
|
||||
```bash
|
||||
gt mail send mayor/ -s \"Escalation: {{polecat_name}} stuck\" -m \"...\"
|
||||
```
|
||||
|
||||
**none**: No action needed.
|
||||
|
||||
Record: action taken, result, updated nudge count.
|
||||
|
||||
## Output
|
||||
|
||||
The arm completes with:
|
||||
- action_taken: none | nudge-1 | nudge-2 | nudge-3 | killed | escalated
|
||||
- result: success | failed | pending
|
||||
- updated_state: New nudge count and timestamp for {{polecat_name}}
|
||||
|
||||
This data feeds back to the parent patrol's aggregate step."""
|
||||
id = "execute"
|
||||
needs = ["decide"]
|
||||
title = "Execute intervention"
|
||||
|
||||
[vars]
|
||||
[vars.nudge_text]
|
||||
default = "How's progress? Need any help?"
|
||||
description = "Text to send when nudging the polecat"
|
||||
[vars.polecat_name]
|
||||
description = "Name of the polecat to inspect"
|
||||
required = true
|
||||
[vars.rig]
|
||||
description = "Rig containing the polecat"
|
||||
required = true
|
||||
@@ -1,61 +0,0 @@
|
||||
{
|
||||
"formula": "mol-witness-patrol",
|
||||
"description": "Per-rig worker monitor patrol loop using the Christmas Ornament pattern.\n\nThe 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.\n\n**You do NOT do implementation work.** Your job is oversight, not coding.\n\nThis molecule uses dynamic bonding to spawn mol-polecat-arm for each worker, enabling parallel inspection with a fanout gate for aggregation.\n\n## The Christmas Ornament Shape\n\n```\n ★ mol-witness-patrol (trunk)\n /|\\\n ┌────────┘ │ └────────┐\n PREFLIGHT DISCOVERY CLEANUP\n │ │ │\n inbox-check survey aggregate (WaitsFor: all-children)\n check-refnry │ save-state\n load-state │ generate-summary\n ↓ context-check\n ┌───────┼───────┐ burn-or-loop\n ● ● ● mol-polecat-arm (dynamic)\n ace nux toast\n```",
|
||||
"version": 1,
|
||||
"steps": [
|
||||
{
|
||||
"id": "inbox-check",
|
||||
"title": "Process witness mail",
|
||||
"description": "Process witness mail: lifecycle requests, help requests.\n\n```bash\ngt mail inbox\n```\n\nHandle by message type:\n- **LIFECYCLE/Shutdown**: Queue for pre-kill verification\n- **Blocked/Help**: Assess if resolvable or escalate\n- **HANDOFF**: Load predecessor state\n- **Work complete**: Verify issue closed, proceed to pre-kill\n\nRecord any pending actions for later steps. Mark messages as processed when complete."
|
||||
},
|
||||
{
|
||||
"id": "check-refinery",
|
||||
"title": "Ensure refinery is alive",
|
||||
"needs": ["inbox-check"],
|
||||
"description": "Ensure the refinery is alive and processing merge requests.\n\n**Redundant system**: This check runs in both gt spawn and Witness patrol to ensure the merge queue processor stays operational.\n\n```bash\n# Check if refinery session is running\ngt session status <rig>/refinery\n\n# Check for merge requests in queue\nbd list --type=merge-request --status=open\n```\n\nIf merge requests are waiting AND refinery is 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 is running but queue is non-empty for >30 min, send nudge. This ensures polecats don't wait forever for their branches to merge."
|
||||
},
|
||||
{
|
||||
"id": "load-state",
|
||||
"title": "Load persisted patrol state",
|
||||
"needs": ["check-refinery"],
|
||||
"description": "Read handoff bead and get nudge counts.\n\nLoad persistent state from the witness handoff bead:\n- Active workers and their status from last cycle\n- Nudge counts per worker per issue\n- Last nudge timestamps\n- Pending escalations\n\n```bash\nbd show <handoff-bead-id>\n```\n\nIf no handoff exists (fresh start), initialize empty state. This state persists across wisp burns and session cycles."
|
||||
},
|
||||
{
|
||||
"id": "survey-workers",
|
||||
"title": "Survey all polecats (fanout)",
|
||||
"needs": ["load-state"],
|
||||
"description": "List polecats and bond mol-polecat-arm for each one.\n\n```bash\n# Get list of polecats\ngt polecat list <rig>\n```\n\nFor each polecat discovered, dynamically bond an inspection arm:\n\n```bash\n# Bond mol-polecat-arm for each polecat\nfor polecat in $(gt polecat list <rig> --names); do\n bd mol bond mol-polecat-arm $PATROL_WISP_ID \\\n --ref arm-$polecat \\\n --var polecat_name=$polecat \\\n --var rig=<rig>\ndone\n```\n\nThis creates child wisps like:\n- patrol-x7k.arm-ace (5 steps)\n- patrol-x7k.arm-nux (5 steps)\n- patrol-x7k.arm-toast (5 steps)\n\nEach arm runs in PARALLEL. The aggregate step will wait for all to complete.\n\nIf no polecats are found, this step completes immediately with no children."
|
||||
},
|
||||
{
|
||||
"id": "aggregate",
|
||||
"title": "Aggregate arm results",
|
||||
"needs": ["survey-workers"],
|
||||
"waits_for": "all-children",
|
||||
"description": "Collect outcomes from all polecat inspection arms.\n\nThis is a **fanout gate** - it cannot proceed until ALL dynamically-bonded polecat arms have completed their inspection cycles.\n\nOnce all arms complete, collect their outcomes:\n- Actions taken per polecat (nudge, kill, escalate, none)\n- Updated nudge counts\n- Any errors or issues discovered\n\nBuild the consolidated state for save-state."
|
||||
},
|
||||
{
|
||||
"id": "save-state",
|
||||
"title": "Persist patrol state",
|
||||
"needs": ["aggregate"],
|
||||
"description": "Update handoff bead with new states.\n\nPersist state to the witness handoff bead:\n- Updated worker statuses from all arms\n- Current nudge counts per worker\n- Nudge timestamps\n- Actions taken this cycle\n- Pending items for next cycle\n\n```bash\nbd update <handoff-bead-id> --description=\"<serialized state>\"\n```\n\nThis state survives wisp burns and session cycles."
|
||||
},
|
||||
{
|
||||
"id": "generate-summary",
|
||||
"title": "Generate handoff summary",
|
||||
"needs": ["save-state"],
|
||||
"description": "Summarize this patrol cycle for digest.\n\nInclude:\n- Workers inspected (count, names)\n- Nudges sent (count, to whom)\n- Sessions killed (count, names)\n- Escalations (count, issues)\n- Issues found (brief descriptions)\n- Actions pending for next cycle\n\nThis becomes the digest when the patrol wisp is squashed."
|
||||
},
|
||||
{
|
||||
"id": "context-check",
|
||||
"title": "Check own context limit",
|
||||
"needs": ["generate-summary"],
|
||||
"description": "Check own context usage.\n\nIf context is HIGH (>80%):\n- Ensure state is saved to handoff bead\n- Prepare for burn/respawn\n\nIf context is LOW:\n- Can continue patrolling"
|
||||
},
|
||||
{
|
||||
"id": "burn-or-loop",
|
||||
"title": "Burn and respawn or loop",
|
||||
"needs": ["context-check"],
|
||||
"description": "End of patrol cycle decision.\n\nIf context is LOW:\n- Burn this wisp (no audit trail needed for patrol cycles)\n- Sleep briefly to avoid tight loop (30-60 seconds)\n- Return to inbox-check step\n\nIf context is HIGH:\n- Burn wisp with summary digest\n- Exit cleanly (daemon will respawn fresh Witness)\n\n```bash\nbd mol burn # Destroy ephemeral wisp\n```\n\nThe daemon ensures Witness is always running."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,212 +1,225 @@
|
||||
description = """
|
||||
Per-rig worker monitor patrol loop using the Christmas Ornament pattern.
|
||||
Per-rig worker monitor patrol loop.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
This molecule uses dynamic bonding to spawn mol-polecat-arm for each worker, enabling parallel inspection with a fanout gate for aggregation.
|
||||
## Design Philosophy
|
||||
|
||||
## The Christmas Ornament Shape
|
||||
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)
|
||||
|
||||
```
|
||||
★ mol-witness-patrol (trunk)
|
||||
/|\\
|
||||
┌────────┘ │ └────────┐
|
||||
PREFLIGHT DISCOVERY CLEANUP
|
||||
│ │ │
|
||||
inbox-check survey aggregate (WaitsFor: all-children)
|
||||
check-refnry │ save-state
|
||||
load-state │ generate-summary
|
||||
↓ context-check
|
||||
┌───────┼───────┐ burn-or-loop
|
||||
● ● ● mol-polecat-arm (dynamic)
|
||||
ace nux toast
|
||||
```"""
|
||||
inbox-check ─► process-cleanups ─► check-refinery ─► survey-workers
|
||||
│
|
||||
┌──────────────────────────────────────────────────┘
|
||||
▼
|
||||
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]]
|
||||
id = "inbox-check"
|
||||
title = "Process witness mail"
|
||||
description = """
|
||||
Process witness mail: lifecycle requests, help requests.
|
||||
Check inbox and handle messages.
|
||||
|
||||
```bash
|
||||
gt mail inbox
|
||||
```
|
||||
|
||||
Handle by message type:
|
||||
- **LIFECYCLE/Shutdown**: Queue for pre-kill verification
|
||||
- **Blocked/Help**: Assess if resolvable or escalate
|
||||
- **HANDOFF**: Load predecessor state
|
||||
- **Work complete**: Verify issue closed, proceed to pre-kill
|
||||
For each message:
|
||||
|
||||
Record any pending actions for later steps. Mark messages as processed when complete."""
|
||||
id = "inbox-check"
|
||||
title = "Process witness mail"
|
||||
**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>
|
||||
```
|
||||
The wisp's existence IS the pending cleanup. Process in next step.
|
||||
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."""
|
||||
|
||||
[[steps]]
|
||||
id = "process-cleanups"
|
||||
title = "Process pending cleanup wisps"
|
||||
needs = ["inbox-check"]
|
||||
description = """
|
||||
Find and process cleanup wisps (the finalizer pattern).
|
||||
|
||||
```bash
|
||||
# Find all cleanup wisps
|
||||
bd list --wisp --labels=cleanup --status=open
|
||||
```
|
||||
|
||||
For each cleanup wisp:
|
||||
|
||||
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 # No unpushed commits
|
||||
bd show <assigned-issue> # Issue closed or deferred
|
||||
```
|
||||
|
||||
3. **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
|
||||
|
||||
4. **If clean**: Execute cleanup
|
||||
```bash
|
||||
gt session kill <rig>/polecats/<name>
|
||||
# Worktree removal handled by session kill
|
||||
```
|
||||
Then burn the cleanup wisp:
|
||||
```bash
|
||||
bd close <wisp-id> # or bd burn <wisp-id>
|
||||
```
|
||||
|
||||
5. **If dirty**: Leave wisp open, log the issue, retry next cycle.
|
||||
|
||||
**Parallelism**: Use Task tool subagents to process multiple cleanups concurrently.
|
||||
Each cleanup is independent - perfect for parallel execution."""
|
||||
|
||||
[[steps]]
|
||||
id = "check-refinery"
|
||||
title = "Ensure refinery is alive"
|
||||
needs = ["process-cleanups"]
|
||||
description = """
|
||||
Ensure the refinery is alive and processing merge requests.
|
||||
|
||||
**Redundant system**: This check runs in both gt spawn and Witness patrol to ensure the merge queue processor stays operational.
|
||||
|
||||
```bash
|
||||
# Check if refinery session is running
|
||||
# Check if refinery session exists
|
||||
gt session status <rig>/refinery
|
||||
|
||||
# Check for merge requests in queue
|
||||
# Check for pending merge requests
|
||||
bd list --type=merge-request --status=open
|
||||
```
|
||||
|
||||
If merge requests are waiting AND refinery is not running:
|
||||
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.\"
|
||||
gt mail send <rig>/refinery -s "PATROL: Wake up" \
|
||||
-m "Merge requests in queue. Please process."
|
||||
```
|
||||
|
||||
If refinery is running but queue is non-empty for >30 min, send nudge. This ensures polecats don't wait forever for their branches to merge."""
|
||||
id = "check-refinery"
|
||||
needs = ["inbox-check"]
|
||||
title = "Ensure refinery is alive"
|
||||
If refinery running but queue stale (>30 min), send nudge."""
|
||||
|
||||
[[steps]]
|
||||
description = """
|
||||
Read handoff bead and get nudge counts.
|
||||
|
||||
Load persistent state from the witness handoff bead:
|
||||
- Active workers and their status from last cycle
|
||||
- Nudge counts per worker per issue
|
||||
- Last nudge timestamps
|
||||
- Pending escalations
|
||||
|
||||
```bash
|
||||
bd show <handoff-bead-id>
|
||||
```
|
||||
|
||||
If no handoff exists (fresh start), initialize empty state. This state persists across wisp burns and session cycles."""
|
||||
id = "load-state"
|
||||
id = "survey-workers"
|
||||
title = "Inspect all active polecats"
|
||||
needs = ["check-refinery"]
|
||||
title = "Load persisted patrol state"
|
||||
|
||||
[[steps]]
|
||||
description = """
|
||||
List polecats and bond mol-polecat-arm for each one.
|
||||
Survey all polecats and take action on stuck workers.
|
||||
|
||||
```bash
|
||||
# Get list of polecats
|
||||
gt polecat list <rig>
|
||||
```
|
||||
|
||||
For each polecat discovered, dynamically bond an inspection arm:
|
||||
**For each polecat, assess:**
|
||||
|
||||
1. **Capture tmux state**:
|
||||
```bash
|
||||
# Bond mol-polecat-arm for each polecat
|
||||
for polecat in $(gt polecat list <rig> --names); do
|
||||
bd mol bond mol-polecat-arm $PATROL_WISP_ID \\
|
||||
--ref arm-$polecat \\
|
||||
--var polecat_name=$polecat \\
|
||||
--var rig=<rig>
|
||||
done
|
||||
tmux capture-pane -t gt-<rig>-<name> -p | tail -50
|
||||
```
|
||||
|
||||
This creates child wisps like:
|
||||
- patrol-x7k.arm-ace (5 steps)
|
||||
- patrol-x7k.arm-nux (5 steps)
|
||||
- patrol-x7k.arm-toast (5 steps)
|
||||
2. **Determine status** (ZFC - you make the judgment):
|
||||
- **working**: Recent tool calls, active processing
|
||||
- **idle**: At prompt, no recent activity
|
||||
- **error**: Showing errors or stack traces
|
||||
- **done**: Showing completion indicators (should have sent POLECAT_DONE)
|
||||
|
||||
Each arm runs in PARALLEL. The aggregate step will wait for all to complete.
|
||||
3. **Check beads progress**:
|
||||
```bash
|
||||
bd hook --agent <rig>/polecats/<name> # What step are they on?
|
||||
```
|
||||
- Has their step changed since you last looked?
|
||||
- How long on current step? (Check step timestamps)
|
||||
|
||||
If no polecats are found, this step completes immediately with no children."""
|
||||
id = "survey-workers"
|
||||
needs = ["load-state"]
|
||||
title = "Survey all polecats (fanout)"
|
||||
4. **Decide action** (fresh judgment each cycle, no counters):
|
||||
|
||||
| Observation | Action |
|
||||
|-------------|--------|
|
||||
| Actively working | None |
|
||||
| Just started step (<5 min) | None |
|
||||
| Idle 5-15 min, same step | Gentle nudge |
|
||||
| Idle 15+ min, same step | Direct nudge with deadline |
|
||||
| Idle 30+ min despite nudges | Escalate to Mayor |
|
||||
| Showing errors | Assess severity, help or escalate |
|
||||
| Says "done" but no POLECAT_DONE | Nudge to send completion mail |
|
||||
|
||||
5. **Execute nudges**:
|
||||
```bash
|
||||
gt nudge <rig>/polecats/<name> "How's progress on <step>? Need help?"
|
||||
```
|
||||
|
||||
6. **Escalate stuck workers**:
|
||||
```bash
|
||||
gt mail send mayor/ -s "Escalation: <polecat> stuck" \
|
||||
-m "Polecat <name> has been idle on <step> for <duration>.
|
||||
Nudges ineffective. Please intervene."
|
||||
```
|
||||
|
||||
**Parallelism**: Use Task tool subagents to inspect multiple polecats concurrently.
|
||||
Launch one subagent per polecat for capture/assess/decide/act cycle.
|
||||
|
||||
**No persistent state**: Each cycle is a fresh observation.
|
||||
"Nudge count" is replaced by "how long stuck on same step" which is discoverable from beads timestamps."""
|
||||
|
||||
[[steps]]
|
||||
description = """
|
||||
Collect outcomes from all polecat inspection arms.
|
||||
|
||||
This is a **fanout gate** - it cannot proceed until ALL dynamically-bonded polecat arms have completed their inspection cycles.
|
||||
|
||||
Once all arms complete, collect their outcomes:
|
||||
- Actions taken per polecat (nudge, kill, escalate, none)
|
||||
- Updated nudge counts
|
||||
- Any errors or issues discovered
|
||||
|
||||
Build the consolidated state for save-state."""
|
||||
id = "aggregate"
|
||||
id = "context-check"
|
||||
title = "Check own context limit"
|
||||
needs = ["survey-workers"]
|
||||
title = "Aggregate arm results"
|
||||
waits_for = "all-children"
|
||||
|
||||
[[steps]]
|
||||
description = """
|
||||
Update handoff bead with new states.
|
||||
|
||||
Persist state to the witness handoff bead:
|
||||
- Updated worker statuses from all arms
|
||||
- Current nudge counts per worker
|
||||
- Nudge timestamps
|
||||
- Actions taken this cycle
|
||||
- Pending items for next cycle
|
||||
|
||||
```bash
|
||||
bd update <handoff-bead-id> --description=\"<serialized state>\"
|
||||
```
|
||||
|
||||
This state survives wisp burns and session cycles."""
|
||||
id = "save-state"
|
||||
needs = ["aggregate"]
|
||||
title = "Persist patrol state"
|
||||
|
||||
[[steps]]
|
||||
description = """
|
||||
Summarize this patrol cycle for digest.
|
||||
|
||||
Include:
|
||||
- Workers inspected (count, names)
|
||||
- Nudges sent (count, to whom)
|
||||
- Sessions killed (count, names)
|
||||
- Escalations (count, issues)
|
||||
- Issues found (brief descriptions)
|
||||
- Actions pending for next cycle
|
||||
|
||||
This becomes the digest when the patrol wisp is squashed."""
|
||||
id = "generate-summary"
|
||||
needs = ["save-state"]
|
||||
title = "Generate handoff summary"
|
||||
|
||||
[[steps]]
|
||||
description = """
|
||||
Check own context usage.
|
||||
|
||||
If context is HIGH (>80%):
|
||||
- Ensure state is saved to handoff bead
|
||||
- Prepare for burn/respawn
|
||||
- Ensure any notes are written to handoff mail
|
||||
- Prepare for session restart
|
||||
|
||||
If context is LOW:
|
||||
- Can continue patrolling"""
|
||||
id = "context-check"
|
||||
needs = ["generate-summary"]
|
||||
title = "Check own context limit"
|
||||
|
||||
[[steps]]
|
||||
id = "loop-or-exit"
|
||||
title = "Loop or exit for respawn"
|
||||
needs = ["context-check"]
|
||||
description = """
|
||||
End of patrol cycle decision.
|
||||
|
||||
If context is LOW:
|
||||
- Burn this wisp (no audit trail needed for patrol cycles)
|
||||
**If context LOW**:
|
||||
- Sleep briefly to avoid tight loop (30-60 seconds)
|
||||
- Return to inbox-check step
|
||||
- Continue patrolling
|
||||
|
||||
If context is HIGH:
|
||||
- Burn wisp with summary digest
|
||||
- Exit cleanly (daemon will respawn fresh Witness)
|
||||
|
||||
**If context HIGH**:
|
||||
- Write handoff mail to self with any notable observations:
|
||||
```bash
|
||||
bd mol burn # Destroy ephemeral wisp
|
||||
gt handoff -s "Witness patrol handoff" -m "<observations>"
|
||||
```
|
||||
- Exit cleanly (daemon respawns fresh Witness)
|
||||
|
||||
The daemon ensures Witness is always running."""
|
||||
id = "burn-or-loop"
|
||||
needs = ["context-check"]
|
||||
title = "Burn and respawn or loop"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
130
docs/polecat-lifecycle.md
Normal file
130
docs/polecat-lifecycle.md
Normal file
@@ -0,0 +1,130 @@
|
||||
# Polecat Lifecycle
|
||||
|
||||
> Polecats restart after each molecule step. This is intentional.
|
||||
|
||||
## Execution Model
|
||||
|
||||
| Phase | What Happens |
|
||||
|-------|--------------|
|
||||
| **Spawn** | Worktree created, session started, molecule slung to hook |
|
||||
| **Step** | Polecat reads hook, executes ONE step, runs `gt mol step done` |
|
||||
| **Restart** | Session respawns with fresh context, next step on hook |
|
||||
| **Complete** | Last step done → POLECAT_DONE mail → cleanup wisp created |
|
||||
| **Cleanup** | Witness verifies git clean, kills session, burns wisp |
|
||||
|
||||
```
|
||||
spawn → step → restart → step → restart → ... → complete → cleanup
|
||||
└──────────────────────────────────────┘
|
||||
(fresh session each step)
|
||||
```
|
||||
|
||||
## Why Restart Every Step?
|
||||
|
||||
| Reason | Explanation |
|
||||
|--------|-------------|
|
||||
| **Atomicity** | Each step completes fully or not at all |
|
||||
| **No wandering** | Polecat can't half-finish and get distracted |
|
||||
| **Context fresh** | No accumulation of stale context across steps |
|
||||
| **Crash recovery** | Restart = re-read hook = continue from last completed step |
|
||||
|
||||
**Trade-off**: Session restart overhead. Worth it for reliability at current cognition levels.
|
||||
|
||||
## Step Packing (Author Responsibility)
|
||||
|
||||
Formula authors must size steps appropriately:
|
||||
|
||||
| Too Small | Too Large |
|
||||
|-----------|-----------|
|
||||
| Restart overhead dominates | Context exhaustion mid-step |
|
||||
| Thrashing | Partial completion, unreliable |
|
||||
|
||||
**Rule of thumb**: A step should use 30-70% of available context. Batch related micro-tasks.
|
||||
|
||||
## The `gt mol step done` Command
|
||||
|
||||
Canonical way to complete a step:
|
||||
|
||||
```bash
|
||||
gt mol step done <step-id>
|
||||
```
|
||||
|
||||
1. Closes the step in beads
|
||||
2. Finds next ready step (dependency-aware)
|
||||
3. Updates hook to next step
|
||||
4. Respawns pane with fresh session
|
||||
|
||||
**Never use `bd close` directly** - it skips the restart logic.
|
||||
|
||||
## Cleanup: The Finalizer Pattern
|
||||
|
||||
When polecat signals completion:
|
||||
|
||||
```
|
||||
POLECAT_DONE mail → Witness creates cleanup wisp → Witness processes wisp → Burn
|
||||
```
|
||||
|
||||
The wisp's existence IS the pending cleanup. No explicit queue.
|
||||
|
||||
| Cleanup Step | Verification |
|
||||
|--------------|--------------|
|
||||
| Git status | Must be clean |
|
||||
| Unpushed commits | None allowed |
|
||||
| Issue state | Closed or deferred |
|
||||
| Productive work | Commits reference issue (ZFC - Witness judges) |
|
||||
|
||||
Failed cleanup? Leave wisp, retry next cycle.
|
||||
|
||||
---
|
||||
|
||||
## Evolution Path
|
||||
|
||||
Current design will evolve as model cognition improves:
|
||||
|
||||
| Phase | Refresh Trigger | Who Decides | Witness Load |
|
||||
|-------|-----------------|-------------|--------------|
|
||||
| **Now** | Step boundary | Formula (fixed) | High |
|
||||
| **Spoon-feeding** | Context % + task size | Witness | Medium |
|
||||
| **Self-managed** | Self-awareness | Polecat | Low |
|
||||
|
||||
### Now (Step-Based Restart)
|
||||
|
||||
- Restart every step, guaranteed
|
||||
- Conservative, reliable
|
||||
- `gt mol step done` handles everything
|
||||
|
||||
### Spoon-feeding (Future)
|
||||
|
||||
Requires: Claude Code exposes context usage
|
||||
|
||||
```
|
||||
Polecat completes step
|
||||
→ Witness checks: 65% context used
|
||||
→ Next task estimate: 10% context
|
||||
→ Decision: "send another" or "recycle"
|
||||
```
|
||||
|
||||
Witness becomes supervisor, not babysitter.
|
||||
|
||||
### Self-Managed (Future)
|
||||
|
||||
Requires: Model cognition threshold + Gas Town patterns in training
|
||||
|
||||
```
|
||||
Polecat completes step
|
||||
→ Self-assesses: "I'm at 80%, should recycle"
|
||||
→ Runs gt handoff, respawns
|
||||
```
|
||||
|
||||
Polecats become autonomous. Witness becomes auditor.
|
||||
|
||||
---
|
||||
|
||||
## Key Commands
|
||||
|
||||
| Command | Effect |
|
||||
|---------|--------|
|
||||
| `gt mol step done <step>` | Complete step, restart for next |
|
||||
| `gt mol status` | Show what's on hook |
|
||||
| `gt mol progress <mol>` | Show molecule completion state |
|
||||
| `gt done` | Signal POLECAT_DONE to Witness |
|
||||
| `gt handoff` | Write notes, respawn (manual refresh) |
|
||||
@@ -1,147 +1,94 @@
|
||||
# Witness Patrol: Theory of Operation
|
||||
# Witness Patrol Design
|
||||
|
||||
## Overview
|
||||
> The Witness is the Pit Boss. Oversight, not implementation.
|
||||
|
||||
The Witness is the per-rig worker monitor. It watches polecats, nudges them toward
|
||||
completion, verifies clean state before cleanup, and escalates stuck workers.
|
||||
## Core Responsibilities
|
||||
|
||||
**Key principle: Claude-driven execution.** The mol-witness-patrol molecule is a
|
||||
playbook that Claude reads and executes. There is no Go "runtime" that auto-executes
|
||||
steps. Claude provides the intelligence; gt/bd commands provide the primitives.
|
||||
| Duty | Action |
|
||||
|------|--------|
|
||||
| Handle POLECAT_DONE | Create cleanup wisp, process later |
|
||||
| Handle HELP requests | Assess, help or escalate to Mayor |
|
||||
| Ensure refinery alive | Restart if needed |
|
||||
| Survey workers | Detect stuck polecats, nudge or escalate |
|
||||
| Process cleanups | Verify git clean, kill session, burn wisp |
|
||||
|
||||
## Architecture
|
||||
## Patrol Shape (Linear)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ THE AGENT (Claude) │
|
||||
│ │
|
||||
│ Reads molecule steps → Executes commands → Closes atoms │
|
||||
│ Uses TodoWrite for complex atoms (optional) │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ PRIMITIVES (gt, bd CLI) │
|
||||
│ │
|
||||
│ gt mail, gt nudge, gt session, bd close, bd show, etc. │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ COORDINATION (Mail) │
|
||||
│ │
|
||||
│ Polecats → POLECAT_DONE → Witness inbox │
|
||||
│ Witness → "You're stuck" → Polecat (via gt nudge) │
|
||||
│ Witness → Escalation → Mayor inbox │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
inbox-check → process-cleanups → check-refinery → survey-workers → context-check → loop
|
||||
```
|
||||
|
||||
## The Patrol Cycle
|
||||
No dynamic arms. No fanout gates. Simple loop like Deacon.
|
||||
|
||||
Each patrol cycle follows the mol-witness-patrol molecule:
|
||||
## Key Design Principles
|
||||
|
||||
### 1. inbox-check
|
||||
Check mail for lifecycle events:
|
||||
- **POLECAT_DONE**: Polecat finished work, ready for cleanup
|
||||
- **Help requests**: Polecat asking for assistance
|
||||
- **Escalations**: Issues requiring attention
|
||||
| Principle | Meaning |
|
||||
|-----------|---------|
|
||||
| **Discovery over tracking** | Observe reality each cycle, don't maintain state |
|
||||
| **Events over state** | POLECAT_DONE triggers wisps, not queue updates |
|
||||
| **Cleanup wisps as finalizers** | Pending cleanup = wisp exists |
|
||||
| **Task tool for parallelism** | Subagents inspect polecats, not molecule arms |
|
||||
| **Fresh judgment each cycle** | No persistent nudge counters |
|
||||
|
||||
```bash
|
||||
gt mail inbox
|
||||
gt mail read <id>
|
||||
## Cleanup: The Finalizer Pattern
|
||||
|
||||
```
|
||||
POLECAT_DONE arrives
|
||||
↓
|
||||
Create wisp: bd create --wisp --title "cleanup:<polecat>" --labels cleanup
|
||||
↓
|
||||
(wisp exists = cleanup pending)
|
||||
↓
|
||||
Witness process-cleanups step:
|
||||
- Verify: git status clean, no unpushed, issue closed
|
||||
- Execute: gt session kill, worktree removed
|
||||
- Burn wisp
|
||||
↓
|
||||
Failed? Leave wisp, retry next cycle
|
||||
```
|
||||
|
||||
### 2. survey-workers
|
||||
For each polecat in the rig:
|
||||
## Assessing Stuck Polecats
|
||||
|
||||
```bash
|
||||
gt polecat list <rig>
|
||||
With step-based restarts, polecats are either:
|
||||
- **Working a step**: Active tool calls, progress
|
||||
- **Starting a step**: Just respawned, reading hook
|
||||
- **Stuck on a step**: No progress, same step for multiple cycles
|
||||
|
||||
| Observation | Action |
|
||||
|-------------|--------|
|
||||
| Active tool calls | None |
|
||||
| Just started step (<5 min) | None |
|
||||
| Idle 5-15 min, same step | Gentle nudge |
|
||||
| Idle 15+ min, same step | Direct nudge |
|
||||
| Idle 30+ min despite nudges | Escalate to Mayor |
|
||||
| Errors visible | Assess, help or escalate |
|
||||
| Says "done" but no POLECAT_DONE | Nudge to signal completion |
|
||||
|
||||
**No persistent nudge counts**. Each cycle: observe reality, make fresh judgment.
|
||||
|
||||
"How long stuck on same step" is discoverable from beads timestamps.
|
||||
|
||||
## Parallelism via Task Tool
|
||||
|
||||
Inspect multiple polecats concurrently using subagents:
|
||||
|
||||
```markdown
|
||||
## survey-workers step
|
||||
|
||||
For each polecat, launch Task tool subagent:
|
||||
- Capture tmux output
|
||||
- Assess state (working/idle/error/done)
|
||||
- Check beads for step progress
|
||||
- Decide and execute action
|
||||
|
||||
Task tool handles parallelism. One subagent per polecat.
|
||||
```
|
||||
|
||||
For each polecat:
|
||||
1. **Capture**: `tmux capture-pane -t gt-<rig>-<name> -p | tail -50`
|
||||
2. **Assess**: Claude reads output, determines state (working/idle/error/done)
|
||||
3. **Load history**: Read nudge count from handoff bead
|
||||
4. **Decide**: Apply escalation matrix (see below)
|
||||
5. **Execute**: Take action (none, nudge, escalate, cleanup)
|
||||
## Formula
|
||||
|
||||
### 3. save-state
|
||||
Persist state to handoff bead for next cycle:
|
||||
- Nudge counts per polecat
|
||||
- Last nudge timestamps
|
||||
- Pending actions
|
||||
See `.beads/formulas/mol-witness-patrol.formula.toml`
|
||||
|
||||
### 4. burn-or-loop
|
||||
- If context low: sleep briefly, loop back to inbox-check
|
||||
- If context high: exit (daemon respawns fresh Witness)
|
||||
## Related
|
||||
|
||||
## Nudge Escalation Matrix
|
||||
|
||||
The Witness applies escalating pressure to idle polecats:
|
||||
|
||||
| Idle Time | Nudge Count | Action |
|
||||
|-----------|-------------|--------|
|
||||
| <10min | any | none |
|
||||
| 10-15min | 0 | Gentle: "How's progress?" |
|
||||
| 15-20min | 1 | Direct: "Please wrap up. What's blocking?" |
|
||||
| 20+min | 2 | Final: "Will escalate in 5min if no response." |
|
||||
| any | 3 | Escalate to Mayor |
|
||||
|
||||
**Key insight**: Only Claude can assess whether a polecat is truly stuck.
|
||||
Looking at tmux output requires understanding context:
|
||||
- "I'm stuck on this error" → needs help
|
||||
- "Running tests..." → actively working
|
||||
- Sitting at prompt with no activity → maybe stuck
|
||||
|
||||
## State Persistence
|
||||
|
||||
The Witness handoff bead tracks:
|
||||
|
||||
```yaml
|
||||
# In handoff bead description
|
||||
nudges:
|
||||
toast:
|
||||
count: 2
|
||||
last: "2025-12-24T10:30:00Z"
|
||||
ace:
|
||||
count: 0
|
||||
last: null
|
||||
pending_cleanup:
|
||||
- nux # received POLECAT_DONE, queued for verification
|
||||
```
|
||||
|
||||
This survives across patrol cycles and context burns.
|
||||
|
||||
## Polecat Cleanup Flow
|
||||
|
||||
When a polecat signals completion:
|
||||
|
||||
1. Polecat runs `gt done` or sends POLECAT_DONE mail
|
||||
2. Witness receives mail in inbox-check
|
||||
3. Witness runs pre-kill verification:
|
||||
```bash
|
||||
cd polecats/<name>
|
||||
git status # Must be clean
|
||||
git log origin/main.. # Check for unpushed
|
||||
bd show <issue> # Verify closed
|
||||
```
|
||||
4. If clean: kill session, remove worktree, delete branch
|
||||
5. If dirty: send nudge asking polecat to fix state
|
||||
|
||||
## What We DON'T Need
|
||||
|
||||
- **Go patrol runtime**: Claude executes the playbook
|
||||
- **Polling for WaitsFor**: Mail tells us when things are ready
|
||||
- **Automated health checks**: Claude reads tmux, assesses
|
||||
- **Go nudge logic**: Claude applies the matrix
|
||||
|
||||
## What We DO Need
|
||||
|
||||
- **mol-witness-patrol**: The playbook (exists)
|
||||
- **Handoff bead**: State persistence (gt-poxd)
|
||||
- **CLI primitives**: gt mail, gt nudge, gt session (exist)
|
||||
- **Molecule tracking**: bd close for step completion (exists)
|
||||
|
||||
## Related Issues
|
||||
|
||||
- gt-poxd: Create handoff beads for Witness and Refinery roles
|
||||
- gt-y481: Patrol parity - Witness and Refinery match Deacon sophistication
|
||||
- gt-tnow: Implement Christmas Ornament pattern for mol-witness-patrol
|
||||
- [polecat-lifecycle.md](polecat-lifecycle.md) - Step-based execution model
|
||||
- [molecular-chemistry.md](molecular-chemistry.md) - MEOW stack
|
||||
|
||||
Reference in New Issue
Block a user