Convert formula files from YAML to JSON (gt-mdgt8)
All 9 formula files converted: - shiny, rule-of-five, security-audit, shiny-enterprise - towers-of-hanoi - mol-deacon-patrol, mol-refinery-patrol, mol-witness-patrol, mol-polecat-arm 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
54
.beads/formulas/mol-deacon-patrol.formula.json
Normal file
54
.beads/formulas/mol-deacon-patrol.formula.json
Normal file
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"formula": "mol-deacon-patrol",
|
||||
"description": "Mayor's daemon patrol loop.\n\nThe Deacon is the Mayor's background process that runs continuously, handling callbacks, monitoring rig health, and performing cleanup. Each patrol cycle runs these steps in sequence, then loops or exits.",
|
||||
"version": 1,
|
||||
"steps": [
|
||||
{
|
||||
"id": "inbox-check",
|
||||
"title": "Handle callbacks from agents",
|
||||
"description": "Handle callbacks from agents.\n\nCheck the Mayor's inbox for messages from:\n- Witnesses reporting polecat status\n- Refineries reporting merge results\n- Polecats requesting help or escalation\n- External triggers (webhooks, timers)\n\n```bash\ngt mail inbox\n# For each message:\ngt mail read <id>\n# Handle based on message type\n```\n\nCallbacks may spawn new polecats, update issue state, or trigger other actions."
|
||||
},
|
||||
{
|
||||
"id": "trigger-pending-spawns",
|
||||
"title": "Nudge newly spawned polecats",
|
||||
"needs": ["inbox-check"],
|
||||
"description": "Nudge newly spawned polecats that are ready for input.\n\nWhen polecats are spawned, their Claude session takes 10-20 seconds to initialize. The spawn command returns immediately without waiting. This step finds spawned polecats that are now ready and sends them a trigger to start working.\n\n```bash\n# For each rig with polecats\nfor rig in gastown beads; do\n gt polecats $rig\n # For each working polecat, check if Claude is ready\n # Use tmux capture-pane to look for \"> \" prompt\ndone\n```\n\nFor each ready polecat that hasn't been triggered yet:\n1. Send \"Begin.\" to trigger UserPromptSubmit hook\n2. The hook injects mail, polecat sees its assignment\n3. Mark polecat as triggered in state\n\nUse WaitForClaudeReady from tmux package (polls for \"> \" prompt). Timeout: 60 seconds per polecat. If not ready, try again next cycle."
|
||||
},
|
||||
{
|
||||
"id": "health-scan",
|
||||
"title": "Check Witness and Refinery health",
|
||||
"needs": ["trigger-pending-spawns"],
|
||||
"description": "Check Witness and Refinery health for each rig.\n\n**ZFC Principle**: You (Claude) make the judgment call about what is \"stuck\" or \"unresponsive\" - there are no hardcoded thresholds in Go. Read the signals, consider context, and decide.\n\nFor each rig, run:\n```bash\ngt witness status <rig>\ngt refinery status <rig>\n```\n\n**Signals to assess:**\n\n| Component | Healthy Signals | Concerning Signals |\n|-----------|-----------------|-------------------|\n| Witness | State: running, recent activity | State: not running, no heartbeat |\n| Refinery | State: running, queue processing | Queue stuck, merge failures |\n\n**Tracking unresponsive cycles:**\n\nMaintain in your patrol state (persisted across cycles):\n```\nhealth_state:\n <rig>:\n witness:\n unresponsive_cycles: 0\n last_seen_healthy: <timestamp>\n refinery:\n unresponsive_cycles: 0\n last_seen_healthy: <timestamp>\n```\n\n**Decision matrix** (you decide the thresholds based on context):\n\n| Cycles Unresponsive | Suggested Action |\n|---------------------|------------------|\n| 1-2 | Note it, check again next cycle |\n| 3-4 | Attempt restart: gt witness restart <rig> |\n| 5+ | Escalate to Mayor with context |\n\n**Restart commands:**\n```bash\ngt witness restart <rig>\ngt refinery restart <rig>\n```\n\n**Escalation:**\n```bash\ngt mail send mayor/ -s \"Health: <rig> <component> unresponsive\" \\\n -m \"Component has been unresponsive for N cycles. Restart attempts failed.\n Last healthy: <timestamp>\n Error signals: <details>\"\n```\n\nReset unresponsive_cycles to 0 when component responds normally."
|
||||
},
|
||||
{
|
||||
"id": "plugin-run",
|
||||
"title": "Execute registered plugins",
|
||||
"needs": ["health-scan"],
|
||||
"description": "Execute registered plugins.\n\nScan ~/gt/plugins/ for plugin directories. Each plugin has a plugin.md with YAML frontmatter defining its gate (when to run) and instructions (what to do).\n\nSee docs/deacon-plugins.md for full documentation.\n\nGate types:\n- cooldown: Time since last run (e.g., 24h)\n- cron: Schedule-based (e.g., \"0 9 * * *\")\n- condition: Metric threshold (e.g., wisp count > 50)\n- event: Trigger-based (e.g., startup, heartbeat)\n\nFor each plugin:\n1. Read plugin.md frontmatter to check gate\n2. Compare against state.json (last run, etc.)\n3. If gate is open, execute the plugin\n\nPlugins marked parallel: true can run concurrently using Task tool subagents. Sequential plugins run one at a time in directory order.\n\nSkip this step if ~/gt/plugins/ does not exist or is empty."
|
||||
},
|
||||
{
|
||||
"id": "orphan-check",
|
||||
"title": "Find abandoned work",
|
||||
"needs": ["health-scan"],
|
||||
"description": "Find abandoned work.\n\nScan for orphaned state:\n- Issues marked in_progress with no active polecat\n- Polecats that stopped responding mid-work\n- Merge queue entries with no polecat owner\n- Wisp sessions that outlived their spawner\n\n```bash\nbd list --status=in_progress\ngt polecats --all --orphan\n```\n\nFor each orphan:\n- Check if polecat session still exists\n- If not, mark issue for reassignment or retry\n- File incident beads if data loss occurred"
|
||||
},
|
||||
{
|
||||
"id": "session-gc",
|
||||
"title": "Clean dead sessions",
|
||||
"needs": ["orphan-check"],
|
||||
"description": "Clean dead sessions.\n\nGarbage collect terminated sessions:\n- Remove stale polecat directories\n- Clean up wisp session artifacts\n- Prune old logs and temp files\n- Archive completed molecule state\n\n```bash\ngt gc --sessions\ngt gc --wisps --age=1h\n```\n\nPreserve audit trail. Only clean sessions confirmed dead."
|
||||
},
|
||||
{
|
||||
"id": "context-check",
|
||||
"title": "Check own context limit",
|
||||
"needs": ["session-gc"],
|
||||
"description": "Check own context limit.\n\nThe Deacon runs in a Claude session with finite context. Check if approaching the limit:\n\n```bash\ngt context --usage\n```\n\nIf context is high (>80%), prepare for handoff:\n- Summarize current state\n- Note any pending work\n- Write handoff to molecule state\n\nThis enables the Deacon to burn and respawn cleanly."
|
||||
},
|
||||
{
|
||||
"id": "loop-or-exit",
|
||||
"title": "Burn and respawn or loop",
|
||||
"needs": ["context-check"],
|
||||
"description": "Burn and let daemon respawn, or exit if context high.\n\nDecision point at end of patrol cycle:\n\nIf context is LOW:\n- Sleep briefly (avoid tight loop)\n- Return to inbox-check step\n\nIf context is HIGH:\n- Write state to persistent storage\n- Exit cleanly\n- Let the daemon orchestrator respawn a fresh Deacon\n\nThe daemon ensures Deacon is always running:\n```bash\n# Daemon respawns on exit\ngt daemon status\n```\n\nThis enables infinite patrol duration via context-aware respawning."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,234 +0,0 @@
|
||||
# Deacon Patrol: Mayor's daemon loop
|
||||
# The Deacon is the Mayor's background process that runs continuously,
|
||||
# handling callbacks, monitoring rig health, and performing cleanup.
|
||||
|
||||
formula: mol-deacon-patrol
|
||||
description: |
|
||||
Mayor's daemon patrol loop.
|
||||
|
||||
The Deacon is the Mayor's background process that runs continuously,
|
||||
handling callbacks, monitoring rig health, and performing cleanup.
|
||||
Each patrol cycle runs these steps in sequence, then loops or exits.
|
||||
version: 1
|
||||
|
||||
steps:
|
||||
- id: inbox-check
|
||||
title: Handle callbacks from agents
|
||||
description: |
|
||||
Handle callbacks from agents.
|
||||
|
||||
Check the Mayor's inbox for messages from:
|
||||
- Witnesses reporting polecat status
|
||||
- Refineries reporting merge results
|
||||
- Polecats requesting help or escalation
|
||||
- External triggers (webhooks, timers)
|
||||
|
||||
```bash
|
||||
gt mail inbox
|
||||
# For each message:
|
||||
gt mail read <id>
|
||||
# Handle based on message type
|
||||
```
|
||||
|
||||
Callbacks may spawn new polecats, update issue state, or trigger other actions.
|
||||
|
||||
- id: trigger-pending-spawns
|
||||
title: Nudge newly spawned polecats
|
||||
needs: [inbox-check]
|
||||
description: |
|
||||
Nudge newly spawned polecats that are ready for input.
|
||||
|
||||
When polecats are spawned, their Claude session takes 10-20 seconds to initialize.
|
||||
The spawn command returns immediately without waiting. This step finds spawned
|
||||
polecats that are now ready and sends them a trigger to start working.
|
||||
|
||||
```bash
|
||||
# For each rig with polecats
|
||||
for rig in gastown beads; do
|
||||
gt polecats $rig
|
||||
# For each working polecat, check if Claude is ready
|
||||
# Use tmux capture-pane to look for "> " prompt
|
||||
done
|
||||
```
|
||||
|
||||
For each ready polecat that hasn't been triggered yet:
|
||||
1. Send "Begin." to trigger UserPromptSubmit hook
|
||||
2. The hook injects mail, polecat sees its assignment
|
||||
3. Mark polecat as triggered in state
|
||||
|
||||
Use WaitForClaudeReady from tmux package (polls for "> " prompt).
|
||||
Timeout: 60 seconds per polecat. If not ready, try again next cycle.
|
||||
|
||||
- id: health-scan
|
||||
title: Check Witness and Refinery health
|
||||
needs: [trigger-pending-spawns]
|
||||
description: |
|
||||
Check Witness and Refinery health for each rig.
|
||||
|
||||
**ZFC Principle**: You (Claude) make the judgment call about what is "stuck" or
|
||||
"unresponsive" - there are no hardcoded thresholds in Go. Read the signals,
|
||||
consider context, and decide.
|
||||
|
||||
For each rig, run:
|
||||
```bash
|
||||
gt witness status <rig>
|
||||
gt refinery status <rig>
|
||||
```
|
||||
|
||||
**Signals to assess:**
|
||||
|
||||
| Component | Healthy Signals | Concerning Signals |
|
||||
|-----------|-----------------|-------------------|
|
||||
| Witness | State: running, recent activity | State: not running, no heartbeat |
|
||||
| Refinery | State: running, queue processing | Queue stuck, merge failures |
|
||||
|
||||
**Tracking unresponsive cycles:**
|
||||
|
||||
Maintain in your patrol state (persisted across cycles):
|
||||
```
|
||||
health_state:
|
||||
<rig>:
|
||||
witness:
|
||||
unresponsive_cycles: 0
|
||||
last_seen_healthy: <timestamp>
|
||||
refinery:
|
||||
unresponsive_cycles: 0
|
||||
last_seen_healthy: <timestamp>
|
||||
```
|
||||
|
||||
**Decision matrix** (you decide the thresholds based on context):
|
||||
|
||||
| Cycles Unresponsive | Suggested Action |
|
||||
|---------------------|------------------|
|
||||
| 1-2 | Note it, check again next cycle |
|
||||
| 3-4 | Attempt restart: gt witness restart <rig> |
|
||||
| 5+ | Escalate to Mayor with context |
|
||||
|
||||
**Restart commands:**
|
||||
```bash
|
||||
gt witness restart <rig>
|
||||
gt refinery restart <rig>
|
||||
```
|
||||
|
||||
**Escalation:**
|
||||
```bash
|
||||
gt mail send mayor/ -s "Health: <rig> <component> unresponsive" \
|
||||
-m "Component has been unresponsive for N cycles. Restart attempts failed.
|
||||
Last healthy: <timestamp>
|
||||
Error signals: <details>"
|
||||
```
|
||||
|
||||
Reset unresponsive_cycles to 0 when component responds normally.
|
||||
|
||||
- id: plugin-run
|
||||
title: Execute registered plugins
|
||||
needs: [health-scan]
|
||||
description: |
|
||||
Execute registered plugins.
|
||||
|
||||
Scan ~/gt/plugins/ for plugin directories. Each plugin has a plugin.md with
|
||||
YAML frontmatter defining its gate (when to run) and instructions (what to do).
|
||||
|
||||
See docs/deacon-plugins.md for full documentation.
|
||||
|
||||
Gate types:
|
||||
- cooldown: Time since last run (e.g., 24h)
|
||||
- cron: Schedule-based (e.g., "0 9 * * *")
|
||||
- condition: Metric threshold (e.g., wisp count > 50)
|
||||
- event: Trigger-based (e.g., startup, heartbeat)
|
||||
|
||||
For each plugin:
|
||||
1. Read plugin.md frontmatter to check gate
|
||||
2. Compare against state.json (last run, etc.)
|
||||
3. If gate is open, execute the plugin
|
||||
|
||||
Plugins marked parallel: true can run concurrently using Task tool subagents.
|
||||
Sequential plugins run one at a time in directory order.
|
||||
|
||||
Skip this step if ~/gt/plugins/ does not exist or is empty.
|
||||
|
||||
- id: orphan-check
|
||||
title: Find abandoned work
|
||||
needs: [health-scan]
|
||||
description: |
|
||||
Find abandoned work.
|
||||
|
||||
Scan for orphaned state:
|
||||
- Issues marked in_progress with no active polecat
|
||||
- Polecats that stopped responding mid-work
|
||||
- Merge queue entries with no polecat owner
|
||||
- Wisp sessions that outlived their spawner
|
||||
|
||||
```bash
|
||||
bd list --status=in_progress
|
||||
gt polecats --all --orphan
|
||||
```
|
||||
|
||||
For each orphan:
|
||||
- Check if polecat session still exists
|
||||
- If not, mark issue for reassignment or retry
|
||||
- File incident beads if data loss occurred
|
||||
|
||||
- id: session-gc
|
||||
title: Clean dead sessions
|
||||
needs: [orphan-check]
|
||||
description: |
|
||||
Clean dead sessions.
|
||||
|
||||
Garbage collect terminated sessions:
|
||||
- Remove stale polecat directories
|
||||
- Clean up wisp session artifacts
|
||||
- Prune old logs and temp files
|
||||
- Archive completed molecule state
|
||||
|
||||
```bash
|
||||
gt gc --sessions
|
||||
gt gc --wisps --age=1h
|
||||
```
|
||||
|
||||
Preserve audit trail. Only clean sessions confirmed dead.
|
||||
|
||||
- id: context-check
|
||||
title: Check own context limit
|
||||
needs: [session-gc]
|
||||
description: |
|
||||
Check own context limit.
|
||||
|
||||
The Deacon runs in a Claude session with finite context.
|
||||
Check if approaching the limit:
|
||||
|
||||
```bash
|
||||
gt context --usage
|
||||
```
|
||||
|
||||
If context is high (>80%), prepare for handoff:
|
||||
- Summarize current state
|
||||
- Note any pending work
|
||||
- Write handoff to molecule state
|
||||
|
||||
This enables the Deacon to burn and respawn cleanly.
|
||||
|
||||
- id: loop-or-exit
|
||||
title: Burn and respawn or loop
|
||||
needs: [context-check]
|
||||
description: |
|
||||
Burn and let daemon respawn, or exit if context high.
|
||||
|
||||
Decision point at end of patrol cycle:
|
||||
|
||||
If context is LOW:
|
||||
- Sleep briefly (avoid tight loop)
|
||||
- Return to inbox-check step
|
||||
|
||||
If context is HIGH:
|
||||
- Write state to persistent storage
|
||||
- Exit cleanly
|
||||
- Let the daemon orchestrator respawn a fresh Deacon
|
||||
|
||||
The daemon ensures Deacon is always running:
|
||||
```bash
|
||||
# Daemon respawns on exit
|
||||
gt daemon status
|
||||
```
|
||||
|
||||
This enables infinite patrol duration via context-aware respawning.
|
||||
54
.beads/formulas/mol-polecat-arm.formula.json
Normal file
54
.beads/formulas/mol-polecat-arm.formula.json
Normal file
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"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,
|
||||
"variables": [
|
||||
{
|
||||
"name": "polecat_name",
|
||||
"required": true,
|
||||
"description": "Name of the polecat to inspect"
|
||||
},
|
||||
{
|
||||
"name": "rig",
|
||||
"required": true,
|
||||
"description": "Rig containing the polecat"
|
||||
},
|
||||
{
|
||||
"name": "nudge_text",
|
||||
"required": false,
|
||||
"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,147 +0,0 @@
|
||||
# Polecat Arm: Single polecat inspection cycle
|
||||
# Dynamically bonded by mol-witness-patrol for each polecat being monitored.
|
||||
|
||||
formula: mol-polecat-arm
|
||||
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?...") |
|
||||
version: 1
|
||||
|
||||
variables:
|
||||
- name: polecat_name
|
||||
required: true
|
||||
description: Name of the polecat to inspect
|
||||
- name: rig
|
||||
required: true
|
||||
description: Rig containing the polecat
|
||||
- name: nudge_text
|
||||
required: false
|
||||
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}}.
|
||||
|
||||
```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: assess
|
||||
title: Assess work status
|
||||
needs: [capture]
|
||||
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: load-history
|
||||
title: Load intervention history
|
||||
needs: [assess]
|
||||
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: decide
|
||||
title: Decide intervention action
|
||||
needs: [load-history]
|
||||
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: execute
|
||||
title: Execute intervention
|
||||
needs: [decide]
|
||||
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.
|
||||
66
.beads/formulas/mol-refinery-patrol.formula.json
Normal file
66
.beads/formulas/mol-refinery-patrol.formula.json
Normal file
@@ -0,0 +1,66 @@
|
||||
{
|
||||
"formula": "mol-refinery-patrol",
|
||||
"description": "Merge queue processor patrol loop.\n\nThe Refinery is the Engineer in the engine room. You process polecat branches, merging them to main one at a time with sequential rebasing.\n\n**The Scotty Test**: Before proceeding past any failure, ask yourself: \"Would Scotty walk past a warp core leak because it existed before his shift?\"",
|
||||
"version": 1,
|
||||
"steps": [
|
||||
{
|
||||
"id": "inbox-check",
|
||||
"title": "Check refinery mail",
|
||||
"description": "Check mail for MR submissions, escalations, messages.\n\n```bash\ngt mail inbox\n# Process any urgent items\n```\n\nHandle shutdown requests, escalations, and status queries."
|
||||
},
|
||||
{
|
||||
"id": "queue-scan",
|
||||
"title": "Scan merge queue",
|
||||
"needs": ["inbox-check"],
|
||||
"description": "Fetch remote and identify polecat branches waiting.\n\n```bash\ngit fetch origin\ngit branch -r | grep polecat\ngt refinery queue <rig>\n```\n\nIf queue empty, skip to context-check step. Track branch list for this cycle."
|
||||
},
|
||||
{
|
||||
"id": "process-branch",
|
||||
"title": "Process next branch",
|
||||
"needs": ["queue-scan"],
|
||||
"description": "Pick next branch. Rebase on current main.\n\n```bash\ngit checkout -b temp origin/<polecat-branch>\ngit rebase origin/main\n```\n\nIf rebase conflicts and unresolvable:\n- git rebase --abort\n- Notify polecat to fix and resubmit\n- Skip to loop-check for next branch"
|
||||
},
|
||||
{
|
||||
"id": "run-tests",
|
||||
"title": "Run test suite",
|
||||
"needs": ["process-branch"],
|
||||
"description": "Run the test suite.\n\n```bash\ngo test ./...\n```\n\nTrack results: pass count, fail count, specific failures."
|
||||
},
|
||||
{
|
||||
"id": "handle-failures",
|
||||
"title": "Handle test failures",
|
||||
"needs": ["run-tests"],
|
||||
"description": "**VERIFICATION GATE**: This step enforces the Beads Promise.\n\nIf tests PASSED: This step auto-completes. Proceed to merge.\n\nIf tests FAILED:\n1. Diagnose: Is this a branch regression or pre-existing on main?\n2. If branch caused it:\n - Abort merge\n - Notify polecat: \"Tests failing. Please fix and resubmit.\"\n - Skip to loop-check\n3. If pre-existing on main:\n - Option A: Fix it yourself (you're the Engineer!)\n - Option B: File a bead: bd create --type=bug --priority=1 --title=\"...\"\n\n**GATE REQUIREMENT**: You CANNOT proceed to merge-push without:\n- Tests passing, OR\n- Fix committed, OR\n- Bead filed for the failure\n\nThis is non-negotiable. Never disavow. Never \"note and proceed.\""
|
||||
},
|
||||
{
|
||||
"id": "merge-push",
|
||||
"title": "Merge and push to main",
|
||||
"needs": ["handle-failures"],
|
||||
"description": "Merge to main and push immediately.\n\n```bash\ngit checkout main\ngit merge --ff-only temp\ngit push origin main\ngit branch -d temp\ngit branch -D <polecat-branch> # Local delete (branches never go to origin)\n```\n\nMain has moved. Any remaining branches need rebasing on new baseline."
|
||||
},
|
||||
{
|
||||
"id": "loop-check",
|
||||
"title": "Check for more work",
|
||||
"needs": ["merge-push"],
|
||||
"description": "More branches to process?\n\nIf yes: Return to process-branch with next branch.\nIf no: Continue to generate-summary.\n\nTrack: branches processed, branches skipped (with reasons)."
|
||||
},
|
||||
{
|
||||
"id": "generate-summary",
|
||||
"title": "Generate handoff summary",
|
||||
"needs": ["loop-check"],
|
||||
"description": "Summarize this patrol cycle.\n\nInclude:\n- Branches processed (count, names)\n- Test results (pass/fail)\n- Issues filed (if any)\n- Branches skipped (with reasons)\n- Any escalations sent\n\nThis becomes the digest when the patrol 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- Write handoff summary\n- Prepare for burn/respawn\n\nIf context is LOW:\n- Can continue processing"
|
||||
},
|
||||
{
|
||||
"id": "burn-or-loop",
|
||||
"title": "Burn and respawn or loop",
|
||||
"needs": ["context-check"],
|
||||
"description": "End of patrol cycle decision.\n\nIf queue non-empty AND context LOW:\n- Burn this wisp, start fresh patrol\n- Return to inbox-check\n\nIf queue empty OR context HIGH:\n- Burn wisp with summary digest\n- Exit (daemon will respawn if needed)"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
# Refinery Patrol: Merge queue processor with verification gates
|
||||
# The Refinery is the Engineer in the engine room. You process polecat branches,
|
||||
# merging them to main one at a time with sequential rebasing.
|
||||
|
||||
formula: mol-refinery-patrol
|
||||
description: |
|
||||
Merge queue processor patrol loop.
|
||||
|
||||
The Refinery is the Engineer in the engine room. You process polecat branches,
|
||||
merging them to main one at a time with sequential rebasing.
|
||||
|
||||
**The Scotty Test**: Before proceeding past any failure, ask yourself:
|
||||
"Would Scotty walk past a warp core leak because it existed before his shift?"
|
||||
version: 1
|
||||
|
||||
steps:
|
||||
- id: inbox-check
|
||||
title: Check refinery mail
|
||||
description: |
|
||||
Check mail for MR submissions, escalations, messages.
|
||||
|
||||
```bash
|
||||
gt mail inbox
|
||||
# Process any urgent items
|
||||
```
|
||||
|
||||
Handle shutdown requests, escalations, and status queries.
|
||||
|
||||
- id: queue-scan
|
||||
title: Scan merge queue
|
||||
needs: [inbox-check]
|
||||
description: |
|
||||
Fetch remote and identify polecat branches waiting.
|
||||
|
||||
```bash
|
||||
git fetch origin
|
||||
git branch -r | grep polecat
|
||||
gt refinery queue <rig>
|
||||
```
|
||||
|
||||
If queue empty, skip to context-check step.
|
||||
Track branch list for this cycle.
|
||||
|
||||
- id: process-branch
|
||||
title: Process next branch
|
||||
needs: [queue-scan]
|
||||
description: |
|
||||
Pick next branch. Rebase on current main.
|
||||
|
||||
```bash
|
||||
git checkout -b temp origin/<polecat-branch>
|
||||
git rebase origin/main
|
||||
```
|
||||
|
||||
If rebase conflicts and unresolvable:
|
||||
- git rebase --abort
|
||||
- Notify polecat to fix and resubmit
|
||||
- Skip to loop-check for next branch
|
||||
|
||||
- id: run-tests
|
||||
title: Run test suite
|
||||
needs: [process-branch]
|
||||
description: |
|
||||
Run the test suite.
|
||||
|
||||
```bash
|
||||
go test ./...
|
||||
```
|
||||
|
||||
Track results: pass count, fail count, specific failures.
|
||||
|
||||
- id: handle-failures
|
||||
title: Handle test failures
|
||||
needs: [run-tests]
|
||||
description: |
|
||||
**VERIFICATION GATE**: This step enforces the Beads Promise.
|
||||
|
||||
If tests PASSED: This step auto-completes. Proceed to merge.
|
||||
|
||||
If tests FAILED:
|
||||
1. Diagnose: Is this a branch regression or pre-existing on main?
|
||||
2. If branch caused it:
|
||||
- Abort merge
|
||||
- Notify polecat: "Tests failing. Please fix and resubmit."
|
||||
- Skip to loop-check
|
||||
3. If pre-existing on main:
|
||||
- Option A: Fix it yourself (you're the Engineer!)
|
||||
- Option B: File a bead: bd create --type=bug --priority=1 --title="..."
|
||||
|
||||
**GATE REQUIREMENT**: You CANNOT proceed to merge-push without:
|
||||
- Tests passing, OR
|
||||
- Fix committed, OR
|
||||
- Bead filed for the failure
|
||||
|
||||
This is non-negotiable. Never disavow. Never "note and proceed."
|
||||
|
||||
- id: merge-push
|
||||
title: Merge and push to main
|
||||
needs: [handle-failures]
|
||||
description: |
|
||||
Merge to main and push immediately.
|
||||
|
||||
```bash
|
||||
git checkout main
|
||||
git merge --ff-only temp
|
||||
git push origin main
|
||||
git branch -d temp
|
||||
git branch -D <polecat-branch> # Local delete (branches never go to origin)
|
||||
```
|
||||
|
||||
Main has moved. Any remaining branches need rebasing on new baseline.
|
||||
|
||||
- id: loop-check
|
||||
title: Check for more work
|
||||
needs: [merge-push]
|
||||
description: |
|
||||
More branches to process?
|
||||
|
||||
If yes: Return to process-branch with next branch.
|
||||
If no: Continue to generate-summary.
|
||||
|
||||
Track: branches processed, branches skipped (with reasons).
|
||||
|
||||
- id: generate-summary
|
||||
title: Generate handoff summary
|
||||
needs: [loop-check]
|
||||
description: |
|
||||
Summarize this patrol cycle.
|
||||
|
||||
Include:
|
||||
- Branches processed (count, names)
|
||||
- Test results (pass/fail)
|
||||
- Issues filed (if any)
|
||||
- Branches skipped (with reasons)
|
||||
- Any escalations sent
|
||||
|
||||
This becomes the digest when the patrol is squashed.
|
||||
|
||||
- id: context-check
|
||||
title: Check own context limit
|
||||
needs: [generate-summary]
|
||||
description: |
|
||||
Check own context usage.
|
||||
|
||||
If context is HIGH (>80%):
|
||||
- Write handoff summary
|
||||
- Prepare for burn/respawn
|
||||
|
||||
If context is LOW:
|
||||
- Can continue processing
|
||||
|
||||
- id: burn-or-loop
|
||||
title: Burn and respawn or loop
|
||||
needs: [context-check]
|
||||
description: |
|
||||
End of patrol cycle decision.
|
||||
|
||||
If queue non-empty AND context LOW:
|
||||
- Burn this wisp, start fresh patrol
|
||||
- Return to inbox-check
|
||||
|
||||
If queue empty OR context HIGH:
|
||||
- Burn wisp with summary digest
|
||||
- Exit (daemon will respawn if needed)
|
||||
61
.beads/formulas/mol-witness-patrol.formula.json
Normal file
61
.beads/formulas/mol-witness-patrol.formula.json
Normal file
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"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,221 +0,0 @@
|
||||
# Witness Patrol: Per-rig worker monitor using Christmas Ornament pattern
|
||||
# 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.
|
||||
|
||||
formula: mol-witness-patrol
|
||||
description: |
|
||||
Per-rig worker monitor patrol loop using the Christmas Ornament pattern.
|
||||
|
||||
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.
|
||||
|
||||
## The Christmas Ornament Shape
|
||||
|
||||
```
|
||||
★ 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
|
||||
```
|
||||
version: 1
|
||||
|
||||
steps:
|
||||
# --- PREFLIGHT PHASE ---
|
||||
|
||||
- id: inbox-check
|
||||
title: Process witness mail
|
||||
description: |
|
||||
Process witness mail: lifecycle requests, help requests.
|
||||
|
||||
```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
|
||||
|
||||
Record 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.
|
||||
|
||||
**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
|
||||
gt session status <rig>/refinery
|
||||
|
||||
# Check for merge requests in queue
|
||||
bd list --type=merge-request --status=open
|
||||
```
|
||||
|
||||
If merge requests are waiting AND refinery is 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 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.
|
||||
|
||||
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.
|
||||
|
||||
# --- DISCOVERY PHASE (Dynamic Bonding) ---
|
||||
|
||||
- id: survey-workers
|
||||
title: Survey all polecats (fanout)
|
||||
needs: [load-state]
|
||||
description: |
|
||||
List polecats and bond mol-polecat-arm for each one.
|
||||
|
||||
```bash
|
||||
# Get list of polecats
|
||||
gt polecat list <rig>
|
||||
```
|
||||
|
||||
For each polecat discovered, dynamically bond an inspection arm:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
This creates child wisps like:
|
||||
- patrol-x7k.arm-ace (5 steps)
|
||||
- patrol-x7k.arm-nux (5 steps)
|
||||
- patrol-x7k.arm-toast (5 steps)
|
||||
|
||||
Each arm runs in PARALLEL. The aggregate step will wait for all to complete.
|
||||
|
||||
If no polecats are found, this step completes immediately with no children.
|
||||
|
||||
# --- CLEANUP PHASE (Gate + Fixed Steps) ---
|
||||
|
||||
- id: aggregate
|
||||
title: Aggregate arm results
|
||||
needs: [survey-workers]
|
||||
waits_for: all-children
|
||||
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: save-state
|
||||
title: Persist patrol state
|
||||
needs: [aggregate]
|
||||
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: generate-summary
|
||||
title: Generate handoff summary
|
||||
needs: [save-state]
|
||||
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: context-check
|
||||
title: Check own context limit
|
||||
needs: [generate-summary]
|
||||
description: |
|
||||
Check own context usage.
|
||||
|
||||
If context is HIGH (>80%):
|
||||
- Ensure state is saved to handoff bead
|
||||
- Prepare for burn/respawn
|
||||
|
||||
If context is LOW:
|
||||
- Can continue patrolling
|
||||
|
||||
- id: burn-or-loop
|
||||
title: Burn and respawn or loop
|
||||
needs: [context-check]
|
||||
description: |
|
||||
End of patrol cycle decision.
|
||||
|
||||
If context is LOW:
|
||||
- Burn this wisp (no audit trail needed for patrol cycles)
|
||||
- Sleep briefly to avoid tight loop (30-60 seconds)
|
||||
- Return to inbox-check step
|
||||
|
||||
If context is HIGH:
|
||||
- Burn wisp with summary digest
|
||||
- Exit cleanly (daemon will respawn fresh Witness)
|
||||
|
||||
```bash
|
||||
bd mol burn # Destroy ephemeral wisp
|
||||
```
|
||||
|
||||
The daemon ensures Witness is always running.
|
||||
32
.beads/formulas/rule-of-five.formula.json
Normal file
32
.beads/formulas/rule-of-five.formula.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"formula": "rule-of-five",
|
||||
"type": "expansion",
|
||||
"description": "Jeffrey Emanuel's discovery: LLM agents produce best work through 4-5 iterative refinements. Breadth-first exploration, then editorial passes.",
|
||||
"version": 1,
|
||||
"template": [
|
||||
{
|
||||
"id": "{target}.draft",
|
||||
"description": "Initial attempt at: {target.description}. Don't aim for perfection. Get the shape right. Breadth over depth."
|
||||
},
|
||||
{
|
||||
"id": "{target}.refine-1",
|
||||
"description": "First refinement pass. Focus: CORRECTNESS. Fix errors, bugs, mistakes. Is the logic sound?",
|
||||
"needs": ["{target}.draft"]
|
||||
},
|
||||
{
|
||||
"id": "{target}.refine-2",
|
||||
"description": "Second refinement pass. Focus: CLARITY. Can someone else understand this? Simplify. Remove jargon.",
|
||||
"needs": ["{target}.refine-1"]
|
||||
},
|
||||
{
|
||||
"id": "{target}.refine-3",
|
||||
"description": "Third refinement pass. Focus: EDGE CASES. What could go wrong? What's missing? Handle the unusual.",
|
||||
"needs": ["{target}.refine-2"]
|
||||
},
|
||||
{
|
||||
"id": "{target}.refine-4",
|
||||
"description": "Final polish. Focus: EXCELLENCE. This is the last pass. Make it shine. Is this something you'd be proud to ship?",
|
||||
"needs": ["{target}.refine-3"]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
formula: rule-of-five
|
||||
type: expansion
|
||||
description: >
|
||||
Jeffrey Emanuel's discovery: LLM agents produce best work through 4-5
|
||||
iterative refinements. Breadth-first exploration, then editorial passes.
|
||||
version: 1
|
||||
template:
|
||||
- id: "{target}.draft"
|
||||
description: >
|
||||
Initial attempt at: {target.description}. Don't aim for perfection.
|
||||
Get the shape right. Breadth over depth.
|
||||
|
||||
- id: "{target}.refine-1"
|
||||
description: >
|
||||
First refinement pass. Focus: CORRECTNESS. Fix errors, bugs, mistakes.
|
||||
Is the logic sound?
|
||||
needs: ["{target}.draft"]
|
||||
|
||||
- id: "{target}.refine-2"
|
||||
description: >
|
||||
Second refinement pass. Focus: CLARITY. Can someone else understand
|
||||
this? Simplify. Remove jargon.
|
||||
needs: ["{target}.refine-1"]
|
||||
|
||||
- id: "{target}.refine-3"
|
||||
description: >
|
||||
Third refinement pass. Focus: EDGE CASES. What could go wrong?
|
||||
What's missing? Handle the unusual.
|
||||
needs: ["{target}.refine-2"]
|
||||
|
||||
- id: "{target}.refine-4"
|
||||
description: >
|
||||
Final polish. Focus: EXCELLENCE. This is the last pass. Make it shine.
|
||||
Is this something you'd be proud to ship?
|
||||
needs: ["{target}.refine-3"]
|
||||
42
.beads/formulas/security-audit.formula.json
Normal file
42
.beads/formulas/security-audit.formula.json
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"formula": "security-audit",
|
||||
"type": "aspect",
|
||||
"description": "Cross-cutting security concern. Applies security scanning before and after implementation steps.",
|
||||
"version": 1,
|
||||
"pointcuts": [
|
||||
{"glob": "*.implement"},
|
||||
{"glob": "*.submit"}
|
||||
],
|
||||
"advice": {
|
||||
"around": {
|
||||
"before": [
|
||||
{
|
||||
"id": "security-prescan",
|
||||
"description": "Pre-implementation security check. Review for secrets/credentials in scope. Check dependencies for known vulnerabilities.",
|
||||
"args": {
|
||||
"target": "{step.id}"
|
||||
}
|
||||
}
|
||||
],
|
||||
"after": [
|
||||
{
|
||||
"id": "security-postscan",
|
||||
"description": "Post-implementation security scan. Scan new code for vulnerabilities (SAST). Check for hardcoded secrets. Review for OWASP Top 10 issues.",
|
||||
"args": {
|
||||
"target": "{step.id}"
|
||||
},
|
||||
"output": {
|
||||
"approved": "boolean",
|
||||
"findings": "list"
|
||||
}
|
||||
},
|
||||
{
|
||||
"gate": {
|
||||
"condition": "security-postscan.output.approved == true",
|
||||
"message": "Security approval required before proceeding"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
formula: security-audit
|
||||
type: aspect
|
||||
description: >
|
||||
Cross-cutting security concern. Applies security scanning before and
|
||||
after implementation steps.
|
||||
version: 1
|
||||
pointcuts:
|
||||
- glob: "*.implement"
|
||||
- glob: "*.submit"
|
||||
advice:
|
||||
around:
|
||||
before:
|
||||
- id: security-prescan
|
||||
description: >
|
||||
Pre-implementation security check. Review for secrets/credentials
|
||||
in scope. Check dependencies for known vulnerabilities.
|
||||
args:
|
||||
target: "{step.id}"
|
||||
after:
|
||||
- id: security-postscan
|
||||
description: >
|
||||
Post-implementation security scan. Scan new code for vulnerabilities
|
||||
(SAST). Check for hardcoded secrets. Review for OWASP Top 10 issues.
|
||||
args:
|
||||
target: "{step.id}"
|
||||
output:
|
||||
approved: boolean
|
||||
findings: list
|
||||
- gate:
|
||||
condition: "security-postscan.output.approved == true"
|
||||
message: Security approval required before proceeding
|
||||
68
.beads/formulas/shiny-enterprise.formula.json
Normal file
68
.beads/formulas/shiny-enterprise.formula.json
Normal file
@@ -0,0 +1,68 @@
|
||||
{
|
||||
"formula": "shiny-enterprise",
|
||||
"extends": "shiny",
|
||||
"description": "Enterprise-grade engineering workflow. Shiny + Rule of Five + Security + Performance Testing + Review Loop.",
|
||||
"version": 1,
|
||||
"compose": [
|
||||
{
|
||||
"expand": {
|
||||
"target": "implement",
|
||||
"with": "rule-of-five"
|
||||
}
|
||||
},
|
||||
{
|
||||
"aspect": {
|
||||
"pointcut": "implement.*",
|
||||
"with": "security-audit"
|
||||
}
|
||||
},
|
||||
{
|
||||
"gate": {
|
||||
"before": "submit",
|
||||
"condition": "security-postscan.approved == true",
|
||||
"message": "Cannot submit without security approval"
|
||||
}
|
||||
},
|
||||
{
|
||||
"branch": {
|
||||
"from": "implement.refine-4",
|
||||
"steps": [
|
||||
{
|
||||
"id": "perf-test",
|
||||
"description": "Run performance benchmarks"
|
||||
},
|
||||
{
|
||||
"id": "load-test",
|
||||
"description": "Run load/stress tests"
|
||||
},
|
||||
{
|
||||
"id": "chaos-test",
|
||||
"description": "Run chaos engineering tests"
|
||||
}
|
||||
],
|
||||
"join": "review"
|
||||
}
|
||||
},
|
||||
{
|
||||
"loop": {
|
||||
"step": "review",
|
||||
"until": "review.output.approved == true",
|
||||
"max": 3,
|
||||
"on-max": "escalate"
|
||||
}
|
||||
},
|
||||
{
|
||||
"advice": {
|
||||
"target": "*",
|
||||
"before": {
|
||||
"id": "log-start",
|
||||
"description": "Log: Starting {step.id}"
|
||||
},
|
||||
"after": {
|
||||
"id": "log-end",
|
||||
"description": "Log: Completed {step.id}"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
formula: shiny-enterprise
|
||||
extends: shiny
|
||||
description: >
|
||||
Enterprise-grade engineering workflow. Shiny + Rule of Five + Security +
|
||||
Performance Testing + Review Loop.
|
||||
version: 1
|
||||
compose:
|
||||
- expand:
|
||||
target: implement
|
||||
with: rule-of-five
|
||||
|
||||
- aspect:
|
||||
pointcut: "implement.*"
|
||||
with: security-audit
|
||||
|
||||
- gate:
|
||||
before: submit
|
||||
condition: "security-postscan.approved == true"
|
||||
message: Cannot submit without security approval
|
||||
|
||||
- branch:
|
||||
from: implement.refine-4
|
||||
steps:
|
||||
- id: perf-test
|
||||
description: Run performance benchmarks
|
||||
- id: load-test
|
||||
description: Run load/stress tests
|
||||
- id: chaos-test
|
||||
description: Run chaos engineering tests
|
||||
join: review
|
||||
|
||||
- loop:
|
||||
step: review
|
||||
until: "review.output.approved == true"
|
||||
max: 3
|
||||
on-max: escalate
|
||||
|
||||
- advice:
|
||||
target: "*"
|
||||
before:
|
||||
id: log-start
|
||||
description: "Log: Starting {step.id}"
|
||||
after:
|
||||
id: log-end
|
||||
description: "Log: Completed {step.id}"
|
||||
35
.beads/formulas/shiny.formula.json
Normal file
35
.beads/formulas/shiny.formula.json
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"formula": "shiny",
|
||||
"description": "Engineer in a Box - the canonical right way. Design before you code. Review before you ship. Test before you submit.",
|
||||
"version": 1,
|
||||
"vars": {
|
||||
"feature": "{{feature}}",
|
||||
"assignee": "{{assignee}}"
|
||||
},
|
||||
"steps": [
|
||||
{
|
||||
"id": "design",
|
||||
"description": "Think carefully about architecture before writing code. Consider: How does this fit into the existing system? What are the edge cases? What could go wrong? Is there a simpler approach?"
|
||||
},
|
||||
{
|
||||
"id": "implement",
|
||||
"description": "Write the code for {{feature}}. Follow the design. Keep it simple. Don't gold-plate.",
|
||||
"needs": ["design"]
|
||||
},
|
||||
{
|
||||
"id": "review",
|
||||
"description": "Review the implementation. Check for: Does it match the design? Are there obvious bugs? Is it readable and maintainable? Are there security concerns?",
|
||||
"needs": ["implement"]
|
||||
},
|
||||
{
|
||||
"id": "test",
|
||||
"description": "Write and run tests. Unit tests for new code, integration tests if needed, run the full test suite, fix any regressions.",
|
||||
"needs": ["review"]
|
||||
},
|
||||
{
|
||||
"id": "submit",
|
||||
"description": "Submit for merge. Final check: git status, git diff. Commit with clear message. Push and create PR.",
|
||||
"needs": ["test"]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
formula: shiny
|
||||
description: >
|
||||
Engineer in a Box - the canonical right way. Design before you code.
|
||||
Review before you ship. Test before you submit.
|
||||
version: 1
|
||||
vars:
|
||||
feature: "{{feature}}"
|
||||
assignee: "{{assignee}}"
|
||||
steps:
|
||||
- id: design
|
||||
description: >
|
||||
Think carefully about architecture before writing code. Consider:
|
||||
How does this fit into the existing system? What are the edge cases?
|
||||
What could go wrong? Is there a simpler approach?
|
||||
|
||||
- id: implement
|
||||
description: >
|
||||
Write the code for {{feature}}. Follow the design. Keep it simple.
|
||||
Don't gold-plate.
|
||||
needs: [design]
|
||||
|
||||
- id: review
|
||||
description: >
|
||||
Review the implementation. Check for: Does it match the design?
|
||||
Are there obvious bugs? Is it readable and maintainable?
|
||||
Are there security concerns?
|
||||
needs: [implement]
|
||||
|
||||
- id: test
|
||||
description: >
|
||||
Write and run tests. Unit tests for new code, integration tests
|
||||
if needed, run the full test suite, fix any regressions.
|
||||
needs: [review]
|
||||
|
||||
- id: submit
|
||||
description: >
|
||||
Submit for merge. Final check: git status, git diff. Commit with
|
||||
clear message. Push and create PR.
|
||||
needs: [test]
|
||||
54
.beads/formulas/towers-of-hanoi.formula.json
Normal file
54
.beads/formulas/towers-of-hanoi.formula.json
Normal file
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"formula": "towers-of-hanoi",
|
||||
"description": "Solve Towers of Hanoi for {disks} disks. Generates 2^{disks} - 1 steps, each a trivial move operation. Demonstrates mechanical structure generation for arbitrarily long workflows.",
|
||||
"version": 1,
|
||||
"vars": {
|
||||
"disks": "{{disks}}",
|
||||
"source_peg": "A",
|
||||
"target_peg": "C",
|
||||
"auxiliary_peg": "B"
|
||||
},
|
||||
"generate": {
|
||||
"for-each": {
|
||||
"var": "move_num",
|
||||
"range": "1..2^{disks}"
|
||||
},
|
||||
"step": {
|
||||
"id": "move-{move_num}",
|
||||
"description": "Move {computed_disk} from {computed_source} to {computed_target}. This is move {move_num} of {total_moves}. Simply execute the move - no decision needed.",
|
||||
"needs": ["move-{move_num - 1}"],
|
||||
"compute": {
|
||||
"disk": "lowest_set_bit({move_num})",
|
||||
"source": "peg_for_disk({disk}, {move_num}, 'source')",
|
||||
"target": "peg_for_disk({disk}, {move_num}, 'target')"
|
||||
}
|
||||
}
|
||||
},
|
||||
"steps": [
|
||||
{
|
||||
"id": "setup",
|
||||
"description": "Verify initial state: {disks} disks stacked on peg {source_peg}. All disks in order (largest on bottom)."
|
||||
},
|
||||
{
|
||||
"id": "solve",
|
||||
"description": "Execute all {total_moves} moves to transfer tower from {source_peg} to {target_peg}.",
|
||||
"needs": ["setup"]
|
||||
},
|
||||
{
|
||||
"id": "verify",
|
||||
"description": "Verify final state: all {disks} disks now on peg {target_peg}. Tower intact, all moves were legal.",
|
||||
"needs": ["solve"]
|
||||
}
|
||||
],
|
||||
"example_3_disk": {
|
||||
"steps": [
|
||||
{"id": "move-1", "description": "Move disk 1 from A to C"},
|
||||
{"id": "move-2", "description": "Move disk 2 from A to B", "needs": ["move-1"]},
|
||||
{"id": "move-3", "description": "Move disk 1 from C to B", "needs": ["move-2"]},
|
||||
{"id": "move-4", "description": "Move disk 3 from A to C", "needs": ["move-3"]},
|
||||
{"id": "move-5", "description": "Move disk 1 from B to A", "needs": ["move-4"]},
|
||||
{"id": "move-6", "description": "Move disk 2 from B to C", "needs": ["move-5"]},
|
||||
{"id": "move-7", "description": "Move disk 1 from A to C", "needs": ["move-6"]}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
# Towers of Hanoi Formula
|
||||
#
|
||||
# Demonstrates that molecule algebra solves the "million step workflow" problem.
|
||||
# LLMs fail on long sequences because errors accumulate (99.9%^1000 ≈ 37% success).
|
||||
# MAKER used voting to reduce error. We use mechanical structure instead.
|
||||
#
|
||||
# Key insight: The move sequence is deterministic. Only execution needs AI.
|
||||
# And each step is trivial: "move disk X from peg Y to peg Z".
|
||||
#
|
||||
# For n disks: 2^n - 1 moves
|
||||
# 20 disks = 1,048,575 moves (the "million step" problem)
|
||||
#
|
||||
# The iterative algorithm (no recursion needed):
|
||||
# For move k (1-indexed):
|
||||
# - disk = largest power of 2 dividing k (disk 1 is smallest)
|
||||
# - direction = computed from disk parity and move number
|
||||
#
|
||||
# This formula uses for-each to generate all moves at cook time.
|
||||
|
||||
formula: towers-of-hanoi
|
||||
description: >
|
||||
Solve Towers of Hanoi for {disks} disks. Generates 2^{disks} - 1 steps,
|
||||
each a trivial move operation. Demonstrates mechanical structure generation
|
||||
for arbitrarily long workflows.
|
||||
version: 1
|
||||
|
||||
vars:
|
||||
disks: "{{disks}}"
|
||||
source_peg: "A"
|
||||
target_peg: "C"
|
||||
auxiliary_peg: "B"
|
||||
|
||||
# The magic: for-each over computed move sequence
|
||||
# Each move is deterministic, computed from move number
|
||||
generate:
|
||||
# This is pseudo-syntax for the runtime expansion we'd need
|
||||
for-each:
|
||||
var: move_num
|
||||
range: "1..2^{disks}" # 1 to 2^n - 1
|
||||
step:
|
||||
id: "move-{move_num}"
|
||||
description: >
|
||||
Move {computed_disk} from {computed_source} to {computed_target}.
|
||||
|
||||
This is move {move_num} of {total_moves}.
|
||||
Simply execute the move - no decision needed.
|
||||
needs:
|
||||
- "move-{move_num - 1}" # Sequential dependency
|
||||
compute:
|
||||
# Disk to move: position of lowest set bit in move_num
|
||||
disk: "lowest_set_bit({move_num})"
|
||||
# Peg calculations based on disk parity and move number
|
||||
source: "peg_for_disk({disk}, {move_num}, 'source')"
|
||||
target: "peg_for_disk({disk}, {move_num}, 'target')"
|
||||
|
||||
# Alternatively, simpler recursive template for smaller N:
|
||||
# (This would need the recursive expansion operator)
|
||||
|
||||
steps:
|
||||
- id: setup
|
||||
description: >
|
||||
Verify initial state: {disks} disks stacked on peg {source_peg}.
|
||||
All disks in order (largest on bottom).
|
||||
|
||||
- id: solve
|
||||
description: >
|
||||
Execute all {total_moves} moves to transfer tower from
|
||||
{source_peg} to {target_peg}.
|
||||
needs: [setup]
|
||||
# This step would be expanded by the generate block above
|
||||
|
||||
- id: verify
|
||||
description: >
|
||||
Verify final state: all {disks} disks now on peg {target_peg}.
|
||||
Tower intact, all moves were legal.
|
||||
needs: [solve]
|
||||
|
||||
# For the prototype, let's show a 3-disk example (7 moves):
|
||||
example_3_disk:
|
||||
# Move sequence for 3 disks: A→C, A→B, C→B, A→C, B→A, B→C, A→C
|
||||
steps:
|
||||
- id: move-1
|
||||
description: "Move disk 1 from A to C"
|
||||
- id: move-2
|
||||
description: "Move disk 2 from A to B"
|
||||
needs: [move-1]
|
||||
- id: move-3
|
||||
description: "Move disk 1 from C to B"
|
||||
needs: [move-2]
|
||||
- id: move-4
|
||||
description: "Move disk 3 from A to C"
|
||||
needs: [move-3]
|
||||
- id: move-5
|
||||
description: "Move disk 1 from B to A"
|
||||
needs: [move-4]
|
||||
- id: move-6
|
||||
description: "Move disk 2 from B to C"
|
||||
needs: [move-5]
|
||||
- id: move-7
|
||||
description: "Move disk 1 from A to C"
|
||||
needs: [move-6]
|
||||
Reference in New Issue
Block a user