fix(source-control): convert the mktemp path to Windows mixed form before it feeds the Write tool - #3497
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Claude finished @kyle-sexton's task in 3m 22s —— View job Code review complete
No blocking findings. This is a small (+7/-2), well-scoped fix and it holds up under review:
Author-claimed verification (live Git Bash run, |
|
Claude finished @kyle-sexton's task in 1m 30s —— View job Security review —
|
|
Last security-reviewed head: |
|
Claude has reviewed this PR 3 times. The lane skips further automatic reviews after 5; deleting this comment resets the count. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 65195aee49
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
Claude finished @kyle-sexton's task in 38s —— View job Security review —
|
|
Claude finished @kyle-sexton's task in 2m 47s —— View job Code review complete
No blocking findings. Reviewed the current HEAD (
One non-blocking observation: the inlined snippet doesn't carry the reference helper's Author-claimed verification (live Git Bash run, |
…fore it feeds the Write tool The worktree create context told the model to print a bare `mktemp -d` path and use it as a `Write` tool `file_path`. On Windows Git Bash that prints the MSYS literal `/tmp/tmp.XXXXXXXXXX`; the Write tool's native side resolves the leading `/` against the current drive, creating a phantom `<drive>:\tmp\...` while the real directory sits in %TEMP% — the same silent drive-root class as #2834. Confirmed live: an empty `C:\tmp\tmp.rSFIkHm5DO` created by exactly this path. Per docs/conventions/windows-path-emit/README.md Rule 3 (cygpath mixed form, correct for both the Write tool and the later Bash consumers of the same printed value; `-l` expands the 8.3 short name %TEMP% carries) and Rule 4 (fail loud — `|| exit 2`, never the unconverted literal). Non-Windows passes through unchanged, gated on $OSTYPE exactly as scripts/emit-windows-path.sh does; the helper itself is marketplace-root tooling the shipped plugin cannot call, so the contract is inlined. `mktemp -d -p "$TEMP"` was rejected: `mktemp -p` is an active flagged token in scripts/shell-portability-tokens.txt, and it yields mixed separators anyway. The load-bearing-details list now documents the conversion so it is not reverted as noise. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ath fix to existing installs The plugin version is the consumer update-cache key: without a bump, installations already carrying an earlier version keep the old create.md and never receive the Windows mixed-form conversion, so the phantom drive-root write stays live on every existing install. Bump the manifest and add the matching '## [0.55.34]' release entry (changelog-parity --check-bump requires the pair; 0.55.31-0.55.33 are already published on main and may not be reused — originally staged as 0.55.32, renumbered on rebase after main released 0.55.32 and 0.55.33). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
c9230f4 to
9f4a700
Compare
|
Claude finished @kyle-sexton's task in 57s —— View job
|
|
Claude finished @kyle-sexton's task in 43s —— View job Security review —
|
…ume-root entries (#3498) ## Summary Adds machine-health catalog check #19, `drive-root-litter`, so stray files and directories at fixed-volume roots surface on a routine health run instead of only during a manual disk audit. The audit that prompted this found an empty `C:\tmp` path-translation artifact and a 0-byte `C:\log.txt` owned by BUILTIN\Administrators — neither visible to any existing check. Closes #3499 ## Fix - **New check** `scripts/windows/checks/Test-DriveRootLitter.ps1`: lists each fixed-volume root non-recursively and diffs it against an expected-entry baseline. Read-only; unelevated; Windows-only. - **Baseline is data, not logic**: `references/windows/drive-root-baseline.jsonc` holds the expected sets (any-volume housekeeping / system-drive-only / known litter-name shapes) as type-aware, case-insensitive `-like` patterns. Admitting a newly legitimate entry is a data edit, never a script change. - **Per-volume posture**: system drive gets the full baseline diff; non-system fixed volumes (data drives, Dev Drives) legitimately hold arbitrary user content, so only litter-name shapes (`tmp`, `temp`, `tmp.*`, `log.txt`, `*.tmp`) are reported there. Removable/network drives never scanned. - **Proportionate severity**: OK / INFO (1–9 residue) / WARN (≥10, something actively dumping) / UNKNOWN (baseline unreadable or a root unlistable). Never CRIT, and excluded from the trend engine's generic upward upgrade (`Invoke-TrendAnalysis.ps1` maps it to `residue_count` for history only). - **Trend-aware**: deterministic output (sorted residue, day-granularity created dates) so an unchanged dropping feeds `identical_streak` demotion instead of reading as news weekly. - Catalog entry in `checks.jsonc`, rubric §19 in `references/windows/check-catalog.md`, plugin bumped to 0.12.0 with changelog entry. Owner and directory-emptiness probes are best-effort (`try/catch` → `null`) so a denied ACL read degrades instead of erroring. ## Verification - **Real `C:\` acceptance run (unelevated)**: the manual audit's ground truth was exactly three unexpected entries, and the check reported exactly those three — `C:\symbols` (directory, SymSrv store), `C:\tmp` (directory, path-translation artifact), `C:\log.txt` (0-byte file, owner BUILTIN\Administrators read without elevation) — plus one entry that postdates the audit: `C:\worktrees`, an empty user-owned directory created 2026-08-30 14:24:32 and registered to no worktree in either repo's `git worktree list` — a second instance of the same leak CLASS from a different producer, not a recurrence of the same artifact. The `C:\tmp` leak itself fired once, at 2026-08-30 14:15:56, from the `mktemp -d` path in `plugins/source-control/skills/worktree/context/create.md` (addressed separately in PR #3497); it was never deleted or re-created. Zero false positives: 16 of 20 `C:\` root entries matched baseline, all stock names (Windows, Program Files, ProgramData, Users, PerfLogs, Recovery, $Recycle.Bin, System Volume Information, pagefile.sys, swapfile.sys, DumpStack.log.tmp, Documents and Settings, OneDriveTemp, Config.Msi, …) suppressed. `D:\tmp` reported on the data volume via the litter-name posture. - New Pester suite `Test-DriveRootLitter.Tests.ps1`: 16/16 — clean baseline-only root, stray file, stray directory, type-aware matching, severity ladder (never CRIT), non-system posture, UNKNOWN degradations, mutation-free assertion, deterministic ordering. - `Invoke-TrendAnalysis` suite extended for the new mapping: 12/12. - Full machine-health suite: 440 passed; the single failure (`Test-EnvironmentHealth` %VAR% expansion) is machine-environment-dependent and fails identically on an untouched main checkout. - `scripts/affected-tests.sh`: every changed file maps (`--explain` exit 0); `--run` exit 3 as documented (all selected suites are Pester, covered above). - markdownlint-cli2 clean on changed markdown; PSScriptAnalyzer clean on the check script (test helper carries the same accepted warning as its siblings). - Independent fresh-context verifier passed all 11 acceptance criteria (catalog shape, rubric anchor, data-only baseline, zero stock false positives, file+dir reporting, severity vocabulary, no mutation, honest needs_admin, windows-only os, Pester, determinism) with no defects. ## Related Closes #3499. The repo-tooling `scripts/check-drive-root-litter.sh` is intentionally untouched — it is CI tooling being widened separately, not a machine-health check. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01Egdf11hXdBch1HTFmjB8FV --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Closes #3496
Summary
plugins/source-control/skills/worktree/context/create.mdstep 1 told the model to print a baremktemp -dpath and hand it onward — as theWritetool'sfile_path(theworktree-root/data-rootsteps) and substituted into the later Bash block. On Windows, Git Bash's GNUmktempprints the POSIX literal/tmp/tmp.XXXXXXXXXX(TMPDIR is unset; MSYS mounts/tmpon%TEMP%). TheWritetool is native node.exe: Win32 resolves the leading/against the current drive, so the file lands in a phantomC:\tmp\tmp.XXXXXXXXXXwhile the real directory sits in%TEMP%. Nothing errors — the silent drive-root classdocs/conventions/windows-path-emit/README.mdowns, previously worked as #2834. Confirmed live on this machine: an emptyC:\tmp\tmp.rSFIkHm5DOcreated at 14:15:56 on 2026-08-30 by exactly this path.Fix
The snippet now converts before printing, per the windows-path-emit convention:
cygpath, mixed form.-m(C:/...) is correct for both consumers of the one printed value: theWritetool and the later Git Bash block (--fallback-root-file,rm -rf), so no half-converted split arises (Rule 2).-ladditionally expands the 8.3 short name%TEMP%carries on this machine (KYLESE~1), whose~misbehaves downstream.|| exit 2; the unconverted MSYS literal is never printed, because it is precisely what writes to the wrong place. Non-Windows passes through unchanged, gated on$OSTYPEexactly asscripts/emit-windows-path.shdoes. The helper itself is marketplace-root tooling not shipped inside the plugin, and create.md is markdown instruction — so its contract is inlined, not called.mktemp -d -p "$TEMP"rejected, twice over:mktemp -pis an ACTIVE flagged GNU/BSD-divergence token inscripts/shell-portability-tokens.txt(it would fail the portability gate), and it yields mixed separators (C:\...\Temp/tmp.XXX).mktemp -d(directory exists, file inside does not — theWrite-overwrite refusal) and the deliberateWrite-tool round-trip for the raw${user_config.worktree_root}substitution.No sibling doc in the skill repeats the snippet (checked: the only other occurrence is the evals.json grading criterion, which is form-agnostic and stays accurate).
Verification
C:/Users/KyleSexton/AppData/Local/Temp/tmp.qiZCfYGBa6— mixed form, short name expanded. WithOSTYPE=linux-gnuthe path passes through unchanged, exit 0. With a stubbed failingcygpath, exit 2 and zero stdout.scripts/check-shell-portability.sh --paths <file>: clean ("No unexcused GNU-only constructs").scripts/check-changed-skills.sh origin/main: CHECK-SKILL worktree PASS — 0 errors.npx markdownlint-cli2 <file>: 0 issues.scripts/affected-tests.sh --run --explain: the changed file is the excused*.mdclass (hygiene lane); basename over-selection picked 6 shell suites that merely namecreate.md. Four completed locally, zero failures — repo-local telemetry sink (2 PASS), claude-ops telemetry sink (PASS=16 FAIL=0), flag-commit-pr-skill-bypass (PASS=35 FAIL=0), check-hook-wiring-liveness (all assertions passed) — plus 71 green assertions from the two pr-linkage suites before the local 10-minute tool cap cut them off (0 failures observed; they still run detached locally). CI re-runs the authoritative suites on this PR.mktemp -p, doc coherence for both consumers, markdownlint, convention-link resolution). Non-blocking notes only (an empty temp dir is orphaned in%TEMP%ifcygpathfails — OS temp cleaning covers it).Related
docs/conventions/windows-path-emit/README.md— the owning convention (Rules 2–4 applied here).scripts/emit-windows-path.sh— the repo helper whose contract (mixed form default,$OSTYPEgate, fail-loud) this inlines.