fix(claude): projected sessions with thinking blocks are resumable again - #151
Merged
Conversation
Two fixes found by live-resuming a projected session against the real API: - toolpath-claude 0.12.2: ContentPart::Thinking serializes a None signature as an absent key instead of "signature": null. The Anthropic API rejects a null signature outright (400 thinking.signature.str: Input should be a valid string), so any session projected by p export claude / path resume that contained thinking blocks — most real agent sessions — failed on the first message after resume. An absent signature is tolerated: the unsigned thinking block is dropped on replay, per the documented writer contract in docs/agents/formats/claude-code/writing-compatible-jsonl.md. Real signatures still pass through untouched. - path-cli: the Claude JSONL export ends with a trailing newline. Claude Code appends to the projected file on resume, and without one the first appended entry landed on the same line as the last projected entry, corrupting the JSONL (gemini/codex exports already did this). Verified live: a projected thinking-bearing session that previously 400'd now resumes cleanly under claude -r and summarizes its own history.
The Claude export newline fix rides a patch bump so the pending 0.16 release carries it distinctly. The toolpath-cli shim tracks path-cli in lockstep as usual.
|
🔍 Preview deployed: https://abfd7acd.toolpath.pages.dev |
ben-emp
pushed a commit
that referenced
this pull request
Jul 30, 2026
…th resume short-circuits p export claude --project refuses to overwrite an existing session file (the error names the id and suggests claude -r); a new --force flag restores the old clobbering behavior. Found the hard way: same-machine round-trips — share your own session, then resume it — silently replaced the richer local original with the lossy projection. path resume short-circuits instead: project_claude returns AlreadyLocal and the resume execs the local copy, which may be newer than the shared document. Tests cover the diverged-local untouched case and the refuse/--force pair. (The signature-serialization and trailing-newline fixes discovered in the same investigation landed on main separately via #151.)
benbaarber
added a commit
that referenced
this pull request
Jul 30, 2026
…th resume short-circuits p export claude --project refuses to overwrite an existing session file (the error names the id and suggests claude -r); a new --force flag restores the old clobbering behavior. Found the hard way: same-machine round-trips — share your own session, then resume it — silently replaced the richer local original with the lossy projection. path resume short-circuits instead: project_claude returns AlreadyLocal and the resume execs the local copy, which may be newer than the shared document. Tests cover the diverged-local untouched case and the refuse/--force pair. (The signature-serialization and trailing-newline fixes discovered in the same investigation landed on main separately via #151.)
ben-emp
pushed a commit
that referenced
this pull request
Aug 7, 2026
…th resume short-circuits p export claude --project refuses to overwrite an existing session file (the error names the id and suggests claude -r); a new --force flag restores the old clobbering behavior. Found the hard way: same-machine round-trips — share your own session, then resume it — silently replaced the richer local original with the lossy projection. path resume short-circuits instead: project_claude returns AlreadyLocal and the resume execs the local copy, which may be newer than the shared document. Tests cover the diverged-local untouched case and the refuse/--force pair. (The signature-serialization and trailing-newline fixes discovered in the same investigation landed on main separately via #151.)
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.
Urgent extraction of the two resume-blocking fixes from #150 so they can land on main today, independent of the plugin stack.
The bug
Claude Code's JSONL carries an Anthropic-signed
signatureon every thinking block. Toolpath's derivation doesn't preserve signatures, and the projector re-emitted thinking blocks with"signature": null— which the API rejects outright:An absent signature key is tolerated (the unsigned thinking block is dropped on replay, per
docs/agents/formats/claude-code/writing-compatible-jsonl.md), so every session projected byp export claudeorpath resumethat contained thinking blocks — most real agent sessions — was unresumable, failing on the first message after resume.Fixes
#[serde(skip_serializing_if = "Option::is_none")]onContentPart::Thinking.signature—Noneserializes as an absent key. Real signatures pass through untouched. Unit test asserts both directions.Verification
claude -rand correctly summarizes its own history; on-disk checks confirm zero"signature":nulloccurrences and a trailing newline.cargo test -p toolpath-claude -p path-cligreen;scripts/quality_gates.sh7/7 on this branch.Not included (stays in #150): the
p export claudeoverwrite guard +--forceand thepath resumealready-local short-circuit — behavior changes, not API compatibility. #150 carries the same two hunks as this PR and will rebase cleanly or trivially after this merges.Follow-up (not here): preserve real signatures through derivation via
Turn.extra["claude"]for lossless thinking replay.Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.