Skip to content

fix(refresh): stop merge-view jitter and preserve session state - #563

Merged
esmuellert merged 8 commits into
mainfrom
fix/issue-558-refresh-model
Sep 14, 2026
Merged

esmuellert merged 8 commits into
mainfrom
fix/issue-558-refresh-model

Conversation

@esmuellert

@esmuellert esmuellert commented Sep 14, 2026 •

Copy link
Copy Markdown
Owner

Summary

Fixes #558.

Stop merge-view jitter and repeated refresh/reinitialization by treating repository notifications as input invalidations, not instructions to reopen the selected comparison. Refresh only what changed, while keeping working buffers and editable merge Results authoritative.

This also consolidates refresh ownership, adds real-Git/Neovim regression coverage, and separates E2E, integration and unit tests.

Refresh model and ownership

  • Preserve the worktree / index / head / refs classifications and the 500 ms polling fallback, including native-process failure recovery.
  • Keep symbolic source identities separate from resolved revisions. Settle and compare actual input contents before updating a comparison, including unchanged-status worktree edits and mutable index/ref inputs.
  • Separate panel-list refresh, comparison refresh and merge input reinitialization. Unrelated changes must not disturb pane identities, focus, cursor, viewport, folds or an unchanged comparison.
  • Protect edited Result buffers when merge inputs change: warn rather than silently replacing the user's work. Resolution actions update decorations without reinitializing Result content.
  • Reject stale results after selections, buffer replacement, tab closure or disposal; respect shared-buffer ownership during cleanup and restoration.
  • Make ui.refresh the single controller. session.panel.data owns list data, query parameters and selection; panel views consume notifications and keep only local presentation state.
  • Move Git reads/comparison definitions into refresh/panel.lua and refresh/inputs.lua, and reuse existing view renderers for in-place updates. Remove the old auto_refresh, Explorer scheduler and per-panel refresh modules.

Regressions caught by the expanded tests

  • Reappearing files could block on W13 after a deleted-file preview.
  • Quoted rename paths, literal -> filenames and renamed index paths could be read incorrectly; status parsing now uses NUL-delimited paths.
  • Parentless History commits could reference an invalid parent or omit their files.
  • History refresh could lose line-range/reverse options.
  • Inline revision updates could trigger unwanted FileType/LSP attachment.
  • Following a file outside Git could retain a stale rather than empty comparison side.
  • Shared-buffer cleanup and inlay-hint restoration could interfere with another session.

CI follow-up: e6d6678

The first cross-platform run exposed issues that were not visible on the initial fast local machine. These were reproduced and corrected rather than handled by weakening result assertions:

  • Slow polling: when a read exceeds 500 ms, repeated fallback ticks must not create an endless backlog. Polls now sample only while idle; real classified invalidations still queue during in-flight reads. Added controller regressions and E2Es delaying real Git results by 700 ms.
  • Synthetic gutter fixtures: automatically attached comparison controllers were overwriting hand-authored projections on slower runners. These component fixtures now explicitly retire that unrelated controller after setup; real merge E2Es keep it active. A delayed screen assertion verifies the fixture survives beyond the polling interval.
  • Atomic index changes: fixtures now write Git blobs/index entries directly, avoiding accidental intermediate working-tree states during race tests.
  • Windows paths: literal-arrow cases use real skip-worktree index entries, so the same Git/parser/UI assertions run on Windows without creating an illegal > filesystem path. The cases are not skipped on Windows.
  • Windows cleanup: plugin-aware fixtures retire their own sessions/watchers before deleting directories, without closing other fixtures' sessions.
  • Progress and budgets: the supervisor prints spec starts and active-worker heartbeats. Windows runs four workers with a bounded 15-minute whole-spec budget (large specs contain up to 120 isolated Git/Neovim cases); individual asynchronous assertions remain bounded.

Upstream scrollbind distinction

Neovim reverted #41519 in 0c9012f, restoring its known tall-virtual-line oscillation. The previous nvim-0.13 test gate incorrectly assumed the fix was still present. The unchanged strict upstream assertion is now explicitly opt-in via CODEDIFF_TEST_UPSTREAM_SCROLLBIND=1 on a build carrying the fix; it was verified on the earlier fixed build. CodeDiff's own scrollbind setup remains mandatory. No new scrolling workaround or claim to fix Neovim's reverted behavior is included.

Tests and fixtures

  • Shared TMP repository/worktree factory: independent refs, indexes and object databases, no hardlink sharing, deterministic reusable profiles, Git environment isolation and cleanup guarantees.
  • Plain repositories, unborn branches, bare local remotes and SHA-256 use the same lifecycle.
  • Real commands/keys, Git changes, native/polling notifications and Neovim state assertions. Embedded-UI cases additionally assert screen-grid text and colors; race tests delay real results rather than fabricate contents.
  • Runnable specs are grouped under tests/unit/ (13 files), tests/integration/ (94 files) and tests/e2e/ (16 files). Support, fixtures and framework implementation are separate.
  • The directory reorganization preserved all 1,590 existing expanded registrations and added 5 discovery checks. CI regressions add 10 more, for 1,605 current registrations.
  • The refresh change-audit matrix has 133 named scenarios / 548 parameterized E2E executions. The complete E2E directory has 591 executions, including previously scattered command/working-file tests; these are different counts, not 591 newly added scenarios.
  • CLI entry points support layers, directories and individual specs; empty or invalid selections fail explicitly. CI paths and documentation are updated.

Validation

PR Validation run 34900369664 completed successfully: 14/14 jobs.

CI platform/check Result
Linux x64 and ARM64 Build, native tests and all 123 Neovim specs passed
macOS x64 and ARM64 Build, native tests and all 123 Neovim specs passed
Windows x64 and ARM64 Build, native tests and all 123 Neovim specs passed
Android ARM64 and x64 Cross-build and ABI checks passed; these jobs do not run Neovim tests
Standalone builds All five platform/OpenMP configurations and command smoke E2Es passed
Diff regression gate Passed

The desktop jobs use official Neovim nightly v0.13.0-dev-1638+ge7e29d9b6d. Windows logs confirm the formerly failing arrow-path cases, slow-read cases, 30 gutter checks and 8 repository-helper checks actually executed and passed.

Additional local validation:

  • The same official nightly: all 123 spec files pass with eight concurrent workers.
  • Neovim 0.12.3: all 16 E2E specs / 591 executions pass. The full 0.12.3 suite is not claimed green: its pre-existing gutter-coordinate differences remain outside this refresh change.
  • make build, C tests 10/10, StyLua on changed paths and git diff --check passed.
  • CLI selection, invocation outside the checkout, invalid-target handling, registration preservation and reference/link audits passed.

Review notes and scope

The original seven commits remain intact, followed by the CI correction commit e6d6678; no history was rewritten. The test-organization commit 2dc5a5a changes no production Lua behavior. Most of the large file count is test relocation.

Merge algorithms, filler placement/count rules, gutter implementation, highlight definitions and unrelated scrolling behavior are unchanged.

The matrix is behavioral evidence, not an absolute safety or exhaustive branch-coverage guarantee.

Add a dependency policy for worktree, index, HEAD, refs and buffer invalidations. Restrict the Git content cache to immutable object IDs and normalize missing merge-stage errors.

This commit contains only the policy foundation and its tests; snapshot application and session scheduling remain separate changes.
Read all comparison inputs before applying changes, compare source contents, and validate working-buffer ticks. Update existing panes without reopening unchanged inputs or overwriting edited merge results.

Centralize revision-buffer content population and reject stale virtual-file loads. Cover snapshot completion, dependency selection, failures, and missing sides.
Route watcher classifications, buffer events, panel refreshes, and the 500ms polling fallback through one session controller. Separate repository-list updates from comparison updates and merge-result initialization.

Preserve symbolic source refs across commands, panels, file following, and layout changes. Retire per-view schedulers, reject stale callbacks, and clean up refresh ownership when buffers or tabs disappear.

Update Explorer and History tests for coalescing, retry, fallback, and lifecycle behavior.
Add 124 real file/Git and embedded screen-grid cases across layouts, Explorer, History, standalone comparisons, merge Results, file following, and session lifecycle transitions.

Require native watcher readiness, exercise the 500ms polling fallback and real process-exit failover, and verify stable panes throughout unrelated repository changes. Document the coverage and extend the screen harness for flushed-frame assertions.
Move panel data, comparison definitions, Git reads, and selection ownership into the session refresh controller. Explorer and History consume data notifications instead of managing refresh and file-loading workflows.

Remove legacy refresh adapters and reuse the existing view renderers for in-place comparison updates. Preserve watcher classifications, the 500ms polling fallback, and edited merge Results.

Add six architecture regression cases, migrate existing tests to session-owned data, and document the shared refresh flow. Verified all 114 spec files on Neovim 0.13-dev, 124 refresh E2Es on Neovim 0.12.3, the native build, and all 10 C tests.
Use isolated temporary repositories and worktrees for Git-backed tests, with deterministic reusable UI fixtures and cleanup guarantees. Expand the refresh matrix to 132 named scenarios and 544 E2E executions across native and polling backends, layouts, and merge configurations.

Fix regressions exposed by the new scenarios in history queries, root commits, renamed paths, file reloads, revision buffers, and shared-buffer lifecycle. Document the main-to-branch behavioral coverage map and fixture reproduction workflow.
Separate unit, integration and E2E specs from shared support, fixtures and the test framework. Preserve all 1590 existing case registrations and add five discovery and directory-boundary checks.

Add layer, directory and single-spec selection to both test entry points. Remove old helper paths and checkout-history dependencies, use registered command and keymap entry points in E2Es, and update CI, documentation and testing guidance.

Validated all 123 specs on Neovim 0.13-dev, all 587 E2E executions on 0.12.3, targeted 0.12.3 integration and unit suites, C tests, formatting, CLI selection and reference audits. Production Lua and the keymap golden are unchanged.
Coalesce fallback ticks while a selection, read or real invalidation is pending, preserving the 500ms interval and classified repository events. Add controller regressions and real delayed-Git E2Es.

Isolate hand-authored gutter projections from the comparison controller, write atomic index-only fixture changes, keep literal-arrow paths in Git rather than invalid Windows filenames, and retire fixture-owned sessions before removing watched directories.

Retain the strict upstream scrollbind probe as an explicit opt-in after Neovim reverted #41519; test CodeDiff scrollbind setup unconditionally. Bound Windows worker concurrency and whole-spec runtime, and report active workers during long runs.
@esmuellert

Copy link
Copy Markdown
Owner Author

CI follow-up at e6d6678: PR Validation run 34900369664 has completed successfully (14/14 jobs), including the full 123-spec suite on both Windows architectures. All required checks are green; auto-merge remains enabled and still awaits the required review approval. The separate GitHub-managed Advanced Security AI run 34900367001 failed with a service-side CAPI 400 "The requested model is not supported"; it is not a required check, and no security check was disabled to obtain the Validation result. CodeQL and GitGuardian checks passed.

@esmuellert
esmuellert merged commit a167094 into main Sep 14, 2026
19 of 20 checks passed
@esmuellert
esmuellert deleted the fix/issue-558-refresh-model branch September 14, 2026 23:50
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.

Merge conflict view jittering and constantly refreshing

2 participants