Repository navigation
fix: restore Windows daemon support (PR #163 regression + new fixes) - #274
Conversation
…fixes) The v0.3 refactor to CompozyOS lost the Windows platform-specific files merged in PR compozy#163 (fix: add Windows daemon support). Reproduces the original issues on Windows 11 with the v0.3.0-beta.2 release binary: Original PR compozy#163 regressions re-fixed: - compozy#148 daemon start fails: "another process has locked a portion of the file" (LockFileEx byte-range lock prevents writeLockPID) - compozy#170 same, root-caused as LockFileEx conflict with in-process write - compozy#157 daemon start fails: "invalid uri authority: C:" (sqliteDSN passes drive-letter path to url.URL which serialises as file://C:/... instead of file:///C:/...) - v0.3 also removed the sync_dir platform split — syncDir on info.go now fails on Windows because FlushFileBuffers requires a writable handle Additional Windows-only bugs discovered during validation: - CREATE_NEW_PROCESS_GROUP was missing from detached_windows.go spawnDetachedLoggedProcess. Unix uses Setpgid: true (detached_unix.go:58) but the Windows sibling forgot the equivalent, so the daemon inherits the parent CLI's console group and dies on parent exit. - procutil.StartedAt crashed with ACCESS_VIOLATION (0xc0000005) inside GetProcessTimes when passed nil pointers for exit/kernel/user times on some Windows builds. Pass valid Filetime pointers for all four outputs. Files: - internal/daemon/lock_pid_other.go (NEW): identity lockPIDPath for Unix - internal/daemon/lock_pid_windows.go (NEW): .pid sidecar for Windows - internal/daemon/sync_dir_unix.go (NEW): syncDir extracted from info.go - internal/daemon/sync_dir_windows.go (NEW): syncDir no-op for Windows - internal/daemon/lock.go: use lockPIDPath() at 3 call sites - internal/daemon/info.go: remove syncDir (moved to platform files) - internal/procutil/detached_windows.go: set CREATE_NEW_PROCESS_GROUP - internal/procutil/process_started_at_windows.go: pass valid Filetime pointers to GetProcessTimes, guard against invalid handles - internal/store/sqlite_config.go: add sqliteFilePath helper that prefixes drive-letter absolute paths with '/' so the URI serialises correctly - internal/store/sessiondb/read_only.go: same drive-letter normalisation inline (package boundary) Zero behaviour change on Unix / Linux / macOS — every fix is behind a //go:build windows tag or an identity function on non-Windows. Validated end-to-end on Windows 11 (build 26200) with go 1.26.5: - compozy daemon start succeeds - compozy daemon status reports "running" with PID + HTTP + UDS - compozy doctor -o json returns diagnostics via the daemon socket
|
@lyonzin is attempting to deploy a commit to the Compozy Team on Vercel. A member of the Team first needs to authorize it. |
WalkthroughThe PR splits daemon directory syncing and PID persistence by platform, isolates detached Windows processes from parent console signals, hardens Windows process-time retrieval, and normalizes filesystem paths used in SQLite file URLs. ChangesPlatform compatibility updates
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches 💡 2⚔️ Resolve merge conflicts 💡
🛠️ Fix failing CI checks 💡
🧪 Generate unit tests (beta)
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. Comment |
| // other file handles — even within the same process — from writing to the | ||
| // locked file. A separate .pid sidecar avoids that conflict. | ||
| func lockPIDPath(lockPath string) string { | ||
| return lockPath + ".pid" |
There was a problem hiding this comment.
Windows stale-lock test uses wrong path
When the daemon tests run on Windows, TestAcquireLockReclaimsStalePID writes the stale PID to the lock file while acquireLock now reads the .pid sidecar, causing the stale-PID assertion to fail in the Windows verification lane.
Context Used: CLAUDE.md (source)
Knowledge Base Used: Kernel and Daemon Lifecycle
|
| Filename | Overview |
|---|---|
| internal/daemon/lock.go | Routes PID persistence through a platform-specific path while retaining the original file as the singleton lock. |
| internal/daemon/lock_pid_windows.go | Selects a .pid sidecar for Windows daemon PID persistence. |
| internal/daemon/sync_dir_windows.go | Avoids unsupported directory-handle synchronization on Windows. |
| internal/procutil/detached_windows.go | Starts detached Windows processes in a new process group. |
| internal/procutil/process_started_at_windows.go | Validates the process handle and supplies all required GetProcessTimes output buffers. |
| internal/store/sqlite_config.go | Canonicalizes absolute drive-letter paths before encoding SQLite file URLs. |
| internal/store/sessiondb/read_only.go | Reuses the shared SQLite path normalization for read-only session databases. |
Reviews (2): Last reviewed commit: "refactor: export SqliteFilePath and reus..." | Re-trigger Greptile
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
internal/store/sqlite_config.go (2)
39-43: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winAdd regression coverage for the platform-specific URL contract.
Cover absolute drive-letter paths, relative paths, and any supported UNC or extended-path forms, asserting the exact SQLite DSN serialization. The PR currently documents manual Windows validation but no automated coverage.
🤖 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 `@internal/store/sqlite_config.go` around lines 39 - 43, Add regression tests for sqliteFilePath covering absolute drive-letter, relative, and supported UNC or extended Windows paths, asserting the exact SQLite DSN serialization produced by each case. Use platform-appropriate test handling so the contract is verified consistently without changing sqliteFilePath itself.
31-45: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winCentralize Windows SQLite path normalization.
The same drive-letter conversion is implemented twice, allowing platform fixes to diverge.
internal/store/sqlite_config.go#L31-L45: makesqliteFilePaththe shared implementation or move it to a shared internal helper.internal/store/sessiondb/read_only.go#L184-L194: replace the local normalization block with that helper.🤖 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 `@internal/store/sqlite_config.go` around lines 31 - 45, Centralize Windows SQLite path normalization in sqliteFilePath, preserving its handling of absolute drive-letter and relative paths. In internal/store/sqlite_config.go:31-45, keep or move sqliteFilePath as the shared implementation; in internal/store/sessiondb/read_only.go:184-194, remove the duplicate local normalization and call the shared helper instead.
🤖 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.
Inline comments:
In `@internal/daemon/lock.go`:
- Around line 75-78: Synchronize the Windows daemon.lock.pid sidecar updates so
readLockPID cannot observe an empty or partially written value while
writeLockPID or Release modifies it. Use a dedicated short-lived sidecar lock or
an atomic replace protocol across the acquire, write, and release paths,
preserving stale-process cleanup and ErrAlreadyRunning behavior. Add coverage
for interrupted and concurrent acquire/release scenarios.
In `@internal/daemon/sync_dir_windows.go`:
- Around line 5-10: Update the Windows syncDir implementation to provide a
supported directory-flush path equivalent to the Unix behavior, or explicitly
document and test the weaker durability guarantee when syncDir remains a no-op.
Ensure WriteInfo’s success semantics and restart-after-crash behavior reflect
the selected durability contract.
---
Nitpick comments:
In `@internal/store/sqlite_config.go`:
- Around line 39-43: Add regression tests for sqliteFilePath covering absolute
drive-letter, relative, and supported UNC or extended Windows paths, asserting
the exact SQLite DSN serialization produced by each case. Use
platform-appropriate test handling so the contract is verified consistently
without changing sqliteFilePath itself.
- Around line 31-45: Centralize Windows SQLite path normalization in
sqliteFilePath, preserving its handling of absolute drive-letter and relative
paths. In internal/store/sqlite_config.go:31-45, keep or move sqliteFilePath as
the shared implementation; in internal/store/sessiondb/read_only.go:184-194,
remove the duplicate local normalization and call the shared helper instead.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 3050141f-be7e-40fe-b58f-b4989911635a
📒 Files selected for processing (10)
internal/daemon/info.gointernal/daemon/lock.gointernal/daemon/lock_pid_other.gointernal/daemon/lock_pid_windows.gointernal/daemon/sync_dir_unix.gointernal/daemon/sync_dir_windows.gointernal/procutil/detached_windows.gointernal/procutil/process_started_at_windows.gointernal/store/sessiondb/read_only.gointernal/store/sqlite_config.go
💤 Files with no reviewable changes (1)
- internal/daemon/info.go
| priorPID, err := readLockPID(lockPIDPath(cleanPath)) | ||
| if err != nil { | ||
| return nil, err | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== files =="
git ls-files | rg '(^|/)lock\.go$|internal/daemon' || true
echo "== lock.go outline =="
ast-grep outline internal/daemon/lock.go --view compact || true
echo "== lock.go relevant sections =="
sed -n '1,220p' internal/daemon/lock.go | cat -nRepository: compozy/compozy
Length of output: 30959
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== pid files =="
for f in internal/daemon/lock_pid_windows.go internal/daemon/lock_pid_other.go; do
if [ -f "$f" ]; then
echo "--- $f ---"
cat -n "$f"
fi
done
echo "== usages of StalePID/ErrAlreadyRunning/l.Path/Release =="
rg -n "StalePID|ErrAlreadyRunning|errAlreadyRunning|Lock\\(|Release\\(|\.Path\(\)|\.Path\(\)" internal/daemon internal -g '*.go' | head -200
echo "== tests mentioning pid/writeLock/readLock/already running =="
rg -n "pid|writeLockPID|readLockPID|already running|alreadyRunning|StalePID|AcquireLock|Release|\\.pid" internal/daemon -g '*test.go' -g '*.go' | head -300Repository: compozy/compozy
Length of output: 40251
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== daemon lock tests around stale pid and existing lock =="
sed -n '1,260p' internal/daemon/daemon_test.go | cat -n
echo "== Windows-specific references =="
rg -n "windows|\\.pid|writeLockPID|readLockPID|AcquireLock\\(" internal/daemon -g '*.go' | rg 'windows|\\.pid|writeLockPID|readLockPID|AcquireLock\\(' || trueRepository: compozy/compozy
Length of output: 10538
🌐 Web query:
Windows flock Go LockFileEx byte range file lock writes same file locked bytes
💡 Result:
On Windows, the LockFileEx API provides mandatory byte-range locking rather than advisory locking [1]. When you use LockFileEx, you are locking a specific range of bytes defined by an offset and a length [2][3]. Key behavior details: - Mandatory Locking: Unlike many POSIX systems where file locks are often advisory, Windows byte-range locks are mandatory [1]. Any process attempting to read from or write to the locked range will receive an error (typically ERROR_LOCK_VIOLATION or 33), even if that process does not use locks itself [1]. - Range Specification: The lock covers a specific byte range starting at the offset provided in the OVERLAPPED structure [2][3]. The length of the range is specified by the nNumberOfBytesToLockLow and nNumberOfBytesToLockHigh parameters [3]. To lock the entire file, you must specify a range that covers all possible bytes, often by setting these parameters to 0xFFFFFFFF (MAXDWORD) [4]. - Overlaps: Exclusive locks cannot overlap an existing locked region of a file [3]. - Go Implementation: In Go, the syscall or golang.org/x/sys/windows packages are used to invoke LockFileEx [5][6]. Standard Go library code (such as that used in the cmd/go tool) typically manages locking by passing an OVERLAPPED structure with a zero offset and a length of all bytes (represented as ^uint32(0)) to effectively lock the entire file [5]. When using these locks to manage writes, the lock prevents other processes from accessing the locked byte range [2]. If a process holds an exclusive lock on a range, it is guaranteed that no other process can perform I/O within that specific range [2][1]. If you require protection for the entire file, you should ensure the locked range encompasses all data you intend to write [4].
Citations:
- 1: https://www.windowssyscalls.com/en/syscalls/nt-lock-file
- 2: https://learn.microsoft.com/en-us/windows/win32/fileio/locking-and-unlocking-byte-ranges-in-files
- 3: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-lockfileex
- 4: https://stackoverflow.com/questions/25382887/locking-entire-file-using-lockfileex-api-in-windows
- 5: https://github.com/golang/go/blob/master/src/cmd/go/internal/lockedfile/internal/filelock/filelock_windows.go
- 6: golang/go@3a0d609
Synchronize or atomically replace the Windows PID sidecar.
On Windows, the daemon PID is written to daemon.lock.pid, an unlocked sidecar. readLockPID observes that file before TryLock, while writeLockPID truncates/replaces it and Release zeroes it while holding only the main lock. A concurrent/acquired-contender or crashed release can read an empty or partially written PID, which turns a stale daemon into ErrAlreadyRunning without a PID or skips stale-process cleanup. Protect the .pid sidecar with its own short-lived lock or use an atomic update protocol, and cover interrupted/concurrent acquire and release paths.
🤖 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 `@internal/daemon/lock.go` around lines 75 - 78, Synchronize the Windows
daemon.lock.pid sidecar updates so readLockPID cannot observe an empty or
partially written value while writeLockPID or Release modifies it. Use a
dedicated short-lived sidecar lock or an atomic replace protocol across the
acquire, write, and release paths, preserving stale-process cleanup and
ErrAlreadyRunning behavior. Add coverage for interrupted and concurrent
acquire/release scenarios.
| // syncDir is a no-op on Windows: FlushFileBuffers requires write access on | ||
| // directory handles, but os.Open opens them read-only. Directory entry | ||
| // durability after an atomic rename is handled by the NTFS journal. | ||
| func syncDir(_ string) error { | ||
| return nil | ||
| } |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== locate files =="
git ls-files | rg '(^|/)sync_dir_windows\.go$|(^|/)sync_dir.*\.go$|internal/daemon|daemon\.json|WriteInfo' || true
echo
echo "== file excerpts =="
for f in $(git ls-files | rg '(^|/)sync_dir_.*\.go$'); do
echo "--- $f"
wc -l "$f"
cat -n "$f"
done
echo
echo "== WriteInfo references =="
rg -n "\bWriteInfo\b|syncDir\(|FlushFileBuffers|os\.Open.*daemon|daemon\.json" internal app cmd website packages 2>/dev/null || true
echo
echo "== changed files summary =="
git diff --stat || true
git diff -- internal/daemon/sync_dir_windows.go internal/daemon 2>/dev/null | sed -n '1,220p' || trueRepository: compozy/compozy
Length of output: 31998
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== internal/daemon/info.go =="
cat -n internal/daemon/info.go
echo
echo "== internal/fileutil atomic directories =="
for f in internal/fileutil/atomic.go internal/fileutil/atomic_dirsync_unix.go internal/fileutil/atomic_dirsync_windows.go; do
if [ -f "$f" ]; then
echo "--- $f"
wc -l "$f"
cat -n "$f"
fi
done
echo
echo "== daemon WriteInfo call sites =="
rg -n -C3 '\bWriteInfo\s*\(' internal/daemon | head -n 220Repository: compozy/compozy
Length of output: 17928
🌐 Web query:
Microsoft FlushFileBuffers directory handle documentation write access requirement
💡 Result:
The FlushFileBuffers function in the Windows API requires the provided handle to have GENERIC_WRITE access rights [1][2]. This requirement applies generally to any handle passed to the function, including handles to files and volumes [1][2]. While official Microsoft documentation explicitly states that FlushFileBuffers accepts handles to files and volumes, it does not document the use of directory handles with this function [1][3]. Although developers may sometimes attempt to use directory handles obtained via CreateFile with the FILE_FLAG_BACKUP_SEMANTICS flag, the behavior of FlushFileBuffers with such handles is not an officially supported or guaranteed operation [3][4]. If you are using FlushFileBuffers for a file or volume, ensure that the handle was opened with at least GENERIC_WRITE access [1][2]. If the function is called with a handle lacking the necessary write permissions, it will fail [5].
Citations:
- 1: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-flushfilebuffers
- 2: https://github.com/MicrosoftDocs/sdk-api/blob/docs/sdk-api-src/content/fileapi/nf-fileapi-flushfilebuffers.md
- 3: https://stackoverflow.com/questions/56661972/windows-flushfilebuffers-system-call-fails-on-network-mapped-drive
- 4: https://learn.microsoft.com/en-us/windows/win32/fileio/obtaining-a-handle-to-a-directory
- 5: https://groups.google.com/g/microsoft.public.win32.programmer.kernel/c/lGiXEO_bp2E
Treat the Windows directory flush as unsupported.
The Windows side is currently no-op, so WriteInfo can return success even though the directory metadata has not been surfaced like the Unix path does. Add a supported Windows directory flush path or document this as a weaker durability guarantee and cover restart-after-crash behavior.
🤖 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 `@internal/daemon/sync_dir_windows.go` around lines 5 - 10, Update the Windows
syncDir implementation to provide a supported directory-flush path equivalent to
the Unix behavior, or explicitly document and test the weaker durability
guarantee when syncDir remains a no-op. Ensure WriteInfo’s success semantics and
restart-after-crash behavior reflect the selected durability contract.
|
@lyonzin soon as you solve the reviews issues and CI checks here I can merge this 🙏🏻 thanks for the work |
CodeRabbit review on PR compozy#274 flagged the duplicate Windows drive-letter normalization in internal/store/sessiondb/read_only.go — the same logic was inlined that already lives in internal/store/sqlite_config.go under the unexported sqliteFilePath helper. Change: - Rename sqliteFilePath -> SqliteFilePath (exported) so sibling packages can reuse the canonical normalization. - Update internal call sites (sqliteReadOnlyDSN, sqliteDSNWithPragmas) to the new name. - In sessiondb/read_only.go, drop the inlined slash-prefix branch and call store.SqliteFilePath(path) instead. Also removes the now-unused path/filepath import. Behaviour is byte-identical: the exported helper contains exactly the same body as the previous unexported one. Windows drive-letter paths still serialize as file:///C:/... and non-absolute paths remain untouched.
|
Thanks for the quick review @pedronauck! Update after CI feedback: CodeRabbit review addressedCommit 4da6e16c — The Windows drive-letter normalization is now centralized:
Byte-identical behaviour — only one canonical implementation now.
|
There was a problem hiding this comment.
🧹 Nitpick comments (1)
internal/store/sqlite_config.go (1)
31-45: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winAdd regression tests for Windows SQLite path serialization.
Cover drive-letter paths, UNC paths, POSIX absolute paths, and relative paths, and assert the final DSNs produced by both builders—not only the helper output. This is the core Windows compatibility fix and should be exercised on a Windows test runner.
🤖 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 `@internal/store/sqlite_config.go` around lines 31 - 45, Add regression tests covering drive-letter, UNC, POSIX absolute, and relative paths, verifying the complete SQLite DSNs produced by both builders that use SqliteFilePath rather than only testing the helper. Ensure the cases run correctly on Windows, including expected file:// serialization and existing relative-path behavior.
🤖 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 `@internal/store/sqlite_config.go`:
- Around line 31-45: Add regression tests covering drive-letter, UNC, POSIX
absolute, and relative paths, verifying the complete SQLite DSNs produced by
both builders that use SqliteFilePath rather than only testing the helper.
Ensure the cases run correctly on Windows, including expected file://
serialization and existing relative-path behavior.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: d6fa59f4-6b26-4dcf-a6d7-a27eb586bb94
📒 Files selected for processing (2)
internal/store/sessiondb/read_only.gointernal/store/sqlite_config.go
🚧 Files skipped from review as they are similar to previous changes (1)
- internal/store/sessiondb/read_only.go
Problem
The v0.3 refactor to CompozyOS silently lost the Windows platform-specific files merged in PR #163 (which itself closed issues #148, #157, #170). Reproducing on Windows 11 with the current beta binary:
This is identical to the failure BrunoCanto reported in #148 on 2026-05-08, resolved in PR #163 on 2026-05-26, and then regressed sometime during the v0.3 refactor.
Root causes
Working through the failures end-to-end on Windows 11 (build 26200) with
go 1.26.5surfaced six distinct bugs — the four originally addressed by PR #163 plus two new Windows-only bugs that only became reachable once the daemon booted past the lock.write daemon.lock: another process has locked a portion of the fileLockFileExbyte-range lock blocks writes from every handle in the same process, including the daemon's ownwriteLockPID.store: initialize sqlite database …: invalid uri authority: C:sqliteDSNcallsfilepath.ToSlashon an absolute drive-letter path and hands it tourl.URL, which serialises asfile://C:/...— SQLite parsesC:as the network authority.sync directory "…": Access is deniedsyncDirininfo.gocallsdir.Sync()on a read-only handle; on WindowsFlushFileBuffersrequires a writable handle.spawnDetachedLoggedProcesson Windows does not setCREATE_NEW_PROCESS_GROUP, so the daemon inherits the parent's console group and receives the same signals. Unix usesSetpgid: truefor the same effect (detached_unix.go:58).Exception 0xc0000005 (ACCESS_VIOLATION)insidewindows.GetProcessTimesprocutil.StartedAtpassesnilfor the exit/kernel/user time outputs. Depending on the Windows build, the kernel dereferences these pointers and segfaults.store.migrations.refused stream=global reason=sum_mismatch(not fixed by this PR)core.autocrlf=trueto.sqlfiles; the embeddedatlas.sumwas hashed with LF line endings on the Linux CI. Any local build on Windows fails migration validation..gitattributespolicy discussion; documented under "Follow-up"Changes
10 files, +106 / -25, all Windows-only or additive.
New files (all guarded by build tags)
internal/daemon/lock_pid_other.go— identitylockPIDPath()for non-Windowsinternal/daemon/lock_pid_windows.go— returnslockPath + \".pid\"so PID persists in a sidecar, avoiding the LockFileEx conflictinternal/daemon/sync_dir_unix.go— extracted frominfo.go, preserves the existingreturnErrpattern the v0.3 refactor addedinternal/daemon/sync_dir_windows.go— no-op; NTFS journal guarantees durability after atomic renameModified files
internal/daemon/lock.go— 3 call sites route throughlockPIDPath()internal/daemon/info.go— drop the inlinesyncDir(moved to platform files)internal/procutil/detached_windows.go—SysProcAttr{CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP}internal/procutil/process_started_at_windows.go— pass validFiletimepointers for all four outputs; guard against invalid handleinternal/store/sqlite_config.go— newsqliteFilePathhelper that prefixes drive-letter absolute paths with/internal/store/sessiondb/read_only.go— same drive-letter normalisation inline (package boundary makes reuse awkward for two lines)Impact on Unix / Linux / macOS
Zero behavioural change.
lockPIDPathon non-Windows isreturn lockPath(identity)sqliteFilePathprefix branch never fires on Unix becausefilepath.ToSlashalready yields a leading/sync_dir_unix.gois the exact function that used to live ininfo.go, just relocated//go:build windowsor//go:build !windowsUnix regression test coverage is unchanged.
Validation
Manual end-to-end on Windows 11 (build 26200),
go 1.26.5,github.com/gofrs/flock v0.13.0:compozy daemon startsucceeds, the daemon accepts connections on the UDS socket, andcompozy doctorreaches the daemon API. Diagnostics still returnstatus: errorin the summary because unrelated defaults are missing on a fresh install — that is expected for a barecompozy installand out of scope.Cross-build
GOOS=windows GOARCH=amd64 go build ./cmd/compozycompiles cleanly, andgo build ./internal/daemon/...passes on Linux/macOS as well (no import churn on the Unix paths).Full
make verifywas not run locally — the fork'satlas.summismatch (bug #6) blocks the pre-migration validate step until.gitattributespins the SQL line endings or the sum policy is relaxed. CI on this PR will run the real Windows gate.Follow-up (not in this PR)
The
atlas.summismatch when building on Windows is a real papercut for any Windows contributor — the checksum is computed against LF-encoded SQL but Git for Windows checks the files out as CRLF by default. The clean fix is:I did not include it in this PR because it touches every SQL file's canonical line ending and deserves its own review. Happy to open a separate PR for it if you want.
Notes
fix: <description>).chore:/ AI-attribution trailers.Summary by CodeRabbit
file://DSN formatting for absolute Windows paths using consistent path normalization.