Status: Accepted Date: 2026-03-02 Issue: B006 Supersedes: N/A Superseded by: N/A

Context

ShellExecutor hardcodes /bin/sh -c, which maps to dash on Debian/Ubuntu. Bash-dependent workflow commands (arrays, [[, process substitution, brace expansion) fail silently or with cryptic errors. Users on those distros cannot use bash syntax without workarounds.

Candidates

OptionProsCons
$SHELL Detection + FallbackAutomatic for users; minimal code; backward-compatible variadic options; matches agents patternSilent fallback for incompatible shells (fish); per-step override deferred
Always /bin/bashSimple, works for most usersBreaks minimal containers (Alpine); not standard POSIX
Per-step shell: fieldFlexible, per-step controlDomain model change; larger surface; YAGNI
Document POSIX-onlyZero code changePoor UX; doesn’t fix the problem; users forced to use workarounds

Decision

Detect the user’s preferred shell at ShellExecutor construction time by reading the $SHELL environment variable. Validate the path is absolute and exists on disk; fall back to /bin/sh if unset, relative, or invalid. Expose a WithShell(path) functional option for explicit override and test injection. Change is isolated to internal/infrastructure/executor/shell_executor.go — zero domain or port modifications.

Implementation:

  • Add shellPath string field to ShellExecutor
  • Add detectShell() helper: reads $SHELL, validates path, returns fallback on error
  • Add WithShell(path) functional option matching agents/options.go pattern
  • Update Execute() to use e.shellPath instead of hardcoded /bin/sh
  • Update documentation: doc.go, ports/executor.go, ports/doc.go, SECURITY.md, architecture.md

Consequences

What becomes easier:

  • Bash-dependent commands work automatically for users with $SHELL=/bin/bash
  • Workflows written on macOS/Arch (bash) now run on Debian/Ubuntu without modification
  • Test injection via WithShell() without environment variable manipulation
  • Zero call-site changes — backward compatibility maintained via variadic options

What becomes harder:

  • Users with exotic shells (fish, nushell) without -c flag will silently fall back to POSIX /bin/sh
  • Per-step shell override deferred — all steps use the same detected shell (can be added later if needed)

Constitution Compliance

PrincipleStatusJustification
P1: Hexagonal ArchitectureCompliantInfrastructure-only change; zero domain/port modifications
P2: Go IdiomsCompliantFunctional options pattern, explicit error handling, os.Getenv
P3: Minimal AbstractionCompliantSingle field + single option; no new types/interfaces
P4: Error TaxonomyCompliantSilent fallback is not an error condition (matches B005 precedent)
P5: Security FirstCompliantValidates shell path is absolute and exists; gosec G204 documented
P6: Test-Driven DevelopmentCompliantTable-driven tests for all scenarios