docs/design/formula-resolution.md
Status: Partially implemented — Basic formula resolution works. Tier enforcement, Mol Mall integration, and HOP federation are planned.
Where formulas live, how they're found, and how they'll scale to Mol Mall
Formulas currently exist in multiple locations with no clear precedence:
internal/formula/formulas/ (source of truth, embedded in binary).beads/formulas/ (provisioned at runtime by gt install).beads/formulas/ (diverging copies)When an agent runs bd cook mol-polecat-work, which version do they get?
┌─────────────────────────────────────────────────────────────────┐
│ FORMULA RESOLUTION ORDER │
│ (most specific wins) │
└─────────────────────────────────────────────────────────────────┘
TIER 1: PROJECT (rig-level)
Location: <project>/.beads/formulas/
Source: Committed to project repo
Use case: Project-specific workflows (deploy, test, release)
Example: ~/gt/gastown/.beads/formulas/mol-gastown-release.formula.toml
TIER 2: TOWN (user-level)
Location: ~/gt/.beads/formulas/
Source: Mol Mall installs, user customizations
Use case: Cross-project workflows, personal preferences
Example: ~/gt/.beads/formulas/mol-polecat-work.formula.toml (customized)
TIER 3: SYSTEM (embedded)
Location: Compiled into gt binary
Source: internal/formula/formulas/ at build time
Use case: Defaults, blessed patterns, fallback
Example: mol-polecat-work.formula.toml (factory default)
func ResolveFormula(name string, cwd string) (Formula, Tier, error) {
// Tier 1: Project-level (walk up from cwd to find .beads/formulas/)
if projectDir := findProjectRoot(cwd); projectDir != "" {
path := filepath.Join(projectDir, ".beads", "formulas", name+".formula.toml")
if f, err := loadFormula(path); err == nil {
return f, TierProject, nil
}
}
// Tier 2: Town-level
townDir := getTownRoot() // ~/gt or $GT_HOME
path := filepath.Join(townDir, ".beads", "formulas", name+".formula.toml")
if f, err := loadFormula(path); err == nil {
return f, TierTown, nil
}
// Tier 3: Embedded (system)
if f, err := loadEmbeddedFormula(name); err == nil {
return f, TierSystem, nil
}
return nil, 0, ErrFormulaNotFound
}
Project wins because:
Town is middle because:
System is fallback because:
formula = "mol-polecat-work"
version = 4
description = "..."
[formula]
name = "mol-polecat-work"
version = "4.0.0" # Semver
author = "[email protected]" # Author identity
license = "MIT"
repository = "https://github.com/steveyegge/gastown"
[formula.registry]
uri = "hop://molmall.gastown.io/formulas/[email protected]"
checksum = "sha256:abc123..." # Integrity verification
signed_by = "[email protected]" # Optional signing
[formula.capabilities]
# What capabilities does this formula exercise? Used for agent routing.
primary = ["go", "testing", "code-review"]
secondary = ["git", "ci-cd"]
When multiple versions exist:
bd cook mol-polecat-work # Resolves per tier order
bd cook mol-polecat-work@4 # Specific major version
bd cook [email protected] # Exact version
bd cook mol-polecat-work@latest # Explicit latest
Crew directories (gastown/crew/max/) are git worktrees of the rigged repo. They have:
.beads/formulas/ (from the worktree)mayor/rig/.beads/formulas/Crew should NOT have their own formula copies. Options:
Option A: Symlink/Redirect
# crew/max/.beads/formulas -> ../../mayor/rig/.beads/formulas
All crew share the rig's formulas.
Option B: Provision on Demand
Crew directories don't have .beads/formulas/. Resolution falls through to:
Option C: Gitignore Exclusion
Exclude .beads/formulas/ from crew worktrees via .gitignore.
Recommendation: Option B - Crew shouldn't need project-level formulas. They work on the project, they don't define its workflows.
bd formula list # Available formulas (should show tier)
bd formula show <name> # Formula details
bd cook <formula> # Formula → Proto
# List with tier information
bd formula list
mol-polecat-work v4 [project]
mol-polecat-code-review v1 [town]
mol-witness-patrol v2 [system]
# Show resolution path
bd formula show mol-polecat-work --resolve
Resolving: mol-polecat-work
✓ Found at: ~/gt/gastown/.beads/formulas/mol-polecat-work.formula.toml
Tier: project
Version: 4
Resolution path checked:
1. [project] ~/gt/gastown/.beads/formulas/ ← FOUND
2. [town] ~/gt/.beads/formulas/
3. [system] <embedded>
# Override tier for testing
bd cook mol-polecat-work --tier=system # Force embedded version
bd cook mol-polecat-work --tier=town # Force town version
# Install from Mol Mall
gt formula install mol-code-review-strict
gt formula install [email protected]
gt formula install hop://acme.corp/formulas/mol-deploy
# Manage installed formulas
gt formula list --installed # What's in town-level
gt formula upgrade mol-polecat-work # Update to latest
gt formula pin [email protected] # Lock version
gt formula uninstall mol-code-review-strict
bd cook--resolve flag to show resolution pathbd formula list to show tiers~/gt/.beads/formulas/ as town formula locationgt formula commands for managing town formulas.installed.json)gt formula install from remote