Repository navigation
Read a pull request's diff from T3 and split its patch into files - #65
Conversation
…nto files pull_request::diff posts to /api/pull-requests/diff, as the desktop does, and reads at most 16 MiB of the answer. Each failure the route encodes reads as its kind. pull_request::patch splits the unified diff into files, hunks and numbered lines, within bounds on files, lines and path length. Nothing in the TUI calls either yet. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…eir prefix Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…nly files Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…into t3term/pull-request-diff-read Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… into t3term/pull-request-diff-read # Conflicts: # README.md
…ack into t3term/pull-request-diff-read
…e line bound Greptile on #65: a patch that ended inside a hunk read as whole, a line past one side's count was taken into the hunk, and a patch of hunk headers alone could keep a hunk for every header past MOST_LINES. Now a line joins a hunk only while each side it is on has lines left, the end of the patch inside a hunk and a hunk-like line after a file's last hunk mark the file unreadable, and each kept hunk counts as a line of the bound. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
After #65's parser fix a file past the line bound keeps no hunk, and a file whose only hunk header is broken never had one. Both read "No lines changed." Now they show the cut or unreadable note instead, with a test for each. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…e50cd Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…page recipe Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n the page recipe Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
FAIL for head
Finding, P2: src/pull_request/patch.rs:343, also line 347. The request matches the pinned diff route and input/result schemas. I also checked the scope handler, GitHub fallback and T3-generated patch headers. The declared errors use 503, 502, 401, 403 and 500, and the reader recognizes them. Optional commit reads remain an explicit known limit. Tests: all 27 Regression sweep: all 11 recipes pass using each side's own checkout. The additional current-master sweep passes 10 of 11 on both sides. Its older page recipe uses 200x60; the stack rows inherited from #63 and #64 put the conversation below that pane. Both branches already contain the updated 200x69 recipe, and it passes. This is an inherited map mismatch, not a regression introduced by the library layer. Connection, environments, appearance, sidebar, lineage, linked PRs, page, live refresh, thread details, transcript and CLI were covered. The source map and request comparisons are in Binary identity, read with
Tools and the additional master recipes came from |
A hunk header took any u64 start and count, and each line then moved the number on unchecked, so a header such as -18446744073709551615 with one line panicked in a debug build and wrapped to 0 in a release one. A range whose start plus count overflows now isn't a header, which marks the file unreadable as other broken headers do. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
README.md's unit test sentence conflicted, and the merge keeps both sides. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
PASS for head
Regression sweep: 12 of 12 recipes pass on master and on head. The 69 paired recipe captures match after accounting for run paths and loopback ports, spinner/elapsed time and one transient feed-reconnect frame. The settled paired recovery capture matches exactly apart from the run path and port. Immediate-stop linked-list tallies vary by one capability resubscription; settling the same actions gives identical RPC counts on both sides, as detailed below. The affected-feature map is Rules and requests: Local checks: fmt, clippy and all 797 tests pass. CI: macOS, Linux and E2E smoke are green on the pinned head. Greptile: the head check passed, all three earlier threads are resolved and answered, and there are no new head findings.
Both builds logged |
Adds
pull_request::diff, which reads a pull request's diff from T3, andpull_request::patch, which splits the patch into files, hunks and numbered lines. Nothing in the TUI calls them yet. The next pull request, stacked on this one, adds the Code tab to a link's details.diffPOSTs to/api/pull-requests/diffwith the session's token, as the desktop does. T3 sends the diff over HTTP, not RPC, because it is often the largest answer. It takes a cursor for the next slice and reads at most 16 MiB of the answer, a chunk at a time, with or without a length up front.UnavailableandNotFound, as they do over RPC. A refused token or scope, T3's own failure, another status, a redirect and no answer read as the newPullRequestError::Httpwith a sentence of t3term's.Diffkeeps the patch,truncated,next_cursorand the host's counts for files it left out.Patch::readreads git's format and T3's own patch for a page of files: added, deleted, renamed and binary files, quoted paths with octal escapes, paths with spaces, a file with a header and no hunks,\ No newline at end of file, Windows line ends and a plain unified diff. Each line's text is a range of the patch, so nothing is copied. A broken or short hunk marks its fileunreadable, and the rest still reads.src/http.rsnow reads and records request bodies, so a test can check what t3term sent.docs/pull-request-reads.mdgets a section on the diff, its bounds and its failures.Stacked on #64.
Decision log
What I noticed. The next step for PR reviews is the desktop's Code tab, which shows a pull request's changed files and their diff. t3term had no way to read a diff:
src/pull_request.rsreads only over RPC, and T3 serves the diff only over HTTP.How I confirmed it. These files in the pinned T3 source define the read:
packages/contracts/src/environmentHttp.ts:650-663defines the route and its five errors.packages/contracts/src/pullRequest.ts:945-988definesPullRequestDiffInputandPullRequestDiffResult, and says a slice holds whole files.apps/server/src/pullRequest/http.tschecksorchestration:read, which a saved login carries.GitHubPullRequestCli.ts:2206-2259reads GitHub's whole diff up to 8 MiB and falls back to 100 files a slice.gitHubPullRequestJson.ts:3219-3266writes T3's own headers for that fallback.Options.
Apithe client already holds, with the RPC error classes reused for the two pull request failures.Api::request. It reads the whole body with no bound and turns every error status into its own error, so the route's failures couldn't be told apart.What review changed. Greptile found two gaps in the parser, both fixed in f70828f. First, a patch that ended inside a hunk read as whole, and a line past one side's count joined the hunk. Now a line joins only while each side it is on has lines left, and a short hunk, an overfull one or a hunk-like line after a file's last hunk marks the file
unreadable. Second, hunk headers didn't count againstMOST_LINES, so a patch of headers alone could keep a record for each. Now each kept hunk counts as a line.The merges of #64 brought #63's steady live read test (15b07f8), master at 47e50cd (95b00ab) and the stack recipes (42f59c1, c48f756). Two conflicted. In e0691b9 and 95b00ab, README.md had changes on both sides, and each keeps both. In 95b00ab, master's
rejectedand this branch'sRpcError::failedwere added at the same place in src/rpc.rs, and both stay. The diff from #64 is still the same 8 files.GPT-6.1 Sol's first verdict on c48f756 was FAIL for one thing.
hunk_headertook anyu64start and count, and each line then moved the number on unchecked.@@ -18446744073709551615 +1 @@with one context line panicked in a debug build and wrapped to 0 in a release one, without marking the file. In e98add4 a range whose start plus count passesu64::MAXisn't a header, so the file is markedunreadableas for other broken headers. A range that ends at the highest number still reads. The new cases, on either side, panic at patch.rs:343 against the old parser. #62 to #64 have since landed, so this pull request now targets master. 3995c30 merges master at f043b63, which brings #48's environment switcher. Only README.md's unit test sentence conflicted, and the merge keeps both sides.Known limits.
diffuntil the next pull request.commit), a file's whole contents (pullRequests.diffFileContents) or viewed files.Tests
u64::MAXon either side, T3's header-only files, a plain unified diff and the file and line bounds.diffagainst a fake T3 on 127.0.0.1. They check the exact request with and without a cursor, each failure the route encodes, an error that isn't JSON, a redirect, no answer, and an answer one byte past its bound with and without its length up front. Unit tests read a full answer, a last slice, counts T3's schema allows but GitHub never sends, more omitted files than the bound and each broken field.---rule, the octal base and the tab before a path's end.🤖 Generated with Claude Code


Confidence Score: 5/5
The PR appears safe to merge, with the earlier caller fix supplied by PR #66.Summary
Adds
pull_request::diffto read bounded diff slices over HTTP andPatch::readto split patches into files, hunks and numbered lines.Diagram
%%{init: {'theme': 'neutral'}}%% flowchart LR A["Reference and optional cursor"] --> B["POST /api/pull-requests/diff"] B --> C["Read up to 16 MiB"] C --> D["Diff: patch and next cursor"] D --> E["Patch::read"] E --> F["Files, hunks and numbered lines"]Reviews (7) · Last reviewed commit: "Merge master at f043b63, which adds the ..." · Reviewed by Greptile