* fix(#2962): enable nullglob in for-glob shell blocks for zsh portability Workflow shell blocks are fenced bash but execute in the user's login shell (zsh on macOS). zsh's nomatch default aborts the WHOLE block on an unmatched glob in a for-list (not just skipping the command), silently bypassing every statement after it — including the verify-phase decision-coverage gate, whose optional *-CONTEXT.md lookup used the unsafe for-list form so the DECISION_RESULT= assignment on the next line never ran under zsh. Fix: prepend a portable nullglob shim to every bash block containing a for-glob loop: shopt -s nullglob 2>/dev/null; setopt NULL_GLOB 2>/dev/null Each command no-ops (stderr suppressed) in the shell that doesn't recognize it; the matching shell enables nullglob so an unmatched glob expands to nothing and the loop body is skipped cleanly. Verified locally: both zsh and bash now reach end-of-block (rc=0) on a no-match glob; bash matched-case behavior unchanged. 14 blocks across 7 files: verify-phase.md (4, incl. the decision-coverage gate), review.md, execute-phase.md, resume-project.md, complete-milestone.md, audit-milestone.md, gsd-integration-checker.md, gsd-plan-checker.md (3). Closes the zsh bypass of the #2770 fix. * chore(#2962): add changeset fragment * chore(#2962): backfill changeset PR number 3087 --------- Co-authored-by: sim <sim@local>
This commit is contained in:
@@ -94,6 +94,9 @@ For each phase, extract what it provides and what it should consume.
|
||||
**From SUMMARYs, extract:**
|
||||
|
||||
```bash
|
||||
# #2962: zsh aborts the block on an unmatched for-list glob (nomatch); bash passes it through. nullglob both.
|
||||
shopt -s nullglob 2>/dev/null; setopt NULL_GLOB 2>/dev/null
|
||||
|
||||
# Key exports from each phase
|
||||
for summary in .planning/phases/*/*-SUMMARY.md; do
|
||||
echo "=== $summary ==="
|
||||
|
||||
@@ -714,6 +714,9 @@ Extract from init JSON: `phase_dir`, `phase_number`, `has_plans`, `plan_count`.
|
||||
Orchestrator provides CONTEXT.md content in the verification prompt. If provided, parse for locked decisions, discretion areas, deferred ideas.
|
||||
|
||||
```bash
|
||||
# #2962: zsh aborts the block on an unmatched for-list glob (nomatch); bash passes it through. nullglob both.
|
||||
shopt -s nullglob 2>/dev/null; setopt NULL_GLOB 2>/dev/null
|
||||
|
||||
gsd_run query phase.list-plans "$phase_number"
|
||||
# Research / brief artifacts (deterministic listing)
|
||||
gsd_run query phase.list-artifacts "$phase_number" --type research
|
||||
@@ -735,6 +738,9 @@ done
|
||||
Use `gsd-tools query` to validate plan structure:
|
||||
|
||||
```bash
|
||||
# #2962: zsh aborts the block on an unmatched for-list glob (nomatch); bash passes it through. nullglob both.
|
||||
shopt -s nullglob 2>/dev/null; setopt NULL_GLOB 2>/dev/null
|
||||
|
||||
for plan in "$PHASE_DIR"/*-PLAN.md; do
|
||||
echo "=== $plan ==="
|
||||
PLAN_STRUCTURE=$(gsd_run query verify.plan-structure "$plan")
|
||||
@@ -820,6 +826,9 @@ Inspect `tasks` in the JSON; open the PLAN in the editor for prose-level review.
|
||||
## Step 6: Verify Dependency Graph
|
||||
|
||||
```bash
|
||||
# #2962: zsh aborts the block on an unmatched for-list glob (nomatch); bash passes it through. nullglob both.
|
||||
shopt -s nullglob 2>/dev/null; setopt NULL_GLOB 2>/dev/null
|
||||
|
||||
for plan in "$PHASE_DIR"/*-PLAN.md; do
|
||||
grep "depends_on:" "$plan"
|
||||
done
|
||||
|
||||
Reference in New Issue
Block a user