Skip to content

fix(ctx_read): never mint an [unchanged] stub in the full-read fallback - #1132

Closed
andig wants to merge 1 commit into
yvgude:mainfrom
andig:fix/1128-full-path-stub-gating
Closed

fix(ctx_read): never mint an [unchanged] stub in the full-read fallback#1132
andig wants to merge 1 commit into
yvgude:mainfrom
andig:fix/1128-full-path-stub-gating

Conversation

@andig

@andig andig commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #1128.

A mode="full" read decided stubbing twice. First through try_stub_hit_readonly, the conversation-scoped gate from #954/#955 — which correctly withholds the stub when a different conversation received the content. Then again inside handle_full_with_auto_delta, off store_result.full_content_delivered, with no conversation context at all: that flag is copied from the cache entry (core/cache.rs:608), so it answers "some conversation got this content", which is the question the gate exists to stop trusting.

Result: a first read of a file in a fresh conversation could come back as [unchanged 143L · fresh=true to re-read] with no content — indistinguishable from a successful read, because the daemon outlives individual chats and an earlier session's delivery satisfied the flag.

The fix removes the duplicate decision rather than adding a second gate to it. Stubbing is now decided in exactly one place, by the one function that has the conversation identity, and handle_full_with_auto_delta is a pure content-deliverer — so a future caller cannot reintroduce an unscoped stub by reaching the fallback. render_unchanged_stub is now the only site that formats the [unchanged …] string.

Same-conversation re-reads are unaffected: they collapse at the gate and never reach the fallback, so cache-hit savings stay as they were.

Test plan

  • cd rust && cargo test -- --test-threads=1 — lib suite 8344 passed / 0 failed. One pre-existing failure in tests/critical_module_integration.rs::pathjail_blocks_traversal, reproduced identically on unmodified origin/main in this environment.
  • cd rust && cargo clippy --all-targets --all-features -- -D warnings — fails on two warnings that also fail on unmodified origin/main (core/ocla/tracing.rs:225 items-after-test-module, proxy/ocla_cache_bridge.rs:84 Duration unit). Nothing new from this change.
  • cd rust && cargo fmt --check — clean.
  • New regression test full_read_fallback_never_serves_a_stub verified red before the change (asserts on the stub text) and green after.
  • cookbook/packages untouched.

Notes for reviewers

  • Risk areas / edge cases: the deleted branch was reachable only when the gate had already declined to stub, so the only behavior it produced was the bug. Worth a second opinion on whether any caller reaches handle_full_with_auto_delta without consulting try_stub_hit_readonly first — mod.rs:952 is the only call site I found.
  • Backwards compatibility: no API or output-format change for same-conversation reads. Foreign-conversation reads now return content where they previously returned a stub, which is the intended Atomic write doesn't fsync the parent directory after rename #954 behavior.
  • Docs updated: none needed.

🤖 Generated with Claude Code

…ck (yvgude#1128)

The full-read path decided stubbing twice: once via the conversation-scoped gate try_stub_hit_readonly, then again inside handle_full_with_auto_delta off StoreResult.full_content_delivered. The second decision has no conversation context — the flag is carried over from the cache entry, so it answers 'some conversation received this content', which is exactly what yvgude#954 stopped trusting. A first read in a fresh conversation could therefore come back as a content-free stub.

Drop the duplicate branch so the gate is the single decision point and the fallback always delivers content. render_unchanged_stub is now the only place that formats the stub.
@andig

andig commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

Adjacent, deliberately out of scope here: cache-hit detection in registered/ctx_read.rs re-derives the hit flag by substring-matching the rendered output (content.contains("[unchanged") and friends), which is the same shape of defect this PR removes — a fact rebuilt downstream from a string instead of passed down from the code that knows it. Filed as #1133 rather than folded in.


🤖 Generated with Claude Code

@yvgude

yvgude commented Jul 21, 2026

Copy link
Copy Markdown
Owner

Cherry-picked to main as 17cbfc7. Fix verified: full_read_fallback_never_mints_stub test passes, [unchanged] stubs no longer emitted for uncached files. Thank you!

@yvgude yvgude closed this Jul 21, 2026
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 21, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ctx_read: '[unchanged]' placeholder returned for a file the current session never read

2 participants