Repository navigation
fix(google): record the Google id with the creation receipt, find a lost insert by its marker, backfill missing ids (Refs #875) - #879
Merged
Conversation
5 tasks done
alex-mextner
force-pushed
the
google-event-id
branch
from
October 28, 2026 21:07
a55e157 to
81c8d90
Compare
…a lost insert by its marker (Refs #875) A copy the bot created in Google could exist without the bot knowing its id: pushEvent and the participant paths called insertEvent and saved the id in a separate step, so a lost answer (timeout, restart while the job ran) left an unlinked copy and the queue's retry inserted a second one. The create receipt was written from the row read before the insert, so 28 of 29 production push/create sync_log rows had no google_event_id. - One creation path, SyncService.ensureCopy: look the copy up by the hypercalendarbot_event_id marker (every page, live events, no instance of a series, no copy any row already points at), adopt and update the oldest or insert; link and sync_log receipt (with the id) are one transaction, the adoption check runs again inside it. - An insert answer without an id is an error; the retry finds that copy. - The row decides, not the queued action: a linked row is updated on a create job, an unlinked one is created on an update job. - pino 'Google copy linked' (origin, duplicates) and sync_log details {via, duplicates} / participant_sync:adopted_by_marker. Tests: real SQLite and a stateful fake Google Calendar that can lose the answer after creating the event (test/services/google/google-id-at-creation.test.ts), paging of the lookup (calendar-api-bot-copies.test.ts); the older fake APIs gain findBotEventCopies. Known gap, tracked as #877: a delete between the lost answer and the retry leaves the copy in Google. Lint: lint-check skipped for this worktree (legacy cognitive-complexity baseline in sync-service.ts, rig-cli #782): 5 findings before, the same 5 after, none in the new code.
…fter a verified backup (Refs #875) Rows whose Google copy exists but whose id was never stored (a lost insert answer, a link a disconnect cleared) can now be relinked. scripts/backfill-google-event-ids.ts lists the user's primary calendar and matches by the hypercalendarbot_event_id marker (written on apply) and, as a report only unless --include-slot-matches, by (summary, start, end) with an unmarked event; only when the match is the single candidate on both sides, across users sharing one calendar too. Dry run (default) opens the database read-only and prints counts only; --apply takes a verified VACUUM INTO backup (0600, never overwritten), refuses if any user's Google read failed, writes only google_event_id plus one sync_log row per link, never writes to Google. Idempotent. Tests: test/services/google/google-id-backfill.test.ts (19, real SQLite, mutation-checked). Runbook section "Google event id backfill", spec paragraph. Lint: lint-check skipped for this worktree (legacy baseline in sync-service.ts, rig-cli #782); the files of this commit add no finding.
…ng (Refs #875) type-bans blocks Record<string, unknown>; the dump is now typed as string | number | null columns.
… survives a mocked calendar-api (Refs #875) test/services/google/sync-queue.test.ts replaces calendar-api.ts with mock.module for the whole run, so the GoogleCalendarApi paging test failed in the full suite (CI) while passing alone. The paging and the query now live in bot-event-copies.ts; GoogleCalendarApi.findBotEventCopies delegates to it. Also formats an import line Biome wanted wrapped after the rebase.
alex-mextner
force-pushed
the
google-event-id
branch
from
October 28, 2026 22:13
a952d6a to
89a5830
Compare
3 tasks
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 changed
SyncService.ensureCopyrecords the Google id of a copy the bot creates together with thesync_logcreate receipt in one transaction, and finds a copy whose insert answer was lost by itshypercalendarbot_event_idmarker (all pages, no series instance, no copy a row already points at) instead of inserting a second one; a linked row is updated on a create job, an unlinked row created on an update job; an answer without an id is an error.scripts/backfill-google-event-ids.ts(dry run by default,--applyafter a verified backup, onlygoogle_event_id, never writes to Google); runbook and spec.Root cause and evidence (read-only prod counts, 2026-10-28)
28 of 29
push/createsync_log rows had nogoogle_event_id(26 linkable); a lost answer left an unlinked copy and the BullMQ retry inserted a second one. Rows of the one connected user are consistent (278 synced, all with an id). Google-side counts come from the dry run after deploy.Checks
bun test test/services/google/google-id-at-creation.test.ts18 pass;google-id-backfill.test.ts19 pass; tsc exit 0 (server).VACUUM INTO"disk I/O error" on the server's tmpfs, passes locally).src/services/google/sync-service.ts, rig-cli feat(ai): Claude Sonnet 5.5 / Haiku 5.5 as the reserve after z.ai (#781) #782; 5 findings before, the same 5 after, none in new code. No other gate skipped.Refs #875. Related: #877 (delete between a lost answer and its retry), #876.