Skip to content

Windows: herdr backend refuses every spawn - canonical socket path rejects the drive-letter path herdr emits on both sides of the identity check #3283

Open

Description

@trngthnh369

Summary

fm_backend_herdr_canonical_socket_path (bin/backends/herdr.sh:699) requires the socket path to be POSIX-absolute. On native Windows, herdr emits a drive-letter path on both sides of the launcher identity check, so fm_backend_herdr_launcher_identity always returns 1 and every herdr-backend spawn is refused:

error: herdr launcher pane 'wN:pM' reports an unusable socket path;
refusing to place a worker from an unverifiable parent identity

Root cause

fm_backend_herdr_canonical_socket_path() {  # <socket-path>
  local socket=$1 sock_dir sock_base
  [ -n "$socket" ] || return 1
  case "$socket" in
    /*) ;;
    *) return 1 ;;    # <-- refuses here
  esac

Both inputs to the identity comparison are native Windows paths:

  • the injected launcher env: HERDR_SOCKET_PATH=C:\Users\<user>\AppData\Roaming\herdr\herdr.sock
  • herdr session list --json → .socket_path:
    {"name":"default","running":true,
     "socket_path":"C:\Users\<user>\AppData\Roaming\herdr\herdr.sock"}

launcher_identity canonicalizes the first (line 1559) and presentation_session_socket_path canonicalizes the second (line 728). Both hit the /* guard, so the check fails before the two are ever compared.

There is no environment workaround. Setting HERDR_SOCKET_PATH to a POSIX path by hand fixes only the launcher side; the session side is read from herdr's own JSON, which the caller does not control.

Relaxing the guard alone is not sufficient either: dirname / cd "$sock_dir" / pwd -P at lines 706-712 cannot consume a backslash path, so the drive form has to be converted, not merely admitted.

Environment

  • Windows 11 (10.0.26200), native (not WSL)
  • Git for Windows 2.49.0, msys2 runtime 3.5.7
  • herdr backend, auto-detected via HERDR_ENV=1
  • firstmate at 1fd7ea2

Local patch that works

Convert only the drive-letter shape, only when cygpath is present, and re-verify the result is POSIX; every other shape stays refused and the POSIX branch is untouched:

  case "$socket" in
    /*) ;;
    [A-Za-z]:[\/]*)
      command -v cygpath >/dev/null 2>&1 || return 1
      socket=$(cygpath -u "$socket" 2>/dev/null) || return 1
      case "$socket" in
        /*) ;;
        *) return 1 ;;
      esac
      ;;
    *) return 1 ;;
  esac

Verified: C:\Users\...\herdr.sock and C:/Users/.../herdr.sock both canonicalize to the same /c/Users/.../herdr.sock, which is what the identity comparison needs; relative paths, bare filenames, empty strings and garbage are still refused.

With this applied, the spawn gets past the identity check and reaches the next Windows blocker, #1796 (treehouse worktree entry undetected because the cwd move happens inside a nested cmd.exe).

Related

Same family as #3267 (POSIX-only assumption in a foundation layer — there ln -s for the lock primitive). Direct evidence for the #504 intent (incremental native-Windows support via the Herdr backend).

Activity

  1. kunchenguid commented on Aug 29, 2026

    @kunchenguid
    Owner

    Speaking as Kun's firstmate: whole thread read (no comments yet; related #504 Windows-herdr intent, #3267 POSIX lock assumption, #1796 nested cmd.exe spawn blocker). Verified still unfixed on latest main (f66be0f).

    fm_backend_herdr_canonical_socket_path in bin/backends/herdr.sh still requires /* POSIX-absolute and then dirname/cd/pwd -P. Native Windows herdr emits a drive-letter path on both the launcher env and session list --json, so fm_backend_herdr_launcher_identity returns 1 before the two paths are compared and every herdr-backend spawn is refused. Setting HERDR_SOCKET_PATH to a POSIX path cannot fix the session-list side. No open PR covers this socket canonicalizer. #3231/#3228 are Git Bash watcher-lock, not this. Do not open a competing PR from this pass.

    A correct fix must stay fail-closed: convert only the drive-letter shape (and only when a converter such as cygpath exists), re-verify the result is POSIX-absolute, and keep refusing relative/empty/garbage paths so parent identity remains verifiable.

    VISION.md per-rule:

    • One captain, one interface: aligns (every spawn refused is not a usable crew).
    • Authority is explicit and never inferred: aligns (no new autonomy; identity check stays a refusal when unverifiable).
    • Scripts own the mechanics, agents own the judgment: aligns (path canonicalization is script mechanics).
    • A restart is a non-event: n/a.
    • Delegation with a spine: aligns (spawn is the worker-delivery contract).
    • The fleet outlives any vendor: aligns (bind to the socket path herdr actually emits, not POSIX pixels).
    • Scope: aligns (backend adapter, not workshop). Direct evidence for Intent: incremental native-Windows support via the Herdr backend #504.

    contract-class: restore (launcher-identity is supposed to compare two spellings of the same socket and allow spawn when they match; the POSIX-only guard makes a same-socket Windows pair unverifiable).

    Labeling ready-for-pr.

  2. added
    ready-for-prTriage: real bug or VISION-aligned feature, open for a PR
    on Aug 29, 2026
  3. trngthnh369 commented on Aug 30, 2026

    @trngthnh369
    Author

    Following up with the rest of the picture: the socket-path refusal above is the first of four independent defects sitting on one code path on native Windows (Git for Windows / MSYS2, no WSL). Each is only reachable once the previous one is cleared, so none of them can be observed in isolation — which is presumably why they have not surfaced together before.

    I have a working patch and a measured result. I am posting it here rather than opening a PR because CONTRIBUTING.md requires PRs to main to be raised through no-mistakes. Happy to run it through the gate and raise a proper PR if you want it — just say the word. Equally happy to split it into four PRs if that reviews better.

    The four defects, in the order a spawn hits them

    1. Socket identity refuses every drive-letter path (this issue)

    fm_backend_herdr_canonical_socket_path only accepts a POSIX-absolute path, but herdr emits a native path on both sides of the launcher identity check, so there is no environment workaround — overriding HERDR_SOCKET_PATH fixes one side, the other comes from herdr session list --json. Relaxing the guard alone is not enough either: dirname / cd / pwd -P just below cannot consume a backslash path.

    2. Worktree discovery polls a field herdr never populates

    fm_backend_herdr_current_path reads .pane.foreground_cwd. On herdr 0.8.0-preview that field is declared in the API schema as ["string","null"] but emitted for no pane at all — measured across 27 panes, in api snapshot, and with a live foreground process demonstrably sitting in another directory. The poll returns empty forever, so treehouse get did not enter a worktree within 60s cannot be avoided by any timeout value.

    Neither fallback works: .pane.cwd does follow a cd in the pane's own shell here, but not into the nested subshell treehouse get opens; and pane process-info's foreground_processes resolves to the pane's own shell, since Windows has no tty foreground process group to read.

    The patch adds an active probe in the same spirit as fm_backend_zellij_current_path. It is wired only into the treehouse-get discovery loop, via a new spawn_discover_path, and deliberately not into fm_backend_herdr_current_path — that function is also the relaunch reader, relaunch adopts an existing endpoint whose pane may hold a live agent, and pane run submits, so probing there would type into that agent's composer. On a platform with no foreground_cwd, relaunch keeps returning empty and refuses, which is the safe outcome.

    3. Task panes get cmd.exe, and the spawn still exits 0

    This is the nastiest one. treehouse get picks its subshell from $SHELL; a herdr pane runs the configured default shell with no SHELL set, so treehouse falls back to %COMSPEC%. fm-spawn.sh then types POSIX into cmd.exe:

    C:\...\fm-sandbox>export GOTMPDIR=/tmp/fm-<id>/gotmp
    'export' is not recognized as an internal or external command
    C:\...\fm-sandbox>env -u CURSOR_AGENT ... claude --dangerously-skip-permissions "$(...)"
    'env' is not recognized as an internal or external command
    

    The crewmate never launches, while fm-spawn.sh prints spawned <id> ... and exits 0.

    Pinning SHELL alone is not sufficient: treehouse execs bash.exe non-login, so /etc/profile never runs and that shell has no msys toolchain at all — ls, head and env are all missing, which kills the launch line twice over (once directly, once because fm-operational-input.sh's #!/usr/bin/env bash interpreter is gone).

    MSYS matters too: fm_lock_try_create's primitive is ln -s, msys silently copies by default, the copy still carries the owner's pid file, and fm_lock_acquire_wait (no timeout, no bail-out) then spins forever on a lock nothing can take. Measured in isolation:

    with MSYS=winsymlinks:nativestrict    ACQUIRE OK               lock=symlink
    without                               HANG (timeout, rc=124)   lock=absent
    

    That is what makes fm-captain-hold.sh complete hang indefinitely. Related to #3279, but a different mechanism — no PID reuse involved. Note that native symlinks additionally need Developer Mode or elevation; without either, nativestrict at least makes ln -s fail loudly instead of leaving a booby-trapped directory behind.

    The patch hands a new pane SHELL, MSYS and the caller's own PATH, spliced into every pane-creating call: tab create, both workspace create sites, the projection task-tab, and the husk replacement. All five are needed — the projection ones only become reachable once (4) is fixed, and missing them puts cmd.exe back silently.

    4. Teardown refuses because a mode bit cannot exist

    fm_backend_herdr_presentation_lock_namespace_valid requires mode 700. Git for Windows mounts / and /tmp noacl, so a directory always stats 755 regardless:

    $ mount | grep /tmp
    C:/Users/<user>/AppData/Local/Temp on /tmp type ntfs (binary,noacl,posix=0,usertemp)
    
    $ mkdir -m 700 d && stat -c '%a' d
    755
    $ chmod 700 d; echo "chmod rc=$?"; stat -c '%a' d
    chmod rc=0
    755
    

    The check can never pass. Spawn tolerates it (warns, drops the projection); teardown treats it as fatal — session presentation lock could not be resolved — and leaves the pane and the pooled worktree behind.

    Relaxing this needed care. My first attempt kept the ownership assertion and dropped only the mode check, which was wrong: on a noacl mount stat is uninformative in both fields — every directory, /c/Windows and /c/Program Files included, reports the caller's own uid and 755. So the patch skips the mode assertion only when the filesystem provably cannot carry mode bits (probed at runtime, so an acl-mounted path on the same machine keeps the strict check) and re-anchors the guarantee on location: the namespace must sit inside the caller's own temp root, which on Windows is the per-user, ACL-protected %TEMP%.

    Result

    Verified on Windows 11 (10.0.26200), Git for Windows 2.49.0 / msys2 3.5.7, herdr 0.8.0-preview.2026-08-04, firstmate at debe4bf, against a scout task in a throwaway sandbox repo:

    measured
    spawn 25-33s every cycle, well inside the 60s/180s budgets
    crewmate scout-probe.txt committed in the pooled worktree + report written
    teardown pane count, workspace count and worktree pool back to baseline every time, via fm-teardown.sh itself

    4 of 5 cycles passed end to end. The one failure lost its crewmate to an API Error: Connection lost mid-response after it had already committed in the worktree — not a firstmate defect.

    Notes for review

    • Non-Windows behaviour is unchanged throughout: every new branch is gated on cygpath plus a drive-letter shape, and fm_backend_herdr_posix_env prints nothing elsewhere, so no --env is passed and no probe runs.
    • The repo has no .gitattributes; my tree is core.autocrlf=true and the committed blobs contain no CR, but worth a glance.
    • tests/fm-backend-herdr.test.sh has one pre-existing failure on a clean origin/main here — the ambiguity refusal did not name the candidate workspaces (missing: 'w1 w7'). Unrelated to this change, but flagging it since it shows up in any Windows run.
    • bin/fm-lint.sh needs ShellCheck, which I have not installed, so the patch is unlinted.
    • Happy to add tests for the new paths. tests/fm-backend-herdr.test.sh already mocks pane get JSON, so a foreground_cwd-absent case and a canonical_socket_path table would slot straight in.

    The branch is on my fork as trngthnh369/firstmate:windows-herdr-backend (one commit, +220/-7, applies cleanly to debe4bf). Tell me which you prefer — a PR through no-mistakes, the patch inline here, or nothing at all if you would rather solve these differently.

  4. kunchenguid commented on Aug 30, 2026

    @kunchenguid
    Owner

    Speaking as Kun's firstmate: whole thread re-read, including the 2026-08-30T08:55:43Z follow-up. Verified still unfixed on latest main (debe4bfafa2ad52bd96f088a8d8ba418931e828f). No open PR covers this spawn path. Do not open a competing PR from this pass.

    The follow-up expands this from one refusal to four independent defects on the same native-Windows herdr spawn/teardown path (Git for Windows / MSYS2, no WSL). Each is only reachable after the previous one is cleared. Inspected on main:

    1. Socket identity (original title): fm_backend_herdr_canonical_socket_path in bin/backends/herdr.sh still requires /* then dirname/cd/pwd -P. No drive-letter/cygpath conversion. Both launcher HERDR_SOCKET_PATH and session list --json still fail the guard before comparison, matching the original error.
    2. Worktree discovery: fm_backend_herdr_current_path still only reads .result.pane.foreground_cwd. No spawn_discover_path (or zellij-style probe) exists.
    3. Task pane shell: fm_backend_herdr_create_task, projection task-tab create, and husk replacement all call tab create with --cwd/--label/--no-focus only — no SHELL/MSYS/PATH. A herdr pane can still land in cmd.exe so POSIX spawn types into cmd and exits 0. The MSYS ln -s silent-copy hang overlaps Windows: fm_lock_try_create can never succeed without symlink privilege (ln -s silently degrades to a copy) #3267.
    4. Teardown mode bits: fm_backend_herdr_presentation_lock_namespace_valid still requires owner uid AND mode 700. No noacl/runtime probe, so a Git-for-Windows noacl /tmp can never satisfy teardown.

    Related and still distinct: #504 (Windows-herdr intent), #3267 (lock ln -s), #1796 (nested cmd.exe cwd). The author's fork branch trngthnh369/firstmate:windows-herdr-backend is not an open PR against this repo.

    A correct fix must stay fail-closed and Windows-gated so POSIX is unchanged: convert only the drive-letter socket shape when a converter exists, then re-verify POSIX-absolute; keep parent identity a refusal when unverifiable; do not type a probe into a live agent pane on relaunch; do not treat a cmd.exe spawn as success; skip mode 700 only when the filesystem cannot carry mode bits, re-anchored on location inside the caller's temp root.

    Yes — raise through no-mistakes per CONTRIBUTING.md. One PR or four is your call; keep each defect independently fail-closed. Do not skip the gate. Firstmate will not open a competing PR.

    VISION.md per-rule:

    • One captain, one interface: aligns (every spawn refused, or spawn reporting success with no crewmate, is not a usable crew).
    • Authority is explicit and never inferred: aligns (identity and lock stay refusals when unverifiable; no new autonomy).
    • Scripts own the mechanics, agents own the judgment: aligns (path canonicalization, pane env, and mode probe are script mechanics).
    • A restart is a non-event: aligns (teardown that leaves panes and pooled worktrees behind is leftover state after the session ends).
    • Delegation with a spine: aligns (spawn/teardown is the worker-delivery contract).
    • The fleet outlives any vendor: aligns (bind to the socket path and pane fields herdr actually emits, not POSIX pixels).
    • Scope: aligns (backend adapter, not workshop). Direct evidence for Intent: incremental native-Windows support via the Herdr backend #504.

    contract-class: restore (herdr launcher-identity, worktree discovery after treehouse get, POSIX spawn into a usable shell, and teardown lock-namespace are already specified; native Windows makes those promised paths unverifiable or false-success. A PR that changes unconfigured POSIX behavior would be new-default and is not requested here.)

    Keeping ready-for-pr. Expanded scope noted. Still open.

  5. trngthnh369 commented on Aug 31, 2026

    @trngthnh369
    Author

    Second Windows blocker, upstream of the one this issue reports: the session lock can never be acquired

    While verifying the herdr fix above on a real workload I hit a defect that sits earlier in the startup path, and it is arguably the more serious of the two: on Git Bash fm-lock.sh can never acquire the session lock, so every session runs read-only and the Claude Stop auto-arm — which only arms while its own session holds the lock — never fires at all.

    LOCK
    --------------------------------------------------------------------------------
    error: cannot locate harness process in ancestry
    ●  READ-ONLY SESSION - FLEET LOCK OWNERSHIP WAS NOT VERIFIED
    

    There is no fleet operation at all from such a session: no spawn, no steer, no merge, no wake drain.

    Four independent causes, any one of which alone breaks the ancestry walk

    1. Cygwin ps has no -o option. fm_harness_ancestry_pids (bin/fm-session-lock-lib.sh:112) starts with ps -o comm= -p "$pid", and Cygwin's ps (3.5.7) answers:

    $ ps -o comm= -p $$
    ps: unknown option -- o
    $ echo $?
    1
    

    The walk therefore breaks on its first hop. This one fails loudly.

    2. The Cygwin process table lists only MSYS processes. A native harness binary is simply absent from it, and the shell it launched reports PPID 1, so even with a working -o the walk stops at a root that is not the harness. ps -W does list native processes but reports PPID 0 for every one of them, so it cannot rebuild a chain either.

    3. MSYS emulates fork with a fresh Windows process, and Windows never reparents. Every subshell has its own Windows pid while $$ keeps naming the script's shell, and ParentProcessId keeps pointing at a pid that has already exited. So a walk that follows the Windows link from a short-lived subshell steps into a dead pid. This one fails silently — measured here, it turned a working chain into a one-hop chain.

    4. /proc/<pid>/exename carries no trailing newline, so read reports failure after assigning, and the common read ... || var='' idiom discards the value. Also silent.

    There is also a namespace consequence worth stating explicitly, because it is easy to get wrong: Cygwin kill -0 does not resolve a native Windows pid. Measured against a live harness process it returns 1, exactly as it does for a pid that never existed — so kill -0 cannot distinguish a live lock owner from a dead one here, and fm_harness_pid_alive cannot use it.

    A second, independent defect this exposed

    bin/fm-sessionstart-nudge.sh duplicates the ancestry logic instead of using the shared library — its own kill -0 at :30 and its own ps -o ppid= walk at :33. Both are dead on Windows, so lock_is_in_ancestry always returns false and the nudge prints the session-start instruction on every single turn, even while the lock is held. bin/fm-lock.sh:20-23 describes the shared lib as the single owner of this identity contract; the nudge was quietly outside it.

    The shape of the fix I am running

    A new bin/fm-ps-lib.sh becomes the single owner of comm/args/ppid/liveness, and the two call sites route through it. The parts that seem worth your attention as design decisions:

    • Selection is by capability, not platform name. ps -o working wins; only uname saying MSYS/MINGW/CYGWIN and ps -o failing takes the Windows path. This matters for your own tests: tests/fm-session-lock-ancestry.test.sh shadows ps with a fake that does support -o, so the unit layer keeps the original POSIX semantics untouched. Gating on uname alone regressed four assertions in that file; capability gating restores them.
    • On Windows the library speaks WINPID throughout, because a native harness exists only as a WINPID and an ancestry that must reach it cannot be expressed in Cygwin pids.
    • Parent links for MSYS processes come from the Cygwin table, never from Windows, since the Windows one dangles across fork emulation. Identity for those processes is read from /proc/<cyg>/exename and cmdline, so a harness that runs as a script under MSYS is not hidden behind a generic bash.exe — that matters for the node-hosted adapters, and FM_HARNESS_RE's anchored ^pi$ / ^pi-signed$ alternatives are the case where path-vs-basename is decisive.
    • The Windows table is crossed into only once the Cygwin chain is exhausted, at the outermost MSYS process, whose Windows parent is real.

    Cost, because this runs at every turn end

    Making the walk actually work puts fm_harness_process_matches on the Stop hook's hot path for the first time on this platform, and under MSYS each fork is a real Windows process (~85 ms here). The first working version cost 8.5–15 s per walk. Four changes brought it to 2.4 s:

    • builtins instead of basename and grep in fm_harness_process_matches (verified semantically identical against the current implementation across 14 probe cases, including the anchored pi alternatives in both bare and path form);
    • one combined per-hop lookup that assigns to caller variables instead of three command substitutions;
    • PowerShell returning the ancestry of one pid rather than the whole machine (10 rows / 3.7 KB instead of ~340 rows / 83 KB);
    • fetching that snapshot lazily, from inside the hop lookup, so an ancestry that never leaves MSYS spawns no PowerShell at all.

    That last one is order-dependent: lazy fetching was slower before the combined lookup existed, because the fallback then ran inside a command substitution and re-snapshotted on every read.

    On the remaining test failures — please do not read them as an incomplete patch

    Against tests/fm-session-lock-ancestry.test.sh, fm-claude-stop-autoarm.test.sh, fm-sessionstart-nudge.test.sh and fm-watcher-lock.test.sh, the patched tree is assertion-for-assertion identical to the unpatched tree on this machine: 22 ok, 4 not ok, same four. Nothing that passed before fails now.

    Two of those four are POSIX assumptions in the fixtures rather than implementation bugs:

    • tests/fm-sessionstart-nudge.test.sh:129 writes printf '%s\n' "$$" into state/.lock. On Windows a valid lock holds a WINPID, and a Cygwin pid is not one. Accepting both namespaces is not safe — the two numbering spaces can collide on the same integer while naming different processes.
    • The ancestry e2e fixture builds its harness as ln -s /bin/bash "$FAKEBIN/claude", i.e. an MSYS process.

    I have not touched either fixture; making them platform-aware is your call, and I did not want to change tests to make my own patch look green.

    What is verified here

    Lock acquires and reports held by live harness pid <n> from a separate shell; fm_session_lock_owned_by_self passes; liveness correctly accepts the harness and rejects both a dead pid and a live non-harness process; session start no longer prints the read-only banner and hands supervision to the Stop-owned auto-arm; the nudge is correctly silent while the lock is held.

    Not verified: the ancestry was only exercised under a herdr-hosted chain (pwsh → herdr → herdr → pwsh → cmd → claude); a session launched directly from a terminal has a different shape, including possibly the nested worker chain the library's own comment describes. And the auto-arm is only proven past its identity gate — need_supervision correctly keeps it inert with an empty fleet, so "it arms a watcher end to end" is still untested.

    Same offer as before: happy to run this through the no-mistakes gate and raise a proper PR, as one change or split from the herdr work, whichever you prefer. If you would rather these Windows differences not land upstream at all, that is a useful answer too — it tells me to stop maintaining a fork.

  6. kunchenguid commented on Aug 31, 2026

    @kunchenguid
    Owner

    Speaking as Kun's firstmate: re-read whole thread including the 2026-08-30 expanded four-defect follow-up and the 2026-08-31 session-lock writeup. Verified still unfixed on latest main 4ad8cbae.

    fm_backend_herdr_canonical_socket_path in bin/backends/herdr.sh still requires /* POSIX-absolute then dirname/cd/pwd -P. Native Windows herdr drive-letter sockets on either side of the launcher-identity check still fail closed before comparison.

    Open PR #2918 (3min-lang) already converts drive-letter shapes via cygpath -u before identity comparison — the same fail-closed shape this issue's local patch proposes. Removing ready-for-pr and leaving this issue open against #2918 rather than inviting a second socket-path PR.

    Newer activity notes:

    VISION.md per-rule:

    • One captain, one interface: aligns (every herdr spawn refused is not a usable crew).
    • Authority is explicit and never inferred: aligns (identity stays a refusal when unverifiable; no new autonomy).
    • Scripts own the mechanics, agents own the judgment: aligns (path canonicalization is script mechanics).
    • A restart is a non-event: cannot tell for the socket fix alone (session-lock follow-up is Windows/Git Bash: session lock always refuses - harness ancestry cannot be resolved #3396).
    • Delegation with a spine: aligns (spawn is the worker-delivery contract).
    • The fleet outlives any vendor: aligns (bind to the socket path herdr actually emits, not POSIX pixels).
    • Scope: aligns (backend adapter, not workshop). Direct evidence for Intent: incremental native-Windows support via the Herdr backend #504.

    contract-class: restore (launcher-identity is supposed to compare two spellings of the same socket; the POSIX-only guard makes a same-socket Windows pair unverifiable).

  7. removed
    ready-for-prTriage: real bug or VISION-aligned feature, open for a PR
    on Aug 31, 2026
  8. devin-ai-integration commented on Sep 24, 2026

    @devin-ai-integration
    No description provided.
  9. cr101 commented on Oct 25, 2026

    @cr101
    Contributor

    @trngthnh369, do you still plan to raise your windows-herdr-backend patch (d17e85f) as a PR? Asking before anyone else picks it up.

    On current main, the open PRs #2918 and #5419 fix only the socket path (1). Folder discovery (2), the pane shell (3) and the mode-700 teardown check (4) are still unfixed, and your commit is the only fix for those three I could find. It is now about 540 commits behind main.

    I also checked Herdr 0.9.5, the latest release, from its source. None of the four blockers changed there. foreground_cwd is still always empty on Windows, a pane's cwd is still its own shell's folder, panes still start PowerShell unless [terminal] default_shell is set, and the per-pane env still reaches the pane process. So your active probe and your SHELL/MSYS/PATH approach should still hold. I have not run your branch against 0.9.5.

    If you'd rather not continue, would you be OK with someone rebasing it onto main and opening the PR with credit to you?

  10. kunchenguid commented on Oct 25, 2026

    @kunchenguid
    Owner

    Speaking as Kun's firstmate: thanks @cr101 for the careful Herdr 0.9.5 source check, and for asking @trngthnh369 before picking anything up — that's exactly the right order.

    Re-checked on latest main 3c1e4684: still unfixed. fm_backend_herdr_canonical_socket_path in bin/backends/herdr.sh still refuses anything that isn't /*, and fm_backend_herdr_current_path still reads only .result.pane.foreground_cwd. Nothing has landed for the pane shell or the mode-700 teardown check either.

    Where it stands:

    Nothing is waiting on Kun here.

    VISION.md (unchanged from the earlier verdict): aligns on every rule. This is backend-adapter mechanics that let the fleet run on another platform (#504), and identity stays a refusal whenever it can't be verified.
    contract-class (socket fix): restore.

  11. cr101 commented on Nov 5, 2026

    @cr101
    Contributor

    Since there has been no reply here in about two weeks, I'm picking up @trngthnh369's windows-herdr-backend fix (d17e85f) for defects 2-4: folder discovery, the pane shell, and the mode-700 teardown check. It is rebased onto current main, with @trngthnh369 credited as co-author on each commit carrying their work.

    The socket path (1) stays with #2918 / #5419 and is not included; the PR will note that dependency. I checked #2918 and #5419 first, and neither touches 2-4.

    Per the conditions above, it goes through git push no-mistakes, every change is Windows-gated and fail-closed with POSIX unchanged, and it carries live Windows evidence (Herdr 0.9.3 in an isolated lab session: Git Bash pane spawn plus cleanup passing, and a PowerShell pane refused before launch). @trngthnh369, if you would rather raise it yourself, say so and I'll step back.

  12. kunchenguid commented on Nov 5, 2026

    @kunchenguid
    Owner

    Speaking as Kun's firstmate: thanks @cr101 for picking up @trngthnh369's branch with co-author credit, and for checking #2918 and #5419 before you built.

    Still unfixed on latest main 840cebed. Open PRs that now cover this issue:

    Please carry on in those PRs rather than opening another one. Nothing here is waiting on the reporters or on Kun.

    VISION.md: my earlier per-rule verdict still holds. Everything aligns, because these are backend-adapter mechanics that let the fleet run on native Windows (#504), and identity is still refused whenever it can't be verified. contract-class for the socket fix: restore.

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

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions