# Multi-Repo Workspace

Most real projects are not one repository. A single logical change — "add
billing" — lands in the API, the web app, and the infra repo, and nothing in the
usual toolchain tells you those three commits are the same piece of work.

`forge workspace` makes that visible: this repo plus every sibling declared
under `repo.related`, each with its own resolved
[git provider](/features/providers) and active changes.

```sh
forge workspace                       # the whole workspace
forge workspace --change add-billing  # which repos contain this change
forge workspace --json                # machine output (absolute paths)
```

```
Workspace: 4 repo(s)

  forge       GitLab    self                          2 change(s)
  spec-kit    GitLab    ../spec-kit                   0 change(s)
  gentle-ai   GitHub    ../gentle-ai                  0 change(s)
  web         GitHub    https://github.com/acme/web   (remote)

Changes spanning multiple repos:
  add-billing   forge, spec-kit
```

Note the two providers in one workspace — a polyrepo project that straddles
GitLab and GitHub is normal, and forge resolves each sibling independently.

## Configuration

```yaml
# .forge/config.yaml
repo:
  related:
    - name: api
      path: ../api                       # local checkout, read fully
    - name: web
      url: https://github.com/acme/web   # remote-only
      # provider: github                 # optional; else detected
```

Paths are absolute or relative to this repo's root. A **remote-only** sibling
gets its provider resolved from the URL but reports no changes — forge does not
clone anything to answer this question.

## Spanning changes

`SharedChanges` — the "spanning" section — lists change names present in more
than one repo. That's the signal worth acting on: it tells you a change is not
done when its API side is merged but its web side isn't, and it's what
coordinated cross-repo delivery builds on.

`--change <name>` inverts the view:

```
Change "add-billing" spans 2 repo(s): forge, api
Change "nothere" is not present in any workspace repo
```

## Coordinated cross-repo delivery

`--workspace` on [`forge deliver`](/features/providers) and
`forge issues create` runs the command in **every workspace repo that contains
the change** — each through its own provider:

```sh
forge deliver --change add-billing --workspace --dry-run
```

```
Workspace delivery for change "add-billing" across 2 repo(s): main, api

── main (GitLab) ──
Opening merge request on GitLab (…) via glab
  [dry-run] glab mr create --title "Add billing" --description "…"

── api (GitHub) ──
Opening pull request on GitHub (…) via gh
  [dry-run] gh pr create --title "Add billing" --body "…"

delivered 2/2 repo(s)
```

One command, one logical change, **a merge request on GitLab and a pull request
on GitHub** — each body built from that repo's own proposal and tasks.

### Per-repo isolation

One repo failing does not stop the others. Its error is reported under its own
section, the remaining repos still run, and the command exits non-zero:

```
delivered 1/2 repo(s); 1 failed
Error: 1 of 2 repo(s) failed
```

That shape is deliberate: a partial delivery is a real state you need to see —
knowing *which* repo refused (and why) is more useful than aborting the batch at
the first failure. Each repo keeps its own [readiness gate](/features/providers)
and `--require-ci` check.

::: warning Local checkouts only
Delivery runs `glab`/`gh` inside each repository, so a **remote-only** sibling
(declared by `url:` with no local `path:`) cannot be delivered to — it is
skipped with a note, never counted as a failure. Clone it and point `path:` at
it to include it.
:::

## Degradation

A workspace never fails because one sibling is broken. A missing path, an
unreadable changes directory, or an entry with neither `path` nor `url` is
included with a note explaining why — carrying the resolved absolute path so the
note is actionable — and every other repo still resolves.

## See also

- [Git Providers & Related Repos](/features/providers) — how each sibling's host is resolved
- [Configuration](/guide/configuration) — the full `repo.related` reference
