Skip to content

fix: rotation note misreports on bad max-bytes, awk -v mangles a backslash CLAUDE_PROJECT_DIR (#423, #424) - #425

Merged
fdaviddpt merged 3 commits into
mainfrom
fix/423-424-rotation-note-backslash
Sep 18, 2026
Merged

fdaviddpt merged 3 commits into
mainfrom
fix/423-424-rotation-note-backslash

Conversation

@fdaviddpt

Copy link
Copy Markdown
Contributor

What changed

#423 -- scripts/session-start-hook.sh's log-rotation size-watch note formatted the raw JIT_CONTEXT_LOG_MAX_BYTES env var (exportable directly, bypassing jit_load_config()'s own validation) through awk with no validation of its own, so a malformed value (abc, -5, 010) silently formatted as 0.0/-0.0 MB and the note claimed rotation was on while jit_log_rotate() (scripts/common.sh) had actually refused the value via its own case-statement validation and rotated nothing. Fixed by mirroring that same case-statement validation in the note branch; on a refused value it now says rotation did NOT run and names the (escaped) refused value.

#424 -- scripts/pre-tool-hook.sh built tools_base/vocab_base awk variables by concatenating $JIT_BASE paths in bash and passing them via -v, which processes backslash escapes -- so a CLAUDE_PROJECT_DIR containing a literal backslash (a Windows-shaped path) silently mangled the path before any rule lookup ran, and the hook answered {} for every rule under tools//vocabulary/, indistinguishable from "no rule matched". Same defect class as #402/#378. Fixed by removing the -v args and building the identical strings inline via ENVIRON["JIT_BASE"] instead.

Self-review round -- two fixes beyond the two issues' own literal scope

Both Explore and oss:auditor were spawned against the first commit's diff. Two real defects the auditor found were fixed directly rather than filed, since both were low-blast-radius, same-file, same-mechanism, and pinnable with a red-first test:

  1. The session-start-hook.sh's rotation note misreports when JIT_CONTEXT_LOG_MAX_BYTES is malformed #423 note branch's own new line embedded the refused JIT_CONTEXT_LOG_MAX_BYTES value UNESCAPED into the JSON systemMessage payload -- a value containing a literal " broke the JSON outright (verified via python3 json.loads). Escaped it the same way $LOG_FILE is already escaped two lines above.
  2. Beyond pre-tool-hook.sh silently answers {} when CLAUDE_PROJECT_DIR contains a backslash escape sequence #424's own literally-named scope (tools_base/vocab_base): scripts/pre-tool-hook.sh's home/project awk variables were STILL built via -v $HOME/-v ${CLAUDE_PROJECT_DIR:-.} -- the identical -v escape-processing defect, on the SAME variable (CLAUDE_PROJECT_DIR) this whole PR is about, in the SAME file. Verified empirically across one-true-awk/gawk/mawk that the corruption is present and INCONSISTENT across engines. Moved onto ENVIRON too.

Both new tests are red-before/green-after; see the report for exact commands and counts.

Adjacent findings, not fixed here (logged to trap.d instead)

Explore found the identical -v-escape defect class LIVE in two sibling files this PR never touches -- scripts/jit-misses.sh (a real functional break: generic-word filtering silently degrades to "off" on a backslash-bearing path) and scripts/rebuild-tsv.sh (two reporting-only breaks in maintainer tooling). Ranked non-blocking (fails-to-preserve/misreports), logged as trap.d/424.jit-misses-generic-words-v-escape.md and trap.d/424.rebuild-tsv-v-escape.md rather than fixed here -- different files, different subsystems, each needs its own fixture.

Tests

Full local suite (bash tests/run-all.sh) intentionally not run this round -- CI covers three platforms and is the source of truth; only the two narrowed suites the diff touches were run, repeatedly, through red/green cycles.

Tree-mutation receipt

could-not-compare: the before-snapshot (scripts/tree_snapshot.py) was deleted by an unrelated scratch-file cleanup before the compare step ran. Corroborated instead by repeated git status --porcelain checks through the session, which never showed an unintended change; both spawns carried explicit no-edit instructions.

Closes #423, closes #424.

[AI-generated]

Florian DAVID and others added 3 commits September 18, 2026 18:31
…ths misreported on two bad inputs (#423, #424)

#423: session-start-hook.sh's size-watch note formatted a malformed
JIT_CONTEXT_LOG_MAX_BYTES through `awk '{ printf "%.1f", $1 / 1000000 }'` with no
validation, so a non-numeric, negative, or leading-zero value silently read as 0 (or
-0) and the note claimed rotation was on ("rotates automatically past 0.0 MB") while
jit_log_rotate() had actually refused the value and done nothing. The note now
validates the value with the same case statement jit_log_rotate() uses and names the
refused value instead. tests/test-log-rotation-406.sh's own A9 never reached this
branch (its fixture log sits under jit-misses.sh's 10MB size-threshold default); C4/C5/C6
pad the log past that threshold to actually exercise it, red before the fix and green
after.

#424: pre-tool-hook.sh built tools_base/vocab_base in bash and passed them to the
decisive awk as `-v` values -- but awk's -v processes backslash escapes in the value
it receives, the same defect #402/#378 already fixed for JIT_WORKTREE_NOTE by routing
it through ENVIRON instead. A CLAUDE_PROJECT_DIR containing a backslash escape
sequence mangled JIT_BASE before any rule lookup ran, so the hook silently answered
"{}" for a rule that fires correctly on the identical tree under a plain path. Both
now read JIT_BASE via ENVIRON, already exported for exactly this reason (common.sh),
the same way jit_transclude_resolve() already does. tests/test-pre-tool-hook.sh adds a
new #424 section building two identical trees, one with a literal backslash in its
path, and asserts they answer identically -- red before the fix (the backslash tree
answered "{}"), green after.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…sibling -v-escape site

oss:auditor found that #423's own new note branch embedded the refused
JIT_CONTEXT_LOG_MAX_BYTES value verbatim into the JSON systemMessage payload, with no
escaping -- and that value never passed jit_load_config()'s validation (it can be
exported straight into the environment), so a value containing a literal '"' broke
the JSON structure outright. Escaped it the same way LOG_FILE is already escaped into
JIT_LOG_ESC. New C7 test in tests/test-log-rotation-406.sh parses the hook's own
stdout as JSON with python3 rather than grepping a substring, since a grep still finds
the raw text even inside now-broken JSON -- red before this fix (JSONDecodeError),
green after.

Same auditor pass found that pre-tool-hook.sh's `home`/`project` awk variables were
still built via `-v $HOME`/`-v ${CLAUDE_PROJECT_DIR:-.}`, the identical escape-
processing defect #424 fixed for tools_base/vocab_base in the same file, on the exact
variable (CLAUDE_PROJECT_DIR) the whole PR is about. Moved both onto ENVIRON too, for
the same reason and by the same mechanism. New test in tests/test-pre-tool-hook.sh
isolates $HOME (deliberately rooted outside $TEST_DIR, so CLAUDE_PROJECT_DIR's own
unaffected strip cannot mask the result) and confirms the project-prefix strip
survives a backslash-bearing $HOME -- red before this fix (the log tail kept the
unstripped absolute path), green after.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…423/#424

jit-misses.sh (generic-word file lookup) and rebuild-tsv.sh (layerdir/generic-word
classify pass) both carry the identical CLAUDE_PROJECT_DIR-derived-path-through-awk-
-v defect #424 fixed for pre-tool-hook.sh's tools_base/vocab_base -- found by Explore
while reviewing that fix, not touched by it (different files, different subsystem,
non-blocking per the ranking table's `fails-to-preserve`/`misreports` row). Logged to
trap.d/ rather than filed, per that row's routing.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@fdaviddpt
fdaviddpt merged commit bc17f57 into main Sep 18, 2026
13 checks passed
@fdaviddpt
fdaviddpt deleted the fix/423-424-rotation-note-backslash branch September 18, 2026 17:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant