brainstorming
brainstorming
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or planning anything new, in software or out of it (a talk, a business, a renovation).
yjs-nextjsCorrects the wrong defaults a capable model has when building Yjs collaborative editing into a Next.js 16 App Router app with tRPC v11 and shadcn/ui — pinned to Yjs 13.6.31. Covers the decisions whose failure mode is silence rather than an error — plain objects in a Y.Map discarding concurrent edits, updates that vanish because a JSON boundary turned a Uint…
Independent GitHub snapshot · Automated classification · Original documentation governs
Yjs 13.6.31 in an App Router codebase — the decisions collaborative editing forces and how to settle them, written so an agent applies them while writing or reviewing code. Each rule names the wrong default it corrects; there is no rule for things the model already gets right.
Most of these failures are silent. Yjs converges, the editor keeps working, and the data loss surfaces later as "my change reverted" — so the rules below lead with the evidence of the failure, most of it measured directly against Yjs 13.6.31 rather than asserted.
Y.Doc state to a database, or compacting stored updatesThis skill does not cover general Next.js App Router patterns (use the nextjs skill), general tRPC usage (trpc), or shadcn/ui component conventions (shadcn) — only where those intersect with a CRDT.
Rules target Yjs 13.6.31 with y-websocket 3.0.0, y-protocols 1.0.7, and y-indexeddb 9.0.12. Yjs is mid-migration to v14 under the @y/* scope, and several packages have already moved latest or main onto that prerelease track — @y/websocket-server@0.1.5 depends on yjs@^14.0.0-7. See host-pin-the-yjs-13-track before installing anything.
| # | Category | Prefix | Covers |
|---|---|---|---|
| 1 | Sync Topology & Dependencies | host- | Where the sync loop runs, which backend to build on, where the trust boundary sits, which versions are on the v13 track |
| 2 | Binary Transport & Persistence | wire- | Moving Uint8Array updates across JSON boundaries and into storage |
| 3 | Modeling State in Shared Types | model- | Which shared type per field — decides whether concurrent edits merge or destroy each other |
| 4 | Doc Lifecycle & React Binding | react- | One document across renders, Strict Mode, subscriptions, the server boundary |
| 5 | Undo, Snapshots & Offline Load | hist- | Origin-scoped history, garbage collection, load ordering |
| 6 | Awareness & Cursors | pres- | Ephemeral presence and positions that survive concurrent edits |
| 7 | Editor & Form Integration | ui- | Tiptap and shadcn/ui form controls against a live document |
host-route-handlers-cannot-upgrade — App Router has no socket-upgrade API; the sync server needs a life independent of a requesthost-vercel-lacks-instance-affinity — Vercel serves WebSockets but does not pin a room to an instance; in-memory registries split one document in twohost-websocket-server-is-not-production — the reference server is a starting point, and YPERSISTENCE no longer existshost-pin-the-yjs-13-track — installing latest pulls a Yjs 14 prerelease into a v13 apphost-trpc-is-not-the-sync-channel — SSE is one-way and binary input is POST-only; use tRPC for authorization and snapshotshost-authorize-rooms-not-updates — a CRDT update cannot be schema-validated or permission-checked, so the room is the trust boundarywire-rehydrate-to-uint8array — applyUpdate accepts a number[] and silently applies nothingwire-base64-not-superjson — superjson round-trips typed arrays at 3.69x payload; base64 costs 1.33xwire-send-updates-not-state — 24 bytes of delta versus 5041 bytes of full state for one keystrokewire-compaction-needs-a-doc-roundtrip — mergeUpdates re-encodes but never reclaims deleted contentmodel-nested-types-not-plain-objects — a plain object in a Y.Map is one conflict unit; concurrent edits to different fields lose onemodel-yarray-has-no-move — delete-then-insert duplicates the row when two people drag it at oncemodel-ytext-only-for-prose — two people retyping a Y.Text title produce both values spliced togethermodel-seed-the-document-once — an emptiness check passes on every client, so the template lands twicereact-stable-doc-and-provider — own creation and teardown in one effect, because Strict Mode runs setup, cleanup, setupreact-subscribe-via-usesyncexternalstore — no official binding exists, and toJSON() returns a new reference every callreact-keep-yjs-client-only — providers need browser globals, and 'use client' still renders on the serverhist-scope-undo-by-origin — the default undo manager tracks remote edits and will revert a colleague's workhist-snapshots-require-gc-disabled — decided at construction; restoring a snapshot otherwise throwshist-wait-for-indexeddb-sync — an unloaded document is indistinguishable from an empty onepres-awareness-is-ephemeral — presence in the document is permanent, versioned, and never cleaned uppres-relative-positions-for-cursors — an index stops meaning the same place as soon as anyone types above itui-do-not-drive-inputs-from-the-doc — assigning value is specified to throw the caret to the end of the fieldui-tiptap-collaboration-wiring — Tiptap v3 uses its own fork, and content re-seeds on every loadRead a reference file when its decision comes up. Each rule names the wrong default it corrects, then shows the canonical way, with an incorrect/correct contrast where the wrong way is a real trap.
When debugging rather than writing, start from the symptom:
| Symptom | Read first |
|---|---|
| Document loads blank | wire-rehydrate-to-uint8array, hist-wait-for-indexeddb-sync |
| Content appears twice | model-seed-the-document-once, ui-tiptap-collaboration-wiring |
| Someone's edit silently reverted | model-nested-types-not-plain-objects, hist-scope-undo-by-origin |
| List items duplicate when dragged | model-yarray-has-no-move |
| Two editors never see each other | host-vercel-lacks-instance-affinity |
| A user edited a field they should not reach | host-authorize-rooms-not-updates |
| A collaborator's avatar lingers after they leave | pres-awareness-is-ephemeral |
| Caret jumps to the end while typing | ui-do-not-drive-inputs-from-the-doc |
| Infinite render loop or snapshot error | react-subscribe-via-usesyncexternalstore |
| Works in production, broken in dev | react-stable-doc-and-provider |
| File | Description |
|---|---|
| references/_sections.md | Category definitions and ordering |
| assets/templates/_template.md | Template for new rules |
| AGENTS.md | Auto-built table of contents across all rules |
| metadata.json | Version and source references |