Repository navigation
## v1.24.0 (2026-10-02)
v1.24.0 (2026-10-02)
The parity program's first release: Phases 14 and 15 land
The 2026-09-29 reopen (roadmap.md) committed five parity phases; this release ships the first two, the completion wave and the maintenance ring, plus the Dependabot bookkeeping. The Cross-Repo Rule consumers affected this round are CalibreQuarry (adopts get_dirtied_formats() in its Phase 20 lane) and Carrel-calibre-web (the residue trio; adoption waived this round per the decision record).
- Restore-from-trash verbs (
cquarry.write): the trash stops being a dead end.copy_format_from_trash/copy_book_from_trashrescue entries out of.caltrash(pure filesystem),move_format_from_trashundeletes one format into its live book,delete_trash_entryremoves one entry, andmove_book_from_trashrebuilds the row from the entry's sidecarmetadata.opf(Calibre's own backup, read with stdlib ElementTree and never generated, keeping the OPF-generation decline intact): core metadata restored, every extension-bearing file re-registered indata, the cover flag from the file, OPF resync and FTS extraction queued, and the directory moved back only after the rows commit. Custom columns, annotations, and plugin data are not restored (the OPF carries none of them). get_dirtied_formats()(read side): the FTS sidecar's extraction queue as sorted(book_id, format)pairs, the formats sibling ofget_dirtied_books(). Retires CalibreQuarry's raw sidecar read when its lane adopts it.set_custom_column_metadata(write side): name/editable/display edits on an existing column, upstreambackend.py:1407's shape. The headline use:display={"enum_values": [...]}populates an enumeration without Calibre open. Honest no-op on equal values; a change setsupdate_all_last_mod_dates_on_startlike upstream's wrapper; datatype and label changes are refused by omission (type is baked into the storage layout; the label is the#labelsearch token).- The Carrel residue trio (read side):
get_book_by_uuid(the Calibre-Companion lookup; case-insensitive, None on miss),get_entity_book_ids(kind, name)(the id-set half ofget_entities; tags resolve the engine's anchored-subtree rule so a browse node's set covers its children), andget_all_formats()(a cached{book_id: [FMT]}map). facet_counts/facet_counts_for_ids(read side): per-value counts over a restricted result set (the builtin browse fields plus custom columns by#label; ratings surfaced as stars exactly asfield()yields them). The_for_idshalf is the restriction plumbing Phase 18's restricted tag browser reuses.WritableCalibreDB.maintain(): vacuum, analyze, and integrity_check over the library DB and the attached FTS sidecar in one standalone pass (upstreambackend.pyvacuumextended with the two statements calibredb users run by hand; the notes DB stays out of scope). Refusesbatch()and mid-transaction states; reports the check rows (["ok"]on a healthy file).check_library_disk()(cquarry.integrity): the extra-side disk checks of upstream'scheck_libraryin one tree walk: extra titles/authors, malformed paths and formats, extra formats, unknown files, extra covers, failed folders. The missing side is deliberately not re-answered (find_missing_format_files/find_missing_cover_filesown it). The format-vs-unknown-file classifier is structural (a token extension minus the image/OPF/junk set), not upstream's curatedBOOK_EXTENSIONS; POSIX case sensitivity assumed.- FTS queue verbs (
cquarry.write):fts_reindex_book,fts_reindex_all, andfts_queue_clearare the queue half of upstream'sfts_unindexfamily as pure sidecar SQL. One boundary named rather than faked: upstream's index-row deletion runsbooks_textdelete triggers that tokenize through Calibre's custom FTS5 tokenizer, which does not exist outside Calibre, so removing indexed text stays Calibre's job (an O-lanecalibredb fts_unindexwrap if a consumer asks). - Typed preference writes (
cquarry.write):set_preference(key, value)over a five-key whitelist (saved_searches,virtual_libraries,user_categories,grouped_search_terms,fts_enabled), each payload validated by key before anything lands and stored as one JSON row in Calibre's shape;saved_search_add/saved_search_delete/saved_search_renameare the calibredbsaved_searchesparity item (rename resolves case-insensitively to the stored spelling and refuses to overwrite an existing name where upstream silently overwrites). - Housekeeping: the Dependabot gate ticked (both major-bump PRs merged 2026-09-30), the import skills swept per the skill-sync rule, and an API.md table row the
maintain()insert had split was repaired in the same window. - Suite: 500 -> 556 tests (pytest; 550 collected by CI's unittest discover).