Repository navigation
test(source-control, playbooks): five suites fail on Windows Git Bash from path form and line endings #6135
Description
Problem
Five shell suites fail when run locally under Windows Git Bash on current main and pass in CI on Linux. CI's Windows lane (.github/workflows/test-windows.yml) does not run any of them, so nothing in CI catches the gap. Every agent working on Windows sees these red when it verifies a change to playbooks or source-control locally. It then has to work out that the failures came before its change, or push to CI to get a trustworthy signal.
Most of the failures fall into two classes. In the first, a path is compared in POSIX /tmp/... form against the /c/Users/.../AppData/Local/Temp/... form Git Bash resolves. In the second, CRLF/LF line endings or a trailing \r break a byte-exact comparison. #5899 (head 4cf65d5fb) first reported them. They reproduce unchanged on today's main.
Reproduction
On Windows 11 with Git Bash, using a detached worktree at origin/main f605a5963 (2026-10-03), run bash <suite> for each suite:
| suite | result | failing assertions |
|---|---|---|
plugins/playbooks/skills/repo-sweep/scripts/tick.test.sh |
3 FAILED | in-progress: body bytes, done lines: body bytes, filed: body bytes. The expected bytes have run:\r\n- [ ] and the actual have run:\n- [ ], so a CRLF body is rewritten with LF. |
plugins/playbooks/skills/repo-sweep/scripts/state.test.sh |
4 FAILED | reconcile: committed lines, web-UI tick, exact skill sets, base excluded, CRLF body from a subdirectory: same report, other branch, one open sweep: exit 14 names the branch, other branch, two open sweeps: exit 15 lists them. For the two exit-14/15 cases, expected and actual print identically, even under cat -A with no \r visible. The difference is in something the test compares but does not print, and its cause is not found yet. |
plugins/source-control/scripts/worktree-claim.test.sh |
5 FAIL | [4] report names the plain-add path, [19] foreign claim names the path, [31] unclaimed entry names the path. Each expects /tmp/tmp.X/external/... and gets /c/Users/KyleSexton/AppData/Local/Temp/tmp.X/external/.... Also [73] stale: a scan that fails mid-way is not provable (exit 1) (got 0) and [74] stale: the refusal says the scan failed. |
plugins/source-control/scripts/lib/worktree-facts.test.sh |
6 of 21 FAIL | claimed after a bare add: the claim written is the claim read (actual is empty), a reason wrapped in double quotes round-trips unchanged, a literal backslash-n and backslash pair round-trip unchanged, list prints the raw quoted reason in the lock_reason column, list prints the raw backslash reason in the lock_reason column, list: a reasonless lock reads locked=yes, lock_reason=- |
plugins/source-control/scripts/worktree-root-scan.test.sh |
2 FAIL | [14] symlink is not proposed: expected linked\tsymlink\tno, got linked\tempty\tyes. Git Bash without developer-mode symlinks copies instead of linking (assumption). Also [24] default root scans the configured root: the same /tmp vs /c/Users/.../Temp path form as above. |
The other source-control worktree suites pass on the same checkout: worktree-add-claim-gate, worktree-add-containment-gate, worktree-create-gate, worktree-create, worktree-root-doctor, and skills/worktree/reattach-slice. worktree-gates-spawn-budget skips because strace is unavailable.
Impact
- Local verification time on Windows. An agent cannot get a green local run for
playbooksorsource-controlchanges, so it either investigates failures that existed before its change or pushes to CI to find out. Both add a CI round to time-to-green. - The worktree-claim and worktree-facts failures cover claim parsing on the platform where most lanes run. If any of these failures is a real Windows behaviour gap rather than a test-fixture gap (unverified), the Windows behaviour of the claim gate is untested.
Proposed fix
- Path form. In the three source-control suites, normalise both sides before comparing: resolve the fixture root once with
cd "$TMP" && pwd -P(orcygpath -uwhen it is present) and build the expected paths from that, instead of the literal/tmp/...thatmktempreturned. - Line endings. In
tick.test.sh, and instate.test.shonce its hidden difference is found, find out whethertick.sh/state.shshould preserve CRLF bodies, which the "CRLF body" case suggests. If so, fix the script, which is likely ased,awkorreadthat drops\r. If not, build the expected fixture bytes with the same line ending the script emits. Fix it in the script if the web-UI tick path really receives CRLF bodies from GitHub. - worktree-facts claim round-trip. Find out why the claim read after a bare add is empty on Windows: whether the lock-reason file path,
git worktree lock --reason, or quoting differs. - Symlink case. Skip
[14]with aSKIP:line whenln -sproduces a copy (test with[ -L ]after creating it), the same wayworktree-gates-spawn-budgetskips without strace. - Add these five suites to the Windows lane in
.github/workflows/test-windows.yml, scoped by path like its existing entries, so a regression is caught in CI.
Acceptance criteria
- On Windows Git Bash at a fresh
main, all five suites exit 0, or printSKIP:only for a capability the host lacks (symlinks). test-windows.ymlruns the five suites when their plugin paths change.- Linux CI results for the five suites are unchanged.
Activity
github-actions commentedon Oct 3, 2026
Thanks for the report. Automated triage handed this issue to a maintainer.
Automated triage. A maintainer reviews it.
kyle-sexton commentedon Oct 4, 2026
This was generated by AI during triage.
Triage
tick.test.shsuite reports3 FAILED, andworktree-root-scan.test.shfails[14] symlink is not proposed(gotlinked empty yes) and[24] default root scans the configured root(expected/tmp/tmp.X/..., got/c/Users/.../Temp/tmp.X/...), as reported. None of the five suites' scripts or tests changed between the report's base and origin/main 698960d.needs-humanis removed because no human decision is pending; the claim-gate fence below stops the worker if the fix turns out to need one.Agent Brief
Type: Bug
Summary: make the five Windows-failing shell suites (repo-sweep
tickandstate, source-controlworktree-claim,worktree-facts,worktree-root-scan) pass on Windows Git Bash, and have the Windows CI lane run them.Current behavior:
On Windows Git Bash the suites fail while Linux CI passes. Two causes are known: expected paths are built from the literal POSIX temp path
mktempreturned, while the code under test reports the Git Bash drive form; and CRLF bodies are rewritten with LF (or a trailing carriage return breaks a byte-exact comparison). Two cases in thestatesuite (other-branch exit 14 and 15) differ in something the test compares but does not print, cause unknown. Theworktree-factsclaim round-trip reads an empty claim after a bare add. The symlink case fails where Git Bash copies instead of linking. The Windows CI lane does not run these suites.Desired behavior:
statesuite's exit-14/15 cases is found and fixed at its cause.worktree-factsclaim written after a bare add is the claim read back on Windows.SKIP:line when the host cannot create a real symlink, the same way other suites skip on a missing capability.Key interfaces:
tick/statescripts and the worktree-facts library.Acceptance criteria:
main, each of the five suites exits 0, printingSKIP:only for a capability the host lacks (symlinks).playbooksorsource-controlplugin makes the Windows CI lane run the matching suites.Out of scope:
kyle-sexton commentedon Oct 4, 2026
This was generated by AI during work-loop.
Ratify autonomous dispatch of this C3 bug-fix item?
Classification:
work-class: scoped(C3), bug-fix-shaped (confirmed at triage). Intended dispatch: the work-loop worker lane, through/work-items:work, to a draft PR the lane never merges. First drain is not complete and no item-level ratification exists, so it is queued here. Ratify through/work-items:attend-queue.Lane: work-loop, instance melo-worker-1, cycle 2.
kyle-sexton commentedon Oct 11, 2026
Draft PR #7107 is open on branch
test/6135-windows-path-crlf-suites. It covers the technical decisions in the 2026-10-04 Agent Brief.What changed
cygpathonly when it is present, thenpwd -P. On Linux, a symlinkedTMPDIRreproduces exactly the failure list in this issue before the change and passes after it.chmod 000, which neither root nor Windows enforces. A stubfindnow carries those two cases everywhere.SKIP:whenln -scopies instead of linking.tick.shandstate.sh. These now pass-bto a native jq under Git Bash or Cygwin. Without it, text-mode stdout added CRs, which is the hidden difference behind exit 14/15. The tick test stub no longer uses--rawfile, which a native jq opens in text mode.test-windows-worktreejob runs all five suites.Left for a human
Refs, notCloses.ratify-c3escalation of 2026-10-04 is still Kyle's to decide. The overnight run does not count as that ratification.needs-humanlabel was left for Kyle to clear.Generated by Claude Code
kyle-sexton commentedon Oct 11, 2026
Handoff note for whoever picks this up next. The overnight Claude run has stopped, and Kyle is moving issue work to Cursor.
test/6135-windows-path-crlf-suitesWhere a branch is listed it is pushed, so it can be taken over as is. A draft PR shows a red
ci-statusby design until it is marked ready.Generated by Claude Code
kyle-sexton commentedon Oct 12, 2026
Owner ratified the C3 admission (work-class: scoped) in an attended session on 2026-10-12. Implementation is draft PR #7107, which now goes through
ready.