Skip to content

Enable unchecked record access lint for replay-driver - #28250

Open
shlevari wants to merge 1 commit into
microsoft:mainfrom
shlevari:wi34164-record-access-lint
Open

shlevari wants to merge 1 commit into
microsoft:mainfrom
shlevari:wi34164-record-access-lint

Conversation

@shlevari

@shlevari shlevari commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

How contribute to this repo.

Guidelines for Pull Requests.

Description

Replaces the noUncheckedIndexedAccess approach merged in #28150 with the Fluid-specific @fluid-internal/fluid/no-unchecked-record-access rule, resolving AB#34164.

This restores the replay control flow and package TypeScript opt-out, promotes the lint rule to error, and explicitly types the two record lookups reported by the rule. The replacement removes the runtime failure paths introduced by the previous migration.

Reviewer Guidance

The review process is outlined in the pull request guidelines.

Please focus on restoration of the original replay behavior and the two record-access narrowings.

Restore noUncheckedIndexedAccess to the package opt-out and enforce the Fluid record access lint rule instead. Remove the runtime checks introduced by the earlier migration.

AB#34164

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions github-actions Bot added area: tools area: driver Driver related issues area: repo Repo related work area: website base: main PRs targeted against main branch labels Sep 18, 2026
@github-actions

github-actions Bot commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

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

Based on the diff (23 lines, 4 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

@github-actions

github-actions Bot commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

🔭 PR Review Fleet Report

Note

This report is generated by an experimental AI review fleet and is provided as a beta feature. Findings are a starting point for discussion, not a gate. Use your own judgement.

Verdict: ⚠️ Approve with Suggestions

0 Spicy, 1 Pungent, 1 Smelly

Findings

Sev # Area File What Fix
🧄 Pungent H1 Testing packages/drivers/replay-driver/src/replayDocumentDeltaConnection.ts:338 The PR removes the explicit lastMessage === undefined guard and now calls messages[messages.length - 1].timestamp directly. If documentStorageService.fetchMessages ever yields a result.value that is an empty array (a legitimate 'done: false' state with zero messages), messages[messages.length - 1] evaluates to undefined and .timestamp throws an uncaught TypeError: Cannot read properties of undefined, replacing the previous clear Error('Delta storage returned an empty batch of messages'). There is no test in the package (no test files exercise ReplayDocumentDeltaConnection/fetchAndPlayback) covering either the old or new behavior, so this silent regression from a descriptive error to a raw TypeError would ship undetected. Add a test that mocks documentStorageService.fetchMessages to return a stream whose read() resolves with { done: false, value: [] } on some iteration, then asserts that fetchAndPlayback either throws a specific, well-formed error or is documented/guaranteed never to receive an empty batch (verifying the invariant the removed check used to enforce).
🧅 Smelly M1 Testing packages/drivers/replay-driver/src/replayDocumentDeltaConnection.ts:130 The PR removes the currentOp === undefined guard in replayNextOps (and the analogous op === undefined guard a few lines below for the inner loop). replay() is called with current = this.skipToIndex(fetchedOps); if skipToIndex ever returns an index >= fetchedOps.length (e.g., an off-by-one in the time-based branch, or an empty fetchedOps array), fetchedOps[current] is undefined and the subsequent currentOp.timestamp access throws inside the setTimeout callback instead of the previous descriptive rejected promise. No existing test in the package calls ReplayControllerStatic.replay with an empty or out-of-range fetchedOps/skipToIndex combination. Add a test that calls controller.replay(emitter, []) (empty ops array) or a case where skipToIndex returns fetchedOps.length, and assert the promise resolves/rejects predictably rather than throwing an unhandled exception from within the timer callback.

View workflow run

@shlevari
shlevari marked this pull request as ready for review September 18, 2026 18:03
Copilot AI lite review requested due to automatic review settings September 18, 2026 18:03

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.

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


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

@github-actions

Copy link
Copy Markdown
Contributor

Bundle size comparison

Base commit: d013fc5fd2922a8f88981bdaa6df91c4120e1b72
Head commit: eae9987b3057a69359cffc7d8414341a23ebc37b

Notable changes

No bundles changed by ≥ 500 bytes parsed.

Per-bundle deltas

@fluid-example/bundle-size-tests

  • fluidFrameworkAllAlpha.js: parsed 807159 → 807213 (+54), gzip 221853 → 221934 (+81)
  • azureClient.js: parsed 634336 → 634331 (-5), gzip 169989 → 170073 (+84)
  • odspClient.js: parsed 606600 → 606711 (+111), gzip 163052 → 163196 (+144)
  • aqueduct.js: parsed 538752 → 538763 (+11), gzip 144632 → 144680 (+48)
  • fluidFramework.js: parsed 415378 → 415411 (+33), gzip 117810 → 117845 (+35)
  • sharedTree.js: parsed 404757 → 404783 (+26), gzip 115252 → 115271 (+19)
  • containerRuntime.js: parsed 315027 → 315009 (-18), gzip 86438 → 86439 (+1)
  • sharedString.js: parsed 175732 → 175739 (+7), gzip 49785 → 49795 (+10)
  • experimentalSharedTree.js: parsed 161846 → 161846 (0), gzip 46722 → 46722 (0)
  • matrix.js: parsed 153720 → 153727 (+7), gzip 44381 → 44388 (+7)
  • loader.js: parsed 147328 → 147344 (+16), gzip 40038 → 40049 (+11)
  • odspDriver.js: parsed 106695 → 106753 (+58), gzip 33227 → 33293 (+66)
  • directory.js: parsed 65669 → 65676 (+7), gzip 18493 → 18502 (+9)
  • 578.js: parsed 58686 → 58686 (0), gzip 17657 → 17657 (0)
  • odspPrefetchSnapshot.js: parsed 46463 → 46444 (-19), gzip 15512 → 15522 (+10)
  • map.js: parsed 45820 → 45827 (+7), gzip 14120 → 14127 (+7)
  • 252.js: parsed 44384 → 44384 (0), gzip 13741 → 13741 (0)
  • summarizerDelayLoadedModule.js: parsed 31287 → 31287 (0), gzip 7929 → 7929 (0)
  • socketModule.js: parsed 27108 → 27078 (-30), gzip 8069 → 8103 (+34)
  • createNewModule.js: parsed 12464 → 12464 (0), gzip 4792 → 4805 (+13)
  • summaryModule.js: parsed 3888 → 3888 (0), gzip 1874 → 1874 (0)
  • connectionState.js: parsed 909 → 909 (0), gzip 500 → 500 (0)
  • sharedTreeAttributes.js: parsed 845 → 852 (+7), gzip 496 → 506 (+10)
  • debugAssert.js: parsed 429 → 429 (0), gzip 299 → 299 (0)
  • FluidFramework-HashFallback.js: parsed 419 → 419 (0), gzip 313 → 313 (0)

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

Labels

area: driver Driver related issues area: repo Repo related work area: tools area: website base: main PRs targeted against main branch

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants