Add staged optional-to-required field migration API for SharedTree schema - #5
Add staged optional-to-required field migration API for SharedTree schema#5noencke wants to merge 223 commits into
Conversation
…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).
## 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 |
There was a problem hiding this comment.
🚫 [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: |
There was a problem hiding this comment.
🚫 [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 |
There was a problem hiding this comment.
📝 [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 |
There was a problem hiding this comment.
[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 |
There was a problem hiding this comment.
📝 [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. |
There was a problem hiding this comment.
🚫 [vale] reported by reviewdog 🐶
[Vale.Spelling] Did you really mean 'rollout'?
|
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:
How this works
|
…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>
15a2cc5 to
7136a32
Compare
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>
7136a32 to
8f57978
Compare
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>
|
🔗 Found some broken links! 💔 Run a link check locally to find them. See linkcheck output |
Scenario
Applications routinely discover that a field they originally modeled as
optionalis really required. Today there is no safe way to make that change: tightening the stored field kind fromOptionaltoRequiredimmediately 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(plusstagedRequiredRecursivefor recursive schema), with the same rollout shape asstagedOptionalbut inverted runtime semantics.Rollout contract
sf.optional(T). Stored field kind isOptional.sf.stagedRequired(T).undefined, nor deleted.Optional, so version N clients are unaffected and documents they created remain viewable. The view explicitly tolerates a matchingOptionalstored field (discrepancies.ts).includeStagedfilter ofextractPersistedSchema, internallyStoredFromViewSchemaGenerationOptions.includeStagedRequired), tightening the stored field kind fromOptionaltoRequired. Without that explicit opt-in,TreeView.upgradeSchemaleaves this staged change a no-op — identical tostagedOptionaltoday.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:
compatibility.canViewistrue.UsageError(Staged required field … has no value in this document.). Unrelated parts of the document remain fully usable.try/catch:TreeAlpha.child(node, key)(existing public alpha API — reused and documented rather than duplicated) for object fields, and the newTreeViewAlpha.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
stagedOptionaldoes not have: after the operator tightens the stored field toRequired, a still-runningstagedRequiredclient would compute an "upgrade" back toOptional— a legal widening — andupgradeSchema()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 bothSchemaCompatibilityTester.checkCompatibilityandSchematizingSimpleTreeView.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
stagedRequiredclient 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.
npx fluid-build --task build "@fluidframework/tree"tsc, testtsc, CJS build,eslint --quiet,biome check,depcruise, and allapi-extractorrunsnpx fluid-build --task build "fluid-framework"npx mocha --grep "staged required"(inpackages/dds/tree)npx mocha(full@fluidframework/treeESM suite, inpackages/dds/tree)npx biome check .(inpackages/dds/tree)npx eslint --quiet --format stylish src(inpackages/dds/tree)npx api-extractor run --local(inpackages/dds/tree)New tests (
src/test/simple-tree/api/stagedSchemaUpgrade.spec.ts,describe("staged required upgrade")):projects to an Optional stored field before opt in and Required afterreports the expected compatibility across all three rollout phases(also assertscomputeUpgradeSchemastaysRequiredafter tightening — the monotonicity guard)reads a present value and fails lazily on an absent root(coversisRootPresent(), the lazy throw, and the blockedundefinedwrite)fails lazily on an absent object field while leaving the rest of the document usable(coversTreeAlpha.childpresence check, blocked assignment anddelete, and repair-by-write)requires a value when constructing nodesworks with stagedRequiredRecursive in a recursive schemaPre-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 whoseupgradeSchemareverts 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:
simple-tree/api/schemaFactoryAlpha.ts— the public API and the authoritative TSDoc for the rollout contract.simple-tree/fieldSchema.ts,simple-tree/simpleSchema.ts,simple-tree/core/toStored.ts— thestagedRequiredUpgrademarker,isStagedRequired, and theincludeStagedRequiredoption.simple-tree/toStoredSchema.ts— view → stored projection (Optionalbefore opt-in,Requiredafter). Note both the restrictive and permissive option sets returnfalse: tightening is never "more permissive".simple-tree/api/stagedRequiredUpgrades.ts(new) — the monotonicity guard. The most novel piece.simple-tree/node-kinds/object/objectNode.tsandshared-tree/schematizingTreeView.ts— lazy read failure, blocked clears,isRootPresent.simple-tree/api/discrepancies.ts— the scoped compatibility tolerance.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-requiredincludeStagedRequiredoption..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
getOwnPropertyDescriptoron object nodes is deliberately left non-throwing (enumerable: field !== undefined) soObject.keys, spread, and deep-equality keep working on documents with an absent staged-required field. Only reading the specific field fails.getStagedRequiredUpgrade/throwStagedRequiredFieldMissingare internal helpers; they are not added to the package's public entrypoints (src/index.tsis untouched, so no entrypoint regeneration was needed).Runtime: copilot · Instructions:
.github/copilot-instructions.mdAuthored with Minions.