From 0be49879b1c51a479d2013a14fe42ed9bd10ce32 Mon Sep 17 00:00:00 2001 From: Jesse Vincent Date: Thu, 13 Aug 2026 00:27:52 +0000 Subject: [PATCH] fix(sdd): invoke sdd-workspace via bash so helpers survive stripped exec bits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Codex marketplace users hit 'Permission denied' running SDD helpers: some extractors (Python zipfile) discard Unix mode attributes when unpacking the package, so task-brief's and review-package's direct exec of their sibling sdd-workspace fails. Our packaging preserves 0755 (git archive | tar -xpf, asserted by the existing packaging test) — the bits are lost on the consumer side, which no packaging change can reach. Invoking the sibling via "${BASH:-bash}" makes the exec bit irrelevant. TDD: new regression case copies the helpers, chmod -x, runs task-brief via bash — RED with the reported rc=126 Permission denied, GREEN after. Reported in #2040 (michaelholcomb-creator). Fixes #2040. --- .../scripts/review-package | 4 +++- .../scripts/task-brief | 4 +++- tests/claude-code/test-sdd-workspace.sh | 15 +++++++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/skills/subagent-driven-development/scripts/review-package b/skills/subagent-driven-development/scripts/review-package index 31852e2ab..c7ce1ef47 100755 --- a/skills/subagent-driven-development/scripts/review-package +++ b/skills/subagent-driven-development/scripts/review-package @@ -25,7 +25,9 @@ git rev-parse --verify --quiet "$head" >/dev/null || { echo "bad HEAD: $head" >& if [ $# -eq 4 ]; then out=$4 else - dir=$("$(cd "$(dirname "$0")" && pwd)/sdd-workspace" "$plan") + # Invoke via bash rather than direct exec: some extractors (Python zipfile) + # strip Unix exec bits when unpacking marketplace packages (#2040). + dir=$("${BASH:-bash}" "$(cd "$(dirname "$0")" && pwd)/sdd-workspace" "$plan") out="$dir/review-$(git rev-parse --short "$base")..$(git rev-parse --short "$head").diff" fi diff --git a/skills/subagent-driven-development/scripts/task-brief b/skills/subagent-driven-development/scripts/task-brief index 612e14a1e..b49fc546c 100755 --- a/skills/subagent-driven-development/scripts/task-brief +++ b/skills/subagent-driven-development/scripts/task-brief @@ -21,7 +21,9 @@ n=$2 if [ $# -eq 3 ]; then out=$3 else - dir=$("$(cd "$(dirname "$0")" && pwd)/sdd-workspace" "$plan") + # Invoke via bash rather than direct exec: some extractors (Python zipfile) + # strip Unix exec bits when unpacking marketplace packages (#2040). + dir=$("${BASH:-bash}" "$(cd "$(dirname "$0")" && pwd)/sdd-workspace" "$plan") out="$dir/task-${n}-brief.md" fi diff --git a/tests/claude-code/test-sdd-workspace.sh b/tests/claude-code/test-sdd-workspace.sh index 841723016..86576df52 100755 --- a/tests/claude-code/test-sdd-workspace.sh +++ b/tests/claude-code/test-sdd-workspace.sh @@ -189,6 +189,21 @@ PLAN echo " status: $wt_status" fi + # --- helpers survive a mode-stripping extractor dropping exec bits (#2040) --- + local stripped="$TEST_ROOT/stripped-scripts" + mkdir -p "$stripped" + cp "$SDD_SCRIPTS/sdd-workspace" "$SDD_SCRIPTS/task-brief" "$SDD_SCRIPTS/review-package" "$stripped/" + chmod -x "$stripped"/* + local noexec_out noexec_rc=0 + noexec_out="$(cd "$repo" && bash "$stripped/task-brief" plan-b.md 1 2>&1)" || noexec_rc=$? + if [[ "$noexec_rc" -eq 0 && -f "$repo/.superpowers/sdd/plan-b/task-1-brief.md" ]]; then + pass "task-brief works with no exec bit on sdd-workspace" + else + fail "task-brief works with no exec bit on sdd-workspace" + echo " rc: $noexec_rc" + echo " output: $noexec_out" + fi + echo "" if [[ "$FAILURES" -ne 0 ]]; then echo "FAILED: $FAILURES assertion(s)."