Repository navigation
Fix #1034: split transcript basenames on backslash too, so the scan cache works on Windows - #1035
Merged
Merged
Conversation
…can cache works on Windows Claude Code on Windows sends a native C:\Users\...\<id>.jsonl transcript_path; after #829 decoding it holds no '/', so ${TRANSCRIPT##*/} kept the whole path, the sanitiser emptied it, and the transcript-scan sidecar was never read or written: every PostToolUse call cold-rescanned the transcript. Split on both separators at all three basename sites (scan-cache name, SESSION_ID fallback, log text). The sanitiser is unchanged. An empty cache name now logs one warning per transcript, fork-free. Reported and diagnosed by @RidoMr. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #1034
Diagnosis and the proposed expansion are @RidoMr's, from the issue: thank you for the clear repro and the before/after numbers.
What was wrong
Claude Code on Windows sends
transcript_pathas a nativeC:\Users\...\<session-id>.jsonlpath. After #829 decodes the JSON escapes,TRANSCRIPTholds single backslashes and no/at all.post-tool-hook.shderived basenames with${TRANSCRIPT##*/}, so the whole path survived, failed the[A-Za-z0-9._-]sanitiser and came out empty. The #913tmp/transcript-scan.<id>sidecar was then never read or written, and every PostToolUse call outside the save cooldown cold-rescanned the whole transcript. Nothing was logged.Change
scripts/post-tool-hook.sh: all three basename sites now use${TRANSCRIPT##*[/\\]}-- the scan-cache name, the SESSION_ID fallback (reached when a payload carriestranscript_pathwithoutsession_id), and the text of the Basename SESSION_ID sanitiser (#620) can empty to "" with no non-empty gate, turning a loud refusal into a silent full-transcript save #633 "failed the session id sanitiser" log line. The./../leading-dash/charset sanitiser is untouched, so nothing new can reach the sidecar path or the save-session argv.TRANSCRIPT, the hook logs oneWARNING: transcript-scan cache disabled for "<path>" ...line, once per transcript. The markertmp/transcript-scan-unnamedholds the last transcript warned about; it is read with thereadbuiltin and written with a builtin redirect, so the guard adds no fork on any path and nothing at all when the name is usable (Windows 25H2: every hook fork leaks a kernel Token object until reboot (win32k CForegroundLaunch bug) — fork tax is a persistent leak, not just latency #1011/post-tool-hook creates ~67 processes per no-save tool call #1029 budgets).tests/test_locale_ranges_695.py: four allowlist line numbers shifted by the new comment and guard lines (no new range added).changelog.d/1034.fixed.md.Tests (
tests/test_post_tool_backslash_transcript_basename_1034.py)Runs the real hook with a stdin payload, through
resolve_bash()so it runs on the Windows leg (unlike the #913 module, which is skipped there).transcript_pathwritestmp/transcript-scan.<id>after one call -- Windows only, becausestr(Path)there is the real native shape. Skipped elsewhere with a reason: on POSIX a backslash is a filename byte, so it would be a different file, not the Windows path.transcript_path, same fixture -- positive control, every platform.session_idin the payload) checked throughtmp/capture-alive.d/<id>: backslash case on Windows, forward-slash control everywhere.bad+name.jsonl) logs the warning exactly once across two calls (must fire); a good name logs nothing (must not fire).Observed vs reasoned
${p##*[/\\]}expansion gives the expected basename for POSIX, drive-letter forward-slash, native backslash and mixed-separator paths on bash 3.2.57 and 5.3. Adjacent suites (post-tool-hook: ~1.2 s per tool call on Windows (0.33.0) - whole-transcript wc -l grows with the session; lib-slug spawns cygpath twice per call #913, Windows 25H2: every hook fork leaks a kernel Token object until reboot (win32k CForegroundLaunch bug) — fork tax is a persistent leak, not just latency #1011, safe_eval drops every bridge variable containing 'I' under tr_TR locale — no save ever completes #695, Hook stdin extractors never decode JSON escapes: Codex on Windows yields a doubled-backslash PROJECT_DIR and the wrong session slug #829, The first-match stdin extractor now feeds cwd on all four hooks, and #344's top-level-wins pin only ever covered source #447, PostToolUse still cannot save on Codex: the session id is taken from the transcript basename, which #459 stopped controlling #468, post-tool-hook.sh: STDIN_SESSION_ID guard gap lets a flag-shaped session_id reach save-session.sh's argv (same class as #576/#600) #610, post-tool-hook.sh basename-derived SESSION_ID still reaches argv unguarded (leading-dash gap closed on two of three routes) #620, Basename SESSION_ID sanitiser (#620) can empty to "" with no non-empty gate, turning a loud refusal into a silent full-transcript save #633, post-tool-hook.sh _pt_scan_lines: quadratic-cost scan hangs the hook on real transcripts (v0.42.5 release-audit, round 1, BLOCKING) #1017, post-tool-hook.sh: marker mtime stops refreshing when SESSION_ID is empty (v0.42.5 release-audit, round 1, non-blocking) #1019, The per-tool-call hot path lost its byte-identity pin and the replacement can be walked around #330, resolve-paths.sh tests REMEMBER_HOOK_CWD for directory existence before the Windows path normalization that would make it resolvable #448, Release tree: inline the sourced shell libraries into each hook so the directory validator can follow every hook (clears COMMAND_SCRIPT_NOT_FOLLOWED) #900 compile/strip) pass locally: 189 passed, 4 skipped.windows-latest. Until that leg is green, the Windows claim here is reasoned.session_id(Claude Code's normal payload carries both). Whether Codex on Windows also sends backslash paths is not checked; if it does, the same change covers it.🤖 Generated with Claude Code
[AI-generated]