35 Commits
Author SHA1 Message Date
funkpower 05b712d084 0.2.2 — a subtitle begins with a capital
title_case counted small words from the start of the whole string, so
every subtitle that opened with one was set in lower case: 'Aesthetic in
Kant: a Critique', 'Born Fighting: the Scots-Irish'. A colon starts a
title of its own and the first word of one is capitalised however small
it is.

Found by book-pipeline, which had been proposing a title page's capitals
verbatim into a catalogue and needed this to stop.
2026-09-21 11:30:26 -04:00
funkpower 60943420b4 0.2.1 — the holder goes between the symbol and the year
'(c) Alfredo Ferrarin 2004' was read as a title. The notice pattern
wanted the year to follow the symbol directly, and a copyright line puts
the holder's name in between, which is the ordinary way to set one.
2026-09-21 11:19:54 -04:00
funkpower 3b1fbb73ce 0.2.0
The Python surface gained enough this week that three different builds
were installed on two machines all calling themselves 0.1.0: moria had
read_colophon through merge_colophon, mirkwood had a partial from the
middle of the afternoon, and PyPI had neither. A version that does not
move is a version nobody can depend on, which is how book-pipeline ended
up running a wheel scp'd into place by hand.

New since 0.1.0: colophon --shelf and catena.shelve, which file a book's
printed Library of Congress headings onto a closed list; catena
.read_colophon; catena.fetch_identifier, the registers in process; and
catena.colophon_prompt / merge_colophon, the question about what the
pages did not settle and the reply folded back in.
2026-09-21 11:10:13 -04:00
funkpower d76cee8abe fmt
check.sh runs cargo fmt --check and three of my commits did not.
2026-09-21 10:26:15 -04:00
funkpower ba8ec81735 colophon: the record a book prints in its own front matter
A catalogue's worst records are the ones whose file says nothing true about
itself: a PDF titled `Untitled` by an author called `Oxford`, which is a
publisher, from a filename that was never anything but a filename. What
those books do have is pages. The title page carries the title as the
publisher set it; the copyright page carries the imprint, the year, the
ISBN, and in most English books since the 1970s the cataloguing block, which
is a catalogue record a librarian already made, subject headings and all.

So this reads the pages. Every field names the page it came from, and a book
that printed nothing comes back empty with its fields in `unsettled` rather
than filled with something plausible — an extraction that starts at chapter
one has no front matter in it, and saying so is the answer.

Two pages look like a title page and are not, and both are how a catalogue
comes to name the wrong book. A series advertisement lists other titles in
the shape a title page uses, over as many as four pages with only the first
announcing itself. A translation's cataloguing block prints the original
title in brackets above the real one. Both are found and refused.

Three things the pages give that no file does: the subject headings a
librarian assigned, an ISBN that exists only in print — which turns a book
with no identifier into one `bibfetch` can look up — and a title in the case
the publisher meant, because a half title prints unbroken what the title
page breaks across two lines and sets in capitals.

`--prompt` and `--merge` are citefind's: a question about exactly the fields
the pages left unsettled, composed here and answered wherever the caller
keeps a model, with the advertising taken out of what the model is shown.
A field a model answered is marked `model`, so a record always says which of
it was read and which was inferred.

Measured against 600 books of the home corpus: the cataloguing block
answered for 21% of them and agreed with the catalogue 89% of the time, most
of the residue being the catalogue's error rather than this one's. Title
pages answered for another 41%. The remaining 38% print nothing readable,
and are what the prompt is for.
2026-09-21 09:28:26 -04:00
funkpower c4e1fb9c02 drop the parity harness, keep what it argued
The harness compared these tools against the code they replaced — Python
imported from book-pipeline, podcast-pipeline and knowledge-mcp, and
JavaScript copied out of the podcast media server and indexer, each pinned to
a commit. All of it has since been deleted, so the live half cannot run and
the golden half pins answers against a source nobody can consult.

What was worth keeping was never the comparison. `KNOWN.md` argued, at
length, why a dozen of these tools deliberately do something a reader would
not expect: why pdfgutter measures the crop box throughout, why avplan finds
the encoder rather than reading one out of the environment, why mdscrub will
not manufacture a page boundary. Those arguments are about this code and
stand on their own, so they are now `docs/decisions.md`, rewritten to say
what each tool does and why rather than what it does differently.

Binding coverage is unaffected: the bindings carry 24 tests of their own, a
node smoke test against the built package, and a typecheck.
2026-09-21 07:25:10 -04:00
funkpower 7a08ed953d bindings: the author a record's own text can offer
book-pipeline had its own copy of bibclean's author recovery. Bound on all
four surfaces so it can stop.

Checked against that Python over all 5,385 books in the library: 5,375
identical. On the other ten the Python had taken a translator, an illustrator
or the publisher off a copyright line — Sally-Ann Spencer for *The Dwarves*,
Thomas Nelson for a table of contents, C. S. Lewis for a book about Tolkien —
and bibclean either finds the real author in the Library of Congress block or
says nothing. Every one of the ten was read by hand.
2026-09-20 15:10:35 -04:00
funkpower c071f84829 citefind: the references in a bibliography, and what could not be read off them
Cuts a reference list into entries — at the note numbers where there are any,
at the layout where there are not — and reads each as far as its punctuation
allows. Fields that would not parse are named in `unsettled` rather than
guessed at, and `--prompt` asks a model about exactly those and no others.

Over 202 real reference sections from the corpus: 8,566 references, 95% with
a title, 82% with a year, 46% settling every field with no model involved.
A title is refused where the entry was never divided, so no record reports
its own text back as the work it cites.

`ibid.` is flagged and left alone — it points at a work, and the entry does
not say which. A rule of dashes is filled in, because the style defines it as
the author above.

chunkmd gains `note_starts` and `cites_something`: recognising a page of
notes and splitting one are the same knowledge, and two copies of that
pattern would drift.
2026-09-20 10:38:48 -04:00
funkpower d24e37bd12 langid and tocfind: what language a book is in, and what it says its chapters are 2026-09-20 10:02:15 -04:00
funkpower 05759ab902 bookmeta: the metadata a file already carries, and a question about the rest 2026-09-20 09:41:37 -04:00
funkpower dd09499e68 bindings: cueseg, so a transcript can be chaptered in process 2026-09-20 09:00:26 -04:00
funkpower d47881ef3b idnorm and bibfetch: an identifier read, and a register asked 2026-09-19 19:19:54 -04:00
funkpower 05b363ea8a seriesof: the series a book belongs to, and which volume this is 2026-09-19 19:03:47 -04:00
funkpower 48600bd90c bindings: a C ABI and an npm package alongside the wheel 2026-09-19 18:40:08 -04:00
funkpower cf126dddc3 bindings: the libraries in process, for Python 2026-09-19 17:54:18 -04:00
funkpower ab33ac4693 every tool writes its own manual page 2026-09-19 15:25:13 -04:00
funkpower 3c697bee50 pdfgutter: two facing pages photographed as one, found and cut 2026-09-19 15:19:35 -04:00
funkpower 49f12c9718 avplan: whether a recording plays, and the one call that fixes it 2026-09-19 15:07:33 -04:00
funkpower ea0a3694b1 mdsplit: the works inside one file, and the books inside scripture 2026-09-19 14:58:54 -04:00
funkpower 14e1fcf3ef bibclean: slugs, and whether the one a record has still names it 2026-09-19 14:23:36 -04:00
funkpower b0743d5bf7 a missing word, a hidden dependency, and a second pass that changed things 2026-09-19 13:00:29 -04:00
funkpower 6e26d5cf4b bibclean: record faults, weighed, with the repairs that need no lookup 2026-09-19 12:50:53 -04:00
funkpower c6575b2d42 emb: lines into vectors, against a server that owns the model 2026-09-19 12:43:20 -04:00
funkpower 1071f0bed3 booksniff, and one place for the parts every tool shares 2026-09-19 12:40:20 -04:00
funkpower 36afe3350b cuepack: cues into passages that keep their timestamps 2026-09-19 12:32:41 -04:00
funkpower b6602ee1e9 rrf: rank fusion without the database 2026-09-19 12:29:54 -04:00
funkpower 339f402610 ported patterns mean in rust what they mean in python 2026-09-19 12:26:41 -04:00
funkpower 17a03b0087 cueseg: topic chapters from a transcript, and a worked pipeline in the readme 2026-09-19 12:21:35 -04:00
funkpower c7d0767376 cuekit and cuenorm: three subtitle formats, one cue stream 2026-09-19 11:54:36 -04:00
funkpower 33c2e40a1a pdftriage: text layer verdicts, including the reflected-font recovery 2026-09-19 11:46:34 -04:00
funkpower 1b3c002b3d mdscrub: residual html, control characters, headings, page markers 2026-09-19 11:30:05 -04:00
funkpower e0473fb8ae chunkmd: page and chapter aware chunking, roles, footnote flag 2026-09-19 11:22:56 -04:00
funkpower d8929371b4 parity harness, pinned to the python tokeniser over 10k lines 2026-09-19 11:06:55 -04:00
funkpower 0208b14b5d textkit: token counting and the two segmenters 2026-09-19 11:04:27 -04:00
funkpower 00f4c38816 workspace, licence, and the three record types 2026-09-19 11:00:40 -04:00