Skip to content

Add staged optional-to-required field migration API for SharedTree schema - #5

Draft
noencke wants to merge 223 commits into
mainfrom
work/W-msqynx4d00al7d6c
Draft

Add staged optional-to-required field migration API for SharedTree schema#5
noencke wants to merge 223 commits into
mainfrom
work/W-msqynx4d00al7d6c

Conversation

@noencke

@noencke noencke commented Aug 13, 2026

Copy link
Copy Markdown
Owner

Scenario

Applications routinely discover that a field they originally modeled as optional is really required. Today there is no safe way to make that change: tightening the stored field kind from Optional to Required immediately breaks every client that is still running the old code, so the change requires a coordinated, all-at-once deployment — which is not something a collaborative document service can do.

SharedTree already solves the opposite direction (SchemaFactoryAlpha.stagedOptional, required → optional) and the analogous problem for allowed types (SchemaStaticsBeta.staged). This PR adds the missing optional → required counterpart so that a schema author can roll the change out incrementally.

What changed

New alpha API SchemaFactoryAlpha.stagedRequired (plus stagedRequiredRecursive for recursive schema), with the same rollout shape as stagedOptional but inverted runtime semantics.

class Point extends sf.objectAlpha("Point", {
	x: sf.number,
	y: sf.stagedRequired(sf.number),
}) {}

Rollout contract

  1. Version N uses sf.optional(T). Stored field kind is Optional.
  2. Version N+1 uses sf.stagedRequired(T).
    • In the view schema and in the TypeScript types the field is required: it must be supplied when constructing nodes, and it cannot be assigned or inserted as undefined, nor deleted.
    • The stored schema stays Optional, so version N clients are unaffected and documents they created remain viewable. The view explicitly tolerates a matching Optional stored field (discrepancies.ts).
  3. Version N+2 (opt-in) — once version N clients are extinct, the application explicitly enables the staged upgrade (the includeStaged filter of extractPersistedSchema, internally StoredFromViewSchemaGenerationOptions.includeStagedRequired), tightening the stored field kind from Optional to Required. Without that explicit opt-in, TreeView.upgradeSchema leaves this staged change a no-op — identical to stagedOptional today.
  4. Version N+3 uses sf.required(T) and drops the staged marker.

Virtualization rationale

A document written by a version N client may contain nodes where the field is empty. Detecting that eagerly would require scanning/materializing the whole document at load, which defeats SharedTree's virtualized loading. So enforcement is lazy:

  • Opening the document and creating a view never scan or materialize the tree; compatibility.canView is true.
  • Reading that specific field throws a UsageError (Staged required field … has no value in this document.). Unrelated parts of the document remain fully usable.
  • Nothing is synthesized and nothing is written during reads.
  • Presence can be tested without try/catch: TreeAlpha.child(node, key) (existing public alpha API — reused and documented rather than duplicated) for object fields, and the new TreeViewAlpha.isRootPresent() for the root field.

Monotonic upgrade guard (stagedRequiredUpgrades.ts)

Because this staged change narrows the stored schema, the naive upgrade computation has a hazard that stagedOptional does not have: after the operator tightens the stored field to Required, a still-running stagedRequired client would compute an "upgrade" back to Optional — a legal widening — and upgradeSchema() would silently revert the tightening.

computeUpgradeSchema(viewSchema, stored) fixes this by computing the upgrade target relative to the current stored schema: staged-required fields that are already tightened stay tightened. It is used by both SchemaCompatibilityTester.checkCompatibility and SchematizingSimpleTreeView.upgradeSchema. It never applies a staged required upgrade on its own.

Compatibility caveat

Step 3 assumes version N clients have been operationally phased out. A stagedRequired client refuses to clear the field itself, so the remaining race is limited to concurrent clients from two rollout generations behind. This is not a guarantee of safety against arbitrarily old concurrent clients — it is an operational precondition, and it is documented as such in the API TSDoc and the changeset.

Existing compatibility behavior outside this explicit staged case is unchanged; schema narrowing is still not generally permitted (the discrepancy tolerance is scoped to fields carrying a staged-required marker).

Validation

All commands run from the worktree root unless noted.

Command Result
npx fluid-build --task build "@fluidframework/tree" Build succeeded (68/68 tasks) — includes tsc, test tsc, CJS build, eslint --quiet, biome check, depcruise, and all api-extractor runs
npx fluid-build --task build "fluid-framework" Build succeeded (53/53 tasks) — regenerated the aggregator API report
npx mocha --grep "staged required" (in packages/dds/tree) 6 passing — the new suite
npx mocha (full @fluidframework/tree ESM suite, in packages/dds/tree) 14205 passing, 447 pending, 2 failing — both failures are pre-existing/environmental, see below
npx biome check . (in packages/dds/tree) ✅ clean
npx eslint --quiet --format stylish src (in packages/dds/tree) ✅ clean
npx api-extractor run --local (in packages/dds/tree) ✅ completed successfully

New tests (src/test/simple-tree/api/stagedSchemaUpgrade.spec.ts, describe("staged required upgrade")):

  1. projects to an Optional stored field before opt in and Required after
  2. reports the expected compatibility across all three rollout phases (also asserts computeUpgradeSchema stays Required after tightening — the monotonicity guard)
  3. reads a present value and fails lazily on an absent root (covers isRootPresent(), the lazy throw, and the blocked undefined write)
  4. fails lazily on an absent object field while leaving the rest of the document usable (covers TreeAlpha.child presence check, blocked assignment and delete, and repair-by-write)
  5. requires a value when constructing nodes
  6. works with stagedRequiredRecursive in a recursive schema

Pre-existing / environmental failures (not introduced here)

  • snapshotCompatibilityChecker › write current view schema snapshot — the assertion compares an error message containing a filesystem path; actual uses \ separators and expected uses /. A Windows-only path-separator mismatch in the test's expected string, unrelated to this change.
  • TableSchema Benchmarks › Undo: Set a cell value 3 times — 2000 ms mocha timeout on a benchmark test; timing/environmental.

Neither test touches staged schema upgrades or any file in this diff.

Scope

Scope: 20 changed files, one concern.

This is a single indivisible contract: a staged field marker is meaningless without its stored-schema projection, its compatibility tolerance, its read/write enforcement, and its upgrade monotonicity guard. Any strict subset ships a broken or misleading intermediate state — e.g. a marker that type-checks but silently permits undefined, or an enforced view kind whose upgradeSchema reverts the operator's tightening. Splitting was considered and rejected for that reason; there is no compatibility seam that makes an intermediate slice independently safe.

Review map — the real change is concentrated in a few files; the rest is small, mechanical fallout.

Read in this order:

  1. simple-tree/api/schemaFactoryAlpha.ts — the public API and the authoritative TSDoc for the rollout contract.
  2. simple-tree/fieldSchema.ts, simple-tree/simpleSchema.ts, simple-tree/core/toStored.ts — the stagedRequiredUpgrade marker, isStagedRequired, and the includeStagedRequired option.
  3. simple-tree/toStoredSchema.ts — view → stored projection (Optional before opt-in, Required after). Note both the restrictive and permissive option sets return false: tightening is never "more permissive".
  4. simple-tree/api/stagedRequiredUpgrades.ts (new) — the monotonicity guard. The most novel piece.
  5. simple-tree/node-kinds/object/objectNode.ts and shared-tree/schematizingTreeView.ts — lazy read failure, blocked clears, isRootPresent.
  6. simple-tree/api/discrepancies.ts — the scoped compatibility tolerance.
  7. test/simple-tree/api/stagedSchemaUpgrade.spec.ts — the new coverage.

Mechanical / generated:

  • api-report/tree.alpha.api.md, api-report/fluid-framework.alpha.api.md — regenerated artifacts.
  • simple-tree/index.ts, simple-tree/api/index.ts, simple-tree/api/storedSchema.ts, simple-tree/api/schemaCompatibilityTester.ts — re-exports and wiring.
  • test/testTrees.ts, test/simple-tree/toStoredSchema.spec.ts — two call sites each gaining the now-required includeStagedRequired option.
  • .changeset/staged-required-field-migration.md — required release metadata.

No required tests, docs, migrations, or generated artifacts were dropped to shrink this number.

Notes for reviewers

  • getOwnPropertyDescriptor on object nodes is deliberately left non-throwing (enumerable: field !== undefined) so Object.keys, spread, and deep-equality keep working on documents with an absent staged-required field. Only reading the specific field fails.
  • getStagedRequiredUpgrade / throwStagedRequiredFieldMissing are internal helpers; they are not added to the package's public entrypoints (src/index.ts is untouched, so no entrypoint regeneration was needed).
  • No visual/UI surface — no screenshots applicable.

Runtime: copilot · Instructions: .github/copilot-instructions.md

Authored with Minions.

brrichards and others added 30 commits June 30, 2026 13:50
…oft#27588)

## Description

Enables Incremental Summariation for plain text. Plain text is contained
in large uniform chunks, and will benefit from incremental
summarization. Formatted is currently not stored in a way where
incremental summarization will provide a benefit compared to the
overhead cost of incremental summarization.

## Testing
Single test in textDomain.spec.ts that confirms the plain text character
array is opted into incremental summarization.

## Reviewer Guidance

The review process is outlined on [this wiki
page](https://github.com/microsoft/FluidFramework/wiki/PR-Guidelines#guidelines).
## Description

Continuation of microsoft#27614 to the server directory.
…icrosoft#27610)

`RunTransactionParams` now accepts an optional `postProcessor` (used by
`runTransaction` and `runTransactionAsync`). When specified, the
post-processor is invoked when the transaction is committed, allowing
for additional processing of the transaction's edits.

The option is supplied when the transaction starts and is opt-in: when
it is omitted the existing behavior is preserved.

Note: a minimization post-processor will be added in a future change.

New test cases:
```text
  sharedTreeView
    Transactions
      ✔ converts a post-processor and injects it as the post-processor at transaction start
      ✔ injects no post-processor when no params are provided to runTransaction
      ✔ injects the post-processor at start for the outermost transaction only
      ✔ converts and injects a post-processor through runTransactionAsync
      ✔ does not commit (and so does not apply the post-processor) when rolled back

  SquashingTransactionStacks
    transaction post-processing
      ✔ is invoked with the squashed change when started with a post-processor
      ✔ is not invoked when the transaction is empty
      ✔ is not invoked when the transaction is aborted
      ✔ invokes an "outermost" post-processor once for the outermost transaction started
      ✔ invokes the "outermost" post-processor for an inner transaction when the outer has no post-processor
      ✔ invokes the "outermost" post-processor for an inner transaction when the outer has different post-processor
      ✔ invokes an "always" post-processor at every transaction commit that supplied it
      ✔ invokes an "always" post-processor only where it was supplied
      ✔ invokes an "outermost" post-processor once per sibling nested transaction that supplied it
      ✔ invokes an "outermost" post-processor once when the outer and both sibling nested transactions supply it
      ✔ invokes a mix of "outermost" and "always" post-processors in the expected order
      ✔ applies the change returned by the post-processor to the branch
```
…ly (microsoft#27612)

## What

Follow-up to microsoft#27503. Removes the `(legacy)` dual-write upload tasks so
the docs storage uploads go **only** to the new Torus account
(`fluid-docs-torus` / `$(STORAGE_ACCOUNT_NEW)` = `fluidframeworkcdn`).

- `publish-api-model-artifact.yml`: 6 upload tasks → 3 (drop the 3
legacy ones)
- `templates/include-upload-release-reports.yml`: 2 upload tasks → 1
(drop the legacy one; keep the `always()` cleanup step)

Remaining tasks renamed back to their original names (the `(Torus)`
qualifier is redundant now).

## Why

The AFD origin behind `storage.fluidframework.com` was cut over to the
`fluidframeworkcdn` (Torus) account via managed identity on
**2026-06-26** and verified in production (both the FF "Website
validation" and office-bohemia "External Partner to Loop Integration"
consumer pipelines are green; endpoint serves 200s). The old MSIT
`fluidframework` account is no longer the CDN origin, so dual-writing to
it is dead weight. This completes SFI task **#47248** (MI auth, no SAS).

## Note

The old account is now write-free from these pipelines and can be
decommissioned separately (it still holds non-served archives: `$web`,
historical `api-extractor` SHA tarballs, `storybook`).

---------

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

Adds onto the testing suite for plain/formatted text in
`textDomain.integration.bench.ts`. Includes helpers and setup for the 7
tests. Tests against multiple sized documents of N-character sizes. The
tests include these performance and memory tests:
1. Summary size
2. Fresh document size (no edits/history) 
3. Forest footprint (chunked forest)
4. full string read time 
5. editing time (done in middle of the doc) 
6. hydrated vs unhydrated views
7. load time from summary

## Whole-document — forest comparison results

### Summary size (bytes) — identical across forests

| size | plain (both) | formatted (both) |
|---|---|---|
| 10 | 3.50 KiB | 15.24 KiB |
| 100 | 4.56 KiB | 22.10 KiB |
| 1,000 | 15.10 KiB | 90.65 KiB |
| 10,000 | 120.57 KiB | 776.20 KiB |

### Memory use of fresh document (Mean Usage)

| size | plain chunked | plain object | formatted chunked | formatted
object |
|---|---|---|---|---|
| 10 | 82.03 KiB | 83.46 KiB | 179.18 KiB | 234.42 KiB |
| 100 | 90.12 KiB | 129.91 KiB | 856.15 KiB | 1.42 MiB |
| 1,000 | 94.65 KiB | 568.87 KiB | 7.29 MiB | 13.25 MiB |
| 10,000 | 199.78 KiB | 4.81 MiB | 71.61 MiB | 131.42 MiB |

### Forest footprint (Mean Usage)

| size | plain chunked | plain object | formatted chunked | formatted
object |
|---|---|---|---|---|
| 10 | 84.51 KiB | 93.01 KiB | 182.95 KiB | 229.39 KiB |
| 100 | 85.73 KiB | 129.91 KiB | 853.87 KiB | 1.42 MiB |
| 1,000 | 93.64 KiB | 570.16 KiB | 7.28 MiB | 13.24 MiB |
| 10,000 | 197.20 KiB | 4.81 MiB | 71.83 MiB | 131.42 MiB |

### End-to-end read — `fullString` (ns/op unless noted)

| size | plain chunked | plain object | formatted chunked | formatted
object |
|---|---|---|---|---|
| 10 | 8,189 | 7,857 | 15,330 | 14,613 |
| 100 | 13,065 | 14,026 | 70,541 | 64,620 |
| 1,000 | 65,591 | 77,088 | 546,402 | 578,532 |
| 10,000 | 613,764 | 708,192 | 5.56 ms | 9.45 ms |

### End-to-end edit — type 1 char (ns/op)

| size | plain chunked | plain object | formatted chunked | formatted
object |
|---|---|---|---|---|
| 10 | 91,650 | 90,969 | 174,693 | 203,411 |
| 100 | 69,992 | 85,681 | 173,108 | 197,504 |
| 1,000 | 68,189 | 86,298 | 186,945 | 212,003 |
| 10,000 | 69,135 | 97,229 | 316,007 | 258,596 |

### View hydration — plain (ns/op unless noted)

| size | unhydrated chunked | hydrated chunked | unhydrated object |
hydrated object |
|---|---|---|---|---|
| 10 | 39,175 | 386,693 | 40,931 | 412,430 |
| 100 | 220,318 | 667,692 | 231,767 | 807,800 |
| 1,000 | 2.60 ms | 3.51 ms | 2.66 ms | 4.54 ms |
| 10,000 | 24.76 ms | 32.14 ms | 25.48 ms | 40.88 ms |

### View hydration — formatted (ns/op unless noted)

| size | unhydrated chunked | hydrated chunked | unhydrated object |
hydrated object |
|---|---|---|---|---|
| 10 | 454,353 | 1.39 ms | 430,186 | 1.63 ms |
| 100 | 4.37 ms | 8.16 ms | 4.37 ms | 9.88 ms |
| 1,000 | 42.80 ms | 90.24 ms | 40.55 ms | 103.17 ms |
| 10,000 | 429.54 ms | 2.15 s | 428.39 ms | 1.99 s |

### Load time from summary (ns/op unless noted)

| size | plain chunked | plain object | formatted chunked | formatted
object |
|---|---|---|---|---|
| 10 | 931,222 | 900,292 | 1.45 ms | 1.56 ms |
| 100 | 1.03 ms | 1.10 ms | 2.86 ms | 3.32 ms |
| 1,000 | 2.81 ms | 2.88 ms | 15.47 ms | 21.71 ms |
| 10,000 | 18.80 ms | 21.94 ms | 180.11 ms | 242.00 ms |



## Reviewer Guidance

The review process is outlined on [this wiki
page](https://github.com/microsoft/FluidFramework/wiki/PR-Guidelines#guidelines).
## Description

Its common for both humans and agents to faile to include
fluid-frameework in tree api impacting changesets. This should help
clarify when its required.
## Description

git ignore .npmrc
## Description

Updates remaining workspaces to pnpm 11.
…rosoft#27592)

TreeView transaction APIs have been promoted to beta

`TreeViewBeta` now exposes `runTransaction` and `runTransactionAsync`
methods.

### Type Name Changes

With the introduction of new beta types, existing alpha types have been
replaced with new alpha and beta variants.
| Old | New Alpha | New Beta |
| --- | --- | --- |
| `RunTransactionParams` | `RunTransactionParamsAlpha` |
`RunTransactionParamsBeta` |
| `TransactionCallbackStatus` | `TransactionCallbackStatusAlpha` |
`TransactionCallbackStatusBeta` |
| `VoidTransactionCallbackStatus` | `VoidTransactionCallbackStatusAlpha`
| `VoidTransactionCallbackStatusBeta` |

**Other Renames**

- `TransactionResult` (alpha) -> `TransactionVoidResult` (beta)
- `TransactionResultExt` (alpha) -> `TransactionValueResult` (beta)


[AB#58860](https://dev.azure.com/fluidframework/235294da-091d-4c29-84fc-cdfc3d90890b/_workitems/edit/58860)
Many API reports are generated from generated entrypoints and not
directly from build. So add common dependency on api-extractor:esnext.
Previously, devtools would (upon initialization) immediately register
on-op event listeners on all provided root DDSs and immediately begin
broadcasting data visualization messages on the window (regardless of
whether or not the browser extension was running and listening).

This PR updates devtools to register these listeners lazily on demand.
This leverages the existing laziness that was implemented for *nested*
DDSs, such that we only register event listeners when we receive the
`GET_DATA_VISAUALIZATION` message from the browser extension.

A corresponding `CLOSE_DATA_VISUALIZATION` message has been added, which
the extension now uses to indicate when it is done needing data from the
client. The client leverages this alongside simple ref-counting to
unsubscribe DDS listeners when there is no longer interest in data.

- Note: this PR does not address the potential listener leak that can
occur if a consumer requests data events but fails to send the
corresponding close message. But since the code previously never
de-registered listeners once initially registered, this is still a
strict improvement. We can follow up on this in the future as needed.

Note that there are no cross-layer compat issues here between the
devtools client library and the extension.
| client ↓ \ extension → | Old extension (doesn't send `CLOSE` message)
| New extension (sends `CLOSE` message) |
| --- | --- | --- |
| **Old client** (eager broadcast, no `CLOSE` handler) | **Baseline.**
Eager broadcasting of all reachable roots; subscriptions never released.
Works as before. | **Compatible.** Old core ignores the unrecognized
`CLOSE_DATA_VISUALIZATION`. Still eager; view filters incoming
`DATA_VISUALIZATION` by `fluidObjectId` and detaches handlers on
unmount, so extra/stale broadcasts are harmless. No regression. |
| **New client** (on-demand, ref-counted, `CLOSE` handler) |
**Compatible, strict improvement.** Nothing broadcasts until the view
sends `GET_DATA_VISUALIZATION`. Old view never sends `CLOSE`, so each
subscribed node stays subscribed after collapse — **subscription leak**,
but behavior is no worse than the Old-core baseline (which monitored
everything unconditionally). | **Fully intended behavior.** On-demand
subscribe on expand/mount, balanced unsubscribe on collapse/unmount.
Monitoring (and broadcasting) occurs only while a view is actively
displaying an object. No leak (assuming proper view teardown). |
…ack configurations (microsoft#27597)

Robustness improvements for summaryDelayLoadedModule

- Tweaked the webpack boundary for the delay-loaded summarizer so that
it ends up in its own chunk even on bundlers that don't trace re-export
provenance through the barrel.
- Added tests.

## Motivation

Webpack assigns each module to a chunk; a dynamically import()-ed module
gets its own lazy chunk unless it is already guaranteed to be present in
an ancestor (initial) chunk. With `providedExports: true`, for example,
webpack traces re-exports per exported symbol, not per whole module. A
barrel being statically imported does not make all of its re-exported
modules statically reachable, only the modules providing the symbols
that are actually used statically.

Before this change, ContainerRuntime delay-loaded the summary barrel
rather than summaryDelayLoadedModule directly. A bundler that treats the
barrel as a monolith (no per-export tree-shaking) would then see the
summarizer as statically available and fold it into the initial chunk. A
bundler that does trace re-exports per symbol (webpack here) already
isolates the summarizer subgraph into its own chunk, because the
barrel's static importers use only non-summarizer symbols.

## Details

- The webpack boundary has a different effect depending on whether the
output bundle is multi-chunk or single-chunk.
- Multi-chunk is most common in web clients where some chunks are
delay-loaded.
  - Single-chunk is typical of a JS bundle included in a mobile client.
- Polyfilling summaryDelayLoadedModule with and without the webpack
boundary change shrinks single-chunk bundles. For example: the Fluid
bundle for a mobile application that does no summarization. Note that
the polyfill itself is outside the scope of this change, and must be
injected by the webpack config producing the bundle. This is typically
defined by the application's build.
- This change is primarily about determinism/robustness for multi-chunk
clients, not bytes. Under webpack's per-export tree-shaking the
summarizer is already split before the change; importing
summaryDelayLoadedModule directly keeps it split even on bundlers that
don't trace re-export provenance while honoring sideEffects. The
summarizer modules are identical, so the multi-chunk size delta is
negligible. For example, with `optimization: { providedExports: false }`
the old barrel import folds summaryDelayLoadedModule into the initial
chunk, whereas the leaf import in this change keeps it in its own lazy
chunk (which is exactly what the added test asserts).
- The block with the isSummarizerClient guard only imports two
definitions, both of which come from
summary/summaryDelayLoadedModule/index.ts before and after the change
(summary/index.ts re-exports these definitions). All other imports from
summaryDelayLoadedModule outside of the delay-load block are type-only
and do not add edges to the webpack graph. As a result the code is
functionally identical with the base revision for both multi-chunk and
single-chunk scenarios.
- Because the base and this change are functionally identical,
polyfilling has the same effect on either revision: the
summaryDelayLoadedModule definitions (the summarizer subgraph) are
replaced by the stub and dropped from the bundle.
## Description

Update to pnpm 11.9

This pulls in a bunch of fixes, see
https://github.com/pnpm/pnpm/releases
…from a connected handler (microsoft#27637)

## Description

Fixes a container crash (assert `0x3eb`, "catchUpMonitor should be
gone") that can occur when an application forces read-only mode
synchronously from a container `"connected"` event handler.

**Root cause:** When a read connection is already caught up at the
moment it reaches `Connected`, `CatchUpMonitor` fires its caught-up
listener *synchronously from its constructor* — before
`ConnectionStateCatchup` assigns `this.catchUpMonitor = new
CatchUpMonitor(...)`. That listener raises the container `"connected"`
event. If an app handler reacts by disconnecting (e.g.
`forceReadonly(true)`), the `Disconnected` case runs
`this.catchUpMonitor?.dispose(); this.catchUpMonitor = undefined`, which
**no-ops against the not-yet-assigned field**. As the stack unwinds, the
stale monitor gets attached, and the next reconnect's transition to
`Connected` trips `assert(this.catchUpMonitor === undefined, 0x3eb)`.

**Fix:** Split the synchronous caught-up check out of the
`CatchUpMonitor` constructor into an explicit `start()` method.
`ConnectionStateCatchup` now assigns `this.catchUpMonitor` **before**
calling `start()`, so any re-entrant disconnect during the connection
transition observes a fully-assigned field and clears the monitor
correctly. This defends against re-entrancy from any source (app
handlers, runtime, etc.).

### Reproduction / verification

A regression test in `connectionStateHandler.spec.ts` reproduces the
exact conditions:
1. connection already caught up at connect (`lastSequenceNumber ===
lastKnownSeqNumber`), and
2. a synchronous re-entrant `Disconnected` from within the `"connected"`
notification.

Against the previous code it threw `0x3eb`; with the fix the reconnect
proceeds cleanly to `Connected`. `catchUpMonitor.spec.ts` was updated
for the constructor/`start()` split. Full suite: 287 passing.

## Reviewer Guidance

The review process is outlined on [this wiki
page](https://github.com/microsoft/FluidFramework/wiki/PR-Guidelines#guidelines).

- `CatchUpMonitor` / `ICatchUpMonitor` are internal (not exported), so
there is no public API surface change and no API report/type-test
updates.
- Worth confirming: the `start()`-after-assignment ordering is the
intended invariant, and no other caller constructs `CatchUpMonitor`
expecting the constructor to fire synchronously.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Abram Sanderson <Abram.sanderson@gmail.com>
Co-authored-by: Joshua Smithrud <54606601+Josmithr@users.noreply.github.com>
…pack-dir / fetch-on-reuse) (microsoft#27627)

This change consists of two improvements to the `bundleAnalysisRepo`
tooling in `build-cli`, one of which enables webpack "scenarios."

## Description

Webpack scenarios are a feature that enables the analysis of webpack
configurations and specific sets of imports that are designed to match
real use cases. This differs from our typical approach of using a single
webpack config for repo-wide analysis. New scenarios are defined under
/examples/utils/bundle-size-tests/scenarios.

1. **`--webpack-dir` flag**: Decouples the directory that webpack runs
in (and where `analyzer.json` / `compareBundlesOutput` are collected)
from `--package-dir` (where `build:compile` runs). This lets a scenario
subdirectory that only contains a webpack config be built and collected
correctly. Previously `npm run webpack` in a config-less subdir walked
up to the parent `package.json` and built the wrong config. The flag is
added to `generate bundleAnalysisRepo`, `generate
bundleAnalysisReposWithComparison`, and `check
bundleAnalysisReposComparison` (defaulting to `--package-dir`, so
existing usage is unchanged).

2. **Fetch-on-reuse for the inner base-repo clone**: When an inner
`base-repo` clone already exists, `ensureInnerRepoAtRevision` now runs
`git fetch origin --no-tags --prune` before checkout, bringing a reused
clone to parity with a fresh one. This fixes the stale-clone `fatal:
unable to read tree <sha>` failure for merge-base / revision SHAs
created after the original clone, without forcing a full re-clone.

Also adds an `encapsulated-no-tree` bundle scenario under
`examples/utils/bundle-size-tests` used to exercise and validate both
features end-to-end.
## Description

Output from flub package commands was a mess.

In at least the cases I tested (mainly errors) I have confirmed this to
be much better.
\
## Description

Now that we are on pnpm 11, dedupe shouldn't risk introducing trust
violations, so time to dedupe again.
## Description

Run `pnpm policy-check:asserts` for release.
## Description

Run `pnpm run -r layerGeneration:gen` for release.
## Description

pnpm flub generate releaseNotes -g client -t minor --outFile
RELEASE_NOTES/2.111.0.md
pnpm flub generate changelog -g client
## Description

Bump version to 2.112.0
## Description

Factor out IdDecodingContext, better encapsulating the ID specific logic
and make it possible to reuse in other contexts.
…ing docs (microsoft#27651)

## Description

The `withDefault` API docs (TSDoc) and the user-facing documentation
page existed independently without linking to each other, making it
harder for users to discover the full picture from either entry point.

This PR improves the `withDefault` documentation in two ways:

- **API docs (TSDoc)**: Expanded the `withDefault` remarks with
structured guidance on required vs optional field defaults, value vs
generator defaults, and a self-contained example showing a realistic
schema. Added `{@link}` references to the fluidframework.com default
field values guide from both `withDefault` and `withDefaultRecursive`.
Added a cross-reference from `NodeProvider` back to `withDefault`.

- **User-facing docs (website)**: Linked `withDefault`,
`withDefaultRecursive`, and `NodeProvider` inline where they are first
referenced, so users can jump to the API reference for type signatures
and parameter details. Expanded the See Also section with direct links
to individual API members.

## Reviewer Guidance

The review process is outlined on [this wiki
page](https://github.com/microsoft/FluidFramework/wiki/PR-Guidelines#guidelines).

Documentation-only change -- no API surface or behavioral changes. No
changeset needed.

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

Updates a set of pinned transitive dependency versions across all pnpm
workspaces in the repository, using the established `overrides:`
mechanism in each `pnpm-workspace.yaml`, and regenerates the affected
lockfiles.

### Packages updated

| Package | Notes |
| --- | --- |
| `axios` | Pre-1.0 line pinned to `^0.32.0`; 1.x line pinned to
`1.16.0` |
| `ws` | 8.x line pinned to `^8.21.0`; 7.x line pinned to `^7.5.11`
where present |
| `fast-uri` | `^3.1.2` |
| `tmp` | `^0.2.6` (no release exists in the 0.0.x/0.1.x lines) |
| `form-data` | 4.x line pinned to `^4.0.6` |
| `langsmith` | `^0.6.0` |
| `systeminformation` | `^5.31.6` |
| `simple-git` | `^3.36.0` |
| `@nevware21/ts-utils` | `^0.14.0` |
| `@github/copilot` | `^1.0.43` |

### Scope

The overrides are applied per-workspace (each release group maintains
its own `overrides:` block and lockfile). Only the workspaces that
actually resolved an affected version were touched. Intentionally
version-pinned cross-compat test workspaces and test fixtures were left
unchanged.

The direct `simple-git` devDependency declarations were also bumped to
`^3.36.0` to keep them consistent with the override, satisfying the
dependency-version consistency check.

### Validation

- Regenerated every affected lockfile and confirmed the intended
versions resolve.
- `flub check policy` passes.
- Lockfile supply-chain policy checks pass.

These are override + lockfile changes plus matching version-range
alignment, so no changesets are required.

---------

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

The normal:
```
# Update type test baseline
pnpm exec flub typetests -g client --reset --normalize --previous
pnpm install --no-frozen-lockfile
pnpm run build
# Update set of prior FluidFramework package versions we run compatibility tests against
pnpm run --filter=@fluid-private/test-version-utils update-compat-versions
```

Plus a `pnpm dedupe` (this is safe now we are on pnpm 11)
…aCompatibility function (microsoft#27658)

## Description

Converts the `SchemaCompatibilityTester` class into a standalone
`checkSchemaCompatibility` function. The class only stored a
`viewSchema` in its constructor and had a single `checkCompatibility`
method, making it an unnecessary wrapper — a plain function is simpler
and more idiomatic.

This is an internal-only refactor; `SchemaCompatibilityTester` was not
part of the public API surface. No behavioral changes.

### Changes

- **`schemaCompatibilityTester.ts`** — replaced class with exported
`checkSchemaCompatibility(viewSchema, stored)` function
- **`schematizingTreeView.ts`** — field type changed from
`SchemaCompatibilityTester` to `TreeSchema`; call sites updated
- **`snapshotCompatibilityChecker.ts`**, **`storedSchema.ts`** —
replaced `new SchemaCompatibilityTester(…).checkCompatibility(…)` with
direct function call
- **`api/index.ts`**, **`simple-tree/index.ts`** — updated re-exports
- **Test files** — updated imports, call sites, and describe block names
- **Doc comments** — updated `@link` and prose references

## Reviewer Guidance

The review process is outlined on [this wiki
page](https://github.com/microsoft/FluidFramework/wiki/PR-Guidelines#guidelines).

Straightforward mechanical refactor — each call site simply inlines what
was previously a constructor + method call into a single function call.
## Description

I don't think we really any anyone making apps based on copying these
"examples" so I don't think there is much value in making the webpack
settings we recommend clear inline in every single webpack config,
especially given they already use our example utils which external apps
shouldn't. Centralizing them makes maintaining them easier.
Adds a new agent skill for triaging Azure DevOps pipeline test failures
in Fluid Framework.

### What it does

• Guides analysis of failures in an ADO pipeline run and classifies each
into one of three buckets: service flakiness, infra/harness, or
deterministic product/test bug.
• Distinguishes flaky from deterministic failures by comparing against
adjacent runs, and can file well-formed ADO bugs for genuine failures.
• Uses the Real Service E2E Tests pipeline as the worked example, but
applies to any FF test pipeline.

### Key guidance baked in

• A tiered, cost-aware source strategy (Playwright Tests tab → ADO MCP →
Test Results/Timeline REST → bulk log download as last resort).
• FF-specific failure signatures and driver awareness ( [odsp] ,
 [r11s-frs] ,  [t9s] , etc.).
• Warns that green builds can hide Attempt-1 failures that passed on
retry.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…microsoft#27628)

## Description

Promotes the `Component` composition namespace out of the
`openPolymorphism.integration.ts` test file and into
`@fluidframework/tree` as a new `@alpha` API (also re-exported from
`fluid-framework`). This pattern was previously validated only by the
open-polymorphism examples; it's now a supported (alpha) part of the
package because an app depends on it.

`Component` provides utilities for composing independently authored
application "components" that contribute to a shared configuration —
useful for "open polymorphism" schema patterns where the set of allowed
types for a field or collection can be extended by separate libraries.
Each component is a `Component.Factory` that receives a lazy reference
to the composed configuration and returns the content it contributes;
`Component.composeComponents` combines them into a
`Component.ComposedComponents`.

Changes:
- New `packages/dds/tree/src/simple-tree/api/componentApi.ts` with the
`@alpha`-tagged, fully documented `Component` namespace, exporting only
what's needed (`Factory`, `LazyArray`, `Configurable`,
`composeComponents`, `ComposedComponents`); the `Config` implementation
class stays internal.
- Exported from `@fluidframework/tree` and (via the alpha entrypoint)
`fluid-framework`; API reports updated.
- The integration test now imports `Component` instead of defining it
locally.
- New focused unit tests in `componentApi.spec.ts` supplementing the
existing example/integration tests.
- Fixed a latent caching bug in `getConfigured` (it previously ran
`configure` on every call; now cached via `getOrCreate`).
- Changeset added.
## Description

There was an update that motivates us to update the repo so that it
points to the public registry feed that we use in our PR builds.

## Reviewer Guidance

The review process is outlined on [this wiki
page](https://github.com/microsoft/FluidFramework/wiki/PR-Guidelines#guidelines).
sonalideshpandemsft and others added 16 commits August 10, 2026 12:37
## Description

Bumps the client release group and package versions from 2.115.0 to
2.116.0 and regenerates package version files for continued development
on `main`.

**Merge this PR last, after the assert-tagging,
compatibility-generation, and release-notes PRs.**

## Reviewer Guidance

The review process is outlined on [this wiki
page](https://github.com/microsoft/FluidFramework/wiki/PR-Guidelines#guidelines).

Confirm all client packages and generated version files consistently use
2.116.0.

Co-authored-by: Sonali Deshpande <sdeshpande@microsoft.com>
Copilot-Session: 52391d83-6d31-49c9-9ede-91cd270fd85c
## Description

`flub ai` cannot start its Copilot SDK session when dependencies are
installed with pnpm. The SDK's default bundled-CLI resolver constructs
an invalid path for pnpm's scoped package layout:

`.../node_modules/@github/index.js`

This change resolves `@github/copilot/npm-loader.js` relative to the
installed `@github/copilot-sdk` module and passes that entry point
explicitly to `CopilotClient`. This keeps CLI-version ownership with the
SDK and avoids adding a separate direct Copilot dependency.

A behavioral test launches the resolved entry point with `--version` and
verifies that it is a runnable GitHub Copilot CLI.

This fix unblocks the held Codespace integration in [PR
microsoft#27869](microsoft#27869) and must
be included in build-tools 0.67.0.

### Why not use Agency's Copilot CLI?

Agency manages its own Copilot CLI version, but it does not expose a
supported machine-readable command for obtaining the resolved executable
path. Its versioned `~/.copilot-cli/<version>` cache is an internal
implementation detail without a stable symlink or public resolver
contract.

Using `agency copilot` itself as the SDK subprocess is also
incompatible. Agency injects `--session-id`, while `CopilotClient`
starts the CLI with `--headless`; Copilot CLI rejects that combination
because SDK headless mode manages sessions through JSON-RPC.

Resolving the SDK-owned dependency therefore provides the stable
compatibility boundary: the SDK selects its compatible Copilot CLI
version, and build-cli launches that executable directly.

### Manual Validation

```
cd /workspaces/FluidFramework/build-tools
pnpm install --frozen-lockfile
pnpm build:fast

node packages/build-cli/bin/dev ai
```

## Reviewer Guidance

The review process is outlined on [this wiki
page](https://github.com/microsoft/FluidFramework/wiki/PR-Guidelines#guidelines).

The important dependency-resolution detail is that the resolver is
rooted at the SDK module using `createRequire`. It therefore selects the
Copilot CLI installed for that SDK even when pnpm isolates scoped
dependencies.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…API item kind (microsoft#27904)

Makes API links shorter / easier to write in the standard case.

Also adds unit tests, including vitest infra for unit testing the site's
React components.
## Description

The existing review skill was often counterproductive for me as it was
very specific about what to review against, often worse than what the
model would come up with on its own. These changes correct its
assumptions, making the skill much more generally useful, even with less
powerful models, explicitly directing it to handle removes with
different names (like my upstream), compare to the merge point with the
target branch not its head, handle local changes (untracked,
modifications and staged changes) etc.

The new approach checks for an existing PRs as a heuristic to find the
correct upstream branch and remote, then looks for an upstream for the
https://github.com/microsoft/FluidFramework repo, regardless of its
name.

It also warns if the merge base and target branch are too out of sync by
commit count, not just diff size (which was slow and confusing when I
hit it in my fork due to using the wrong remote).

I also refactored our two existing skills which hard coded origin/main
to both use this extracted common logic for improved consistency and
maintainability.

I used this skill to review itself, with a couple models and at a couple
points in the process (like when it was just local changes on main, when
it was staged on a branch, and after the PR was created. Even Haiku 4.5
could follow the steps and produce a review using this skill (its review
quality wasn't great, but it found the correct diff to review).
…icrosoft#27573)

## Description

Adds a new `@fluid-example/claims-example` app that demonstrates the
**Claims DDS** (`@fluid-internal/claims`) running inside a real Fluid
container, and makes a small supporting change to the Claims DDS API
itself.

The Claims DDS is an internal building block for first-writer-wins
ownership: clients compete to bind a key to a value, and the ordering
service picks a single winner that every client converges on. This
example wires it up by hand inside a custom `ClaimsDataObject`, since
the Claims DDS has no public consumption path yet.

**What the example shows**

- A `ClaimsDataObject` owns a single Claims DDS (stored by handle on the
root) and exposes a narrow `trySetClaim` / `getOwner` surface, so the
view never touches the Claims DDS directly.
- Each browser tab gets its own `claimant` identity. Opening the same
container URL in a second tab gives you a competing client.
- There is a small, fixed set of known keys (`ClaimKey1`, `ClaimKey2`).
Claiming a key creates a fresh `SharedDirectory` that records the owner
and binds its handle as the claim value, so every client resolves the
winning handle to the same shared object — no key enumeration or side
structure required.
- **First-writer-wins:** the first client to claim a key wins, and a
losing client is switched to reflect the winner. Connected claims come
back `Pending` and settle after the op roundtrip; detached claims
resolve synchronously.
- The view reads each key's owner live from the resolved backing
directory and re-renders on the Claims DDS `claimed` event, so ownership
stays in sync both locally and remotely.

The app runs against the shared `@fluid-example/example-driver`, so the
backing service (local/t9s, ODSP, …) is selected at build time rather
than hard-coded.

**Claims DDS change**

Removes `currentValue` from the Claims DDS result types
(`ClaimConfirmation` / `ClaimResult`), which also makes them
non-generic. Callers that need the current owner read it back via
`claims.get(key)` — which is fresher and, for write-once keys,
immutable. This narrows the `@internal` API surface with no change to
claim/consensus behavior.

## Reviewer Guidance

The review process is outlined on [this wiki
page](https://github.com/microsoft/FluidFramework/wiki/PR-Guidelines#guidelines).

The example deliberately uses `/internal` entry points because the
Claims DDS has no public consumption path, and we don't intend to add
one.


[AB#74216](https://dev.azure.com/fluidframework/235294da-091d-4c29-84fc-cdfc3d90890b/_workitems/edit/74216)

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4e4794d0-196d-45b7-b9f4-1cc94acd1b45
)

## Description

Prevent the network-isolated build-tools and client pipelines from
contacting `registry.npmjs.org`.

The shared pnpm setup continues to create and authenticate a temporary
user-level `.npmrc` for the configured Azure Artifacts registry. It now:

- passes that userconfig explicitly while installing pnpm;
- fails fast unless both npm and pnpm resolve the expected registry;
- publishes the existing CI mirror workarounds (`trustPolicy=off` and
`minimumReleaseAge=0`) as job variables; and
- sets `trustLockfile=true` for subsequent CI installs so pnpm 11 does
not query registry metadata to reapply those policies to every
dependency already represented by a committed lockfile.

`trustLockfile` does not affect lockfile creation or updates,
frozen-lockfile consistency, structural validation, or downloaded
tarball integrity. The setting is emitted only by the ADO template, so
local dependency-update workflows retain their existing behavior.

Shared build, lint, and test scripts now run through `Bash@3` instead of
`Npm@1`. `Npm@1` replaces the user-level npm configuration, which
allowed nested registry operations such as compatibility-test `pnpm
view` calls to fall back to npmjs. Bash preserves the authenticated
job-level userconfig. The npmrc setup documentation now calls out this
behavior for scripts that directly or indirectly perform registry
operations.

## Reviewer Guidance

The review process is outlined on [this wiki
page](https://github.com/microsoft/FluidFramework/wiki/PR-Guidelines#guidelines).

Please focus on:

- registry verification in `include-install-pnpm.yml`;
- the CI-only `trustLockfile` tradeoff described above; and
- the intentional use of Bash for build and test scripts that may
perform nested registry operations.

### Validation

Build Tools and Client Packages pipelines all show CFS Client COMPLIANT
✅

Prior validation done with forced cold pnpm cache, will be done again
after review signoff, before merging:

- Internal cold-cache Build - build-tools run 415527: cache miss and
CFSClean compliant.
- Internal cold-cache Build - client packages run 415518: cache miss and
CFSClean compliant. The unrelated RealsvcTinyliciousTest job failed;
Build and Coverage tests succeeded.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Abram Sanderson <Abram.Sanderson@microsoft.com>
[How contribute to this
repo](https://github.com/microsoft/FluidFramework/blob/main/CONTRIBUTING.md).

[Guidelines for Pull
Requests](https://github.com/microsoft/FluidFramework/wiki/PR-Guidelines#guidelines).

## Description

The client packages pipeline failed in its telemetry upload stages after
registry validation was added by [PR
microsoft#27893](microsoft#27893). The
`ado-feeds-ff-download-only` value contained trailing whitespace, so the
validation logic normalized it to `…/registry/ /` while npm correctly
resolved `…/registry/`, causing an otherwise successful build to fail.

Trim surrounding whitespace before normalizing the configured registry
to one trailing slash. This preserves the strict registry safety check
while accepting equivalent registry values with incidental whitespace.

Validated the normalization for registry values with trailing
whitespace, surrounding whitespace, and an existing trailing slash.

## Reviewer Guidance

The review process is outlined on [this wiki
page](https://github.com/microsoft/FluidFramework/wiki/PR-Guidelines#guidelines).

Please verify that trimming whitespace is appropriate before comparing
npm and pnpm's resolved registries.

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

Adds tests that assert the ordering guarantees of SharedTree's change
events (`nodeChanged`, `treeChanged`, `rootChanged`) and their
interaction with `withBufferedTreeEvents`. These tests document and lock
in the following behaviors:

- `nodeChanged` fires before `treeChanged` on the same node
- Events propagate bottom-up through the tree hierarchy
- `rootChanged` fires after `nodeChanged`/`treeChanged` (via
`afterBatch`)
- `withBufferedTreeEvents` inverts `rootChanged` relative to buffered
node events
- Node events respect ordering within buffered flushes

Also updates a TODO comment in `treeChangeEvents.ts` to document the
confirmed bottom-up ordering behavior.

## Reviewer Guidance

[Reviewer Guidance
Wiki](https://dev.azure.com/fluidframework/internal/_wiki/wikis/FF%20Internal%20Wiki/1217/Reviewer-guidance)

The test file is self-contained. The only production code change is
replacing a TODO with documentation (line 69 of `treeChangeEvents.ts`).

---------

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…rosoft#27930)

[How contribute to this
repo](https://github.com/microsoft/FluidFramework/blob/main/CONTRIBUTING.md).

[Guidelines for Pull
Requests](https://github.com/microsoft/FluidFramework/wiki/PR-Guidelines#guidelines).

## Description

Bump the prebuild versions for the stable and unstable AI agent
devcontainers to test Codespace prebuild triggering. This change only
invalidates the existing prebuilds; it does not change the devcontainer
configuration or runtime behavior.

## Reviewer Guidance

The review process is outlined on [this wiki
page](https://github.com/microsoft/FluidFramework/wiki/PR-Guidelines#guidelines).

Confirm that the version bumps trigger fresh Codespace prebuilds for
both AI agent devcontainers.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ft#27529)

Additive enum entry so FRS can reference the 2027 tenant encryption key
version one rotation ahead of need (buffer). key2026 already exists.

Companion to FRS scripts/tenantEncryptionKey/keyRotationProcess.md steps
10-12.
This PR updates typetests baseline post release to 2.114.0. Updating it
to 2.115.0 does not work since the version is not found due to package
quarantined

```
# Update type test baseline
pnpm exec flub typetests -g client --reset --normalize --previous
pnpm install --no-frozen-lockfile
pnpm run build
# Update set of prior FluidFramework package versions we run compatibility tests against
pnpm run --filter=@fluid-private/test-version-utils update-compat-versions
```

---------

Co-authored-by: Sonali Deshpande <sdeshpande@microsoft.com>
Copilot-Session: aab57a6c-0f66-4145-ba14-75ff8e6d9691
Update iterator interfaces, test mocks, extraneous casts, and undefined
test variables to match the newer standard library and compiler checks.

ArrayBuffer incompatibility and use of built-in types to be addressed
separately via microsoft#27815 and microsoft#27857.

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

---------

Copilot-Session: 62172399-447d-4b34-a88c-1fbb53b94268
## Description

BubbleBench needs to support both Fluid Tree arrays and native arrays.
This PR introduces a minimal shared collection contract so the benchmark
remains compatible with both container implementations without requiring
the full built-in array interface.

This is an internal-only change to private BubbleBench packages, so it
does not include a changeset.

Validation performed:

- `build:compile` for all five BubbleBench packages: `common`,
`baseline`, `ot`, `experimental-tree`, and `shared-tree`
- Jest suites run sequentially for `ot`, `experimental-tree`, and
`shared-tree` (all passed)

## Reviewer Guidance

The review process is outlined on [this wiki
page](https://github.com/microsoft/FluidFramework/wiki/PR-Guidelines#guidelines).

The diff is limited to the shared BubbleBench collection type and its
three Tree/OT adapters.

---------

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

The stable AI-enabled Codespaces prebuild hangs while installing
`@fluid-tools/build-cli` because pnpm 11 prompts for approval to run the
transitive `core-js` build script. Since prebuilds are unattended, the
prompt remains open until the workflow is cancelled.

Run the global installation in CI mode and explicitly allow the
`core-js` build script. Bump both AI-enabled devcontainer prebuild
versions so GitHub regenerates the Stable prebuild to exercise the fix
and the Insiders prebuild to verify configuration-change triggering.

This can be confirmed by checking that both Codespaces Prebuild
workflows trigger after merge and that the Stable run completes the
build CLI installation without displaying the package-build approval
prompt.

## Reviewer Guidance

The review process is outlined on [this wiki
page](https://github.com/microsoft/FluidFramework/wiki/PR-Guidelines#guidelines).

Please verify that explicitly allowing `core-js` is the preferred pnpm
11 approach for this global installation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…uption bug (microsoft#27920)

## Description

Improves validation and diagnostics for the optimized SharedTree forest.
Internal attach, detach, create, and destroy operations are now
validated before mutating forest state, preventing a failed operation
from partially modifying the forest or consuming detached content.
Additional assertions fail closer to the source of inconsistent
operations, making failures such as later out-of-bounds chunk indexes
easier to triage.

This PR also corrects chunk indexing when a shared multi-node
`SequenceChunk` is normalized for editing. The previous implementation
could clone the selected child into the wrong slot and replace a
sibling. This issue is currently unreachable through
`ForestTypeOptimized`: its current chunk policy does not create sequence
chunks, decoded fields are deaggregated, and delta builds split sequence
content into individual node cursors. The fix and targeted tests
preserve correctness if sequence chunks are used by this forest in the
future or through internal/custom chunk configurations.

Other changes ensure replaced chunk references are released correctly
(improving our ability to edit in place) and add focused coverage for
chunk lookup, copy-on-write normalization, operation atomicity,
reference ownership, and field splitting.
Add staged optional-to-required field migration API

`SchemaFactoryAlpha.stagedRequired` (and `stagedRequiredRecursive` for recursive schema) allow migrating a field from
optional to required without a coordinated deployment, analogous in rollout shape to

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚫 [vale] reported by reviewdog 🐶
[Vale.Spelling] Did you really mean 'rollout'?

optional to required without a coordinated deployment, analogous in rollout shape to
`SchemaFactoryAlpha.stagedOptional` and `SchemaStaticsBeta.staged`.

The rollout is:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚫 [vale] reported by reviewdog 🐶
[Vale.Spelling] Did you really mean 'rollout'?

}) {}
```

Because a document may still contain a node where the field is empty, this is enforced lazily: opening a document and

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📝 [vale] reported by reviewdog 🐶
[Microsoft.SentenceLength] Try to keep sentences short (< 30 words).

}) {}
```

Because a document may still contain a node where the field is empty, this is enforced lazily: opening a document and

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ [vale] reported by reviewdog 🐶
[Microsoft.Adverbs] Remove 'lazily' if it's not important to the meaning of the statement.


Because a document may still contain a node where the field is empty, this is enforced lazily: opening a document and
creating a view never scan or materialize the tree, and unrelated parts of the document remain usable.
Reading that specific field throws a `UsageError` describing the missing value; no value is synthesized and nothing is

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📝 [vale] reported by reviewdog 🐶
[Microsoft.Semicolon] Try to simplify this sentence.

when the field is empty) and the new `TreeViewAlpha.isRootPresent()` for the root field.

Operational precondition: step 3 assumes version N clients have been phased out. A `stagedRequired` client refuses to
clear the field itself, so the remaining race is limited to concurrent clients from two rollout generations behind.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚫 [vale] reported by reviewdog 🐶
[Vale.Spelling] Did you really mean 'rollout'?

@github-actions

github-actions Bot commented Aug 13, 2026

Copy link
Copy Markdown

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

Based on the diff (101918 lines, 1357 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

Abe27342 and others added 2 commits August 13, 2026 09:04
…icrosoft#27941)

## Description

SharedTree recomputes refresher data when pending edits are resubmitted
after reconnecting. `updateRefreshers` reconstructed the modular
changeset without preserving its no-change constraints, which allowed a
constrained transaction to be applied after a concurrent sequenced edit.

This change preserves `noChangeConstraint` and
`noChangeConstraintOnRevert` while updating refreshers. It adds a
focused unit test for the reconstructed changeset and a local-server
end-to-end test covering the disconnected edit and resubmit flow.

## Reviewer Guidance

The review process is outlined on [this wiki
page](https://github.com/microsoft/FluidFramework/wiki/PR-Guidelines#guidelines).

Please verify that preserving the global no-change constraint fields is
the appropriate behavior whenever refresher data is recomputed.

---------

Co-authored-by: Abram Sanderson (He/Him) <absander@microsoft.com>
Copilot-Session: fc4af71e-2ada-42d4-8be0-4332301b2d74
## Description

GitHub does not support the "on configuration change" prebuild trigger
for `devcontainer.json` files in subdirectories of `.devcontainer`. As a
result, the Lightweight and AI-enabled profiles could not be
automatically rebuilt through their existing `prebuild-version`
comments.

Document that these nested profiles run weekly and can be investigated
or manually triggered from the Codespaces repository settings after JIT
elevation. Remove their ineffective version-bump comments and link to
GitHub's documented nested-configuration limitation.

Keep the Standard root profile on "on configuration change" and narrow
its CI guard so only the root `.devcontainer/devcontainer.json` or
`.devcontainer/Dockerfile` satisfy the required trigger update.

The weekly schedules themselves are configured in GitHub repository
settings and are not stored in this repository.

## Reviewer Guidance

The review process is outlined on [this wiki
page](https://github.com/microsoft/FluidFramework/wiki/PR-Guidelines#guidelines).

Please verify that the documented weekly schedules match the
configurations under [Settings >
Codespaces](https://github.com/microsoft/FluidFramework/settings/codespaces).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@noencke
noencke force-pushed the work/W-msqynx4d00al7d6c branch from 15a2cc5 to 7136a32 Compare August 13, 2026 19:25
Adds SchemaFactoryAlpha.stagedRequired / stagedRequiredRecursive, allowing an
application to migrate a field from optional to required across a staged rollout
without a coordinated deployment.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@noencke
noencke force-pushed the work/W-msqynx4d00al7d6c branch from 7136a32 to 8f57978 Compare August 13, 2026 19:28
noencke and others added 5 commits August 13, 2026 13:05
Make stagedRequired use the looser (Optional) view field kind during the
staged phase, mirroring stagedOptional, instead of a Required view kind
with a read-time throw. Reads now return T | undefined honestly; writing
or constructing undefined is rejected at runtime.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Enforce the staged-required invariant on the cursor and insertable
construction paths, and correct two inaccurate TSDoc claims.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Shorten the staged-required empty-content error in TreeAlpha.importVerbose to
match the sibling non-optional error message.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Disambiguate {@link StagedSchemaUpgradePolicy} TSDoc references, which
API Extractor rejected with ae-unresolved-link because the name has both
an interface and a const declaration.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Make the staged-required tightening reachable through TreeView.upgradeSchema
- Cover the end-to-end upgrade path in tests
- Make the staged optional/required markers mutually exclusive

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

Copy link
Copy Markdown

🔗 Found some broken links! 💔

Run a link check locally to find them. See
https://github.com/microsoft/FluidFramework/wiki/Checking-for-broken-links-in-the-documentation for more information.

linkcheck output

1: starting server using command "npm run serve -- --no-open"
and when url "[ 'http://127.0.0.1:3000' ]" is responding with HTTP status code 200
running tests using command "npm run check-links"


> fluid-framework-website@0.0.0 serve
> docusaurus serve --no-open

[SUCCESS] Serving "build" directory at: http://localhost:3000/
[ELIFECYCLE] Command failed with exit code 1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.