Release / build-and-push (push) Successful in 2m14s
Progress, highlights, notes, and bookmarks entered position conversion through three different doors: progress converted inline with an uncached converter, annotations through the facade, bookmarks not at all (the raw xpointer was stored verbatim, cfi_position stayed empty, and the web drawer's goToBookmark silently no-ops on cfi-less entries). Unify on the facade (ConvertToCanonical/ConvertFromCanonical): - annotationEpub context resolved once per push: media item + EPUB path shared by every annotation instead of re-fetched per entry - progress forward: the inline block becomes one facade call; non-reflowable formats pass through unchanged, and the cached converter stops re-parsing the book on every sync - progress reverse: convertCFIToXPointer delegates to reverseConvertCFI, keeping the stored percentage in play for the fallback ladder - bookmarks (bulk progress and /sync-bookmarks): pos0 resolves structural-only — bookmark text is a display label, never book text, so no context is supplied; webUsableCFI stores the result only for structural/exact epubcfi landings, discarding href/percentage results rather than storing dead drawer links. Also records percentage_location and origin_source on the legacy endpoint. - percentages thread through: highlights/notes/bookmarks pass the device percentage or the derived section percentage instead of a hardcoded 0, so the last-resort fallback lands near the true position instead of the document start - extendCFIByLength end-derivation now also fires on structural starts (it had silently stopped matching when the structural rung began landing starts with precision 'structural' rather than 'exact') Tests: the drop-cap xpointer through the facade with empty context (the bookmark scenario) must land structurally, not doc-start; webUsableCFI table covers the store/discard gate.