diff --git a/.beads/formulas/mol-convoy-feed.formula.toml b/.beads/formulas/mol-convoy-feed.formula.toml new file mode 100644 index 00000000..4ad39a28 --- /dev/null +++ b/.beads/formulas/mol-convoy-feed.formula.toml @@ -0,0 +1,259 @@ +description = """ +Feed stranded convoys by dispatching ready work to available polecats. + +Dogs execute this formula when the Deacon detects a stranded convoy. A convoy +is stranded when it has ready issues (open, unblocked, no assignee) but no +workers are processing them. + +## Dog Contract + +This is infrastructure work. You: +1. Receive convoy ID via variable +2. Load convoy and find ready issues +3. Check idle polecat capacity across rigs +4. Dispatch min(ready_issues, idle_polecats) using gt sling +5. Report actions taken +6. Return to kennel + +## Variables + +| Variable | Source | Description | +|----------|--------|-------------| +| convoy | --var | The stranded convoy ID to feed | + +## Single Pass Design + +Dog doesn't babysit or wait for completion. The workflow is: +1. Find ready issues in convoy +2. Dispatch each to an available polecat +3. Exit immediately + +If convoy is still stranded next Deacon patrol cycle, another dog will be +dispatched. This keeps the system stateless and batch-oriented. + +## Failure Modes + +| Situation | Action | +|-----------|--------| +| Convoy not found | Exit with error, notify Deacon | +| No ready issues | Exit success (false positive, convoy is fine) | +| No idle polecats | Exit success, note in report (will retry next cycle) | +| Sling fails | Continue with remaining issues, note failures |""" +formula = "mol-convoy-feed" +version = 1 + +[squash] +trigger = "on_complete" +template_type = "work" +include_metrics = true + +[[steps]] +id = "load-convoy" +title = "Load convoy and identify ready issues" +description = """ +Load the convoy and find issues ready for dispatch. + +**1. Check assignment:** +```bash +gt hook # Shows convoy in hook_bead or vars +``` + +**2. Load convoy details:** +```bash +gt convoy status {{convoy}} --json +``` + +**3. Identify ready issues:** + +For each tracked issue in the convoy: +```bash +bd show --json +``` + +An issue is "ready" if ALL of these are true: +- status = "open" (NOT in_progress, closed, or hooked) +- not in blocked list (check: bd blocked --json) +- assignee is empty OR assignee session is dead + +Check blocked status: +```bash +bd blocked --json +# If issue ID appears here, it's blocked (skip it) +``` + +Check assignee session if set: +```bash +# If assignee like "gastown/polecats/nux" +tmux has-session -t gt-gastown-polecat-nux 2>/dev/null && echo "alive" || echo "dead" +``` + +**4. Build ready list:** +Collect all ready issues with their metadata: +- Issue ID +- Title +- Priority +- Rig (extracted from prefix) + +Sort by priority (P0 first) for dispatch order. + +**Exit criteria:** Ready issues identified and prioritized.""" + +[[steps]] +id = "check-capacity" +title = "Check polecat capacity across rigs" +needs = ["load-convoy"] +description = """ +Determine how many polecats are available for dispatch. + +**1. For each rig that has ready issues:** +```bash +gt polecats +# Shows polecat status: idle, working, etc. +``` + +**2. Count available capacity:** +Available polecats are those that: +- Exist in the rig's polecat pool +- Currently idle (no hooked work) +- Session is running + +**3. Calculate dispatch count:** +``` +dispatch_count = min(ready_issues, available_polecats) +``` + +If dispatch_count = 0: +- Log: "No capacity available, will retry next cycle" +- Proceed to report step (no dispatches to make) + +**4. Match issues to rigs:** +For each ready issue, determine target rig from issue prefix: +- gt-* issues → gastown rig +- bd-* issues → beads rig +- etc. + +**Exit criteria:** Dispatch plan created with issue→rig mappings.""" + +[[steps]] +id = "dispatch-work" +title = "Dispatch ready issues to polecats" +needs = ["check-capacity"] +description = """ +Sling each ready issue to an available polecat. + +**For each issue in dispatch plan:** + +```bash +# Dispatch issue to the appropriate rig +# This spawns a fresh polecat or assigns to idle one +gt sling + +# Example: +gt sling gt-abc123 gastown +gt sling bd-xyz789 beads +``` + +**Track results:** +For each dispatch: +- Success: Note issue ID, target rig, polecat assigned +- Failure: Note issue ID, error message + +**Important notes:** +- `gt sling` handles polecat selection automatically +- It will spawn a new polecat if none available +- The polecat gets the issue hooked and starts immediately +- Don't wait for polecat to complete - fire and forget + +**If sling fails:** +- Continue with remaining issues +- Note the failure for the report +- Don't escalate individual failures (will retry next cycle) + +**Exit criteria:** All dispatchable issues have been slung.""" + +[[steps]] +id = "report-results" +title = "Generate and send feeding report" +needs = ["dispatch-work"] +description = """ +Create summary report of convoy feeding actions. + +**1. Generate report:** +```markdown +## Convoy Feed Report: {{convoy}} + +**Ready issues found**: {{ready_count}} +**Polecats available**: {{available_count}} +**Issues dispatched**: {{dispatch_count}} + +### Dispatched Work +{{#each dispatched}} +- {{issue_id}}: {{title}} → {{rig}}/{{polecat}} +{{/each}} + +### Skipped (no capacity) +{{#if skipped}} +{{#each skipped}} +- {{issue_id}}: {{title}} (will retry next cycle) +{{/each}} +{{else}} +(none) +{{/if}} + +### Errors +{{#if errors}} +{{#each errors}} +- {{issue_id}}: {{error}} +{{/each}} +{{else}} +(none) +{{/if}} +``` + +**2. Send to Deacon:** +```bash +gt mail send deacon/ -s "Convoy fed: {{convoy}}" -m "$(cat <