Files
gastown/docs/cross-project-deps.md
Steve Yegge 0e942c71f2 Add cross-project dependency design and update molecules doc (gt-hbg5)
New design for tracking dependencies across project boundaries:
- Capability-based: reference provides:X labels, not issue IDs
- bd ship command for publishing capabilities
- external: prefix in blocked_by for cross-project refs
- Molecule parking for blocked work

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-21 22:41:07 -08:00

249 lines
6.3 KiB
Markdown

# Cross-Project Dependencies
> Design for tracking dependencies across project boundaries without coupling to
> specific orchestrators.
## Problem Statement
When working on Gas Town, we frequently hit dependencies on Beads features (and
vice versa). Currently there's no formal mechanism to:
1. Declare "I need capability X from project Y"
2. Signal "capability X is now available"
3. Park work waiting on external dependencies
4. Resume work when dependencies are satisfied
## Design Principles
1. **Beads-native**: The core mechanism lives in Beads, not Gas Town
2. **Orchestrator-agnostic**: Works with Gas Town, other orchestrators, or none
3. **Capability-based**: Reference published capabilities, not internal issue IDs
4. **Semaphore pattern**: Producer signals, consumers wait, no coordinator required
## The Mechanism
### Provider Side: Shipping Capabilities
Projects declare and ship capabilities using labels:
```bash
# Declare intent (optional, for visibility)
bd create --title="Add --assignee to mol run" \
--add-label=export:mol-run-assignee
# ... do work, close issue ...
# Ship the capability (adds provides: label)
bd ship mol-run-assignee
```
The `bd ship` command:
- Finds issue with `export:mol-run-assignee` label
- Validates issue is closed (or `--force`)
- Adds `provides:mol-run-assignee` label
- Optionally notifies downstream (future)
**Protected namespace**: `provides:*` labels can only be added via `bd ship`.
### Consumer Side: Declaring Dependencies
Projects declare external dependencies on capabilities:
```bash
# At creation
bd create --title="Use --assignee in spawn.go" \
--blocked-by="external:beads:mol-run-assignee"
# Or later
bd update gt-xyz --blocked-by="external:beads:mol-run-assignee"
```
The `external:project:capability` syntax:
- `external:` - prefix indicating cross-project reference
- `project` - name from `external_projects` config
- `capability` - the capability name (matches `provides:X` label)
### Configuration
Projects configure paths to external projects:
```yaml
# .beads/config.yaml
external_projects:
beads: ../beads
gastown: ../gastown
# Can also use absolute paths
other: /Users/steve/projects/other
```
### Resolution
`bd ready` checks external dependencies:
```bash
bd ready
# gt-xyz: blocked by external:beads:mol-run-assignee (not provided)
# gt-abc: ready
# After beads ships the capability:
bd ready
# gt-xyz: ready
# gt-abc: ready
```
## Molecule Integration
### Parking a Molecule
When a polecat hits an external dependency mid-molecule:
```bash
# Polecat discovers external dep not satisfied
gt park --step=gt-mol.3 --waiting="beads:mol-run-assignee"
```
This command:
1. Adds `blocked_by: external:beads:mol-run-assignee` to the step
2. Clears assignee on the step and molecule root
3. Sends handoff mail to self with context
4. Shuts down the polecat
**"Parked" is a derived state**, not a new status:
- Molecule status: `in_progress`
- Molecule assignee: `null`
- Has step with unsatisfied external `blocked_by`
### Querying Parked Work
```bash
# Find parked molecules
bd list --status=in_progress --no-assignee --type=molecule
# See what's blocked and on what
bd list --has-external-block
```
### Resuming Parked Work
**Manual (launch):**
```bash
gt spawn --continue gt-mol-root
# Spawns polecat, which reads handoff mail and continues
```
**Automated (future):**
Deacon patrol checks parked molecules:
```yaml
- step: check-parked-molecules
action: |
For each molecule with status=in_progress, no assignee:
Check if external deps are satisfied
If yes: spawn polecat to resume
```
## Implementation Plan
### Phase 1: Beads Core (bd-* issues)
1. **bd ship command**: Add `provides:` label, protect namespace
2. **external: blocked_by**: Parse and store external references
3. **external_projects config**: Add to config schema
4. **bd ready resolution**: Check external deps via configured paths
### Phase 2: Gas Town Integration (gt-* issues)
1. **gt park command**: Set blocked_by, clear assignee, handoff, shutdown
2. **gt spawn --continue**: Resume parked molecule
3. **Patrol step**: Check parked molecules for unblocked
### Phase 3: Automation (future)
1. Push notifications on `bd ship`
2. Auto-resume via patrol
3. Cross-rig visibility in `gt status`
## Examples
### Full Flow: Adding --assignee to bd mol run
**In beads repo:**
```bash
# Dave creates the issue
bd create --title="Add --assignee flag to bd mol run" \
--type=feature \
--add-label=export:mol-run-assignee
# Dave implements, tests, closes
bd close bd-xyz
# Dave ships the capability
bd ship mol-run-assignee
# Output: Shipped mol-run-assignee (bd-xyz)
```
**In gastown repo:**
```bash
# Earlier: Joe created dependent issue
bd create --title="Use --assignee in spawn.go" \
--blocked-by="external:beads:mol-run-assignee"
# bd ready showed it as blocked
bd ready
# gt-abc: blocked by external:beads:mol-run-assignee
# After Dave ships:
bd ready
# gt-abc: ready
# Joe picks it up
bd update gt-abc --status=in_progress --assignee=gastown/joe
```
### Parking Mid-Molecule
```bash
# Polecat working on molecule, hits step 3
# Step 3 needs beads:mol-run-assignee which isn't shipped
gt park --step=gt-mol.3 --waiting="beads:mol-run-assignee"
# Setting blocked_by on gt-mol.3...
# Clearing assignee on gt-mol.3...
# Clearing assignee on gt-mol-root...
# Sending handoff mail...
# Polecat shutting down.
# Later, after beads ships:
gt spawn --continue gt-mol-root
# Resuming molecule gt-mol-root...
# Reading handoff context...
# Continuing from step gt-mol.3
```
## Design Decisions
### Why capability labels, not issue references?
Referencing `beads:bd-xyz` couples consumer to producer's internal tracking.
Referencing `beads:mol-run-assignee` couples to a published interface.
The producer can refactor, split, or reimplement bd-xyz without breaking consumers.
### Why "parked" as derived state?
Adding a new status creates migration burden and complicates the state machine.
Deriving from existing fields (in_progress + no assignee + blocked) is simpler.
### Why handoff via mail?
Mail already handles context preservation. Parking is just a handoff to future-self
(or future-polecat). No new mechanism needed.
### Why config-based project resolution?
Alternatives considered:
- Git remote queries (complex, requires network)
- Hardcoded paths (inflexible)
- Central registry (single point of failure)
Config is simple, explicit, and works offline.