From 34e456f05e489e4c4d7fb777c7ac0ede04580d41 Mon Sep 17 00:00:00 2001 From: Pascal Date: Mon, 22 Jun 2026 06:45:30 +0200 Subject: [PATCH 1/4] docs: add monorepo guide Adds docs/guides/monorepo.md covering per-project .specify/, targeting a member project from the repo root with SPECIFY_INIT_DIR, agent env propagation, the git extension scoping limitation (#3081), and per-project constitutions. Wires it into docs/toc.yml under Development. --- docs/guides/monorepo.md | 104 ++++++++++++++++++++++++++++++++++++++++ docs/toc.yml | 2 + 2 files changed, 106 insertions(+) create mode 100644 docs/guides/monorepo.md diff --git a/docs/guides/monorepo.md b/docs/guides/monorepo.md new file mode 100644 index 0000000000..355d6c9576 --- /dev/null +++ b/docs/guides/monorepo.md @@ -0,0 +1,104 @@ +# Using Spec Kit in a Monorepo + +A Spec Kit project is **directory-scoped**: the project is whichever directory +contains `.specify/`. A monorepo can hold several independent Spec Kit projects +under one repository root, each with its own `.specify/`, `specs/`, constitution, +and feature numbering. + +Root resolution already prefers the **nearest** `.specify/` over the Git +toplevel, so commands run from inside a member project resolve to that project, +not the repo root. + +## Layout + +```text +my-monorepo/ +├── .git/ # one Git repository at the root +├── apps/ +│ ├── web/ +│ │ └── .specify/ # Spec Kit project "web" +│ │ └── memory/constitution.md +│ └── api/ +│ └── .specify/ # Spec Kit project "api" +│ └── memory/constitution.md +└── packages/ + └── ui/ + └── .specify/ # Spec Kit project "ui" +``` + +Initialize each member project independently: + +```bash +specify init apps/web --integration claude +specify init apps/api --integration claude +``` + +Each project keeps its own `specs/` directory and numbers features +independently (`apps/web/specs/001-…`, `apps/api/specs/001-…`). + +## Working inside a member project + +The default workflow is unchanged: change into the project directory and run the +slash commands. Root resolution finds the nearest `.specify/`. + +```bash +cd apps/web +# then run /speckit.specify, /speckit.plan, … in your agent +``` + +## Targeting a member project from the repo root + +For non-interactive or CI runs where you do not want to `cd`, set +**`SPECIFY_INIT_DIR`** to the member project root (the directory *containing* +`.specify/`). Relative paths resolve against the current directory. + +```bash +# from the monorepo root, operate on apps/web without cd-ing in +SPECIFY_INIT_DIR=apps/web # exported in the shell your agent runs commands in +``` + +The path must exist and contain `.specify/`. If it does not, the command +**errors and does not fall back** to the current directory or the Git toplevel — +this is deliberate, so a typo never writes specs into the wrong project: + +```text +ERROR: SPECIFY_INIT_DIR does not point to an existing directory: apps/wbe +ERROR: SPECIFY_INIT_DIR is not a Spec Kit project (no .specify/ directory): apps/web +``` + +`SPECIFY_INIT_DIR` selects the **project**; `SPECIFY_FEATURE_DIRECTORY` selects +the **feature** within it. They compose — set both to pick a project and a +feature non-interactively. See the +[`SPECIFY_INIT_DIR` reference](../reference/core.md#environment-variables) for +the full contract and the two-axes model. + +## How `SPECIFY_INIT_DIR` reaches your agent + +`SPECIFY_INIT_DIR` is read by the shell scripts that the slash commands invoke +(`get_repo_root` in Bash, `Get-RepoRoot` in PowerShell). It takes effect only +when it is present in the environment of the shell that runs those scripts. + +- **Scripted / CI runs:** export it in the same shell that drives the commands — + reliable. +- **Interactive agents:** whether an exported variable reaches the shell tool an + agent uses is agent-specific. Export `SPECIFY_INIT_DIR` *before* launching the + agent, and verify once (e.g. run `/speckit.specify` and confirm the new feature + landed under the intended project's `specs/`). + +## Git in a monorepo + +> [!NOTE] +> The Git extension scopes branch creation to the **resolved project root**. In a +> monorepo with a single Git repository at the root and projects in +> subdirectories, a member project directory (e.g. `apps/web`) is not itself a +> Git work tree, so feature-branch creation is skipped with a +> *"Git repository not detected"* notice. Manage branches and commits at the +> repository root, or initialize Git per member project if you want per-project +> branches. + +## Constitutions + +Each member project has its own `.specify/memory/constitution.md` and +`/speckit.constitution` edits the local project's file. There is no base/inherit +mechanism today — shared engineering rules must be duplicated per project or kept +out of the constitution. diff --git a/docs/toc.yml b/docs/toc.yml index aba93ab432..711abb3375 100644 --- a/docs/toc.yml +++ b/docs/toc.yml @@ -53,6 +53,8 @@ href: local-development.md - name: Evolving Specs href: guides/evolving-specs.md + - name: Monorepos + href: guides/monorepo.md # Community - name: Community From 43adf237e3a18ab69bf9977a30a9c54a5259b914 Mon Sep 17 00:00:00 2001 From: Pascal Date: Mon, 22 Jun 2026 07:24:36 +0200 Subject: [PATCH 2/4] docs: correct monorepo Git guidance --- docs/guides/monorepo.md | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/docs/guides/monorepo.md b/docs/guides/monorepo.md index 355d6c9576..4c8461b72c 100644 --- a/docs/guides/monorepo.md +++ b/docs/guides/monorepo.md @@ -54,7 +54,7 @@ For non-interactive or CI runs where you do not want to `cd`, set ```bash # from the monorepo root, operate on apps/web without cd-ing in -SPECIFY_INIT_DIR=apps/web # exported in the shell your agent runs commands in +export SPECIFY_INIT_DIR=apps/web ``` The path must exist and contain `.specify/`. If it does not, the command @@ -88,13 +88,16 @@ when it is present in the environment of the shell that runs those scripts. ## Git in a monorepo > [!NOTE] -> The Git extension scopes branch creation to the **resolved project root**. In a -> monorepo with a single Git repository at the root and projects in -> subdirectories, a member project directory (e.g. `apps/web`) is not itself a -> Git work tree, so feature-branch creation is skipped with a -> *"Git repository not detected"* notice. Manage branches and commits at the -> repository root, or initialize Git per member project if you want per-project -> branches. +> Spec Kit project files are scoped to the **resolved project root**, but Git +> operations still run in the containing Git work tree. In a monorepo with a +> single Git repository at the root and projects in subdirectories, feature +> branch creation creates or switches branches in the shared root repository. +> Spec directories still live under the selected member project, while the Git +> branch namespace is shared by the whole monorepo. Manage branches and commits +> at the repository root, or initialize Git per member project if you want +> isolated per-project branch namespaces. Tighter Git scoping for monorepos is a +> known limitation tracked in +> [#3081](https://github.com/github/spec-kit/issues/3081). ## Constitutions From 516878d8e5963f54a6ee79bec9f63660d9115f5e Mon Sep 17 00:00:00 2001 From: Pascal Date: Mon, 22 Jun 2026 19:47:27 +0200 Subject: [PATCH 3/4] docs: drop open-issue reference and polish monorepo guide prose --- docs/guides/monorepo.md | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/docs/guides/monorepo.md b/docs/guides/monorepo.md index 4c8461b72c..4699f4137f 100644 --- a/docs/guides/monorepo.md +++ b/docs/guides/monorepo.md @@ -58,8 +58,8 @@ export SPECIFY_INIT_DIR=apps/web ``` The path must exist and contain `.specify/`. If it does not, the command -**errors and does not fall back** to the current directory or the Git toplevel — -this is deliberate, so a typo never writes specs into the wrong project: +**errors and does not fall back** to the current directory or the Git toplevel. +This is deliberate: a typo never writes specs into the wrong project: ```text ERROR: SPECIFY_INIT_DIR does not point to an existing directory: apps/wbe @@ -67,7 +67,7 @@ ERROR: SPECIFY_INIT_DIR is not a Spec Kit project (no .specify/ directory): apps ``` `SPECIFY_INIT_DIR` selects the **project**; `SPECIFY_FEATURE_DIRECTORY` selects -the **feature** within it. They compose — set both to pick a project and a +the **feature** within it. They compose: set both to pick a project and a feature non-interactively. See the [`SPECIFY_INIT_DIR` reference](../reference/core.md#environment-variables) for the full contract and the two-axes model. @@ -78,8 +78,8 @@ the full contract and the two-axes model. (`get_repo_root` in Bash, `Get-RepoRoot` in PowerShell). It takes effect only when it is present in the environment of the shell that runs those scripts. -- **Scripted / CI runs:** export it in the same shell that drives the commands — - reliable. +- **Scripted / CI runs:** export it in the same shell that drives the commands; + it is reliable there. - **Interactive agents:** whether an exported variable reaches the shell tool an agent uses is agent-specific. Export `SPECIFY_INIT_DIR` *before* launching the agent, and verify once (e.g. run `/speckit.specify` and confirm the new feature @@ -95,13 +95,11 @@ when it is present in the environment of the shell that runs those scripts. > Spec directories still live under the selected member project, while the Git > branch namespace is shared by the whole monorepo. Manage branches and commits > at the repository root, or initialize Git per member project if you want -> isolated per-project branch namespaces. Tighter Git scoping for monorepos is a -> known limitation tracked in -> [#3081](https://github.com/github/spec-kit/issues/3081). +> isolated per-project branch namespaces. ## Constitutions Each member project has its own `.specify/memory/constitution.md` and `/speckit.constitution` edits the local project's file. There is no base/inherit -mechanism today — shared engineering rules must be duplicated per project or kept +mechanism today, so shared engineering rules must be duplicated per project or kept out of the constitution. From 211af06b30dda21ae18c4cad5b4cfa92981f49ff Mon Sep 17 00:00:00 2001 From: Pascal Date: Tue, 23 Jun 2026 06:26:37 +0200 Subject: [PATCH 4/4] docs: fix SPECIFY_INIT_DIR error example (absolute path, non-project dir) --- docs/guides/monorepo.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/guides/monorepo.md b/docs/guides/monorepo.md index 4699f4137f..507d2dc3fe 100644 --- a/docs/guides/monorepo.md +++ b/docs/guides/monorepo.md @@ -59,11 +59,16 @@ export SPECIFY_INIT_DIR=apps/web The path must exist and contain `.specify/`. If it does not, the command **errors and does not fall back** to the current directory or the Git toplevel. -This is deliberate: a typo never writes specs into the wrong project: +This is deliberate: a typo never writes specs into the wrong project. A +nonexistent path is reported as you typed it; a path that exists but is not a +Spec Kit project is reported as its resolved absolute path: ```text +# SPECIFY_INIT_DIR=apps/wbe (typo: no such directory) ERROR: SPECIFY_INIT_DIR does not point to an existing directory: apps/wbe -ERROR: SPECIFY_INIT_DIR is not a Spec Kit project (no .specify/ directory): apps/web + +# SPECIFY_INIT_DIR=apps (exists, but has no .specify/ of its own) +ERROR: SPECIFY_INIT_DIR is not a Spec Kit project (no .specify/ directory): /home/you/my-monorepo/apps ``` `SPECIFY_INIT_DIR` selects the **project**; `SPECIFY_FEATURE_DIRECTORY` selects