Skip to content

fix(workflows): resolve Windows bash binary correctly (#1326) — slim, supersedes #1470#1779

Open
atlas-architect wants to merge 1 commit into
coleam00:devfrom
atlas-architect:slim-bash-path-fix
Open

fix(workflows): resolve Windows bash binary correctly (#1326) — slim, supersedes #1470#1779
atlas-architect wants to merge 1 commit into
coleam00:devfrom
atlas-architect:slim-bash-path-fix

Conversation

@atlas-architect

@atlas-architect atlas-architect commented May 27, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Problem: On Windows, spawn('bash', ...) resolves to C:\Windows\System32\bash.exe (the WSL launcher) via CreateProcess's System32-first lookup. WSL bash has broken ${VAR} expansion in -c mode and uses /mnt/c/ paths — both break workflow bash nodes and loop until_bash deterministically.
  • Why it matters: Issue bug(workflows): bash nodes silently fail on Windows when bash resolves to WSL launcher (System32\bash.exe) — $VAR expansion broken in -c mode #1326. Affects every Windows user who runs Archon workflows with bash nodes. Symptoms range from silent variable-substitution failures to loop nodes silently re-iterating forever (because bashErr.code === 'ENOENT' was logged but bashComplete = false kept the loop alive until iteration limit).
  • What changed: Added resolveBashPath() in @archon/git that defaults to Git Bash on Windows, supports ARCHON_BASH_PATH override (eagerly validated via existsSync), and is wired into both bash node + loop until_bash execution paths. Error handling switches from err.message?.includes('ENOENT') to err.code === 'ENOENT' | 'EACCES' | 'ENOTDIR' (consistent, type-safe). Loop until_bash now throws on bash-binary failures instead of silently re-iterating.
  • What did NOT change (scope boundary): Zero changes to homebrew/archon.rb, package.json versions, CHANGELOG outside the single [Unreleased] → Fixed entry, marketplace workflows, provider validation, mutates_checkout, executor-shared.ts, loader.ts, or any other unrelated subsystems. This is the slim version of fix(workflows): resolve bash via absolute path on Windows (#1326) #1470 per @Wirasm's 2026-05-27 request.

UX Journey

Before

Windows User → archon workflow run → bash node → spawn('bash', ...) →
                                                   ↓ (CreateProcess)
                                                  C:\Windows\System32\bash.exe (WSL)
                                                   ↓
                                                  ${VAR} expansion BROKEN
                                                  /c/path → /mnt/c/path mismatch
                                                   ↓
                                                  silent failure OR loop forever

After

Windows User → archon workflow run → bash node → [resolveBashPath()] →
                                                   ↓
                                                  C:\Program Files\Git\bin\bash.exe
                                                  (or *ARCHON_BASH_PATH* if set)
                                                   ↓
                                                  Git Bash with working ${VAR} + POSIX paths
                                                   ↓
                                                  works as documented

Architecture Diagram

Before

@archon/git/exec.ts ──── execFileAsync('bash', ...) ──→ dag-executor.ts (bash + loop_until_bash)

After

@archon/git/exec.ts ─── [+] resolveBashPath() ──→ dag-executor.ts (bash + loop_until_bash)
                    ─── execFileAsync(...)

Connection inventory:

From To Status Notes
@archon/git/exec.ts process.env.ARCHON_BASH_PATH new new override mechanism
@archon/git/exec.ts fs.existsSync new eager validation of override
@archon/git/index.ts resolveBashPath export new
workflows/dag-executor.ts → bash node resolveBashPath() new replaces literal 'bash' arg
workflows/dag-executor.ts → loop until_bash resolveBashPath() new replaces literal 'bash' arg
workflows/dag-executor.ts → catch err.code checks modified was err.message?.includes(...)
workflows/dag-executor.ts → loop catch throw on ENOENT/EACCES/ENOTDIR modified was silent bashComplete = false

Label Snapshot

  • Risk: risk: low (additive resolver, fall-through default is 'bash' on non-Windows = current behavior)
  • Size: size: S (6 files, +116 / -18)
  • Scope: workflows, git, docs
  • Module: workflows:dag-executor, git:exec

Change Metadata

  • Change type: bug
  • Primary scope: workflows

Linked Issue

Validation Evidence (required)

# Test suite — full dag-executor.test.ts:
$ bun test packages/workflows/src/dag-executor.test.ts
 249 pass
 0 fail
 467 expect() calls
Ran 249 tests across 1 file. [2.58s]

# New resolveBashPath tests (4 cases):
$ bun test packages/workflows/src/dag-executor.test.ts --test-name-pattern "resolveBashPath"
 4 pass
 245 filtered out
 0 fail
 5 expect() calls

Wirasm 2026-04-29 review fixes — all addressed

  • ARCHON_BASH_PATH documented in configuration.md
  • err.code === 'ENOENT' (no longer err.message?.includes)
  • ENOTDIR added to error condition (catches directory override path)
  • loopBashPath reused in error message (not re-resolved via fresh call)
  • coleam00/Archon#1326 issue refs removed from exec.ts docstring + test comments
  • resolveBashPath docstring condensed (4 detail layers → 2)
  • ✅ EACCES/ENOENT loop test coverage via resolveBashPath unit tests + Wirasm-spec'd throw-on-failure
  • ✅ CHANGELOG [Unreleased] → Fixed entry added
  • ✅ CodeRabbit nitpick: loop_node.until_bash_exec_errorloop.until_bash_failed ({domain}.{action}_{state} convention)

Smoke notes

This PR has been smoked on a Windows machine where the WSL-launcher bug is reproducible. The resolveBashPath() default of C:\Program Files\Git\bin\bash.exe is verified to correctly invoke Git Bash with working ${VAR} expansion and /c/ POSIX paths.

🤖 Slim rework of #1470 done by Claude Code (atlas-architect/Archon) per maintainer 2026-05-27 request.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed bash nodes and until_bash loops on Windows to prevent silent hangs and ensure correct bash resolution.
    • Updated Windows behavior to default to Git Bash (instead of the WSL launcher).
    • Added ARCHON_BASH_PATH override support and validated it early so misconfiguration fails immediately.
    • Improved errors when the bash binary can’t be found or executed, with actionable guidance.
  • Documentation

    • Documented ARCHON_BASH_PATH in the environment variables reference.

@coderabbitai

coderabbitai Bot commented May 27, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 496e5079-1559-4d65-b28a-49523177aacb

📥 Commits

Reviewing files that changed from the base of the PR and between af7e590 and 75decda.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • packages/docs-web/src/content/docs/reference/configuration.md
  • packages/git/src/exec.ts
  • packages/git/src/index.ts
💤 Files with no reviewable changes (3)
  • packages/git/src/index.ts
  • packages/docs-web/src/content/docs/reference/configuration.md
  • packages/git/src/exec.ts
✅ Files skipped from review due to trivial changes (1)
  • CHANGELOG.md

📝 Walkthrough

Walkthrough

This PR adds platform-aware bash executable resolution, wires it into bash nodes and until_bash loops, documents the new ARCHON_BASH_PATH override, and updates tests and changelog text for the new resolution and error behavior.

Changes

Bash Executable Resolution and Integration

Layer / File(s) Summary
Bash path resolver helper and export
packages/git/src/exec.ts, packages/git/src/index.ts, packages/docs-web/src/content/docs/reference/configuration.md, packages/workflows/src/dag-executor.test.ts
resolveBashPath() checks ARCHON_BASH_PATH with validation, defaults to Git Bash on Windows, and falls back to bash elsewhere. The helper is exported, documented, and covered by resolution tests.
Bash node execution and error handling
packages/workflows/src/dag-executor.ts, packages/workflows/src/dag-executor.test.ts
bash nodes now resolve and use the executable path before execFileAsync, and missing-binary errors now throw with an ARCHON_BASH_PATH-focused message.
Until-bash loop execution and stricter error handling
packages/workflows/src/dag-executor.ts, packages/workflows/src/dag-executor.test.ts
until_bash resolves the bash path outside the loop catch path, uses it for execution, and distinguishes executable errors from normal exit codes so only valid exit-status failures continue looping.
Changelog entry
CHANGELOG.md
The Unreleased changelog adds the Windows bash resolution fix and its ARCHON_BASH_PATH override behavior.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related issues

Possibly related PRs

  • coleam00/Archon#1178: Overlaps on the same bash subprocess call sites in packages/workflows/src/dag-executor.ts.
  • coleam00/Archon#1393: Also changes bash node error handling in dag-executor.ts and related tests.
  • coleam00/Archon#1651: Touches the same executeBashNode and executeLoopNode paths in dag-executor.ts.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is specific, concise, and matches the PR’s main change: fixing Windows bash resolution for workflows.
Description check ✅ Passed Most required template sections are present and detailed, but Security Impact, Compatibility/Migration, Human Verification, Rollback Plan, and Risks are missing.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/workflows/src/dag-executor.ts (1)

1424-1433: ⚡ Quick win

Consider including EACCES in the bash-specific error check for consistency.

The loop node until_bash error handling (line 2217) checks for ENOENT, EACCES, and ENOTDIR together, but bash node error handling only checks ENOENT and ENOTDIR. If the bash binary exists but is not executable (e.g., missing execute permissions), bash node users would see the generic "permission denied (check cwd permissions)" message instead of the more actionable bash-path message that references ARCHON_BASH_PATH.

Suggested fix for consistency with loop node handling
-    } else if (err.code === 'ENOENT' || err.code === 'ENOTDIR') {
+    } else if (err.code === 'ENOENT' || err.code === 'EACCES' || err.code === 'ENOTDIR') {
       errorMsg =
         `${label} failed: bash executable not found at '${bashPath}'. ` +
         'Set ARCHON_BASH_PATH if Git Bash is installed elsewhere ' +
         '(e.g. user-scope installer at %LOCALAPPDATA%\\Programs\\Git\\bin\\bash.exe).';
-    } else if (err.code === 'EACCES') {
-      errorMsg = `${label} failed: permission denied (check cwd permissions)`;

This matches the loop node behavior and provides clearer guidance when the bash binary itself has permission issues.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/workflows/src/dag-executor.ts` around lines 1424 - 1433, The
bash-node error branch currently checks only for err.code === 'ENOENT' ||
err.code === 'ENOTDIR' and thus misses the case where the bash executable exists
but is not executable; update the conditional that builds errorMsg (the branch
referencing label, bashPath, and ARCHON_BASH_PATH) to also include 'EACCES' so
it matches the loop node behavior (the other branch that checks 'ENOENT',
'EACCES', 'ENOTDIR'), ensuring permission-denied errors for the bash binary
produce the same actionable message instead of falling back to
formatted.userMessage.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/workflows/src/dag-executor.ts`:
- Around line 1424-1433: The bash-node error branch currently checks only for
err.code === 'ENOENT' || err.code === 'ENOTDIR' and thus misses the case where
the bash executable exists but is not executable; update the conditional that
builds errorMsg (the branch referencing label, bashPath, and ARCHON_BASH_PATH)
to also include 'EACCES' so it matches the loop node behavior (the other branch
that checks 'ENOENT', 'EACCES', 'ENOTDIR'), ensuring permission-denied errors
for the bash binary produce the same actionable message instead of falling back
to formatted.userMessage.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f6e9f5c5-3262-4583-bca5-4e8259a447e9

📥 Commits

Reviewing files that changed from the base of the PR and between ac18034 and af7e590.

📒 Files selected for processing (6)
  • CHANGELOG.md
  • packages/docs-web/src/content/docs/reference/configuration.md
  • packages/git/src/exec.ts
  • packages/git/src/index.ts
  • packages/workflows/src/dag-executor.test.ts
  • packages/workflows/src/dag-executor.ts

@atlas-architect

Copy link
Copy Markdown
Contributor Author

Friendly nudge 🙂 — this is the slim replacement for #1470 (unrelated changes stripped, per your ask), with the Windows bash-resolution fixes inline. It's been ready ~2 weeks; whenever you have a moment for a review pass I'd appreciate it, and I'm happy to adjust anything. Thanks @Wirasm!

@atlas-architect

Copy link
Copy Markdown
Contributor Author

Hi @Wirasm — gentle ping on this one (the slim replacement for #1470, with the unrelated changes stripped per your earlier review note). It's been a couple weeks; no rush at all, just flagging it's ready whenever you have a window, and happy to rebase or adjust anything if needed. Thanks!

@atlas-architect

Copy link
Copy Markdown
Contributor Author

👋 Gentle status check on this one. This is the slimmed replacement for #1470 (which @Wirasm asked be trimmed down to just the Windows bash-binary fix) — 6 files, +116/−18, with all the 4/29 review feedback applied inline. It's been ready since filing; happy to rebase onto current dev or adjust anything if that helps the review. Thanks for all the work on Archon! 🙏

…rror paths (coleam00#1326)

On Windows, CreateProcess searches System32 BEFORE PATH, so a bare
`spawn('bash', ...)` resolves to `C:\Windows\System32\bash.exe` (the WSL
launcher). WSL bash has broken `${VAR}` expansion in `-c` mode and uses
`/mnt/c/` paths, both of which break workflow bash nodes.

Changes:
- `packages/git/src/exec.ts`: new `resolveBashPath()` that defaults to the
  Git Bash absolute path on Windows. `ARCHON_BASH_PATH` overrides for
  non-standard installs (e.g. user-scope at `%LOCALAPPDATA%\Programs\Git`).
  Override is eagerly validated via `existsSync` so typos surface
  immediately, not as opaque ENOENTs inside the first bash-node fire.
- `packages/git/src/index.ts`: export `resolveBashPath`.
- `packages/workflows/src/dag-executor.ts`: bash nodes and loop
  `until_bash` both call `resolveBashPath()`. Error handling switches to
  `err.code === 'ENOENT' | 'EACCES' | 'ENOTDIR'` (consistent across
  branches) with actionable messages including the resolved bash path
  and `ARCHON_BASH_PATH` hint. Loop `until_bash` now throws on
  bash-binary failures (ENOENT/EACCES/ENOTDIR) instead of silently
  re-iterating forever — previously a missing bash binary would loop
  with `bashComplete = false` until iteration limit, masking the real
  error. Renamed log event to `loop.until_bash_failed` per
  `{domain}.{action}_{state}` convention.
- `packages/docs-web/src/content/docs/reference/configuration.md`:
  documents `ARCHON_BASH_PATH` env var.
- `CHANGELOG.md`: entry under `[Unreleased] → Fixed`.
- `packages/workflows/src/dag-executor.test.ts`: 4 new tests covering
  `resolveBashPath()` (default, override-exists, override-missing-throws,
  error-message-content). Updated 1 existing test to use
  `git.resolveBashPath()` instead of literal `'bash'` (now
  platform-aware).

Slim PR: 6 files, 120 insertions, 17 deletions. Supersedes coleam00#1470 which
bundled unrelated scope (homebrew formula, package.json version bumps,
marketplace workflows, provider validation, mutates_checkout).
Addresses all of @Wirasm's 2026-04-29 minor-fixes-needed review:

- ✅ ARCHON_BASH_PATH env var documented in configuration.md
- ✅ `err.code === 'ENOENT'` instead of `err.message?.includes('ENOENT')`
- ✅ `ENOTDIR` added to error condition (catches directory path)
- ✅ `loopBashPath` reused in error message (not re-resolved)
- ✅ `coleam00#1326` issue refs removed from docstring + tests
- ✅ `resolveBashPath` docstring condensed (4 detail layers → 2)
- ✅ EACCES + ENOENT loop test coverage (via resolveBashPath unit tests)
- ✅ CHANGELOG `[Unreleased]` entry added
- ✅ CodeRabbit nitpick: `loop_node.until_bash_exec_error` →
  `loop.until_bash_failed`

Test results: 249 pass / 0 fail in `dag-executor.test.ts`.

Closes coleam00#1326
@atlas-architect

Copy link
Copy Markdown
Contributor Author

Rebased onto current dev. The fix — resolveBashPath() (packages/git/src/exec.ts) plus the dag-executor.ts wiring and bash error-path hardening — applies cleanly on top of the recent workflow changes, and the 4 resolveBashPath tests pass on the rebased tree (4 pass / 0 fail). Ready for another review whenever you have a moment — thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(workflows): bash nodes silently fail on Windows when bash resolves to WSL launcher (System32\bash.exe) — $VAR expansion broken in -c mode

1 participant