main
md 98 lines 3.73 KB
Rendered Raw
1 ---
2 name: "windows-compatibility"
3 description: "Cross-platform path handling and command patterns"
4 domain: "platform"
5 confidence: "high"
6 source: "earned (multiple Windows-specific bugs: colons in filenames, git -C failures, path separators)"
7 ---
8
9 ## Context
10
11 Squad runs on Windows, macOS, and Linux. Several bugs have been traced to platform-specific assumptions: ISO timestamps with colons (illegal on Windows), `git -C` with Windows paths (unreliable), forward-slash paths in Node.js on Windows.
12
13 ## Patterns
14
15 ### Filenames & Timestamps
16 - **Never use colons in filenames:** ISO 8601 format `2026-03-15T05:30:00Z` is illegal on Windows
17 - **Use `safeTimestamp()` utility:** Replaces colons with hyphens → `2026-03-15T05-30-00Z`
18 - **Centralize formatting:** Don't inline `.toISOString().replace(/:/g, '-')` — use the utility
19
20 ### Git Commands
21 - **Never use `git -C {path}`:** Unreliable with Windows paths (backslashes, spaces, drive letters)
22 - **Always `cd` first:** Change directory, then run git commands
23 - **Check for changes before commit:** `git diff --cached --quiet` (exit 0 = no changes)
24
25 ### Commit Messages
26 - **Never embed newlines in `-m` flag:** Backtick-n (`\n`) fails silently in PowerShell
27 - **Use temp file + `-F` flag:** Write message to file, commit with `git commit -F $msgFile`
28
29 ### Paths
30 - **Never assume CWD is repo root:** Always use `TEAM ROOT` from spawn prompt or run `git rev-parse --show-toplevel`
31 - **Use path.join() or path.resolve():** Don't manually concatenate with `/` or `\`
32
33 ### Path Comparison (Case Sensitivity)
34 - **Never use case-sensitive `startsWith` or `===` for path comparison on Windows or macOS:** These filesystems are case-insensitive — `C:\Users\` and `c:\users\` refer to the same location
35 - **Use platform-aware comparison:** Check `process.platform === 'win32' || process.platform === 'darwin'` and lowercase both sides before comparing
36 - **Pattern:**
37 ```typescript
38 const CASE_INSENSITIVE = process.platform === 'win32' || process.platform === 'darwin';
39
40 function pathStartsWith(fullPath: string, prefix: string): boolean {
41 if (CASE_INSENSITIVE) {
42 return fullPath.toLowerCase().startsWith(prefix.toLowerCase());
43 }
44 return fullPath.startsWith(prefix);
45 }
46 ```
47 - **Where it matters:** Security checks (path traversal prevention), rootDir confinement, any path-contains-path validation
48 - **Linux is case-sensitive:** Do NOT lowercase on Linux — `/Home/` and `/home/` are different directories
49
50 ## Examples
51
52 ✓ **Correct:**
53 ```javascript
54 // Timestamp utility
55 const safeTimestamp = () => new Date().toISOString().replace(/:/g, '-').split('.')[0] + 'Z';
56
57 // Git workflow (PowerShell)
58 cd $teamRoot
59 git add .squad/
60 if ($LASTEXITCODE -eq 0) {
61 $msg = @"
62 docs(ai-team): session log
63
64 Changes:
65 - Added decisions
66 "@
67 $msgFile = [System.IO.Path]::GetTempFileName()
68 Set-Content -Path $msgFile -Value $msg -Encoding utf8
69 git commit -F $msgFile
70 Remove-Item $msgFile
71 }
72 ```
73
74 ✗ **Incorrect:**
75 ```javascript
76 // Colon in filename
77 const logPath = `.squad/log/${new Date().toISOString()}.md`; // ILLEGAL on Windows
78
79 // git -C with Windows path
80 exec('git -C C:\\src\\squad add .squad/'); // UNRELIABLE
81
82 // Inline newlines in commit message
83 exec('git commit -m "First line\nSecond line"'); // FAILS silently in PowerShell
84 ```
85
86 ## Anti-Patterns
87
88 - Testing only on one platform (bugs ship to other platforms)
89 - Assuming Unix-style paths work everywhere
90 - Using `git -C` because it "looks cleaner" (it doesn't work)
91 - Skipping `git diff --cached --quiet` check (creates empty commits)
92 - **Wrong — case-sensitive path check on Windows and macOS:**
93 ```typescript
94 if (!resolved.startsWith(rootDir + path.sep)) {
95 throw new Error('Path traversal blocked');
96 }
97 // Fails: 'c:\\Users\\temp\\file'.startsWith('C:\\Users\\temp\\') → false
98 ```