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>
This commit is contained in:
248
docs/cross-project-deps.md
Normal file
248
docs/cross-project-deps.md
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
# 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.
|
||||||
@@ -621,6 +621,45 @@ Codebase improves overnight
|
|||||||
Repeat weekly
|
Repeat weekly
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Cross-Project Dependencies and Parking
|
||||||
|
|
||||||
|
Molecules can hit external dependencies that block progress. See
|
||||||
|
[cross-project-deps.md](cross-project-deps.md) for the full design.
|
||||||
|
|
||||||
|
### Parking a Molecule
|
||||||
|
|
||||||
|
When a molecule step depends on a capability from another project:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Polecat discovers external dependency not satisfied
|
||||||
|
gt park --step=gt-mol.3 --waiting="beads:mol-run-assignee"
|
||||||
|
```
|
||||||
|
|
||||||
|
The molecule enters a **parked** state (derived from: in_progress + no assignee + blocked step).
|
||||||
|
|
||||||
|
### Resuming a Parked Molecule
|
||||||
|
|
||||||
|
When the external dependency is satisfied:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Manual resume
|
||||||
|
gt spawn --continue gt-mol-root
|
||||||
|
|
||||||
|
# Or automated via Deacon patrol (future)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Parked State
|
||||||
|
|
||||||
|
"Parked" is not a new status - it's derived from existing fields:
|
||||||
|
- Molecule status: `in_progress`
|
||||||
|
- Molecule assignee: `null` (no polecat owns it)
|
||||||
|
- Has step with unsatisfied `blocked_by: external:...`
|
||||||
|
|
||||||
|
Query parked molecules:
|
||||||
|
```bash
|
||||||
|
bd list --status=in_progress --no-assignee --type=molecule
|
||||||
|
```
|
||||||
|
|
||||||
## Future Extensions
|
## Future Extensions
|
||||||
|
|
||||||
### Custom Molecule Types
|
### Custom Molecule Types
|
||||||
|
|||||||
Reference in New Issue
Block a user