Skip to content

Commit 8f01a75

Browse files
committed
docs: improve restart plan
1 parent 23ae5fc commit 8f01a75

1 file changed

Lines changed: 103 additions & 6 deletions

File tree

docs/RESTART_PLAN.md

Lines changed: 103 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
# DeepNotes — Restart (greenfield) plan — v2
1+
# DeepNotes — Restart (greenfield) plan — v3
22

33
> **Last updated:** 2026-05-29
44
> **Status:** Phase 2–3 backend largely complete. Phase 4–5 SPA partially complete. **Foundation bugs and spatial canvas NOT started.**
5-
> **This document replaces all prior restart plan versions.** If a prior statement conflicts with this one, this version wins.
5+
> **This document replaces all prior restart plan versions.** If a prior statement conflicts with this one, this version wins.
6+
> **Analyzed:** 2026-05-29 — additional gaps identified in §0.4, §3, §5–8.
67
78
---
89

@@ -75,6 +76,41 @@ The legacy collab syncs the **entire page state** (note positions, arrow endpoin
7576

7677
---
7778

79+
## 0.4 Additional critical gaps discovered
80+
81+
These were found during the v3 analysis and must be addressed in the phases below.
82+
83+
1. **`usePageCollabEditor.ts` is already a god object (708 lines)**
84+
- The plan itself recommends capping composables at 300 lines (§7). The main collab composable already violates this.
85+
- **Fix:** Split into `useCollabWebSocket.ts`, `useCollabCrypto.ts`, `useCollabPush.ts`, `usePageEditor.ts` before any spatial work.
86+
87+
2. **`PageCollabRoom` DO is a stateless relay, not an in-memory Yjs host**
88+
- Legacy `collab-server` held Yjs `Doc` instances in memory and synced via `y-protocols`. The new DO only decrypts/relays opaque blobs via `WORKER_SELF`.
89+
- **Impact:** The server cannot enforce page size limits, merge updates intelligently, or validate structure.
90+
- **Fix:** Document the architectural trade-off in `docs/COLLAB_DO_ARCHITECTURE.md`. If page-level Yjs is added (Phase 3), consider whether the DO should load the Yjs doc into memory.
91+
92+
3. **`page_updates` backward compatibility**
93+
- Existing production rows contain encrypted ProseMirror-only Yjs updates. Phase 3 will introduce page-level Yjs docs (notes + arrows).
94+
- **Decision required:** Add `page_spatial_updates` table, or version the update format within `page_updates` so old rows remain readable.
95+
96+
4. **SSR and i18n regressions vs legacy**
97+
- Legacy `apps/client` had SSR (`src-ssr`) and `vue-i18n`. New `apps/web` is pure SPA with no i18n infrastructure.
98+
- **Fix:** Product decision — document as accepted regressions or schedule recovery.
99+
100+
5. **Group password unlock is unscheduled**
101+
- `unlockPageCollabSymmetricKeyring` throws when a group requires a password. The comment says "Unlock is not implemented in the web MVP."
102+
- **Fix:** Add to Phase 8 (group/account polish) or document as v2 scope.
103+
104+
6. **`page_links` / backlink UI is missing**
105+
- The backend has `pageLinks` table and routes (`POST /api/pages/:pageId/backlinks`). No SPA UI exposes backlinks.
106+
- **Fix:** Add backlink display to Phase 5 or Phase 8.
107+
108+
7. **No scheduler / manager CLI replacement**
109+
- Legacy had `apps/scheduler` (cleanup) and `apps/manager` (ops CLI). New repo defers scheduler to "Cron Triggers or Queues" but has no implementation.
110+
- **Fix:** Add deferred scheduler task to Phase 8 or Phase 9.
111+
112+
---
113+
78114
## 1. What "restart" should mean (revised)
79115

80116
| Goal | Meaning in practice |
@@ -217,18 +253,29 @@ Each phase has:
217253
- Add `services: postgres` to the GitHub Actions `test` job (or use `docker-compose up -d` in a step).
218254
- Export `DATABASE_URL`, `DATABASE_ADMIN_URL`, `TEST_DB_TEMPLATE_NAME` so `template-db.test.ts` and `account-flows.integration.test.ts` run instead of skipping.
219255

256+
6. **Refactor `usePageCollabEditor.ts` into focused composables**
257+
- Split the 708-line composable into `useCollabWebSocket.ts`, `useCollabPush.ts`, `useCollabCrypto.ts`, `usePageEditor.ts`. Each must be < 300 lines.
258+
- Update `PageEditorView.vue` imports.
259+
260+
7. **Add root `vitest.workspace.ts` in `new-deepnotes`**
261+
- The outer repo root (`DeepNotes/`) has its own `vitest.config.ts` for legacy. `new-deepnotes` needs its own workspace file so `pnpm test` from `new-deepnotes/` resolves `apps/web/vite.config.ts` correctly.
262+
- Verify `pnpm test` from `new-deepnotes/` root passes with 0 failures.
263+
220264
**Verification:**
221265
```bash
266+
# From new-deepnotes/
222267
pnpm test
223268
# Expected: 0 failures, 0 skips for core tests.
224269
# Integration tests may still be long-running but must not be skipped for env reasons.
225270
```
226271

227272
**Exit criteria (all must be yes):**
228-
- [ ] `pnpm test` from repo root passes with 0 failures.
273+
- [ ] `pnpm test` from `new-deepnotes/` root passes with 0 failures.
229274
- [ ] `apps/web` unit tests run in `happy-dom` and can mount `.vue` files.
230275
- [ ] `useSession.test.ts` passes in isolation and in batch (`--run` 3 times).
231276
- [ ] CI test job runs integration tests against a real Postgres service.
277+
- [ ] `usePageCollabEditor.ts` is split into composables ≤ 300 lines each.
278+
- [ ] `router.ts` exports a factory and has zero module-load `window` access.
232279

233280
---
234281

@@ -244,6 +291,7 @@ pnpm test
244291
- `apps/client/src/code/pages/page/` (notes, arrows, camera, space, elems, selection, regions, collab)
245292
- `apps/client/src/layouts/PagesLayout/MainContent/DisplayPage/DisplayScreens/DisplayWorld/`
246293
- `apps/client/src/code/pages/page/collab/`
294+
- Specifically include `space/pos.ts`, `space/rects.ts`, `space/sizes.ts` for coordinate transform parity.
247295

248296
2. **Produce `docs/SPATIAL_PARITY_CHECKLIST.md`** with one table per subsystem:
249297
- **Notes:** create, delete, move (drag), resize, align, clone, collapsing, head/body/container sections, color, link, z-index.
@@ -253,6 +301,9 @@ pnpm test
253301
- **Clipboard:** cut, copy, paste across pages.
254302
- **Editing:** find-and-replace, undo/redo.
255303
- **Collab:** SyncedStore Yjs doc with `notes` and `arrows` maps, awareness, remote cursor positions.
304+
- **Templates:** default note / arrow from `users.encrypted_default_note` / `encrypted_default_arrow`.
305+
- **Backlinks:** incoming page links display.
306+
- **Group access:** password-protected group unlock flow.
256307

257308
3. **For each checklist item, specify:**
258309
- Legacy file(s) to reference.
@@ -261,7 +312,7 @@ pnpm test
261312

262313
**Verification:**
263314
- Review checklist with a human who has used the legacy app. Sign off on completeness.
264-
- Checklist must contain **at least 50 rows** (if it has fewer, the inventory is incomplete).
315+
- Checklist must contain **at least 60 rows** (if it has fewer, the inventory is incomplete).
265316

266317
**Exit criteria:**
267318
- [ ] `docs/SPATIAL_PARITY_CHECKLIST.md` exists and is reviewed.
@@ -328,7 +379,7 @@ The new `usePageCollabEditor` only syncs a ProseMirror `Y.XmlFragment`. We need
328379
2. **Page collab bootstrap**
329380
- `GET /api/pages/:pageId/collab-updates` already returns encrypted Yjs updates.
330381
- Verify that the server can persist and serve **page-level updates** (not just ProseMirror).
331-
- If the current `page_updates` table stores only ProseMirror diffs, extend the schema or add a separate `page_state_updates` table. **Decision required.**
382+
- **Decision required (see §0.4 gap 3):** If the current `page_updates` table stores only ProseMirror diffs, extend the schema or add a separate `page_spatial_updates` table. Document the compatibility strategy in `docs/COLLAB_DATA_MIGRATION.md`.
332383

333384
3. **SPA page document loader**
334385
- Replace `createPageCollabDoc()` (which creates a bare `Y.Doc`) with a function that loads the page structure from the server bootstrap and initializes `Y.Map`s for notes and arrows.
@@ -338,6 +389,16 @@ The new `usePageCollabEditor` only syncs a ProseMirror `Y.XmlFragment`. We need
338389
- Add `PAGE_DOC` message type for page-level Yjs updates (note positions, arrow creation, etc.).
339390
- Update `PageCollabRoom` DO to accept and relay `PAGE_DOC` updates.
340391

392+
5. **`page_updates` backward compatibility**
393+
- Existing rows contain ProseMirror-only encrypted Yjs updates. Page-level updates must not corrupt old rows.
394+
- **Option A:** Add `page_spatial_updates` table for page-level Yjs diffs; keep `page_updates` for ProseMirror-only legacy rows.
395+
- **Option B:** Embed a version byte in the encrypted payload or add a `formatVersion` column.
396+
- **Decision required before coding.** Document in `docs/COLLAB_DATA_MIGRATION.md`.
397+
398+
6. **DO architecture decision document**
399+
- Document why `PageCollabRoom` is a stateless relay (no in-memory Yjs doc) vs legacy's stateful `collab-server`.
400+
- If the DO should load the Yjs doc into memory for validation/size limits, include a spike in Phase 3.
401+
341402
**Verification:**
342403
- Unit test: create a `YPageDoc`, add a note, encode state, decode state, assert note position matches.
343404
- Integration test: two clients connect to `PageCollabRoom` via WS; client A creates a note; client B receives the update and the note appears in its Yjs doc within 2 seconds.
@@ -346,6 +407,8 @@ The new `usePageCollabEditor` only syncs a ProseMirror `Y.XmlFragment`. We need
346407
- [ ] `packages/collab-wire` can encode/decode a page-level Yjs update.
347408
- [ ] `PageCollabRoom` persists and relays page-level updates (not just ProseMirror).
348409
- [ ] Two browser tabs can sync note creation/deletion via WS (integration test or manual QA with sign-off).
410+
- [ ] `docs/COLLAB_DATA_MIGRATION.md` documents the `page_updates` compatibility strategy.
411+
- [ ] `docs/COLLAB_DO_ARCHITECTURE.md` documents stateless-relay trade-offs.
349412

350413
---
351414

@@ -418,6 +481,10 @@ The new `usePageCollabEditor` only syncs a ProseMirror `Y.XmlFragment`. We need
418481
- WS fallback to REST `POST /collab-updates` works.
419482
- Demo mode uses local-only Yjs (no WS, no REST push).
420483

484+
4. **Backlink display**
485+
- The backend exposes `POST /api/pages/:pageId/backlinks` and `DELETE /api/pages/:pageId/backlinks/:targetPageId`.
486+
- Add a backlinks card to `PageEditorView.vue` showing incoming links with decrypted titles.
487+
421488
**Verification:**
422489
- `page-editor-tiptap-extensions.test.ts` passes.
423490
- Manual QA: open a page in two tabs, type in both, verify text syncs within 1 second.
@@ -487,6 +554,17 @@ The new `usePageCollabEditor` only syncs a ProseMirror `Y.XmlFragment`. We need
487554
- When an arrow is created, it appears on remote clients within 1 second.
488555
- Remote cursor awareness shows which user is editing which note.
489556

557+
8. **DOM / world coordinate system**
558+
- Replicate legacy `space/pos.ts`, `space/rects.ts`, `space/sizes.ts` behavior:
559+
- `clientToWorld`, `worldToClient`, `screenToWorld`, `worldToScreen` transforms.
560+
- `getContainerWorldRect`, `getOriginWorldPos` for nested regions (containers).
561+
- Required for accurate drag, resize, arrow anchor placement, and fit-to-screen.
562+
563+
9. **Default note / arrow templates**
564+
- On creation, new notes must use the user's `encrypted_default_note` column (decrypted via session keyrings).
565+
- New arrows must use `encrypted_default_arrow`.
566+
- These set default colors, widths, head/body enabled states, and arrow styles.
567+
490568
**Verification:**
491569
- Unit tests for camera math (world ↔ screen transforms).
492570
- Unit tests for note model (read/write to Yjs doc).
@@ -583,6 +661,16 @@ The new `usePageCollabEditor` only syncs a ProseMirror `Y.XmlFragment`. We need
583661
- Recents, favorites, starting page, spatial defaults.
584662
- Search (if legacy had it).
585663

664+
5. **Group password unlock**
665+
- `unlockPageCollabSymmetricKeyring` currently throws for password-protected groups.
666+
- Implement group password UI and key derivation so users can unlock password-protected groups.
667+
- Add integration test for password-protected group join + page decrypt.
668+
669+
6. **Scheduler / background cleanup**
670+
- Legacy `apps/scheduler` ran scheduled cleanup (purge soft-deleted data).
671+
- Implement a Cloudflare Cron Trigger or Queue worker that calls `performScheduledCleanup` from `@deepnotes/session`.
672+
- Document in `docs/SCHEDULER.md`.
673+
586674
**Verification:**
587675
- E2E smoke test: register → create group → create page → invite member → member joins → both edit page → logout.
588676
- This smoke test must pass against a preview deployment or local compose stack.
@@ -641,25 +729,34 @@ The new `usePageCollabEditor` only syncs a ProseMirror `Y.XmlFragment`. We need
641729
| **Stripe-only after dropping RevenueCat** | Low | User churn | Communicate to IAP users before cutover. Offer migration grace period. |
642730
| **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. |
643731
| **God-object state returns** | Medium | Unmaintainable code | Cap composable size at 300 lines. If `useSpatialViewport.ts` grows beyond that, split into `useCamera`, `usePanning`, `useZooming`. |
732+
| **`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. |
733+
| **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. |
734+
| **i18n / SSR regressions** | Low | Accessibility, SEO, share-ability loss | Document as accepted v2 regressions or schedule recovery. |
735+
| **Group password not implemented** | Low | Users cannot access password-protected groups in new app | Add to Phase 8. If deferred, document v2 scope. |
736+
| **No scheduler = soft-deleted data accumulates** | Medium | DB bloat | Add Cron Trigger or Queue cleanup to Phase 8/9. |
644737

645738
---
646739

647740
## 8. Success criteria (revised — objective, verifiable)
648741

649742
A criterion is **not met** until the verification command or check passes in CI.
650743

651-
- [ ] **Test foundation:** `pnpm test` from repo root passes with 0 failures. `apps/web` tests mount `.vue` files and run in `happy-dom`.
744+
- [ ] **Test foundation:** `pnpm test` from `new-deepnotes/` root passes with 0 failures. `apps/web` tests mount `.vue` files and run in `happy-dom`.
745+
- [ ] **Composable size:** No SPA composable > 300 lines (`usePageCollabEditor.ts` split before spatial work).
652746
- [ ] **OpenAPI:** `GET /api/openapi.json` returns a valid OpenAPI 3 document. Client types are regenerated from it in CI.
653747
- [ ] **Drizzle:** `drizzle-kit migrate` applies cleanly from empty DB to current schema. `drizzle-kit check` passes in CI.
654748
- [ ] **Backend parity:** Every row in `docs/TRPC_REST_MAP.md` marked "implemented" has a passing automated test (unit or integration).
655749
- [ ] **Collab:** `PageCollabRoom` integration test: two clients sync note creation via WS within 2 seconds.
750+
- [ ] **Collab data migration:** `docs/COLLAB_DATA_MIGRATION.md` exists and explains how legacy `page_updates` rows remain compatible.
656751
- [ ] **Postgres tests:** Integration tests use template DB clones (§5.7). No test re-migrates from empty DB.
657752
- [ ] **Auth + crypto:** 2FA enable/disable flow tested end-to-end. Password change invalidates all sessions.
658753
- [ ] **No banned tech:** No tRPC, no `superjson`, no RevenueCat, no key rotation code paths. Enforced by ESLint `no-restricted-imports`.
659754
- [ ] **Spatial canvas (Phase 6):** User can create, move, resize, delete notes and arrows on an infinite canvas. Changes sync via WS.
660755
- [ ] **Spatial polish (Phase 7):** ≥ 80% of `docs/SPATIAL_PARITY_CHECKLIST.md` rows marked done.
756+
- [ ] **Backlinks:** SPA displays incoming page backlinks with decrypted titles.
661757
- [ ] **E2E smoke:** Playwright test covers register → create page → edit → invite → logout in < 60 seconds.
662758
- [ ] **Staging:** Hyperdrive + Postgres + Redis + WS proven in staging. Load test: 50 concurrent pages, p95 latency < 200 ms.
759+
- [ ] **Scheduler:** Cron Trigger or Queue cleanup job purges soft-deleted data periodically.
663760
- [ ] **Cutover:** 100 random legacy pages decrypt correctly. 24-hour canary error < 0.1%.
664761

665762
---

0 commit comments

Comments
 (0)