Repository navigation
fix: complete + deterministic dedup candidate set on sync (GIT-70) - #22
Merged
Merged
Conversation
The last item before any tag conversation exists. session_close now awaits this sync (GIT-69 item 3), so the window runs synchronously on the close path. The defect: an unordered `limit: 200` single page feeding the text-dedup map, against a table already at 256 rows. Every thread still synced — the per-thread lookup is an exact thread_id match, unbounded by the window — so what truncated was DEDUP COVERAGE. A text-matching row outside the loaded 200 meant the map missed it and a DUPLICATE row was created, silently, with the dedup log never firing. A write that fires because a read was incomplete. Two changes: 1. Pagination via directQueryAll, which already existed — checked before building, same as the harness. The candidate set is now actually complete rather than an arbitrary slice. 2. A deterministic total order (thread_id.asc). Required, not cosmetic: PostgREST Range pagination over an unordered result set can repeat and skip rows between pages, so without it the pagination would introduce the very gaps it was added to close. thread_id is unique, so it orders totally on its own. directQueryAll stops at its maxRows cap WITHOUT saying so. Inheriting that silence would rebuild the defect one layer up, so the cap is detected and reported: dedup_coverage is "complete" | "partial" | "unavailable", with dedup_candidates so the claim is auditable. Coverage is deliberately a separate axis from all_synced, and session_close surfaces it as a warning rather than a failure. Every thread syncs by identity regardless; incomplete coverage means a duplicate MAY exist — which all_synced structurally cannot detect, because writing a duplicate IS a successful write. That distinction is the whole ticket: the outcome contract makes the path honest about failures, not about correctness. Identity matching at the exact-thread_id lookup is untouched, per R8a. Tests: +5 (deterministic order asserted at the call; complete coverage past the old 200 window; cap reported as partial not absorbed; load failure reported as unavailable; sync still succeeds while coverage is incomplete). Also updated the 4 existing sync tests whose mocks assumed directQuery for the candidate load — they caught the change correctly. 1093 unit + 12 e2e green, typecheck and build clean. Refs GIT-70, GIT-69, GIT-63 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
nTEG-dev
marked this pull request as ready for review
August 7, 2026 02:26
nTEG-dev
added a commit
that referenced
this pull request
Aug 7, 2026
GIT-74 token audit + GIT-76 field-drop fix (draft, stacked on #22)
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.
Draft — verification only. Do not merge. Base is
feature/GIT-51-session-recovery(stack tip), notmain.Third in the stack: PR #20 (GIT-69 + GIT-67/63) → PR #21 (GIT-51) → this. Based on the stack tip so the diff shows only the GIT-70 delta. Retarget as the stack merges.
The defect
An unordered
limit: 200single page feeding the text-dedup map insyncThreadsToSupabase, against a table already at 256 rows.Every thread still synced — the per-thread lookup is an exact
thread_idmatch, unbounded by the window. What truncated was dedup coverage. A text-matching row outside the loaded 200 meant the map missed it and a duplicate row was created, silently, with the dedup log never firing.A write that fires because a read was incomplete. And because there was no
ORDER BY, which 200 rows loaded was arbitrary — so whether a given close duplicated was nondeterministic between identical runs.session_closenow awaits this sync (GIT-69 item 3), so it runs synchronously on the close path.Fix
directQueryAll, which already existed — checked before building, same as the harness.thread_id.asc). Required, not cosmetic: PostgREST Range pagination over an unordered result set can repeat and skip rows between pages, so without it the pagination would introduce the very gaps it was added to close.thread_idis unique, so it orders totally on its own.directQueryAllstops at itsmaxRowscap without saying so. Inheriting that silence would rebuild the defect one layer up, so the cap is detected and reported.Why coverage is a separate axis from success
dedup_coverage: "complete" | "partial" | "unavailable", withdedup_candidatesso the claim is auditable.session_closesurfaces it as a warning, not a failure.Every thread syncs by identity regardless of coverage. Incomplete coverage means a duplicate may exist — which
all_syncedstructurally cannot detect, because from the sync's perspective writing a duplicate is a successful write.The gate makes close honest about failures. This makes it honest about correctness.
Identity matching at the exact-
thread_idlookup is untouched, per R8a.Verification
npm run typechecknpm run buildnpm run test:unitNew coverage (+5): deterministic order asserted at the call site; complete coverage past the old 200 window; cap reported as partial rather than absorbed; load failure reported as unavailable; sync still succeeds while coverage is incomplete. Four existing sync tests were updated — their mocks assumed
directQueryfor the candidate load and caught the change correctly.Refs GIT-70, GIT-69, GIT-63