计划执行与检查节点
executing-plans
按既定实施计划完成工作,保持过程检查和反馈。
windows-compatibility【效率与协作】Cross-platform path handling and command patterns
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
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.
2026-03-15T05:30:00Z is illegal on WindowssafeTimestamp() utility: Replaces colons with hyphens → 2026-03-15T05-30-00Z.toISOString().replace(/:/g, '-') — use the utilitygit -C {path}: Unreliable with Windows paths (backslashes, spaces, drive letters)cd first: Change directory, then run git commandsgit diff --cached --quiet (exit 0 = no changes)-m flag: Backtick-n (\n) fails silently in PowerShell-F flag: Write message to file, commit with git commit -F $msgFileTEAM ROOT from spawn prompt or run git rev-parse --show-toplevel/ or \process.platform alone: Windows is case-insensitive, but Darwin volumes are not all case-insensitive. APFS and HFS+ can be case-sensitive; a blanket lowercase conversion on macOS can conflate distinct sibling directories.FSStorageProvider currently folds case for all Darwin paths, so its root confinement is not volume-aware on case-sensitive macOS filesystems.rootDir only when it equals the normalized root exactly or starts with rootDir + path.sep; a bare substring/prefix match lets /root-escape slip past /root. Filesystem roots are valid roots and must remain exact-match-or-separator checks.rootDir confinement, and any validation that a resolved path stays under an allowed directoryimport path from 'node:path';
function normalizeForRootComparison(value: string, volumeIsCaseInsensitive = false): string {
return volumeIsCaseInsensitive ? value.toLowerCase() : value;
}
function isPathWithin(candidate: string, rootDir: string, volumeIsCaseInsensitive = false): boolean {
const a = normalizeForRootComparison(path.resolve(candidate), volumeIsCaseInsensitive);
const b = normalizeForRootComparison(path.resolve(rootDir), volumeIsCaseInsensitive);
const boundary = b.replace(/[\\/]+$/, '') + path.sep;
return a === b || a.startsWith(boundary);
}
An eol=lf attribute affects checkout behavior; it does not repair files that were already
materialized with CRLF. For the known shebang failure mode, use the repository's
scripts/fix-crlf-worktree.mjs rather than a broad renormalization:
git checkout-index -f.This is a local repair, not a source rewrite. Do not use git add --renormalize .; it rewrites
the index and creates unrelated line-ending churn. Do not force-checkout a locally modified file.
✓ Correct:
# Timestamp utility
$safeTimestamp = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH-mm-ssZ')
# Git workflow (PowerShell)
cd $teamRoot
# NEVER use `git add .squad/` or broad globs — only stage files you intentionally changed
# Stage only files you actually modified — use git status to build explicit list
$filesToStage = git status --porcelain | Where-Object { $_.Length -gt 3 } | ForEach-Object { $_.Substring(3) -replace '^.* -> ','' } | Where-Object {
$_ -eq '.squad/decisions.md' -or
$_ -eq '.squad/decisions-archive.md' -or
$_ -like '.squad/agents/*/history.md' -or
$_ -like '.squad/agents/*/history-archive.md'
}
if ($filesToStage) { $filesToStage | Where-Object { $_ } | ForEach-Object { git add -- $_ } }
git diff --cached --quiet
if ($LASTEXITCODE -ne 0) {
$msg = @"
docs(ai-team): session log
Changes:
- Added decisions
"@
$msgFile = [System.IO.Path]::GetTempFileName()
Set-Content -Path $msgFile -Value $msg -Encoding utf8
git commit -F $msgFile
Remove-Item $msgFile
}
✗ Incorrect:
// Colon in filename
const logPath = `.squad/log/${new Date().toISOString()}.md`; // ILLEGAL on Windows
// git -C with Windows path
exec('git -C C:\\src\\squad add .squad/'); // UNRELIABLE
// Inline newlines in commit message
exec('git commit -m "First line\nSecond line"'); // FAILS silently in PowerShell
git -C because it "looks cleaner" (it doesn't work)git diff --cached --quiet check (creates empty commits)