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>
249 lines
6.3 KiB
Markdown
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.
|