Skip to content

feat(container-loader): add snapshot history for flat checkpoint storage#26400

Draft
anthony-murphy-agent wants to merge 5 commits intomicrosoft:mainfrom
anthony-murphy-agent:snapshot-history
Draft

feat(container-loader): add snapshot history for flat checkpoint storage#26400
anthony-murphy-agent wants to merge 5 commits intomicrosoft:mainfrom
anthony-murphy-agent:snapshot-history

Conversation

@anthony-murphy-agent
Copy link
Contributor

Summary

  • Add a .history subtree to the summary tree that preserves historical document states as checkpoint handles
  • Each checkpoint is an ISummaryTree with its own groupId for delay-loading, containing handles to .app and .protocol only (not .history) to enable independent GC
  • Checkpoints are created based on configurable time/ops intervals and retained based on a configurable max age, with support for pinned checkpoints
  • A versioned index blob provides cheap enumeration of all available checkpoints
  • HistoryTreeStorageService intercepts summary uploads to manage the .history tree
  • SnapshotHistoryManager provides a read-side API for enumerating and looking up checkpoints
  • Feature-gated via Fluid.Container.enableSnapshotHistory and related config flags
  • All isCombinedAppAndProtocolSummary call sites updated to accept .history as optional root tree

Test plan

  • Unit tests for HistoryTreeStorageService (disabled passthrough, checkpoint creation, structure, groupId, index blob, interval enforcement, retention, handle carry-forward)
  • Unit tests for parseHistoryIndex (empty, missing, valid, unknown version)
  • Unit tests for SnapshotHistoryManager (getCheckpoints, getCheckpoint, getClosestCheckpoint)
  • Integration test with local driver: create → edit → summarize → enumerate checkpoints → load checkpoint as frozen container
  • Verify no regression in existing tests (247 passing)

🤖 Generated with Claude Code

…int storage

Add a .history subtree to summaries that preserves historical document states as
checkpoint handles. Each checkpoint uses a loading group for delay-loading and
references only .app and .protocol (not .history) to enable independent GC.

Key components:
- HistoryTreeStorageService: intercepts summary uploads to add .history tree
- SnapshotHistoryManager: read-side API for enumerating/looking up checkpoints
- Config-gated via Fluid.Container.enableSnapshotHistory and related flags
- All isCombinedAppAndProtocolSummary call sites updated to accept .history

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: anthony-murphy <anthony.murphy@microsoft.com>
anthony-murphy-agent and others added 4 commits February 10, 2026 15:39
…feature

Exercise the full snapshot history flow through a real local server:
create container, edit, summarize (writes .history), load from snapshot,
and verify checkpoints. Tests use a seed summary with summarizeProtocolTree2
to establish the .app/.protocol snapshot structure needed by the local server
before enabling history-enabled summaries.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: anthony-murphy <anthony.murphy@microsoft.com>
- Clarify that `pinned` field is future-proofing, not yet used
- Add template literal return types to checkpointGroupId and checkpointKey

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: anthony-murphy <anthony.murphy@microsoft.com>
…oadCheckpoint

Promote snapshotHistory to the alpha API surface so consumers use
asLegacyAlpha(container).snapshotHistory instead of unsafe casts.
Add loadCheckpoint() method to SnapshotHistoryManager for fetching
historical checkpoint data via the driver's getSnapshot API.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: anthony-murphy <anthony.murphy@microsoft.com>
…inline literal types

Address review feedback: use `fluid-history-${number}` and `cp-${number}` directly
instead of `${typeof constant}${number}` for better readability.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: anthony-murphy <anthony.murphy@microsoft.com>
@anthony-murphy-agent
Copy link
Contributor Author

Pipeline checks are missing (fork PR). Please post the following commands to trigger them:

/azp run Build - api-markdown-documenter, Build - benchmark-tool, Build - build-common, Build - build-tools, Build - client packages, Build - common-utils, Build - eslint-config-fluid, Build - eslint-plugin-fluid, Build - protocol-definitions, Build site
/azp run Build - test-tools, repo-policy-check, server-gitrest, server-gitssh, server-historian, server-routerlicious

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds snapshot history functionality to the Fluid Framework, allowing the preservation of historical document states as checkpoints within a .history subtree in the summary tree.

Changes:

  • Implements HistoryTreeStorageService to intercept summary uploads and manage checkpoint creation, retention, and storage
  • Implements SnapshotHistoryManager as a read-side API for enumerating and loading historical checkpoints
  • Updates isCombinedAppAndProtocolSummary across all call sites to accept .history as an optional root tree
  • Adds comprehensive unit and integration tests
  • Feature-gated via Fluid.Container.enableSnapshotHistory and related configuration flags

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
packages/loader/container-loader/src/snapshotHistory.ts Core implementation of HistoryTreeStorageService and SnapshotHistoryManager classes, plus helper functions for parsing checkpoint index
packages/loader/container-loader/src/container.ts Integrates snapshot history into Container, exposes snapshotHistory property on ContainerAlpha interface, parses and initializes checkpoints on load
packages/loader/container-loader/src/containerStorageAdapter.ts Creates HistoryTreeStorageService in the storage service middleware chain, exposes it via historyStorageService property
packages/loader/container-loader/src/utils.ts Updates isCombinedAppAndProtocolSummary calls to accept .history as optional tree
packages/loader/container-loader/src/index.ts Exports snapshot history types and classes
packages/loader/driver-utils/src/treeConversions.ts Updates convertSummaryTreeToSnapshotITree to include .history tree when present
packages/drivers/*/ Updates all driver create-new paths to accept .history as optional root tree
packages/loader/container-loader/src/test/snapshotHistory.spec.ts Comprehensive unit tests for HistoryTreeStorageService, parseHistoryIndex, and SnapshotHistoryManager
packages/test/local-server-tests/src/test/snapshotHistory.spec.ts Integration tests verifying end-to-end functionality with local server
packages/loader/container-loader/api-report/container-loader.legacy.alpha.api.md API surface additions for snapshot history feature

Comment on lines +26 to +27
...((summaryTree as ISummaryTree).tree[".history"] !== undefined
? ([[".history", (summaryTree as ISummaryTree).tree[".history"]]] as [string, ISummaryTree["tree"][string]][])
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type assertion (summaryTree as ISummaryTree) is redundant since summaryTree is already typed as ISummaryTree from the function parameter. The code can directly access summaryTree.tree[".history"] without the cast.

Suggested change
...((summaryTree as ISummaryTree).tree[".history"] !== undefined
? ([[".history", (summaryTree as ISummaryTree).tree[".history"]]] as [string, ISummaryTree["tree"][string]][])
...(summaryTree.tree[".history"] !== undefined
? ([[".history", summaryTree.tree[".history"]]] as [string, ISummaryTree["tree"][string]][])

Copilot uses AI. Check for mistakes.
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.

2 participants