Skip to content

feat: Plan-aware nested repo branching for multi-module workspaces#2121

Closed
sakitA wants to merge 8 commits intogithub:mainfrom
sakitA:feat/nested-repo-branching
Closed

feat: Plan-aware nested repo branching for multi-module workspaces#2121
sakitA wants to merge 8 commits intogithub:mainfrom
sakitA:feat/nested-repo-branching

Conversation

@sakitA
Copy link
Copy Markdown
Contributor

@sakitA sakitA commented Apr 8, 2026

Summary

Adds support for nested independent git repositories in multi-module workspaces - sub-components that live under the root workspace with their own .git but are not formal git submodules.

Instead of automatically branching all nested repos during feature creation, this PR implements a plan-aware, task-driven workflow where the AI agent identifies affected repos from the spec and generates targeted branch-creation tasks.

Resolves #2120

Problem

In multi-module projects where sub-components maintain independent git histories (nested under a shared root), spec-kit only operates on the root repo. Developers must manually create matching feature branches in each affected sub-component.

Workflow

Phase What Happens
Specify Creates root branch + spec only
Plan setup-plan.sh/.ps1 discovers nested repos (NESTED_REPOS in JSON). AI identifies affected repos from spec
Tasks Generates Phase 1 setup task for branching only the affected nested repos
Implement Executes the branch-creation task via git commands

Why Core, Not an Extension or Preset

We evaluated all three extensibility mechanisms before choosing a core contribution:

  • Extension: Cannot modify core script output. The key requirement is NESTED_REPOS in setup-plan.sh JSON so the AI agent receives discovery data alongside FEATURE_SPEC and BRANCH. Extensions can only add new commands (speckit.{ext-id}.{cmd}), not inject fields into existing script output. A separate discovery command would force a fragile two-script workflow.
  • Preset: Can override command templates but cannot modify shell scripts. Overriding plan.md would require maintaining a full copy of the command that drifts from upstream on every core update.
  • Core: Nested repo discovery is a platform capability that requires script-level integration (setup-plan.sh JSON output, common.sh discovery function). The template changes are minimal additions (a step, a section), not full replacements. Zero impact on single-repo projects.

Design Decisions

  • Discovery in plan phase, not specify: At specify time the spec does not exist yet, so we cannot know which repos are affected
  • AI-driven selection: The AI agent determines affected repos by analyzing the spec against discovered repos, rather than branching everything
  • Hybrid discovery: Two-tier approach explicit paths from init-options.json take priority, otherwise scan using .gitignore-based filtering (no hardcoded skip list)
  • Configurable depth: --scan-depth N / -ScanDepth N controls how deep to scan (default: 2). Also configurable via nested_repo_scan_depth in init-options.json (priority: CLI > config > default 2)
  • Gitignored parent behavior: Scanning will NOT descend into gitignored parent directories. A repo directly at a gitignored path IS discovered, but a repo beneath a gitignored parent (e.g., vendor/foo/.git when vendor/ is ignored) requires explicit nested_repos config
  • Non-blocking: Discovery failures produce warnings but do not abort the workflow
  • Backward-compatible: Single-repo projects see zero behavior change
  • No namespace pollution: Discovery helpers run in a subshell to avoid leaking function names

Discovery Modes

1. Explicit paths (init-options.json)

json { "nested_repos": ["components/core", "components/api"], "nested_repo_scan_depth": 3 }
When nested_repos is defined, paths are validated directly no filesystem scanning.
nested_repo_scan_depth sets the default scan depth (overridden by CLI --scan-depth).

2. .gitignore-based scan (fallback)

When no explicit paths are configured, the discovery function scans subdirectories up to the configured depth. Instead of a hardcoded skip list, it uses git check-ignore to skip gitignored directories. Only .git is hardcoded. Directories with their own .git are always treated as nested repos, even if gitignored in the parent.

Changes

File Change
scripts/bash/common.sh find_nested_git_repos() hybrid discovery (explicit paths or .gitignore-based scan), configurable depth, subshell isolation
scripts/bash/setup-plan.sh --scan-depth flag, reads nested_repos and nested_repo_scan_depth from init-options.json (jq or Python fallback), NESTED_REPOS in JSON output
scripts/powershell/common.ps1 Find-NestedGitRepos with -MaxDepth and -ExplicitPaths params, .gitignore-based filtering
scripts/powershell/setup-plan.ps1 -ScanDepth param (default 2, ValidateRange 1+), reads nested_repos and nested_repo_scan_depth from init-options.json, NESTED_REPOS in JSON output
templates/commands/plan.md Step 3: AI identifies affected repos from spec analysis
templates/plan-template.md Affected Nested Repositories section with table
templates/commands/tasks.md Guidance for generating branch-creation setup task
templates/tasks-template.md Phase 1 example for nested repo branching task
tests/test_nested_repos.py 20 tests covering discovery, .gitignore filtering, gitignored parent behavior, explicit paths, depth config, setup-plan output, specify isolation, init-options integration

Testing

  • 20 tests in tests/test_nested_repos.py, all passing
  • Covers: discovery functions, .gitignore-based filtering, gitignored parent directory behavior (scan vs explicit), explicit paths mode, configurable depth (CLI + init-options), setup-plan JSON output, init-options.json integration (nested_repos + nested_repo_scan_depth), verification that create-new-feature does NOT branch nested repos

AI Disclosure

This contribution was made with AI assistance (GitHub Copilot), as required by CONTRIBUTING.md.

@sakitA sakitA requested a review from mnriem as a code owner April 8, 2026 07:37
Copilot AI review requested due to automatic review settings April 8, 2026 07:37
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds coordinated feature-branch creation for workspaces that contain nested, independent git repositories (not formal submodules), so create-new-feature can create/switch the same feature branch across the root repo and detected nested repos and report results in JSON.

Changes:

  • Add nested repo discovery helpers (find_nested_git_repos / Find-NestedGitRepos) scanning up to 2 levels deep with a skip list.
  • Create/switch matching branches in detected nested repos from both Bash and PowerShell create-new-feature scripts, and emit NESTED_REPOS in JSON output.
  • Document NESTED_REPOS and add pytest coverage for Bash discovery/branching behavior.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
scripts/bash/common.sh Adds nested git repo discovery helper for Bash.
scripts/bash/create-new-feature.sh Creates matching branches in discovered nested repos and includes NESTED_REPOS in JSON output.
scripts/powershell/common.ps1 Adds nested git repo discovery helper for PowerShell.
scripts/powershell/create-new-feature.ps1 Creates matching branches in discovered nested repos and includes NESTED_REPOS in JSON output.
templates/commands/specify.md Documents the new NESTED_REPOS JSON field and behavior.
tests/test_nested_repos.py Adds tests for nested repo discovery + Bash branching + JSON output.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copilot AI review requested due to automatic review settings April 8, 2026 08:14
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 7 comments.

Comments suppressed due to low confidence (1)

templates/commands/plan.md:88

  • Outline numbering is inconsistent: there are two items numbered 5. (Stop and report, then Check for extension hooks). This can confuse agents/instructions parsing; please renumber the second one to 6. (and ensure subsequent numbering remains consistent).
   - Phase 1: Generate data-model.md, contracts/, quickstart.md
   - Phase 1: Update agent context by running the agent script
   - Re-evaluate Constitution Check post-design

5. **Stop and report**: Command ends after Phase 2 planning. Report branch, IMPL_PLAN path, generated artifacts, and affected nested repos (if any).

5. **Check for extension hooks**: After reporting, check if `.specify/extensions.yml` exists in the project root.
   - If it exists, read it and look for entries under the `hooks.after_plan` key
   - If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@sakitA sakitA changed the title feat: Support coordinated feature branching across nested independent git repos feat: Plan-aware nested repo branching for multi-module workspaces Apr 8, 2026
@mnriem
Copy link
Copy Markdown
Collaborator

mnriem commented Apr 8, 2026

Please deliver this as a community preset. See https://github.com/github/spec-kit/tree/main/presets

sakitA and others added 3 commits April 8, 2026 19:16
… git repos

Add auto-detection of nested independent git repositories (not submodules)
and create matching feature branches in each when running create-new-feature.

Changes:
- common.sh: Add find_nested_git_repos() - discovers nested repos up to 2 levels deep
- common.ps1: Add Find-NestedGitRepos - PowerShell equivalent
- create-new-feature.sh: Create feature branches in all nested repos after root
- create-new-feature.ps1: Same for PowerShell
- specify.md: Document NESTED_REPOS JSON output field
- test_nested_repos.py: 9 tests covering discovery and branch creation

Design decisions:
- Auto-detect via .git presence (no configuration required)
- Non-blocking: nested repo failures are warnings, not errors
- JSON output extended with NESTED_REPOS array [{path, status}]
- Backward-compatible: no change for single-repo workflows

Resolves github#2120
Related to github#1050

Note: This contribution was made with AI assistance (GitHub Copilot).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Enhance nested repo support with two key improvements:

1. Configurable scan depth:
   - find_nested_git_repos() / Find-NestedGitRepos now accept a depth parameter
   - Uses recursive traversal instead of hardcoded 2-level nesting
   - New --scan-depth (Bash) / -ScanDepth (PowerShell) flag
   - Reads nested_repo_scan_depth from init-options.json

2. Selective branching via --repos flag:
   - New --repos (Bash) / -Repos (PowerShell) flag accepts comma-separated paths
   - Only branches repos matching the specified paths
   - Omit flag to branch all discovered repos (backward compatible)
   - Enables spec-driven branching: AI agents select relevant repos per feature

Tests: 17 tests (8 new for depth + filtering), all passing.
Docs: Updated specify.md with --repos, --scan-depth, and init-options.json config.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Instead of automatically creating branches in all nested repos during
/speckit.specify, this redesign defers branching to the plantasksimplement
workflow:

- Specify phase: creates root branch + spec only (no nested repo work)
- Plan phase: setup-plan.sh discovers nested repos (NESTED_REPOS in JSON)
  and the AI agent identifies affected modules from the spec
- Tasks phase: generates a Phase 1 setup task for creating feature branches
  in only the affected nested repos identified by the plan
- Implement phase: executes the branch-creation task via git commands

Changes:
- Revert create-new-feature.sh/.ps1: remove --repos, --scan-depth, and
  NESTED_REPOS output (specify phase is clean again)
- Add nested repo discovery to setup-plan.sh/.ps1 with --scan-depth flag
- Update plan.md template: add step for AI to identify affected repos
- Update plan-template.md: add Affected Nested Repositories section
- Update tasks.md command: guidance for generating branch-creation task
- Update tasks-template.md: Phase 1 example for nested repo branching
- Update specify.md: remove nested repo guidance, point to plan+tasks
- Rewrite tests: 13 tests covering discovery, depth config, setup-plan
  output, and verification that specify phase doesn't branch nested repos

Resolves the concern that at specify time the spec doesn't exist yet,
so we cannot know which repos are affected. The AI agent now makes this
determination during planning based on spec analysis.

Refs: github#2120

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 9, 2026 02:18
@sakitA sakitA force-pushed the feat/nested-repo-branching branch from c87e0bb to 914606d Compare April 9, 2026 02:18
- Wrap find_nested_git_repos helpers in subshell to prevent bash
  global namespace pollution (_should_skip, _scan_dir)
- Validate --scan-depth is a positive integer in setup-plan.sh
- Remove non-existent nested_repo_scan_depth config reference from
  plan.md template
- Fix duplicate step numbering (two items numbered 5) in plan.md
- Quote repo paths in tasks.md git command template
- Update Find-NestedGitRepos comment to reflect configurable depth

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@sakitA sakitA force-pushed the feat/nested-repo-branching branch from 914606d to 749e95b Compare April 9, 2026 02:23
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@sakitA
Copy link
Copy Markdown
Contributor Author

sakitA commented Apr 9, 2026

Please deliver this as a community preset. See https://github.com/github/spec-kit/tree/main/presets

@mnriem does presets support script override? when I review it shows in future consideration, do I miss something?

sakitA and others added 2 commits April 8, 2026 19:45
Replace hardcoded skip list with two-tier discovery:

1. Explicit paths: If nested_repos is defined in init-options.json,
   validate and return those paths directly (no scanning).

2. .gitignore-based scan: If no explicit config, scan directories but
   use git check-ignore to skip gitignored dirs instead of a hardcoded
   skip list. Only .git is hardcoded. Dirs with their own .git are
   always treated as nested repos (even if gitignored in parent).

- Bash: find_nested_git_repos() accepts optional explicit paths (3rd+ args)
- PowerShell: Find-NestedGitRepos accepts -ExplicitPaths param
- setup-plan.sh/.ps1: reads nested_repos from init-options.json
- Python fallback for JSON parsing when jq is unavailable
- Windows \r line-ending handling in Python subprocess output
- 18 tests: discovery, gitignore filtering, explicit paths, depth, init-options

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- plan.md: clarify JSON parsing instructions (extract first { line)
- common.sh: validate max_depth is a positive integer
- common.ps1: add ValidateRange(1, MaxValue) on -MaxDepth
- setup-plan.ps1: add ValidateRange(0, MaxValue) on -ScanDepth

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 9, 2026 03:07
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 9 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

…ored parents

- Update function header comments in common.sh and common.ps1 to
  accurately describe gitignore-based filtering (no hardcoded skip list)
- Document that scanning will NOT descend into gitignored parent
  directories; use init-options.json nested_repos for those
- Implement nested_repo_scan_depth from init-options.json in both
  setup-plan.sh and setup-plan.ps1 (priority: CLI > config > default 2)
- Fix PowerShell -ScanDepth param: default 2, ValidateRange(1, MaxValue),
  use PSBoundParameters to detect explicit CLI usage
- Add returncode assertion before branch-name comparison in tests
- Add test: repo under gitignored parent not discovered by scan
- Add test: nested_repo_scan_depth from init-options.json

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 9, 2026 03:19
@sakitA sakitA force-pushed the feat/nested-repo-branching branch from 1e8d148 to 80bcbb4 Compare April 9, 2026 03:19
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- Fix stale comment in common.sh (remove 'common non-project directories')
- Validate nested_repo_scan_depth from init-options.json (positive integer)
- Wrap find_nested_git_repos calls with || fallback in bash (set -e safe)
- Wrap Find-NestedGitRepos calls in try/catch in PowerShell
- Discovery failures now emit warnings and fall back to empty list

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@sakitA
Copy link
Copy Markdown
Contributor Author

sakitA commented Apr 9, 2026

Closing this PR the functionality has been reimplemented as a community preset per @mnriem's feedback.

Preset repository: https://github.com/sakitA/spec-kit-preset-nested-repos
Version: v1.0.0

The preset approach is better because:

  • No core script modifications needed the AI agent handles discovery via shell commands during the plan phase
  • Templates and commands are additive overrides (all core behavior preserved)
  • Supports both independent repos AND git submodules (broader scope than this PR)
  • Configuration via init-options.json (optional, auto-detect as default)
  • Easy to install: specify preset add --from https://github.com/sakitA/spec-kit-preset-nested-repos/archive/refs/tags/v1.0.0.zip

Will submit a catalog PR to catalog.community.json shortly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Support coordinated feature branching across nested independent git repositories

3 participants