summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-08-20 19:03:02 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-08-20 19:03:02 +0200
commit1da70dc7ac03fe33fb92b172a0e26932764170d6 (patch)
tree01e1ee37a2be97a0b06fad70de975d8007beb702
parent46ffa91fd4fcc2249cd097b3b3a639d37a7d592e (diff)
downloadcolitur-1da70dc7ac03fe33fb92b172a0e26932764170d6.tar.gz
colitur-1da70dc7ac03fe33fb92b172a0e26932764170d6.zip
fix(citation): close the final review's blocking findings
The branch was RED and reported green. `dune test` exited 1: test/cli.t pinned the pre-fix output `kings_1 19:3-8`, which the previous commit had already fixed to `1 Reg 19:3-8`. The gate command piped dune through `tail`, so it reported tail's exit status, and cram prints its diff BEFORE the alcotest summary, so the two lines shown were the passing ones. Verify with `dune test; echo $?`, never through a pipe. A style file's own `book` key was unreachable. sigla_book resolved against a hardcoded "abbr" and the result was applied unconditionally, so the documented `[sigla] book = full` could never win. Render gains book_string, and the style's own value is now the default that a flag or config overrides. The unit test pinned style_of_fields correctly while the wiring defeated it. `lang --check` filtered the reference set to the celebration prefix, so a file with no [bible] section at all reported a clean bill of health -- contradicting both the reason the keys change was made and lang.ml's own comment. It now reports missing book names too. The token test missed a FOURTH citation-bearing file: adjustments.sexp writes citations as `Set_citation`, not `(reference ...)`. Its 16 citations all parse, so nothing was broken, but nothing was checking. The first attempt at this fix read the file and extracted NOTHING -- the marker stopped before the opening quote, so every payload was the part label -- which is recorded in the code rather than left as a trap. Also: colitur-config(5) claimed a trailing period the data does not carry, and two la.ini scan quotes silently corrected OCR damage ("Ionae 3, I - I O", "Epistolse") while presenting themselves as verbatim. Both are now marked as corrections.
-rw-r--r--bin/main.ml31
-rw-r--r--lang/la.ini13
-rw-r--r--lib/citation/render.ml1
-rw-r--r--lib/citation/render.mli10
-rw-r--r--man/colitur-config.52
-rw-r--r--test/cli.t23
-rw-r--r--test/test_citation.ml32
7 files changed, 94 insertions, 18 deletions
diff --git a/bin/main.ml b/bin/main.ml
index c76a082..1207b1d 100644
--- a/bin/main.ml
+++ b/bin/main.ml
@@ -780,7 +780,11 @@ let load_sigla ~raw ~lang_t ~sigla_style_flag ~sigla_book_flag ~sigla_tradition_
in
let sigla_book_value, _ =
Colitur_naming.Config.resolve ~flag:sigla_book_flag
- ~config:(Colitur_naming.Config.sigla_book config) ~default:"abbr"
+ ~config:(Colitur_naming.Config.sigla_book config)
+ (* The STYLE's own [book] key is the default, so a style file can
+ set it and a flag/config still overrides. A hardcoded "abbr"
+ here made the documented [sigla] book key unreachable. *)
+ ~default:(Colitur_citation.Render.book_string style)
in
let book_form =
match sigla_book_value with
@@ -1870,11 +1874,36 @@ let lang_check path =
in
let missing = List.filter (fun s -> not (List.mem s have)) known in
let unknown = List.filter (fun s -> not (List.mem s known)) have in
+ (* Book names are checked the same way, and separately. Without
+ this the reference set gained a [bible] half that nothing ever
+ consulted: a file with [bible] entirely absent reported a clean
+ bill of health while every citation silently fell back to the
+ data's own Latin spelling. A miss is the TOTAL-lookup contract's
+ own signature -- [Lang.bible] returns the KEY when there is no
+ entry -- so equality with the key IS the test. *)
+ let missing_books =
+ List.concat_map
+ (fun id ->
+ let n = Colitur_citation.Book.to_string id in
+ List.filter_map
+ (fun form ->
+ let key = n ^ "." ^ form in
+ if Colitur_naming.Lang.bible t key = key then Some key
+ else None)
+ [ "full"; "abbr" ])
+ Colitur_citation.Book.all
+ in
List.iter (fun s -> Printf.printf "missing: %s\n" s) (List.sort compare missing);
+ List.iter (fun s -> Printf.printf "missing book: %s\n" s)
+ (List.sort compare missing_books);
List.iter (fun s -> Printf.printf "unknown slug: %s\n" s) (List.sort compare unknown);
+ let books_total = 2 * List.length Colitur_citation.Book.all in
Printf.printf "%s: %d of %d celebrations named, %d missing, %d unknown\n" path
(List.length known - List.length missing)
(List.length known) (List.length missing) (List.length unknown);
+ Printf.printf "%s: %d of %d book names, %d missing\n" path
+ (books_total - List.length missing_books) books_total
+ (List.length missing_books);
if unknown <> [] then exit 1)
(* `colitur config --show` -- each effective setting, its resolved value,
diff --git a/lang/la.ini b/lang/la.ini
index 3783482..821acff 100644
--- a/lang/la.ini
+++ b/lang/la.ini
@@ -1997,7 +1997,12 @@ jonas.full = Ionas Propheta
; scan1.txt:11021 "Lectio Ionae Prophetae." -- CORRECTS the sourcing note
; (genitive "Ionae" -> nominative "Ionas").
jonas.abbr = Ionae
-; scan1.txt:11022 "Ionae 3, 1-10", same page as the .full citation -- the
+; scan1.txt:11022 -- OCR-NORMALISED, not verbatim: the line actually reads
+; "Ionae 3, I - I O", the scanner having read the digits 1 and 0 as capital
+; letter I and letter O. The reading is unambiguous in context (Jonas 3 has
+; 10 verses and the pericope below runs to verse 10), but the quote is a
+; correction, not a transcription, and is marked so rather than presented as
+; if the page said it. Same page as the .full citation -- the
; Missal's own locator uses the genitive form directly, unabbreviated.
malachi.full = Malachias Propheta
; scan1.txt:26697 "Lectio Malachiae Prophetae." -- CORRECTS the sourcing
@@ -2101,7 +2106,11 @@ hebrews.abbr = Hebr
; --- Catholic epistles --------------------------------------------------
james.full = Epistola beati Iacobi Apostoli
-; scan1.txt:20348 "Lectio Epistolae beati Iacobi Apostoli.", also
+; scan1.txt:20348 -- OCR-NORMALISED, not verbatim: the line reads
+; "Epistolse beati Iacobi Apostoli", the scanner having read the ligature ae
+; as "se". The correction is certain (no Latin word "Epistolse" exists, and
+; the same phrase is clean elsewhere), but it is a correction and is marked
+; as one. Also
; 20432/20509/34828/38918/40963/41197/45872/49193 -- no "ad X" destination
; to shorten to, so the attribution stays; see the header's own note on this
; pattern.
diff --git a/lib/citation/render.ml b/lib/citation/render.ml
index c66a0ca..79aaa86 100644
--- a/lib/citation/render.ml
+++ b/lib/citation/render.ml
@@ -20,6 +20,7 @@ let default_style =
let part_sep st = st.part_sep
let range st = st.range
let book_sep st = st.book_sep
+let book_string st = match st.book with `Full -> "full" | `Abbr -> "abbr"
(* Arabic -> Roman, for the {chapter_roman} placeholder. Lifted from
[Colitur_render.View.roman_numeral], which prints "Hebdomada I" week
diff --git a/lib/citation/render.mli b/lib/citation/render.mli
index d741931..274900c 100644
--- a/lib/citation/render.mli
+++ b/lib/citation/render.mli
@@ -44,5 +44,15 @@ val range : style -> string
[\textasciitilde{}]. *)
val book_sep : style -> string
+(** The style's own book form, as the string a config file would write
+ (["full"] or ["abbr"]).
+
+ Exists so a caller can use the STYLE's value as the default when
+ resolving the [sigla_book] setting. Resolving against a hardcoded
+ ["abbr"] instead makes a style file's own [book] key unreachable --
+ the value is always overwritten before it can apply -- which is what
+ shipped until this accessor existed. *)
+val book_string : style -> string
+
val render :
style -> names:(Book.id -> [ `Full | `Abbr ] -> string) -> Parse.t -> string
diff --git a/man/colitur-config.5 b/man/colitur-config.5
index 6b3a574..0e242bf 100644
--- a/man/colitur-config.5
+++ b/man/colitur-config.5
@@ -201,7 +201,7 @@ Overrides the selected style's own
setting. Default
.BR abbr ,
giving
-.B "Luc. 5:12\-14"
+.B "Luc 5:12\-14"
rather than
.BR "Evangelium secundum Lucam 5:12\-14" .
An unrecognised value is a hard error, the same discipline as an unknown
diff --git a/test/cli.t b/test/cli.t
index d9d8ec5..b44a256 100644
--- a/test/cli.t
+++ b/test/cli.t
@@ -1261,12 +1261,14 @@ slug the engine can produce over 2020-2045:
$ colitur lang --check d.ini
d.ini: 725 of 725 celebrations named, 0 missing, 0 unknown
+ d.ini: 104 of 104 book names, 0 missing
`--check` reports what is MISSING (a real slug with no entry):
$ printf '[meta]\nlang = zz\n[celebration]\nef-epiphany = Test\n' > partial.ini
- $ colitur lang --check partial.ini | tail -1
+ $ colitur lang --check partial.ini | tail -2
partial.ini: 1 of 725 celebrations named, 724 missing, 0 unknown
+ partial.ini: 0 of 104 book names, 104 missing
`--check` REJECTS an unknown slug (exit 1), so a typo is visible rather than
silently dead -- its author would otherwise never learn why the name they
@@ -1431,15 +1433,20 @@ Latin-convention punctuation. The synthetic file below overrides only
`--sigla-tradition` renumbers which book an id DENOTES (lang/traditions.ini),
independently of style or naming -- `modern` maps `3 Kings` onto the id
-`kings_1`. Task 10's `la.ini` now carries a `[bible]` row for `kings_1` (a
-tradition target the Vulgate data never cites directly, book.mli), but only
-its own bare id -- an honest UNSOURCED placeholder, not a name (la.ini's own
-`[bible]` header note) -- so it still prints literally, now BY DESIGN rather
-than by the row's absence, still a real, if plain, witness that the
-tradition actually applied rather than a no-op:
+`kings_1`, whose own Latin name la.ini marks CONSTRUCTED: the 1962 Missal
+uses Vulgate numbering throughout, so it can contain no incipit for a book
+that exists only under a later convention. In Latin the modern tradition
+therefore only really moves Kings and Esdras -- Osee, Ionas, Ecclesiasticus
+and the Apocalypse keep their Vulgate names either way, because modern
+numbering is a vernacular convention:
$ colitur readings 2027 --sigla-tradition modern | grep '^2027-02-17'
- 2027-02-17 ef-lent-ember-wed | kings_1 19:3-8 | Matth 12:38-50 | Feria IV Quatuor Temporum Quadragesimae
+ 2027-02-17 ef-lent-ember-wed | 1 Reg 19:3-8 | Matth 12:38-50 | Feria IV Quatuor Temporum Quadragesimae
+
+Under an English file the same mapping shows its usual face:
+
+ $ colitur readings 2027 --lang en --sigla-tradition modern | grep '^2027-02-17'
+ 2027-02-17 ef-lent-ember-wed | 1 Kgs 19:3-8 | Matt 12:38-50 | Lenten Ember Wednesday
`table`/`render`, `emit` and `publish` accept the same three flags too --
smoke-tested for exit status alone here (a minimal inline template, the
diff --git a/test/test_citation.ml b/test/test_citation.ml
index 1efaa10..133553b 100644
--- a/test/test_citation.ml
+++ b/test/test_citation.ml
@@ -117,8 +117,7 @@ let starts_with_at content pos prefix =
[Str]/regex -- a plain forward scan for the marker, then read to the
closing quote. Mirrors the coordinator's own survey command (grep -oh
over the reference marker, quote-delimited). *)
-let references content =
- let marker = "(reference \"" in
+let references_with marker content =
let mlen = String.length marker in
let len = String.length content in
let rec loop pos acc =
@@ -151,14 +150,35 @@ let book_token r =
let stop = if !i < len && r.[!i] = '.' then !i + 1 else !i in
String.sub r 0 stop
+(* A citation is written TWO ways in this project's data, and a survey that
+ knows only one of them silently under-reads the corpus:
+ (reference "Isa 60:1-6") -- lectionary/sanctoral/commons
+ (Set_citation First "Wis 7:7-14") -- adjustments.sexp, an overlay
+ Both markers are scanned below. *)
+let references content =
+ (* Each marker must include the OPENING QUOTE. [references_with] returns
+ the text between the marker and the next '"', so a marker stopping at
+ "(Set_citation " yields "First " / "Gospel " -- the part label, never
+ the citation -- and every entry is then silently discarded. That was
+ this function's first version, and it read the file while extracting
+ nothing at all. *)
+ references_with "(reference \"" content
+ @ references_with "(Set_citation First \"" content
+ @ references_with "(Set_citation Gospel \"" content
+
let test_every_data_file_token_resolves () =
(* The check whose absence caused fix round 1: the brief surveyed only
the lectionary and missed 21 tokens, several common, living in
- sanctoral.sexp and commons.sexp. Read all three files at test time and
- re-derive the token set from them, rather than hardcoding a list, so
- this keeps working when the data changes. *)
+ sanctoral.sexp and commons.sexp.
+
+ adjustments.sexp was then missed AGAIN, by this very test, because it
+ writes citations as `Set_citation` rather than `(reference ...)` -- the
+ same "one file too few" shape twice over. All FOUR shipped files are
+ read here, and the token set is re-derived from them at test time
+ rather than hardcoded, so this keeps working when the data changes. *)
let files =
- [ "../data/ef/lectionary.sexp"; "../data/ef/sanctoral.sexp"; "../data/ef/commons.sexp" ]
+ [ "../data/ef/lectionary.sexp"; "../data/ef/sanctoral.sexp";
+ "../data/ef/commons.sexp"; "../data/ef/adjustments.sexp" ]
in
let tokens =
files