Repository navigation
basou: Import a forked Claude Code transcript without the records it copied - #377
Merged
Merged
Conversation
…copied A transcript that forks another (a session resumed into a new session id, or one started from another session's conversation) opens with copies of the records it took over, under their original uuids and timestamps, and each transcript was imported as a session of its own. The shared history was counted once per transcript: its commands, decisions, file changes, output tokens and per-session active time, and every fork's session started at the original's first record. basou import claude-code now groups the transcripts of the source roots by the conversation they hold: every transcript of one conversation opens with the same first record, so each is read up to its first uuid. The transcript whose file was created first keeps the shared records (birth time; the session id decides where none is recorded or two are equal), and each later one is derived from the records it added (claudeTranscriptOwnRecords). A fork that added no action is not imported. A session imported with the copies is re-derived in place on the next import even though its transcript did not change (its start is at or before the conversation's first record). reimportPreservingId takes dropDerivedBefore: a prior derived event from before the fresh start may be dropped; one from the session's own time that is not reproduced still refuses the re-import. A fork that added no action of its own, and a session imported before source sizes were recorded (it waits for --force), keep their copies. The JSON result counts these re-derived sessions in reimported_count and in a new dropped_copies_count, and refresh says how many there were. A re-import, --force included, leaves out records from before the start of the session an earlier import made, so the copies stay out after the older transcript is deleted. docs/spec/terminal-and-import.md section 14.2 describes the rule and a known limit: the order is read from birth times on every import, so if they change after an import and the order flips, the shared records end up counted in neither session. The shapes of session.yaml and events are unchanged.
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
A Claude Code transcript that forks another (a session resumed into a new session id, or one started from another session's conversation) opens with copies of the records it took over, under their original
uuidandtimestamp. Each transcript was imported as a session of its own, so the shared history was counted once per transcript: commands, decisions, file changes, output tokens and per-session active time, with every fork's session starting at the original's first record.What changes
basou import claude-codegroups the transcripts of the source roots by conversation: every transcript of one conversation opens with the same first uuid-bearing record, so each file is read only up to that record. Within a conversation the file created first (birth time; the session id decides where none is recorded or two are equal) keeps the shared records, and each later transcript is derived from the records it added (claudeTranscriptOwnRecords, new in@basou/core). A fork seen for the first time that added no action is not imported.reimportPreservingIdtakesdropDerivedBefore: a prior derived event dated before the fresh start may be dropped; one from the session's own time that is not reproduced still refuses the re-import (prior_derived_dropped). The session id and the ids of the kept events are preserved, and the hash chain is rewritten.--forceincluded, leaves out records dated before the start of the session an earlier import made from the same transcript, so the copies stay out after the older transcript is deleted.basou refreshsay how many re-imported sessions dropped copies; the--jsonresult ofbasou importgainsdropped_copies_count(sessions that held copies and no longer do), andreimported_countnow also counts these in-place re-derivations. A fork that added no action of its own, and a session imported before source sizes were recorded (it waits for--force), keep their copies.docs/spec/terminal-and-import.mdsection 14.2 describes the rule and a known limit: the order is read from birth times on every import, so if they change after an import (a backup restored, a copy to another disk) and the order flips, the shared records end up counted in neither session. CHANGELOG Unreleased has the entry. The shapes ofsession.yamland events are unchanged.Verification
transcript-fork.test.ts(the filter),session-reimport.test.ts(dropDerivedBefore, including the exact-bound case),import.test.ts(15 fork scenarios: beside the original, creation-time order vs. name order, unknown birth times,--session, nothing of its own, in-place re-derivation with kept ids and a verified chain, idempotence,--dry-run, original deleted then fork grows,--force, a non-append change still refused, the older transcript vanishing mid-run, copies that derived no event, a fork with no action of its own),refresh.test.ts.older.length >= 0) is behaviourally equivalent because theholds_copiespath already skips a session from which nothing was left out.basou verifyreported no tampered session.