A multi-repo product
Some products are several repos — an API and a web client — but one product. sdd handles a feature that spans them: one spec and plan grounded across the parts, and a build that fans out a PR per repo.
The layout
Keep the parts as sibling checkouts under one folder — that folder is the product root:
acme/ ← run `sdd` from here
├── .sdd/project.json ← the parts registry (written by `sdd product init`)
├── PRODUCT.md ← the shared north star
├── api/ ← part: its own git repo, its own CLAUDE.md
├── web/ ← part
└── legacy-viewer/ ← reference part: context only, never edited
1 Scaffold the registry
cd ~/code/acme
sdd product init --name acme --reference legacy-viewer
Scans the child git repos and writes .sdd/project.json — each part
owned (sdd builds features here) or reference
(context/contracts only) — plus a PRODUCT.md scaffold to fill in:
{ "product": "acme",
"parts": [
{ "name": "api", "path": "./api", "role": "owned",
"repo": "acme/api", "contract": "openapi.json" },
{ "name": "web", "path": "./web", "role": "owned", "repo": "acme/web" },
{ "name": "legacy-viewer", "path": "./legacy-viewer", "role": "reference" }
] }
Add a contract to a part — like the API's openapi.json — and the
other parts will build against it.
2 Spec — grounded across the parts
sdd spec "add CSV export of the items list"
The spec is grounded in the shared PRODUCT.md plus each part's own
CLAUDE.md and contract, and the agent proposes a
## Touchpoints section — which parts this feature changes — for you to
confirm:
## Touchpoints
- api
- web
The acceptance criteria come out cross-repo-aware: the API adds
GET /items/export and updates openapi.json; the web
client regenerates its API client from the updated contract and puts the button
behind a feature flag — each side following its own conventions. (The reference part is
correctly left out.)
3 Plan — per-part task groups
sdd plan csv-export-items
Scopes its grounding to the touched parts and decomposes into tasks tagged by part, ordered so a contract provider lands before its consumer:
### 1. [api] Add CSV serialization helper
### 2. [api] Add GET /items/export endpoint
### 3. [api] Document /items/export in openapi.json
### 4. [web] Regenerate API client from openapi.json
### 5. [web] Add "Export CSV" button, gated by a flag
4 Build — a PR per repo
On the board runner (see the board example), the build stage
fans out: it groups the [part] tasks and builds each part in
its own checkout, on an sdd/<slug> branch, opening a PR in that part's
repo — then comments a summary on the feature ticket:
## sdd build — fan-out
2/2 part(s) opened a PR.
- api → PR #16 (tests green) https://github.com/acme/api/pull/16
- web → PR #1 (tests green) https://github.com/acme/web/pull/1
A part with no changes, failing tests, or no checkout opens no PR and is left for another pass. Merge stays yours — review and land the per-part PRs, contract provider first.
The board across repos
With a product registry, sdd board provisions gate labels in every owned
part's repo and one org-level GitHub Project that aggregates all the
parts' issues and PRs — so a feature spanning three repos is still one thing you watch, on
one board.
Single-repo projects are completely unaffected by any of this: no
project.json, no change in behaviour.