github / github/spec-kit

[Bug] create-new-feature.sh uses ${word^^} (bash 4+) and silently drops acronyms on macOS (bash 3.2)

Aperta Adatta ai principianti
#3,927 2 commenti 0 reazioni 0 assegnatari Vedi su GitHub
bug-assess invalid
Lingua principale
Python
Stelle
137k
Fork
12.3k
Merge medio
2g 12h
PR unite (30g)
159

Descrizione

## Summary

`create-new-feature.sh` (line 231) uses `${word^^}`, a **bash 4+ uppercase expansion**. macOS ships bash 3.2.57 (Apple's last GPLv2 build) and the script **fails silently** on that line: stderr is full of `bad substitution` warnings, the script keeps running, and the generated branch name silently **drops acronyms** that were short (≤2 chars) and uppercase in the original description.

Because the script still emits valid JSON and the resulting branch name is plausible, end users on macOS get a quietly truncated branch without realizing it.

## Environment

- macOS (any current version; bash 3.2.57 has been the system bash on every macOS since 2007)
- Any git repo initialised with `specify init`
- No `bash` from Homebrew installed (or installed but not first in `PATH` for the running shell)

```sh
$ /bin/bash --version
GNU bash, version 3.2.57(1)-release (arm64-apple-darwin24)

$ bash .specify/scripts/bash/create-new-feature.sh --json --dry-run \
"Transparencia Art. 50 AI Act: aviso de interaccion con IA"
{"BRANCH_NAME":"001-transparencia-art-act", ...} # ← missing "AI"

$ bash .specify/scripts/bash/create-new-feature.sh --json --dry-run "AI Act transparency"
{"BRANCH_NAME":"001-act-transparency", ...} # ← missing "AI" (top-3 cutoff also affected)
bad substitution # (only visible in stderr; the JSON looks fine)
```

The same inputs against bash 5.x preserve the acronym:

```sh
$ bash .specify/scripts/bash/create-new-feature.sh --json --dry-run "AI Act transparency"
{"BRANCH_NAME":"001-ai-act-transparency", ...} # ← "ai" preserved
```

## Root cause

`create-new-feature.sh`, line 231:

```sh
elif echo "$description" | grep -q "\b${word^^}\b"; then
# Keep short words if they appear as uppercase in original (likely acronyms)
meaningful_words+=("$word")
fi
```

`${word^^}` is case-conversion syntax introduced in bash 4.0 (2009). It is not available in bash 3.2. On bash 3.2 the line errors out, `grep` runs with the literal placeholder string, the grep returns non-zero, the elif is false, and the short word is silently dropped from `meaningful_words[]`. The same `${var^^}` / `${var,,}` pattern likely exists in other vendored scripts; I have not audited them all.

The shebang `#!/usr/bin/env bash` does not protect against this — `env` picks whatever `bash` resolves to in the current `PATH`, and on a stock macOS that is the system's bash 3.2.

## Suggested fix

Two options, in order of preference:

1. **POSIX-portable** — replace `${word^^}` with a portable uppercase conversion, e.g. `echo "$word" | tr '[:lower:]' '[:upper:]'`, so the script works on bash 3.2 too. This is a one-line change per occurrence and removes the silent failure mode entirely.
2. **Bash 4+ required** — if the project decides to require bash 4+ intentionally, make the failure loud: at the top of every vendored script, add a runtime check that fails fast with a clear message and a hint to install bash from Homebrew (`brew install bash`). Currently the failure is silent and the user has no way to know their branch name is wrong.

I'd recommend option 1 (POSIX-portable) — the function is a small, isolated transformation and the project already has a `clean_branch_name` that uses `tr '[:upper:]' '[:lower:]'` to do the inverse. Symmetry argues for replacing the bash-only expansion with `tr` as well.

## Workaround on macOS today

Install bash 5.x via Homebrew and ensure it is first in `PATH` for the shell that runs Spec-Kit:

```sh
brew install bash
# ~/.zshrc or ~/.bashrc, idempotent:
case "$PATH" in
/opt/homebrew/bin:*) ;;
/opt/homebrew/bin) ;;
*) export PATH="/opt/homebrew/bin${PATH:+:$PATH}" ;;
esac
```

Note that on login shells, `/etc/zprofile` runs `path_helper` which re-derives `PATH` from `/etc/paths` + `/etc/paths.d/*` and puts `/opt/homebrew/bin` at the *end* — so the prefix must be re-asserted in a file that runs after `/etc/zprofile` (e.g. `~/.zshrc`).

## Impact

This affects every macOS developer or agent using Spec-Kit with a description containing acronyms (e.g. "AI Act", "RGPD", "GDPR", "API", "OAuth", "HIPAA", "PCI"). The "feature name" used downstream in `specs/NNN-short-name/spec.md` is the same truncated string, so the spec file itself also loses the acronym, and any tooling that relies on the branch/spec name to understand the feature (e.g. CI labels, dashboards, search) gets the wrong subject.

Discovered while piloting Spec-Kit on ada-legal-ai (PR #11 in the HOILAB org). Same root cause likely affects any other team running Spec-Kit on macOS.

Guida per i contributori

Apri la guida per i contributori

Direzione di ricerca

Start at .specify/scripts/bash/create-new-feature.sh around line 231 and inspect the acronym branch-name path. Run the supplied --json --dry-run commands under macOS’s /bin/bash 3.2 and compare with Bash 5.x. Done means short uppercase acronyms remain in BRANCH_NAME and stderr has no bad substitution warning.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
bash
Ambito
cli, tooling
Tipo di issue
Bug
Difficoltà
2/5
Tempo stimato
1-3 ore
Stato di attività
Tranquilla
Chiarezza
Specificata chiaramente
Idoneità per principianti
84/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.