Skip to content

feat(tomesync): builds 32+33 — clock-offset guard, pull strategy, state file, device search, author axis, status write-back#122

Merged
bndct-devops merged 3 commits into
mainfrom
feat/tomesync-catalog
Jul 5, 2026
Merged

feat(tomesync): builds 32+33 — clock-offset guard, pull strategy, state file, device search, author axis, status write-back#122
bndct-devops merged 3 commits into
mainfrom
feat/tomesync-catalog

Conversation

@bndct-devops

Copy link
Copy Markdown
Owner

Two sequential plugin builds from the teardown adoption arc, stacked because plugin builds are monotonic.

Build 32 — hygiene batch (b18aa3d)

  • Clock-offset guard (the silent highlight-swallowing class): web create/edit/delete stamp LWW keys with the server clock; on a device whose clock runs behind, those stamps sat in the device's future and outranked every later local change — a re-highlight after a web delete just vanished until the clocks crossed. The plugin now stamps device_time on annotation syncs; the server marks the stamps it minted itself (server_minted, additive columns) and shifts exactly those into the requesting device's frame — in comparisons and responses. Device side scrubs/clamps future stamps. Cross-device skew stays the documented accepted edge; old plugins keep prior behavior.
  • Pull-conflict strategy: "Server position is ahead / behind" settings, each prompt / silent / never (defaults preserve historic behavior). The ask-dialog is deferred off the open path so it can't eat a Profiles auto-exec — the old layout-reset class.
  • Dedicated state file: the seven data tables move out of G_reader_settings (parsed by KOReader every boot, growing with the library) into tomesync_state.lua, with a crash-safe migration and pruning for books no longer on disk. tomesync_update stays global — the frozen shim reads it.

Build 33 — catalog batch (92f564c)

  • Search from the device: submit-based input + recent searches; GET /tome-sync/search (LIKE terms over title/author/series, visibility-gated, capped at 50 with true total).
  • Author browse axis: the natural way into standalones (previously one flat "No Series" bucket). Mixed lists file each download by the book's own identity.
  • Read-status write-back: hold any book row → unread/reading/read via PUT /tome-sync/status/{id}; lists show status markers next to the on-device marker.
  • Series N+1 fix: /tome-sync/series ran one first-book query per series; now a single ordered query, same response shape.

Verification

  • 877 tests green (10 clock-offset + 12 impl-contract + 9 catalog-endpoint tests new); luajit compile check in CI.
  • Emulator round-trips against the showcase for both builds: real pre-existing state migrated (6 keys moved, dead paths pruned), settings submenus render with correct defaults, annotation sync round-trips with device_time/server_time, search/authors/status-dialog exercised end-to-end (status PUT verified in the DB).
  • Caveat: emulator runs were plain-HTTP localhost; neither build touches transport/URL code, but no proxied-HTTPS pass was done.

bndct-devops and others added 2 commits July 5, 2026 21:37
…ate file (build 32)

Tier-3 of the teardown adoption arc: the three S-sized correctness/hygiene
gaps, as one plugin build.

Clock-offset guard (the silent highlight-swallowing class):
- Web create/edit/delete stamp LWW keys with the SERVER clock; on a device
  whose clock runs behind, those stamps sit in the device's future and
  outrank every later local change (a re-highlight after a web delete just
  vanished until the clocks crossed).
- The plugin now stamps device_time on annotation syncs; the server marks
  stamps it minted itself (server_minted, additive columns on annotations +
  annotation_tombstones) and shifts exactly those into the requesting
  device's clock frame — in the tombstone/LWW comparisons and in the
  response. Device-minted stamps pass through verbatim (cross-device skew
  stays the documented accepted edge). A device upsert winning a row clears
  the flag; bumped web-edit stamps inherit the frame they were bumped from.
- Device side: future stamps are scrubbed from local annotations + baseline
  before diffing (annotation and baseline clamped to the same value, so no
  spurious re-push), and incoming stamps are clamped to the device clock
  before compare/store. Old plugins keep the previous behavior.

Pull-conflict strategy (kosync muscle memory):
- "Server position is ahead" / "Server position is behind" settings, each
  prompt / silent / never. Defaults preserve historic behavior (forward
  silent, backward never). The prompt is deferred 1.5s off the open path —
  a ConfirmBox at open time would eat the Profiles auto-exec dispatch
  exactly like the InfoMessage layout-reset bug. Jump mechanics extracted
  to _gotoServerPosition (shared by silent + prompt, xpointer-shape guard
  intact).

Dedicated state file:
- The seven data tables (book_map, pending_sessions, adopt_pending,
  repair_map, annot_baseline, rating_baseline, pending_ratings) move out of
  G_reader_settings — which KOReader parses at every boot and which these
  tables bloat unboundedly — into settings/tomesync_state.lua (write-through
  LuaSettings). Migration is crash-safe (new file flushed before old keys
  deleted; marker branch re-deletes leftovers). Per-book state is pruned for
  books no longer on disk; pending queues are never pruned (owed to the
  server); baseline pruning is delete-safe by construction. tomesync_update
  stays in G_reader_settings — the frozen shim reads it.

Verification: 868 tests green (10 new server clock-offset tests, 12 new
impl contract tests; two legacy contract tests updated to the new storage
location). Emulator round-trip on the showcase: real pre-existing state
migrated (6 keys moved, global file cleaned, dead paths pruned), both
settings submenus render with correct defaults, annotation sync round-trips
200 with device_time/server_time. luajit-compile check in CI via
test_impl_compiles_under_luajit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ite-back (build 33)

The three M-sized catalog gaps from the teardown, as one plugin build.

Search from the device:
- "Search library…" atop the series browser: submit-based InputDialog (the
  right call on e-ink) with the last searches one tap away; results land in
  the shared drill-down list. Server: GET /tome-sync/search?q= — LIKE terms
  over title/author/series (all must match), visibility-gated, capped at 50
  with the true total reported.

Author browse axis:
- "Browse by author" — the natural way into standalone books, which the
  series browser lumps into one No Series bucket. GET /tome-sync/authors
  (+ __unknown__ bucket) and /tome-sync/author-books?author= (query param:
  author names contain slashes). Mixed lists prefix rows with each book's
  own series and file downloads by the book's own identity — a book with a
  series lands under it, standalones under book-type/author, same as the
  No Series flow.

Read-status write-back:
- Hold any book row (series/author/search list) → unread/reading/read via
  PUT /tome-sync/status/{book_id}. A deliberate user action, so it writes
  status directly — unlike telemetry, which only ever suggests status via
  the sticky rule. Lists show "· reading/read" markers next to the existing
  on-device marker (book entries now carry per-user status; additive).

Plus the series N+1 fix: /tome-sync/series ran one first-book query per
series; now a single ordered query with an unchanged response shape. The
volume-list serializer is shared (_book_entry) across series/author/search.

Verification: 877 tests green (9 new endpoint tests incl. visibility +
status upsert + the 50-cap). Emulator round-trip on the showcase: search
"good guys" → 16 series-prefixed results with real status markers; status
dialog PUT → 200 and the DB row flips; authors menu renders with correct
counts. luajit compile check green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@bndct-devops

Copy link
Copy Markdown
Owner Author

Closed pending hands-on testing — opened prematurely; will reopen once the branch is verified on dev/emulator.

@bndct-devops bndct-devops reopened this Jul 5, 2026
@bndct-devops
bndct-devops merged commit fa73509 into main Jul 5, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant