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.
This commit is contained in:
funkpower
2026-09-20 10:38:48 -04:00
parent 30ff3361ee
commit c071f84829
22 changed files with 2612 additions and 6 deletions
@@ -14,9 +14,12 @@ from ._catena import (
__version__,
cue_windows,
chunk_markdown,
citation_prompt,
count_tokens,
find_citations,
fuse_hits,
identify,
merge_citation_reply,
pack_cues,
parse_identifier,
parse_subtitles,
@@ -30,9 +33,12 @@ __all__ = [
"__version__",
"cue_windows",
"chunk_markdown",
"citation_prompt",
"count_tokens",
"find_citations",
"fuse_hits",
"identify",
"merge_citation_reply",
"pack_cues",
"parse_identifier",
"parse_subtitles",
@@ -37,6 +37,11 @@ def fuse_hits(
limit: int | None = ...,
) -> list[dict[str, Any]]: ...
def identify(name: str, data: bytes) -> dict[str, Any]: ...
def find_citations(text: str) -> list[dict[str, Any]]: ...
def citation_prompt(references: Iterable[Mapping[str, Any]]) -> str | None: ...
def merge_citation_reply(
references: Iterable[Mapping[str, Any]], reply: str
) -> list[dict[str, Any]]: ...
def parse_identifier(text: str) -> dict[str, Any] | None: ...
def series_of(
title: str, author: str | None = ...