51712bbf4f
Three places where the answer and the record disagreed. A windowed `read` reported the window's length as the file's: `total_lines` and `file_size` were counted off the payload, which is the whole file only for an unranged read. A 20-line page of a 100-line file went out as `total_lines: 20` next to `range_applied: true`, which a paginating server reads as the end of the file. The count now comes from the read's own record of the file, `details.meta.truncation.totalLines` — deliberately not the flat `details.truncation.totalLines`, which counts from the window's start line. Counting the payload remains the answer for a read that returned the file whole, where it is exact. `pi_grep`'s `context` and `limit` left no trace. The bridge honors both by building a scoped tool, and neither is expressible in the model-facing `grep` schema, so the synthesized block recorded a plain pattern/path search — replaying a context-widened or capped search as an ordinary grep beside output no ordinary grep produces. Both are now on the block, the same way `pi_read` renders its range into the displayed path. An MCP listing shrank to a count. The full URI/name/mime catalog goes out on the wire while the paired result recorded `Listed N MCP resource(s)`, and rebuilt history is serialized from that result — so one reload later the model knew it had seen N resources and could name none of the URIs a follow-up read needs. The result now lists what the answer carried, still derived from the same `execResult` so the block cannot drift from the wire. `file_size` under a window is left as-is: no source available here records the file's byte length, and inventing one would trade a visible inconsistency for an invisible guess. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014oA3H7aHUL85ydp9PJ3ryF (cherry picked from commit 64afea3f7197adc32e2099a7d0ee74408d3012a6)