Bug Description
When YAOS plugin is active, copying content from one Obsidian .md file and pasting into another .md file causes a repeat-paste loop — the pasted content duplicates over and over.
The issue does NOT occur when YAOS is disabled.
Root Cause Analysis
Race condition between editor binding settle window and reconciliation recovery path.
Mechanism
- User switches from File A to File B → binding is established for File B with
settleWindowMs = 1600ms (fast tab switch)
- During settle window, user pastes content into File B
handleLiveEditorUpdate updates lastEditorChangeAtMs but yCollab hasn't fully connected yet → paste does NOT propagate to Y.Text
- Obsidian autosave writes to disk →
vault.on("modify") → dirty set
- Dirty-set drain →
handleBoundFileSyncGap → localOnly divergence (editor=disk=pasted, CRDT≠pasted)
- Idle guard defers for 3000ms, then recovery applies diff: Y.Text gets pasted content
- Binding eventually settles → yCollab syncs editor ↔ Y.Text → detects divergence → inserts paste AGAIN → DUPLICATE
Key Files
src/sync/editorBinding.ts:1072 — lastEditorChangeAtMs set to bind time, not editor activity time
src/sync/editorBinding.ts:784-791 — handleLiveEditorUpdate fires on paste but yCollab may not be connected yet
src/runtime/reconciliationController.ts:1583-1643 — localOnly idle guard doesn't check binding health/settle status
Why Existing Guards Don't Catch It
| Guard |
Why It Fails |
| Idle guard (3000ms) |
Defers the race, doesn't prevent yCollab's late insert |
| Amplification quarantine |
Growth may oscillate, not strictly monotonic |
| Fingerprint quarantine |
Each cycle has different (prev,next) → new fingerprint |
| Recovery lock (1500ms) |
yCollab's insert is independent, not a recovery operation |
Proposed Fix
In the localOnly branch of handleBoundFileSyncGap, check binding health/settle status before running recovery. If binding is still settling or unhealthy, skip recovery and let yCollab handle the paste once settled:
// Before the idle guard check, add:
const bindHealth = editorBindings?.getBindingHealthForView(openViews[0]);
if (bindHealth && (bindHealth.settling || !bindHealth.healthy)) {
this.amplificationHistory.delete(file.path);
return true;
}
Environment
- OS: Windows
- YAOS version: 1.6.1
- Reproducible: consistently when YAOS is active
Bug Description
When YAOS plugin is active, copying content from one Obsidian .md file and pasting into another .md file causes a repeat-paste loop — the pasted content duplicates over and over.
The issue does NOT occur when YAOS is disabled.
Root Cause Analysis
Race condition between editor binding settle window and reconciliation recovery path.
Mechanism
settleWindowMs = 1600ms(fast tab switch)handleLiveEditorUpdateupdateslastEditorChangeAtMsbut yCollab hasn't fully connected yet → paste does NOT propagate to Y.Textvault.on("modify")→ dirty sethandleBoundFileSyncGap→ localOnly divergence (editor=disk=pasted, CRDT≠pasted)Key Files
src/sync/editorBinding.ts:1072—lastEditorChangeAtMsset to bind time, not editor activity timesrc/sync/editorBinding.ts:784-791—handleLiveEditorUpdatefires on paste but yCollab may not be connected yetsrc/runtime/reconciliationController.ts:1583-1643— localOnly idle guard doesn't check binding health/settle statusWhy Existing Guards Don't Catch It
Proposed Fix
In the localOnly branch of
handleBoundFileSyncGap, check binding health/settle status before running recovery. If binding is still settling or unhealthy, skip recovery and let yCollab handle the paste once settled:Environment