Repository navigation
Conversation
Extend vcs.diff with mode=turn, which diffs the files changed by a session's last turn from the per-step snapshots: the first step's start tree against the last step's end tree, or the working copy while that step still runs. Vcs.Mode stays the VCS backend contract; the wider Vcs.DiffSource is route-only and the server dispatches turn to Session.turnDiff instead of the VCS provider.
Add a Last turn option to the /diff source chooser when the viewer opens from a session. It requests mode=turn with the session ID, skips base resolution, and falls back to branch when a remembered or configured turn source is used outside a session. Accept turn in diffs.source.
Replace the vcs.diff?mode=turn source and TUI picker with session routes: GET /api/session/:id/turn lists turns (one busy period each, projected into a new session_turn table because durable event persistence is opt-in) and GET /api/session/:id/diff diffs a contiguous turn range from its first recorded start snapshot to its last recorded end snapshot.
Git.tree.diff ran name-status, numstat, and a patch per changed file. Run each once over the tree pair, split the patch with VcsPatch.chunksByFile, cap patch output at MAX_TOTAL_PATCH_BYTES with empty patches past the cap, and let Snapshot.diff filter ignored paths from the result instead of passing every changed path as a pathspec.
A turn is the assistant steps that follow a user message up to the next user message, so a steer is its own turn. That is fully derivable from session_message, so the session_turn projection, its migration, the turn list route, and the Session.Turn contract go away. session.diff takes messageID (default: newest user message) plus an optional to for contiguous ranges, resolves the range's Location from location-switched messages, and only compares against the working copy while the Session is actively executing. Shared missingMessage and failedSnapshot handler helpers replace the inlined mappings.
Patch headers have no -z form, so core.quotepath=false keeps chunksByFile matching non-ASCII names, and --no-ext-diff keeps a user's external diff tool out of the batched spawn. Snapshot.diff now diffs first and filters ignored paths from the result instead of listing changed files twice, and returns early for an empty explicit selection.
Contributor
Author
|
Superseded by #47821: same feature reworked per the design discussion (turn = between idle states, recorded as an |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
summary
GET /api/session/:sessionID/diff?messageID&to&context(session.diff) →{ data: FileDiff.Info[] }: the files changed by a turn, computed on request from the per-step snapshots already stored on assistant messages. Nothing new is persisted.messageIDdefaults to the newest user message;toextends the range through a later user message's turn, so a client can show "all my steers" or a commit-range-like selection. Fully derivable fromsession_message, so forks and reverts need no special handling.context→ full-file patches likevcs.diff.location-switchedmessages and resolved throughLocationServiceMap.get, so pre-move turns stay diffable; a range spanning a move is rejected. Errors: 400InvalidRequestError(tobeforemessageID, not a user message, location change), 404MessageNotFoundError/SessionNotFoundError, 500UnknownErrorwith a logged ref on snapshot failure (sharedfailedSnapshothelper, also used by the revert handlers;missingMessagereplaces three inlined mappings).Git.tree.diffruns--name-status,--numstat, and the patch once per tree pair instead of three spawns per changed file, caps patch output atMAX_TOTAL_PATCH_BYTES(capped files get an empty patch, stats stay exact), usescore.quotepath=falseso non-ASCII paths still match their patch chunk, and--no-ext-diff.Snapshot.diffdiffs first and filters ignored paths from the result instead of listing changed files twice.vcs.diff?mode=turnand then an idle-based turn model with asession_turnprojection; both were reverted after discussion in favour of the per-user-message definition, so the intermediate commits net to zero and the PR should be squash-merged.Note for reviewers (not changed here):
packages/core/src/session/stats.ts:296filtersEventTable.typewith the unversionedSessionEvent.UsageRecorded.typewhilebus.tsstores versioned type names (session.usage.recorded.1), so that query cannot match persisted rows.testing
bun typecheckin schema, protocol, core, server, client, plugin, tui, cli, app, sdk, www.test/session-diff.test.ts(one scenario: default/last turn, explicit turn, ranges, steer as its own turn,toordering and non-user-message errors, unknown message, unrecorded end fallback, running-step working-copy diff only when active, revert truncation, fork) andtest/git.test.ts(patch cap + non-ASCII header match; verified the assertion fails withoutcore.quotepath=false); plus snapshot/revert/projector/compaction/prompt suites — 97 pass.test/session-diff.test.ts(200/400/404 contract on the default non-persisting bus); full server suite 57 pass. client 160 pass; schema 61 pass + the pre-existingcontract-hygiene"limits Any" failure.generate-openapi.ts --check, coremigration --check, wwwcheck:generated+build, prettier on touched files.