wsl-windows-boundary-guards
Section titled “wsl-windows-boundary-guards”Background
Section titled “Background”Talbot, 2026-08-05: “The windows versus WSL challenges continue to haunt me. After decades of using Windows, my WSL use in the last year or two … has resulted in much frustration and system issues such as this one. What additional configuration changes can be made to help reduce these ongoing frustrations? Review all of my tools that are used and see if there can be any hooks or warnings that can help.”
Approved with “go” in my_backup-verification-alert, which also produced the analysis below.
The friction is not diffuse — it is six specific mechanisms, and five are automatable. Every cross-boundary incident in my_backup’s history is one of these:
| # | Mechanism | Incidents | This task |
|---|---|---|---|
| 1 | Wrong-side interpreter — Linux Python can’t see D:, UNC, robocopy, kopia.exe, 7z.exe | 3× (2026-03-29, 06-01, 08-03) | generalise the guard |
| 2 | Shared-drive .venv poisoning — a bare uv run from either OS rebuilds the other’s venv | 2× (07-19→26 silent outage; again 08-03) | PreToolUse hook + per-project isolation |
| 3 | Line endings — CRLF written into WSL-side files | ongoing | CRLF util |
| 4 | Case sensitivity — NTFS folds case, ext4 doesn’t | 7-Zip failure 08-01 | tracked in my_backup-silent-failure-gaps — not here |
| 5 | Encoding — cp1252 vs UTF-8 through cmd.exe | v1.5.0; still present in backup.log | fold into #1’s guard |
| 6 | WSL not running — \\wsl$ unreachable, nobody logged on | 07-30 | fixed per-job; no general guard needed |
Measured CRLF exposure (2026-08-05):
| Location | Files with CRLF |
|---|---|
/mnt/d/FSS/Software/Utils | 29 of 65 (45%) |
~/utils | 16 of 115 |
~/projects | 0 of 163 |
The shared drive — the only tree Windows editors touch — is nearly half CRLF; pure-WSL ~/projects is spotless. git config is already correct (core.autocrlf=input, core.eol=lf, core.safecrlf=warn), which protects committed files but does nothing for untracked files or working-tree drift.
TextPad is a dead end, confirmed by Talbot 2026-08-05: Configure → Preferences → File offers no line-ending option in TextPad 9. Its ConfigState.xml stores every relevant setting as an opaque hex blob, so there is nothing safe to configure. The fix must therefore be outside the editor — which is better anyway, since it then covers Obsidian, VS Code and anything Talbot switches to later.
1. PreToolUse hook — block the .venv poisoning class (highest leverage, do first)
Section titled “1. PreToolUse hook — block the .venv poisoning class (highest leverage, do first)”Mechanism #2 caused the two worst outages: an 8-day silent backup failure (07-19→26) and a repeat on 08-03. Both originated the same way — a human or agent ran a bare uv run in a shared-drive project from WSL, rebuilding .venv as a Linux venv that Windows uv.exe then could not sync.
- Add a
PreToolUsehook (matcher:Bash) that warns when a command runsuv run/uv syncwith cwd under/mnt/<drive>/andUV_PROJECT_ENVIRONMENTis unset. - Use the
update-configskill to editsettings.json— do not hand-roll the hook JSON. - A shell function in
~/.bashrcis not sufficient and should not be the primary fix: cron does not source the profile. The failures came from interactive/agent sessions, which is exactly what a PreToolUse hook covers. A~/.bashrcguard may be added as a secondary net for Talbot’s own manual shells. - Test both directions: a bare
uv rununder/mnt/d/...warns; the same command withUV_PROJECT_ENVIRONMENTset, and anyuv rununder~/, stay silent.
2. CRLF detect-and-repair utility
Section titled “2. CRLF detect-and-repair utility”- Build in
~/utils/(WSL-side per the placement rule), registered inCore/IT/Utils/Custom/as part of creating it — an unregistered util is invisible to the “does a tool already exist” check. - Detect mode (default, read-only): report files containing CRLF across configured roots, grouped by tree, with counts. Repair mode: convert in place, opt-in per path.
- Must not touch files that are legitimately CRLF:
*.bat,*.cmd,*.ps1, and anything under.git/. Windows batch wrappers are consumed bycmd.exe. - Consider wiring a periodic run into
job_monitorso drift is reported rather than discovered. - Baseline to verify against: the three counts in the table above.
- Talbot addition
- A detect-repair util would only work if a reliable hook triggered it, after manual file editing. My default text editor was Textpad. I have installed Notepad++ and will try it instead, as it indicates in the status line the line endings for either Linux or Windows.
- Claude, 2026-08-05: Notepad++ is the right call and it changes the shape of this item. Unlike TextPad, its preferences are readable XML, not opaque blobs —
%APPDATA%\Notepad++\config.xmlcarries<GUIConfig name="NewDocDefaultSettings" format="…">where0=Windows CRLF,1=Mac CR,2=Unix LF. It was0; set to2on 2026-08-05 while Notepad++ was closed (it rewrites config.xml on exit, so edits made while running are lost). Backup:notepadpp-config.xml.bak-20260805in the session scratchpad. UI equivalent: Settings → Preferences → New Document → Format. - This narrows the util’s job substantially. Notepad++ preserves an existing file’s line endings on save and shows them in the status bar, so the remaining CRLF risk is only (a) newly created files — now defaulted to LF — and (b) an accidental Edit → EOL Conversion. Talbot’s point stands that a detect-repair util needs a trigger, but with the editor no longer introducing CRLF, a periodic scan (daily via
job_monitor, reporting drift) is sufficient — no per-save hook needed. Prefer that over trying to hook the editor. - Still worth building the detector for the 29 of 65 shared-drive files that already carry CRLF, which no editor setting fixes retroactively.
3. Isolate the three unprotected shared-drive projects
Section titled “3. Isolate the three unprotected shared-drive projects”Talbot confirmed folding these in. my_backup is the only shared-drive Python project with an isolated UV_PROJECT_ENVIRONMENT; these three have the identical exposure:
| Project | Exposure |
|---|---|
diskcheck | ⚠️ live weekly Scheduled Task (\DiskSpaceCheck) running C:\Users\Admin\.local\bin\uv.exe run diskcheck from D:\... with no isolated env — same shape as the July outage |
rename_receipts | manual util, no scheduled task |
folder_structure | manual util, no scheduled task |
- Give each a wrapper
.batinD:\FSS\Software\Utils\Windows\followingmy_backup-daily-task.bat(isolatedUV_PROJECT_ENVIRONMENT,PYTHONUTF8=1, no trailing spaces aftersetvalues). - Repoint the
DiskSpaceCheckscheduled task at its wrapper. Note its history: the task’s real name isDiskSpaceCheck, notdiskcheck, and it previously failed silently for 5 months because the elevated action couldn’t resolve a bareuvon PATH — see System-MaintenanceLESSONS.md. Verify by actually triggering the task and checkingLastTaskResultand a fresh log line, not by running the command interactively.
4. Generalise the wrong-side-interpreter guard
Section titled “4. Generalise the wrong-side-interpreter guard”check_backups.py::check_environment() (shipped 98e83d1) refuses to run off Windows and alerts as MISCONFIGURED rather than reporting missing data. That pattern belongs anywhere a util depends on one OS.
- Extract it into a small shared helper rather than copying it — candidates:
create_system_image,update_static_zips,debug_kopia,diskcheck, andvirus_scan(which is the mirror image: WSL-only, and broke in exactly this way on 2026-08-03 when a duplicate cron entry invoked it throughcmd.exe). - Decide where the helper lives —
my_backup.utilsworks for that repo, butdiskcheckis a separate project; a tiny shared module underPythonUtils/may be cleaner. - While in here:
logs/backup.logstill contains cp1252/NEL bytes, which makesgreptreat it as binary unless-ais passed.send_status_reportsurvives it (errors="ignore") but silently drops the bad bytes. ConfirmPYTHONUTF8=1is now set on everycmd.exeentry point, and consider a one-time clean of the existing log.
Success Criteria
Section titled “Success Criteria”- A bare
uv runin a shared-drive project from an agent session produces a warning before it can rebuild the venv. - CRLF counts on
/mnt/d/FSS/Software/Utilsand~/utilsare reduced and measurable on demand, via a registered util — with*.bat/*.ps1correctly left alone. DiskSpaceCheckruns from a wrapper with an isolated env, verified by triggering the real task.- No util that depends on one OS can silently produce wrong results when run from the other.
Context
Section titled “Context”- Analysis + measurements: my_backup-verification-alert, 2026-08-05 round-2 response.
- Related, do not duplicate: my_backup-silent-failure-gaps (case-sensitivity #4 lives there), disk-space-monitoring.
- Standing rules:
AGENTS.md→ Dev Standards (utility placement, package managers),GlobalDevRules.md§3.9 (WSL + AI utility gotchas — the SSOT for this whole class). - Repos:
D:\FSS\Software\Utils\PythonUtils\*,~/utils/,~/ai-config/(hook lives in deployed~/.claude/settings.json; edit the SSOT, not the deployed copy).