Skip to content

fix(tree): retainHistory now retains history in summaries - #28036

Merged
Noah Encke (noencke) merged 4 commits into
microsoft:mainfrom
noencke:fix-trunk-retention
Aug 21, 2026
Merged

fix(tree): retainHistory now retains history in summaries#28036
Noah Encke (noencke) merged 4 commits into
microsoft:mainfrom
noencke:fix-trunk-retention

Conversation

@noencke

Copy link
Copy Markdown
Contributor

Description

retainHistory on SharedTreeOptions was documented as causing growth in both memory and summaries/snapshots, but it only ever prevented trunk commits from being evicted from memory. Summaries continued to contain just the collaboration window, so retained history was discarded at the next summary and was unavailable to clients that loaded from it.

This was an oversight, not an intentional design choice — confirmed against the original PR (#27696) that introduced the option, whose shipped changeset already documented the intended (summary-inclusive) behavior.

Summaries produced by a client with retainHistory enabled now contain the full trunk. History accumulated while the flag is enabled survives summarization and is available to clients that join later. History from before the flag was enabled (already evicted in a prior session) cannot be recovered. There is no change to the default (retainHistory: false) behavior, and no change to the persisted format.

New tests confirm 3 of them fail without the production fix (proving they exercise it); the full @fluidframework/tree suite (15,203 tests) passes with no regressions.

Reviewer Guidance

The review process is outlined in the pull request guidelines.

This PR received an automated rigorous code-review pass; one test assertion was strengthened as a result (exact equality instead of <= on retained commit count).

Copilot AI lite review requested due to automatic review settings August 21, 2026 04:59
@noencke
Noah Encke (noencke) requested review from a team as code owners August 21, 2026 04:59
@github-actions github-actions Bot added area: tools area: dds Issues related to distributed data structures area: repo Repo related work area: website area: dds: tree changeset-present base: main PRs targeted against main branch labels Aug 21, 2026
@github-actions

github-actions Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Hi! Thank you for opening this PR. Want me to review it?

Based on the diff (213 lines, 6 files), I've queued these reviewers:

  • Correctness — logic errors, race conditions, lifecycle issues
  • Security — vulnerabilities, secret exposure, injection
  • API Compatibility — breaking changes, release tags, type design
  • Performance — algorithmic regressions, memory leaks
  • Testing — coverage gaps, hollow tests

How this works

  • Adjust the reviewer set by ticking/unticking boxes above. Reviewer toggles alone don't trigger anything.

  • Tick Start review below to dispatch the review fleet.

  • After review finishes, tick Start review again to request another run — it auto-resets after each dispatch.

  • This comment updates as new commits land; your reviewer selections are preserved.

  • Start review

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes an oversight in SharedTreeOptions.retainHistory: enabling the flag now retains trunk history not only in-memory but also in summaries/snapshots, so later-joining clients loading from a summary can recover the retained trunk history.

Changes:

  • Update EditManager.getSummaryData() to widen the main-branch summary lower bound to the trunk base when retainHistory is enabled, causing summaries to include the full trunk.
  • Add new test coverage validating summary round-trip behavior for both retainHistory: true and the default behavior, including shared-branch base resolution across a round trip.
  • Add a changeset documenting the behavior correction and its implications (summary size/load/summarize time growth when enabled).

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
packages/dds/tree/src/shared-tree-core/editManager.ts Adjusts summarization bounds so retainHistory causes the full trunk to be written into summaries.
packages/dds/tree/src/shared-tree/sharedTree.ts Updates retainHistory option documentation to reflect summary persistence behavior and limitations.
packages/dds/tree/src/test/shared-tree/sharedTree.spec.ts Adds end-to-end-ish tests that summarize and reload a client to validate retained-history persistence and shared-branch behavior.
packages/dds/tree/src/test/shared-tree-core/edit-manager/editManagerCorrectness.test.ts Adds correctness-level tests asserting summary content differs with/without retainHistory, and persists across repeated load/summarize generations.
packages/dds/tree/src/test/shared-tree-core/edit-manager/editManagerTestUtils.ts Plumbs retainHistory through test EditManager factory helpers to support new tests.
.changeset/retain-history-in-summaries.md Records the behavior fix as a release note for @fluidframework/tree and fluid-framework.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread packages/dds/tree/src/test/shared-tree/sharedTree.spec.ts
Comment thread packages/dds/tree/src/test/shared-tree/sharedTree.spec.ts Outdated
@github-actions

Copy link
Copy Markdown
Contributor

Bundle size comparison

Base commit: 42f17839eb4b69ccb7d45489fea1244750640023
Head commit: d2284b2c6aab8cd10da01815cd019a98efe20056

Notable changes

No bundles changed by ≥ 500 bytes parsed.

Per-bundle deltas

@fluid-example/bundle-size-tests

  • fluidFrameworkAllAlpha.js: parsed 782366 → 782475 (+109), gzip 215005 → 215080 (+75)
  • azureClient.js: parsed 632351 → 632346 (-5), gzip 169406 → 169503 (+97)
  • odspClient.js: parsed 609607 → 609704 (+97), gzip 163773 → 163896 (+123)
  • aqueduct.js: parsed 536725 → 536736 (+11), gzip 144124 → 144165 (+41)
  • fluidFramework.js: parsed 401229 → 401315 (+86), gzip 114017 → 114062 (+45)
  • sharedTree.js: parsed 390608 → 390687 (+79), gzip 111442 → 111480 (+38)
  • containerRuntime.js: parsed 313633 → 313615 (-18), gzip 86017 → 86013 (-4)
  • sharedString.js: parsed 175134 → 175141 (+7), gzip 49635 → 49640 (+5)
  • experimentalSharedTree.js: parsed 161726 → 161726 (0), gzip 46665 → 46665 (0)
  • matrix.js: parsed 159511 → 159520 (+9), gzip 45849 → 45854 (+5)
  • loader.js: parsed 146720 → 146736 (+16), gzip 39875 → 39888 (+13)
  • odspDriver.js: parsed 115403 → 115458 (+55), gzip 35585 → 35663 (+78)
  • directory.js: parsed 65559 → 65566 (+7), gzip 18444 → 18451 (+7)
  • 578.js: parsed 58686 → 58686 (0), gzip 17657 → 17657 (0)
  • odspPrefetchSnapshot.js: parsed 45838 → 45819 (-19), gzip 15321 → 15331 (+10)
  • map.js: parsed 45710 → 45717 (+7), gzip 14077 → 14084 (+7)
  • 252.js: parsed 44362 → 44362 (0), gzip 13735 → 13735 (0)
  • summarizerDelayLoadedModule.js: parsed 31287 → 31287 (0), gzip 7929 → 7929 (0)
  • socketModule.js: parsed 26992 → 26962 (-30), gzip 8019 → 8053 (+34)
  • createNewModule.js: parsed 8523 → 8517 (-6), gzip 3550 → 3561 (+11)
  • summaryModule.js: parsed 3888 → 3888 (0), gzip 1874 → 1874 (0)
  • connectionState.js: parsed 909 → 909 (0), gzip 500 → 500 (0)
  • sharedTreeAttributes.js: parsed 845 → 852 (+7), gzip 493 → 503 (+10)
  • debugAssert.js: parsed 429 → 429 (0), gzip 299 → 299 (0)
  • FluidFramework-HashFallback.js: parsed 419 → 419 (0), gzip 313 → 313 (0)

@noencke
Noah Encke (noencke) merged commit 3b665a2 into microsoft:main Aug 21, 2026
35 checks passed
@noencke
Noah Encke (noencke) deleted the fix-trunk-retention branch August 21, 2026 22:02
Sonali Deshpande (sonalideshpandemsft) pushed a commit that referenced this pull request Aug 27, 2026
…8036)"

This reverts commit 3b665a2.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: db28c8d7-8cb9-4efe-bb78-a1af7d1cdc0b
Noah Encke (noencke) added a commit that referenced this pull request Aug 27, 2026
…17 (#28104)

## Description

Cherry-picks the SharedTree history and persisted-commit-metadata work
onto the
`release/client/2.117` branch. This is step 4 of the 2.117 release plan
(branch created from
`release/client/2.116` + version bump); release notes, changelogs and
assert tagging are handled
separately by the release engineers.

Four commits, applied in merge order (each with `git cherry-pick -x`, so
the original SHA is recorded
in the commit message):

| Commit on `main` | PR | What it provides |
|---|---|---|
| `22e5b4ee` |
[#27932](#27932) |
Renames the alpha `TreeBranchAlpha` interface to `UntypedTreeViewAlpha`
(old name kept as a deprecated alias, so it is additive). Carries no
feature, but #28012 is written against the new name, so it is a
prerequisite rather than an optional cleanup. |
| `3b665a2a` |
[#28036](#28036) |
`retainHistory` now retains the trunk in *summaries*, not just in
memory. Without it, retained history is discarded at the next summary
and never reaches clients that load from it. No persisted format change;
the `retainHistory: false` default is untouched. |
| `920e9469` |
[#28012](#28012) |
`UntypedTreeViewAlpha.branchHistory` (`length`, `getHead()` →
`TreeBranchCommitMetadata { revision, getParent() }`), plus
`rewindTo(revision)` and `revertTo(revision, options?)`. |
| `90337165` |
[#28064](#28064) |
Persisted commit metadata: `customMetadata` on
`RunTransactionParamsAlpha` and the revert options, read back as
`.custom` / `.customTree`. Stored inline on commits in the `EditManager`
summary under `EditManagerFormatVersion.v7` / `MessageFormatVersion.v7`,
written only when `minVersionForCollab` is at least `2.117.0`. |

A fifth commit adapts the picks to this branch — details below.

### Why a minor rather than a patch

#28064 changes a persisted format, and the format gate is a version
string baked into SharedTree's
source. The config map that selects a format version rejects patch
versions, so the gate has to be an
`X.Y.0`. It is `FluidClientVersion.v2_117 = "2.117.0"`.

### Forward compatibility with 3.0

All four commits are already on `main`, and `main`'s tip was #28064 when
this was prepared, so the two
lines could be compared directly. Everything that ships here is
identical to 3.0:

- The whole persisted-format surface — `src/codec/` and
`src/shared-tree-core/` — is byte-identical
between this branch and `main`, including the `v2_117` → v7 gate. A
mixed 2.117/3.0 session cannot
  silently disagree about the format.
- Every alpha symbol added here (`TreeBranchHistory`,
`TreeBranchCommitMetadata`, `branchHistory`,
`rewindTo`, `revertTo`, `customMetadata`, `customTree`, `retainHistory`,
`UntypedTreeViewAlpha`,
`RunTransactionParamsAlpha`, `RevertOptionsAlpha`,
`RevertToOptionsAlpha`) is identical on both
  lines, at the same API tier, with the same deprecation state.
- `minVersionForCollab: '2.117.0'` stays valid on 3.0:
`OldestSupportedClientVersion` *widens* from
`` `${1 | 2}.${bigint}.${bigint}` `` to `` `${1 | 2 | 3}....` ``, so
consumers who adopt 2.117 need
  no change in this area when they later move to 3.0.

## Adapting the picks to this branch

`main` is on TypeScript 6 and past the 3.0 bump; this branch is
TypeScript 5.4 and pre-3.0. Conflicts
were all of one shape — an import list where `main` has accumulated
names from commits that are *not*
being backported — and were resolved by keeping only what this branch
actually uses:

- `src/index.ts` — kept `asTreeViewAlpha` (removed on `main` by 3.0
work) alongside the incoming
  `TreeBranchCommitMetadata` / `TreeBranchHistory`.
- `src/shared-tree/treeCheckout.ts` — kept `StableId` and
`findAncestor`, which #28012 uses; dropped
`tagCodeArtifacts` (used on `main` only by schema-change telemetry,
#27996) and
`getDeltaChangeProfile` (introduced by #27989). Neither is backported
here.
- `src/test/shared-tree/schematizeTree.spec.ts` — kept
`TreeBranchHistory`; dropped
`UntypedTreeViewAlpha`, which was added to that import by the TypeScript
6 upgrade (#28052) rather
  than by #28012.

One genuine TypeScript-version difference, in the fifth commit:

- `customCommitMetadata.spec.ts` used `.filter((m) => m !== undefined)`
and then read `.tag`, relying
on **inferred type predicates**, a TypeScript 5.5 feature. On 5.4 the
element type stays
`T | undefined` and the compiler reports TS18048. The predicate is now
spelled explicitly. This was
  the only TypeScript error in the entire build.

The two conflicted `*.api.md` files are generated artifacts, so rather
than hand-resolving them they
were regenerated by a full clean build from the repo root. That
correctly drops `asAlpha`,
`codePointCount` and `utf16LengthForCodePoints` — whose exports come
from #28011 and #28004, not
backported here — and narrows `OldestSupportedServiceClientVersion` to
`` `2.${bigint}.0` ``.

## Validation

- Full `pnpm clean && pnpm build` from the repo root: succeeded, and
left the working tree clean apart
  from the intended API report updates. No unexpected API report drift.
- `@fluidframework/tree` test suite: **15301 passing, 488 pending, 1
failing**. The one failure is a
pre-existing Windows-only issue in
`snapshotCompatibilityChecker.spec.ts`, which builds the
directory under test with `path.join(...)` but hardcodes forward slashes
in the expected error
  string. It is untouched by these commits and passes on Linux CI.

## Reviewer Guidance

The review process is outlined in [the pull request
guidelines](../docs/content/Contributing/PR-Guidelines.md#guidelines).

- The first four commits are unmodified cherry-picks; review effort is
best spent on the fifth
(`fix(tree): adapt the backported history work to the 2.117 line`) and
on the conflict resolutions
described above, since those are the only places this branch diverges
from what was reviewed on
  `main`.
- **Assert tagging is deliberately not included here.** The three new
asserts are still untagged
string literals, so `flub release prepare client` will report them.
Running
`flub generate assertTags` on a release branch allocates short codes
from the branch's own
high-water mark, which can disagree with `main` — on `main` today it
would reassign `0xd33`, a code
that shipped in 2.116 as `"compatibilityMode must be defined"`, to a
tree assert. Happy to follow
  whatever sequencing the release engineers prefer.

---------

Co-authored-by: jzaffiro <110866475+jzaffiro@users.noreply.github.com>
Co-authored-by: yann-achard-MS <97201204+yann-achard-MS@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3af96e03-702e-49da-adaa-bc99ca75ee27
Copilot-Session: eedf6254-a76e-4efe-af90-1f7b43da8665
Copilot-Session: a037b96b-478f-4a65-9555-d7a970e7855e
Copilot-Session: 42b443d7-0621-42a1-b087-f4e4765046af
Copilot-Session: 797fba3e-0e9a-48db-86c6-530aa2837434
Sonali Deshpande (sonalideshpandemsft) pushed a commit that referenced this pull request Aug 28, 2026
This reverts commit 3b665a2.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: db28c8d7-8cb9-4efe-bb78-a1af7d1cdc0b
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: dds: tree area: dds Issues related to distributed data structures area: repo Repo related work area: tools area: website base: main PRs targeted against main branch changeset-present

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants