feat(sync): manifest-driven board PULL with bulk body fetch#2214
Merged
Conversation
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>
This was referenced Jun 4, 2026
Closed
tomivm
approved these changes
Jun 5, 2026
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.
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 owngetBoard()request.The hard-coded
limit: 500also 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 viagetBoardsSync()(complete, unpaged — no 500 cap).applyRemoteChangesToState()is now single-path:getBoard()404-verification is removed.getBoardsByIds()request (POST/board/byids, so a large id list can't exceed URL limits MAX 3000) and applied from an in-memory map.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 indocs/sync-engine.md.Commits
feat(api)—getBoardsSync+getBoardsByIds(+ mocks)refactor(sync)— manifest-driven PULL with one bulk body fetchtest(sync)— manifest-trust deletions + bulk-fetch failure pathdocs(sync)— updatesync-engine.mdTesting
Board.actions.test.js— 93/93 pass.Close #2213