Skip to content

fix(sessions): remap compactMetadata preserved message UUIDs on fork (#1419) - #1428

Open
somuai wants to merge 1 commit into
anthropics:mainfrom
somuai:fix/fork-compact-metadata-uuid-remap
Open

somuai wants to merge 1 commit into
anthropics:mainfrom
somuai:fix/fork-compact-metadata-uuid-remap

Conversation

@somuai

@somuai somuai commented Oct 22, 2026

Copy link
Copy Markdown

Summary

When forking a session via fork_session() or fork_session_via_store(), transcript entries receive fresh UUIDs to ensure branch independence and parent chain integrity. However, compactMetadata on compact_boundary records was previously copied verbatim via dictionary spread (**original).

As reported in #1419, this left:

  • preservedSegment references (headUuid, tailUuid, anchorUuid)
  • preservedMessages references (anchorUuid, uuids, allUuids)

pointing to source message UUIDs that do not exist within the forked transcript, causing downstream compaction normalization and parent relinking to fail.

Changes

  1. Compaction UUID Remapping: Added _remap_compact_metadata() in src/claude_agent_sdk/_internal/session_mutations.py to remap preservedSegment (headUuid, tailUuid, anchorUuid) and preservedMessages (anchorUuid, uuids, allUuids) using uuid_mapping.
  2. Immutability & Safety: Source metadata objects and unmodified transcripts remain strictly unchanged. Unmapped IDs (e.g. when slicing with up_to_message_id) are retained safely without raising exceptions.
  3. Test Coverage:
    • Added test_fork_remaps_compact_metadata_references in tests/test_session_mutations.py verifying disk-backed forks remap all boundary references and preserve source immutability.
    • Added test_fork_session_via_store_remaps_compact_metadata in tests/test_session_helpers_store.py verifying SessionStore async forks remap all references.

Verification

  • uv run --extra dev pytest tests/test_session_mutations.py tests/test_session_helpers_store.py passed (145/145 tests).
  • Full test suite: 1631 passed, 6 skipped.
  • uv run ruff check src/ tests/ scripts/ passed with zero errors.
  • uv run ruff format --check src/ tests/ scripts/ passed.
  • uv run mypy src/ scripts/ passed in strict mode.

Closes #1419.

…nthropics#1419)

When forking a session via fork_session() or fork_session_via_store(),
message UUIDs are remapped across transcript entries. However, the
embedded compactMetadata on compact_boundary records was previously
copied verbatim, leaving preservedSegment and preservedMessages
references pointing to source message UUIDs that do not exist in the
forked transcript.

Remap headUuid, tailUuid, anchorUuid in preservedSegment, as well as
anchorUuid, uuids, and allUuids in preservedMessages using uuid_mapping.
Unmapped or sliced-out IDs are preserved without error. Source session
transcripts remain strictly unmodified.

Closes anthropics#1419.

Signed-off-by: Soumyajit Ghosh <[email protected]>
salma2vec added a commit to salma2vec/claude-agent-sdk-python that referenced this pull request Oct 28, 2026
Remap runningTurnSends using the same map as the written transcript.
Trim lists for records deliberately excluded by a cutoff or sidechain/
progress filtering, and drop blocks whose required endpoint was excluded.
Preserve historical IDs absent from the source and uninterpreted metadata.

Add public disk/store regressions and checks against actual output IDs.
Update the existing partial-fork expectation and retain malformed metadata
and source-immutability coverage.

Thanks to @tonydzi for the runningTurnSends report and @xg-gh-25 for the
partial-fork/integrity suggestions in anthropics#1419. Compared with @somuai's anthropics#1428
to keep the overlapping fixes on one reviewable path.

Developed with Codex assistance.
@salma2vec

Copy link
Copy Markdown

Hi @somuai, thanks for working on #1419 and testing both fork paths. I compared our patches: they take the same approach to preservedSegment and preservedMessages, and both need the follow-up from @tonydzi's new runningTurnSends report and @xg-gh-25's partial-fork suggestions.

I've added that follow-up to #1420 in c9fd201. It remaps the three running-turn fields, trims references to deliberately excluded records, drops blocks with excluded endpoints, and preserves historical IDs already absent from the source. The new public disk/store tests also check emitted reference integrity without interpreting arbitrary UUID-shaped strings.

On current main plus the update: 215 affected tests pass; full suite 1,701 passed, 6 skipped; Ruff/format/mypy pass. These are local unit results, not end-to-end resume validation; hosted checks await maintainer approval.

Could we consolidate into one landing PR? I suggest #1420 because it retains the broader parameterized coverage, but I'm happy to move the follow-up and tests here if maintainers prefer this PR. I've credited your overlapping implementation and both issue contributors in #1420, and posted the technical details on #1419. Let's preserve attribution whichever PR lands.

Developed with Codex assistance.

@somuai

somuai commented Oct 28, 2026

Copy link
Copy Markdown
Author

Hi @salma2vec, thanks for the thorough follow-up and for benchmarking both implementations.

Consolidating into a single PR makes total sense for the maintainers so they don't have to navigate two competing patches. Proceeding with #1420 works for me, provided co-authorship attribution is preserved in the squash commit message:

Co-authored-by: Soumyajit Ghosh <[email protected]>

I will keep #1428 open for reference until the maintainers review and land #1420, and will close it once merged. Thanks again for collaborating on this!

This branch has not been deployed

No deployments
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.

Forking a compacted session leaves preserved-message references pointing to source UUIDs

2 participants