Skip to content

Add /fork and /rewind slash commands (stack 2/2) - #2

Draft
Aarya2004 wants to merge 9 commits into
aarya/session-fork-rewindfrom
aarya/session-fork-slash-commands
Draft

Add /fork and /rewind slash commands (stack 2/2)#2
Aarya2004 wants to merge 9 commits into
aarya/session-fork-rewindfrom
aarya/session-fork-slash-commands

Conversation

@Aarya2004

Copy link
Copy Markdown
Owner

Review mirror of vercel-labs#568, the interactive half of vercel-labs#537. Stacked on #1.

The base branch here is stack 1/2, so this diff shows only the nine slash-command commits. When #1's branch changes, this PR tracks it. Review and discussion belong on the upstream PR; do not merge this one.

Aarya2004 added 9 commits September 1, 2026 09:00
The specs, the parsed variants, and the handler slots land first with
stub bodies so the exhaustive switches and the registry-walking router
test prove the wiring before any behavior exists. `route` now splits into
`parse` plus `dispatch` so a caller can inspect the parsed command once
instead of parsing it twice.

Claude-Session: https://claude.ai/code/session_01Hjm7J6N3SL5Y62TJ3bPxwD
A rewind runs only when the identical request is repeated. The gate arms
on the whole target, so a turn arriving between the preview and the
repeat re-arms rather than firing a rewind the user never saw described.

Claude-Session: https://claude.ai/code/session_01Hjm7J6N3SL5Y62TJ3bPxwD
`dropHistoryTurnsAfter` owns freeing the dropped turns and clamping the
model-context cursor. The durable store keeps its slice reallocation and
the live runtime shrinks its list, but neither repeats the free-and-clamp
rule, so an in-place rewind cannot drift from the stored one.

Claude-Session: https://claude.ai/code/session_01Hjm7J6N3SL5Y62TJ3bPxwD
`/rewind <count>` drops trailing turns from the live session. The shell
already holds the session writer lock, so the truncation happens in
process and commits through the live replacement path instead of through
`Store.rewindSession`, which would block on the lock this shell owns.

Both the confirmation and the completion say that file changes are
untouched, because the equivalent command in other agents restores files
and this one does not. Any command other than a repeated `/rewind`
disarms the gate.

Claude-Session: https://claude.ai/code/session_01Hjm7J6N3SL5Y62TJ3bPxwD
`/fork <turn>` branches the live session and moves the shell into the
branch. The session is closed before the store call because
`forkSessionCopy` takes the source's writer lock, which this process
holds while a session is open. That makes every failure past the close a
shell with no session, so the source is reopened on each failing path and
the notice always says which session the shell ended up on.

Background work carries forward rather than stopping, because the source
survives a fork and its in-flight commands belong to the branch too.

Claude-Session: https://claude.ai/code/session_01Hjm7J6N3SL5Y62TJ3bPxwD
Both commands accept 1 through the live history length. Holding that
rule in one tested function keeps the two ranges from drifting.

Claude-Session: https://claude.ai/code/session_01Hjm7J6N3SL5Y62TJ3bPxwD
The e2e file drives the real shell through tmux: it builds turns against
the fake gateway, proves the rewind needs two identical requests and that
an intervening command cancels it, proves the fork names both ids and
that the next prompt lands in the branch while the source keeps every
turn, and reads every session back off disk afterwards. Classified
verification-only, since branching and undo are deliberate rare
operations that must stay correct without being made hot.

Claude-Session: https://claude.ai/code/session_01Hjm7J6N3SL5Y62TJ3bPxwD
A successful fork always has both ids and always lands in the branch; a
failed one always has a reason and lands somewhere else. Modelling them
as one struct left `forked_id` and `problem` coupled by convention. One
`forkFailure` helper now owns the rule that every failure past the
session close reopens a session before answering, which also closes the
path where an allocation failure returned without reopening one.

Claude-Session: https://claude.ai/code/session_01Hjm7J6N3SL5Y62TJ3bPxwD
Only `RewindTarget` crosses into the command layer.

Claude-Session: https://claude.ai/code/session_01Hjm7J6N3SL5Y62TJ3bPxwD
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant