# Checkpoints and deletions Keep these three positions separate: | Position | Owner | Purpose | | ----------------- | ------------ | --------------------------------------- | | Execution journal | Browserlayer | Resume completed steps within a run | | Source checkpoint | Your sync | Continue fetching the website's dataset | | Consumer cursor | Your app | Continue applying published changes | ## Incremental publication Inside a sync with a checkpoint schema such as `z.object({ next: z.string() })`: ```ts const checkpoint = await ctx.getCheckpoint(); // null on the first run // Fetch the next page through your connector using checkpoint?.next. await ctx.publish("page", records, { checkpoint: { next: nextPageToken }, deleted: deletedIds, }); ``` `records`, `nextPageToken` and `deletedIds` come from the upstream page you fetched. Publication commits records, checkpoint and notification intent atomically. ## Full snapshots ```ts await ctx.publish("snapshot", records, { checkpoint: null, scan, completeScan: true, }); ``` Use one unique scan ID for every page in a snapshot. Generate it in a durable step. Only mark the final successful page `completeScan: true`. | Case | Result | | ------------------------------------- | ----------------------------------------------------------------- | | Complete snapshot omits an old record | Publish a deletion | | Complete snapshot is empty | Delete all old records and publish an update notification | | Partial scan fails | Already published batches remain; missing records are not deleted | | Scan continues across runs | Carry its scan ID in the checkpoint | Model changes need a new sync identity or an explicit development dataset reset. Renaming a sync starts a different stream.