Skip to content

feat(sync): manifest-driven board PULL with bulk body fetch#2214

Merged
tomivm merged 4 commits into
masterfrom
feat/sync-manifest-pull
Jun 5, 2026
Merged

feat(sync): manifest-driven board PULL with bulk body fetch#2214
tomivm merged 4 commits into
masterfrom
feat/sync-manifest-pull

Conversation

@RodriSanchez1

@RodriSanchez1 RodriSanchez1 commented Jun 4, 2026

Copy link
Copy Markdown
Collaborator

What

Replaces the board sync PULL phase's full-list fetch with a lightweight sync manifest + a single bulk body fetch.

This is the frontend half of #2213. The matching backend endpoints (GET /board/sync/:email, POST /board/byids) ship in a separate cboard-api PR referencing the same issue.

Why

PULL previously called API.getMyBoards({ limit: 500 }) on every sync, re-downloading all board bodies even when nothing changed, and verified each server-side deletion with its own getBoard() request.

The hard-coded limit: 500 also silently broke accounts with more than 500 boards — boards past the 500th were never returned, so they never synced.

How

  • getApiMyBoards() now pulls the { id, lastEdited } manifest via getBoardsSync() (complete, unpaged — no 500 cap).
  • applyRemoteChangesToState() is now single-path:
    • Deletions — a board absent from the manifest is deleted locally directly; the manifest is authoritative for existence, so the per-id getBoard() 404-verification is removed.
    • Adds/updates — full bodies for all new/changed boards are fetched in one getBoardsByIds() request (POST /board/byids, so a large id list can't exceed URL limits MAX 3000) and applied from an in-memory map.
    • Concurrent-edit guard preserved — a board the user marks PENDING mid-fetch is not overwritten.
    • Failure handling — on a bulk-fetch failure the engine logs and returns without throwing: manifest-driven deletions stay applied, adds/updates defer to the next sync, and PUSH still runs.

Net effect: steady-state syncs transfer only deltas; a full-body download happens only implicitly on a fresh device / first login; accounts with >500 boards sync fully.

Design dependency

Freshness is detected solely via lastEdited, so every server-side board mutation must bump it. Documented in docs/sync-engine.md.

Commits

  • feat(api)getBoardsSync + getBoardsByIds (+ mocks)
  • refactor(sync) — manifest-driven PULL with one bulk body fetch
  • test(sync) — manifest-trust deletions + bulk-fetch failure path
  • docs(sync) — update sync-engine.md

Testing

Board.actions.test.js — 93/93 pass.

Close #2213

RodriSanchez1 and others added 4 commits June 3, 2026 21:39
Add two API methods backing the new manifest-driven sync:

- getBoardsSync(): GET /board/sync/:email — returns the complete,
  unpaged { id, lastEdited } manifest. Authoritative for board
  existence and not subject to the old 500-board page cap.
- getBoardsByIds(ids): POST /board/byids — fetches the full bodies of
  a specific set of boards in one request. POST (not GET) so a large
  id list on a fresh device can't exceed URL-length limits.

Mirror both in the api mock so the bulk PULL path is testable.

Refs #2213

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…fetch

getApiMyBoards() now pulls the lightweight { id, lastEdited } manifest
via getBoardsSync() instead of re-downloading up to 500 full board
bodies on every cycle (which also silently dropped boards past the
500th on large accounts).

applyRemoteChangesToState() becomes a single path:
- Deletions: a board absent from the manifest is deleted locally
  directly. The manifest is authoritative for existence, so the old
  per-id getBoard() 404-verification is gone.
- Adds/updates: full bodies for every new/changed board are fetched in
  one getBoardsByIds() request and applied from an in-memory map — no
  per-id request storm and no body-count threshold.
- Concurrent-edit guard preserved: a board the user marked PENDING
  during the fetch is not overwritten.
- Failure handling: on a bulk-fetch failure we log and return without
  throwing — manifest-driven deletions stay applied, adds/updates defer
  to the next sync, and PUSH still runs.

Refs #2213

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Update the PULL tests for the new semantics:
- Adds/updates fetch bodies via a single getBoardsByIds() call.
- Boards absent from the manifest are deleted without any getBoard()
  verification, even when the server would still return them.
- New test: a failed bulk body fetch still applies deletions, defers
  adds/updates, and does not throw.

Refs #2213

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Update sync-engine.md to match the new engine:
- Sync manifest glossary term and the lastEdited freshness contract.
- Rewrite applyRemoteChangesToState (manifest-trust deletions, single
  getBoardsByIds fetch, failure handling) and the flow diagram.
- Correct the lifecycle section: sync runs on app start, tab focus,
  reconnect, resume, and manual sync (2-min throttle), not only login;
  no routine sync re-downloads all bodies.
- Refresh line numbers, error-handling table, and file reference.

Refs #2213

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@tomivm tomivm merged commit 225dfac into master Jun 5, 2026
5 checks passed
@tomivm tomivm deleted the feat/sync-manifest-pull branch June 5, 2026 17:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Board sync: replace full-list PULL with a lightweight sync manifest + bulk body fetch

2 participants