fix: Commit embedded formulas for go install @latest (#117)
* fix: Commit embedded formulas for go install @latest The internal/formula/formulas/ directory was gitignored, causing `go install github.com/steveyegge/gastown/cmd/gt@latest` to fail with: pattern formulas/*.formula.json: no matching files found The go:embed directive requires these files at build time, but go install @latest doesn't run go:generate. By committing the generated formulas, users can install directly without cloning. Maintainers should run `go generate ./...` after modifying .beads/formulas/ to keep the embedded copy in sync. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * ci: Add check for committed embedded formulas Adds a new CI job that: 1. Builds without running go:generate (catches missing formulas) 2. Verifies committed formulas match .beads/formulas/ source Also removes redundant go:generate steps from other jobs since formulas are now committed. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore: exclude towers-of-hanoi test formulas from embed These are durability stress test fixtures (pre-computed move sequences), not production formulas users need. Excluding them reduces embedded content by ~10K lines. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: gus <steve.yegge@gmail.com>
This commit is contained in:
committed by
GitHub
parent
4ffdc4fe40
commit
1e76bfd7ce
39
.github/workflows/ci.yml
vendored
39
.github/workflows/ci.yml
vendored
@@ -33,6 +33,36 @@ jobs:
|
||||
fi
|
||||
echo "No .beads/issues.jsonl changes detected"
|
||||
|
||||
# Verify committed formulas allow build without go:generate
|
||||
# This catches issues where go install @latest would fail
|
||||
check-embedded-formulas:
|
||||
name: Check embedded formulas
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.24'
|
||||
|
||||
- name: Build without go:generate
|
||||
run: |
|
||||
# This must succeed with committed formulas only
|
||||
# If this fails, run: go generate ./... && git add -A && git commit
|
||||
go build -v ./cmd/gt
|
||||
|
||||
- name: Verify formulas are in sync
|
||||
run: |
|
||||
# Regenerate and check for differences
|
||||
go generate ./internal/formula/...
|
||||
if ! git diff --exit-code internal/formula/formulas/; then
|
||||
echo ""
|
||||
echo "ERROR: Committed formulas are out of sync with .beads/formulas/"
|
||||
echo "Run: go generate ./... && git add -A && git commit"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
test:
|
||||
name: Test
|
||||
runs-on: ubuntu-latest
|
||||
@@ -49,9 +79,6 @@ jobs:
|
||||
git config --global user.name "CI Bot"
|
||||
git config --global user.email "ci@gastown.test"
|
||||
|
||||
- name: Generate embedded files
|
||||
run: go generate ./internal/formula/...
|
||||
|
||||
- name: Build
|
||||
run: go build -v ./cmd/gt
|
||||
|
||||
@@ -69,9 +96,6 @@ jobs:
|
||||
with:
|
||||
go-version: '1.24'
|
||||
|
||||
- name: Generate embedded files
|
||||
run: go generate ./internal/formula/...
|
||||
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v9
|
||||
with:
|
||||
@@ -97,9 +121,6 @@ jobs:
|
||||
- name: Install beads (bd)
|
||||
run: go install github.com/steveyegge/beads/cmd/bd@latest
|
||||
|
||||
- name: Generate embedded files
|
||||
run: go generate ./internal/formula/...
|
||||
|
||||
- name: Build gt
|
||||
run: go build -v -o gt ./cmd/gt
|
||||
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -34,5 +34,5 @@ state.json
|
||||
# Clone-specific CLAUDE.md (regenerated locally per clone)
|
||||
CLAUDE.md
|
||||
|
||||
# Generated by go:generate from .beads/formulas/
|
||||
internal/formula/formulas/
|
||||
# Embedded formulas are committed so `go install @latest` works
|
||||
# Run `go generate ./...` after modifying .beads/formulas/
|
||||
|
||||
345
internal/formula/formulas/beads-release.formula.toml
Normal file
345
internal/formula/formulas/beads-release.formula.toml
Normal file
@@ -0,0 +1,345 @@
|
||||
description = """
|
||||
Beads release workflow - from version bump to verified release.
|
||||
|
||||
This formula orchestrates a complete release cycle:
|
||||
1. Preflight checks (clean git, up to date)
|
||||
2. Documentation updates (CHANGELOG, info.go)
|
||||
3. Version bump (all components)
|
||||
4. Git operations (commit, tag, push)
|
||||
5. CI verification (GitHub Actions)
|
||||
6. Artifact verification (GitHub, npm, PyPI)
|
||||
7. Local installation update
|
||||
8. Daemon restart
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
bd mol wisp create beads-release --var version=0.37.0
|
||||
```
|
||||
|
||||
Or assign to a polecat:
|
||||
```bash
|
||||
gt sling beads/polecats/p1 --formula beads-release --var version=0.37.0
|
||||
```
|
||||
"""
|
||||
formula = "beads-release"
|
||||
type = "workflow"
|
||||
version = 1
|
||||
|
||||
[vars.version]
|
||||
description = "The semantic version to release (e.g., 0.37.0)"
|
||||
required = true
|
||||
|
||||
[[steps]]
|
||||
id = "preflight-git"
|
||||
title = "Preflight: Check git status"
|
||||
description = """
|
||||
Ensure working tree is clean before starting release.
|
||||
|
||||
```bash
|
||||
git status
|
||||
```
|
||||
|
||||
If there are uncommitted changes, either:
|
||||
- Commit them first
|
||||
- Stash them: `git stash`
|
||||
- Abort and resolve
|
||||
"""
|
||||
|
||||
[[steps]]
|
||||
id = "preflight-pull"
|
||||
title = "Preflight: Pull latest"
|
||||
needs = ["preflight-git"]
|
||||
description = """
|
||||
Ensure we're up to date with origin.
|
||||
|
||||
```bash
|
||||
git pull --rebase
|
||||
```
|
||||
|
||||
Resolve any conflicts before proceeding.
|
||||
"""
|
||||
|
||||
[[steps]]
|
||||
id = "review-changes"
|
||||
title = "Review changes since last release"
|
||||
needs = ["preflight-pull"]
|
||||
description = """
|
||||
Understand what's being released.
|
||||
|
||||
```bash
|
||||
git log $(git describe --tags --abbrev=0)..HEAD --oneline
|
||||
```
|
||||
|
||||
Categorize changes:
|
||||
- Features (feat:)
|
||||
- Fixes (fix:)
|
||||
- Breaking changes
|
||||
- Documentation
|
||||
"""
|
||||
|
||||
[[steps]]
|
||||
id = "update-changelog"
|
||||
title = "Update CHANGELOG.md"
|
||||
needs = ["review-changes"]
|
||||
description = """
|
||||
Write the [Unreleased] section with all changes for {{version}}.
|
||||
|
||||
Format: Keep a Changelog (https://keepachangelog.com)
|
||||
|
||||
Sections:
|
||||
- ### Added
|
||||
- ### Changed
|
||||
- ### Fixed
|
||||
- ### Documentation
|
||||
|
||||
The bump script will stamp the date automatically.
|
||||
"""
|
||||
|
||||
[[steps]]
|
||||
id = "update-info-go"
|
||||
title = "Update info.go versionChanges"
|
||||
needs = ["update-changelog"]
|
||||
description = """
|
||||
Add entry to versionChanges in cmd/bd/info.go.
|
||||
|
||||
This powers `bd info --whats-new` for agents.
|
||||
|
||||
```go
|
||||
"{{version}}": {
|
||||
"summary": "Brief description",
|
||||
"changes": []string{
|
||||
"Key change 1",
|
||||
"Key change 2",
|
||||
},
|
||||
},
|
||||
```
|
||||
|
||||
Focus on workflow-impacting changes agents need to know.
|
||||
"""
|
||||
|
||||
[[steps]]
|
||||
id = "run-bump-script"
|
||||
title = "Run bump-version.sh"
|
||||
needs = ["update-info-go"]
|
||||
description = """
|
||||
Update all component versions atomically.
|
||||
|
||||
```bash
|
||||
./scripts/bump-version.sh {{version}}
|
||||
```
|
||||
|
||||
This updates:
|
||||
- cmd/bd/version.go
|
||||
- .claude-plugin/*.json
|
||||
- integrations/beads-mcp/pyproject.toml
|
||||
- integrations/beads-mcp/src/beads_mcp/__init__.py
|
||||
- npm-package/package.json
|
||||
- Hook templates
|
||||
- README.md
|
||||
- CHANGELOG.md (adds date)
|
||||
"""
|
||||
|
||||
[[steps]]
|
||||
id = "verify-versions"
|
||||
title = "Verify version consistency"
|
||||
needs = ["run-bump-script"]
|
||||
description = """
|
||||
Confirm all versions match {{version}}.
|
||||
|
||||
```bash
|
||||
grep 'Version = ' cmd/bd/version.go
|
||||
jq -r '.version' .claude-plugin/plugin.json
|
||||
jq -r '.version' npm-package/package.json
|
||||
grep 'version = ' integrations/beads-mcp/pyproject.toml
|
||||
```
|
||||
|
||||
All should show {{version}}.
|
||||
"""
|
||||
|
||||
[[steps]]
|
||||
id = "commit-release"
|
||||
title = "Commit release"
|
||||
needs = ["verify-versions"]
|
||||
description = """
|
||||
Stage and commit all version changes.
|
||||
|
||||
```bash
|
||||
git add -A
|
||||
git commit -m "chore: Bump version to {{version}}"
|
||||
```
|
||||
|
||||
Review the commit to ensure all expected files are included.
|
||||
"""
|
||||
|
||||
[[steps]]
|
||||
id = "create-tag"
|
||||
title = "Create release tag"
|
||||
needs = ["commit-release"]
|
||||
description = """
|
||||
Create annotated git tag.
|
||||
|
||||
```bash
|
||||
git tag -a v{{version}} -m "Release v{{version}}"
|
||||
```
|
||||
|
||||
Verify: `git tag -l | tail -5`
|
||||
"""
|
||||
|
||||
[[steps]]
|
||||
id = "push-main"
|
||||
title = "Push to main"
|
||||
needs = ["create-tag"]
|
||||
description = """
|
||||
Push the release commit to origin.
|
||||
|
||||
```bash
|
||||
git push origin main
|
||||
```
|
||||
|
||||
If rejected, someone else pushed. Pull, rebase, try again.
|
||||
"""
|
||||
|
||||
[[steps]]
|
||||
id = "push-tag"
|
||||
title = "Push release tag"
|
||||
needs = ["push-main"]
|
||||
description = """
|
||||
Push the version tag to trigger CI release.
|
||||
|
||||
```bash
|
||||
git push origin v{{version}}
|
||||
```
|
||||
|
||||
This triggers GitHub Actions to build artifacts and publish.
|
||||
"""
|
||||
|
||||
[[steps]]
|
||||
id = "wait-ci"
|
||||
title = "Wait for CI"
|
||||
needs = ["push-tag"]
|
||||
description = """
|
||||
Monitor GitHub Actions for release completion.
|
||||
|
||||
https://github.com/steveyegge/beads/actions
|
||||
|
||||
Expected time: 5-10 minutes
|
||||
|
||||
Watch for:
|
||||
- Build artifacts (all platforms)
|
||||
- Test suite pass
|
||||
- npm publish
|
||||
- PyPI publish
|
||||
"""
|
||||
|
||||
[[steps]]
|
||||
id = "verify-github-release"
|
||||
title = "Verify GitHub release"
|
||||
needs = ["wait-ci"]
|
||||
description = """
|
||||
Check the GitHub releases page.
|
||||
|
||||
https://github.com/steveyegge/beads/releases/tag/v{{version}}
|
||||
|
||||
Verify:
|
||||
- Release created
|
||||
- Binaries attached (linux, darwin, windows)
|
||||
- Checksums present
|
||||
"""
|
||||
|
||||
[[steps]]
|
||||
id = "verify-npm"
|
||||
title = "Verify npm package"
|
||||
needs = ["verify-github-release"]
|
||||
description = """
|
||||
Confirm npm package published.
|
||||
|
||||
```bash
|
||||
npm show @beads/bd version
|
||||
```
|
||||
|
||||
Should show {{version}}.
|
||||
|
||||
Also check: https://www.npmjs.com/package/@beads/bd
|
||||
"""
|
||||
|
||||
[[steps]]
|
||||
id = "verify-pypi"
|
||||
title = "Verify PyPI package"
|
||||
needs = ["verify-github-release"]
|
||||
description = """
|
||||
Confirm PyPI package published.
|
||||
|
||||
```bash
|
||||
pip index versions beads-mcp 2>/dev/null | head -3
|
||||
```
|
||||
|
||||
Or check: https://pypi.org/project/beads-mcp/
|
||||
|
||||
Should show {{version}}.
|
||||
"""
|
||||
|
||||
[[steps]]
|
||||
id = "local-install"
|
||||
title = "Update local installation"
|
||||
needs = ["verify-npm", "verify-pypi"]
|
||||
description = """
|
||||
Update local bd to the new version.
|
||||
|
||||
Option 1 - Homebrew:
|
||||
```bash
|
||||
brew upgrade bd
|
||||
```
|
||||
|
||||
Option 2 - Install script:
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/steveyegge/beads/main/scripts/install.sh | bash
|
||||
```
|
||||
|
||||
Verify:
|
||||
```bash
|
||||
bd --version
|
||||
```
|
||||
|
||||
Should show {{version}}.
|
||||
"""
|
||||
|
||||
[[steps]]
|
||||
id = "restart-daemons"
|
||||
title = "Restart daemons"
|
||||
needs = ["local-install"]
|
||||
description = """
|
||||
Restart bd daemons to pick up new version.
|
||||
|
||||
```bash
|
||||
bd daemons killall
|
||||
```
|
||||
|
||||
Daemons will auto-restart with new version on next bd command.
|
||||
|
||||
Verify:
|
||||
```bash
|
||||
bd daemons list
|
||||
```
|
||||
"""
|
||||
|
||||
[[steps]]
|
||||
id = "release-complete"
|
||||
title = "Release complete"
|
||||
needs = ["restart-daemons"]
|
||||
description = """
|
||||
Release v{{version}} is complete!
|
||||
|
||||
Summary:
|
||||
- All version files updated
|
||||
- Git tag pushed
|
||||
- CI artifacts built
|
||||
- npm and PyPI packages published
|
||||
- Local installation updated
|
||||
- Daemons restarted
|
||||
|
||||
Optional next steps:
|
||||
- Announce on social media
|
||||
- Update documentation site
|
||||
- Close related milestone
|
||||
"""
|
||||
319
internal/formula/formulas/code-review.formula.toml
Normal file
319
internal/formula/formulas/code-review.formula.toml
Normal file
@@ -0,0 +1,319 @@
|
||||
# Code Review Convoy Formula
|
||||
#
|
||||
# A convoy-style formula that spawns multiple polecats in parallel,
|
||||
# each focusing on a different review aspect. Results are collected
|
||||
# and synthesized into a unified review.
|
||||
#
|
||||
# Usage:
|
||||
# gt formula run code-review --pr=123
|
||||
# gt formula run code-review --files="src/*.go"
|
||||
|
||||
description = """
|
||||
Comprehensive code review via parallel specialized reviewers.
|
||||
|
||||
Each leg examines the code from a different perspective. Findings are
|
||||
collected and synthesized into a prioritized, actionable review.
|
||||
|
||||
## Legs (parallel execution)
|
||||
- **correctness**: Logic errors, bugs, edge cases
|
||||
- **performance**: Bottlenecks, efficiency issues
|
||||
- **security**: Vulnerabilities, OWASP concerns
|
||||
- **elegance**: Design clarity, abstraction quality
|
||||
- **resilience**: Error handling, failure modes
|
||||
- **style**: Convention compliance, consistency
|
||||
- **smells**: Anti-patterns, technical debt
|
||||
|
||||
## Execution Model
|
||||
1. Each leg spawns as a separate polecat
|
||||
2. Polecats work in parallel
|
||||
3. Each writes findings to their designated output
|
||||
4. Synthesis step combines all findings into unified review
|
||||
"""
|
||||
formula = "code-review"
|
||||
type = "convoy"
|
||||
version = 1
|
||||
|
||||
# Input variables - provided at runtime
|
||||
[inputs]
|
||||
[inputs.pr]
|
||||
description = "Pull request number to review"
|
||||
type = "number"
|
||||
required_unless = ["files", "branch"]
|
||||
|
||||
[inputs.files]
|
||||
description = "File glob pattern to review"
|
||||
type = "string"
|
||||
required_unless = ["pr", "branch"]
|
||||
|
||||
[inputs.branch]
|
||||
description = "Branch name to review (diff against main)"
|
||||
type = "string"
|
||||
required_unless = ["pr", "files"]
|
||||
|
||||
# Base prompt template - injected into all leg prompts
|
||||
# NOTE: Uses Go text/template syntax (not Handlebars)
|
||||
[prompts]
|
||||
base = """
|
||||
# Code Review Assignment
|
||||
|
||||
You are a specialized code reviewer participating in a convoy review.
|
||||
|
||||
## Context
|
||||
- **Formula**: {{.formula_name}}
|
||||
- **Review target**: {{.target_description}}
|
||||
- **Your focus**: {{.leg.focus}}
|
||||
- **Leg ID**: {{.leg.id}}
|
||||
|
||||
## Files Under Review
|
||||
{{if .pr_number -}}
|
||||
PR #{{.pr_number}}: {{.pr_title}}
|
||||
|
||||
Changed files:
|
||||
{{range .changed_files -}}
|
||||
- {{.path}} (+{{.additions}}/-{{.deletions}})
|
||||
{{end -}}
|
||||
{{else -}}
|
||||
{{range .files -}}
|
||||
- {{.}}
|
||||
{{end -}}
|
||||
{{end}}
|
||||
|
||||
## Your Task
|
||||
{{.leg.description}}
|
||||
|
||||
## Output Requirements
|
||||
Write your findings to: **{{.output_path}}**
|
||||
|
||||
Structure your output as follows:
|
||||
```markdown
|
||||
# {{.leg.title}} Review
|
||||
|
||||
## Summary
|
||||
(1-2 paragraph overview of findings)
|
||||
|
||||
## Critical Issues
|
||||
(P0 - Must fix before merge)
|
||||
- Issue description with file:line reference
|
||||
- Explanation of impact
|
||||
- Suggested fix
|
||||
|
||||
## Major Issues
|
||||
(P1 - Should fix before merge)
|
||||
- ...
|
||||
|
||||
## Minor Issues
|
||||
(P2 - Nice to fix)
|
||||
- ...
|
||||
|
||||
## Observations
|
||||
(Non-blocking notes and suggestions)
|
||||
- ...
|
||||
```
|
||||
|
||||
Use specific file:line references. Be actionable. Prioritize impact.
|
||||
"""
|
||||
|
||||
# Output configuration
|
||||
[output]
|
||||
directory = ".reviews/{{.review_id}}"
|
||||
leg_pattern = "{{.leg.id}}-findings.md"
|
||||
synthesis = "review-summary.md"
|
||||
|
||||
# Leg definitions - each spawns a parallel polecat
|
||||
[[legs]]
|
||||
id = "correctness"
|
||||
title = "Correctness Review"
|
||||
focus = "Logical correctness and edge case handling"
|
||||
description = """
|
||||
Review the code for logical errors and edge case handling.
|
||||
|
||||
**Look for:**
|
||||
- Logic errors and bugs
|
||||
- Off-by-one errors
|
||||
- Null/nil/undefined handling
|
||||
- Unhandled edge cases
|
||||
- Race conditions in concurrent code
|
||||
- Dead code or unreachable branches
|
||||
- Incorrect assumptions in comments vs code
|
||||
- Integer overflow/underflow potential
|
||||
- Floating point comparison issues
|
||||
|
||||
**Questions to answer:**
|
||||
- Does the code do what it claims to do?
|
||||
- What inputs could cause unexpected behavior?
|
||||
- Are all code paths tested or obviously correct?
|
||||
"""
|
||||
|
||||
[[legs]]
|
||||
id = "performance"
|
||||
title = "Performance Review"
|
||||
focus = "Performance bottlenecks and efficiency"
|
||||
description = """
|
||||
Review the code for performance issues.
|
||||
|
||||
**Look for:**
|
||||
- O(n²) or worse algorithms where O(n) is possible
|
||||
- Unnecessary allocations in hot paths
|
||||
- Missing caching opportunities
|
||||
- N+1 query patterns (database or API)
|
||||
- Blocking operations in async contexts
|
||||
- Memory leaks or unbounded growth
|
||||
- Excessive string concatenation
|
||||
- Unoptimized regex or parsing
|
||||
|
||||
**Questions to answer:**
|
||||
- What happens at 10x, 100x, 1000x scale?
|
||||
- Are there obvious optimizations being missed?
|
||||
- Is performance being traded for readability appropriately?
|
||||
"""
|
||||
|
||||
[[legs]]
|
||||
id = "security"
|
||||
title = "Security Review"
|
||||
focus = "Security vulnerabilities and attack surface"
|
||||
description = """
|
||||
Review the code for security vulnerabilities.
|
||||
|
||||
**Look for:**
|
||||
- Input validation gaps
|
||||
- Authentication/authorization bypasses
|
||||
- Injection vulnerabilities (SQL, XSS, command, LDAP)
|
||||
- Sensitive data exposure (logs, errors, responses)
|
||||
- Hardcoded secrets or credentials
|
||||
- Insecure cryptographic usage
|
||||
- Path traversal vulnerabilities
|
||||
- SSRF (Server-Side Request Forgery)
|
||||
- Deserialization vulnerabilities
|
||||
- OWASP Top 10 concerns
|
||||
|
||||
**Questions to answer:**
|
||||
- What can a malicious user do with this code?
|
||||
- What data could be exposed if this fails?
|
||||
- Are there defense-in-depth gaps?
|
||||
"""
|
||||
|
||||
[[legs]]
|
||||
id = "elegance"
|
||||
title = "Elegance Review"
|
||||
focus = "Design clarity and abstraction quality"
|
||||
description = """
|
||||
Review the code for design quality.
|
||||
|
||||
**Look for:**
|
||||
- Unclear abstractions or naming
|
||||
- Functions doing too many things
|
||||
- Missing or over-engineered abstractions
|
||||
- Coupling that should be loose
|
||||
- Dependencies that flow the wrong direction
|
||||
- Unclear data flow or control flow
|
||||
- Magic numbers/strings without explanation
|
||||
- Inconsistent design patterns
|
||||
- Violation of SOLID principles
|
||||
- Reinventing existing utilities
|
||||
|
||||
**Questions to answer:**
|
||||
- Would a new team member understand this?
|
||||
- Does the structure match the problem domain?
|
||||
- Is the complexity justified?
|
||||
"""
|
||||
|
||||
[[legs]]
|
||||
id = "resilience"
|
||||
title = "Resilience Review"
|
||||
focus = "Error handling and failure modes"
|
||||
description = """
|
||||
Review the code for resilience and error handling.
|
||||
|
||||
**Look for:**
|
||||
- Swallowed errors or empty catch blocks
|
||||
- Missing error propagation
|
||||
- Unclear error messages
|
||||
- Insufficient retry/backoff logic
|
||||
- Missing timeout handling
|
||||
- Resource cleanup on failure (files, connections)
|
||||
- Partial failure states
|
||||
- Missing circuit breakers for external calls
|
||||
- Unhelpful panic/crash behavior
|
||||
- Recovery path gaps
|
||||
|
||||
**Questions to answer:**
|
||||
- What happens when external services fail?
|
||||
- Can the system recover from partial failures?
|
||||
- Are errors actionable for operators?
|
||||
"""
|
||||
|
||||
[[legs]]
|
||||
id = "style"
|
||||
title = "Style Review"
|
||||
focus = "Convention compliance and consistency"
|
||||
description = """
|
||||
Review the code for style and convention compliance.
|
||||
|
||||
**Look for:**
|
||||
- Naming convention violations
|
||||
- Formatting inconsistencies
|
||||
- Import organization issues
|
||||
- Comment quality (missing, outdated, or obvious)
|
||||
- Documentation gaps for public APIs
|
||||
- Inconsistent patterns within the codebase
|
||||
- Lint/format violations
|
||||
- Test naming and organization
|
||||
- Log message quality and levels
|
||||
|
||||
**Questions to answer:**
|
||||
- Does this match the rest of the codebase?
|
||||
- Would the style guide approve?
|
||||
- Is the code self-documenting where possible?
|
||||
"""
|
||||
|
||||
[[legs]]
|
||||
id = "smells"
|
||||
title = "Code Smells Review"
|
||||
focus = "Anti-patterns and technical debt"
|
||||
description = """
|
||||
Review the code for code smells and anti-patterns.
|
||||
|
||||
**Look for:**
|
||||
- Long methods (>50 lines is suspicious)
|
||||
- Deep nesting (>3 levels)
|
||||
- Shotgun surgery patterns
|
||||
- Feature envy
|
||||
- Data clumps
|
||||
- Primitive obsession
|
||||
- Temporary fields
|
||||
- Refused bequest
|
||||
- Speculative generality
|
||||
- God classes/functions
|
||||
- Copy-paste code (DRY violations)
|
||||
- TODO/FIXME accumulation
|
||||
|
||||
**Questions to answer:**
|
||||
- What will cause pain during the next change?
|
||||
- What would you refactor if you owned this code?
|
||||
- Is technical debt being added or paid down?
|
||||
"""
|
||||
|
||||
# Synthesis step - combines all leg outputs
|
||||
[synthesis]
|
||||
title = "Review Synthesis"
|
||||
description = """
|
||||
Combine all leg findings into a unified, prioritized review.
|
||||
|
||||
**Your input:**
|
||||
All leg findings from: {{.output.directory}}/
|
||||
|
||||
**Your output:**
|
||||
A synthesized review at: {{.output.directory}}/{{.output.synthesis}}
|
||||
|
||||
**Structure:**
|
||||
1. **Executive Summary** - Overall assessment, merge recommendation
|
||||
2. **Critical Issues** - P0 items from all legs, deduplicated
|
||||
3. **Major Issues** - P1 items, grouped by theme
|
||||
4. **Minor Issues** - P2 items, briefly listed
|
||||
5. **Positive Observations** - What's done well
|
||||
6. **Recommendations** - Actionable next steps
|
||||
|
||||
Deduplicate issues found by multiple legs (note which legs found them).
|
||||
Prioritize by impact and effort. Be actionable.
|
||||
"""
|
||||
depends_on = ["correctness", "performance", "security", "elegance", "resilience", "style", "smells"]
|
||||
333
internal/formula/formulas/design.formula.toml
Normal file
333
internal/formula/formulas/design.formula.toml
Normal file
@@ -0,0 +1,333 @@
|
||||
# Design Convoy Formula
|
||||
#
|
||||
# A convoy-style formula that spawns multiple polecats in parallel,
|
||||
# each exploring a different dimension of a design problem. Results
|
||||
# are synthesized into a unified design document.
|
||||
#
|
||||
# Usage:
|
||||
# gt formula run design --problem="Add notification levels to mayor"
|
||||
# gt formula run design --problem="Redesign the merge queue"
|
||||
|
||||
description = """
|
||||
Structured design exploration via parallel specialized analysts.
|
||||
|
||||
Each leg examines the design problem from a different perspective. Findings
|
||||
are collected and synthesized into a unified design proposal with options.
|
||||
|
||||
## Legs (parallel execution)
|
||||
- **api**: Interface design, ergonomics, developer experience
|
||||
- **data**: Data model, storage, migrations, schema
|
||||
- **ux**: User experience, CLI ergonomics, discoverability
|
||||
- **scale**: Performance at scale, bottlenecks, limits
|
||||
- **security**: Threat model, attack surface, trust boundaries
|
||||
- **integration**: How it fits existing system, compatibility
|
||||
|
||||
## Execution Model
|
||||
1. Each leg spawns as a separate polecat
|
||||
2. Polecats work in parallel
|
||||
3. Each writes analysis to their designated output
|
||||
4. Synthesis step combines all analyses into unified design
|
||||
|
||||
## Output
|
||||
A .designs/<design-id>/ directory containing:
|
||||
- Individual dimension analyses
|
||||
- design-doc.md with unified proposal and decision points
|
||||
"""
|
||||
formula = "design"
|
||||
type = "convoy"
|
||||
version = 1
|
||||
|
||||
# Input variables - provided at runtime
|
||||
[inputs]
|
||||
[inputs.problem]
|
||||
description = "Problem statement or feature request to design"
|
||||
type = "string"
|
||||
required = true
|
||||
|
||||
[inputs.context]
|
||||
description = "Additional context (existing code, constraints, etc.)"
|
||||
type = "string"
|
||||
required = false
|
||||
|
||||
[inputs.scope]
|
||||
description = "Scope hint: 'small' (1 file), 'medium' (package), 'large' (system)"
|
||||
type = "string"
|
||||
default = "medium"
|
||||
|
||||
# Base prompt template - injected into all leg prompts
|
||||
[prompts]
|
||||
base = """
|
||||
# Design Analysis Assignment
|
||||
|
||||
You are a specialized design analyst participating in a convoy design exploration.
|
||||
|
||||
## Context
|
||||
- **Formula**: {{.formula_name}}
|
||||
- **Problem**: {{.problem}}
|
||||
- **Your dimension**: {{.leg.focus}}
|
||||
- **Leg ID**: {{.leg.id}}
|
||||
- **Scope**: {{.scope}}
|
||||
|
||||
{{if .context}}
|
||||
## Additional Context
|
||||
{{.context}}
|
||||
{{end}}
|
||||
|
||||
## Your Task
|
||||
{{.leg.description}}
|
||||
|
||||
## Output Requirements
|
||||
Write your analysis to: **{{.output_path}}**
|
||||
|
||||
Structure your output as follows:
|
||||
```markdown
|
||||
# {{.leg.title}}
|
||||
|
||||
## Summary
|
||||
(1-2 paragraph overview of this dimension)
|
||||
|
||||
## Analysis
|
||||
|
||||
### Key Considerations
|
||||
(Bulleted list of important factors)
|
||||
|
||||
### Options Explored
|
||||
(For each option considered:)
|
||||
#### Option N: <name>
|
||||
- **Description**: What is it?
|
||||
- **Pros**: Benefits
|
||||
- **Cons**: Drawbacks
|
||||
- **Effort**: Low/Medium/High
|
||||
|
||||
### Recommendation
|
||||
(Your recommended approach for this dimension)
|
||||
|
||||
## Constraints Identified
|
||||
(Hard constraints discovered during analysis)
|
||||
|
||||
## Open Questions
|
||||
(Questions needing human input or cross-dimension discussion)
|
||||
|
||||
## Integration Points
|
||||
(How this dimension connects to other dimensions)
|
||||
```
|
||||
|
||||
Be thorough but actionable. Flag decisions needing human input.
|
||||
"""
|
||||
|
||||
# Output configuration
|
||||
[output]
|
||||
directory = ".designs/{{.design_id}}"
|
||||
leg_pattern = "{{.leg.id}}.md"
|
||||
synthesis = "design-doc.md"
|
||||
|
||||
# Leg definitions - each spawns a parallel polecat
|
||||
[[legs]]
|
||||
id = "api"
|
||||
title = "API & Interface Design"
|
||||
focus = "Interface design and developer ergonomics"
|
||||
description = """
|
||||
Analyze the interface design for this feature.
|
||||
|
||||
**Explore:**
|
||||
- Command-line interface: flags, subcommands, ergonomics
|
||||
- Programmatic API: function signatures, return types
|
||||
- Configuration interface: files, environment variables
|
||||
- Error messages and help text
|
||||
- Naming conventions and discoverability
|
||||
- Consistency with existing interfaces
|
||||
|
||||
**Questions to answer:**
|
||||
- How will users discover and learn this feature?
|
||||
- What's the happy path vs edge cases?
|
||||
- Does it follow existing CLI/API patterns?
|
||||
- What would make this a joy to use?
|
||||
|
||||
**Deliverable:** api-design.md with interface proposals
|
||||
"""
|
||||
|
||||
[[legs]]
|
||||
id = "data"
|
||||
title = "Data Model Design"
|
||||
focus = "Data model, storage, and migrations"
|
||||
description = """
|
||||
Analyze the data model requirements for this feature.
|
||||
|
||||
**Explore:**
|
||||
- Data structures: types, relationships, constraints
|
||||
- Storage format: JSON, TOML, SQLite, in-memory
|
||||
- Schema design: fields, indices, normalization
|
||||
- Migration strategy: versioning, backwards compatibility
|
||||
- Data lifecycle: creation, updates, deletion
|
||||
- Persistence vs ephemeral considerations
|
||||
|
||||
**Questions to answer:**
|
||||
- What data needs to persist vs be computed?
|
||||
- How will the data grow over time?
|
||||
- What queries/access patterns are needed?
|
||||
- How do we handle schema evolution?
|
||||
|
||||
**Deliverable:** data-model.md with schema proposals
|
||||
"""
|
||||
|
||||
[[legs]]
|
||||
id = "ux"
|
||||
title = "User Experience Analysis"
|
||||
focus = "User experience and CLI ergonomics"
|
||||
description = """
|
||||
Analyze the user experience implications of this feature.
|
||||
|
||||
**Explore:**
|
||||
- Mental model: how users think about this
|
||||
- Workflow integration: where does this fit in daily use?
|
||||
- Learning curve: progressive disclosure
|
||||
- Error experience: what happens when things go wrong?
|
||||
- Feedback: how does the user know it's working?
|
||||
- Discoverability: --help, docs, examples
|
||||
|
||||
**Questions to answer:**
|
||||
- What's the user's goal when using this?
|
||||
- What's the minimum viable interaction?
|
||||
- How do we handle power users vs beginners?
|
||||
- What would surprise or confuse users?
|
||||
|
||||
**Deliverable:** ux-analysis.md with UX recommendations
|
||||
"""
|
||||
|
||||
[[legs]]
|
||||
id = "scale"
|
||||
title = "Scalability Analysis"
|
||||
focus = "Performance at scale and bottlenecks"
|
||||
description = """
|
||||
Analyze the scalability implications of this feature.
|
||||
|
||||
**Explore:**
|
||||
- Scale dimensions: data size, request rate, user count
|
||||
- Resource usage: memory, CPU, disk, network
|
||||
- Bottlenecks: what limits growth?
|
||||
- Complexity: algorithmic, space, time
|
||||
- Caching opportunities
|
||||
- Degradation modes: what happens at limits?
|
||||
|
||||
**Questions to answer:**
|
||||
- What happens at 10x, 100x, 1000x current scale?
|
||||
- What are the hard limits?
|
||||
- Where should we optimize vs keep simple?
|
||||
- What needs to be lazy vs eager?
|
||||
|
||||
**Deliverable:** scalability.md with performance analysis
|
||||
"""
|
||||
|
||||
[[legs]]
|
||||
id = "security"
|
||||
title = "Security Analysis"
|
||||
focus = "Threat model and attack surface"
|
||||
description = """
|
||||
Analyze the security implications of this feature.
|
||||
|
||||
**Explore:**
|
||||
- Trust boundaries: what trusts what?
|
||||
- Attack surface: new inputs, outputs, permissions
|
||||
- Threat model: who might attack this and how?
|
||||
- Sensitive data: what's exposed or stored?
|
||||
- Authentication/authorization implications
|
||||
- Failure modes: what if security fails?
|
||||
|
||||
**Questions to answer:**
|
||||
- What's the worst case if this is exploited?
|
||||
- What new permissions or access does this need?
|
||||
- How do we validate/sanitize inputs?
|
||||
- Are there defense-in-depth opportunities?
|
||||
|
||||
**Deliverable:** security.md with threat analysis
|
||||
"""
|
||||
|
||||
[[legs]]
|
||||
id = "integration"
|
||||
title = "Integration Analysis"
|
||||
focus = "How it fits existing system"
|
||||
description = """
|
||||
Analyze how this feature integrates with the existing system.
|
||||
|
||||
**Explore:**
|
||||
- Existing components: what does this touch?
|
||||
- Dependencies: what does this need from others?
|
||||
- Dependents: what will depend on this?
|
||||
- Migration path: how do we get from here to there?
|
||||
- Backwards compatibility: what might break?
|
||||
- Testing strategy: how do we verify integration?
|
||||
|
||||
**Questions to answer:**
|
||||
- Where does this code live?
|
||||
- How does it affect existing workflows?
|
||||
- What needs to change in dependent code?
|
||||
- Can we feature-flag or gradually roll out?
|
||||
|
||||
**Deliverable:** integration.md with integration plan
|
||||
"""
|
||||
|
||||
# Synthesis step - combines all leg outputs
|
||||
[synthesis]
|
||||
title = "Design Synthesis"
|
||||
description = """
|
||||
Combine all dimension analyses into a unified design document.
|
||||
|
||||
**Your input:**
|
||||
All dimension analyses from: {{.output.directory}}/
|
||||
|
||||
**Your output:**
|
||||
A synthesized design at: {{.output.directory}}/{{.output.synthesis}}
|
||||
|
||||
**Structure:**
|
||||
```markdown
|
||||
# Design: {{.problem}}
|
||||
|
||||
## Executive Summary
|
||||
(2-3 paragraph overview of proposed design)
|
||||
|
||||
## Problem Statement
|
||||
(Clear statement of what we're solving)
|
||||
|
||||
## Proposed Design
|
||||
|
||||
### Overview
|
||||
(High-level approach)
|
||||
|
||||
### Key Components
|
||||
(Main pieces and how they fit together)
|
||||
|
||||
### Interface
|
||||
(CLI/API summary from api dimension)
|
||||
|
||||
### Data Model
|
||||
(Schema summary from data dimension)
|
||||
|
||||
## Trade-offs and Decisions
|
||||
|
||||
### Decisions Made
|
||||
(Key choices and rationale)
|
||||
|
||||
### Open Questions
|
||||
(Decisions needing human input - highlight these!)
|
||||
|
||||
### Trade-offs
|
||||
(What we're trading off and why)
|
||||
|
||||
## Risks and Mitigations
|
||||
(From security and scale dimensions)
|
||||
|
||||
## Implementation Plan
|
||||
(From integration dimension)
|
||||
|
||||
### Phase 1: MVP
|
||||
### Phase 2: Polish
|
||||
### Phase 3: Future
|
||||
|
||||
## Appendix: Dimension Analyses
|
||||
(Links to full dimension documents)
|
||||
```
|
||||
|
||||
Identify conflicts between dimensions. Flag decisions needing human input.
|
||||
Be concrete and actionable.
|
||||
"""
|
||||
depends_on = ["api", "data", "ux", "scale", "security", "integration"]
|
||||
228
internal/formula/formulas/mol-boot-triage.formula.toml
Normal file
228
internal/formula/formulas/mol-boot-triage.formula.toml
Normal file
@@ -0,0 +1,228 @@
|
||||
description = """
|
||||
Boot triage cycle - the daemon's watchdog for Deacon health.
|
||||
|
||||
Boot is spawned fresh on each daemon tick to decide whether to start/wake/nudge/interrupt
|
||||
the Deacon, or do nothing. This centralizes the "when to wake" decision in an agent that
|
||||
can reason about context rather than relying on mechanical thresholds.
|
||||
|
||||
Boot lifecycle:
|
||||
1. Observe (wisps, mail, git state, tmux panes)
|
||||
2. Decide (start/wake/nudge/interrupt/nothing)
|
||||
3. Act
|
||||
4. Clean inbox (discard stale handoffs)
|
||||
5. Exit (or handoff in non-degraded mode)
|
||||
|
||||
Boot is always fresh - no persistent state between invocations.
|
||||
Handoff mail provides continuity for the next Boot instance.
|
||||
"""
|
||||
formula = "mol-boot-triage"
|
||||
version = 1
|
||||
|
||||
[[steps]]
|
||||
id = "observe"
|
||||
title = "Observe system state"
|
||||
description = """
|
||||
Observe the current system state to inform triage decisions.
|
||||
|
||||
**Step 1: Check Deacon state**
|
||||
```bash
|
||||
# Is Deacon session alive?
|
||||
tmux has-session -t gt-deacon 2>/dev/null && echo "alive" || echo "dead"
|
||||
|
||||
# If alive, what's the pane output showing?
|
||||
gt peek deacon --lines 20
|
||||
```
|
||||
|
||||
**Step 2: Check agent bead state**
|
||||
```bash
|
||||
bd show hq-deacon 2>/dev/null
|
||||
# Look for:
|
||||
# - state: running/working/idle
|
||||
# - last_activity: when was last update?
|
||||
```
|
||||
|
||||
**Step 3: Check recent activity**
|
||||
```bash
|
||||
# Recent feed events
|
||||
gt feed --since 10m --plain | head -20
|
||||
|
||||
# Recent wisps (operational state)
|
||||
ls -lt ~/gt/.beads-wisp/*.wisp.json 2>/dev/null | head -5
|
||||
```
|
||||
|
||||
**Step 4: Check Deacon mail**
|
||||
```bash
|
||||
# Does Deacon have unread mail?
|
||||
gt mail inbox deacon 2>/dev/null | head -10
|
||||
```
|
||||
|
||||
Record observations for the decide step:
|
||||
- deacon_alive: true/false
|
||||
- pane_activity: active/idle/stuck
|
||||
- last_activity_age: duration since last activity
|
||||
- pending_mail: count of unread messages
|
||||
- error_signals: any errors observed
|
||||
"""
|
||||
|
||||
[[steps]]
|
||||
id = "decide"
|
||||
title = "Decide on action"
|
||||
needs = ["observe"]
|
||||
description = """
|
||||
Analyze observations and decide what action to take.
|
||||
|
||||
**Decision Matrix**
|
||||
|
||||
| Deacon State | Pane Activity | Action |
|
||||
|--------------|---------------|--------|
|
||||
| Dead session | N/A | START |
|
||||
| Alive, active output | N/A | NOTHING |
|
||||
| Alive, idle < 5 min | N/A | NOTHING |
|
||||
| Alive, idle 5-15 min | No mail | NOTHING |
|
||||
| Alive, idle 5-15 min | Has mail | NUDGE |
|
||||
| Alive, idle > 15 min | Any | WAKE |
|
||||
| Alive, stuck (errors) | Any | INTERRUPT |
|
||||
|
||||
**Judgment Guidance**
|
||||
|
||||
Agents may take several minutes on legitimate work. Ten minutes or more in edge cases.
|
||||
Don't be too aggressive - false positives are disruptive.
|
||||
|
||||
Signs of stuck:
|
||||
- Same error repeated in pane
|
||||
- Tool prompt waiting indefinitely
|
||||
- Silence with pending mail
|
||||
- Agent reporting issues but not progressing
|
||||
|
||||
Signs of working:
|
||||
- Tool calls in progress
|
||||
- File reads/writes happening
|
||||
- Recent commits or beads updates
|
||||
|
||||
**Output**: Record decision as one of:
|
||||
- NOTHING: Let Deacon continue
|
||||
- NUDGE: Gentle wake signal (gt nudge)
|
||||
- WAKE: Stronger wake (escape + message)
|
||||
- INTERRUPT: Force restart needed
|
||||
- START: Session is dead, start fresh
|
||||
"""
|
||||
|
||||
[[steps]]
|
||||
id = "act"
|
||||
title = "Execute decided action"
|
||||
needs = ["decide"]
|
||||
description = """
|
||||
Execute the action decided in the previous step.
|
||||
|
||||
**NOTHING**
|
||||
No action needed. Log observation and exit.
|
||||
|
||||
**NUDGE**
|
||||
```bash
|
||||
gt nudge deacon "Boot check-in: you have pending work"
|
||||
```
|
||||
|
||||
**WAKE**
|
||||
```bash
|
||||
# Send escape to break any tool waiting
|
||||
tmux send-keys -t gt-deacon Escape
|
||||
|
||||
# Brief pause
|
||||
sleep 1
|
||||
|
||||
# Send wake message
|
||||
gt nudge deacon "Boot wake: please check your inbox and pending work"
|
||||
```
|
||||
|
||||
**INTERRUPT**
|
||||
```bash
|
||||
# This is more aggressive - signals Deacon to restart
|
||||
gt mail send deacon -s "INTERRUPT: Boot detected stuck state" \
|
||||
-m "Boot observed stuck state. Please check your context and consider handoff.
|
||||
|
||||
Observations:
|
||||
- <summary of what was observed>
|
||||
|
||||
If you're making progress, please update your agent bead to reflect activity."
|
||||
```
|
||||
|
||||
**START**
|
||||
```bash
|
||||
# Deacon is dead - daemon will restart it
|
||||
# Just log that we detected this
|
||||
echo "Boot detected dead Deacon session - daemon will restart"
|
||||
```
|
||||
|
||||
Record action taken for status update.
|
||||
"""
|
||||
|
||||
[[steps]]
|
||||
id = "cleanup"
|
||||
title = "Clean stale handoffs"
|
||||
needs = ["act"]
|
||||
description = """
|
||||
Clean up stale handoff messages from Deacon's inbox.
|
||||
|
||||
Handoff messages older than 1 hour are likely stale - the intended recipient
|
||||
either processed them or crashed before seeing them.
|
||||
|
||||
**Step 1: List Deacon inbox**
|
||||
```bash
|
||||
gt mail inbox deacon --json 2>/dev/null
|
||||
```
|
||||
|
||||
**Step 2: Archive stale handoffs**
|
||||
For each message:
|
||||
- Check if subject contains "HANDOFF" or "handoff"
|
||||
- Check if age > 1 hour
|
||||
- If both: archive it
|
||||
|
||||
```bash
|
||||
# For each stale handoff:
|
||||
gt mail archive <message-id>
|
||||
```
|
||||
|
||||
**Step 3: Archive Boot's own old mail**
|
||||
Boot doesn't need persistent inbox. Archive anything processed:
|
||||
```bash
|
||||
gt mail inbox boot --json 2>/dev/null
|
||||
# Archive any messages older than current session
|
||||
```
|
||||
|
||||
Keep the system clean - old handoffs just add noise.
|
||||
"""
|
||||
|
||||
[[steps]]
|
||||
id = "exit"
|
||||
title = "Exit or handoff"
|
||||
needs = ["cleanup"]
|
||||
description = """
|
||||
Complete this Boot cycle.
|
||||
|
||||
**In degraded mode (GT_DEGRADED=true)**
|
||||
Exit directly - no handoff needed:
|
||||
```bash
|
||||
# Log completion
|
||||
echo "Boot triage complete: <action taken>"
|
||||
exit 0
|
||||
```
|
||||
|
||||
**In normal mode**
|
||||
Write brief handoff for next Boot instance:
|
||||
```bash
|
||||
gt mail send boot -s "Boot handoff" -m "Completed triage cycle.
|
||||
Action: <action taken>
|
||||
Observations: <brief summary>
|
||||
Time: $(date)"
|
||||
```
|
||||
|
||||
Then exit. The next daemon tick will spawn a fresh Boot.
|
||||
|
||||
**Update status file**
|
||||
```bash
|
||||
# The gt boot command handles this automatically
|
||||
# Status is written to ~/gt/deacon/dogs/boot/.boot-status.json
|
||||
```
|
||||
|
||||
Boot is ephemeral by design. Each instance runs fresh.
|
||||
"""
|
||||
209
internal/formula/formulas/mol-convoy-cleanup.formula.toml
Normal file
209
internal/formula/formulas/mol-convoy-cleanup.formula.toml
Normal file
@@ -0,0 +1,209 @@
|
||||
description = """
|
||||
Archive completed convoys and notify overseer.
|
||||
|
||||
Dogs work through molecules (poured from this formula) when convoys complete. The Deacon detects completed
|
||||
convoys (all tracked issues closed) and slings this work to a dog for:
|
||||
- Generating convoy summary
|
||||
- Archiving convoy state
|
||||
- Notifying the overseer (Mayor)
|
||||
- Updating activity feed
|
||||
|
||||
## Dog Contract
|
||||
|
||||
This is infrastructure work. You:
|
||||
1. Receive convoy ID via hook_bead
|
||||
2. Generate summary of completed work
|
||||
3. Archive to appropriate location
|
||||
4. Notify stakeholders
|
||||
5. Return to kennel
|
||||
|
||||
## Variables
|
||||
|
||||
| Variable | Source | Description |
|
||||
|----------|--------|-------------|
|
||||
| convoy | hook_bead | The convoy ID to archive |
|
||||
|
||||
## Failure Modes
|
||||
|
||||
| Situation | Action |
|
||||
|-----------|--------|
|
||||
| Convoy not found | Exit with error, notify Deacon |
|
||||
| Archive location full | Create space, retry, or escalate |
|
||||
| Mail send fails | Retry once, then proceed anyway |"""
|
||||
formula = "mol-convoy-cleanup"
|
||||
version = 1
|
||||
|
||||
[squash]
|
||||
trigger = "on_complete"
|
||||
template_type = "work"
|
||||
include_metrics = true
|
||||
|
||||
[[steps]]
|
||||
id = "load-convoy"
|
||||
title = "Load convoy and verify completion"
|
||||
description = """
|
||||
Load the convoy bead and verify it's ready for archival.
|
||||
|
||||
**1. Check your assignment:**
|
||||
```bash
|
||||
gt hook # Shows hook_bead = convoy ID
|
||||
bd show {{convoy}} # Full convoy details
|
||||
```
|
||||
|
||||
**2. Verify convoy is complete:**
|
||||
- Status should be 'closed' or all tracked issues closed
|
||||
- If convoy is still open, exit - Deacon dispatched too early
|
||||
|
||||
```bash
|
||||
bd show {{convoy}}
|
||||
# Check 'tracks' or 'dependencies' field
|
||||
# All tracked issues should be closed
|
||||
```
|
||||
|
||||
**3. Gather convoy metadata:**
|
||||
- Start date (created_at)
|
||||
- End date (last closure timestamp)
|
||||
- Total issues tracked
|
||||
- Contributing polecats
|
||||
|
||||
**Exit criteria:** Convoy loaded, verified complete, metadata gathered."""
|
||||
|
||||
[[steps]]
|
||||
id = "generate-summary"
|
||||
title = "Generate convoy summary"
|
||||
needs = ["load-convoy"]
|
||||
description = """
|
||||
Create a summary of the convoy's completed work.
|
||||
|
||||
**1. Collect tracked issue details:**
|
||||
```bash
|
||||
# For each tracked issue
|
||||
bd show <tracked-id>
|
||||
# Extract: title, type, assignee, duration
|
||||
```
|
||||
|
||||
**2. Calculate statistics:**
|
||||
- Total duration (convoy start to finish)
|
||||
- Issues by type (task, bug, feature)
|
||||
- Contributors (unique assignees)
|
||||
- Commits generated (if tracked)
|
||||
|
||||
**3. Create summary text:**
|
||||
```markdown
|
||||
## Convoy Summary: {{convoy.title}}
|
||||
|
||||
**Duration**: X days/hours
|
||||
**Issues completed**: N
|
||||
|
||||
### Work Breakdown
|
||||
- Tasks: N
|
||||
- Bugs: N
|
||||
- Features: N
|
||||
|
||||
### Contributors
|
||||
- polecat-1: N issues
|
||||
- polecat-2: N issues
|
||||
|
||||
### Key Outcomes
|
||||
- <notable achievement 1>
|
||||
- <notable achievement 2>
|
||||
```
|
||||
|
||||
**Exit criteria:** Summary text generated and ready for notification."""
|
||||
|
||||
[[steps]]
|
||||
id = "archive-convoy"
|
||||
title = "Archive convoy to cold storage"
|
||||
needs = ["generate-summary"]
|
||||
description = """
|
||||
Move convoy from active to archived state.
|
||||
|
||||
**1. Update convoy status:**
|
||||
```bash
|
||||
bd update {{convoy}} --status=archived
|
||||
# Or close if not already closed
|
||||
bd close {{convoy}} --reason="Convoy complete, archived"
|
||||
```
|
||||
|
||||
**2. Generate archive record:**
|
||||
The convoy bead with all metadata is the archive record. Beads retention
|
||||
handles moving it to `.beads/archive/` after the retention period.
|
||||
|
||||
**3. Verify archive:**
|
||||
```bash
|
||||
bd show {{convoy}}
|
||||
# Status should reflect archived state
|
||||
```
|
||||
|
||||
**4. Sync to persist:**
|
||||
```bash
|
||||
bd sync
|
||||
```
|
||||
|
||||
**Exit criteria:** Convoy archived, changes synced."""
|
||||
|
||||
[[steps]]
|
||||
id = "notify-overseer"
|
||||
title = "Send completion notification to overseer"
|
||||
needs = ["archive-convoy"]
|
||||
description = """
|
||||
Notify the Mayor (overseer) of convoy completion.
|
||||
|
||||
**1. Send completion mail:**
|
||||
```bash
|
||||
gt mail send mayor/ -s "Convoy complete: {{convoy.title}}" -m "$(cat <<EOF
|
||||
Convoy {{convoy}} has completed and been archived.
|
||||
|
||||
## Summary
|
||||
{{generated_summary}}
|
||||
|
||||
## Metrics
|
||||
- Duration: {{duration}}
|
||||
- Issues: {{issue_count}}
|
||||
- Contributors: {{contributor_list}}
|
||||
|
||||
This convoy has been archived. View details: bd show {{convoy}}
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
**2. Post to activity feed:**
|
||||
The convoy closure already creates a feed entry. Verify:
|
||||
```bash
|
||||
gt feed --since 5m
|
||||
# Should show convoy completion
|
||||
```
|
||||
|
||||
**Exit criteria:** Overseer notified via mail, activity feed updated."""
|
||||
|
||||
[[steps]]
|
||||
id = "return-to-kennel"
|
||||
title = "Signal completion and return to kennel"
|
||||
needs = ["notify-overseer"]
|
||||
description = """
|
||||
Signal work complete and return to available pool.
|
||||
|
||||
**1. Signal completion to Deacon:**
|
||||
```bash
|
||||
gt mail send deacon/ -s "DOG_DONE $(hostname)" -m "Task: convoy-cleanup
|
||||
Convoy: {{convoy}}
|
||||
Status: COMPLETE
|
||||
Duration: {{work_duration}}
|
||||
|
||||
Ready for next assignment."
|
||||
```
|
||||
|
||||
**2. Clean workspace:**
|
||||
- No convoy-specific state to clean
|
||||
- Workspace should already be clean
|
||||
|
||||
**3. Return to kennel:**
|
||||
Dog returns to available state in the pool. Deacon will assign next work
|
||||
or retire the dog if pool is oversized.
|
||||
|
||||
**Exit criteria:** Deacon notified, dog ready for next work or retirement."""
|
||||
|
||||
[vars]
|
||||
[vars.convoy]
|
||||
description = "The convoy ID to archive"
|
||||
required = true
|
||||
259
internal/formula/formulas/mol-convoy-feed.formula.toml
Normal file
259
internal/formula/formulas/mol-convoy-feed.formula.toml
Normal file
@@ -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 <issue-id> --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 <rig>
|
||||
# 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 <issue-id> <rig>
|
||||
|
||||
# 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 <<EOF
|
||||
Convoy {{convoy}} feeding complete.
|
||||
|
||||
Dispatched: {{dispatch_count}}/{{ready_count}} issues
|
||||
{{#if errors}}Errors: {{error_count}}{{/if}}
|
||||
|
||||
{{report_summary}}
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
**3. Update convoy (optional):**
|
||||
If convoy has a notify field, could add a note about feeding activity.
|
||||
Not required - the dispatch tracking handles visibility.
|
||||
|
||||
**Exit criteria:** Report generated and sent."""
|
||||
|
||||
[[steps]]
|
||||
id = "return-to-kennel"
|
||||
title = "Signal completion and return to kennel"
|
||||
needs = ["report-results"]
|
||||
description = """
|
||||
Signal work complete and return to available pool.
|
||||
|
||||
**1. Signal completion to Deacon:**
|
||||
```bash
|
||||
gt mail send deacon/ -s "DOG_DONE $(hostname)" -m "Task: convoy-feed
|
||||
Convoy: {{convoy}}
|
||||
Ready: {{ready_count}}
|
||||
Dispatched: {{dispatch_count}}
|
||||
Status: COMPLETE
|
||||
|
||||
Ready for next assignment."
|
||||
```
|
||||
|
||||
**2. Return to kennel:**
|
||||
Dog returns to available state in the pool. Deacon will assign next work
|
||||
or retire the dog if pool is oversized.
|
||||
|
||||
**Exit criteria:** Deacon notified, dog ready for next work or retirement."""
|
||||
|
||||
[vars]
|
||||
[vars.convoy]
|
||||
description = "The convoy ID to feed"
|
||||
required = true
|
||||
635
internal/formula/formulas/mol-deacon-patrol.formula.toml
Normal file
635
internal/formula/formulas/mol-deacon-patrol.formula.toml
Normal file
@@ -0,0 +1,635 @@
|
||||
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.
|
||||
|
||||
## Idle Town Principle
|
||||
|
||||
**The Deacon should be silent/invisible when the town is healthy and idle.**
|
||||
|
||||
- Skip HEALTH_CHECK nudges when no active work exists
|
||||
- Sleep 60+ seconds between patrol cycles (longer when idle)
|
||||
- Let the feed subscription wake agents on actual events
|
||||
- The daemon (10-minute heartbeat) is the safety net for dead sessions
|
||||
|
||||
This prevents flooding idle agents with health checks every few seconds.
|
||||
|
||||
## Second-Order Monitoring
|
||||
|
||||
Witnesses send WITNESS_PING messages to verify the Deacon is alive. This
|
||||
prevents the "who watches the watchers" problem - if the Deacon dies,
|
||||
Witnesses detect it and escalate to the Mayor.
|
||||
|
||||
The Deacon's agent bead last_activity timestamp is updated during each patrol
|
||||
cycle. Witnesses check this timestamp to verify health."""
|
||||
formula = "mol-deacon-patrol"
|
||||
version = 4
|
||||
|
||||
[[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
|
||||
```
|
||||
|
||||
**WITNESS_PING**:
|
||||
Witnesses periodically ping to verify Deacon is alive. Simply acknowledge
|
||||
and archive - the fact that you're processing mail proves you're running.
|
||||
Your agent bead last_activity is updated automatically during patrol.
|
||||
```bash
|
||||
gt mail archive <message-id>
|
||||
```
|
||||
|
||||
**HELP / Escalation**:
|
||||
Assess and handle or forward to Mayor.
|
||||
Archive after handling:
|
||||
```bash
|
||||
gt mail archive <message-id>
|
||||
```
|
||||
|
||||
**LIFECYCLE messages**:
|
||||
Polecats reporting completion, refineries reporting merge results.
|
||||
Archive after processing:
|
||||
```bash
|
||||
gt mail archive <message-id>
|
||||
```
|
||||
|
||||
**DOG_DONE messages**:
|
||||
Dogs report completion after infrastructure tasks (orphan-scan, session-gc, etc.).
|
||||
Subject format: `DOG_DONE <hostname>`
|
||||
Body contains: task name, counts, status.
|
||||
```bash
|
||||
# Parse the report, log metrics if needed
|
||||
gt mail read <id>
|
||||
# Archive after noting completion
|
||||
gt mail archive <message-id>
|
||||
```
|
||||
Dogs return to idle automatically. The report is informational - no action needed
|
||||
unless the dog reports errors that require escalation.
|
||||
|
||||
Callbacks may spawn new polecats, update issue state, or trigger other actions.
|
||||
|
||||
**Hygiene principle**: Archive messages after they're fully processed.
|
||||
Keep inbox near-empty - only unprocessed items should remain."""
|
||||
|
||||
[[steps]]
|
||||
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.
|
||||
|
||||
**ZFC-Compliant Observation** (AI observes AI):
|
||||
|
||||
```bash
|
||||
# View pending spawns with captured terminal output
|
||||
gt deacon pending
|
||||
```
|
||||
|
||||
For each pending session, analyze the captured output:
|
||||
- Look for Claude's prompt indicator "> " at the start of a line
|
||||
- If prompt is visible, Claude is ready for input
|
||||
- Make the judgment call yourself - you're the AI observer
|
||||
|
||||
For each ready polecat:
|
||||
```bash
|
||||
# 1. Trigger the polecat
|
||||
gt nudge <session> "Begin."
|
||||
|
||||
# 2. Clear from pending list
|
||||
gt deacon pending <session>
|
||||
```
|
||||
|
||||
This triggers the UserPromptSubmit hook, which injects mail so the polecat sees its assignment.
|
||||
|
||||
**Bootstrap mode** (daemon-only, no AI available):
|
||||
The daemon uses `gt deacon trigger-pending` with regex detection. This ZFC violation is acceptable during cold startup when no AI agent is running yet."""
|
||||
|
||||
[[steps]]
|
||||
id = "gate-evaluation"
|
||||
title = "Evaluate pending async gates"
|
||||
needs = ["inbox-check"]
|
||||
description = """
|
||||
Evaluate pending async gates.
|
||||
|
||||
Gates are async coordination primitives that block until conditions are met.
|
||||
The Deacon is responsible for monitoring gates and closing them when ready.
|
||||
|
||||
**Timer gates** (await_type: timer):
|
||||
Check if elapsed time since creation exceeds the timeout duration.
|
||||
|
||||
```bash
|
||||
# List all open gates
|
||||
bd gate list --json
|
||||
|
||||
# For each timer gate, check if elapsed:
|
||||
# - CreatedAt + Timeout < Now → gate is ready to close
|
||||
# - Close with: bd gate close <id> --reason "Timer elapsed"
|
||||
```
|
||||
|
||||
**GitHub gates** (await_type: gh:run, gh:pr) - handled in separate step.
|
||||
|
||||
**Human/Mail gates** - require external input, skip here.
|
||||
|
||||
After closing a gate, the Waiters field contains mail addresses to notify.
|
||||
Send a brief notification to each waiter that the gate has cleared."""
|
||||
|
||||
[[steps]]
|
||||
id = "check-convoy-completion"
|
||||
title = "Check convoy completion"
|
||||
needs = ["inbox-check"]
|
||||
description = """
|
||||
Check convoy completion status.
|
||||
|
||||
Convoys are coordination beads that track multiple issues across rigs. When all tracked issues close, the convoy auto-closes.
|
||||
|
||||
**Step 1: Find open convoys**
|
||||
```bash
|
||||
bd list --type=convoy --status=open
|
||||
```
|
||||
|
||||
**Step 2: For each open convoy, check tracked issues**
|
||||
```bash
|
||||
bd show <convoy-id>
|
||||
# Look for 'tracks' or 'dependencies' field listing tracked issues
|
||||
```
|
||||
|
||||
**Step 3: If all tracked issues are closed, close the convoy**
|
||||
```bash
|
||||
# Check each tracked issue
|
||||
for issue in tracked_issues:
|
||||
bd show <issue-id>
|
||||
# If status is open/in_progress, convoy stays open
|
||||
# If all are closed (completed, wontfix, etc.), convoy is complete
|
||||
|
||||
# Close convoy when all tracked issues are done
|
||||
bd close <convoy-id> --reason "All tracked issues completed"
|
||||
```
|
||||
|
||||
**Note**: Convoys support cross-prefix tracking (e.g., hq-* convoy can track gt-*, bd-* issues). Use full IDs when checking."""
|
||||
|
||||
[[steps]]
|
||||
id = "resolve-external-deps"
|
||||
title = "Resolve external dependencies"
|
||||
needs = ["check-convoy-completion"]
|
||||
description = """
|
||||
Resolve external dependencies across rigs.
|
||||
|
||||
When an issue in one rig closes, any dependencies in other rigs should be notified. This enables cross-rig coordination without tight coupling.
|
||||
|
||||
**Step 1: Check recent closures from feed**
|
||||
```bash
|
||||
gt feed --since 10m --plain | grep "✓"
|
||||
# Look for recently closed issues
|
||||
```
|
||||
|
||||
**Step 2: For each closed issue, check cross-rig dependents**
|
||||
```bash
|
||||
bd show <closed-issue>
|
||||
# Look at 'blocks' field - these are issues that were waiting on this one
|
||||
# If any blocked issue is in a different rig/prefix, it may now be unblocked
|
||||
```
|
||||
|
||||
**Step 3: Update blocked status**
|
||||
For blocked issues in other rigs, the closure should automatically unblock them (beads handles this). But verify:
|
||||
```bash
|
||||
bd blocked
|
||||
# Should no longer show the previously-blocked issue if dependency is met
|
||||
```
|
||||
|
||||
**Cross-rig scenarios:**
|
||||
- bd-xxx closes → gt-yyy that depended on it is unblocked
|
||||
- External issue closes → internal convoy step can proceed
|
||||
- Rig A issue closes → Rig B issue waiting on it proceeds
|
||||
|
||||
No manual intervention needed if dependencies are properly tracked - this step just validates the propagation occurred."""
|
||||
|
||||
[[steps]]
|
||||
id = "fire-notifications"
|
||||
title = "Fire notifications"
|
||||
needs = ["resolve-external-deps"]
|
||||
description = """
|
||||
Fire notifications for convoy and cross-rig events.
|
||||
|
||||
After convoy completion or cross-rig dependency resolution, notify relevant parties.
|
||||
|
||||
**Convoy completion notifications:**
|
||||
When a convoy closes (all tracked issues done), notify the Overseer:
|
||||
```bash
|
||||
# Convoy gt-convoy-xxx just completed
|
||||
gt mail send mayor/ -s "Convoy complete: <convoy-title>" \\
|
||||
-m "Convoy <id> has completed. All tracked issues closed.
|
||||
Duration: <start to end>
|
||||
Issues: <count>
|
||||
|
||||
Summary: <brief description of what was accomplished>"
|
||||
```
|
||||
|
||||
**Cross-rig resolution notifications:**
|
||||
When a cross-rig dependency resolves, notify the affected rig:
|
||||
```bash
|
||||
# Issue bd-xxx closed, unblocking gt-yyy
|
||||
gt mail send gastown/witness -s "Dependency resolved: <bd-xxx>" \\
|
||||
-m "External dependency bd-xxx has closed.
|
||||
Unblocked: gt-yyy (<title>)
|
||||
This issue may now proceed."
|
||||
```
|
||||
|
||||
**Notification targets:**
|
||||
- Convoy complete → mayor/ (for strategic visibility)
|
||||
- Cross-rig dep resolved → <rig>/witness (for operational awareness)
|
||||
|
||||
Keep notifications brief and actionable. The recipient can run bd show for details."""
|
||||
|
||||
[[steps]]
|
||||
id = "health-scan"
|
||||
title = "Check Witness and Refinery health"
|
||||
needs = ["trigger-pending-spawns", "gate-evaluation", "fire-notifications"]
|
||||
description = """
|
||||
Check Witness and Refinery health for each rig.
|
||||
|
||||
**IMPORTANT: Idle Town Protocol**
|
||||
Before sending health check nudges, check if the town is idle:
|
||||
```bash
|
||||
# Check for active work
|
||||
bd list --status=in_progress --limit=5
|
||||
```
|
||||
|
||||
If NO active work (empty result or only patrol molecules):
|
||||
- **Skip HEALTH_CHECK nudges** - don't disturb idle agents
|
||||
- Just verify sessions exist via status commands
|
||||
- The town should be silent when healthy and idle
|
||||
|
||||
If ACTIVE work exists:
|
||||
- Proceed with health check nudges below
|
||||
|
||||
**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>
|
||||
|
||||
# ONLY if active work exists - health ping (clears backoff as side effect)
|
||||
gt nudge <rig>/witness 'HEALTH_CHECK from deacon'
|
||||
gt nudge <rig>/refinery 'HEALTH_CHECK from deacon'
|
||||
```
|
||||
|
||||
**Health Ping Benefit**: The nudge commands serve dual purposes:
|
||||
1. **Liveness verification** - Agent responds to prove it's alive
|
||||
2. **Backoff reset** - Any nudge resets agent's backoff to base interval
|
||||
|
||||
This ensures patrol agents remain responsive during active work periods.
|
||||
|
||||
**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."""
|
||||
|
||||
[[steps]]
|
||||
id = "zombie-scan"
|
||||
title = "Backup check for zombie polecats"
|
||||
needs = ["health-scan"]
|
||||
description = """
|
||||
Defense-in-depth check for zombie polecats that Witness should have cleaned.
|
||||
|
||||
**Why this exists:**
|
||||
The Witness is responsible for nuking polecats after they complete work (via POLECAT_DONE).
|
||||
This step provides backup detection in case the Witness fails to clean up.
|
||||
|
||||
**Zombie criteria:**
|
||||
- State: idle or done (no active work assigned)
|
||||
- Session: not running (tmux session dead)
|
||||
- No hooked work (nothing pending for this polecat)
|
||||
- Last activity: older than 10 minutes
|
||||
|
||||
**Run the zombie scan:**
|
||||
```bash
|
||||
gt deacon zombie-scan --dry-run
|
||||
```
|
||||
|
||||
**If zombies detected:**
|
||||
1. Review the output to confirm they are truly abandoned
|
||||
2. Run without --dry-run to nuke them:
|
||||
```bash
|
||||
gt deacon zombie-scan
|
||||
```
|
||||
3. This will:
|
||||
- Nuke each zombie polecat
|
||||
- Notify the Mayor about Witness failure
|
||||
- Log the cleanup action
|
||||
|
||||
**If no zombies:**
|
||||
No action needed - Witness is doing its job.
|
||||
|
||||
**Note:** This is a backup mechanism. If you frequently find zombies,
|
||||
investigate why the Witness isn't cleaning up properly."""
|
||||
|
||||
[[steps]]
|
||||
id = "plugin-run"
|
||||
title = "Execute registered plugins"
|
||||
needs = ["zombie-scan"]
|
||||
description = """
|
||||
Execute registered plugins.
|
||||
|
||||
Scan ~/gt/plugins/ for plugin directories. Each plugin has a plugin.md with TOML 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."""
|
||||
|
||||
[[steps]]
|
||||
id = "dog-pool-maintenance"
|
||||
title = "Maintain dog pool"
|
||||
needs = ["health-scan"]
|
||||
description = """
|
||||
Ensure dog pool has available workers for dispatch.
|
||||
|
||||
**Step 1: Check dog pool status**
|
||||
```bash
|
||||
gt dog status
|
||||
# Shows idle/working counts
|
||||
```
|
||||
|
||||
**Step 2: Ensure minimum idle dogs**
|
||||
If idle count is 0 and working count is at capacity, consider spawning:
|
||||
```bash
|
||||
# If no idle dogs available
|
||||
gt dog add <name>
|
||||
# Names: alpha, bravo, charlie, delta, etc.
|
||||
```
|
||||
|
||||
**Step 3: Retire stale dogs (optional)**
|
||||
Dogs that have been idle for >24 hours can be removed to save resources:
|
||||
```bash
|
||||
gt dog status <name>
|
||||
# Check last_active timestamp
|
||||
# If idle > 24h: gt dog remove <name>
|
||||
```
|
||||
|
||||
**Pool sizing guidelines:**
|
||||
- Minimum: 1 idle dog always available
|
||||
- Maximum: 4 dogs total (balance resources vs throughput)
|
||||
- Spawn on demand when pool is empty
|
||||
|
||||
**Exit criteria:** Pool has at least 1 idle dog."""
|
||||
|
||||
[[steps]]
|
||||
id = "orphan-check"
|
||||
title = "Detect abandoned work"
|
||||
needs = ["dog-pool-maintenance"]
|
||||
description = """
|
||||
**DETECT ONLY** - Check for orphaned state and dispatch to dog if found.
|
||||
|
||||
**Step 1: Quick orphan scan**
|
||||
```bash
|
||||
# Check for in_progress issues with dead assignees
|
||||
bd list --status=in_progress --json | head -20
|
||||
```
|
||||
|
||||
For each in_progress issue, check if assignee session exists:
|
||||
```bash
|
||||
tmux has-session -t <session> 2>/dev/null && echo "alive" || echo "orphan"
|
||||
```
|
||||
|
||||
**Step 2: If orphans detected, dispatch to dog**
|
||||
```bash
|
||||
# Sling orphan-scan formula to an idle dog
|
||||
gt sling mol-orphan-scan deacon/dogs --var scope=town
|
||||
```
|
||||
|
||||
**Important:** Do NOT fix orphans inline. Dogs handle recovery.
|
||||
The Deacon's job is detection and dispatch, not execution.
|
||||
|
||||
**Step 3: If no orphans detected**
|
||||
Skip dispatch - nothing to do.
|
||||
|
||||
**Exit criteria:** Orphan scan dispatched to dog (if needed)."""
|
||||
|
||||
[[steps]]
|
||||
id = "session-gc"
|
||||
title = "Detect cleanup needs"
|
||||
needs = ["orphan-check"]
|
||||
description = """
|
||||
**DETECT ONLY** - Check if cleanup is needed and dispatch to dog.
|
||||
|
||||
**Step 1: Preview cleanup needs**
|
||||
```bash
|
||||
gt doctor -v
|
||||
# Check output for issues that need cleaning
|
||||
```
|
||||
|
||||
**Step 2: If cleanup needed, dispatch to dog**
|
||||
```bash
|
||||
# Sling session-gc formula to an idle dog
|
||||
gt sling mol-session-gc deacon/dogs --var mode=conservative
|
||||
```
|
||||
|
||||
**Important:** Do NOT run `gt doctor --fix` inline. Dogs handle cleanup.
|
||||
The Deacon stays lightweight - detection only.
|
||||
|
||||
**Step 3: If nothing to clean**
|
||||
Skip dispatch - system is healthy.
|
||||
|
||||
**Cleanup types (for reference):**
|
||||
- orphan-sessions: Dead tmux sessions
|
||||
- orphan-processes: Orphaned Claude processes
|
||||
- wisp-gc: Old wisps past retention
|
||||
|
||||
**Exit criteria:** Session GC dispatched to dog (if needed)."""
|
||||
|
||||
[[steps]]
|
||||
id = "log-maintenance"
|
||||
title = "Rotate logs and prune state"
|
||||
needs = ["session-gc"]
|
||||
description = """
|
||||
Maintain daemon logs and state files.
|
||||
|
||||
**Step 1: Check daemon.log size**
|
||||
```bash
|
||||
# Get log file size
|
||||
ls -la ~/.beads/daemon*.log 2>/dev/null || ls -la ~/gt/.beads/daemon*.log 2>/dev/null
|
||||
```
|
||||
|
||||
If daemon.log exceeds 10MB:
|
||||
```bash
|
||||
# Rotate with date suffix and gzip
|
||||
LOGFILE="$HOME/gt/.beads/daemon.log"
|
||||
if [ -f "$LOGFILE" ] && [ $(stat -f%z "$LOGFILE" 2>/dev/null || stat -c%s "$LOGFILE") -gt 10485760 ]; then
|
||||
DATE=$(date +%Y-%m-%dT%H-%M-%S)
|
||||
mv "$LOGFILE" "${LOGFILE%.log}-${DATE}.log"
|
||||
gzip "${LOGFILE%.log}-${DATE}.log"
|
||||
fi
|
||||
```
|
||||
|
||||
**Step 2: Archive old daemon logs**
|
||||
|
||||
Clean up daemon logs older than 7 days:
|
||||
```bash
|
||||
find ~/gt/.beads/ -name "daemon-*.log.gz" -mtime +7 -delete
|
||||
```
|
||||
|
||||
**Step 3: Prune state.json of dead sessions**
|
||||
|
||||
The state.json tracks active sessions. Prune entries for sessions that no longer exist:
|
||||
```bash
|
||||
# Check for stale session entries
|
||||
gt daemon status --json 2>/dev/null
|
||||
```
|
||||
|
||||
If state.json references sessions not in tmux:
|
||||
- Remove the stale entries
|
||||
- The daemon's internal cleanup should handle this, but verify
|
||||
|
||||
**Note**: Log rotation prevents disk bloat from long-running daemons.
|
||||
State pruning keeps runtime state accurate."""
|
||||
|
||||
[[steps]]
|
||||
id = "patrol-cleanup"
|
||||
title = "End-of-cycle inbox hygiene"
|
||||
needs = ["log-maintenance"]
|
||||
description = """
|
||||
Verify inbox hygiene before ending patrol cycle.
|
||||
|
||||
**Step 1: Check inbox state**
|
||||
```bash
|
||||
gt mail inbox
|
||||
```
|
||||
|
||||
Inbox should be EMPTY or contain only just-arrived unprocessed messages.
|
||||
|
||||
**Step 2: Archive any remaining processed messages**
|
||||
|
||||
All message types should have been archived during inbox-check processing:
|
||||
- WITNESS_PING → archived after acknowledging
|
||||
- HELP/Escalation → archived after handling
|
||||
- LIFECYCLE → archived after processing
|
||||
|
||||
If any were missed:
|
||||
```bash
|
||||
# For each stale message found:
|
||||
gt mail archive <message-id>
|
||||
```
|
||||
|
||||
**Goal**: Inbox should have ≤2 active messages at end of cycle.
|
||||
Deacon mail should flow through quickly - no accumulation."""
|
||||
|
||||
[[steps]]
|
||||
id = "context-check"
|
||||
title = "Check own context limit"
|
||||
needs = ["patrol-cleanup"]
|
||||
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."""
|
||||
|
||||
[[steps]]
|
||||
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 60 seconds minimum** before next patrol cycle
|
||||
- If town is idle (no in_progress work), sleep longer (2-5 minutes)
|
||||
- Return to inbox-check step
|
||||
|
||||
**Why longer sleep?**
|
||||
- Idle agents should not be disturbed
|
||||
- Health checks every few seconds flood inboxes and waste context
|
||||
- The daemon (10-minute heartbeat) is the safety net for dead sessions
|
||||
- Active work triggers feed events, which wake agents naturally
|
||||
|
||||
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."""
|
||||
208
internal/formula/formulas/mol-dep-propagate.formula.toml
Normal file
208
internal/formula/formulas/mol-dep-propagate.formula.toml
Normal file
@@ -0,0 +1,208 @@
|
||||
description = """
|
||||
Propagate cross-rig dependency resolution.
|
||||
|
||||
Dogs work through molecules (poured from this formula) when dependencies resolve across rig boundaries.
|
||||
When an issue in one rig closes, dependent issues in other rigs may unblock.
|
||||
This formula handles:
|
||||
- Finding cross-rig dependents
|
||||
- Notifying affected rigs
|
||||
- Updating blocked status
|
||||
- Triggering work dispatch if appropriate
|
||||
|
||||
## Dog Contract
|
||||
|
||||
This is infrastructure work. You:
|
||||
1. Receive closed issue ID via hook_bead
|
||||
2. Find all cross-rig dependents (issues in other rigs blocked by this)
|
||||
3. Notify affected Witnesses
|
||||
4. Optionally trigger dispatch if issues are now ready
|
||||
5. Return to kennel
|
||||
|
||||
## Variables
|
||||
|
||||
| Variable | Source | Description |
|
||||
|----------|--------|-------------|
|
||||
| resolved_issue | hook_bead | The issue that just closed |
|
||||
|
||||
## Why Dogs?
|
||||
|
||||
Cross-rig work requires multi-rig worktrees. Dogs have these, polecats don't.
|
||||
The Deacon detects the closure, but the propagation needs rig access."""
|
||||
formula = "mol-dep-propagate"
|
||||
version = 1
|
||||
|
||||
[squash]
|
||||
trigger = "on_complete"
|
||||
template_type = "work"
|
||||
include_metrics = true
|
||||
|
||||
[[steps]]
|
||||
id = "load-resolved-issue"
|
||||
title = "Load resolved issue and find dependents"
|
||||
description = """
|
||||
Load the closed issue and identify cross-rig dependents.
|
||||
|
||||
**1. Check your assignment:**
|
||||
```bash
|
||||
gt hook # Shows hook_bead = resolved issue ID
|
||||
bd show {{resolved_issue}} # Full issue details
|
||||
```
|
||||
|
||||
**2. Verify issue is closed:**
|
||||
```bash
|
||||
bd show {{resolved_issue}}
|
||||
# Status should be 'closed' or similar terminal state
|
||||
```
|
||||
|
||||
**3. Find dependents (issues blocked by this one):**
|
||||
```bash
|
||||
bd show {{resolved_issue}}
|
||||
# Look at 'blocks' field - these are issues waiting on this one
|
||||
```
|
||||
|
||||
**4. Identify cross-rig dependents:**
|
||||
- Same-rig dependents: Already handled by local beads (automatic unblock)
|
||||
- Cross-rig dependents: Different prefix (e.g., gt- vs bd-) need propagation
|
||||
|
||||
```bash
|
||||
# Example: resolved_issue is bd-xxx, blocks gt-yyy
|
||||
# gt-yyy is cross-rig and needs notification
|
||||
```
|
||||
|
||||
**Exit criteria:** Resolved issue loaded, cross-rig dependents identified."""
|
||||
|
||||
[[steps]]
|
||||
id = "update-blocked-status"
|
||||
title = "Update blocked status in affected rigs"
|
||||
needs = ["load-resolved-issue"]
|
||||
description = """
|
||||
Update the blocked status for cross-rig dependents.
|
||||
|
||||
**1. For each cross-rig dependent:**
|
||||
```bash
|
||||
# Navigate to the rig containing the dependent issue
|
||||
# Dogs have multi-rig worktrees for this
|
||||
|
||||
bd show <dependent-id>
|
||||
# Check if this was the only blocker
|
||||
```
|
||||
|
||||
**2. Check if now unblocked:**
|
||||
```bash
|
||||
bd blocked <dependent-id>
|
||||
# If empty or only shows other blockers, issue is now unblocked
|
||||
```
|
||||
|
||||
**3. Verify automatic unblock worked:**
|
||||
Beads should auto-update blocked status when dependencies close.
|
||||
This step verifies and fixes if needed:
|
||||
```bash
|
||||
# If still showing as blocked by resolved issue (shouldn't happen):
|
||||
bd dep remove <dependent-id> {{resolved_issue}}
|
||||
```
|
||||
|
||||
**Exit criteria:** All cross-rig dependents have updated blocked status."""
|
||||
|
||||
[[steps]]
|
||||
id = "notify-witnesses"
|
||||
title = "Notify affected rig Witnesses"
|
||||
needs = ["update-blocked-status"]
|
||||
description = """
|
||||
Send notifications to Witnesses of affected rigs.
|
||||
|
||||
**1. Group dependents by rig:**
|
||||
- gastown/witness: for gt-* issues
|
||||
- beads/witness: for bd-* issues
|
||||
- etc.
|
||||
|
||||
**2. For each affected rig, send notification:**
|
||||
```bash
|
||||
gt mail send <rig>/witness -s "Dependency resolved: {{resolved_issue}}" -m "$(cat <<EOF
|
||||
External dependency has closed, unblocking work in your rig.
|
||||
|
||||
## Resolved Issue
|
||||
- ID: {{resolved_issue}}
|
||||
- Title: {{resolved_issue.title}}
|
||||
- Rig: {{resolved_issue.prefix}}
|
||||
|
||||
## Unblocked in Your Rig
|
||||
{{range dependent}}
|
||||
- {{dependent.id}}: {{dependent.title}} ({{dependent.status}})
|
||||
{{end}}
|
||||
|
||||
These issues may now proceed. Check bd ready for available work.
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
**3. Log notification:**
|
||||
Note which Witnesses were notified for audit trail.
|
||||
|
||||
**Exit criteria:** All affected Witnesses notified."""
|
||||
|
||||
[[steps]]
|
||||
id = "trigger-dispatch"
|
||||
title = "Optionally trigger work dispatch"
|
||||
needs = ["notify-witnesses"]
|
||||
description = """
|
||||
Trigger work dispatch for newly-unblocked issues if appropriate.
|
||||
|
||||
**1. For each unblocked issue, check if ready for work:**
|
||||
```bash
|
||||
bd show <issue-id>
|
||||
# Check:
|
||||
# - Status: should be 'open' (not already in_progress)
|
||||
# - Priority: high priority may warrant immediate dispatch
|
||||
# - No other blockers: bd blocked should be empty
|
||||
```
|
||||
|
||||
**2. Decision: trigger dispatch?**
|
||||
|
||||
| Condition | Action |
|
||||
|-----------|--------|
|
||||
| High priority (P0-P1) + open + unblocked | Recommend immediate dispatch |
|
||||
| Medium priority (P2) + open + unblocked | Note in Witness notification |
|
||||
| Low priority (P3-P4) | Let Witness handle in next patrol |
|
||||
|
||||
**3. If triggering dispatch:**
|
||||
```bash
|
||||
# For high priority, suggest to Mayor:
|
||||
gt mail send mayor/ -s "High-priority work unblocked: <issue>" -m "..."
|
||||
```
|
||||
|
||||
Usually, the Witness notification (previous step) is sufficient - Witnesses
|
||||
handle their own dispatch decisions.
|
||||
|
||||
**Exit criteria:** Dispatch recommendations sent where appropriate."""
|
||||
|
||||
[[steps]]
|
||||
id = "return-to-kennel"
|
||||
title = "Signal completion and return to kennel"
|
||||
needs = ["trigger-dispatch"]
|
||||
description = """
|
||||
Signal work complete and return to available pool.
|
||||
|
||||
**1. Signal completion to Deacon:**
|
||||
```bash
|
||||
gt mail send deacon/ -s "DOG_DONE $(hostname)" -m "Task: dep-propagate
|
||||
Resolved: {{resolved_issue}}
|
||||
Cross-rig dependents: {{dependent_count}}
|
||||
Witnesses notified: {{witness_list}}
|
||||
Status: COMPLETE
|
||||
|
||||
Ready for next assignment."
|
||||
```
|
||||
|
||||
**2. Update activity feed:**
|
||||
The propagation creates implicit feed entries (dependency updates).
|
||||
No explicit entry needed.
|
||||
|
||||
**3. Return to kennel:**
|
||||
Dog returns to available state in the pool.
|
||||
|
||||
**Exit criteria:** Deacon notified, dog ready for next work or retirement."""
|
||||
|
||||
[vars]
|
||||
[vars.resolved_issue]
|
||||
description = "The issue ID that just closed and needs propagation"
|
||||
required = true
|
||||
227
internal/formula/formulas/mol-digest-generate.formula.toml
Normal file
227
internal/formula/formulas/mol-digest-generate.formula.toml
Normal file
@@ -0,0 +1,227 @@
|
||||
description = """
|
||||
Generate daily digest for overseer (Mayor).
|
||||
|
||||
Dogs work through molecules (poured from this formula) on a scheduled basis (daily, or triggered by plugin)
|
||||
to create summary digests of Gas Town activity. This aggregates:
|
||||
- Work completed across all rigs
|
||||
- Issues filed and closed
|
||||
- Incidents and escalations
|
||||
- Agent health metrics
|
||||
- Key statistics and trends
|
||||
|
||||
## Dog Contract
|
||||
|
||||
This is infrastructure work. You:
|
||||
1. Receive digest period via hook_bead (e.g., daily, weekly)
|
||||
2. Collect data from all rigs you have access to
|
||||
3. Generate formatted digest
|
||||
4. Send to overseer
|
||||
5. Archive digest as bead
|
||||
6. Return to kennel
|
||||
|
||||
## Variables
|
||||
|
||||
| Variable | Source | Description |
|
||||
|----------|--------|-------------|
|
||||
| period | hook_bead | Time period for digest (daily, weekly) |
|
||||
| since | computed | Start timestamp for data collection |
|
||||
| until | computed | End timestamp (usually now) |
|
||||
|
||||
## Why Dogs?
|
||||
|
||||
Digest generation requires reading from multiple rigs. Dogs have multi-rig
|
||||
worktrees. This is also a periodic task that doesn't need a dedicated polecat."""
|
||||
formula = "mol-digest-generate"
|
||||
version = 1
|
||||
|
||||
[squash]
|
||||
trigger = "on_complete"
|
||||
template_type = "work"
|
||||
include_metrics = true
|
||||
|
||||
[[steps]]
|
||||
id = "determine-period"
|
||||
title = "Determine digest time period"
|
||||
description = """
|
||||
Establish the time range for this digest.
|
||||
|
||||
**1. Check assignment:**
|
||||
```bash
|
||||
gt hook # Shows period type
|
||||
```
|
||||
|
||||
**2. Calculate time range:**
|
||||
|
||||
| Period | Since | Until |
|
||||
|--------|-------|-------|
|
||||
| daily | Yesterday 00:00 | Today 00:00 |
|
||||
| weekly | Last Monday 00:00 | This Monday 00:00 |
|
||||
| custom | From hook_bead | From hook_bead |
|
||||
|
||||
```bash
|
||||
# For daily digest
|
||||
since=$(date -v-1d +%Y-%m-%dT00:00:00)
|
||||
until=$(date +%Y-%m-%dT00:00:00)
|
||||
```
|
||||
|
||||
**3. Record period for reporting:**
|
||||
Note the exact timestamps for the digest header.
|
||||
|
||||
**Exit criteria:** Time period established with precise timestamps."""
|
||||
|
||||
[[steps]]
|
||||
id = "collect-rig-data"
|
||||
title = "Collect activity data from all rigs"
|
||||
needs = ["determine-period"]
|
||||
description = """
|
||||
Gather activity data from each rig in the town.
|
||||
|
||||
**1. List accessible rigs:**
|
||||
```bash
|
||||
gt rigs
|
||||
# Returns list of rigs: gastown, beads, etc.
|
||||
```
|
||||
|
||||
**2. For each rig, collect:**
|
||||
|
||||
a) **Issues filed and closed:**
|
||||
```bash
|
||||
# From rig beads
|
||||
bd list --created-after={{since}} --created-before={{until}}
|
||||
bd list --status=closed --updated-after={{since}}
|
||||
```
|
||||
|
||||
b) **Agent activity:**
|
||||
```bash
|
||||
gt polecats <rig> # Polecat activity
|
||||
gt feed --since={{since}} # Activity feed entries
|
||||
```
|
||||
|
||||
c) **Merges:**
|
||||
```bash
|
||||
# Git log for merges to main
|
||||
git -C <rig-path> log --merges --since={{since}} --oneline main
|
||||
```
|
||||
|
||||
d) **Incidents:**
|
||||
```bash
|
||||
# Issues tagged as incident or high-priority
|
||||
bd list --label=incident --created-after={{since}}
|
||||
```
|
||||
|
||||
**3. Aggregate across rigs:**
|
||||
Sum counts, collect notable items, identify trends.
|
||||
|
||||
**Exit criteria:** Raw data collected from all accessible rigs."""
|
||||
|
||||
[[steps]]
|
||||
id = "generate-digest"
|
||||
title = "Generate formatted digest"
|
||||
needs = ["collect-rig-data"]
|
||||
description = """
|
||||
Transform collected data into formatted digest.
|
||||
|
||||
**1. Calculate summary statistics:**
|
||||
- Total issues filed
|
||||
- Total issues closed
|
||||
- Net change (closed - filed)
|
||||
- By type (task, bug, feature)
|
||||
- By rig
|
||||
|
||||
**2. Identify highlights:**
|
||||
- Biggest completions (epics, large features)
|
||||
- Incidents (any P0/P1 issues)
|
||||
- Notable trends (increasing backlog, fast closure rate)
|
||||
|
||||
**3. Generate digest text:**
|
||||
```markdown
|
||||
# Gas Town Daily Digest: {{date}}
|
||||
|
||||
## Summary
|
||||
- **Issues filed**: N (tasks: X, bugs: Y, features: Z)
|
||||
- **Issues closed**: N
|
||||
- **Net change**: +/-N
|
||||
|
||||
## By Rig
|
||||
| Rig | Filed | Closed | Active Polecats |
|
||||
|-----|-------|--------|-----------------|
|
||||
| gastown | X | Y | Z |
|
||||
| beads | X | Y | Z |
|
||||
|
||||
## Highlights
|
||||
### Completed
|
||||
- {{epic or feature}} - completed by {{polecat}}
|
||||
|
||||
### Incidents
|
||||
- {{incident summary if any}}
|
||||
|
||||
## Agent Health
|
||||
- Polecats spawned: N
|
||||
- Polecats retired: N
|
||||
- Average work duration: Xh
|
||||
|
||||
## Trends
|
||||
- Backlog: {{increasing/stable/decreasing}}
|
||||
- Throughput: {{issues/day}}
|
||||
```
|
||||
|
||||
**Exit criteria:** Formatted digest ready for delivery."""
|
||||
|
||||
[[steps]]
|
||||
id = "send-digest"
|
||||
title = "Send digest to overseer"
|
||||
needs = ["generate-digest"]
|
||||
description = """
|
||||
Deliver digest to the Mayor.
|
||||
|
||||
**1. Send via mail:**
|
||||
```bash
|
||||
gt mail send mayor/ -s "Gas Town Digest: {{date}}" -m "$(cat <<EOF
|
||||
{{formatted_digest}}
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
**2. Archive as bead:**
|
||||
Create a digest bead for permanent record:
|
||||
```bash
|
||||
bd create --title="Digest: {{date}}" --type=digest \
|
||||
--description="{{formatted_digest}}" \
|
||||
--label=digest,{{period}}
|
||||
```
|
||||
|
||||
**3. Sync:**
|
||||
```bash
|
||||
bd sync
|
||||
```
|
||||
|
||||
**Exit criteria:** Digest sent to Mayor and archived as bead."""
|
||||
|
||||
[[steps]]
|
||||
id = "return-to-kennel"
|
||||
title = "Signal completion and return to kennel"
|
||||
needs = ["send-digest"]
|
||||
description = """
|
||||
Signal work complete and return to available pool.
|
||||
|
||||
**1. Signal completion to Deacon:**
|
||||
```bash
|
||||
gt mail send deacon/ -s "DOG_DONE $(hostname)" -m "Task: digest-generate
|
||||
Period: {{period}}
|
||||
Date range: {{since}} to {{until}}
|
||||
Status: COMPLETE
|
||||
|
||||
Digest sent to Mayor.
|
||||
Ready for next assignment."
|
||||
```
|
||||
|
||||
**2. Return to kennel:**
|
||||
Dog returns to available state in the pool.
|
||||
|
||||
**Exit criteria:** Deacon notified, dog ready for next work."""
|
||||
|
||||
[vars]
|
||||
[vars.period]
|
||||
description = "The digest period type (daily, weekly, custom)"
|
||||
required = true
|
||||
default = "daily"
|
||||
62
internal/formula/formulas/mol-gastown-boot.formula.json
Normal file
62
internal/formula/formulas/mol-gastown-boot.formula.json
Normal file
@@ -0,0 +1,62 @@
|
||||
{
|
||||
"formula": "mol-gastown-boot",
|
||||
"description": "Mayor bootstraps Gas Town via a verification-gated lifecycle molecule.\n\n## Purpose\nWhen Mayor executes \"boot up gas town\", this proto provides the workflow.\nEach step has action + verification - steps stay open until outcome is confirmed.\n\n## Key Principles\n1. **Verification-gated steps** - Not \"command ran\" but \"outcome confirmed\"\n2. **gt peek for verification** - Capture session output to detect stalls\n3. **gt nudge for recovery** - Reliable message delivery to unstick agents\n4. **Parallel where possible** - Witnesses and refineries can start in parallel\n5. **Ephemeral execution** - Boot is a wisp, squashed to digest after completion\n\n## Execution\n```bash\nbd mol wisp mol-gastown-boot # Create wisp\n```",
|
||||
"version": 1,
|
||||
"steps": [
|
||||
{
|
||||
"id": "ensure-daemon",
|
||||
"title": "Ensure daemon",
|
||||
"description": "Verify the Gas Town daemon is running.\n\n## Action\n```bash\ngt daemon status || gt daemon start\n```\n\n## Verify\n1. Daemon PID file exists: `~/.gt/daemon.pid`\n2. Process is alive: `kill -0 $(cat ~/.gt/daemon.pid)`\n3. Daemon responds: `gt daemon status` returns success\n\n## OnFail\nCannot start daemon. Log error and continue - some commands work without daemon."
|
||||
},
|
||||
{
|
||||
"id": "ensure-deacon",
|
||||
"title": "Ensure deacon",
|
||||
"needs": ["ensure-daemon"],
|
||||
"description": "Start the Deacon and verify patrol mode is active.\n\n## Action\n```bash\ngt deacon start\n```\n\n## Verify\n1. Session exists: `tmux has-session -t gt-deacon 2>/dev/null`\n2. Not stalled: `gt peek deacon/` does NOT show \"> Try\" prompt\n3. Heartbeat fresh: `deacon/heartbeat.json` modified < 2 min ago\n\n## OnStall\n```bash\ngt nudge deacon/ \"Start patrol.\"\nsleep 30\n# Re-verify\n```"
|
||||
},
|
||||
{
|
||||
"id": "ensure-witnesses",
|
||||
"title": "Ensure witnesses",
|
||||
"needs": ["ensure-deacon"],
|
||||
"type": "parallel",
|
||||
"description": "Parallel container: Start all rig witnesses.\n\nChildren execute in parallel. Container completes when all children complete.",
|
||||
"children": [
|
||||
{
|
||||
"id": "ensure-gastown-witness",
|
||||
"title": "Ensure gastown witness",
|
||||
"description": "Start the gastown rig Witness.\n\n## Action\n```bash\ngt witness start gastown\n```\n\n## Verify\n1. Session exists: `tmux has-session -t gastown-witness 2>/dev/null`\n2. Not stalled: `gt peek gastown/witness` does NOT show \"> Try\" prompt\n3. Heartbeat fresh: Last patrol cycle < 5 min ago"
|
||||
},
|
||||
{
|
||||
"id": "ensure-beads-witness",
|
||||
"title": "Ensure beads witness",
|
||||
"description": "Start the beads rig Witness.\n\n## Action\n```bash\ngt witness start beads\n```\n\n## Verify\n1. Session exists: `tmux has-session -t beads-witness 2>/dev/null`\n2. Not stalled: `gt peek beads/witness` does NOT show \"> Try\" prompt\n3. Heartbeat fresh: Last patrol cycle < 5 min ago"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "ensure-refineries",
|
||||
"title": "Ensure refineries",
|
||||
"needs": ["ensure-deacon"],
|
||||
"type": "parallel",
|
||||
"description": "Parallel container: Start all rig refineries.\n\nChildren execute in parallel. Container completes when all children complete.",
|
||||
"children": [
|
||||
{
|
||||
"id": "ensure-gastown-refinery",
|
||||
"title": "Ensure gastown refinery",
|
||||
"description": "Start the gastown rig Refinery.\n\n## Action\n```bash\ngt refinery start gastown\n```\n\n## Verify\n1. Session exists: `tmux has-session -t gastown-refinery 2>/dev/null`\n2. Not stalled: `gt peek gastown/refinery` does NOT show \"> Try\" prompt\n3. Queue processing: Refinery can receive merge requests"
|
||||
},
|
||||
{
|
||||
"id": "ensure-beads-refinery",
|
||||
"title": "Ensure beads refinery",
|
||||
"description": "Start the beads rig Refinery.\n\n## Action\n```bash\ngt refinery start beads\n```\n\n## Verify\n1. Session exists: `tmux has-session -t beads-refinery 2>/dev/null`\n2. Not stalled: `gt peek beads/refinery` does NOT show \"> Try\" prompt\n3. Queue processing: Refinery can receive merge requests"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "verify-town-health",
|
||||
"title": "Verify town health",
|
||||
"needs": ["ensure-witnesses", "ensure-refineries"],
|
||||
"description": "Final verification that Gas Town is healthy.\n\n## Action\n```bash\ngt status\n```\n\n## Verify\n1. Daemon running: Shows daemon status OK\n2. Deacon active: Shows deacon in patrol mode\n3. All witnesses: Each rig witness shows active\n4. All refineries: Each rig refinery shows active\n\n## OnFail\nLog degraded state but consider boot complete. Some agents may need manual recovery.\nRun `gt doctor` for detailed diagnostics."
|
||||
}
|
||||
]
|
||||
}
|
||||
169
internal/formula/formulas/mol-gastown-boot.formula.toml
Normal file
169
internal/formula/formulas/mol-gastown-boot.formula.toml
Normal file
@@ -0,0 +1,169 @@
|
||||
description = """
|
||||
Mayor bootstraps Gas Town via a verification-gated lifecycle molecule.
|
||||
|
||||
## Purpose
|
||||
When Mayor executes \"boot up gas town\", this proto provides the workflow.
|
||||
Each step has action + verification - steps stay open until outcome is confirmed.
|
||||
|
||||
## Key Principles
|
||||
1. **Verification-gated steps** - Not \"command ran\" but \"outcome confirmed\"
|
||||
2. **gt peek for verification** - Capture session output to detect stalls
|
||||
3. **gt nudge for recovery** - Reliable message delivery to unstick agents
|
||||
4. **Parallel where possible** - Witnesses and refineries can start in parallel
|
||||
5. **Ephemeral execution** - Boot is a wisp, squashed to digest after completion
|
||||
|
||||
## Execution
|
||||
```bash
|
||||
bd mol wisp mol-gastown-boot # Create wisp
|
||||
```"""
|
||||
formula = "mol-gastown-boot"
|
||||
version = 1
|
||||
|
||||
[[steps]]
|
||||
description = """
|
||||
Verify the Gas Town daemon is running.
|
||||
|
||||
## Action
|
||||
```bash
|
||||
gt daemon status || gt daemon start
|
||||
```
|
||||
|
||||
## Verify
|
||||
1. Daemon PID file exists: `~/.gt/daemon.pid`
|
||||
2. Process is alive: `kill -0 $(cat ~/.gt/daemon.pid)`
|
||||
3. Daemon responds: `gt daemon status` returns success
|
||||
|
||||
## OnFail
|
||||
Cannot start daemon. Log error and continue - some commands work without daemon."""
|
||||
id = "ensure-daemon"
|
||||
title = "Ensure daemon"
|
||||
|
||||
[[steps]]
|
||||
description = """
|
||||
Start the Deacon and verify patrol mode is active.
|
||||
|
||||
## Action
|
||||
```bash
|
||||
gt deacon start
|
||||
```
|
||||
|
||||
## Verify
|
||||
1. Session exists: `tmux has-session -t gt-deacon 2>/dev/null`
|
||||
2. Not stalled: `gt peek deacon/` does NOT show \"> Try\" prompt
|
||||
3. Heartbeat fresh: `deacon/heartbeat.json` modified < 2 min ago
|
||||
|
||||
## OnStall
|
||||
```bash
|
||||
gt nudge deacon/ \"Start patrol.\"
|
||||
sleep 30
|
||||
# Re-verify
|
||||
```"""
|
||||
id = "ensure-deacon"
|
||||
needs = ["ensure-daemon"]
|
||||
title = "Ensure deacon"
|
||||
|
||||
[[steps]]
|
||||
description = """
|
||||
Parallel container: Start all rig witnesses.
|
||||
|
||||
Children execute in parallel. Container completes when all children complete."""
|
||||
id = "ensure-witnesses"
|
||||
needs = ["ensure-deacon"]
|
||||
title = "Ensure witnesses"
|
||||
type = "parallel"
|
||||
|
||||
[[steps.children]]
|
||||
description = """
|
||||
Start the gastown rig Witness.
|
||||
|
||||
## Action
|
||||
```bash
|
||||
gt witness start gastown
|
||||
```
|
||||
|
||||
## Verify
|
||||
1. Session exists: `tmux has-session -t gastown-witness 2>/dev/null`
|
||||
2. Not stalled: `gt peek gastown/witness` does NOT show \"> Try\" prompt
|
||||
3. Heartbeat fresh: Last patrol cycle < 5 min ago"""
|
||||
id = "ensure-gastown-witness"
|
||||
title = "Ensure gastown witness"
|
||||
|
||||
[[steps.children]]
|
||||
description = """
|
||||
Start the beads rig Witness.
|
||||
|
||||
## Action
|
||||
```bash
|
||||
gt witness start beads
|
||||
```
|
||||
|
||||
## Verify
|
||||
1. Session exists: `tmux has-session -t beads-witness 2>/dev/null`
|
||||
2. Not stalled: `gt peek beads/witness` does NOT show \"> Try\" prompt
|
||||
3. Heartbeat fresh: Last patrol cycle < 5 min ago"""
|
||||
id = "ensure-beads-witness"
|
||||
title = "Ensure beads witness"
|
||||
|
||||
[[steps]]
|
||||
description = """
|
||||
Parallel container: Start all rig refineries.
|
||||
|
||||
Children execute in parallel. Container completes when all children complete."""
|
||||
id = "ensure-refineries"
|
||||
needs = ["ensure-deacon"]
|
||||
title = "Ensure refineries"
|
||||
type = "parallel"
|
||||
|
||||
[[steps.children]]
|
||||
description = """
|
||||
Start the gastown rig Refinery.
|
||||
|
||||
## Action
|
||||
```bash
|
||||
gt refinery start gastown
|
||||
```
|
||||
|
||||
## Verify
|
||||
1. Session exists: `tmux has-session -t gastown-refinery 2>/dev/null`
|
||||
2. Not stalled: `gt peek gastown/refinery` does NOT show \"> Try\" prompt
|
||||
3. Queue processing: Refinery can receive merge requests"""
|
||||
id = "ensure-gastown-refinery"
|
||||
title = "Ensure gastown refinery"
|
||||
|
||||
[[steps.children]]
|
||||
description = """
|
||||
Start the beads rig Refinery.
|
||||
|
||||
## Action
|
||||
```bash
|
||||
gt refinery start beads
|
||||
```
|
||||
|
||||
## Verify
|
||||
1. Session exists: `tmux has-session -t beads-refinery 2>/dev/null`
|
||||
2. Not stalled: `gt peek beads/refinery` does NOT show \"> Try\" prompt
|
||||
3. Queue processing: Refinery can receive merge requests"""
|
||||
id = "ensure-beads-refinery"
|
||||
title = "Ensure beads refinery"
|
||||
|
||||
[[steps]]
|
||||
description = """
|
||||
Final verification that Gas Town is healthy.
|
||||
|
||||
## Action
|
||||
```bash
|
||||
gt status
|
||||
```
|
||||
|
||||
## Verify
|
||||
1. Daemon running: Shows daemon status OK
|
||||
2. Deacon active: Shows deacon in patrol mode
|
||||
3. All witnesses: Each rig witness shows active
|
||||
4. All refineries: Each rig refinery shows active
|
||||
|
||||
## OnFail
|
||||
Log degraded state but consider boot complete. Some agents may need manual recovery.
|
||||
Run `gt doctor` for detailed diagnostics."""
|
||||
id = "verify-town-health"
|
||||
needs = ["ensure-witnesses", "ensure-refineries"]
|
||||
title = "Verify town health"
|
||||
312
internal/formula/formulas/mol-orphan-scan.formula.toml
Normal file
312
internal/formula/formulas/mol-orphan-scan.formula.toml
Normal file
@@ -0,0 +1,312 @@
|
||||
description = """
|
||||
Find and reassign orphaned work.
|
||||
|
||||
Dogs work through molecules (poured from this formula) to scan for orphaned state:
|
||||
- Issues marked in_progress with no active polecat
|
||||
- Molecules attached but worker gone
|
||||
- Merge queue entries with dead owners
|
||||
- Wisps from terminated sessions
|
||||
|
||||
This is a cleanup and recovery formula. Found orphans are either:
|
||||
- Reassigned to available workers
|
||||
- Reset to open status for next dispatch
|
||||
- Escalated if data loss occurred
|
||||
|
||||
## Dog Contract
|
||||
|
||||
This is infrastructure work. You:
|
||||
1. Receive scan scope via hook_bead (rig or town-wide)
|
||||
2. Scan for orphaned state
|
||||
3. Classify and triage orphans
|
||||
4. Take recovery action
|
||||
5. Report findings
|
||||
6. Return to kennel
|
||||
|
||||
## Variables
|
||||
|
||||
| Variable | Source | Description |
|
||||
|----------|--------|-------------|
|
||||
| scope | hook_bead | Scan scope: 'town' or specific rig name |
|
||||
|
||||
## Why Dogs?
|
||||
|
||||
Orphan scanning requires multi-rig access and may need to interact with
|
||||
multiple Witnesses. Dogs have the cross-rig worktrees needed for this."""
|
||||
formula = "mol-orphan-scan"
|
||||
version = 1
|
||||
|
||||
[squash]
|
||||
trigger = "on_complete"
|
||||
template_type = "work"
|
||||
include_metrics = true
|
||||
|
||||
[[steps]]
|
||||
id = "determine-scope"
|
||||
title = "Determine scan scope"
|
||||
description = """
|
||||
Establish what to scan for orphans.
|
||||
|
||||
**1. Check assignment:**
|
||||
```bash
|
||||
gt hook # Shows scope in hook_bead
|
||||
```
|
||||
|
||||
**2. Resolve scope:**
|
||||
- 'town': Scan all rigs
|
||||
- '<rig>': Scan specific rig only
|
||||
|
||||
```bash
|
||||
# If town-wide
|
||||
gt rigs # Get list of all rigs
|
||||
|
||||
# If specific rig
|
||||
# Just use that rig
|
||||
```
|
||||
|
||||
**Exit criteria:** Scope determined, rig list established."""
|
||||
|
||||
[[steps]]
|
||||
id = "scan-orphaned-issues"
|
||||
title = "Scan for orphaned issues"
|
||||
needs = ["determine-scope"]
|
||||
description = """
|
||||
Find issues marked in_progress with no active worker.
|
||||
|
||||
**1. For each rig in scope, find in_progress issues:**
|
||||
```bash
|
||||
bd list --status=in_progress
|
||||
```
|
||||
|
||||
**2. For each in_progress issue, check assignee:**
|
||||
```bash
|
||||
bd show <issue-id>
|
||||
# Get assignee field
|
||||
```
|
||||
|
||||
**3. Check if assignee session exists:**
|
||||
```bash
|
||||
# If assignee is a polecat
|
||||
gt polecats <rig> # Is the polecat active?
|
||||
tmux has-session -t <session> 2>/dev/null
|
||||
```
|
||||
|
||||
**4. Identify orphans:**
|
||||
- Issue in_progress + assignee session dead = orphan
|
||||
- Issue in_progress + no assignee = orphan
|
||||
|
||||
Record each orphan with:
|
||||
- Issue ID
|
||||
- Last assignee (if any)
|
||||
- How long orphaned (last update timestamp)
|
||||
|
||||
**Exit criteria:** Orphaned issues identified."""
|
||||
|
||||
[[steps]]
|
||||
id = "scan-orphaned-molecules"
|
||||
title = "Scan for orphaned molecules"
|
||||
needs = ["determine-scope"]
|
||||
description = """
|
||||
Find molecules attached to dead sessions.
|
||||
|
||||
**1. List active molecules:**
|
||||
```bash
|
||||
bd mol list --active
|
||||
```
|
||||
|
||||
**2. For each molecule, check owner session:**
|
||||
```bash
|
||||
bd mol show <mol-id>
|
||||
# Get agent/session info
|
||||
```
|
||||
|
||||
**3. Check if owner session exists:**
|
||||
```bash
|
||||
tmux has-session -t <session> 2>/dev/null
|
||||
```
|
||||
|
||||
**4. Identify orphans:**
|
||||
- Molecule in_progress + owner session dead = orphan
|
||||
- Molecule hooked + owner session dead = orphan
|
||||
|
||||
Record each orphan for triage.
|
||||
|
||||
**Exit criteria:** Orphaned molecules identified."""
|
||||
|
||||
[[steps]]
|
||||
id = "scan-orphaned-wisps"
|
||||
title = "Scan for orphaned wisps"
|
||||
needs = ["determine-scope"]
|
||||
description = """
|
||||
Find wisps from terminated sessions.
|
||||
|
||||
**1. List wisps in ephemeral storage:**
|
||||
```bash
|
||||
ls .beads-wisp/ # Or equivalent location
|
||||
```
|
||||
|
||||
**2. For each wisp, check spawner session:**
|
||||
Wisps should have metadata indicating the spawning session.
|
||||
|
||||
**3. Identify orphans:**
|
||||
- Wisp age > 1 hour + spawner session dead = orphan
|
||||
- Wisp with no spawner metadata = orphan
|
||||
|
||||
**4. Check for unsquashed content:**
|
||||
Orphaned wisps may have audit-worthy content that wasn't squashed.
|
||||
|
||||
**Exit criteria:** Orphaned wisps identified."""
|
||||
|
||||
[[steps]]
|
||||
id = "triage-orphans"
|
||||
title = "Classify and triage orphans"
|
||||
needs = ["scan-orphaned-issues", "scan-orphaned-molecules", "scan-orphaned-wisps"]
|
||||
description = """
|
||||
Classify orphans by severity and determine action.
|
||||
|
||||
**1. Classify by type:**
|
||||
|
||||
| Type | Severity | Typical Action |
|
||||
|------|----------|----------------|
|
||||
| Issue in_progress, no work done | Low | Reset to open |
|
||||
| Issue in_progress, work in progress | Medium | Check branch, reassign |
|
||||
| Molecule mid-execution | Medium | Resume or restart |
|
||||
| Wisp with content | Low | Squash or burn |
|
||||
| Wisp empty | None | Delete |
|
||||
|
||||
**2. Check for data loss:**
|
||||
For issues/molecules with possible work:
|
||||
```bash
|
||||
# Check for branch with work
|
||||
git branch -a | grep <polecat-or-issue>
|
||||
git log --oneline <branch>
|
||||
```
|
||||
|
||||
**3. Categorize for action:**
|
||||
- RESET: Return to open status for normal dispatch
|
||||
- REASSIGN: Assign to specific worker immediately
|
||||
- RECOVER: Salvage work from branch/state
|
||||
- ESCALATE: Data loss or complex situation
|
||||
- BURN: Safe to delete (empty wisps, etc.)
|
||||
|
||||
**Exit criteria:** All orphans categorized with planned action."""
|
||||
|
||||
[[steps]]
|
||||
id = "execute-recovery"
|
||||
title = "Execute recovery actions"
|
||||
needs = ["triage-orphans"]
|
||||
description = """
|
||||
Take action on each orphan based on triage.
|
||||
|
||||
**1. RESET orphans:**
|
||||
```bash
|
||||
bd update <issue> --status=open --assignee=""
|
||||
# Clears in_progress, ready for dispatch
|
||||
```
|
||||
|
||||
**2. REASSIGN orphans:**
|
||||
```bash
|
||||
# Notify Witness to handle assignment
|
||||
gt mail send <rig>/witness -s "Orphan needs assignment: <issue>" \
|
||||
-m "Issue <id> was orphaned. Has partial work. Needs reassignment."
|
||||
```
|
||||
|
||||
**3. RECOVER orphans:**
|
||||
```bash
|
||||
# For issues with work on branch:
|
||||
# - Preserve the branch
|
||||
# - Create recovery note
|
||||
bd update <issue> --status=open \
|
||||
--note="Recovery: work exists on branch <branch>"
|
||||
```
|
||||
|
||||
**4. ESCALATE orphans:**
|
||||
```bash
|
||||
gt mail send mayor/ -s "Orphan requires escalation: <issue>" \
|
||||
-m "Issue <id> orphaned with possible data loss.
|
||||
Details: ...
|
||||
Recommended action: ..."
|
||||
```
|
||||
|
||||
**5. BURN orphans:**
|
||||
```bash
|
||||
# For empty wisps, etc.
|
||||
rm .beads-wisp/<wisp-file>
|
||||
```
|
||||
|
||||
**Exit criteria:** All orphans handled."""
|
||||
|
||||
[[steps]]
|
||||
id = "report-findings"
|
||||
title = "Generate and send orphan report"
|
||||
needs = ["execute-recovery"]
|
||||
description = """
|
||||
Create summary report of orphan scan and actions.
|
||||
|
||||
**1. Generate report:**
|
||||
```markdown
|
||||
## Orphan Scan Report: {{timestamp}}
|
||||
|
||||
**Scope**: {{scope}}
|
||||
**Orphans found**: {{total_count}}
|
||||
|
||||
### By Type
|
||||
- Issues: {{issue_count}}
|
||||
- Molecules: {{mol_count}}
|
||||
- Wisps: {{wisp_count}}
|
||||
|
||||
### Actions Taken
|
||||
- Reset to open: {{reset_count}}
|
||||
- Reassigned: {{reassign_count}}
|
||||
- Recovered: {{recover_count}}
|
||||
- Escalated: {{escalate_count}}
|
||||
- Burned: {{burn_count}}
|
||||
|
||||
### Details
|
||||
{{#each orphan}}
|
||||
- {{type}} {{id}}: {{action}} - {{reason}}
|
||||
{{/each}}
|
||||
```
|
||||
|
||||
**2. Send to Deacon (for logs):**
|
||||
```bash
|
||||
gt mail send deacon/ -s "Orphan scan complete: {{total_count}} found" \
|
||||
-m "{{report}}"
|
||||
```
|
||||
|
||||
**3. Send to Mayor (if escalations):**
|
||||
```bash
|
||||
# Only if there were escalations
|
||||
gt mail send mayor/ -s "Orphan scan: {{escalate_count}} escalations" \
|
||||
-m "{{escalations_section}}"
|
||||
```
|
||||
|
||||
**Exit criteria:** Reports sent."""
|
||||
|
||||
[[steps]]
|
||||
id = "return-to-kennel"
|
||||
title = "Signal completion and return to kennel"
|
||||
needs = ["report-findings"]
|
||||
description = """
|
||||
Signal work complete and return to available pool.
|
||||
|
||||
**1. Signal completion to Deacon:**
|
||||
```bash
|
||||
gt mail send deacon/ -s "DOG_DONE $(hostname)" -m "Task: orphan-scan
|
||||
Scope: {{scope}}
|
||||
Orphans found: {{total_count}}
|
||||
Actions taken: {{action_summary}}
|
||||
Status: COMPLETE
|
||||
|
||||
Ready for next assignment."
|
||||
```
|
||||
|
||||
**2. Return to kennel:**
|
||||
Dog returns to available state in the pool.
|
||||
|
||||
**Exit criteria:** Deacon notified, dog ready for next work."""
|
||||
|
||||
[vars]
|
||||
[vars.scope]
|
||||
description = "Scan scope: 'town' or specific rig name"
|
||||
required = true
|
||||
default = "town"
|
||||
@@ -0,0 +1,385 @@
|
||||
description = """
|
||||
Conflict resolution workflow for polecats handling merge conflicts.
|
||||
|
||||
This molecule guides a polecat through resolving merge conflicts for a previously
|
||||
submitted MR that failed to rebase. The workflow uses the merge-slot gate to
|
||||
serialize conflict resolution and prevent racing.
|
||||
|
||||
## Task Recognition
|
||||
|
||||
Conflict resolution tasks are created by the Refinery when a mechanical rebase
|
||||
fails. They are identified by:
|
||||
- Title prefix: "Resolve merge conflicts:"
|
||||
- Metadata fields in description: Original MR, Branch, Conflict SHA
|
||||
|
||||
## Key Differences from Regular Polecat Work
|
||||
|
||||
| Aspect | Regular Work | Conflict Resolution |
|
||||
|--------|--------------|---------------------|
|
||||
| Branch source | Create new branch | Checkout existing MR branch |
|
||||
| Merge path | Submit to queue via `gt done` | Push directly to main |
|
||||
| Issue closure | Refinery closes after merge | Close MR bead yourself |
|
||||
| Serialization | None | Merge-slot gate required |
|
||||
|
||||
## Variables
|
||||
|
||||
| Variable | Source | Description |
|
||||
|----------|--------|-------------|
|
||||
| task | hook_bead | The conflict-resolution task ID |
|
||||
| original_mr | task metadata | The MR bead that had conflicts |
|
||||
| branch | task metadata | The branch to rebase |
|
||||
| conflict_sha | task metadata | Main SHA when conflict occurred |
|
||||
|
||||
## Failure Modes
|
||||
|
||||
| Situation | Action |
|
||||
|-----------|--------|
|
||||
| Merge slot held | Wait (--wait flag adds you to queue) |
|
||||
| Complex conflicts | Use judgment; escalate if unsure |
|
||||
| Tests fail after resolve | Fix them before pushing |
|
||||
| Resolution unclear | Read original issue for context |"""
|
||||
formula = "mol-polecat-conflict-resolve"
|
||||
version = 1
|
||||
|
||||
[[steps]]
|
||||
id = "load-task"
|
||||
title = "Load task and extract metadata"
|
||||
description = """
|
||||
Initialize your session and understand the conflict resolution task.
|
||||
|
||||
**1. Prime your environment:**
|
||||
```bash
|
||||
gt prime # Load role context
|
||||
bd prime # Load beads context
|
||||
```
|
||||
|
||||
**2. Check your hook:**
|
||||
```bash
|
||||
gt hook # Shows your pinned molecule and hook_bead
|
||||
```
|
||||
|
||||
**3. Read the conflict resolution task:**
|
||||
```bash
|
||||
bd show {{task}}
|
||||
```
|
||||
|
||||
**4. Extract metadata from the task description:**
|
||||
|
||||
The task description contains structured metadata:
|
||||
```
|
||||
## Metadata
|
||||
- Original MR: <mr-id>
|
||||
- Branch: <branch>
|
||||
- Conflict with: <target>@<main-sha>
|
||||
- Original issue: <source-issue>
|
||||
- Retry count: <count>
|
||||
```
|
||||
|
||||
Parse and note:
|
||||
- **original_mr**: The MR bead ID (you'll close this after merge)
|
||||
- **branch**: The branch to checkout and rebase
|
||||
- **source_issue**: The original work issue (read for context if needed)
|
||||
- **retry_count**: How many times this has been attempted
|
||||
|
||||
**5. Understand the context:**
|
||||
|
||||
If the conflict seems complex, read the original issue:
|
||||
```bash
|
||||
bd show <source-issue> # What was the original work?
|
||||
```
|
||||
|
||||
**Exit criteria:** You have all metadata and understand the conflict context."""
|
||||
|
||||
[[steps]]
|
||||
id = "acquire-slot"
|
||||
title = "Acquire merge slot"
|
||||
needs = ["load-task"]
|
||||
description = """
|
||||
Acquire exclusive access to the merge slot before proceeding.
|
||||
|
||||
The merge slot prevents multiple conflict-resolution polecats from racing
|
||||
to push to main simultaneously (the "Monkey Knife Fight" problem).
|
||||
|
||||
**1. Check slot availability:**
|
||||
```bash
|
||||
bd merge-slot check --json
|
||||
```
|
||||
|
||||
**2. Acquire the slot:**
|
||||
```bash
|
||||
bd merge-slot acquire --holder=$(whoami) --wait --json
|
||||
```
|
||||
|
||||
The `--wait` flag adds you to the waiters queue if the slot is held.
|
||||
You'll proceed when the current holder releases.
|
||||
|
||||
**3. Verify acquisition:**
|
||||
The output should show:
|
||||
```json
|
||||
{"available": false, "holder": "your-name", ...}
|
||||
```
|
||||
|
||||
If you're in the waiters list, wait for the holder to release. Check
|
||||
periodically:
|
||||
```bash
|
||||
bd merge-slot check --json
|
||||
```
|
||||
|
||||
**Important:** Once you have the slot, complete the workflow promptly.
|
||||
Other polecats may be waiting.
|
||||
|
||||
**Exit criteria:** You hold the merge slot exclusively."""
|
||||
|
||||
[[steps]]
|
||||
id = "checkout-branch"
|
||||
title = "Checkout and prepare the conflicting branch"
|
||||
needs = ["acquire-slot"]
|
||||
description = """
|
||||
Fetch and checkout the branch that needs conflict resolution.
|
||||
|
||||
**1. Ensure clean workspace:**
|
||||
```bash
|
||||
git status # Should be clean
|
||||
git stash list # Should be empty
|
||||
```
|
||||
|
||||
If dirty, clean up first (stash or discard).
|
||||
|
||||
**2. Fetch latest state:**
|
||||
```bash
|
||||
git fetch origin
|
||||
git fetch origin {{branch}}:refs/remotes/origin/{{branch}}
|
||||
```
|
||||
|
||||
**3. Checkout the branch:**
|
||||
```bash
|
||||
git checkout -b temp-resolve origin/{{branch}}
|
||||
```
|
||||
|
||||
Using `temp-resolve` as the local branch name keeps things clear.
|
||||
|
||||
**4. Verify the branch state:**
|
||||
```bash
|
||||
git log --oneline -5 # Recent commits
|
||||
git log origin/main..HEAD # Commits not on main
|
||||
```
|
||||
|
||||
**Exit criteria:** On temp-resolve branch, ready to rebase."""
|
||||
|
||||
[[steps]]
|
||||
id = "rebase-resolve"
|
||||
title = "Rebase onto main and resolve conflicts"
|
||||
needs = ["checkout-branch"]
|
||||
description = """
|
||||
Perform the rebase and resolve any conflicts.
|
||||
|
||||
**1. Start the rebase:**
|
||||
```bash
|
||||
git rebase origin/main
|
||||
```
|
||||
|
||||
**2. If conflicts occur:**
|
||||
|
||||
For each conflicted file:
|
||||
```bash
|
||||
git status # See conflicted files
|
||||
git diff # See conflict markers
|
||||
```
|
||||
|
||||
**Resolve using your judgment:**
|
||||
- Read both versions carefully
|
||||
- Consider the original intent (from source issue)
|
||||
- If the MR was adding a feature, preserve that addition
|
||||
- If the MR was fixing a bug, ensure the fix remains
|
||||
|
||||
**After resolving each file:**
|
||||
```bash
|
||||
git add <resolved-file>
|
||||
git rebase --continue
|
||||
```
|
||||
|
||||
**3. If stuck on a conflict:**
|
||||
- Read the original issue for context: `bd show <source-issue>`
|
||||
- If still unclear, escalate to Witness:
|
||||
```bash
|
||||
gt mail send <rig>/witness -s "HELP: Complex conflict" -m "Task: {{task}}
|
||||
File: <conflicted-file>
|
||||
Issue: Cannot determine correct resolution"
|
||||
```
|
||||
|
||||
**4. Verify rebase success:**
|
||||
```bash
|
||||
git log --oneline origin/main..HEAD # Your commits rebased
|
||||
git status # Clean working tree
|
||||
```
|
||||
|
||||
**Exit criteria:** Branch successfully rebased onto origin/main."""
|
||||
|
||||
[[steps]]
|
||||
id = "run-tests"
|
||||
title = "Run tests to verify resolution"
|
||||
needs = ["rebase-resolve"]
|
||||
description = """
|
||||
Verify the resolution doesn't break anything.
|
||||
|
||||
**1. Run the test suite:**
|
||||
```bash
|
||||
go test ./... # Or appropriate test command
|
||||
```
|
||||
|
||||
**ALL TESTS MUST PASS.** Do not push with failures.
|
||||
|
||||
**2. If tests fail:**
|
||||
- Determine if it's a resolution error or pre-existing
|
||||
- If your resolution broke something: fix it
|
||||
- If pre-existing: file a bead, but still must fix before pushing
|
||||
|
||||
```bash
|
||||
# Quick check: does main pass?
|
||||
git stash
|
||||
git checkout origin/main
|
||||
go test ./...
|
||||
git checkout temp-resolve
|
||||
git stash pop
|
||||
```
|
||||
|
||||
**3. Run build check:**
|
||||
```bash
|
||||
go build ./...
|
||||
```
|
||||
|
||||
**Exit criteria:** All tests pass, build succeeds."""
|
||||
|
||||
[[steps]]
|
||||
id = "push-to-main"
|
||||
title = "Push resolved changes directly to main"
|
||||
needs = ["run-tests"]
|
||||
description = """
|
||||
Push the resolved branch directly to main.
|
||||
|
||||
**Important:** Unlike normal polecat work, conflict resolution pushes directly
|
||||
to main. This is because:
|
||||
1. The original MR was already reviewed/approved by being in the queue
|
||||
2. We're just resolving conflicts, not adding new functionality
|
||||
3. Going back through the queue would create an infinite loop
|
||||
|
||||
**1. Rebase one more time (in case main moved):**
|
||||
```bash
|
||||
git fetch origin
|
||||
git rebase origin/main
|
||||
```
|
||||
|
||||
If new conflicts: resolve them (return to rebase-resolve step).
|
||||
|
||||
**2. Push to main:**
|
||||
```bash
|
||||
git push origin temp-resolve:main
|
||||
```
|
||||
|
||||
**3. Verify the push:**
|
||||
```bash
|
||||
git log origin/main --oneline -3 # Your commits should be there
|
||||
```
|
||||
|
||||
**Exit criteria:** Changes are on origin/main."""
|
||||
|
||||
[[steps]]
|
||||
id = "close-beads"
|
||||
title = "Close the original MR bead and this task"
|
||||
needs = ["push-to-main"]
|
||||
description = """
|
||||
Close the beads to complete the work chain.
|
||||
|
||||
**1. Close the original MR bead:**
|
||||
```bash
|
||||
bd close {{original_mr}} --reason="merged after conflict resolution"
|
||||
```
|
||||
|
||||
This completes the MR that was blocked on conflicts.
|
||||
|
||||
**2. Close the source issue (if not already closed):**
|
||||
```bash
|
||||
bd show <source-issue> # Check status
|
||||
bd close <source-issue> --reason="merged via conflict resolution"
|
||||
```
|
||||
|
||||
The Refinery normally closes issues after merge, but since we pushed
|
||||
directly to main, we handle it here.
|
||||
|
||||
**3. Sync beads:**
|
||||
```bash
|
||||
bd sync
|
||||
```
|
||||
|
||||
**Exit criteria:** Original MR and source issue are closed."""
|
||||
|
||||
[[steps]]
|
||||
id = "release-slot"
|
||||
title = "Release merge slot"
|
||||
needs = ["close-beads"]
|
||||
description = """
|
||||
Release the merge slot so other polecats can proceed.
|
||||
|
||||
**1. Release the slot:**
|
||||
```bash
|
||||
bd merge-slot release --holder=$(whoami) --json
|
||||
```
|
||||
|
||||
**2. Verify release:**
|
||||
```bash
|
||||
bd merge-slot check --json
|
||||
```
|
||||
|
||||
Should show either:
|
||||
- `available: true` (no one waiting)
|
||||
- `holder: <next-waiter>` (slot passed to next in queue)
|
||||
|
||||
**Exit criteria:** Merge slot released."""
|
||||
|
||||
[[steps]]
|
||||
id = "cleanup-and-exit"
|
||||
title = "Clean up and close task"
|
||||
needs = ["release-slot"]
|
||||
description = """
|
||||
Clean up workspace and close the conflict resolution task.
|
||||
|
||||
**1. Clean up local branch:**
|
||||
```bash
|
||||
git checkout main
|
||||
git branch -D temp-resolve
|
||||
git fetch origin
|
||||
git reset --hard origin/main
|
||||
```
|
||||
|
||||
**2. Verify clean state:**
|
||||
```bash
|
||||
git status # Clean
|
||||
git stash list # Empty
|
||||
```
|
||||
|
||||
**3. Close this task:**
|
||||
```bash
|
||||
bd close {{task}} --reason="Conflicts resolved and merged to main"
|
||||
```
|
||||
|
||||
**4. Signal completion:**
|
||||
```bash
|
||||
gt done
|
||||
```
|
||||
|
||||
You're now recyclable. The Witness knows you've completed conflict resolution.
|
||||
|
||||
**Exit criteria:** Task closed, workspace clean, polecat recyclable."""
|
||||
|
||||
[vars]
|
||||
[vars.task]
|
||||
description = "The conflict resolution task ID assigned to this polecat"
|
||||
required = true
|
||||
|
||||
[vars.original_mr]
|
||||
description = "The original MR bead ID (extracted from task metadata)"
|
||||
required = true
|
||||
|
||||
[vars.branch]
|
||||
description = "The branch to rebase (extracted from task metadata)"
|
||||
required = true
|
||||
198
internal/formula/formulas/mol-polecat-lease.formula.toml
Normal file
198
internal/formula/formulas/mol-polecat-lease.formula.toml
Normal file
@@ -0,0 +1,198 @@
|
||||
description = """
|
||||
Witness-side tracking of a single polecat's lifecycle.
|
||||
|
||||
The Witness bonds this molecule for each active polecat, creating a lease that
|
||||
tracks the polecat from spawn through work to cleanup. This is the WITNESS'S
|
||||
view of the polecat, not the polecat's own work molecule.
|
||||
|
||||
## Lifecycle States
|
||||
|
||||
```
|
||||
BOOT ─► WORKING ─► VERIFYING ─► MERGE_REQUESTED ─► DONE
|
||||
│ │ │ │
|
||||
└─► STUCK ─┴─► STUCK ──┴──► STUCK ───┘
|
||||
```
|
||||
|
||||
## Variables
|
||||
|
||||
| Variable | Required | Description |
|
||||
|----------|----------|-------------|
|
||||
| polecat | Yes | Name of the polecat |
|
||||
| issue | Yes | The issue assigned to the polecat |
|
||||
| rig | Yes | The rig this polecat belongs to |"""
|
||||
formula = "mol-polecat-lease"
|
||||
version = 2
|
||||
|
||||
[[steps]]
|
||||
id = "boot"
|
||||
title = "Verify polecat boots successfully"
|
||||
description = """
|
||||
Polecat has been spawned. Verify it initializes and starts working.
|
||||
|
||||
**Check if alive:**
|
||||
```bash
|
||||
tmux capture-pane -t gt-{{rig}}-{{polecat}} -p | tail -20
|
||||
```
|
||||
|
||||
Look for:
|
||||
- Claude prompt visible ("> " at start of line)
|
||||
- `gt prime` output
|
||||
- Signs of reading the assigned issue
|
||||
|
||||
**If idle for >60 seconds:**
|
||||
```bash
|
||||
gt nudge {{rig}}/polecats/{{polecat}} "Begin work on {{issue}}."
|
||||
```
|
||||
|
||||
**If still no response after nudge:**
|
||||
```bash
|
||||
gt nudge {{rig}}/polecats/{{polecat}} "Are you there? Please acknowledge."
|
||||
```
|
||||
|
||||
After 3 failed nudges, mark as stuck and escalate.
|
||||
|
||||
**Exit criteria:** Polecat shows signs of active work on {{issue}}."""
|
||||
|
||||
[[steps]]
|
||||
id = "working"
|
||||
title = "Monitor polecat progress"
|
||||
needs = ["boot"]
|
||||
description = """
|
||||
Polecat is actively working. Monitor for stuck or completion.
|
||||
|
||||
**Periodic checks:**
|
||||
- Use standard nudge protocol from Witness CLAUDE.md
|
||||
- Watch for POLECAT_DONE mail or agent_state=done
|
||||
|
||||
**Signs of progress:**
|
||||
- Git commits appearing
|
||||
- File changes visible in peek
|
||||
- Active tool usage in tmux capture
|
||||
|
||||
**Signs of stuck:**
|
||||
- Idle >15 minutes
|
||||
- Repeated errors
|
||||
- Explicit "I'm stuck" messages
|
||||
|
||||
**If POLECAT_DONE received or agent_state=done:**
|
||||
Proceed to verifying step.
|
||||
|
||||
**Exit criteria:** Polecat signals completion (POLECAT_DONE mail or state=done)."""
|
||||
|
||||
[[steps]]
|
||||
id = "verifying"
|
||||
title = "Verify polecat work is merge-ready"
|
||||
needs = ["working"]
|
||||
description = """
|
||||
Polecat claims completion. Verify before sending to Refinery.
|
||||
|
||||
**1. Check git state:**
|
||||
```bash
|
||||
cd polecats/{{polecat}}
|
||||
git status # Must be "working tree clean"
|
||||
git stash list # Must be empty
|
||||
git log origin/main..HEAD # Should have commits
|
||||
```
|
||||
|
||||
**2. Verify branch is pushed:**
|
||||
```bash
|
||||
git log origin/$(git branch --show-current)..HEAD # Should be empty
|
||||
```
|
||||
|
||||
**3. Verify issue is closed:**
|
||||
```bash
|
||||
bd show {{issue}} # Status should be 'closed'
|
||||
```
|
||||
|
||||
**4. Spot-check quality (ZFC - your judgment):**
|
||||
- Commits have reasonable messages
|
||||
- Changes look related to issue
|
||||
- No obvious problems in git log
|
||||
|
||||
**If verification fails:**
|
||||
Nudge polecat to fix:
|
||||
```bash
|
||||
gt nudge {{rig}}/polecats/{{polecat}} "Verification failed: <issue>. Please fix."
|
||||
```
|
||||
Return to working step.
|
||||
|
||||
**If verification passes:**
|
||||
Proceed to merge_requested step.
|
||||
|
||||
**Exit criteria:** Git clean, branch pushed, issue closed, work looks legit."""
|
||||
|
||||
[[steps]]
|
||||
id = "merge_requested"
|
||||
title = "Request merge from Refinery"
|
||||
needs = ["verifying"]
|
||||
description = """
|
||||
Work verified. Send MERGE_READY to Refinery and wait.
|
||||
|
||||
**Send merge request:**
|
||||
```bash
|
||||
gt mail send {{rig}}/refinery -s "MERGE_READY {{polecat}}" -m "Branch: $(cd polecats/{{polecat}} && git branch --show-current)
|
||||
Issue: {{issue}}
|
||||
Polecat: {{polecat}}
|
||||
Verified: clean git state, issue closed"
|
||||
```
|
||||
|
||||
**Update cleanup wisp state:**
|
||||
```bash
|
||||
bd update <wisp-id> --labels cleanup,polecat:{{polecat}},state:merge-requested
|
||||
```
|
||||
|
||||
**Wait for MERGED response:**
|
||||
The Refinery will:
|
||||
1. Fetch and rebase the branch
|
||||
2. Run tests
|
||||
3. Merge to main (if pass)
|
||||
4. Send MERGED mail back
|
||||
|
||||
This may take several minutes.
|
||||
|
||||
**If MERGED received:** Proceed to done step.
|
||||
**If merge fails:** Refinery notifies, return to working state.
|
||||
|
||||
**Exit criteria:** MERGED mail received from Refinery."""
|
||||
|
||||
[[steps]]
|
||||
id = "done"
|
||||
title = "Complete polecat cleanup"
|
||||
needs = ["merge_requested"]
|
||||
description = """
|
||||
Merge confirmed. Clean up the polecat.
|
||||
|
||||
**1. Kill the polecat session:**
|
||||
```bash
|
||||
gt session kill {{rig}}/polecats/{{polecat}}
|
||||
```
|
||||
|
||||
**2. Remove worktree (if ephemeral):**
|
||||
```bash
|
||||
git worktree remove polecats/{{polecat}} --force
|
||||
```
|
||||
|
||||
**3. Delete local branch (if exists):**
|
||||
```bash
|
||||
git branch -D polecat/{{polecat}} 2>/dev/null || true
|
||||
```
|
||||
|
||||
**4. Close this lease:**
|
||||
```bash
|
||||
bd close <this-lease-id>
|
||||
```
|
||||
|
||||
**Exit criteria:** Polecat session killed, worktree removed, lease closed."""
|
||||
|
||||
[vars]
|
||||
[vars.polecat]
|
||||
description = "Name of the polecat"
|
||||
required = true
|
||||
|
||||
[vars.issue]
|
||||
description = "The issue assigned to the polecat"
|
||||
required = true
|
||||
|
||||
[vars.rig]
|
||||
description = "The rig this polecat belongs to"
|
||||
required = true
|
||||
459
internal/formula/formulas/mol-polecat-work.formula.toml
Normal file
459
internal/formula/formulas/mol-polecat-work.formula.toml
Normal file
@@ -0,0 +1,459 @@
|
||||
description = """
|
||||
Full polecat work lifecycle from assignment through MR submission.
|
||||
|
||||
This molecule guides a polecat through a complete work assignment. Each step
|
||||
has clear entry/exit criteria and specific commands to run. A polecat can
|
||||
crash after any step and resume from the last completed step.
|
||||
|
||||
## Polecat Contract (Ephemeral Model)
|
||||
|
||||
You are an ephemeral worker. You:
|
||||
1. Receive work via your hook (pinned molecule + issue)
|
||||
2. Work through molecule steps using `bd ready` / `bd close <step>`
|
||||
3. Submit to merge queue via `gt done`
|
||||
4. Become recyclable - Refinery handles the rest
|
||||
|
||||
**Important:** This formula defines the template. Your molecule already has step
|
||||
beads created from it. Use `bd ready` to find them - do NOT read this file directly.
|
||||
|
||||
**You do NOT:**
|
||||
- Push directly to main (Refinery merges)
|
||||
- Close your own issue (Refinery closes after merge)
|
||||
- Wait for merge (you're done at MR submission)
|
||||
- Handle rebase conflicts (Refinery dispatches fresh polecats for that)
|
||||
|
||||
## Variables
|
||||
|
||||
| Variable | Source | Description |
|
||||
|----------|--------|-------------|
|
||||
| issue | hook_bead | The issue ID you're assigned to work on |
|
||||
|
||||
## Failure Modes
|
||||
|
||||
| Situation | Action |
|
||||
|-----------|--------|
|
||||
| Tests fail | Fix them. Do not proceed with failures. |
|
||||
| Blocked on external | Mail Witness for help, mark yourself stuck |
|
||||
| Context filling | Use gt handoff to cycle to fresh session |
|
||||
| Unsure what to do | Mail Witness, don't guess |"""
|
||||
formula = "mol-polecat-work"
|
||||
version = 4
|
||||
|
||||
[[steps]]
|
||||
id = "load-context"
|
||||
title = "Load context and verify assignment"
|
||||
description = """
|
||||
Initialize your session and understand your assignment.
|
||||
|
||||
**1. Prime your environment:**
|
||||
```bash
|
||||
gt prime # Load role context
|
||||
bd prime # Load beads context
|
||||
```
|
||||
|
||||
**2. Check your hook:**
|
||||
```bash
|
||||
gt hook # Shows your pinned molecule and hook_bead
|
||||
```
|
||||
|
||||
The hook_bead is your assigned issue. Read it carefully:
|
||||
```bash
|
||||
bd show {{issue}} # Full issue details
|
||||
```
|
||||
|
||||
**3. Check inbox for additional context:**
|
||||
```bash
|
||||
gt mail inbox
|
||||
# Read any HANDOFF or assignment messages
|
||||
```
|
||||
|
||||
**4. Understand the requirements:**
|
||||
- What exactly needs to be done?
|
||||
- What files are likely involved?
|
||||
- Are there dependencies or blockers?
|
||||
- What does "done" look like?
|
||||
|
||||
**5. Verify you can proceed:**
|
||||
- No unresolved blockers on the issue
|
||||
- You understand what to do
|
||||
- Required resources are available
|
||||
|
||||
If blocked or unclear, mail Witness immediately:
|
||||
```bash
|
||||
gt mail send <rig>/witness -s "HELP: Unclear requirements" -m "Issue: {{issue}}
|
||||
Question: <what you need clarified>"
|
||||
```
|
||||
|
||||
**Exit criteria:** You understand the work and can begin implementation."""
|
||||
|
||||
[[steps]]
|
||||
id = "branch-setup"
|
||||
title = "Set up working branch"
|
||||
needs = ["load-context"]
|
||||
description = """
|
||||
Ensure you're on a clean feature branch ready for work.
|
||||
|
||||
**1. Check current branch state:**
|
||||
```bash
|
||||
git status
|
||||
git branch --show-current
|
||||
```
|
||||
|
||||
**2. If not on a feature branch, create one:**
|
||||
```bash
|
||||
# Standard naming: polecat/<your-name> or feature/<issue-id>
|
||||
git checkout -b polecat/<name>
|
||||
```
|
||||
|
||||
**3. Ensure clean working state:**
|
||||
```bash
|
||||
git status # Should show "working tree clean"
|
||||
git stash list # Should be empty
|
||||
```
|
||||
|
||||
If dirty state from previous work:
|
||||
```bash
|
||||
# If changes are relevant to this issue:
|
||||
git add -A && git commit -m "WIP: <description>"
|
||||
|
||||
# If changes are unrelated cruft:
|
||||
git stash push -m "unrelated changes before {{issue}}"
|
||||
# Or discard if truly garbage:
|
||||
git checkout -- .
|
||||
```
|
||||
|
||||
**4. Sync with main:**
|
||||
```bash
|
||||
git fetch origin
|
||||
git rebase origin/main # Get latest, rebase your branch
|
||||
```
|
||||
|
||||
If rebase conflicts:
|
||||
- Resolve them carefully
|
||||
- Test after resolution
|
||||
- If stuck, mail Witness
|
||||
|
||||
**Exit criteria:** You're on a clean feature branch, rebased on latest main."""
|
||||
|
||||
[[steps]]
|
||||
id = "preflight-tests"
|
||||
title = "Verify tests pass on main"
|
||||
needs = ["branch-setup"]
|
||||
description = """
|
||||
Check if the codebase is healthy BEFORE starting your work.
|
||||
|
||||
**The Scotty Principle:** Don't walk past a broken warp core. But also don't
|
||||
let someone else's mess consume your entire mission.
|
||||
|
||||
**1. Check tests on main:**
|
||||
```bash
|
||||
git stash # Save your branch state
|
||||
git checkout origin/main
|
||||
go test ./... # Or appropriate test command
|
||||
```
|
||||
|
||||
**2. If tests PASS:**
|
||||
```bash
|
||||
git checkout - # Back to your branch
|
||||
git stash pop # Restore state
|
||||
```
|
||||
Continue to implement step.
|
||||
|
||||
**3. If tests FAIL on main:**
|
||||
|
||||
Make a judgment call:
|
||||
|
||||
| Situation | Action |
|
||||
|-----------|--------|
|
||||
| Quick fix (<15 min) | Fix it, commit to main, then continue |
|
||||
| Medium fix (15-60 min) | Fix if it blocks your work, else file bead |
|
||||
| Big fix (>1 hour) | File bead, notify Witness, proceed with your work |
|
||||
|
||||
**Quick fix path:**
|
||||
```bash
|
||||
# Fix the issue
|
||||
git add <files>
|
||||
git commit -m "fix: <description> (pre-existing failure)"
|
||||
git push origin main
|
||||
git checkout -
|
||||
git stash pop
|
||||
git rebase origin/main # Get your fix
|
||||
```
|
||||
|
||||
**File and proceed path:**
|
||||
```bash
|
||||
bd create --title "Pre-existing test failure: <description>" \
|
||||
--type bug --priority 1
|
||||
|
||||
gt mail send <rig>/witness -s "NOTICE: Main has failing tests" \
|
||||
-m "Found pre-existing test failures on main.
|
||||
Filed: <bead-id>
|
||||
Proceeding with my assigned work ({{issue}})."
|
||||
|
||||
git checkout -
|
||||
git stash pop
|
||||
```
|
||||
|
||||
**Context consideration:**
|
||||
If fixing pre-existing failures consumed significant context:
|
||||
```bash
|
||||
gt handoff -s "Fixed pre-existing failures, ready for assigned work" \
|
||||
-m "Issue: {{issue}}
|
||||
Fixed: <what you fixed>
|
||||
Ready to start: implement step"
|
||||
```
|
||||
Fresh session continues from implement.
|
||||
|
||||
**Exit criteria:** Tests pass on main (or issue filed), ready to implement."""
|
||||
|
||||
[[steps]]
|
||||
id = "implement"
|
||||
title = "Implement the solution"
|
||||
needs = ["preflight-tests"]
|
||||
description = """
|
||||
Do the actual implementation work.
|
||||
|
||||
**Working principles:**
|
||||
- Follow existing codebase conventions
|
||||
- Make atomic, focused commits
|
||||
- Keep changes scoped to the assigned issue
|
||||
- Don't gold-plate or scope-creep
|
||||
|
||||
**Commit frequently:**
|
||||
```bash
|
||||
# After each logical unit of work:
|
||||
git add <files>
|
||||
git commit -m "<type>: <description> ({{issue}})"
|
||||
```
|
||||
|
||||
Commit types: feat, fix, refactor, test, docs, chore
|
||||
|
||||
**Discovered work:**
|
||||
If you find bugs or improvements outside your scope:
|
||||
```bash
|
||||
bd create --title "Found: <description>" --type bug --priority 2
|
||||
# Note the ID, continue with your work
|
||||
```
|
||||
|
||||
Do NOT fix unrelated issues in this branch.
|
||||
|
||||
**If stuck:**
|
||||
Don't spin for more than 15 minutes. Mail Witness:
|
||||
```bash
|
||||
gt mail send <rig>/witness -s "HELP: Stuck on implementation" -m "Issue: {{issue}}
|
||||
Trying to: <what you're attempting>
|
||||
Problem: <what's blocking you>
|
||||
Tried: <what you've attempted>"
|
||||
```
|
||||
|
||||
**Exit criteria:** Implementation complete, all changes committed."""
|
||||
|
||||
[[steps]]
|
||||
id = "self-review"
|
||||
title = "Self-review changes"
|
||||
needs = ["implement"]
|
||||
description = """
|
||||
Review your own changes before running tests.
|
||||
|
||||
**1. Review the diff:**
|
||||
```bash
|
||||
git diff origin/main...HEAD # All changes vs main
|
||||
git log --oneline origin/main..HEAD # All commits
|
||||
```
|
||||
|
||||
**2. Check for common issues:**
|
||||
|
||||
| Category | Look For |
|
||||
|----------|----------|
|
||||
| Bugs | Off-by-one, null handling, edge cases |
|
||||
| Security | Injection, auth bypass, exposed secrets |
|
||||
| Style | Naming, formatting, code organization |
|
||||
| Completeness | Missing error handling, incomplete paths |
|
||||
| Cruft | Debug prints, commented code, TODOs |
|
||||
|
||||
**3. Fix issues found:**
|
||||
Don't just note them - fix them now. Amend or add commits as needed.
|
||||
|
||||
**4. Verify no unintended changes:**
|
||||
```bash
|
||||
git diff --stat origin/main...HEAD
|
||||
# Only files relevant to {{issue}} should appear
|
||||
```
|
||||
|
||||
If you accidentally modified unrelated files, remove those changes.
|
||||
|
||||
**Exit criteria:** Changes are clean, reviewed, and ready for testing."""
|
||||
|
||||
[[steps]]
|
||||
id = "run-tests"
|
||||
title = "Run tests and verify coverage"
|
||||
needs = ["self-review"]
|
||||
description = """
|
||||
Verify your changes don't break anything and are properly tested.
|
||||
|
||||
**1. Run the full test suite:**
|
||||
```bash
|
||||
go test ./... # For Go projects
|
||||
# Or appropriate command for your stack
|
||||
```
|
||||
|
||||
**ALL TESTS MUST PASS.** Do not proceed with failures.
|
||||
|
||||
**2. If tests fail:**
|
||||
- Read the failure output carefully
|
||||
- Determine if your change caused it:
|
||||
- If yes: Fix it. Return to implement step if needed.
|
||||
- If no (pre-existing): File a bead, but still must pass for your PR
|
||||
|
||||
```bash
|
||||
# Check if failure exists on main:
|
||||
git stash
|
||||
git checkout main
|
||||
go test ./...
|
||||
git checkout -
|
||||
git stash pop
|
||||
```
|
||||
|
||||
**3. Verify test coverage for new code:**
|
||||
- New features should have tests
|
||||
- Bug fixes should have regression tests
|
||||
- If you added significant code without tests, add them now
|
||||
|
||||
**4. Run any other quality checks:**
|
||||
```bash
|
||||
# Linting (if configured)
|
||||
golangci-lint run ./...
|
||||
|
||||
# Build check
|
||||
go build ./...
|
||||
```
|
||||
|
||||
**Exit criteria:** All tests pass, new code has appropriate test coverage."""
|
||||
|
||||
[[steps]]
|
||||
id = "cleanup-workspace"
|
||||
title = "Clean up workspace"
|
||||
needs = ["run-tests"]
|
||||
description = """
|
||||
Ensure workspace is pristine before handoff.
|
||||
|
||||
**1. Check for uncommitted changes:**
|
||||
```bash
|
||||
git status
|
||||
```
|
||||
Must show "working tree clean". If not:
|
||||
- Commit legitimate changes
|
||||
- Discard garbage: `git checkout -- .`
|
||||
|
||||
**2. Check for untracked files:**
|
||||
```bash
|
||||
git status --porcelain
|
||||
```
|
||||
Should be empty. If not:
|
||||
- Add to .gitignore if appropriate
|
||||
- Remove if temporary: `rm <file>`
|
||||
- Commit if needed
|
||||
|
||||
**3. Check stash:**
|
||||
```bash
|
||||
git stash list
|
||||
```
|
||||
Should be empty. If not:
|
||||
- Pop and commit: `git stash pop && git add -A && git commit`
|
||||
- Or drop if garbage: `git stash drop`
|
||||
|
||||
**4. Push your branch:**
|
||||
```bash
|
||||
git push -u origin $(git branch --show-current)
|
||||
```
|
||||
|
||||
**5. Verify nothing left behind:**
|
||||
```bash
|
||||
git status # Clean
|
||||
git stash list # Empty
|
||||
git log origin/main..HEAD # Your commits
|
||||
git diff origin/main...HEAD # Your changes (expected)
|
||||
```
|
||||
|
||||
**Exit criteria:** Branch pushed, workspace clean, no cruft."""
|
||||
|
||||
[[steps]]
|
||||
id = "prepare-for-review"
|
||||
title = "Prepare work for review"
|
||||
needs = ["cleanup-workspace"]
|
||||
description = """
|
||||
Verify work is complete and ready for merge queue.
|
||||
|
||||
**Note:** Do NOT close the issue. The Refinery will close it after successful merge.
|
||||
This enables conflict-resolution retries without reopening closed issues.
|
||||
|
||||
**1. Verify the issue shows your work:**
|
||||
```bash
|
||||
bd show {{issue}}
|
||||
# Status should still be 'in_progress' (you're working on it)
|
||||
```
|
||||
|
||||
**2. Add completion notes:**
|
||||
```bash
|
||||
bd update {{issue}} --notes "Implemented: <brief summary of what was done>"
|
||||
```
|
||||
|
||||
**3. Sync beads:**
|
||||
```bash
|
||||
bd sync
|
||||
```
|
||||
|
||||
**Exit criteria:** Issue updated with completion notes, beads synced."""
|
||||
|
||||
[[steps]]
|
||||
id = "submit-and-exit"
|
||||
title = "Submit to merge queue and exit"
|
||||
needs = ["prepare-for-review"]
|
||||
description = """
|
||||
Submit your work to the merge queue. You become recyclable after this.
|
||||
|
||||
**Ephemeral Polecat Model:**
|
||||
Once you submit, you're done. The Refinery will:
|
||||
1. Process your merge request
|
||||
2. Handle rebasing (mechanical rebases done automatically)
|
||||
3. Close your issue after successful merge
|
||||
4. Create conflict-resolution tasks if needed (fresh polecat handles those)
|
||||
|
||||
**1. Submit with gt done:**
|
||||
```bash
|
||||
gt done
|
||||
```
|
||||
|
||||
This single command:
|
||||
- Creates an MR bead in the merge queue
|
||||
- Notifies the Witness (POLECAT_DONE)
|
||||
- Updates your agent state to 'done'
|
||||
- Reports cleanup status (ZFC compliance)
|
||||
|
||||
**2. Verify submission:**
|
||||
You should see output like:
|
||||
```
|
||||
✓ Work submitted to merge queue
|
||||
MR ID: gt-xxxxx
|
||||
Source: polecat/<name>
|
||||
Target: main
|
||||
Issue: {{issue}}
|
||||
```
|
||||
|
||||
**3. You're recyclable:**
|
||||
Your work is in the queue. The Witness knows you're done.
|
||||
Your sandbox can be cleaned up - all work is pushed to origin.
|
||||
|
||||
If you have context remaining, you may:
|
||||
- Pick up new work from `bd ready`
|
||||
- Or use `gt handoff` to cycle to a fresh session
|
||||
|
||||
If the Refinery needs conflict resolution, it will dispatch a fresh polecat.
|
||||
You do NOT need to wait around.
|
||||
|
||||
**Exit criteria:** MR submitted, Witness notified, polecat recyclable."""
|
||||
|
||||
[vars]
|
||||
[vars.issue]
|
||||
description = "The issue ID assigned to this polecat"
|
||||
required = true
|
||||
469
internal/formula/formulas/mol-refinery-patrol.formula.toml
Normal file
469
internal/formula/formulas/mol-refinery-patrol.formula.toml
Normal file
@@ -0,0 +1,469 @@
|
||||
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?"
|
||||
|
||||
## Merge Flow
|
||||
|
||||
The Refinery receives MERGE_READY mail from Witnesses when polecats complete work:
|
||||
|
||||
```
|
||||
Witness Refinery Git
|
||||
│ │ │
|
||||
│ MERGE_READY │ │
|
||||
│─────────────────────────>│ │
|
||||
│ │ │
|
||||
│ (verify branch) │
|
||||
│ │ fetch & rebase │
|
||||
│ │──────────────────────────>│
|
||||
│ │ │
|
||||
│ (run tests) │
|
||||
│ │ │
|
||||
│ (if pass) │
|
||||
│ │ merge & push │
|
||||
│ │──────────────────────────>│
|
||||
│ │ │
|
||||
│ MERGED │ │
|
||||
│<─────────────────────────│ │
|
||||
│ │ │
|
||||
```
|
||||
|
||||
After successful merge, Refinery sends MERGED mail back to Witness so it can
|
||||
complete cleanup (nuke the polecat worktree)."""
|
||||
formula = "mol-refinery-patrol"
|
||||
version = 4
|
||||
|
||||
[[steps]]
|
||||
id = "inbox-check"
|
||||
title = "Check refinery mail"
|
||||
description = """
|
||||
Check mail for MERGE_READY submissions, escalations, and messages.
|
||||
|
||||
```bash
|
||||
gt mail inbox
|
||||
```
|
||||
|
||||
For each message:
|
||||
|
||||
**MERGE_READY**:
|
||||
A polecat's work is ready for merge. Extract details and track for processing.
|
||||
|
||||
```bash
|
||||
# Parse MERGE_READY message body:
|
||||
# Branch: <branch>
|
||||
# Issue: <issue-id>
|
||||
# Polecat: <polecat-name>
|
||||
# MR: <mr-bead-id>
|
||||
# Verified: clean git state, issue closed
|
||||
|
||||
# Track in your merge queue for this patrol cycle:
|
||||
# - Branch name
|
||||
# - Issue ID
|
||||
# - Polecat name (REQUIRED for MERGED notification)
|
||||
# - MR bead ID (REQUIRED for closing after merge)
|
||||
```
|
||||
|
||||
**IMPORTANT**: You MUST track the polecat name, MR bead ID, AND message ID - you will need them
|
||||
in merge-push step to send MERGED notification, close the MR bead, and archive the mail.
|
||||
|
||||
Mark as read. The work will be processed in queue-scan/process-branch.
|
||||
**Do NOT archive yet** - archive after merge/reject decision in merge-push step.
|
||||
|
||||
**PATROL: Wake up**:
|
||||
Witness detected MRs waiting but refinery idle. Acknowledge and archive:
|
||||
```bash
|
||||
gt mail archive <message-id>
|
||||
```
|
||||
|
||||
**HELP / Blocked**:
|
||||
Assess and respond. If you can't help, escalate to Mayor.
|
||||
Archive after handling:
|
||||
```bash
|
||||
gt mail archive <message-id>
|
||||
```
|
||||
|
||||
**HANDOFF**:
|
||||
Read predecessor context. Check for in-flight merges.
|
||||
Archive after absorbing context:
|
||||
```bash
|
||||
gt mail archive <message-id>
|
||||
```
|
||||
|
||||
**Hygiene principle**: Archive messages after they're fully processed.
|
||||
Keep only: pending MRs in queue. Inbox should be near-empty."""
|
||||
|
||||
[[steps]]
|
||||
id = "queue-scan"
|
||||
title = "Scan merge queue"
|
||||
needs = ["inbox-check"]
|
||||
description = """
|
||||
Check the beads merge queue - this is the SOURCE OF TRUTH for pending merges.
|
||||
|
||||
```bash
|
||||
git fetch --prune origin
|
||||
gt mq list <rig>
|
||||
```
|
||||
|
||||
The beads MQ tracks all pending merge requests. Do NOT rely on `git branch -r | grep polecat`
|
||||
as branches may exist without MR beads, or MR beads may exist for already-merged work.
|
||||
|
||||
If queue empty, skip to context-check step.
|
||||
|
||||
For each MR in the queue, verify the branch still exists:
|
||||
```bash
|
||||
git branch -r | grep <branch>
|
||||
```
|
||||
|
||||
If branch doesn't exist for a queued MR:
|
||||
- Close the MR bead: `bd close <mr-id> --reason "Branch no longer exists"`
|
||||
- Remove from processing queue
|
||||
|
||||
Track verified MR list for this cycle."""
|
||||
|
||||
[[steps]]
|
||||
id = "process-branch"
|
||||
title = "Mechanical rebase"
|
||||
needs = ["queue-scan"]
|
||||
description = """
|
||||
Pick next branch from queue. Attempt mechanical rebase on current main.
|
||||
|
||||
**Step 1: Checkout and attempt rebase**
|
||||
```bash
|
||||
git checkout -b temp origin/<polecat-branch>
|
||||
git rebase origin/main
|
||||
```
|
||||
|
||||
**Step 2: Check rebase result**
|
||||
|
||||
The rebase exits with:
|
||||
- Exit code 0: Success - proceed to run-tests
|
||||
- Exit code 1 (conflicts): Conflict detected - proceed to Step 3
|
||||
|
||||
To detect conflict state after rebase fails:
|
||||
```bash
|
||||
# Check if we're in a conflicted rebase state
|
||||
ls .git/rebase-merge 2>/dev/null && echo "CONFLICT_STATE"
|
||||
```
|
||||
|
||||
**Step 3: Handle conflicts (if any)**
|
||||
|
||||
If rebase SUCCEEDED (exit code 0):
|
||||
- Skip to run-tests step (continue normal merge flow)
|
||||
|
||||
If rebase FAILED with conflicts:
|
||||
|
||||
1. **Abort the rebase** (DO NOT leave repo in conflicted state):
|
||||
```bash
|
||||
git rebase --abort
|
||||
```
|
||||
|
||||
2. **Record conflict metadata**:
|
||||
```bash
|
||||
# Capture main SHA for reference
|
||||
MAIN_SHA=$(git rev-parse origin/main)
|
||||
BRANCH_SHA=$(git rev-parse origin/<polecat-branch>)
|
||||
```
|
||||
|
||||
3. **Create conflict-resolution task**:
|
||||
```bash
|
||||
bd create --type=task --priority=1 \
|
||||
--title="Resolve merge conflicts: <original-issue-title>" \
|
||||
--description="## Conflict Resolution Required
|
||||
|
||||
Original MR: <mr-bead-id>
|
||||
Branch: <polecat-branch>
|
||||
Original Issue: <issue-id>
|
||||
Conflict with main at: ${MAIN_SHA}
|
||||
Branch SHA: ${BRANCH_SHA}
|
||||
|
||||
## Instructions
|
||||
1. Clone/checkout the branch
|
||||
2. Rebase on current main: git rebase origin/main
|
||||
3. Resolve conflicts
|
||||
4. Force push: git push -f origin <branch>
|
||||
5. Close this task when done
|
||||
|
||||
The MR will be re-queued for processing after conflicts are resolved."
|
||||
```
|
||||
|
||||
4. **Skip this MR** (do NOT delete branch or close MR bead):
|
||||
- Leave branch intact for conflict resolution
|
||||
- Leave MR bead open (will be re-processed after resolution)
|
||||
- Continue to loop-check for next branch
|
||||
|
||||
**CRITICAL**: Never delete a branch that has conflicts. The branch contains
|
||||
the original work and must be preserved for conflict resolution.
|
||||
|
||||
Track: rebase result (success/conflict), conflict task ID if created."""
|
||||
|
||||
[[steps]]
|
||||
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."""
|
||||
|
||||
[[steps]]
|
||||
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." """
|
||||
|
||||
[[steps]]
|
||||
id = "merge-push"
|
||||
title = "Merge and push to main"
|
||||
needs = ["handle-failures"]
|
||||
description = """
|
||||
Merge to main and push. CRITICAL: Notifications come IMMEDIATELY after push.
|
||||
|
||||
**Step 1: Merge and Push**
|
||||
```bash
|
||||
git checkout main
|
||||
git merge --ff-only temp
|
||||
git push origin main
|
||||
```
|
||||
|
||||
⚠️ **STOP HERE - DO NOT PROCEED UNTIL STEPS 2-3 COMPLETE**
|
||||
|
||||
**Step 2: Send MERGED Notification (REQUIRED - DO THIS IMMEDIATELY)**
|
||||
|
||||
RIGHT NOW, before any cleanup, send MERGED mail to Witness:
|
||||
|
||||
```bash
|
||||
gt mail send <rig>/witness -s "MERGED <polecat-name>" -m "Branch: <branch>
|
||||
Issue: <issue-id>
|
||||
Merged-At: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||
```
|
||||
|
||||
This signals the Witness to nuke the polecat worktree. WITHOUT THIS NOTIFICATION,
|
||||
POLECAT WORKTREES ACCUMULATE INDEFINITELY AND THE LIFECYCLE BREAKS.
|
||||
|
||||
**Step 3: Close MR Bead (REQUIRED - DO THIS IMMEDIATELY)**
|
||||
|
||||
⚠️ **VERIFICATION BEFORE CLOSING**: Confirm the work is actually on main:
|
||||
```bash
|
||||
# Get the commit message/issue from the branch
|
||||
git log origin/main --oneline | grep "<issue-id>"
|
||||
# OR verify the commit SHA is on main:
|
||||
git branch --contains <commit-sha> | grep main
|
||||
```
|
||||
|
||||
If work is NOT on main, DO NOT close the MR bead. Investigate first.
|
||||
|
||||
```bash
|
||||
bd close <mr-bead-id> --reason "Merged to main at $(git rev-parse --short HEAD)"
|
||||
```
|
||||
|
||||
The MR bead ID was in the MERGE_READY message or find via:
|
||||
```bash
|
||||
bd list --type=merge-request --status=open | grep <polecat-name>
|
||||
```
|
||||
|
||||
**VALIDATION**: The MR bead's source_issue should be a valid bead ID (gt-xxxxx),
|
||||
not a branch name. If source_issue contains a branch name, flag for investigation.
|
||||
|
||||
**Step 4: Archive the MERGE_READY mail (REQUIRED)**
|
||||
```bash
|
||||
gt mail archive <merge-ready-message-id>
|
||||
```
|
||||
The message ID was tracked when you processed inbox-check.
|
||||
|
||||
**Step 5: Cleanup (only after Steps 2-4 confirmed)**
|
||||
```bash
|
||||
git branch -d temp
|
||||
git push origin --delete <polecat-branch>
|
||||
```
|
||||
|
||||
**VERIFICATION GATE**: You CANNOT proceed to loop-check without:
|
||||
- [x] MERGED mail sent to witness
|
||||
- [x] MR bead closed
|
||||
- [x] MERGE_READY mail archived
|
||||
|
||||
If you skipped notifications or archiving, GO BACK AND DO THEM NOW.
|
||||
|
||||
Main has moved. Any remaining branches need rebasing on new baseline."""
|
||||
|
||||
[[steps]]
|
||||
id = "loop-check"
|
||||
title = "Check for more work"
|
||||
needs = ["merge-push"]
|
||||
description = """
|
||||
More branches to process?
|
||||
|
||||
**Entry paths:**
|
||||
- Normal: After successful merge-push
|
||||
- Conflict-skip: After process-branch created conflict-resolution task
|
||||
|
||||
If yes: Return to process-branch with next branch.
|
||||
If no: Continue to generate-summary.
|
||||
|
||||
**Track for this cycle:**
|
||||
- branches_merged: count and names of successfully merged branches
|
||||
- branches_conflict: count and names of branches skipped due to conflicts
|
||||
- conflict_tasks: IDs of conflict-resolution tasks created
|
||||
|
||||
This tracking feeds into generate-summary for the patrol digest."""
|
||||
|
||||
[[steps]]
|
||||
id = "generate-summary"
|
||||
title = "Generate handoff summary"
|
||||
needs = ["loop-check"]
|
||||
description = """
|
||||
Summarize this patrol cycle.
|
||||
|
||||
**VERIFICATION**: Before generating summary, confirm for each merged branch:
|
||||
- [ ] MERGED mail was sent to witness
|
||||
- [ ] MR bead was closed
|
||||
- [ ] MERGE_READY mail archived
|
||||
|
||||
If any notifications or archiving were missed, do them now!
|
||||
|
||||
Include in summary:
|
||||
- Branches merged (count, names)
|
||||
- MERGED mails sent (count - should match branches merged)
|
||||
- MR beads closed (count - should match branches merged)
|
||||
- MERGE_READY mails archived (count - should match branches merged)
|
||||
- Test results (pass/fail)
|
||||
- Branches with conflicts (count, names)
|
||||
- Conflict-resolution tasks created (IDs)
|
||||
- Issues filed (if any)
|
||||
- Any escalations sent
|
||||
|
||||
**Conflict tracking is important** for monitoring MQ health. If many branches
|
||||
conflict, it may indicate main is moving too fast or branches are too stale.
|
||||
|
||||
This becomes the digest when the patrol is squashed."""
|
||||
|
||||
[[steps]]
|
||||
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"""
|
||||
|
||||
[[steps]]
|
||||
id = "patrol-cleanup"
|
||||
title = "End-of-cycle inbox hygiene"
|
||||
needs = ["context-check"]
|
||||
description = """
|
||||
Verify inbox hygiene before ending patrol cycle.
|
||||
|
||||
**Step 1: Check inbox state**
|
||||
```bash
|
||||
gt mail inbox
|
||||
```
|
||||
|
||||
Inbox should contain ONLY:
|
||||
- Unprocessed MERGE_READY messages (will process next cycle)
|
||||
- Active work items
|
||||
|
||||
**Step 2: Archive any stale messages**
|
||||
|
||||
Look for messages that were processed but not archived:
|
||||
- PATROL: Wake up that was acknowledged → archive
|
||||
- HELP/Blocked that was handled → archive
|
||||
- MERGE_READY where merge completed but archive was missed → archive
|
||||
|
||||
```bash
|
||||
# For each stale message found:
|
||||
gt mail archive <message-id>
|
||||
```
|
||||
|
||||
**Step 3: Check for orphaned MR beads**
|
||||
|
||||
Look for open MR beads with no corresponding branch:
|
||||
```bash
|
||||
bd list --type=merge-request --status=open
|
||||
```
|
||||
|
||||
For each open MR bead:
|
||||
1. Check if branch exists: `git ls-remote origin refs/heads/<branch>`
|
||||
2. If branch gone, verify work is on main: `git log origin/main --oneline | grep "<source_issue>"`
|
||||
3. If work on main → close MR with reason "Merged (verified on main)"
|
||||
4. If work NOT on main → investigate before closing:
|
||||
- Check source_issue validity (should be gt-xxxxx, not branch name)
|
||||
- Search reflog/dangling commits if possible
|
||||
- If unverifiable, close with reason "Unverifiable - no audit trail"
|
||||
- File bead if this indicates lost work
|
||||
|
||||
**NEVER close an MR bead without verifying the work landed or is unrecoverable.**
|
||||
|
||||
**Goal**: Inbox should have ≤3 active messages at end of cycle.
|
||||
Keep only: pending MRs in queue."""
|
||||
|
||||
[[steps]]
|
||||
id = "burn-or-loop"
|
||||
title = "Burn and respawn or loop"
|
||||
needs = ["patrol-cleanup"]
|
||||
description = """
|
||||
End of patrol cycle decision.
|
||||
|
||||
**Step 1: Estimate remaining context**
|
||||
|
||||
Ask yourself:
|
||||
- Have I processed many branches this cycle?
|
||||
- Is the conversation getting long?
|
||||
- Am I starting to lose track of earlier context?
|
||||
|
||||
Rule of thumb: If you've done 3+ merges or processed significant cleanup work,
|
||||
it's time for a fresh session.
|
||||
|
||||
**Step 2: Decision tree**
|
||||
|
||||
If queue non-empty AND context LOW:
|
||||
- Squash this wisp to digest
|
||||
- Spawn fresh patrol wisp
|
||||
- Return to inbox-check
|
||||
|
||||
If queue empty OR context HIGH OR good stopping point:
|
||||
- Squash wisp with summary digest
|
||||
- Use `gt handoff` for clean session transition:
|
||||
|
||||
```bash
|
||||
gt handoff -s "Patrol complete" -m "Merged X branches, Y tests passed.
|
||||
Queue: empty/N remaining
|
||||
Next: [any notes for successor]"
|
||||
```
|
||||
|
||||
**Why gt handoff?**
|
||||
- Sends handoff mail to yourself with context
|
||||
- Respawns with fresh Claude instance
|
||||
- SessionStart hook runs gt prime
|
||||
- Successor picks up from your hook
|
||||
|
||||
**DO NOT just exit.** Always use `gt handoff` for proper lifecycle."""
|
||||
250
internal/formula/formulas/mol-session-gc.formula.toml
Normal file
250
internal/formula/formulas/mol-session-gc.formula.toml
Normal file
@@ -0,0 +1,250 @@
|
||||
description = """
|
||||
Clean stale sessions and garbage collect.
|
||||
|
||||
Dogs work through molecules (poured from this formula) to clean up dead sessions, orphaned processes, and
|
||||
other system cruft. This is the garbage collector for Gas Town's runtime:
|
||||
- Dead tmux sessions (no Claude process)
|
||||
- Orphaned Claude processes (no tmux parent)
|
||||
- Stale wisps past retention
|
||||
- Leftover state files
|
||||
|
||||
## Dog Contract
|
||||
|
||||
This is infrastructure work. You:
|
||||
1. Receive gc scope via hook_bead (aggressive or conservative)
|
||||
2. Identify garbage across the system
|
||||
3. Safely remove dead state
|
||||
4. Report what was cleaned
|
||||
5. Return to kennel
|
||||
|
||||
## Variables
|
||||
|
||||
| Variable | Source | Description |
|
||||
|----------|--------|-------------|
|
||||
| mode | hook_bead | GC mode: 'conservative' (safe) or 'aggressive' (thorough) |
|
||||
|
||||
## Safety
|
||||
|
||||
GC is destructive. This formula errs on the side of caution:
|
||||
- Conservative mode: Only obviously dead things
|
||||
- Aggressive mode: Includes old but possibly-recoverable state
|
||||
|
||||
Running `gt doctor --fix` handles most of this. This formula wraps it with
|
||||
reporting and multi-rig scope."""
|
||||
formula = "mol-session-gc"
|
||||
version = 1
|
||||
|
||||
[squash]
|
||||
trigger = "on_complete"
|
||||
template_type = "work"
|
||||
include_metrics = true
|
||||
|
||||
[[steps]]
|
||||
id = "determine-mode"
|
||||
title = "Determine GC mode"
|
||||
description = """
|
||||
Establish GC aggressiveness level.
|
||||
|
||||
**1. Check assignment:**
|
||||
```bash
|
||||
gt hook # Shows mode in hook_bead
|
||||
```
|
||||
|
||||
**2. Mode definitions:**
|
||||
|
||||
| Mode | Description | Risk |
|
||||
|------|-------------|------|
|
||||
| conservative | Only clearly dead state | Very low |
|
||||
| aggressive | Includes stale state | Low but non-zero |
|
||||
|
||||
**Conservative targets:**
|
||||
- tmux sessions with no processes
|
||||
- Claude processes with no tmux parent
|
||||
- Wisps > 24 hours old
|
||||
|
||||
**Aggressive additions:**
|
||||
- Wisps > 1 hour old
|
||||
- Branches with no matching polecat
|
||||
- State files > 7 days old
|
||||
|
||||
**Exit criteria:** GC mode determined."""
|
||||
|
||||
[[steps]]
|
||||
id = "preview-cleanup"
|
||||
title = "Preview what will be cleaned"
|
||||
needs = ["determine-mode"]
|
||||
description = """
|
||||
Identify garbage without removing it yet.
|
||||
|
||||
**1. Run doctor in preview mode:**
|
||||
```bash
|
||||
gt doctor -v
|
||||
# Shows what would be cleaned, doesn't do it
|
||||
```
|
||||
|
||||
**2. Parse doctor output for:**
|
||||
- orphan-sessions: Tmux sessions to kill
|
||||
- orphan-processes: Claude processes to terminate
|
||||
- wisp-gc: Wisps to delete
|
||||
|
||||
**3. Additional scans (for aggressive mode):**
|
||||
```bash
|
||||
# Old branches
|
||||
git branch --list 'polecat/*' | while read branch; do
|
||||
last_commit=$(git log -1 --format=%ct "$branch")
|
||||
# If > 7 days old and no matching polecat, candidate for cleanup
|
||||
done
|
||||
|
||||
# Old state files
|
||||
find ~/.gt/ -name "*.state" -mtime +7
|
||||
```
|
||||
|
||||
**4. Compile cleanup manifest:**
|
||||
Record each item to be cleaned with:
|
||||
- Type (session, process, wisp, branch, state)
|
||||
- Identifier
|
||||
- Age
|
||||
- Reason for cleanup
|
||||
|
||||
**Exit criteria:** Cleanup manifest ready, nothing deleted yet."""
|
||||
|
||||
[[steps]]
|
||||
id = "execute-gc"
|
||||
title = "Execute garbage collection"
|
||||
needs = ["preview-cleanup"]
|
||||
description = """
|
||||
Actually remove the garbage.
|
||||
|
||||
**1. Run doctor with fix:**
|
||||
```bash
|
||||
gt doctor --fix
|
||||
# This handles sessions, processes, and wisps
|
||||
```
|
||||
|
||||
**2. For aggressive mode, additional cleanup:**
|
||||
```bash
|
||||
# Old branches (if aggressive mode)
|
||||
git branch -D <old-branch>
|
||||
|
||||
# Old state files (if aggressive mode)
|
||||
rm <state-file>
|
||||
```
|
||||
|
||||
**3. Track what was deleted:**
|
||||
Record each item actually removed for the report.
|
||||
|
||||
**Safety checks:**
|
||||
- Never delete active sessions (tmux list-clients)
|
||||
- Never delete branches with uncommitted polecat work
|
||||
- Never delete wisps < 1 hour old
|
||||
|
||||
**Exit criteria:** Garbage collected."""
|
||||
|
||||
[[steps]]
|
||||
id = "verify-cleanup"
|
||||
title = "Verify cleanup was successful"
|
||||
needs = ["execute-gc"]
|
||||
description = """
|
||||
Confirm garbage was actually removed.
|
||||
|
||||
**1. Re-run doctor to verify:**
|
||||
```bash
|
||||
gt doctor -v
|
||||
# Should show no issues (or fewer issues)
|
||||
```
|
||||
|
||||
**2. Check for stragglers:**
|
||||
```bash
|
||||
# Tmux sessions
|
||||
tmux list-sessions 2>/dev/null
|
||||
|
||||
# Claude processes
|
||||
pgrep -f claude
|
||||
|
||||
# Wisps
|
||||
ls .beads-wisp/ 2>/dev/null | wc -l
|
||||
```
|
||||
|
||||
**3. Compare before/after:**
|
||||
- Sessions before: N → after: M
|
||||
- Processes before: N → after: M
|
||||
- Wisps before: N → after: M
|
||||
|
||||
**Exit criteria:** Cleanup verified."""
|
||||
|
||||
[[steps]]
|
||||
id = "report-gc"
|
||||
title = "Generate GC report"
|
||||
needs = ["verify-cleanup"]
|
||||
description = """
|
||||
Create summary report of garbage collection.
|
||||
|
||||
**1. Generate report:**
|
||||
```markdown
|
||||
## Session GC Report: {{timestamp}}
|
||||
|
||||
**Mode**: {{mode}}
|
||||
|
||||
### Summary
|
||||
| Type | Before | After | Cleaned |
|
||||
|------|--------|-------|---------|
|
||||
| Sessions | X | Y | Z |
|
||||
| Processes | X | Y | Z |
|
||||
| Wisps | X | Y | Z |
|
||||
| Branches | X | Y | Z |
|
||||
| State files | X | Y | Z |
|
||||
|
||||
### Items Cleaned
|
||||
{{#each cleaned}}
|
||||
- {{type}}: {{identifier}} (age: {{age}}, reason: {{reason}})
|
||||
{{/each}}
|
||||
|
||||
### Errors
|
||||
{{#if errors}}
|
||||
{{#each errors}}
|
||||
- {{item}}: {{error}}
|
||||
{{/each}}
|
||||
{{else}}
|
||||
None
|
||||
{{/if}}
|
||||
|
||||
### Space Recovered
|
||||
~{{bytes_freed}} bytes
|
||||
```
|
||||
|
||||
**2. Send to Deacon:**
|
||||
```bash
|
||||
gt mail send deacon/ -s "GC complete: {{total_cleaned}} items" \
|
||||
-m "{{report}}"
|
||||
```
|
||||
|
||||
**Exit criteria:** Report sent."""
|
||||
|
||||
[[steps]]
|
||||
id = "return-to-kennel"
|
||||
title = "Signal completion and return to kennel"
|
||||
needs = ["report-gc"]
|
||||
description = """
|
||||
Signal work complete and return to available pool.
|
||||
|
||||
**1. Signal completion to Deacon:**
|
||||
```bash
|
||||
gt mail send deacon/ -s "DOG_DONE $(hostname)" -m "Task: session-gc
|
||||
Mode: {{mode}}
|
||||
Items cleaned: {{total_cleaned}}
|
||||
Space recovered: {{bytes_freed}}
|
||||
Status: COMPLETE
|
||||
|
||||
Ready for next assignment."
|
||||
```
|
||||
|
||||
**2. Return to kennel:**
|
||||
Dog returns to available state in the pool.
|
||||
|
||||
**Exit criteria:** Deacon notified, dog ready for next work."""
|
||||
|
||||
[vars]
|
||||
[vars.mode]
|
||||
description = "GC mode: 'conservative' or 'aggressive'"
|
||||
required = true
|
||||
default = "conservative"
|
||||
491
internal/formula/formulas/mol-sync-workspace.formula.toml
Normal file
491
internal/formula/formulas/mol-sync-workspace.formula.toml
Normal file
@@ -0,0 +1,491 @@
|
||||
description = """
|
||||
Workspace synchronization molecule for batch prep.
|
||||
|
||||
This molecule prepares an agent's workspace for a new batch of work. It syncs git
|
||||
and beads state, cleans up cruft, verifies the baseline is healthy, and reports
|
||||
readiness. Designed to be broadcast to all agents when preparing for coordinated
|
||||
work.
|
||||
|
||||
## When to Use
|
||||
|
||||
- Before major batch assignments
|
||||
- After extended idle periods
|
||||
- When repo baseline has diverged significantly
|
||||
- Proactive cleanup between work sessions
|
||||
|
||||
## Conflict Resolution Philosophy
|
||||
|
||||
**Preserve and re-land, not discard.**
|
||||
|
||||
When git pull/rebase hits conflicts, the agent should:
|
||||
1. Assess if the conflicting work is still valuable
|
||||
2. If valuable: re-envision against new baseline, file a bead for re-implementation
|
||||
3. If obsolete (e.g., touches deleted system): discard with a note
|
||||
4. If unclear: escalate to human/mayor
|
||||
|
||||
Polecats can file a bead and delegate. Refinery must resolve inline.
|
||||
|
||||
## Variables
|
||||
|
||||
| Variable | Source | Description |
|
||||
|----------|--------|-------------|
|
||||
| role | auto-detected | Agent role (crew/polecat/refinery/witness) |
|
||||
| build_command | config or default | Build command (default: `go build ./...`) |
|
||||
| test_command | config or default | Test command (default: `go test ./...`) |
|
||||
|
||||
## Failure Modes
|
||||
|
||||
| Situation | Action |
|
||||
|-----------|--------|
|
||||
| Rebase conflict | Preserve work via bead, complete sync on clean state |
|
||||
| Uncommitted work | Stash or commit, then sync |
|
||||
| Untracked files | Warn, let agent decide (keep/delete/gitignore) |
|
||||
| bd doctor errors | Report findings, agent triages |
|
||||
| Test failures | File bead if not already tracked, don't block sync |
|
||||
| Build failure | Must be resolved before marking sync complete |"""
|
||||
formula = "mol-sync-workspace"
|
||||
version = 1
|
||||
|
||||
[[steps]]
|
||||
id = "assess-state"
|
||||
title = "Assess current workspace state"
|
||||
description = """
|
||||
Capture current state before any changes.
|
||||
|
||||
**0. Prime context (critical for fresh sessions):**
|
||||
```bash
|
||||
gt prime # Load gt/mol context
|
||||
bd prime # Load beads context
|
||||
```
|
||||
These ensure the agent has full system context, especially after session
|
||||
start, compaction, or context clear.
|
||||
|
||||
**1. Check git status:**
|
||||
```bash
|
||||
git status --porcelain
|
||||
git stash list
|
||||
git branch --show-current
|
||||
```
|
||||
|
||||
**2. Check beads state:**
|
||||
```bash
|
||||
bd sync --status
|
||||
```
|
||||
|
||||
**3. Document starting state:**
|
||||
- Branch name
|
||||
- Uncommitted changes (staged/unstaged)
|
||||
- Untracked files (list them)
|
||||
- Stash entries
|
||||
- Beads sync status
|
||||
|
||||
This information guides decisions in subsequent steps.
|
||||
|
||||
**Exit criteria:** Context primed, starting state documented."""
|
||||
|
||||
[[steps]]
|
||||
id = "handle-dirty-state"
|
||||
title = "Handle uncommitted and untracked work"
|
||||
needs = ["assess-state"]
|
||||
description = """
|
||||
Clean up any in-flight work before syncing.
|
||||
|
||||
**If uncommitted changes exist:**
|
||||
|
||||
Option A - Commit if ready:
|
||||
```bash
|
||||
git add -A && git commit -m "WIP: <description>"
|
||||
```
|
||||
|
||||
Option B - Stash if not ready:
|
||||
```bash
|
||||
git stash push -m "pre-sync stash $(date +%Y%m%d-%H%M%S)"
|
||||
```
|
||||
|
||||
**If untracked files exist:**
|
||||
|
||||
For each untracked file, decide:
|
||||
- **Keep**: Add to .gitignore or commit
|
||||
- **Delete**: `rm <file>`
|
||||
- **Stash**: Can't stash untracked directly; commit or delete
|
||||
|
||||
**WARN the agent**: Report untracked files prominently. They may be:
|
||||
- WIP that should be preserved
|
||||
- Build artifacts that should be gitignored
|
||||
- Cruft that should be deleted
|
||||
|
||||
Don't auto-delete without confirmation.
|
||||
|
||||
**If stash entries exist:**
|
||||
|
||||
List and assess:
|
||||
```bash
|
||||
git stash list
|
||||
git stash show -p stash@{0} # Preview each
|
||||
```
|
||||
|
||||
Old stashes (>1 week) are likely stale. Consider dropping.
|
||||
Recent stashes may contain valuable WIP - preserve or commit.
|
||||
|
||||
**Exit criteria:** Working tree clean or explicitly stashed."""
|
||||
|
||||
[[steps]]
|
||||
id = "sync-git"
|
||||
title = "Sync with git remote"
|
||||
needs = ["cleanup-worktrees"]
|
||||
description = """
|
||||
Fetch and rebase onto current main.
|
||||
|
||||
**1. Fetch latest:**
|
||||
```bash
|
||||
git fetch origin
|
||||
```
|
||||
|
||||
**2. Check divergence:**
|
||||
```bash
|
||||
git log --oneline HEAD..origin/main | head -5 # What's new on main
|
||||
git log --oneline origin/main..HEAD | head -5 # What we have locally
|
||||
```
|
||||
|
||||
**3. Rebase onto main:**
|
||||
```bash
|
||||
git pull --rebase origin main
|
||||
```
|
||||
|
||||
**If rebase succeeds:** Continue to next step.
|
||||
|
||||
**If rebase conflicts:**
|
||||
|
||||
The Preservation Protocol:
|
||||
1. **Assess the conflicting work:**
|
||||
- Is it still valuable against new baseline?
|
||||
- Is the conflicting area still relevant (not deleted/refactored)?
|
||||
|
||||
2. **If work is valuable:**
|
||||
```bash
|
||||
git rebase --abort
|
||||
# Create a bead capturing the work
|
||||
bd create --title "Re-land: <description>" --type task --priority 2
|
||||
# Note the changes needed in the bead description
|
||||
# Reset to clean state
|
||||
git reset --hard origin/main
|
||||
```
|
||||
|
||||
3. **If work is obsolete:**
|
||||
```bash
|
||||
git rebase --abort
|
||||
git reset --hard origin/main
|
||||
# Note why it was discarded
|
||||
```
|
||||
|
||||
4. **If unclear:**
|
||||
- Escalate to human/mayor
|
||||
- Document the conflict
|
||||
- Proceed with sync on clean baseline
|
||||
|
||||
**Refinery-specific conflict resolution:**
|
||||
|
||||
Unlike polecats who can file a bead and delegate, the Refinery MUST resolve
|
||||
conflicts inline. The Refinery processes the merge queue - if it can't resolve
|
||||
a conflict, the queue backs up and polecats' completed work never lands.
|
||||
|
||||
**Refinery conflict protocol:**
|
||||
|
||||
1. **Analyze the conflict:**
|
||||
```bash
|
||||
git status # See conflicted files
|
||||
git diff # Examine conflict markers
|
||||
```
|
||||
|
||||
2. **Trivial conflicts (resolve immediately):**
|
||||
- Import ordering changes
|
||||
- Whitespace or formatting
|
||||
- Non-overlapping additions in same file
|
||||
- Version bumps or changelog entries
|
||||
|
||||
Resolution:
|
||||
```bash
|
||||
# Edit conflicted files to resolve
|
||||
git add <resolved-files>
|
||||
git rebase --continue
|
||||
```
|
||||
|
||||
3. **Semantic conflicts (assess carefully):**
|
||||
- Both sides modified same function
|
||||
- Deleted code that the other side modified
|
||||
- Structural changes (renamed files, moved code)
|
||||
|
||||
If you can understand both intents and merge correctly:
|
||||
```bash
|
||||
# Carefully merge the changes, preserving both intents
|
||||
git add <resolved-files>
|
||||
git rebase --continue
|
||||
```
|
||||
|
||||
4. **Complex conflicts (abort and notify polecat):**
|
||||
- Conflicting business logic changes
|
||||
- Unclear which version is correct
|
||||
- Risk of subtle bugs from incorrect merge
|
||||
|
||||
```bash
|
||||
git rebase --abort
|
||||
gt mail send {{ rig }}/polecats/<worker> -s "Rebase needed" \
|
||||
-m "Your branch conflicts with main in <files>. Please rebase and resubmit via gt done."
|
||||
# Skip this branch, continue with queue
|
||||
```
|
||||
|
||||
**The Refinery judgment call:** When in doubt, abort and notify rather than
|
||||
risk merging incorrectly. A resubmitted branch is better than a broken main.
|
||||
|
||||
**Exit criteria:** Local branch matches or is cleanly ahead of origin/main."""
|
||||
|
||||
[[steps]]
|
||||
id = "sync-beads"
|
||||
title = "Sync beads state"
|
||||
needs = ["sync-git"]
|
||||
description = """
|
||||
Sync the beads database with remote.
|
||||
|
||||
```bash
|
||||
bd sync
|
||||
```
|
||||
|
||||
**If conflicts:**
|
||||
Beads uses JSONL append-only format, so true conflicts are rare.
|
||||
If they occur:
|
||||
```bash
|
||||
bd sync --status # Diagnose
|
||||
```
|
||||
|
||||
Likely causes:
|
||||
- Two agents edited same bead simultaneously
|
||||
- Corrupted beads-sync branch
|
||||
|
||||
Resolution:
|
||||
```bash
|
||||
# Try pulling fresh
|
||||
git fetch origin beads-sync
|
||||
bd sync
|
||||
```
|
||||
|
||||
If still failing, escalate to mayor.
|
||||
|
||||
**Exit criteria:** Beads synced successfully."""
|
||||
|
||||
[[steps]]
|
||||
id = "run-doctor"
|
||||
title = "Run beads health check"
|
||||
needs = ["sync-beads"]
|
||||
description = """
|
||||
Check for beads system issues.
|
||||
|
||||
```bash
|
||||
bd doctor
|
||||
```
|
||||
|
||||
**Triage findings:**
|
||||
|
||||
| Finding | Action |
|
||||
|---------|--------|
|
||||
| Orphaned issues (committed but not closed) | Review and close if complete |
|
||||
| Missing dependencies | Fix with `bd dep add` |
|
||||
| Circular dependencies | Resolve or escalate |
|
||||
| Stale in_progress | Check if still active, update status |
|
||||
| Route misconfigurations | Fix routes.jsonl |
|
||||
|
||||
**For each issue found:**
|
||||
- Quick fix (<5 min): Fix now
|
||||
- Medium fix: File a bead
|
||||
- Unclear: Note for human review
|
||||
|
||||
**Exit criteria:** Doctor findings triaged (not necessarily all fixed)."""
|
||||
|
||||
[[steps]]
|
||||
id = "verify-build"
|
||||
title = "Verify project builds"
|
||||
needs = ["run-doctor"]
|
||||
description = """
|
||||
Ensure the codebase compiles.
|
||||
|
||||
```bash
|
||||
{{ build_command }}
|
||||
# Default: go build ./...
|
||||
# Configure via build_command variable for non-Go projects
|
||||
```
|
||||
|
||||
**If build succeeds:** Continue to next step.
|
||||
|
||||
**If build fails:**
|
||||
|
||||
This is a blocking issue. Options:
|
||||
|
||||
1. **Quick fix (<15 min):** Fix it now
|
||||
```bash
|
||||
# Fix the issue
|
||||
git add <files>
|
||||
git commit -m "fix: build failure from sync"
|
||||
git push
|
||||
```
|
||||
|
||||
2. **Non-trivial fix:** File and escalate
|
||||
```bash
|
||||
bd create --title "Build broken on main" --type bug --priority 0
|
||||
gt mail send --human -s "ALERT: Main doesn't build" -m "Details..."
|
||||
```
|
||||
Cannot mark sync complete with broken build.
|
||||
|
||||
**Exit criteria:** Project builds successfully."""
|
||||
|
||||
[[steps]]
|
||||
id = "run-tests"
|
||||
title = "Run test suite"
|
||||
needs = ["verify-build"]
|
||||
description = """
|
||||
Verify tests pass on current baseline.
|
||||
|
||||
```bash
|
||||
{{ test_command }}
|
||||
# Default: go test ./...
|
||||
# Configure via test_command variable for non-Go projects
|
||||
```
|
||||
|
||||
**If tests pass:** Continue to next step.
|
||||
|
||||
**If tests fail:**
|
||||
|
||||
Unlike build failures, test failures don't block sync completion.
|
||||
However, they must be tracked.
|
||||
|
||||
1. **Check if already tracked:**
|
||||
```bash
|
||||
bd list --type=bug --status=open | grep -i test
|
||||
```
|
||||
|
||||
2. **If not tracked, file a bead:**
|
||||
```bash
|
||||
bd create --title "Test failure: <test name>" --type bug --priority 1
|
||||
```
|
||||
|
||||
3. **Report in summary:**
|
||||
Note which tests are failing so batch assignment accounts for it.
|
||||
|
||||
**Exit criteria:** Test results documented, failures tracked in beads."""
|
||||
|
||||
[[steps]]
|
||||
id = "cleanup-worktrees"
|
||||
title = "Clean up stale worktrees"
|
||||
needs = ["handle-dirty-state"]
|
||||
description = """
|
||||
Remove orphaned worktrees from cross-rig work.
|
||||
|
||||
**1. List worktrees:**
|
||||
```bash
|
||||
git worktree list
|
||||
```
|
||||
|
||||
**2. Identify stale worktrees:**
|
||||
- Worktrees for branches that no longer exist on remote
|
||||
- Worktrees older than 1 week without recent commits
|
||||
- Worktrees for completed/merged work
|
||||
|
||||
**3. Remove stale worktrees:**
|
||||
```bash
|
||||
git worktree remove <path>
|
||||
# Or force if needed:
|
||||
git worktree remove --force <path>
|
||||
```
|
||||
|
||||
**4. Prune worktree metadata:**
|
||||
```bash
|
||||
git worktree prune
|
||||
```
|
||||
|
||||
**CAUTION:** Don't remove worktrees for active cross-rig work.
|
||||
Check with `gt worktree list` if available.
|
||||
|
||||
**Exit criteria:** Stale worktrees removed."""
|
||||
|
||||
[[steps]]
|
||||
id = "generate-report"
|
||||
title = "Generate sync report"
|
||||
needs = ["run-tests"]
|
||||
description = """
|
||||
Summarize sync results for broadcast response.
|
||||
|
||||
**Git status check:**
|
||||
```bash
|
||||
# Get current branch
|
||||
BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
||||
|
||||
# Check divergence from origin/main
|
||||
BEHIND=$(git rev-list HEAD..origin/main --count)
|
||||
AHEAD=$(git rev-list origin/main..HEAD --count)
|
||||
```
|
||||
|
||||
**Report format (for feature branches):**
|
||||
```
|
||||
SYNC COMPLETE: <agent-name>
|
||||
|
||||
Git:
|
||||
- Branch: <branch>
|
||||
- Commits behind main: <n>
|
||||
- Commits ahead of main: <n>
|
||||
- Conflicts resolved: <y/n, details if yes>
|
||||
|
||||
Beads:
|
||||
- Sync status: OK
|
||||
- Doctor findings: <count> (fixed: <n>, filed: <n>, deferred: <n>)
|
||||
|
||||
Build: PASS
|
||||
Tests: PASS | FAIL (<count> failures, tracked in <bead-ids>)
|
||||
|
||||
Worktrees cleaned: <count>
|
||||
|
||||
Ready for work: YES | NO (<reason>)
|
||||
```
|
||||
|
||||
**Report format (for crew on main):**
|
||||
|
||||
When the agent is on the main branch directly (common for crew workers),
|
||||
use "Unpushed commits" instead of "Commits ahead of main":
|
||||
|
||||
```
|
||||
SYNC COMPLETE: <agent-name>
|
||||
|
||||
Git:
|
||||
- Branch: main
|
||||
- Commits behind origin: <n>
|
||||
- Unpushed commits: <n>
|
||||
- Conflicts resolved: <y/n, details if yes>
|
||||
...
|
||||
```
|
||||
|
||||
This distinction is important because:
|
||||
- Crew workers often work directly on main
|
||||
- "Commits ahead of main" is confusing when you ARE on main
|
||||
- "Unpushed commits" is clearer - it means local work not yet on origin
|
||||
|
||||
**Exit criteria:** Report generated."""
|
||||
|
||||
[[steps]]
|
||||
id = "signal-ready"
|
||||
title = "Signal readiness"
|
||||
needs = ["generate-report"]
|
||||
description = """
|
||||
Report sync completion to coordinator (if broadcast) or to self (if autonomous).
|
||||
|
||||
**If responding to broadcast:**
|
||||
```bash
|
||||
gt mail send <coordinator> -s "SYNC_COMPLETE $(hostname)" -m "<report>"
|
||||
```
|
||||
|
||||
**If autonomous sync:**
|
||||
- Log the report
|
||||
- Update any relevant agent status
|
||||
|
||||
**If sync failed (couldn't build):**
|
||||
```bash
|
||||
gt mail send --human -s "SYNC_FAILED $(hostname)" -m "<report with failure details>"
|
||||
```
|
||||
|
||||
**Exit criteria:** Readiness signaled, molecule complete."""
|
||||
198
internal/formula/formulas/mol-town-shutdown.formula.toml
Normal file
198
internal/formula/formulas/mol-town-shutdown.formula.toml
Normal file
@@ -0,0 +1,198 @@
|
||||
description = """
|
||||
Full Gas Town shutdown and restart.
|
||||
|
||||
This is an idempotent shutdown - it stops Claude sessions but preserves
|
||||
polecat sandboxes and hooks. Polecats can resume their work after restart.
|
||||
|
||||
Use when you need to:
|
||||
- Reset all state for a fresh start
|
||||
- Recover from corrupted agent state
|
||||
- Prepare for maintenance or upgrades
|
||||
|
||||
Sling to Mayor when ready to reboot:
|
||||
gt sling mol-town-shutdown mayor
|
||||
"""
|
||||
formula = "mol-town-shutdown"
|
||||
type = "workflow"
|
||||
version = 2
|
||||
|
||||
[[steps]]
|
||||
id = "preflight-check"
|
||||
title = "Preflight safety check"
|
||||
description = """
|
||||
Scan for conditions that might make shutdown inadvisable.
|
||||
|
||||
```bash
|
||||
gt shutdown preflight
|
||||
```
|
||||
|
||||
This checks for:
|
||||
- **Uncommitted changes**: Polecats with dirty git status
|
||||
- **Unpushed commits**: Work that hasn't reached remote
|
||||
- **Active merges**: Refinery mid-merge (could corrupt state)
|
||||
- **Pending CI**: PRs waiting on GitHub Actions
|
||||
|
||||
Output is a report with warnings/blockers:
|
||||
|
||||
| Severity | Condition | Action |
|
||||
|----------|-----------|--------|
|
||||
| BLOCKER | Active merge in progress | Abort shutdown |
|
||||
| WARNING | Uncommitted polecat changes | List affected polecats |
|
||||
| WARNING | Unpushed commits | List repos needing push |
|
||||
| INFO | Pending CI runs | Note for awareness |
|
||||
|
||||
If blockers exist, STOP and resolve them first.
|
||||
If only warnings, decide whether to proceed (work will be preserved).
|
||||
"""
|
||||
|
||||
[[steps]]
|
||||
id = "stop-sessions"
|
||||
title = "Stop Claude sessions (preserve sandboxes)"
|
||||
needs = ["preflight-check"]
|
||||
description = """
|
||||
Kill Claude processes but leave polecat sandboxes intact.
|
||||
|
||||
```bash
|
||||
# Stop all polecat Claude sessions
|
||||
gt stop --all --preserve-sandbox
|
||||
|
||||
# Verify sandboxes still exist
|
||||
gt polecats --all --status
|
||||
```
|
||||
|
||||
What this does:
|
||||
- Kills tmux sessions running Claude
|
||||
- Leaves git clones untouched
|
||||
- Leaves hook files (pinned molecules) intact
|
||||
- Leaves uncommitted work in place
|
||||
|
||||
What this does NOT do:
|
||||
- Delete polecat directories
|
||||
- Remove hook attachments
|
||||
- Lose any git state
|
||||
|
||||
After restart, polecats can be respawned and will resume from their hooks.
|
||||
|
||||
Note: Crew workers are NOT stopped (they're user-managed).
|
||||
"""
|
||||
|
||||
[[steps]]
|
||||
id = "clear-inboxes"
|
||||
title = "Archive and clear inboxes"
|
||||
needs = ["stop-sessions"]
|
||||
description = """
|
||||
Archive and clear all agent inboxes across all rigs.
|
||||
|
||||
```bash
|
||||
# For each rig
|
||||
for rig in $(gt rigs --names); do
|
||||
gt mail clear $rig/witness --archive
|
||||
gt mail clear $rig/refinery --archive
|
||||
done
|
||||
|
||||
# Clear Mayor inbox
|
||||
gt mail clear mayor --archive
|
||||
```
|
||||
|
||||
Messages are archived to `.beads/mail-archive/` before deletion.
|
||||
Crew inboxes are NOT cleared (user manages those).
|
||||
"""
|
||||
|
||||
[[steps]]
|
||||
id = "stop-daemon"
|
||||
title = "Stop the daemon"
|
||||
needs = ["clear-inboxes"]
|
||||
description = """
|
||||
Stop the Gas Town daemon gracefully.
|
||||
|
||||
```bash
|
||||
gt daemon stop
|
||||
```
|
||||
|
||||
The daemon handles:
|
||||
- Heartbeat monitoring
|
||||
- Pending spawn triggers
|
||||
- Background coordination
|
||||
|
||||
It will be restarted in the final step.
|
||||
"""
|
||||
|
||||
[[steps]]
|
||||
id = "rotate-logs"
|
||||
title = "Rotate and archive logs"
|
||||
needs = ["stop-daemon"]
|
||||
description = """
|
||||
Rotate logs to prevent unbounded growth.
|
||||
|
||||
```bash
|
||||
# Rotate daemon logs
|
||||
gt daemon rotate-logs
|
||||
|
||||
# Clean up old session captures (but not current sandboxes)
|
||||
gt doctor --fix
|
||||
```
|
||||
|
||||
Old logs are moved to `~/gt/logs/archive/` with timestamps.
|
||||
"""
|
||||
|
||||
[[steps]]
|
||||
id = "sync-state"
|
||||
title = "Sync beads and push"
|
||||
needs = ["rotate-logs"]
|
||||
description = """
|
||||
Ensure all beads state is persisted.
|
||||
|
||||
```bash
|
||||
bd sync
|
||||
```
|
||||
|
||||
Note: We do NOT force-commit polecat work here. Their sandboxes
|
||||
are preserved with whatever state they had. They'll commit their
|
||||
own work when they resume.
|
||||
"""
|
||||
|
||||
[[steps]]
|
||||
id = "handoff-mayor"
|
||||
title = "Send Mayor handoff"
|
||||
needs = ["sync-state"]
|
||||
description = """
|
||||
Record shutdown context for the fresh Mayor session.
|
||||
|
||||
```bash
|
||||
gt mail send mayor -s "🤝 HANDOFF: Town shutdown complete" -m "
|
||||
Town shutdown completed. State preserved.
|
||||
|
||||
Polecat sandboxes: PRESERVED (will resume from hooks)
|
||||
Inboxes: ARCHIVED and cleared
|
||||
Daemon: STOPPED
|
||||
|
||||
Next steps:
|
||||
1. gt daemon start
|
||||
2. gt prime
|
||||
3. gt status (verify health)
|
||||
4. Resume operations or respawn polecats
|
||||
|
||||
Shutdown reason: {{shutdown_reason}}
|
||||
"
|
||||
```
|
||||
"""
|
||||
|
||||
[[steps]]
|
||||
id = "restart-daemon"
|
||||
title = "Restart daemon"
|
||||
needs = ["handoff-mayor"]
|
||||
description = """
|
||||
Start the daemon with fresh state.
|
||||
|
||||
```bash
|
||||
gt daemon start
|
||||
```
|
||||
|
||||
The daemon will:
|
||||
- Begin heartbeat monitoring
|
||||
- Watch for pending spawns
|
||||
- Resume background coordination
|
||||
|
||||
Polecats are NOT auto-respawned. Use `gt sling` or let Witness
|
||||
restart them based on their preserved hooks.
|
||||
"""
|
||||
62
internal/formula/formulas/mol-witness-patrol.formula.toml
Normal file
62
internal/formula/formulas/mol-witness-patrol.formula.toml
Normal file
@@ -0,0 +1,62 @@
|
||||
description = "Per-rig worker monitor patrol loop.\n\nThe Witness is the Pit Boss for your rig. You watch polecats, nudge them toward\ncompletion, verify clean git state before kills, and escalate stuck workers.\n\n**You do NOT do implementation work.** Your job is oversight, not coding.\n\n## Ephemeral Polecat Model\n\nPolecats are truly ephemeral - done at MR submission, recyclable immediately:\n\n```\nPolecat lifecycle: spawning → working → mr_submitted → nuked\nMR lifecycle: created → queued → processed → merged (Refinery handles)\n```\n\nOnce a polecat's branch is pushed (cleanup_status=clean), the polecat can be\nnuked immediately. The MR continues independently in the Refinery. If conflicts\narise, Refinery creates a NEW conflict-resolution task for a NEW polecat.\n\n**Key principle**: Polecat lifecycle is separate from MR lifecycle.\n\n## Design Philosophy\n\nThis patrol follows Gas Town principles:\n- **Discovery over tracking**: Observe reality each cycle, don't maintain state\n- **Events over state**: POLECAT_DONE mail triggers immediate cleanup\n- **Ephemeral by default**: Clean polecats are nuked immediately, no waiting\n- **Cleanup wisps for exceptions**: Only created when intervention needed\n- **Task tool for parallelism**: Subagents inspect polecats, not molecule arms\n\n## Patrol Shape (Linear, Deacon-style)\n\n```\ninbox-check ─► process-cleanups ─► check-refinery ─► survey-workers\n │\n ┌──────────────────────────────────────────────────┘\n ▼\n check-timer-gates ─► check-swarm ─► ping-deacon ─► patrol-cleanup ─► context-check ─► loop-or-exit\n```\n\nNo dynamic arms. No fanout gates. No persistent nudge counters.\nState is discovered each cycle from reality (tmux, beads, mail)."
|
||||
formula = 'mol-witness-patrol'
|
||||
version = 2
|
||||
|
||||
[[steps]]
|
||||
description = "Check inbox and handle messages.\n\n```bash\ngt mail inbox\n```\n\nFor each message:\n\n**POLECAT_STARTED**:\nA new polecat has started working. Acknowledge and archive.\n```bash\n# Acknowledge startup (optional: log for activity tracking)\ngt mail archive <message-id>\n```\nNo action needed beyond acknowledgment - archive immediately.\n\n**POLECAT_DONE / LIFECYCLE:Shutdown**:\n\n*EPHEMERAL MODEL*: Polecats are truly ephemeral - done at MR submission,\nrecyclable immediately. Once the branch is pushed (cleanup_status=clean),\nthe polecat can be nuked. The MR lifecycle continues independently in the\nRefinery. If conflicts arise, Refinery creates a NEW conflict-resolution\ntask for a NEW polecat.\n\nPolecat lifecycle: spawning → working → mr_submitted → nuked\nMR lifecycle: created → queued → processed → merged (handled by Refinery)\n\nThe handler (HandlePolecatDone) will:\n1. Check cleanup_status from agent bead\n2. If \"clean\" (branch pushed): AUTO-NUKE immediately, archive mail\n3. If dirty: Create cleanup wisp for manual intervention\n\n```bash\n# The handler does this automatically:\n# - For clean state: gt polecat nuke <name> → archive mail\n# - For dirty state: create wisp → process in next step\n```\n\nCleanup wisps are only created when something is wrong (uncommitted changes,\nunpushed commits). Most POLECAT_DONE messages result in immediate nuke.\n\n**MERGED**:\nA branch was merged successfully. This is informational in the ephemeral model\nsince the polecat was already nuked after MR submission.\n\nIf a cleanup wisp exists (dirty state), complete the cleanup:\n```bash\n# Find the cleanup wisp for this polecat\nbd list --wisp --labels=polecat:<name>,state:merge-requested --status=open\n\n# If found, proceed with full polecat nuke:\ngt polecat nuke <name>\n\n# Burn the cleanup wisp\nbd close <wisp-id>\n```\nArchive after cleanup is complete.\n\n**HELP / Blocked**:\nAssess the request. Can you help? If not, escalate to Mayor:\n```bash\ngt mail send mayor/ -s \"Escalation: <polecat> needs help\" -m \"<details>\"\n```\nArchive after handling (escalated or resolved):\n```bash\ngt mail archive <message-id>\n```\n\n**HANDOFF**:\nRead predecessor context. Continue from where they left off.\nArchive after absorbing context:\n```bash\ngt mail archive <message-id>\n```\n\n**SWARM_START**:\nMayor initiating batch polecat work. Initialize swarm tracking.\n```bash\n# Parse swarm info from mail body: {\"swarm_id\": \"batch-123\", \"beads\": [\"bd-a\", \"bd-b\"]}\nbd create --wisp --title \"swarm:<swarm_id>\" --description \"Tracking batch: <swarm_id>\" --labels swarm,swarm_id:<swarm_id>,total:<N>,completed:0,start:<timestamp>\n```\nArchive after creating swarm tracking wisp:\n```bash\ngt mail archive <message-id>\n```\n\n**Hygiene principle**: Archive messages after they're fully processed.\nKeep only: active work, unprocessed requests. Inbox should be near-empty."
|
||||
id = 'inbox-check'
|
||||
title = 'Process witness mail'
|
||||
|
||||
[[steps]]
|
||||
description = "Process cleanup wisps (exception handling for dirty polecats).\n\nIn the ephemeral model, cleanup wisps are only created when a polecat has\ndirty state (uncommitted changes, unpushed commits) that prevented immediate\nnuke. Most polecats are nuked immediately on POLECAT_DONE and never create wisps.\n\n```bash\n# Find all cleanup wisps\nbd list --wisp --labels=cleanup --status=open\n```\n\nIf no wisps, skip this step (most common case in ephemeral model).\n\nFor each cleanup wisp, investigate and resolve the dirty state:\n\n## State: pending (needs investigation)\n\n1. **Extract polecat name** from wisp title/labels\n\n2. **Diagnose the problem**:\n```bash\ncd polecats/<name>\ngit status # What's uncommitted?\ngit stash list # Any stashed work?\ngit log origin/main..HEAD # Any unpushed commits?\n```\n\n3. **Resolution options**:\n - **Uncommitted changes**: Commit and push, then nuke\n - **Stashed work**: Pop and commit, or discard if not valuable\n - **Unpushed commits**: Push to origin, then nuke\n - **All valuable work lost**: Escalate to Mayor for recovery\n\n4. **If resolvable locally**: Fix and nuke\n```bash\n# Example: push unpushed commits\ngit push origin HEAD\n\n# Then nuke\ngt polecat nuke <name>\n\n# Close the wisp\nbd close <wisp-id> --reason \"Resolved: pushed commits, nuked\"\n```\n\n5. **If needs escalation**: Send RECOVERY_NEEDED to Mayor\n```bash\ngt mail send mayor/ -s \"RECOVERY_NEEDED <rig>/<polecat>\" \\\n -m \"Cleanup Status: <status>\nBranch: <branch>\nIssue: <issue-id>\n\nCannot auto-resolve. Please advise.\"\n```\nLeave wisp open until Mayor resolves.\n\n## State: merge-requested (legacy, rare)\n\nThis state was used before the ephemeral model. If found, the polecat is\nwaiting for a MERGED signal. The inbox-check step handles these.\n\n**Parallelism**: Use Task tool subagents to process multiple cleanups concurrently.\nEach cleanup is independent - perfect for parallel execution."
|
||||
id = 'process-cleanups'
|
||||
needs = ['inbox-check']
|
||||
title = 'Process pending cleanup wisps'
|
||||
|
||||
[[steps]]
|
||||
description = "Ensure the refinery is alive and processing merge requests.\n\n```bash\n# Check if refinery session exists\ngt session status <rig>/refinery\n\n# Check for pending merge requests\nbd list --type=merge-request --status=open\n```\n\nIf MRs waiting AND refinery not running:\n```bash\ngt session start <rig>/refinery\ngt mail send <rig>/refinery -s \"PATROL: Wake up\" -m \"Merge requests in queue. Please process.\"\n```\n\nIf refinery running but queue stale (>30 min), send nudge."
|
||||
id = 'check-refinery'
|
||||
needs = ['process-cleanups']
|
||||
title = 'Ensure refinery is alive'
|
||||
|
||||
[[steps]]
|
||||
description = "Survey all polecats using agent beads (ZFC: trust what agents report).\n\n**Step 1: List polecat agent beads**\n\n```bash\nbd list --type=agent --json\n```\n\nFilter the JSON output for entries where description contains `role_type: polecat`.\nEach polecat agent bead has fields in its description:\n- `role_type: polecat`\n- `rig: <rig-name>`\n- `agent_state: running|idle|stuck|done`\n- `hook_bead: <current-work-id>`\n\n**Step 2: For each polecat, check agent_state**\n\n| agent_state | Meaning | Action |\n|-------------|---------|--------|\n| running | Actively working | Check progress (Step 3) |\n| idle | No work assigned | Auto-nuke if clean (Step 3a) |\n| stuck | Self-reported stuck | Handle stuck protocol |\n| done | Work complete | Verify cleanup triggered (see Step 4a) |\n\n**Step 3: For running polecats, assess progress**\n\nCheck the hook_bead field to see what they're working on:\n```bash\nbd show <hook_bead> # See current step/issue\n```\n\nYou can also verify they're responsive:\n```bash\ntmux capture-pane -t gt-<rig>-<name> -p | tail -20\n```\n\nLook for:\n- Recent tool activity → making progress\n- Idle at prompt → may need nudge\n- Error messages → may need help\n\n**Step 3a: For idle polecats, auto-nuke if clean**\n\nWhen agent_state=idle, the polecat has no work assigned. Check if it's safe to nuke:\n\n```bash\n# Check git status in the polecat's worktree\ncd polecats/<name>\ngit status --porcelain # Should be empty (clean)\ngit log origin/main..HEAD # Should have no unpushed commits\n```\n\n**If clean** (no uncommitted changes, no unpushed commits):\n```bash\n# Safe to nuke - no work to lose\ngt polecat nuke <name>\n```\nLog the auto-nuke for audit purposes. No escalation needed.\n\n**If dirty** (uncommitted or unpushed work):\n```bash\n# Escalate to Mayor - polecat has work that might be valuable\ngt mail send mayor/ -s \\\"IDLE_DIRTY: <polecat> has uncommitted work\\\" \\\n -m \\\"Polecat: <name>\nState: idle (no hook_bead)\nGit status: <uncommitted-files>\nUnpushed commits: <count>\n\nPlease advise: recover work or discard?\\\"\n```\n\n**Rationale**: Idle polecats with clean git state are pure overhead. They have\nno work and no state worth preserving. Nuking them immediately frees resources\nand reduces noise. Only escalate when there's actual work at risk.\n\n**Step 4: Decide action**\n\n| Observation | Action |\n|-------------|--------|\n| agent_state=running, recent activity | None |\n| agent_state=running, idle 5-15 min | Gentle nudge |\n| agent_state=running, idle 15+ min | Direct nudge with deadline |\n| agent_state=stuck | Assess and help or escalate |\n| agent_state=done | Verify cleanup triggered (see Step 4a) |\n\n**Step 4a: Handle agent_state=done**\n\nIn the ephemeral model, polecats with agent_state=done and cleanup_status=clean\nshould already be nuked by HandlePolecatDone. Finding one here indicates:\n\n1. **Stale agent bead** - polecat was nuked but bead remains\n ```bash\n # Verify polecat doesn't exist anymore\n ls polecats/<name> 2>/dev/null || echo \"Already nuked\"\n ```\n If nuked, the agent bead is stale. Clean it up or ignore.\n\n2. **Cleanup wisp exists** - polecat has dirty state needing intervention\n ```bash\n bd list --wisp --labels=polecat:<name> --status=open\n ```\n Process in process-cleanups step.\n\n3. **No wisp, polecat exists** - POLECAT_DONE mail was missed\n Try auto-nuke directly (ephemeral model):\n ```bash\n # Check cleanup_status and nuke if clean\n gt polecat nuke <name> # Will fail if dirty\n ```\n If nuke fails (dirty state), create cleanup wisp for investigation.\n\n**Step 5: Execute nudges**\n```bash\ngt nudge <rig>/polecats/<name> \"How's progress? Need help?\"\n```\n\n**Step 6: Escalate if needed**\n```bash\ngt mail send mayor/ -s \"Escalation: <polecat> stuck\" \\\n -m \"Polecat <name> reports stuck. Please intervene.\"\n```\n\n**Parallelism**: Use Task tool subagents to inspect multiple polecats concurrently.\n\n**ZFC Principle**: Trust agent_state from beads. Don't infer state from PID/tmux."
|
||||
id = 'survey-workers'
|
||||
needs = ['check-refinery']
|
||||
title = 'Inspect all active polecats'
|
||||
|
||||
[[steps]]
|
||||
description = "Check for expired timer gates and escalate as needed.\n\nTimer gates are async wait conditions with a timeout. When the timeout expires,\nthe gate should be escalated to the overseer for human intervention.\n\n**Step 1: Run timer gate check**\n```bash\nbd gate check --type=timer --escalate\n```\n\nThis command:\n1. Finds all open gate issues with await_type=timer\n2. Checks if `now > created_at + timeout`\n3. Escalates expired gates via `gt escalate` (HIGH severity)\n4. Reports summary of gate status\n\n**Step 2: Review output**\n\nIf expired gates were found and escalated:\n- The escalation creates an audit trail bead\n- Overseer will be notified via mail\n- Gate remains open until manually resolved\n\nIf no expired gates:\n- Continue patrol normally\n\n**Note**: Timer gates do NOT auto-close on expiration. They escalate.\nThis ensures human oversight of timeout conditions.\n\n**Parallelism**: This is a single command, no parallel execution needed."
|
||||
id = 'check-timer-gates'
|
||||
needs = ['survey-workers']
|
||||
title = 'Check timer gates for expiration'
|
||||
|
||||
[[steps]]
|
||||
description = "If Mayor started a batch (SWARM_START), check if all polecats have completed.\n\n**Step 1: Find active swarm tracking wisps**\n```bash\nbd list --wisp --labels=swarm --status=open\n```\nIf no active swarm, skip this step.\n\n**Step 2: Count completed polecats for this swarm**\n\nExtract from wisp labels: swarm_id, total, completed, start timestamp.\nCheck how many cleanup wisps have been closed for this swarm's polecats.\n\n**Step 3: If all complete, notify Mayor**\n```bash\ngt mail send mayor/ -s \"SWARM_COMPLETE: <swarm_id>\" -m \"All <total> polecats merged.\nDuration: <minutes> minutes\nSwarm: <swarm_id>\"\n\n# Close the swarm tracking wisp\nbd close <swarm-wisp-id> --reason \"All polecats merged\"\n```\n\nNote: Runs every patrol cycle. Notification sent exactly once when all complete."
|
||||
id = 'check-swarm-completion'
|
||||
needs = ['check-timer-gates']
|
||||
title = 'Check if active swarm is complete'
|
||||
|
||||
[[steps]]
|
||||
description = "Send WITNESS_PING to Deacon for second-order monitoring.\n\nThe Witness fleet collectively monitors Deacon health - this prevents the\n\"who watches the watchers\" problem. If Deacon dies, Witnesses detect it.\n\n**Step 1: Send ping**\n```bash\ngt mail send deacon/ -s \"WITNESS_PING <rig>\" -m \"Rig: <rig>\nTimestamp: $(date -u +%Y-%m-%dT%H:%M:%SZ)\nPatrol: <cycle-number>\"\n```\n\n**Step 2: Check Deacon health**\n```bash\n# Check Deacon agent bead for last_activity\nbd list --type=agent --json | jq '.[] | select(.description | contains(\"deacon\"))'\n```\n\nLook at the `last_activity` timestamp. If stale (>5 minutes since last update):\n- Deacon may be dead or stuck\n\n**Step 3: Escalate if needed**\n```bash\n# If Deacon appears down\ngt mail send mayor/ -s \"ALERT: Deacon appears unresponsive\" -m \"No Deacon activity for >5 minutes.\nLast seen: <timestamp>\nWitness: <rig>/witness\"\n```\n\nNote: Multiple Witnesses may send this alert. Mayor should handle deduplication."
|
||||
id = 'ping-deacon'
|
||||
needs = ['check-swarm-completion']
|
||||
title = 'Ping Deacon for health check'
|
||||
|
||||
[[steps]]
|
||||
description = "Verify inbox hygiene before ending patrol cycle.\n\n**Step 1: Check inbox state**\n```bash\ngt mail inbox\n```\n\nIn the ephemeral model, most POLECAT_DONE messages are handled immediately\n(auto-nuke) and archived. Inbox should contain ONLY:\n- Unprocessed messages (just arrived, will handle next cycle)\n- MERGED notifications (informational, archive after reading)\n\n**Step 2: Archive any stale messages**\n\nLook for messages that were processed but not archived:\n- POLECAT_STARTED older than this cycle → archive\n- POLECAT_DONE that was auto-nuked → should be archived already\n- MERGED notifications → archive after acknowledging\n- HELP/Blocked that was escalated → archive\n- SWARM_START that created tracking wisp → archive\n\n```bash\n# For each stale message found:\ngt mail archive <message-id>\n```\n\n**Step 3: Verify cleanup wisp hygiene**\n\nIn the ephemeral model, cleanup wisps should be rare (only for dirty polecats):\n```bash\nbd list --wisp --labels=cleanup --status=open\n```\n\n- state:pending → Needs investigation in process-cleanups\n- state:merge-requested → Legacy state, handle in inbox-check\n\nIf cleanup wisps are accumulating, investigate why polecats aren't clean.\n\n**Goal**: Inbox should be nearly empty. Cleanup wisps should be rare."
|
||||
id = 'patrol-cleanup'
|
||||
needs = ['ping-deacon']
|
||||
title = 'End-of-cycle inbox hygiene'
|
||||
|
||||
[[steps]]
|
||||
description = "Check own context usage.\n\nIf context is HIGH (>80%):\n- Ensure any notes are written to handoff mail\n- Prepare for session restart\n\nIf context is LOW:\n- Can continue patrolling"
|
||||
id = 'context-check'
|
||||
needs = ['patrol-cleanup']
|
||||
title = 'Check own context limit'
|
||||
|
||||
[[steps]]
|
||||
description = "End of patrol cycle decision.\n\n**If context LOW**:\n- Sleep briefly to avoid tight loop (30-60 seconds)\n- Return to inbox-check step\n- Continue patrolling\n\n**If context HIGH**:\n- Write handoff mail to self with any notable observations:\n```bash\ngt handoff -s \"Witness patrol handoff\" -m \"<observations>\"\n```\n- Exit cleanly (daemon respawns fresh Witness)\n\nThe daemon ensures Witness is always running."
|
||||
id = 'loop-or-exit'
|
||||
needs = ['context-check']
|
||||
title = 'Loop or exit for respawn'
|
||||
33
internal/formula/formulas/rule-of-five.formula.toml
Normal file
33
internal/formula/formulas/rule-of-five.formula.toml
Normal file
@@ -0,0 +1,33 @@
|
||||
description = "Jeffrey Emanuel's discovery: LLM agents produce best work through 4-5 iterative refinements. Breadth-first exploration, then editorial passes."
|
||||
formula = "rule-of-five"
|
||||
type = "expansion"
|
||||
version = 1
|
||||
|
||||
[[template]]
|
||||
description = "Initial attempt at: {target.description}. Don't aim for perfection. Get the shape right. Breadth over depth."
|
||||
id = "{target}.draft"
|
||||
title = "Draft: {target.title}"
|
||||
|
||||
[[template]]
|
||||
description = "First refinement pass. Focus: CORRECTNESS. Fix errors, bugs, mistakes. Is the logic sound?"
|
||||
id = "{target}.refine-1"
|
||||
needs = ["{target}.draft"]
|
||||
title = "Refine 1: Correctness"
|
||||
|
||||
[[template]]
|
||||
description = "Second refinement pass. Focus: CLARITY. Can someone else understand this? Simplify. Remove jargon."
|
||||
id = "{target}.refine-2"
|
||||
needs = ["{target}.refine-1"]
|
||||
title = "Refine 2: Clarity"
|
||||
|
||||
[[template]]
|
||||
description = "Third refinement pass. Focus: EDGE CASES. What could go wrong? What's missing? Handle the unusual."
|
||||
id = "{target}.refine-3"
|
||||
needs = ["{target}.refine-2"]
|
||||
title = "Refine 3: Edge Cases"
|
||||
|
||||
[[template]]
|
||||
description = "Final polish. Focus: EXCELLENCE. This is the last pass. Make it shine. Is this something you'd be proud to ship?"
|
||||
id = "{target}.refine-4"
|
||||
needs = ["{target}.refine-3"]
|
||||
title = "Refine 4: Excellence"
|
||||
38
internal/formula/formulas/security-audit.formula.toml
Normal file
38
internal/formula/formulas/security-audit.formula.toml
Normal file
@@ -0,0 +1,38 @@
|
||||
description = "Cross-cutting security concern. Applies security scanning before and after implementation steps."
|
||||
formula = "security-audit"
|
||||
type = "aspect"
|
||||
version = 1
|
||||
|
||||
[[advice]]
|
||||
target = "implement"
|
||||
[advice.around]
|
||||
|
||||
[[advice.around.after]]
|
||||
description = "Post-implementation security scan. Scan new code for vulnerabilities (SAST). Check for hardcoded secrets. Review for OWASP Top 10 issues."
|
||||
id = "{step.id}-security-postscan"
|
||||
title = "Security postscan for {step.id}"
|
||||
|
||||
[[advice.around.before]]
|
||||
description = "Pre-implementation security check. Review for secrets/credentials in scope. Check dependencies for known vulnerabilities."
|
||||
id = "{step.id}-security-prescan"
|
||||
title = "Security prescan for {step.id}"
|
||||
|
||||
[[advice]]
|
||||
target = "submit"
|
||||
[advice.around]
|
||||
|
||||
[[advice.around.after]]
|
||||
description = "Post-submission security verification. Confirm no new vulnerabilities introduced."
|
||||
id = "{step.id}-security-postscan"
|
||||
title = "Security postscan for {step.id}"
|
||||
|
||||
[[advice.around.before]]
|
||||
description = "Pre-submission security check. Final vulnerability scan before merge."
|
||||
id = "{step.id}-security-prescan"
|
||||
title = "Security prescan for {step.id}"
|
||||
|
||||
[[pointcuts]]
|
||||
glob = "implement"
|
||||
|
||||
[[pointcuts]]
|
||||
glob = "submit"
|
||||
11
internal/formula/formulas/shiny-enterprise.formula.toml
Normal file
11
internal/formula/formulas/shiny-enterprise.formula.toml
Normal file
@@ -0,0 +1,11 @@
|
||||
description = "Enterprise-grade engineering workflow. Shiny + Rule of Five expansion on implement step."
|
||||
extends = ["shiny"]
|
||||
formula = "shiny-enterprise"
|
||||
type = "workflow"
|
||||
version = 1
|
||||
|
||||
[compose]
|
||||
|
||||
[[compose.expand]]
|
||||
target = "implement"
|
||||
with = "rule-of-five"
|
||||
8
internal/formula/formulas/shiny-secure.formula.toml
Normal file
8
internal/formula/formulas/shiny-secure.formula.toml
Normal file
@@ -0,0 +1,8 @@
|
||||
description = "Shiny workflow with security audit aspect applied."
|
||||
extends = ["shiny"]
|
||||
formula = "shiny-secure"
|
||||
type = "workflow"
|
||||
version = 1
|
||||
|
||||
[compose]
|
||||
aspects = ["security-audit"]
|
||||
40
internal/formula/formulas/shiny.formula.toml
Normal file
40
internal/formula/formulas/shiny.formula.toml
Normal file
@@ -0,0 +1,40 @@
|
||||
description = "Engineer in a Box - the canonical right way. Design before you code. Review before you ship. Test before you submit."
|
||||
formula = "shiny"
|
||||
type = "workflow"
|
||||
version = 1
|
||||
|
||||
[[steps]]
|
||||
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 = "design"
|
||||
title = "Design {{feature}}"
|
||||
|
||||
[[steps]]
|
||||
description = "Write the code for {{feature}}. Follow the design. Keep it simple. Don't gold-plate."
|
||||
id = "implement"
|
||||
needs = ["design"]
|
||||
title = "Implement {{feature}}"
|
||||
|
||||
[[steps]]
|
||||
description = "Review the implementation. Check for: Does it match the design? Are there obvious bugs? Is it readable and maintainable? Are there security concerns?"
|
||||
id = "review"
|
||||
needs = ["implement"]
|
||||
title = "Review implementation"
|
||||
|
||||
[[steps]]
|
||||
description = "Write and run tests. Unit tests for new code, integration tests if needed, run the full test suite, fix any regressions."
|
||||
id = "test"
|
||||
needs = ["review"]
|
||||
title = "Test {{feature}}"
|
||||
|
||||
[[steps]]
|
||||
description = "Submit for merge. Final check: git status, git diff. Commit with clear message. Push and create PR."
|
||||
id = "submit"
|
||||
needs = ["test"]
|
||||
title = "Submit for merge"
|
||||
|
||||
[vars]
|
||||
[vars.assignee]
|
||||
description = "Who is assigned to this work"
|
||||
[vars.feature]
|
||||
description = "The feature being implemented"
|
||||
required = true
|
||||
Reference in New Issue
Block a user