fix: skip identity changesets during timeslider playback#7438
fix: skip identity changesets during timeslider playback#7438JohnMcLear merged 5 commits intoether:developfrom
Conversation
When a pad's revision history contains an identity changeset (Z:N>0$, representing no actual change), the timeslider playback would crash or break because broadcast.ts tried to apply it via mutateAttributionLines and mutateTextLines. Now all three applyChangeset call sites in broadcast.ts check for identity changesets using the existing isIdentity() helper and skip them. This also prevents errors when compose() produces an identity changeset from multiple revisions that cancel each other out. Fixes: ether#5214 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Review Summary by QodoSkip identity changesets during timeslider playback
WalkthroughsDescription• Skip identity changesets during timeslider playback to prevent crashes • Add isIdentity() checks at all three applyChangeset call sites • Import isIdentity helper from Changeset.ts module • Add two Playwright regression tests for timeslider with identity changesets Diagramflowchart LR
A["Timeslider playback"] --> B["Check if changeset is identity"]
B -->|Identity| C["Skip applyChangeset"]
B -->|Real change| D["Apply changeset normally"]
C --> E["No crash or corruption"]
D --> E
File Changes1. src/static/js/broadcast.ts
|
Code Review by Qodo
1. Protocol-specific URLs in tests
|
| await page.waitForTimeout(1000); | ||
|
|
||
| // Navigate to timeslider | ||
| await page.goto(`http://localhost:9001/p/${padId}/timeslider`); |
There was a problem hiding this comment.
1. Protocol-specific urls in tests 📘 Rule violation ⚙ Maintainability
New code introduces protocol-specific URLs (for localhost navigation and an issue link), violating the requirement to use protocol-independent URLs. This can reduce portability across environments and breaks the documented coding style rule.
Agent Prompt
## Issue description
Newly added code uses protocol-specific URLs (e.g., `http://...` and `https://...`) where the coding style requires protocol-independent URLs.
## Issue Context
This is explicitly disallowed by the PR compliance checklist (Coding Style requires protocol-independent URLs such as `//example.com`).
## Fix Focus Areas
- src/tests/frontend-new/specs/timeslider_identity_changeset.spec.ts[36-36]
- src/tests/frontend-new/specs/timeslider_identity_changeset.spec.ts[78-82]
- src/static/js/broadcast.ts[281-283]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
SamTV12345
left a comment
There was a problem hiding this comment.
I see so we just skip identical changesets. How can they be created one after the other directly?
Move the isIdentity() guard from the call sites into applyChangeset() itself, so that identity changesets still advance currentRevision, currentTime, slider position, and author UI — just skipping the mutation (mutateAttributionLines/mutateTextLines). This prevents the timeslider from getting stuck on a stale revision when an identity changeset is encountered. Also removes unused `identity` import. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
@SamTV12345 Good question! Identity changesets (net-zero changes) can appear in a few ways:
The original code had Also addressed the Qodo review feedback: moved the |
Review Summary by QodoSkip identity changesets during timeslider playback
WalkthroughsDescription• Move identity changeset detection inside applyChangeset() to skip mutations while advancing state • Identity changesets now properly advance revision, time, slider position, and author UI • Add regression tests for timeslider playback with delete-retype revision sequences • Prevent timeslider from getting stuck on stale revisions when encountering identity changesets Diagramflowchart LR
A["Identity Changeset<br/>Z:N>0$"] --> B["applyChangeset()"]
B --> C{"isIdentity<br/>check"}
C -->|True| D["Skip mutations<br/>mutateAttributionLines<br/>mutateTextLines"]
C -->|False| E["Apply mutations"]
D --> F["Advance state<br/>revision/time/slider"]
E --> F
F --> G["Timeslider continues<br/>without errors"]
File Changes1. src/static/js/broadcast.ts
|
- Verify slider position advances during playback (confirms revisions including identity changesets are processed, not skipped) - Scrub through every revision individually instead of just rev 0 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The test was starting playback from the latest revision, so the slider had nowhere to advance — causing the position assertion to fail in CI. Now navigates to #0 first so playback progresses through all revisions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Persistent review updated to latest commit d01c946 |
The isIdentity() check was moved inside applyChangeset() but the old comment remained at the call sites, creating a misleading code/comment mismatch. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
Fix in
broadcast.ts: Identity changesets are now handled insideapplyChangeset()— the mutation steps (mutateAttributionLines/mutateTextLines) are skipped, but revision state, timer, slider position, and author UI are still advanced correctly.Root Cause
When a pad's revision history contains an identity changeset (
Z:N>0$, meaning no actual change), the timeslider playback tried to apply it viamutateAttributionLinesandmutateTextLines. These functions can error or corrupt state when given a changeset with no operations.Identity changesets can appear in the revision history when:
compose()of multiple revisions produces a net-zero changeThe existing truthiness check (
if (changeset)) didn't catch identity changesets because they're non-empty strings like"Z:1>0$".Test plan
messages.tstests pass (10/10)Fixes #5214
🤖 Generated with Claude Code