Repository navigation
perf(read): dedup touched-but-identical files and covered sub-ranges - #2259
Conversation
The Read dedup stub only fired for the exact same range with an unchanged mtime. Two common cases re-sent content the model already had: - mtime moved but bytes did not (git checkout/stash pop, formatters, touch) - a sub-range of a file that was already read in full Extract the eligibility check into readDedup.ts. On an mtime mismatch, re-read the stored range and compare it byte-for-byte before deduping, refreshing the cached timestamp on a match. Treat an in-bounds range of a prior full Read as already seen. Edit/Write entries, partial views, out-of-range offsets, and any stat/read failure still fall through to a normal read.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configuration
📒 Files selected for processing (3)
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review. 📜 Recent review details🧰 Additional context used📓 Path-based instructions (3)Review permission prompts, auto-allow logic, sandbox behavior, SDK permission schemas, shell/PowerShell execution, and background execution paths as security-sensitive.⚙️ CodeRabbit configuration file Files:
Review tests for meaningful coverage of the changed behavior, isolation of global/env/config state, async cleanup, fake timers, provider profile leaks, and Windows-compatible assumptions.⚙️ CodeRabbit configuration file Files:
Apply the OpenClaude maintainer review rubric from AGENTS.md.⚙️ CodeRabbit configuration file Files:
🔇 Additional comments (3)
📝 WalkthroughWalkthroughThe Read tool now uses a dedicated check to compare a requested range with prior read state. The check returns unchanged, touched, or miss. The tool returns ChangesFile Read Deduplication
Priority: ⬇️ Low Estimated code review effort: 3 (Moderate) | ~20 minutes Change: Other Merge Risk: ⚪ Minimal · up to No issue identified here prevents merging. When the inspected edge cases cannot safely reuse a prior Read, they fall back to a normal read. Security Architecture ReviewSecurity architecture risk: 🔵 Low · up to The optimization preserves existing file-read permissions and does not grant additional access. Remaining uncertainty concerns content freshness and concurrent cache updates, rather than new privileges or demonstrated data exposure. Retained concerns Security review detailsSecurity Blast Radius
Trust Boundaries and Controls
Resilience and Maintainability Implications
Hardening Proposals
🚥 Pre-merge checks | ✅ 6 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (6 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
kevincodex1
left a comment
There was a problem hiding this comment.
LGTM, thanks for contributing
…wigpine#2259) The Read dedup stub only fired for the exact same range with an unchanged mtime. Two common cases re-sent content the model already had: - mtime moved but bytes did not (git checkout/stash pop, formatters, touch) - a sub-range of a file that was already read in full Extract the eligibility check into readDedup.ts. On an mtime mismatch, re-read the stored range and compare it byte-for-byte before deduping, refreshing the cached timestamp on a match. Treat an in-bounds range of a prior full Read as already seen. Edit/Write entries, partial views, out-of-range offsets, and any stat/read failure still fall through to a normal read.
…wigpine#2259) Cherry-pick of Twigpine/openclaude@5ff58c4f (PR Twigpine#2259 there). Extracts the Read-dedup eligibility check into src/tools/FileReadTool/readDedup.ts (checkReadDedup): on mtime mismatch the stored range is re-read and byte-compared before deduping (handles touch/checkout/formatter), and any in-bounds sub-range of a prior full Read counts as already seen. Edit/Write entries, partial views, out-of-range offsets, and stat/read failures still fall through to a miss. Validation: FileReadTool 22/22, src/tools 798/800, test:provider 1704/1705, test:provider-recommendation 165/165 (all non-passing items reproduced identically on base 41a6ef9 — pre-existing env flakes); build, smoke, launcher, typecheck:type-tests, pr-scan clean; all 5 CI jobs green.
I reviewed both CONTRIBUTING.md and AGENTS.md before opening this PR.
Summary
git checkoutorgit stash popback to the same content, a formatter writing identical output, andtouch. On an mtime mismatch, the tool re-reads the range stored inreadFileStateand compares it byte-for-byte with the cached content. If they match, it returns the stub and refreshes the cached timestamp.offset: 40, limit: 20after a full Read. The earlier tool_result already has those lines with line numbers, so they are treated as already seen.FileReadTool.callinto a small pure helper,src/tools/FileReadTool/readDedup.ts, so it can be unit-tested without building a fullToolUseContext. The stub text, thetengu_file_read_dedupevent, and the killswitch are unchanged.These cases still do a normal read:
offset === undefined), as before.isPartialViewentries, as before.Impact
call().Testing
bun install --frozen-lockfile: okbun run check: exit 0. Smoke, any-budget and knip pass. See below for test failures in the log.bun run typecheck: passbun run typecheck:type-tests: pass (10 files)node bin/openclaude --versionandNODE_DISABLE_COMPILE_CACHE=1 node bin/openclaude --version:0.31.0 (OpenClaude)bun run test:provider: 1703 pass, 1 fail. The failure already exists on main; see below.npm run test:provider-recommendation: 165 pass, 0 failgit fetch https://github.com/Gitlawb/openclaude.git main && bun run security:pr-scan -- --base FETCH_HEAD --head HEAD: "no suspicious additions found"bun test ./src/tools/FileReadTool/passes 22 tests, including the newreadDedup.test.ts.53c7ed9c). All of these were reproduced with this change stashed:src/services/api/claude.streamWatchdog.test.ts: "falls back when the top-level stream iterator never settles" fails 3 out of 3 runs both with and without this change.src/memdir/autoExtractFacts.test.tshas 20 failures andsrc/utils/sessionStorage.atomicReplace.test.tshas 1. The counts are identical with and without this change.test:fullrun and pass on their own both with and without this change, which points to cross-test state leakage:fastMode,context,modelCost.modelGate,sideQuery.attribution,attachments.ultracode,http,knowledgeGraph. None of these touch the Read path.Notes
[Old tool result content cleared]without evicting the matchingreadFileStateentry. A later Read can then return the "unchanged" stub that points at content no longer in context. That gap predates this PR, but because this change returns the stub more often, it makes the gap more likely to show up. I'd suggest a separate fix that clears thereadFileStateentries for the Read results microcompact clears, and I'm happy to open an issue for it.🤖 Generated with Claude Code
Summary by CodeRabbit