Mark records synced via bulk PATCH /api/records - #128
Merged
Conversation
Switch mark-synced off the per-uuid PATCH /api/records/[uuid] onto markpost's bulk PATCH /api/records, chunking to MAX_MARK_SYNCED_BATCH_SIZE (100) so a large first sync settles in ceil(N/100) requests instead of one per record. Closes #123
- Abort remaining chunks on a systemic (auth/rate-limit/5xx) failure, not just a timeout, to back off instead of hammering a doomed burst. - Guard outcomesFromResponse against a non-array data body (fall back to meta.updated) so an off-contract/proxied response can't crash or spuriously fail a chunk. - Reuse the exported MarkSyncedItem type; refresh stale outcome docs. - Tests: cross-chunk index alignment, per-record uuid/filePath/syncedAt pairing, empty/null/non-array data, systemic abort; extract a shared echo helper.
Collaborator
Author
Independent code review trailRan the independent reviewer (Opus, fresh context) as a loop; each round's findings were applied or skipped with a reason, then the diff was re-reviewed. The reviewer is an unbounded improver, so the loop was run to convergence on correctness/robustness/standards and stopped once only speculative enhancements remained. Rounds 1–2 — applied
Rounds 3–5 — applied
Skipped, with reasons (moved to the follow-up suggestions block on the PR)
All checks green after each round: |
5 tasks
grimicorn-agent
pushed a commit
that referenced
this pull request
Aug 28, 2026
…-synced Reconciles #138 (stop autoSync on a permanent mark-synced failure) with main's bulk chunked mark-synced (#128). Permanence is now a run-level MarkAbortReason ('permanent' stops the daemon; 'timeout' and transient-systemic keep it alive) surfaced by markSyncedChunk/markRecordsSynced, instead of a per-record outcome on the removed single-record path.
This was referenced Aug 28, 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 & why
sync's mark-synced step previously fired onePATCH /api/records/[uuid]per written record behind a concurrency-10 batcher whose own comment warned that a first sync of hundreds of records risks rate-limit/connection failures "exactly when the batch is biggest." This switches it to markpost's bulkPATCH /api/records, which takes arecords[]array up toMAX_UPDATE_BATCH_SIZE(100) per request.A large first sync now settles in
ceil(N / 100)requests (e.g. 250 records = 3 requests) instead of one request per record.Changes
src/libs/records.ts— replaced the single-recordmarkRecordSyncedwithmarkRecordsSynced(items, syncedAt?), which chunks toMAX_MARK_SYNCED_BATCH_SIZE(100, mirroring markpost's cap) and PATCHes each chunk as one bulk request. Per-record outcomes come from the responsedatacollection (a uuid present = synced, absent = still pending), giving real partial-failure detection.src/index.ts— the mark-synced path now calls the bulk function once; removed the now-unusedMARK_SYNCED_CONCURRENCYbatcher. The written→settled bookkeeping, deferred-record handling, and failure reporting are unchanged.Key decisions
data(verified againstserver/api/records/index.patch.ts). A uuid absent from the response stayspendingand is reportedMARK_FAILED(fail loud) so the user knows which files weren't marked, rather than a bare 2xx being trusted.datais ever not an array (a proxy/off-contract response the declared contract never produces), the per-uuid diff can't run, so it falls back to the corroboratingmeta.updatedcount; anything short of "all accepted" fails the chunk loud so records retry next run.Viewable
The behavior change is in
markpost sync(default sync, autoDelete off) — the "Marking records synced..." phase. No user-facing UI; verify via the CLI against a markpost account with pending records, or via the tests (tests/libs/records.test.ts,tests/index.test.ts).Closes #123
Follow-up suggestions
Abort mark-synced on a repeated non-transient 4xx— a 422 fromMAX_UPDATE_BATCH_SIZEdrift (or a malformed-payload 400) is not systemic, so every chunk fires and fails identically; abort after the first such 4xx since the payload shape is uniform across chunks (suggested: P3, effort: S, evidence: src/libs/records.ts markSyncedChunk)Distinct headline for a systemic mark-synced abort— a systemic abort returns a short outcomes array withtimedOut: false, so the report uses the generic "Failed to mark N" wording and never tells the user the run stopped early with records unattempted (suggested: P3, effort: S, evidence: src/index.ts markFailureHeadline)Stop the autoSync daemon on a permanent mark-synced failure— a permanent 403 on the PATCH leaves autoSync on, so the daemon re-fetches and re-attempts the same records forever; the delete path already guards this viadeletePermanentlyFailed(pre-existing, not introduced here) (suggested: P2, effort: M, evidence: src/index.ts runDefaultSync mark-synced branch)