Conversation
…-56) Replace replaceYjsUpdates (full DELETE + INSERT) with compactUpToId that deletes only rows with id <= maxId so concurrent renderer writes landing after the snapshot are preserved; refactor the five YJS db functions to take an injected db handle matching the artifacts.ts pattern.
Rewrites materializeNoteContent to replay yjs_updates rows into a transient Y.Doc and read the canonical XmlFragment via yXmlFragmentToProsemirrorJSON, replacing the stale Y.Text-as-JSON approach. Falls back to notes.content + stripMarkdownLite for notes with zero Yjs rows. Updates extractPlainText to accept ProseMirror JSON objects in addition to legacy Lexical JSON strings.
Adds the note_snapshots table (migration 0011), saveNoteSnapshot helper, and pruneNoteSnapshots with two-axis retention + manual-snapshot protection. 6 new tests covering save round-trip, compaction-independence, and retention semantics. No UI, no auto-capture, no integration sites (PRSM-58).
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.
Summary
Y.Text("content")-as-JSON-string sync with TipTap'sCollaborationextension bound to a realY.Docviay-prosemirror. Editor now has a true CRDT binding instead of a per-keystroke JSON re-serialize-and-stuff.notes.contentas a one-way debounced markdown sidecar (1.5s after last edit) — used for export, search, RAG. The authoritative state lives inyjs_updates.compactUpToId(noteId, maxId, encoded)deletes only updates at or below a high-watermark id, so a renderer save concurrent with a compaction is never destroyed. Includes a regression test that demonstrates the originalreplaceYjsUpdateswas lossy.note_snapshotstable (self-contained encoded Y.Doc state, independent ofyjs_updatescompaction),saveNoteSnapshot, andpruneNoteSnapshotswith two-axis retention (age + count,manualsnapshots exempt). No capture sites wired — PRSM-58 builds the UI on top.What's NOT in this PR
yXmlFragmentToProsemirrorJSONusage incollect-input.tsis@deprecatedin y-prosemirror 1.3.7. Functional today; cleanup follow-up: migrate toyXmlFragmentToProseMirrorRootNode(fragment, schema).toJSON().COLLAB_FRAGMENT_NAMEconstant currently lives inmarkdown-to-ydoc.ts; a more neutral home (editor-constants.ts) is a follow-up.Architectural notes for reviewers
db/notes.tsYJS + snapshot functions now takedb: DBas first parameter, matching the pre-existing pattern indb/artifacts.tsanddb/skills.ts. Avoids a singleton in tests.NoteSyncProviderkeeps a 150ms batched persister (Y.mergeUpdates) and a 1.5s markdown debouncer.destroy()flushes BOTH before settingdestroyed = true(load-bearing — both flush methods short-circuit on the guard).Collaboration.configure({ document, field: COLLAB_FRAGMENT_NAME })andmarkdownToYDocUpdateboth reference the same"default"fragment constant. StarterKitundoRedois disabled only when a Y.Doc is provided — non-collab callers (artifact editor) keep default undo.note_snapshotsrows store the full encoded Y.Doc state (not a marker onyjs_updates), so aggressive compaction never invalidates a historical snapshot. Retention: 30 days / 50 newest per note, manuals never pruned.Migrations included
0011_overrated_bushwacker.sql— createsnote_snapshotstable + composite index on(note_id, created_at). Drizzle regenerated an unrelated artifacts index in the same generation pass; that duplicate was hand-removed so the migration applies cleanly.Test plan
pnpm --filter @prismical/desktop test). 18 new tests added across the 13 commits (compaction race regression, save-failure retry, markdown helper, sync-provider batching/debounce/destroy, artifact serializer regression, snapshot save round-trip, snapshot-independence-from-compaction, retention policies).pnpm --filter @prismical/desktop type:checkclean.Y.applyUpdate→Y.XmlFragment("default")→yXmlFragmentToProsemirrorJSONproduced JSON whose top-level shape (heading, paragraphs, artifact node with full attrs) matched the markdown sidecar.Y.UndoManageris wired correctly (StarterKit's undo is disabled).Linked
docs/superpowers/specs/2026-05-14-prsm-56-yjs-pivot-design.mddocs/superpowers/plans/2026-05-14-prsm-56-yjs-pivot.md