fix: preserve frozen slides when slide count changes#537
Merged
Conversation
generateActions resolved freeze by overwriting before[i] with after[i] after slide-count padding, on the assumption that index i in before corresponds to index i in after. Inserting or deleting slides before a frozen slide breaks that assumption. The overwrite corrupted the padded slides and their internal new/delete marks, so the Hungarian mapping ended up updating or deleting the very slide that should have been preserved. Instead, resolve each frozen slide's counterpart before padding, using an order-preserving alignment where non-frozen slides act as anchors through content similarity and frozen slides are matched by relative order between those anchors. Frozen slides cannot be matched by content because their actual content may have diverged from the markdown source, which is often the reason for freezing in the first place. The counterpart's current content is then substituted into after, so the similarity-based pipeline sees the pair as identical and generates no update or delete actions for it while still allowing moves. The "freeze slide moved" test expectation changes from [A, C, C] to [A, C, B] because the frozen slide's actual content is now preserved and moved instead of being overwritten by its neighbor. Fix #479
Contributor
Code Metrics Report
Details | | main (e5d55df) | #537 (8a636e3) | +/- |
|---------------------|----------------|----------------|-------|
+ | Coverage | 33.9% | 34.8% | +0.8% |
| Files | 31 | 32 | +1 |
| Lines | 3416 | 3467 | +51 |
+ | Covered | 1161 | 1207 | +46 |
+ | Code to Test Ratio | 1:0.9 | 1:0.9 | +0.0 |
| Code | 7037 | 7118 | +81 |
+ | Test | 6616 | 6969 | +353 |
- | Test Execution Time | 11s | 12s | +1s |Code coverage of files in pull request scope (80.7% → 82.0%)
Reported by octocov |
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes slide-freeze preservation when slide counts change (insert/delete), by resolving frozen slide counterparts via an order-preserving alignment rather than assuming before[i] corresponds to after[i]. This keeps frozen slides from being incorrectly updated/deleted during the existing similarity + Hungarian mapping pipeline while still allowing move actions.
Changes:
- Added
resolveFrozenSlidesalignment logic to map frozen “after” slides to their most likely “before” counterparts without relying on index equality. - Updated
generateActionsto substitute preserved slide content intoafterfor resolved frozen pairs (and to clearFreezefor brand-new frozen slides so append applies content). - Added unit tests for the frozen-slide resolver and expanded action-generation tests to cover insert/delete + divergence scenarios.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| freeze.go | Introduces frozen-slide counterpart resolution via order-preserving DP alignment + similarity fallback. |
| freeze_test.go | Adds focused unit coverage for frozen-slide resolution scenarios. |
| action.go | Integrates frozen-slide resolution into generateActions and removes index-based freeze overwrite logic. |
| action_test.go | Updates an existing expectation and adds regression tests for insert/delete/divergence/new-frozen-slide cases. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
k1LoW
marked this pull request as ready for review
July 7, 2026 07:31
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
ref: #479
Problem
When a slide with
freeze: trueexists and the total number of slides changes by inserting or deleting slides, the frozen slide disappears or a wrong slide is preserved.generateActionsresolved freeze by overwritingbefore[i]withafter[i]after slide-count padding, assuming that indexiin before corresponds to indexiin after. Inserting or deleting slides before a frozen slide breaks that assumption. The overwrite corrupted the padded slides and their internal new/delete marks, so the Hungarian mapping ended up updating or deleting the very slide that should have been preserved.Approach (frozen slides as order-constrained wildcards)
Frozen slides cannot be matched by content similarity because their actual content may have diverged from the markdown source, which is often the reason for freezing in the first place. So the freeze resolution is moved out of the index-based pre-processing.
resolveFrozenSlides(newfreeze.go) determines the counterpart of each frozen slide with an order-preserving alignment (Needleman-Wunsch style). Non-frozen slides act as anchors through content similarity, and frozen slides are matched by relative order between those anchors, with a score floor so that content divergence does not prevent matching. A second pass matches remaining frozen slides against unmatched before slides by similarity, covering frozen slides moved across anchors.generateActionssubstitutes the counterpart's current content intoafter, so the existing similarity-based pipeline sees the pair as identical and generates no update or delete actions for it, while move actions still work. The former pre-processing that overwrotebefore[i]is removed.freeze: true) is created from its markdown content. Freezing protects it from the next apply onward.Behavior change
The existing
freeze slide movedtest expected[A, C, C], where the frozen slide B was overwritten by an update and C ended up duplicated. The expectation is now[A, C, B]. The frozen slide keeps its actual content and is moved to the desired position, which matches the intent of freezing.Tests
resolveFrozenSlidesinfreeze_test.go.go test ./...,golangci-lint run ./..., and a 30sFuzzGenerateActionsrun all pass.Known limitations
This PR narrows the freeze failure surface to the two issue-reported cases and their close variants, but the
Frozen-as-Anchorapproach still infers the markdown-page ⟷ existing-slide correspondence rather than recording it. The following combinations remain ambiguous and can produce a surprising result.before = [A, B, C],after = [C(freeze), B(freeze), A(freeze)]. With no non-frozen anchors, the order-preserving alignment matches by position, so each slide's content stays put and the reorder intent is silently lost.before = [A(title), B(custom layout, hand-edited), C(title)],after = [A(title), B(freeze, title)].getSimilarityreturns 0 on layout mismatch, so bothBandCland at the frozen floor with the same score; the DP tie-break can pickCas the counterpart and the intendedBis then removed as a delete-marker.resolveFrozenSlidesis a greedy per-frozen-slide argmax. When several frozen slides compete for the same before slide, the winner depends on iteration order rather than joint optimality.These are all instances of the residual identity gap noted in the architecture analysis; a follow-up along the "Stable Slide Identity" direction is needed to close them structurally.