Skip to content

feat(container-runtime): expose versionMarkResolver on IContainerRuntime - #28168

Open
lindsnguyen wants to merge 3 commits into
microsoft:mainfrom
lindsnguyen:version-marks-resolver-access-point
Open

feat(container-runtime): expose versionMarkResolver on IContainerRuntime#28168
lindsnguyen wants to merge 3 commits into
microsoft:mainfrom
lindsnguyen:version-marks-resolver-access-point

Conversation

@lindsnguyen

@lindsnguyen lindsnguyen commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Description

Expose the version mark resolver as a supported access point on the @legacy @beta
IContainerRuntime interface, so hosts obtain it from a supported interface instead of
casting to the concrete @internal ContainerRuntime class. Fixes AB#82258.

  • Moves the resolver public types (IVersionMarkResolver, ResolveResult,
    VersionMarkCapture) into @fluidframework/container-runtime-definitions and adds
    versionMarkResolver: IVersionMarkResolver to IContainerRuntime.
    @fluidframework/container-runtime re-exports the types (its barrel imports them from
    the definitions package's /internal entrypoint, the standard cross-package import
    door). api-extractor inlines the type declarations into container-runtime's own rolled-up
    .d.ts, so existing consumers still import them unchanged from
    @fluidframework/container-runtime/legacy and no internal module specifier is exposed.
  • Adds an optional reason?: string to the pending and unresolvable variants of
    ResolveResult. It is an opaque, log-only diagnostic string (the runtime currently
    sets awaitingSequence / historicalOpsUnavailable on pending and historyTrimmed
    on unresolvable). Hosts drive behavior from kind and must not branch on reason.
    A plain string keeps additions non-breaking and avoids a second discriminator; a state
    that needs different host behavior should become a new kind.

The three existing kind values are unchanged.

Reviewer Guidance

  • Adding versionMarkResolver to the @sealed IContainerRuntime is backCompat-safe;
    the forward-compat break is only on the non-sealed
    IContainerRuntimeWithResolveHandle_Deprecated and is acknowledged via
    typeValidation.broken plus the regenerated type-test.
  • reason?: string is deliberately a plain string, not a string-literal union, so it is
    diagnostic-only and additions never break exhaustive kind consumers.
  • The barrel re-exports the trio from the definitions package's /internal entrypoint,
    matching how production packages (fluid-framework, container-definitions) re-export
    sibling types. api-extractor inlines them into the consumer-facing rollup, so the
    /internal specifier does not leak into the published .d.ts.

Copilot AI lite review requested due to automatic review settings September 3, 2026 20:37
@lindsnguyen
lindsnguyen requested review from a team as code owners September 3, 2026 20:37
@github-actions github-actions Bot added area: tools area: runtime Runtime related issues area: repo Repo related work area: website public api change Changes to a public API changeset-present base: main PRs targeted against main branch labels Sep 3, 2026
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

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

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

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

How this works

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

  • Tick Start review below to dispatch the review fleet.

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

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

  • Start review

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

@fluidframework/container-runtime currently re-exports the version-mark types from @fluidframework/container-runtime-definitions/internal, which conflicts with the stated intent to re-export from /legacy and may leak an internal module specifier into the public .d.ts surface.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR graduates the version mark resolver to a supported host-facing access point by adding versionMarkResolver to the @legacy @beta IContainerRuntime interface, moving the related public types into @fluidframework/container-runtime-definitions, and extending ResolveResult with an optional diagnostic reason?: string for pending/unresolvable.

Changes:

  • Move IVersionMarkResolver, ResolveResult, and VersionMarkCapture into @fluidframework/container-runtime-definitions and export them from the package entrypoint.
  • Add versionMarkResolver: IVersionMarkResolver to IContainerRuntime so hosts no longer need to cast to ContainerRuntime.
  • Add an optional reason?: string to ResolveResult’s pending/unresolvable outcomes and update runtime + tests/docs accordingly.
File summaries
File Description
packages/runtime/container-runtime/src/versionMarks/versionMarkResolver.ts Switches to imported shared public types and adds reason when returning pending/unresolvable.
packages/runtime/container-runtime/src/versionMarks/index.ts Re-exports version mark public types from container-runtime-definitions and continues exporting the implementation.
packages/runtime/container-runtime/src/versionMarks/DEV.md Updates design notes to document the reason?: string policy and improves formatting.
packages/runtime/container-runtime/src/test/versionMarks/versionMarkResolver.spec.ts Updates expected ResolveResult shapes to include reason where applicable.
packages/runtime/container-runtime/src/test/containerRuntime.spec.ts Updates the loader-compat regression test to expect reason: "historicalOpsUnavailable".
packages/runtime/container-runtime/api-report/container-runtime.legacy.beta.api.md Updates legacy beta API report for ResolveResult.reason?: string.
packages/runtime/container-runtime/api-report/container-runtime.legacy.alpha.api.md Updates legacy alpha API report for ResolveResult.reason?: string.
packages/runtime/container-runtime-definitions/src/versionMarks.ts Introduces the new shared @legacy @beta version mark API types and docs.
packages/runtime/container-runtime-definitions/src/test/types/validateContainerRuntimeDefinitionsPrevious.generated.ts Updates type-test baseline to acknowledge expected forward-compat break for the deprecated interface.
packages/runtime/container-runtime-definitions/src/index.ts Re-exports the new version mark types from the package root.
packages/runtime/container-runtime-definitions/src/containerRuntime.ts Adds versionMarkResolver to IContainerRuntime.
packages/runtime/container-runtime-definitions/package.json Marks the known forward-compat break for the deprecated interface in typeValidation.broken.
packages/runtime/container-runtime-definitions/api-report/container-runtime-definitions.legacy.beta.api.md Adds the new IVersionMarkResolver/ResolveResult/VersionMarkCapture exports and IContainerRuntime.versionMarkResolver.
.changeset/version-mark-resolver-access-point.md Adds changeset for the API surface move and ResolveResult.reason.
Review details
  • Files reviewed: 14/14 changed files
  • Comments generated: 1
  • Review effort level: Lite

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

Comment thread packages/runtime/container-runtime/src/versionMarks/index.ts
@anthony-murphy

Copy link
Copy Markdown
Contributor

packages/runtime/container-runtime/src/versionMarks/versionMarkResolver.ts:185

Deep Review: The diagnostic reason is set on returned results — historicalOpsUnavailable at :152, awaitingSequence at :259/:272, historyTrimmed at :265/:269 — but it never reaches the "Resolve" telemetry event. The finally block builds { eventName: "Resolve", outcome, path, durationMs, ...(resolvedSequenceNumber === undefined ? {} : { sequenceNumber }) } with no reason. versionMarkResolver.spec.ts:946-958 confirms the gap: a no-reader resolve returns { kind: "pending", reason: "historicalOpsUnavailable" } while the matching telemetry expectation carries only eventName/outcome/path.

Consequence: the runtime never logs why a mark did not resolve — every host must forward the field itself. The value is low-cardinality and PII-free, so add it to the payload under the same "diagnostic-only, unstable, log-only" rule already applied to the returned field, and update the shape assertion in the spec. If the omission is intentional (cardinality/PII), document it as a non-goal instead — and state the supported way for the runtime, not each host, to log the non-resolution cause.

@anthony-murphy

Copy link
Copy Markdown
Contributor

packages/runtime/container-runtime/src/versionMarks/DEV.md:92

Deep Review: This PR adds readonly versionMarkResolver: IVersionMarkResolver to the @legacy @beta @sealed IContainerRuntime and makes the concrete getter public (no @internal), but DEV.md — a file this PR edits — still describes the old state. Lines 92-95 say the getter is @internal and that "A future public API may move this onto container-runtime definitions rather than the concrete runtime class" — that future move is exactly what this PR does. Line 122 ("Get the resolver: ContainerRuntime.versionMarkResolver") names the superseded access point.

Rewrite the "Host exposure" paragraph (lines 92-95) to state the resolver is now exposed on the @legacy @beta IContainerRuntime interface — drop the @internal wording and the "future public API may move this" sentence — and change line 122 to reference IContainerRuntime.versionMarkResolver.

@dannimad Daniel Madrid (dannimad) left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Do not merge until bump PR is merged first

lindsnguyen and others added 3 commits September 3, 2026 16:43
Move the version mark resolver public types (IVersionMarkResolver,
ResolveResult, VersionMarkCapture) into container-runtime-definitions and
expose `versionMarkResolver` on the `@legacy @beta` IContainerRuntime
interface, so hosts obtain the resolver from a supported interface rather
than the concrete `@internal` ContainerRuntime class. container-runtime
re-exports the types for back-compat; its API surface is unchanged.
…nding/unresolvable ResolveResult

- `reason` is an opaque, log-only diagnostic string, not a typed union. The runtime
  currently sets `awaitingSequence` / `historicalOpsUnavailable` on `pending` and
  `historyTrimmed` on `unresolvable`.
- Hosts drive all behavior from `kind` and must not branch on `reason`. A plain string
  keeps additions non-breaking and avoids a second de facto discriminator; a future
  state needing different behavior should be a new `kind`, not a new `reason`.
- `reason` is transient operational context, not persisted. office-bohemia will log it
  when moving to the supported access point but makes no behavioral change.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@lindsnguyen
lindsnguyen force-pushed the version-marks-resolver-access-point branch from 42ca62f to dd89cdc Compare September 3, 2026 23:43
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

🔗 No broken links found! ✅

Your attention to detail is admirable.

linkcheck output

$ start-server-and-test "npm run serve -- --host 127.0.0.1 --no-open" http://127.0.0.1:3000 check-links
1: starting server using command "npm run serve -- --host 127.0.0.1 --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 --host 127.0.0.1 --no-open

[SUCCESS] Serving "build" directory at: http://127.0.0.1:3000/

> fluid-framework-website@0.0.0 check-links
> linkcheck http://127.0.0.1:3000 --skip-file skipped-urls.txt

Crawling...

Stats:
  340615 links
    2042 destination URLs
    2297 URLs ignored
       0 warnings
       0 errors


* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
* Licensed under the MIT License.
*/

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should these types be @sealed?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Deep Review: Good question. IVersionMarkResolver should be @sealed. It's runtime-owned ("Runtime-owned resolver for app-stored version mark locators") and implemented only by Fluid. The same forward-compat argument this PR relies on for adding a required member to the @sealed IContainerRuntime applies here: without @sealed, a future member addition to IVersionMarkResolver would be a forward-compat break for any host implementer/deriver, whereas @sealed documents that only Fluid implements it. Adding it and regenerating the api-reports is the safe, consistent call.

For ResolveResult and VersionMarkCapture, @sealed doesn't apply the same way — they're type-alias unions, not interfaces, so there's no implementer to seal against.

One thing to confirm: if hosts are only ever expected to consume IVersionMarkResolver (never implement it), seal it; if some host implementation is intended, that changes the call — worth stating either way.

---
Expose the version mark resolver on IContainerRuntime

`IContainerRuntime` now exposes `versionMarkResolver: IVersionMarkResolver`, the supported access point for the version mark resolver APIs (`IVersionMarkResolver`, `ResolveResult`, `VersionMarkCapture`), which move to `@fluidframework/container-runtime-definitions`. Hosts obtain the resolver from the runtime instead of the concrete `ContainerRuntime` class.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit: can we link to the API docs for the existing APIs here?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approving docs changes. I didn't review the code or API changes in much detail (I see that Tony has already been reviewing those).

@anthony-murphy

Copy link
Copy Markdown
Contributor

Deep Review

Reviewed commit dd89cdc on 2026-09-04.

Readiness: 9/10 — ALMOST READY

Ready for human review — no blocking defects. This completes the planned graduation of the version-mark resolver onto the @legacy @beta @sealed IContainerRuntime, and the four doc/telemetry polish items from the prior review (telemetry reason, DEV.md, the orderSequentially caveat, the re-export description) are all resolved in this commit. One open API-convention question remains — whether the exposed IVersionMarkResolver should be @sealed — flagged inline.

Path to Ready

  • Resolve inline threads

Context for Reviewers

  • Planned access-point graduation for the version-mark resolver (@internal feat(container-runtime): add internal version mark resolver #27834@legacy @alpha Move marker and load to sequence number API to legacy alpha #27891@legacy @beta @sealed IContainerRuntime here). DEV.md:159-161 recorded the precondition this PR executes: move the access point off the concrete @internal ContainerRuntime class onto IContainerRuntime. Resolves AB#82258. App-owned mark storage is unchanged.
  • Opaque reason?: string (not a typed union) is deliberate — the contract forbids hosts branching on it, so new diagnostic values stay non-breaking. It consciously overrides an earlier DEV.md preference for a string-literal union.
  • The forward-compat break is scoped to the already-deprecated IContainerRuntimeWithResolveHandle_Deprecated (Deprecate resolveHandle and IFluidHandleContext on IContainerRuntime #17029, migrate to getEntryPoint()), handled via typeValidation.broken plus the regenerated type-test. Adding members to the @sealed IContainerRuntime itself is compat-safe (Fluid-only implementer).
  • Three blind proposers independently reproduced the exact design (add readonly versionMarkResolver to IContainerRuntime, move the three types into container-runtime-definitions, re-export for compat) and all rejected the FluidObject/provider alternative for the same reasons the PR did.
For human reviewer
  • Needs human judgment@sealed policy for the exposed version-mark types (Josmithr's open thread): whether hosts are ever expected to implement IVersionMarkResolver. An API-convention owner (Josmithr / tylerbutler) should confirm intent.
  • Needs human judgment — Opaque reason?: string vs a typed union: a deliberate API-contract tradeoff diverging from the earlier DEV.md preference and all three blind proposals. Owner sign-off is appropriate.
  • Needs human judgment — Promotion cadence / bake-time: locking a surface that changed shape six times in one month (bug as recent as fix(container-runtime): skip clipped leading chunk streams when resol… #27919) onto @sealed @beta IContainerRuntime. markfields authored the resolver-semantics reviews and should weigh in.
  • Cannot be assessed by the pipeline — Whether the typeValidation.broken suppression is the intended handling for the deprecated interface; best assessed by anthony-murphy.
  • Cannot be assessed by the pipeline — The bundle-size report was still pending at review time; verify Build - client packages completes clean.
Review history (1 prior review)
  • 1015bcd 2026-09-03 · 8/10 — four doc/telemetry polish items flagged inline

@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Bundle size comparison

Base commit: 253e3ad858341d185338d3a8e02a92a6e6246b54
Head commit: dd89cdc5005a11dc720114b01355d611d679e149

Pending — Build - client packages is running. Results will appear here when the build completes.

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

Labels

area: repo Repo related work area: runtime Runtime related issues area: tools area: website base: main PRs targeted against main branch changeset-present deep-review public api change Changes to a public API

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants