fix(lock): acquire the session lock on Windows/Git Bash - #3023
seanofabob wants to merge 1 commit into
Conversation
The per-home session lock never acquired under Git Bash/MSYS, forcing every Windows session into read-only mode. Three layered platform defects caused it: - The harness-ancestry walk used `ps -o comm=` / `-o ppid=`, which the MSYS ps bundled with Git Bash rejects, so the walk failed on its first call. Add a native Windows path that reads the process tree from a single Win32_Process query and climbs Windows PIDs, handing the same (comm, args) evidence to the existing harness-identity contract. - MSYS fork emulation orphans spawned scripts: a child's Win32 parent pid names an already-exited launcher, so no parent-chain walk can reach the harness. Anchor the walk on CLAUDE_PID, which Claude Code exports to every descendant and which survives the orphaning, validated against a live harness process and falling back to the honest walk when absent or stale. - fm_lock_try_create builds each lock with `ln -s`, which MSYS silently turns into a copy, so fm_lock_acquire_wait spun forever. Request winsymlinks:nativestrict so ln -s makes a real NTFS symlink. The Windows paths are gated on OSTYPE/FM_FORCE_WINDOWS and leave the POSIX paths byte-for-byte unchanged. Adds cross-platform regression tests that inject a deterministic Windows process table and assert the anchor, orphan-rescue, parent-walk, fail-closed, and native-symlink-request behavior on any host.
Confidence Score: 4/5The PR should not merge until existing non-strict winsymlinks policies are handled without leaving Windows lock acquisition in an infinite retry. The new policy guard preserves Windows symlink modes that can still copy the lock target, so the lock primitive fails validation and its unbounded acquisition loop never completes. Files Needing Attention: bin/fm-wake-lib.sh and tests/fm-wake-queue.test.sh Reviews (1): Last reviewed commit: "fix(lock): acquire the session lock on W..." | Re-trigger Greptile |
| case "${OSTYPE:-}" in | ||
| msys*|mingw*|cygwin*) | ||
| case "${MSYS:-}" in | ||
| *winsymlinks*) : ;; |
There was a problem hiding this comment.
Non-strict policy preserves lock failure
If MSYS already selects winsymlinks:native, winsymlinks:copy, winsymlinks:deepcopy, or winsymlinks:lnk, this branch preserves a mode that can copy the lock target instead of creating a native symlink. Lock validation then fails and fm_lock_acquire_wait retries indefinitely, leaving the session unable to acquire fleet locks.
|
Speaking as Kun's firstmate: first look on current class=corrective. Windows/Git Bash session lock never acquired: MSYS VISION.md (inspected
This HEAD: First-time fork (seanofabob has no other firstmate PRs; Behavior/Lint/CI had not run — Greptile-only does not count). Approved after full diff review. Workflows this pass: CI 32813622208, Require no-mistakes 32813622260. Not green at comment time. Security: none that blocks CI approval. Local WMI process listing; test-only Overlap / standing hold: lock #2437 (OPEN) / #2839 (OPEN) / issue #2883 (OPEN). Do not squash a third lock PR. Not spawn-freshen / herdr. Land-eligible rec: NO. Captain-flag NOW: no. This is a captain-decision hold on the lock family (which mechanism, if any), not waiting-on-author for that product call. Fork CI was approved this pass and is not yet green. Not a merge I will recommend. |
Problem
On Windows / Git Bash (MSYS), the per-home session lock never acquires, so every session is forced into permanent read-only mode. Three layered platform defects combine to cause it:
Harness-ancestry walk uses unsupported
psflags.fm_harness_ancestry_pidsreads the process tree withps -o comm= -pandps -o ppid= -p. The MSYSpsbundled with Git Bash rejects-o("unknown option -- o"), so the walk fails on its first call andfm_harness_ancestry_pidnever resolves the harness.Spawned scripts are orphaned from the harness. MSYS emulates
fork()through a helper that then exits, so a script launched as a child (session-start spawnsfm-lock.sh) has a Win32 parent pid pointing at an already-dead launcher. No parent-chain walk can climb from it toclaude.exe, even with a native process query.The symlink-based lock primitive silently copies.
fm_lock_try_createbuilds every lock withln -s. MSYS's defaultln -scopies the target instead of creating a symlink, sofm_lock_points_to_ownernever validates andfm_lock_acquire_waitspins forever.Fix
fm-session-lock-lib.sh): read the process tree from a singleWin32_Processquery and climb Windows PIDs, handing the same(comm, args)evidence to the existingfm_harness_process_matchescontract.CLAUDE_PIDanchor: Claude Code exports its own pid to every descendant; it survives the orphaning. Anchor the walk there when it names a live harness, falling back to the honest parent-chain walk when absent or stale.fm-wake-lib.sh): requestwinsymlinks:nativestricton Windows soln -screates a real NTFS symlink (needs Developer Mode /SeCreateSymbolicLink) instead of copying.All Windows behavior is gated on
OSTYPE/FM_FORCE_WINDOWS; the POSIX paths are byte-for-byte unchanged.Tests
tests/fm-session-lock-ancestry.test.sh: new cases inject a deterministic Windows process table (FM_WIN_PROCTABLE_CMD/FM_WIN_SELF_WINPID/FM_FORCE_WINDOWS) and assert anchor resolution, orphan rescue, parent-walk resolution, fail-closed on an invalid/stale anchor, and that an unrelated harness never owns the lock. Existing POSIX cases are pinned to the POSIX path so they stay host-independent; the POSIX-pse2e fixtures skip on hosts withoutps -o.tests/fm-wake-queue.test.sh: asserts a Windows source requests native symlinks, preserves an operator's explicit policy, and is a no-op off Windows.Verified on Windows 10 / Git Bash: the lock now acquires (
lock acquired: harness pid <n>), holds idempotently, and reports correct status; the new tests pass, and shellcheck (0.11.0, LF-normalized) is clean.🤖 Generated with Claude Code