diff --git a/README.md b/README.md index 88e58b0..4c38752 100644 --- a/README.md +++ b/README.md @@ -266,7 +266,7 @@ The application is stable and production-ready (see **`package.json`** → `vers - ✅ **Provider Pattern:** Multi-booru support via `IBooruProvider` interface (Rule34, Gelbooru) - ✅ **Rate Limiting:** Intelligent rate limiting with 1.5s delay between artists, 0.5s between pages - ✅ **Anti-Bot Measures:** Shared throttling/UA strategy applied across current providers. -- ⚠️ **Open P0:** sync `lastPostId` cursor integrity — see [Roadmap](./docs/roadmap.md#open-p0-audit--remaining). Video-cache writes are atomic (tmp+rename) with size-capped eviction. +- ✅ **Sync integrity:** `lastPostId` advances only after complete pagination; unfinished runs set `lastSyncIncomplete`. Video-cache writes are atomic (tmp+rename) with size-capped eviction — see [Roadmap](./docs/roadmap.md). ### UI/UX diff --git a/docs/api-guide.md b/docs/api-guide.md index 9143da0..227f0b4 100644 --- a/docs/api-guide.md +++ b/docs/api-guide.md @@ -471,6 +471,7 @@ type Artist = { apiEndpoint: string; lastPostId: number; newPostsCount: number; + lastSyncIncomplete: boolean; lastChecked: number | null; createdAt: number; }; diff --git a/docs/architecture.md b/docs/architecture.md index 2eb66e1..5055b4c 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -1170,9 +1170,7 @@ sequenceDiagram }); ``` - g. **Updates artist** - Updates artist's `lastPostId` and `newPostsCount`. - - ⚠️ **Known integrity gap (open P0):** `lastPostId` may advance mid-pagination or after a partial error commit. An interrupted sync can leave a cursor ahead of fully persisted posts and skip gaps on the next run. Do not document “cursor = fully synced watermark” until the sync-cursor integrity fix lands. + g. **Updates artist** - Mid-batch and error paths update `newPostsCount` (and may set `lastSyncIncomplete`) without moving `lastPostId`. After natural pagination end (`postsData.length < PAGE_SIZE`), a single commit writes `lastPostId`, `lastChecked`, and clears `lastSyncIncomplete`. h. **Progress event** - Emits IPC event: `emit('sync:progress', 'Syncing artist_name...')` diff --git a/docs/database.md b/docs/database.md index 34d37ce..5b8bd05 100644 --- a/docs/database.md +++ b/docs/database.md @@ -80,6 +80,9 @@ export const artists = sqliteTable( apiEndpoint: text("api_endpoint").notNull(), lastPostId: integer("last_post_id").default(0).notNull(), newPostsCount: integer("new_posts_count").default(0).notNull(), + lastSyncIncomplete: integer("last_sync_incomplete", { mode: "boolean" }) + .notNull() + .default(false), lastChecked: integer("last_checked", { mode: "timestamp" }), createdAt: integer("created_at", { mode: "timestamp" }) .notNull() diff --git a/docs/glossary.md b/docs/glossary.md index c8836f3..603a5e1 100644 --- a/docs/glossary.md +++ b/docs/glossary.md @@ -154,9 +154,9 @@ Main-process `VideoProxyServer` serves local `http://127.0.0.1` URLs for `