Skip to content

Fix concurrent session resumes with clone-on-write branches - #698

Closed
yxlyx wants to merge 5 commits into
mainfrom
fix/689-session-branching
Closed

Fix concurrent session resumes with clone-on-write branches#698
yxlyx wants to merge 5 commits into
mainfrom
fix/689-session-branching

Conversation

@yxlyx

@yxlyx yxlyx commented Aug 31, 2026

Copy link
Copy Markdown
Collaborator

Closes #689.

What changed

  • Added --resume SOURCE --branch DESTINATION and /resume SOURCE --branch DESTINATION across the default line session, scripted/TTY REPL, fullscreen TUI, ACP startup, and one-shot runtime paths.
  • Persisted branch ancestry, assigned each branch a fresh cache/session UUID, and atomically claimed new destination names so concurrent creators cannot both win.
  • Seeded fullscreen TUI conversations from provider-native restored history and synchronized messages, goal/todo policy, strict/ultracode state, rename state, meters, and shutdown saves back to the selected durable branch.
  • Added an offline process regression covering source immutability, two live branches, close-order independence, reopen behavior, input-buffer ownership, cache identity, transcript validity, and duplicate-destination races.
  • Recorded the clone-on-write identity contract in ADR 0048.
  • Prevented durable .session.json / .transcript.jsonl handoffs from being mistaken for JavaScript by the named-source nudge, which had injected an unrelated post-compaction model turn.
  • Kept learning-program reads bounded while raising the ceiling from 128 MiB to 160 MiB so the supported Linux debug binary remains pinnable after this feature is linked.

Why

Problem / failure mode

Two processes resuming the same named session continued writing the same .session.json, so autosaves and shutdown writes could silently replace one continuation with the other. The fullscreen TUI also did not start from the restored provider-native conversation or reliably return its final state to the root session, which made branching only the line REPL insufficient.

Reason for this approach

Clone-on-write gives each continuation an explicit durable identity while preserving the source as a stable common ancestor. A filesystem-exclusive destination claim closes the check-then-create race, and sharing one restore implementation across frontends keeps branch ownership, goal/todo state, compaction, transcripts, and final saves consistent.

Constraints and trade-offs

Branches deliberately do not merge automatically: provider-native tool/reasoning envelopes and compaction boundaries cannot be safely combined by concatenating message arrays. Peer cursor/inbox state is copied once, while git worktree isolation remains a separate user choice. Existing plain /resume SOURCE keeps its same-tip compatibility behavior and therefore remains unsuitable for concurrent continuation.

Rejected alternatives

A process-lifetime lock on the source would prevent corruption by rejecting the second user, but it would not support the requested concurrent work. Last-writer-wins merging was rejected because it preserves neither branch and can create invalid provider history.

Verification

  • Five independent TUI/runtime test agents exercised line REPL, scripted REPL, fullscreen TUI, ACP, one-shot, compaction, shutdown, metadata, malformed names, and concurrency edge cases.
  • python3 scripts/test-session-branching.py ./zig-out/bin/graff
  • Live fullscreen /resume SOURCE --branch DEST PTY regression
  • Fullscreen startup/close-order/duplicate-race stress (8 race rounds)
  • Fullscreen automatic and explicit compaction probes
  • Three consecutive Codex WS/SSE mid-turn compaction and transactional rollback runs
  • python3 tests/learn_bootstrap_e2e.py --graff zig-out/bin/graff
  • scripts/eval-tier1.sh (1,754 unit tests passed, 1 skipped; 460/460 TUI tests; all 17 PTY guard probes; build/reachability/SDK/invariants green)

yxlyx added 3 commits August 31, 2026 09:16
Concurrent resumes previously shared one durable session identity, so autosaves and shutdown writes could overwrite another continuation. Add clone-on-write resume targets across line, scripted, ACP, and fullscreen TUI paths, claim destination names atomically, and keep provider-native history plus session metadata attached to the selected branch.\n\nRecord the identity contract in ADR 0042 and cover source immutability, branch isolation, ownership stability, cache identity, and destination races with an offline process regression.\n\nCo-Authored-By: Codegraff <blackfloofie@codegraff.com>
Resolve the ADR-number collision by moving the session branching decision to ADR 0048, and retain both the restored scripted-REPL conversation sync and main's usage footer.\n\nCo-Authored-By: Codegraff <blackfloofie@codegraff.com>
The Linux debug executable crossed the 128 MiB learning-program ceiling after session branching was linked, causing the hosted zero-configuration bootstrap e2e to fail before it could pin the current binary. Keep the safety bound but raise it to 160 MiB so supported debug builds remain valid learning evaluators.\n\nCo-Authored-By: Codegraff <blackfloofie@codegraff.com>
yxlyx and others added 2 commits August 31, 2026 09:58
Durable compaction handoffs name  and ; substring matching interpreted their  prefix as JavaScript and could inject an unrelated fifth model turn. Match source suffixes at a boundary while explicitly supporting JSX/TSX.\n\nAlso make the Codex compaction probe parse both chat strings and normalized Responses  blocks, and wait for prompt-first startup settings before counting transport turns.\n\nCo-Authored-By: Codegraff <blackfloofie@codegraff.com>
Responses history may carry user content as either a chat string or normalized input_text blocks. Parse both forms so the compaction probe validates request meaning rather than racing normalization, and let prompt-first startup apply persisted auxiliary-call settings before transport counting begins.

Co-Authored-By: Codegraff <blackfloofie@codegraff.com>
cursor Bot pushed a commit that referenced this pull request Aug 31, 2026
Debug learn-pin ceiling, source-nudge suffix boundaries, and Codex
compaction wire parsing from the reopened session-branch head.
@justrach

Copy link
Copy Markdown
Owner

Folded into release/v0.0.282 (#696). The original clone-on-write work was already on that tip from #697; the three follow-ups from this reopen are now there too:

  • eeea332 / 999a333 — 160 MiB learn-pin headroom so a Linux debug image still pins after session branching linked in
  • aacecf3 / 493627c — source-nudge suffixes match at a token boundary, so a compaction handoff's .session.json / .transcript.jsonl is not a JavaScript nudge
  • b6eee4c / cf195f6 — Codex compaction probe reads Responses input_text blocks and waits for prompt-first settings

Closing as superseded. ADR stays 0049 on 282.

@justrach justrach closed this Aug 31, 2026
@justrach

Copy link
Copy Markdown
Owner

Closing as duplicate of #697, already folded onto #696 (v0.0.282, ADR 0049). Same head: clone-on-write --resume SOURCE --branch DEST. The three follow-ups on this reopen (160 MiB learn-pin headroom, source-nudge suffix boundaries so .session.json / .transcript.jsonl are not JavaScript, Codex compaction input_text parse) are also on the 282 tip. Do not merge this against main.

@cursor
cursor Bot deleted the fix/689-session-branching branch August 31, 2026 02:25
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.

Make /resume clone-on-write so concurrent TUI sessions can branch safely

2 participants