Skip to content

Editor tab state is written to its buffer record, not copied into it afterwards (phase 1a) - #31251

Merged
nwparker merged 3 commits into
mainfrom
nwparker/editor-buffer-phase-1a
Oct 20, 2026
Merged

nwparker merged 3 commits into
mainfrom
nwparker/editor-buffer-phase-1a

Conversation

@nwparker

@nwparker nwparker commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 16 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​42 $\color{#cf222e}{\Huge{\mathbf{−}}}$​38 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​4
Prod 8 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​91 $\color{#cf222e}{\Huge{\mathbf{−}}}$​100 $\color{#cf222e}{\Huge{\mathbf{−}}}$​9

ELI5

Each open editor tab carries a few flags: "has unsaved edits", "which version of the file on disk it came from", "the file changed on disk", and a few "still checking the disk" markers. Since #30885 those flags also live in a per-tab record, which is the groundwork for later sharing one record between every tab showing the same file. But the record was only a copy: code still wrote the flags on the tab, and a piece of store code copied them into the record after every change. This PR makes the record the place the flags are written, with the tab kept as a mirror updated in the same step.

What Changed

  • Writers write the record. A new updateEditorBuffer(state, fileId, patch) checks the change against the record, then writes the record and the tab's mirror in one store update. The setters in open-file-mutations.ts and setRestoredEditorOwnerMigrationPending use it; markFileDirty reads its current values from the record.
  • The copy-back is gone. The store middleware no longer copies tab values into the record on every write. It now only keeps the set of records in step with the open tabs: a new tab's record is seeded from the tab, a closed tab's record is dropped.
  • Remote and web-mirrored tabs adopt the host's published values explicitly (adoptMirroredEditorBuffers), only for tabs the host owns. Wire format unchanged.
  • Drift check. In dev and e2e builds, the store throws before committing if the records differ from the open tab ids or any mirror field differs from its record. Production skips the per-field scan.
  • Small cleanups from review: one restore-check setter delegates to the other, the state type is declared once, and an unneeded editorBuffers guard in remote session sync is removed (its type already requires the field).

User-visible behaviour is unchanged. Readers still read the tab's fields; drafts, text models and the content-compare dirty check are untouched.

Why

This follows the "1a" step of the editor-buffer design discussed in #30851: writers update the record, the tab fields stay a synchronized mirror until a later phase removes them. Copying tab → record after every write hid writers that skipped the record and cost a reconcile pass on every openFiles update. Writing the record directly, with a dev/e2e check that fails loudly on drift, is what the next phase (one record shared across workspaces) needs.

Linked Issue

Follow-up to #30885.

Visual Proof

N/A: no visible behaviour change.

Testing

  • pnpm tc, pnpm run check:code-quality:changed, oxfmt --check pass.

  • Renderer unit tests: runtime, store, editor, floating-terminal and tab-group: 1,169 files / 10,232 tests pass; an earlier full src/renderer run passed apart from terminal-pane timeouts that also occur without this change.

  • Reviewed by GPT and Opus independently. Both audited every renderer write of the six fields (open, preview, hydrate, layout frames, re-keys, teardown, owner moves, web session sync, mobile bridge) and found no production writer that skips the record. Their one shared finding, an e2e fixture that set isDirty directly, now uses markFileDirty.

  • The e2e suite was not run locally; CI covers it.

  • I manually tested these changes locally

  • Automated tests added/updated, or explained why not below

AI Disclosure

Review

Agent skill upstream boundary

  • Not applicable, or this change copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Notes

Ensure no issues in: Security, Cross-platoform support (Linux, Windows, Mac), Remote SSH, Mobile, general backwards compatibility, performance

Mixed-version remote/web hosts: host-published tabs are adopted as before (a missing host isDirty still reads as clean). No persisted-state change.

Checklist

  • This PR is small and focused
  • I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)

nwparker and others added 2 commits October 20, 2026 15:01
…or tab buffer state

Writers of the six buffer fields read and write the record, with the OpenFile
fields written as a mirror in the same store update. The store middleware no
longer copies tab values into the record; it only keeps records keyed by open
tabs (seeded from the tab on create, dropped on close) and, in dev and test
builds, asserts every mirror field equals its record. Host-mirrored web tabs
adopt the host's published values explicitly in the session sync patch.

Co-Authored-By: Claude <noreply@anthropic.com>
…he record helpers

Co-Authored-By: Claude <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 6 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 10 included reviews currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: baa9ad01-3d54-441d-81f2-4ce64960c10c
📥 Commits

Reviewing files that changed from the base of the PR and between f812c67 and 3dc74fa.

📒 Files selected for processing: 24
  • src/renderer/src/components/editor/editor-autosave-conflict-flow.test.ts
  • src/renderer/src/components/editor/editor-restored-tab-conflict-scan.test.ts
  • src/renderer/src/components/floating-terminal/use-floating-terminal-close-actions.test.tsx
  • src/renderer/src/components/tab-group/workspace-tab-close-commands.floating-reclaim.test.ts
  • src/renderer/src/runtime/local-structured-session-empty-worktree-visibility.test.ts
  • src/renderer/src/runtime/local-structured-session-tabs-sync.test.ts
  • src/renderer/src/runtime/local-structured-session-unverifiable-inventory.test.ts
  • src/renderer/src/runtime/remote-server-parity.test.ts
  • src/renderer/src/runtime/web-session-open-files-batch-equivalence.test.ts
  • src/renderer/src/runtime/web-session-structured-tab-focus.test.ts
  • src/renderer/src/runtime/web-session-tabs-sync-mirror-identity.test.ts
  • src/renderer/src/runtime/web-session-tabs-sync-test-harness.ts
  • src/renderer/src/runtime/web-session-tabs-sync/apply-final-patch.ts
  • src/renderer/src/runtime/web-session-tabs-sync/state.ts
  • src/renderer/src/store/editor-buffer-record-lifecycle.test.ts
  • src/renderer/src/store/editor-buffer-record-lifecycle.ts
  • src/renderer/src/store/index.ts
  • src/renderer/src/store/slices/editor-rekey-open-files.test.ts
  • src/renderer/src/store/slices/editor/actions/open-file-mutations.ts
  • src/renderer/src/store/slices/editor/actions/restored-editor-owner.ts
  • src/renderer/src/store/slices/editor/buffers/editor-buffer-record.ts
  • tests/e2e/editor-model-host-isolation.spec.ts
  • tests/e2e/remote-terminal-tab-retirement.unit.test.ts
  • tests/e2e/session-tabs-decorative-title-fanout.unit.test.ts
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Co-Authored-By: Claude <noreply@anthropic.com>
@nwparker
nwparker merged commit ebc8b06 into main Oct 20, 2026
45 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant