From 60720ea263ccac18665999cb8cf6740d77fe6029 Mon Sep 17 00:00:00 2001 From: jawwad-ali Date: Tue, 28 Jul 2026 16:31:39 +0500 Subject: [PATCH 1/3] docs(core): SPECIFY_FEATURE sets the feature label, not the feature directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit docs/reference/core.md told users to set SPECIFY_FEATURE "to the feature directory name ... to work on a specific feature when not using Git branches". That does not work: SPECIFY_FEATURE only feeds get_current_branch / Get-CurrentBranch (the feature *label*). The directory comes from SPECIFY_FEATURE_DIRECTORY or .specify/feature.json. Verified on main with the real helper -- with ONLY SPECIFY_FEATURE set: $ SPECIFY_FEATURE=001-photo-albums ... get_feature_paths ERROR: Feature directory not found. Set SPECIFY_FEATURE_DIRECTORY or run the specify command to create .specify/feature.json. exit=1 $ SPECIFY_FEATURE_DIRECTORY=specs/001-photo-albums ... get_feature_paths FEATURE_DIR -> CURRENT_BRANCH -> 001-photo-albums The code's own error message points at the other variable, and the doc's own "Two resolution axes" note directly below already says the feature is selected by SPECIFY_FEATURE_DIRECTORY / .specify/feature.json -- so the table row contradicted both the code and the paragraph under it. Describe what the variable actually does, note that /speckit.specify and the Git extension normally set it, and point at the directory axis. Docs only. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 5 (1M context) --- docs/reference/core.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/core.md b/docs/reference/core.md index fad62fc36b..4311e06182 100644 --- a/docs/reference/core.md +++ b/docs/reference/core.md @@ -52,7 +52,7 @@ specify init my-project --integration copilot --preset compliance | ----------------- | ------------------------------------------------------------------------ | | `SPECIFY_INIT_DIR` | Target a member project from outside its directory (e.g. a monorepo root) without `cd`, for non-interactive / CI use. Set it to the **project root** — the directory *containing* `.specify/` (relative paths resolve against the current directory). The path must exist and contain `.specify/`, otherwise the command errors and does **not** fall back to the current directory. Resolved once in the core root helper (`get_repo_root` in Bash, `Get-RepoRoot` in PowerShell), so it is honored by the core feature scripts (`/speckit.plan`, `/speckit.tasks`, …) and the Git extension's feature-branch creation, which inherit it. The `specify` CLI applies the **same** validation rules to every project-scoped subcommand (`specify integration …`, `specify extension …`, `specify workflow …`, `specify preset …`, and the rest that operate on a `.specify/` project), so those can target a member project too. When unset, Bash/PowerShell helpers keep their existing upward search; the `specify` CLI keeps its project-scoped resolver cwd-only unless a command explicitly defines broader detection (for example, bundle commands). | | `SPECIFY_FEATURE_DIRECTORY` | Override the active feature directory *within* the resolved project (takes precedence over `.specify/feature.json`). Relative paths resolve under the project root. Combine with `SPECIFY_INIT_DIR` to pick both the project and the feature non-interactively. | -| `SPECIFY_FEATURE` | Override feature detection for non-Git repositories. Set to the feature directory name (e.g., `001-photo-albums`) to work on a specific feature when not using Git branches. Must be set in the context of the agent prior to using `/speckit.plan` or follow-up commands. | +| `SPECIFY_FEATURE` | Set the active feature **label** (e.g. `001-photo-albums`) — the value the core helpers report as the current feature/branch (`get_current_branch` in Bash, `Get-CurrentBranch` in PowerShell) when there is no Git branch context. Normally set for you by `/speckit.specify` (feature creation) and by the Git extension. It does **not** locate the feature directory on its own: with only `SPECIFY_FEATURE` set, `get_feature_paths` still fails with *"Feature directory not found. Set `SPECIFY_FEATURE_DIRECTORY` or run the specify command to create `.specify/feature.json`."* Use `SPECIFY_FEATURE_DIRECTORY` (above) or `.specify/feature.json` to select the directory. | > **Two resolution axes.** `SPECIFY_INIT_DIR` selects the **project** (which directory contains `.specify/`); `SPECIFY_FEATURE_DIRECTORY` / `.specify/feature.json` select the **feature** within that project. They are independent — project first, then feature. From 4617184b46548bbf75ade4a35324145ef4586459 Mon Sep 17 00:00:00 2001 From: jawwad-ali Date: Thu, 30 Jul 2026 20:47:01 +0500 Subject: [PATCH 2/3] docs(core): describe SPECIFY_FEATURE as an explicit label override MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The row still misstated when and how the label is applied: * "when there is no Git branch context" — get_current_branch and Get-CurrentBranch never inspect Git at all. They return the variable verbatim when set, and otherwise fall back to the basename of the resolved feature directory. * "Normally set for you by /speckit.specify" — specify.md persists feature_directory to .specify/feature.json and never sets this variable. * The Bash and Python feature scripts can only *print* a commented export hint, because a child process cannot change its parent's environment. The PowerShell scripts do assign $env:SPECIFY_FEATURE, but only reach the caller when run inside the current session. Rewrite it as an explicit user-set label override, and distinguish the printed persistence hint from actually setting the caller's environment. Co-Authored-By: Claude Opus 5 (1M context) --- docs/reference/core.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/core.md b/docs/reference/core.md index 4311e06182..5a52f3fb7b 100644 --- a/docs/reference/core.md +++ b/docs/reference/core.md @@ -52,7 +52,7 @@ specify init my-project --integration copilot --preset compliance | ----------------- | ------------------------------------------------------------------------ | | `SPECIFY_INIT_DIR` | Target a member project from outside its directory (e.g. a monorepo root) without `cd`, for non-interactive / CI use. Set it to the **project root** — the directory *containing* `.specify/` (relative paths resolve against the current directory). The path must exist and contain `.specify/`, otherwise the command errors and does **not** fall back to the current directory. Resolved once in the core root helper (`get_repo_root` in Bash, `Get-RepoRoot` in PowerShell), so it is honored by the core feature scripts (`/speckit.plan`, `/speckit.tasks`, …) and the Git extension's feature-branch creation, which inherit it. The `specify` CLI applies the **same** validation rules to every project-scoped subcommand (`specify integration …`, `specify extension …`, `specify workflow …`, `specify preset …`, and the rest that operate on a `.specify/` project), so those can target a member project too. When unset, Bash/PowerShell helpers keep their existing upward search; the `specify` CLI keeps its project-scoped resolver cwd-only unless a command explicitly defines broader detection (for example, bundle commands). | | `SPECIFY_FEATURE_DIRECTORY` | Override the active feature directory *within* the resolved project (takes precedence over `.specify/feature.json`). Relative paths resolve under the project root. Combine with `SPECIFY_INIT_DIR` to pick both the project and the feature non-interactively. | -| `SPECIFY_FEATURE` | Set the active feature **label** (e.g. `001-photo-albums`) — the value the core helpers report as the current feature/branch (`get_current_branch` in Bash, `Get-CurrentBranch` in PowerShell) when there is no Git branch context. Normally set for you by `/speckit.specify` (feature creation) and by the Git extension. It does **not** locate the feature directory on its own: with only `SPECIFY_FEATURE` set, `get_feature_paths` still fails with *"Feature directory not found. Set `SPECIFY_FEATURE_DIRECTORY` or run the specify command to create `.specify/feature.json`."* Use `SPECIFY_FEATURE_DIRECTORY` (above) or `.specify/feature.json` to select the directory. | +| `SPECIFY_FEATURE` | Explicitly override the active feature **label** (e.g. `001-photo-albums`) — the identifier the core helpers report as the current feature/branch (`get_current_branch` in Bash, `Get-CurrentBranch` in PowerShell). Those helpers never inspect Git: when the variable is set they return it verbatim, and when it is unset the label falls back to the basename of the resolved feature directory. You set it yourself: the Bash and Python feature scripts only **print** a commented `export SPECIFY_FEATURE=…` / `$env:SPECIFY_FEATURE = …` hint for you to run, because a child process cannot change its parent's environment, and `/speckit.specify` persists `feature_directory` to `.specify/feature.json` instead of setting this variable. (The PowerShell feature scripts do assign `$env:SPECIFY_FEATURE`, but that only reaches you when the script runs inside your current PowerShell session.) It does **not** locate the feature directory: with only `SPECIFY_FEATURE` set, `get_feature_paths` fails with *"Feature directory not found. Set `SPECIFY_FEATURE_DIRECTORY` or run the specify command to create `.specify/feature.json`."* Use `SPECIFY_FEATURE_DIRECTORY` (above) or `.specify/feature.json` to select the directory. | > **Two resolution axes.** `SPECIFY_INIT_DIR` selects the **project** (which directory contains `.specify/`); `SPECIFY_FEATURE_DIRECTORY` / `.specify/feature.json` select the **feature** within that project. They are independent — project first, then feature. From b8a446d67e31566393266eae6484b4a94bd338f6 Mon Sep 17 00:00:00 2001 From: jawwad-ali Date: Fri, 31 Jul 2026 13:26:49 +0500 Subject: [PATCH 3/3] docs(core): attribute the label fallback to get_feature_paths, not get_current_branch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The row said the label "falls back to the basename of the resolved feature directory" when SPECIFY_FEATURE is unset, and attributed that to get_current_branch / Get-CurrentBranch. Those helpers return an EMPTY string when the variable is unset — scripts/bash/common.sh:87 says so outright ("Return empty to signal 'unknown'") and scripts/python/common.py is `return os.environ.get("SPECIFY_FEATURE", "")`. The basename substitution happens later, in get_feature_paths / Get-FeaturePaths, after the feature directory has been resolved (scripts/python/common.py:168-169). Measured: get_current_branch (unset) -> [] get_current_branch (set) -> [my-label] get_feature_paths CURRENT_BRANCH -> [001-photo-albums] So a caller invoking the named helpers directly does not get the fallback. Distinguish the two behaviours. Co-Authored-By: Claude Opus 5 (1M context) --- docs/reference/core.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/core.md b/docs/reference/core.md index 5a52f3fb7b..585cefdde3 100644 --- a/docs/reference/core.md +++ b/docs/reference/core.md @@ -52,7 +52,7 @@ specify init my-project --integration copilot --preset compliance | ----------------- | ------------------------------------------------------------------------ | | `SPECIFY_INIT_DIR` | Target a member project from outside its directory (e.g. a monorepo root) without `cd`, for non-interactive / CI use. Set it to the **project root** — the directory *containing* `.specify/` (relative paths resolve against the current directory). The path must exist and contain `.specify/`, otherwise the command errors and does **not** fall back to the current directory. Resolved once in the core root helper (`get_repo_root` in Bash, `Get-RepoRoot` in PowerShell), so it is honored by the core feature scripts (`/speckit.plan`, `/speckit.tasks`, …) and the Git extension's feature-branch creation, which inherit it. The `specify` CLI applies the **same** validation rules to every project-scoped subcommand (`specify integration …`, `specify extension …`, `specify workflow …`, `specify preset …`, and the rest that operate on a `.specify/` project), so those can target a member project too. When unset, Bash/PowerShell helpers keep their existing upward search; the `specify` CLI keeps its project-scoped resolver cwd-only unless a command explicitly defines broader detection (for example, bundle commands). | | `SPECIFY_FEATURE_DIRECTORY` | Override the active feature directory *within* the resolved project (takes precedence over `.specify/feature.json`). Relative paths resolve under the project root. Combine with `SPECIFY_INIT_DIR` to pick both the project and the feature non-interactively. | -| `SPECIFY_FEATURE` | Explicitly override the active feature **label** (e.g. `001-photo-albums`) — the identifier the core helpers report as the current feature/branch (`get_current_branch` in Bash, `Get-CurrentBranch` in PowerShell). Those helpers never inspect Git: when the variable is set they return it verbatim, and when it is unset the label falls back to the basename of the resolved feature directory. You set it yourself: the Bash and Python feature scripts only **print** a commented `export SPECIFY_FEATURE=…` / `$env:SPECIFY_FEATURE = …` hint for you to run, because a child process cannot change its parent's environment, and `/speckit.specify` persists `feature_directory` to `.specify/feature.json` instead of setting this variable. (The PowerShell feature scripts do assign `$env:SPECIFY_FEATURE`, but that only reaches you when the script runs inside your current PowerShell session.) It does **not** locate the feature directory: with only `SPECIFY_FEATURE` set, `get_feature_paths` fails with *"Feature directory not found. Set `SPECIFY_FEATURE_DIRECTORY` or run the specify command to create `.specify/feature.json`."* Use `SPECIFY_FEATURE_DIRECTORY` (above) or `.specify/feature.json` to select the directory. | +| `SPECIFY_FEATURE` | Explicitly override the active feature **label** (e.g. `001-photo-albums`) — the identifier the core helpers report as the current feature/branch (`get_current_branch` in Bash, `Get-CurrentBranch` in PowerShell). Those helpers never inspect Git: when the variable is set they return it verbatim, and when it is unset they return an empty string. The basename fallback happens later — `get_feature_paths` / `Get-FeaturePaths` substitute the resolved feature directory's basename so the reported label is still usable — so calling the named helpers directly does **not** give you that fallback. You set it yourself: the Bash and Python feature scripts only **print** a commented `export SPECIFY_FEATURE=…` / `$env:SPECIFY_FEATURE = …` hint for you to run, because a child process cannot change its parent's environment, and `/speckit.specify` persists `feature_directory` to `.specify/feature.json` instead of setting this variable. (The PowerShell feature scripts do assign `$env:SPECIFY_FEATURE`, but that only reaches you when the script runs inside your current PowerShell session.) It does **not** locate the feature directory: with only `SPECIFY_FEATURE` set, `get_feature_paths` fails with *"Feature directory not found. Set `SPECIFY_FEATURE_DIRECTORY` or run the specify command to create `.specify/feature.json`."* Use `SPECIFY_FEATURE_DIRECTORY` (above) or `.specify/feature.json` to select the directory. | > **Two resolution axes.** `SPECIFY_INIT_DIR` selects the **project** (which directory contains `.specify/`); `SPECIFY_FEATURE_DIRECTORY` / `.specify/feature.json` select the **feature** within that project. They are independent — project first, then feature.