You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
> **This document replaces all prior restart plan versions.** If a prior statement conflicts with this one, this version wins.
6
6
> **Analyzed:** 2026-05-30 — additional gaps identified in §0.2–0.4, §3, §4, §6–8. Collab protocol gap and routing/product-model divergence newly documented.
7
7
@@ -95,7 +95,7 @@ The legacy collab syncs the **entire page state** (note positions, arrow endpoin
95
95
96
96
**Routing / product-model divergence:** In the new SPA, `/pages/:pageId` renders a **single Tiptap text card** (`PageEditorView.vue`), while `/spatial` is a separate stub showing page pins. In legacy, a "page" IS the spatial canvas — there is no bifurcation. The new architecture implicitly redefines "page" as a text document, which will conflict with spatial parity unless `/pages/:pageId` becomes the canvas and the single-note editor becomes one component inside it (the active note's head/body). **This is a deeper problem than "missing UI" — it is a routing and data-model decision that must be made before Phase 6.**
97
97
98
-
**Decision required:**Do we commit to full spatial parity, or do we ship a **single-note-per-page** product first and add the canvas later? This plan assumes **full spatial parity is required** because the legacy product is defined by it. If product wants to defer spatial canvas to a v2, rewrite §0.3 and all Phase 6+ references accordingly.
98
+
**Decision:**This plan commits to **full spatial parity**. The single-note-per-page stepping stone has been removed; `/pages/:pageId` will be the spatial canvas directly.
99
99
100
100
---
101
101
@@ -122,15 +122,15 @@ These were found during the v3–v4 analysis and must be addressed in the phases
122
122
123
123
5.**Group password unlock is unscheduled**
124
124
-`unlockPageCollabSymmetricKeyring` throws when a group requires a password. The comment says "Unlock is not implemented in the web MVP."
125
-
-**Fix:** Add to Phase 8 (group/account polish) or document as v2 scope.
125
+
-**Fix:** Add to Phase 7 (group/account polish) or document as v2 scope.
126
126
127
127
6.**`page_links` / backlink UI is missing**
128
128
- The backend has `pageLinks` table and routes (`POST /api/pages/:pageId/backlinks`). No SPA UI exposes backlinks.
129
-
-**Fix:** Add backlink display to Phase 5 or Phase 8.
129
+
-**Fix:** Add backlink display to Phase 7.
130
130
131
131
7.**No scheduler / manager CLI replacement**
132
132
- Legacy had `apps/scheduler` (cleanup) and `apps/manager` (ops CLI). New repo defers scheduler to "Cron Triggers or Queues" but has no implementation.
133
-
-**Fix:** Add deferred scheduler task to Phase 8 or Phase 9.
133
+
-**Fix:** Add deferred scheduler task to Phase 7 or Phase 8.
134
134
135
135
8.**Collab protocol is narrower than legacy; missing bootstrap-over-WS and unacked-buffer retry**
136
136
- Legacy client maintains `_unackedUpdates: Map<number, Uint8Array>` and re-sends on reconnect. New client has `collabClientUpdateId` but no `_unackedUpdates` buffer. Legacy collab-server sends `ALL_UPDATES_UNMERGED` on connect. New DO sends nothing — client must `GET /collab-updates` via REST.
@@ -139,11 +139,11 @@ These were found during the v3–v4 analysis and must be addressed in the phases
139
139
140
140
9.**`@deepnotes/session` package contains 71 files — potential god package**
141
141
- It mixes auth, users, groups, pages, billing, collab, crypto, and realtime logic. This violates the feature-based vertical-slice principle.
142
-
-**Fix:** Before Phase 8, audit file count per domain. If any subfolder exceeds 20 files, extract to a dedicated package (e.g., `@deepnotes/billing`, `@deepnotes/scheduler`).
142
+
-**Fix:** Before Phase 7, audit file count per domain. If any subfolder exceeds 20 files, extract to a dedicated package (e.g., `@deepnotes/billing`, `@deepnotes/scheduler`).
143
143
144
144
10.**No `@syncedstore/core` or equivalent reactive Yjs wrapper for Vue**
145
145
- Legacy uses `@syncedstore/core` so Vue components re-render when CRDT state changes. New repo has `yjs` + `y-protocols` + `@tiptap/y-tiptap` but no SyncedStore.
146
-
-**Impact:** Phase 6 spatial canvas cannot use Vue reactivity against Yjs maps directly without a wrapper.
146
+
-**Impact:** Phase 5 spatial canvas cannot use Vue reactivity against Yjs maps directly without a wrapper.
147
147
-**Fix:** Evaluate SyncedStore with Vite 6 (spike in Phase 3). If bundling fails, document Option C (hybrid reactive proxy) in `docs/SPATIAL_ARCHITECTURE_DECISION.md`.
@@ -156,8 +156,8 @@ These were found during the v3–v4 analysis and must be addressed in the phases
156
156
-**Fix:** Before coding Phase 3, produce a complete schema diff table in `docs/SPATIAL_PARITY_CHECKLIST.md`.
157
157
158
158
13.**No Playwright E2E infrastructure exists**
159
-
- Phase 8/9 success criteria require a Playwright smoke test, but there is no `apps/web/playwright.config.ts`, no `e2e/` folder, and no `@playwright/test` dependency.
160
-
-**Fix:** Add Playwright to Phase 4 or Phase 5 as a deliverable.
159
+
- Phase 7/8 success criteria require a Playwright smoke test, but there is no `apps/web/playwright.config.ts`, no `e2e/` folder, and no `@playwright/test` dependency.
160
+
-**Fix:** Add Playwright to Phase 4 as a deliverable.
161
161
162
162
14.**`PageCollabRoom` broadcast has no backpressure throttling**
163
163
- The DO calls `this.broadcast()` synchronously for every connected socket. Under high load (many clients, rapid edits), DO CPU time could exceed Cloudflare limits.
@@ -196,7 +196,7 @@ These were found during the v3–v4 analysis and must be addressed in the phases
196
196
-**Collab:**`PageCollabRoom` Durable Object. Yjs updates persisted to Postgres `page_updates`. **Only ProseMirror content is synced.**
197
197
-**Realtime:**`UserRealtimeRoom` Durable Object. Hash HGET/HSET + pub/sub via Upstash.
198
198
-**Scheduler:** Cron Triggers or Queues (not yet implemented; deferred).
199
-
-**Routing divergence:**`/pages/:pageId`= single-note text editor; `/spatial` = stub canvas. Legacy: `/pages/:pageId` = spatial canvas. This must be reconciled before Phase 6.
199
+
-**Routing decision:**`/pages/:pageId`will be the spatialcanvas. The Tiptap editor becomes the head/body editing component inside a note. Legacy divergence resolved in Phase 4.
200
200
201
201
---
202
202
@@ -532,9 +532,9 @@ The new `usePageCollabEditor` only syncs a ProseMirror `Y.XmlFragment`. We need
532
532
-`pnpm --filter @deepnotes/web test` runs in < 30 seconds.
533
533
534
534
5.**Route consolidation decision**
535
-
- Resolve the §0.3 routing divergence: either (a) make `/pages/:pageId` the spatial canvas and move the single-note Tiptap editor into a note-editing mode, or (b) keep `/pages/:pageId` as text-only and accept that spatial parity is a v2 feature.
535
+
- Resolve the §0.3 routing divergence: make `/pages/:pageId` the spatial canvas. The Tiptap editor becomes the head/body editing component inside a note.
536
536
- Document the decision in `docs/ROUTING_DECISION.md`.
537
-
-If option (a), create a migration plan for existing page bookmarks and shared links.
537
+
-Create a migration plan for existing page bookmarks and shared links if URLs change.
538
538
539
539
**Verification:**
540
540
-`app.test.ts` passes (shell renders, auth state reflects cookie).
@@ -549,60 +549,9 @@ The new `usePageCollabEditor` only syncs a ProseMirror `Y.XmlFragment`. We need
**Goal:** The `PageEditorView` is a fully functional **single-note** editor with all rich-text features from legacy. This is a **stepping stone** to the spatial canvas, not the final state.
557
-
558
-
**Deliverables:**
559
-
560
-
1.**Rich-text feature completeness**
561
-
- Verify every Tiptap extension from legacy is present:
**Goal:** A `PageEditorView` that renders an **infinite canvas** with draggable, resizable notes and connectable arrows. This is the core DeepNotes product differentiator.
608
557
@@ -682,9 +631,9 @@ The new `usePageCollabEditor` only syncs a ProseMirror `Y.XmlFragment`. We need
682
631
683
632
---
684
633
685
-
### Phase 7: Spatial canvas polish (3 weeks)
634
+
### Phase 6: Spatial canvas polish (3 weeks)
686
635
687
-
**Prerequisites:** Phase 6 done.
636
+
**Prerequisites:** Phase 5 done.
688
637
689
638
**Goal:** All remaining spatial interactions from the legacy checklist.
690
639
@@ -737,7 +686,7 @@ The new `usePageCollabEditor` only syncs a ProseMirror `Y.XmlFragment`. We need
737
686
738
687
---
739
688
740
-
### Phase 8: Account, billing, groups polish (1 week)
689
+
### Phase 7: Account, billing, groups polish (1 week)
741
690
742
691
**Prerequisites:** Phase 4 and Phase 5 done.
743
692
@@ -782,9 +731,9 @@ The new `usePageCollabEditor` only syncs a ProseMirror `Y.XmlFragment`. We need
782
731
783
732
---
784
733
785
-
### Phase 9: Mobile shells and cutover (2 weeks)
734
+
### Phase 8: Mobile shells and cutover (2 weeks)
786
735
787
-
**Prerequisites:** Phase 7 and Phase 8 done.
736
+
**Prerequisites:** Phase 6 and Phase 7 done.
788
737
789
738
**Goal:** Prepare for production cutover.
790
739
@@ -824,22 +773,22 @@ The new `usePageCollabEditor` only syncs a ProseMirror `Y.XmlFragment`. We need
824
773
|------|------------|--------|------------|
825
774
|**Test infrastructure stays broken**| High if not prioritized | Blocks all other work |**Phase 0 is mandatory and comes first.** No feature work until tests pass. |
826
775
|**Spatial canvas underestimated**| Already happened | 6+ weeks slip | Acknowledged in §0.3. Do not allow agents to mark stubs as "done." Use checklist in Phase 1. |
827
-
|**SyncedStore / Yjs reactivity issues**| Medium | Blocks Phase 6| Make architecture decision (§6) before coding. Spike 1 day to test SyncedStore with Vite 6 + Vue 3.5. |
776
+
|**SyncedStore / Yjs reactivity issues**| Medium | Blocks Phase 5| Make architecture decision (§6) before coding. Spike 1 day to test SyncedStore with Vite 6 + Vue 3.5. |
828
777
|**Collab protocol mismatch**| Medium | Data corruption | Version the collab protocol (`v1` = ProseMirror-only, `v2` = page-level). Reject unknown message types gracefully. |
829
778
|**Performance: many notes on one page**| Medium | Laggy canvas | Set a soft limit (e.g., 200 notes) and benchmark. Use virtual rendering or canvas-based rendering if DOM scales poorly. |
830
779
|**Stripe-only after dropping RevenueCat**| Low | User churn | Communicate to IAP users before cutover. Offer migration grace period. |
831
-
|**Worker CPU limits under collab load**| Medium | Dropped connections | Load test early (Phase 9 staging). If DO CPU is the bottleneck, shard `PageCollabRoom` by page ID prefix. |
780
+
|**Worker CPU limits under collab load**| Medium | Dropped connections | Load test early (Phase 8 staging). If DO CPU is the bottleneck, shard `PageCollabRoom` by page ID prefix. |
832
781
|**God-object state returns**| Medium | Unmaintainable code | Cap composable size at 300 lines. If `useSpatialViewport.ts` grows beyond that, split into `useCamera`, `usePanning`, `useZooming`. |
833
782
|**`page_updates` format migration**| Medium | Data corruption or unreadable legacy pages | Decide Option A/B in Phase 3 before any spatial collab code. Test decrypt of 100 random legacy pages after migration. |
834
783
|**DO hibernation drops WS state**| Medium | Users see collab reconnects |`PageCollabRoom` is stateless relay, so hibernation is safe. Document in `docs/COLLAB_DO_ARCHITECTURE.md`. If stateful DO chosen later, implement reconnect protocol. |
835
784
|**i18n / SSR regressions**| Low | Accessibility, SEO, share-ability loss | Document as accepted v2 regressions or schedule recovery. |
836
-
|**Group password not implemented**| Low | Users cannot access password-protected groups in new app | Add to Phase 8. If deferred, document v2 scope. |
837
-
|**No scheduler = soft-deleted data accumulates**| Medium | DB bloat | Add Cron Trigger or Queue cleanup to Phase 8/9. |
785
+
|**Group password not implemented**| Low | Users cannot access password-protected groups in new app | Add to Phase 7. If deferred, document v2 scope. |
786
+
|**No scheduler = soft-deleted data accumulates**| Medium | DB bloat | Add Cron Trigger or Queue cleanup to Phase 7/8. |
838
787
|**Collab protocol narrower than legacy**| Medium | Slower reconnects, lost ACK edge cases | Document in `docs/COLLAB_PROTOCOL_PARITY.md`. Monitor unacked-update metrics. |
839
-
|**`@deepnotes/session` god package**| Medium | Cross-domain coupling, slow test feedback | Audit and split into dedicated packages before Phase 8. |
788
+
|**`@deepnotes/session` god package**| Medium | Cross-domain coupling, slow test feedback | Audit and split into dedicated packages before Phase 7. |
840
789
|**`page_updates` no pagination**| Medium | OOM on large page bootstrap | Fixed in Phase 0 with `?sinceIndex=`. Monitor max response size in production. |
841
790
|**Routing divergence (page vs spatial)**| Medium | User confusion, broken bookmarks | Decide in Phase 4. Communicate clearly if URLs change. |
842
-
|**No Playwright = no E2E gate**| Medium | Regressions slip into production | Add skeleton in Phase 0; build smoke test in Phase 8. |
791
+
|**No Playwright = no E2E gate**| Medium | Regressions slip into production | Add skeleton in Phase 0; build smoke test in Phase 7. |
843
792
|**Legacy schema fields omitted in new model**| Medium | Subtle data-loss or UI bugs | Enforce Phase 1 schema diff table as a hard gate before Phase 3 coding. |
844
793
845
794
---
@@ -862,8 +811,8 @@ A criterion is **not met** until the verification command or check passes in CI.
-[ ]**Schema completeness:** Phase 3 Yjs schema includes every field from the Phase 1 diff table.
868
817
-[ ]**Backlinks:** SPA displays incoming page backlinks with decrypted titles.
869
818
-[ ]**Playwright:** E2E smoke test covers register → create page → edit → invite → logout in < 60 seconds.
@@ -925,16 +874,16 @@ What was wrong: the original plan **catastrophically underestimated the spatial
925
874
926
875
What v4 adds beyond v3:
927
876
-**Collab protocol gap is wider than described.** The new protocol lacks bootstrap-over-WS, unacked-update retry, and the ACK handler has a logic error. These must be fixed in Phase 0 before spatial work touches Yjs.
928
-
-**Routing/product-model divergence.**`/pages/:pageId`is a text editor; `/spatial` is a stub. Legacy has no such split. Phase 4 must resolve this before Phase 6.
877
+
-**Routing/product-model divergence.**`/pages/:pageId`will become the spatial canvas; `/spatial`stub is removed. Legacy has no such split. Phase 4 must resolve this before Phase 5.
929
878
-**Schema incompleteness risk.** The proposed Phase 3 schema omitted ~10 legacy fields. Phase 1 now requires a complete diff table as a hard gate.
930
879
-**Missing infrastructure.** No `vitest.workspace.ts`, no Playwright, no collab pagination, no `ydoc.on('updateV2')` listener. Phase 0 now includes all of these.
931
880
932
881
What must happen now:
933
882
1.**Fix the test foundation (Phase 0).** No agent should add features while tests are broken. Split `usePageCollabEditor`, fix ACK logic, add `updateV2` listener, add pagination, add Playwright.
934
883
2.**Inventory spatial features (Phase 1).** Produce a checklist **and a complete schema diff table** that prevents misreporting stubs as done.
935
884
3.**Extend collab to page-level Yjs (Phase 3).** The current ProseMirror-only collab cannot support spatial notes. Include SyncedStore spike and incremental bootstrap.
936
-
4.**Resolve routing divergence (Phase 4).**Decide whether `/pages/:pageId`becomes the canvas or stays text-only.
937
-
5.**Build the spatial canvas incrementally (Phases 6–7).** MVP first (create/move/resize/delete notes + arrows), then polish (selection, containers, clipboard, undo).
885
+
4.**Resolve routing divergence (Phase 4).**Make `/pages/:pageId` the spatial canvas and integrate the Tiptap editor as a note component.
886
+
5.**Build the spatial canvas incrementally (Phases 5–6).** MVP first (create/move/resize/delete notes + arrows), then polish (selection, containers, clipboard, undo).
938
887
6.**Verify everything with automated tests.** Every phase has objective exit criteria.
939
888
940
889
Retire the legacy repo only when spatial parity, auth smoke, and data checks are proven.
0 commit comments