summaryrefslogtreecommitdiff
path: root/tools/bootstrap_lectionary.ml
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-08-15 04:16:24 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-08-15 04:16:24 +0200
commit96bf100a4de32bd5f3887ffdb15b59b8bbd0376b (patch)
treeea3be84f551016a617adf50abdd34af5ae390546 /tools/bootstrap_lectionary.ml
parentb0ff86ae552b90613be21c52433f4180deba7758 (diff)
downloadcolitur-96bf100a4de32bd5f3887ffdb15b59b8bbd0376b.tar.gz
colitur-96bf100a4de32bd5f3887ffdb15b59b8bbd0376b.zip
test(differential): fix round 2 -- Good Friday, Passion Tuesday, stale header
Critical, and worse than what it replaced: removing a lectionary key does not mean "no reading" to Lectionary_ef.readings -- it means step 3 silently resumes the preceding Sunday. Good Friday, which has no Mass at all, was emitting Palm Sunday's own Epistle and Passion narrative every year; Holy Wednesday had the same defect. Fixed by authoring both days fully: the Gospel was always unambiguous and is now always authored; the Epistle uses the same "last lesson before the Gospel" convention this file's own Lenten Ember Wednesday entry already established for an identical two-peer-lesson shape, applied for consistency rather than invented to close this gap. C24 is retired, folded into a widened C23 covering all six Holy Week days. Important: Passion Tuesday was shipping Holy Tuesday's Mass, a defect the previous round's own footnote had found and left unrecorded. Fixed at the source -- colitur_keys now excludes lectio's own (wrong) ini section for this one weekday rather than translating it; a new passion_tuesday_entry hand-authors the real Missal citation (Dan 14:27, 28-42 / John 7:1-13, both scans). New entry C26 records the resulting divergence, 43 of 46 years, traced (not merely counted) to three years where a competing I-class feast impedes the day entirely. Important: the allow-list header was stale one level up, still naming and restating the diagnosis of an id (C21) retired in the previous round. Rewritten in full rather than layered with another correction. Minors: all three Lent Ember values now pinned (only Wednesday was before); C22's citation corrected to attribute the date-anchor text and the rank text to their own separate sources; assert_reachable's own comment no longer overclaims what it can catch; C25's falsified reasoning replaced with the directly observed fact (a first replacement derivation was itself found wrong on a second reading and is recorded as rejected, not silently discarded); hand-authored citations standardised to English book names with a colon, matching this file's own dominant convention, with one stated exception where a value must stay byte-identical to lectio's own data rather than be reformatted. dune test: 358/358. COLITUR_EXHAUSTIVE_SWEEP=1 dune test --force: 359/359.
Diffstat (limited to 'tools/bootstrap_lectionary.ml')
-rw-r--r--tools/bootstrap_lectionary.ml207
1 files changed, 150 insertions, 57 deletions
diff --git a/tools/bootstrap_lectionary.ml b/tools/bootstrap_lectionary.ml
index ed40c92..4939770 100644
--- a/tools/bootstrap_lectionary.ml
+++ b/tools/bootstrap_lectionary.ml
@@ -135,6 +135,28 @@ let colitur_keys name =
[ "ef-lent-ember-wed" ]
else if String.equal name "ef-lent-1-friday" then [ "ef-lent-ember-fri" ]
else if String.equal name "ef-lent-1-saturday" then [ "ef-lent-ember-sat" ]
+ else if String.equal name "ef-passiontide-0-tuesday" then
+ (* Important, fix round 2 (coordinator review): the general
+ Passiontide rename below carries every OTHER weekday's citation
+ straight through from lectio's own ini, correct for Passion week
+ (Critical 1's own fix, this comment's own sibling branch). Tuesday
+ is the ONE exception, already flagged, not yet acted on, by
+ [holy_week_entries]'s own footnote in the previous fix round: the
+ ini's "ef-passiontide-0-tuesday" section is not Passion week's own
+ Mass at all -- it is HOLY Tuesday's (Jer 11:18-20 / the Passion
+ according to Mark), confirmed independently against BOTH scans at
+ the time. Passion week's own real Tuesday Mass ("Feria tertia, III
+ classis, Statio ad S. Cyriacum", scan1.txt:11085-11121, corroborated
+ scan2.txt:12063-12104) is Dan 14:27, 28-42 / John 7:1-13 -- a
+ DIFFERENT Epistle and a DIFFERENT Gospel from what the ini section
+ carries. Excluded here (empty translation, not a rename) so the
+ wrong ini value is never used; the correct one is hand-authored
+ below, [passion_tuesday_entry]. lectio itself never surfaces this,
+ since its own computed reading is drawn from the SAME (Holy
+ Tuesday's) data on the day it labels "Passion Tuesday" too --
+ data/ef/expected-divergences.sexp's own new entry records the
+ resulting divergence. *)
+ []
else (
match List.find_opt (fun wd -> String.equal name ("ef-passiontide-0-" ^ wd)) weekdays with
| Some wd ->
@@ -147,16 +169,14 @@ let colitur_keys name =
CONFLATES the two weeks -- it has no Holy Week propers of its
own -- not that the Missal does: Holy Monday's real Mass
(docs/research/scan1.txt, "Feria II Hebdomadae sanctae, I
- classis") is Isai. 50,5-10 / Io. 12,1-9, nothing like
- Passion-week Monday's Ionae 3,1-10 / Io. 7,32-39 that the widen
- was putting there, and Holy Thursday's is 1 Cor. 11,20-32 / Io.
- 13,1-15 -- the Mass of the Lord's Supper reading Passion
+ classis") is Isa 50:5-10 / John 12:1-9, nothing like
+ Passion-week Monday's Jonas 3:1-10 / John 7:32-39 that the widen
+ was putting there, and Holy Thursday's is 1 Cor 11:20-32 / John
+ 13:1-15 -- the Mass of the Lord's Supper reading Passion
Sunday's own ferial Mass was the actual defect, not merely a
citation nicety. Narrowed to Passion week ONLY; Holy Week's own
- citations (Monday, Tuesday, Thursday, Saturday) are
- hand-authored below, [holy_week_entries] -- see its own comment
- for which two days of Holy Week (Wednesday, Good Friday) are
- deliberately NOT included and why. *)
+ citations (all six weekdays, fix round 2) are hand-authored
+ below, [holy_week_entries]. *)
[ "ef-passiontide-1-" ^ wd ]
| None -> [ name ])
@@ -196,6 +216,19 @@ let convert sec =
generator's own single-ini design cannot reach on its own. *)
let slug_or_die name = match Slug.of_string name with Ok s -> s | Error e -> die "%s" e
+(* CITATION FORMAT, fix round 2 (coordinator review, Minor): every
+ hand-authored reference below uses ENGLISH book abbreviations with a
+ colon before the verse ("John 12:1-9"), matching the form already
+ DOMINANT in this file's own ini-derived data (counted directly: "John"
+ 40 occurrences vs "Io." 2; "Luke" 34 vs "Luc." 3; "Titus" 1 vs "Tit." 3
+ -- the Latin forms were ALL this generator's own earlier, inconsistent
+ choice, not lectio's), the same "one form per book, matching what's
+ already dominant" discipline data/ef/commons.sexp's own header already
+ states for hand-authored citations. An earlier version of every entry
+ below used Latin book names and Missal-style comma verse separators
+ ("Isai. 50, 5-10"), inconsistent with the rest of the file and, in one
+ entry, inconsistent with ITSELF (Ier./Mark mixed in the same pair) --
+ all converted here, content unchanged, only the spelling. *)
let pair ~first ~gospel =
[ { Citation.part = Citation.First; reference = first };
{ Citation.part = Citation.Gospel; reference = gospel } ]
@@ -213,60 +246,98 @@ let vigil_entries =
between):
- [ef-passiontide-2-monday] (Holy Monday, "Feria II Hebdomadae sanctae,
- I classis"): Isai. 50,5-10 / Io. 12,1-9 -- scan1.txt "Lectio Isaiae
+ I classis"): Isa 50:5-10 / John 12:1-9 -- scan1.txt "Lectio Isaiae
Prophetae... Isai. 50, 5-10" + "Sequentia... secundum Ioannem. Io. 12,
1-9"; scan2.txt corroborates both citations word for word.
- [ef-passiontide-2-tuesday] (Holy Tuesday, "Feria III Hebdomadae
- sanctae, I classis"): Ier. 11,18-20 / the Passion according to Mark,
- 14,32-72;15,1-46 -- scan1.txt "Lectio Ieremiae Prophetae... Ier. 11,
+ sanctae, I classis"): Jer 11:18-20 / the Passion according to Mark,
+ 14:32-72; 15:1-46 -- scan1.txt "Lectio Ieremiae Prophetae... Ier. 11,
18-20" + "Evangelium Passionis et Mortis Domini secundum Marcum.
14,32-72; 15,1-46"; scan2.txt corroborates both.
- [ef-passiontide-2-thursday] (Holy Thursday, "Feria V in Cena Domini"):
- 1 Cor. 11,20-32 / Io. 13,1-15 -- scan1.txt "Lectio Epistolae beati
+ 1 Cor 11:20-32 / John 13:1-15 -- scan1.txt "Lectio Epistolae beati
Pauli Apostoli ad Corinthios... 1 Cor. 11, 20-32" + "Sequentia...
secundum Ioannem... Io. 13,1-15"; scan2.txt corroborates both.
- [ef-passiontide-2-saturday] (Holy Saturday, Missa Vigiliae Paschalis'
own Epistle+Gospel -- the actual Mass, distinct from the earlier
- prophecies, explicitly labelled "Lectio EPISTOLAE"): Col. 3,1-4 /
- Matt. 28,1-7 -- scan1.txt "Lectio Epistolae beati Pauli Apostoli ad
+ prophecies, explicitly labelled "Lectio EPISTOLAE"): Col 3:1-4 /
+ Matt 28:1-7 -- scan1.txt "Lectio Epistolae beati Pauli Apostoli ad
Colossenses... Col. 3,1-4" + "Sequentia... secundum Matthaeum. Mt.
28,1-7"; scan2.txt corroborates both.
- DELIBERATELY NOT INCLUDED, and recorded here rather than guessed:
+ Fix round 2 (coordinator review, Critical): the remaining two days,
+ Holy Wednesday and Good Friday, were left ENTIRELY absent by the first
+ pass, on the reasoning that neither has a single reading in the
+ "Epistle" position. That reasoning was correct for the FIRST slot but
+ the wrong conclusion for the pair as a whole, and it broke something
+ worse than what it replaced: an absent lectionary key does not mean
+ "no reading" to {!Rite_ef.Lectionary_ef.readings} -- it means STEP 3
+ resumes the preceding Sunday, so Good Friday -- which has no Mass at
+ all -- was emitting Palm Sunday's own Epistle and Passion narrative.
+ Fixed properly:
- - [ef-passiontide-2-wednesday] (Holy Wednesday, "Feria IV Hebdomadae
- sanctae"): TWO Old Testament lessons (Isai. 62,11;63,1-7, then Isai.
- 53,1-12), neither labelled "Epistola", before the Passion according
- to Luke (22,39-71;23,1-53) -- no single reading occupies the
- "Epistle" position this schema's [First]/[Gospel] pair assumes every
- other entry in this file has, unlike Holy Saturday's genuinely
- labelled Epistle above.
- - [ef-passiontide-2-friday] (Good Friday, "Feria VI in Passione et
- Morte Domini"): not even a Mass ("Solemnis Actio liturgica"), with
- TWO peer lessons (Osee 6,1-6, then Exodus 12,1-11), again neither
- labelled "Epistola", directly into the Passion according to John
- (18,1-40;19,1-42).
-
- Forcing either into a single First/Gospel pair would be an editorial
- choice this generator has no textual warrant to make on its own --
- unlike Holy Saturday, where the Mass's own Epistle is explicitly
- labelled and distinct from its own preceding prophecies. Left absent:
- data/ef/expected-divergences.sexp's own Layer C entry records what this
- means for the differential (colitur emits [], an honest absence, not a
- guess). *)
+ - The GOSPEL is unambiguous on both days -- one labelled Passion
+ narrative each, nothing to choose among -- so it is always authored:
+ Holy Wednesday, Luke 22:39-71; 23:1-53 (scan1.txt "Evangelium
+ Passionis et Mortis Domini secundum Lucam. 22, 39-71; 23, 1-53");
+ Good Friday, John 18:1-40; 19:1-42 (scan1.txt "Evangelium Passionis
+ et Mortis Domini secundum Ioannem. 18,1-40; 19,1-42"). Both
+ corroborated scan2.txt.
+ - The FIRST slot is filled too, by the SAME "last lesson before the
+ Gospel" convention this file's own Lenten Ember Wednesday entry
+ (below, in [colitur_keys]'s own ini translation, not hand-authored
+ here) already applies to an identical two-lesson shape: Exodi
+ 24,12-18 then 3 Reg. 19,3-8 (scan1.txt:8311/8377), and the file ships
+ the SECOND. Applied here for the same reason -- consistency with
+ already-shipped precedent, not a fresh editorial choice invented for
+ this entry: Holy Wednesday's own two Isaiah lessons are Isai. 62,
+ 11;63,1-7 THEN Isai. 53,1-12 (scan1.txt:12379/12415, corroborated
+ scan2.txt) -- the file ships Isa 53:1-12, the second. Good Friday's
+ own two lessons are Osee 6,1-6 THEN Ex. 12,1-11 (scan1.txt:
+ 13143/13181, corroborated scan2.txt) -- the file ships Ex 12:1-11,
+ the second. Neither lesson is labelled "Epistola" on either day
+ (unlike Holy Saturday's genuinely labelled Epistle above), so this
+ remains a stated CONVENTION, not a textual fact the Missal asserts
+ -- but it is the SAME convention already load-bearing elsewhere in
+ this exact file, not a new one invented to paper over this gap. *)
let holy_week_entries =
- [ (slug_or_die "ef-passiontide-2-monday", pair ~first:"Isai. 50, 5-10" ~gospel:"Io. 12, 1-9");
+ [ (slug_or_die "ef-passiontide-2-monday", pair ~first:"Isa 50:5-10" ~gospel:"John 12:1-9");
(slug_or_die "ef-passiontide-2-tuesday",
- pair ~first:"Ier. 11, 18-20" ~gospel:"Mark 14, 32-72; 15, 1-46");
- (slug_or_die "ef-passiontide-2-thursday", pair ~first:"1 Cor. 11, 20-32" ~gospel:"Io. 13, 1-15");
- (slug_or_die "ef-passiontide-2-saturday", pair ~first:"Col. 3, 1-4" ~gospel:"Matt. 28, 1-7")
+ pair ~first:"Jer 11:18-20" ~gospel:"Mark 14:32-72; 15, 1-46");
+ (* The Gospel's own punctuation ("15, 1-46", not "15:1-46") is
+ deliberately kept exactly as lectio's own ini already has it
+ (`ef-passiontide-0-tuesday`'s own `gospel` field, byte for byte)
+ rather than normalised to this file's own colon convention: this
+ value is not merely CORROBORATED by lectio's data, it is the
+ SAME data (Holy Tuesday's Mass, which the ini's Tuesday section
+ happens to carry outright) -- reformatting it would turn a
+ genuine byte-identical match into a false, punctuation-only
+ divergence the differential would then have to explain away. *)
+ (slug_or_die "ef-passiontide-2-wednesday",
+ pair ~first:"Isa 53:1-12" ~gospel:"Luke 22:39-71; 23:1-53");
+ (slug_or_die "ef-passiontide-2-thursday", pair ~first:"1 Cor 11:20-32" ~gospel:"John 13:1-15");
+ (slug_or_die "ef-passiontide-2-friday", pair ~first:"Ex 12:1-11" ~gospel:"John 18:1-40; 19:1-42");
+ (slug_or_die "ef-passiontide-2-saturday", pair ~first:"Col 3:1-4" ~gospel:"Matt 28:1-7")
]
+(* Important, fix round 2 (coordinator review): Passion week's own real
+ Tuesday Mass ("Feria tertia, III classis, Statio ad S. Cyriacum",
+ scan1.txt:11085-11121, corroborated scan2.txt:12063-12104, word for
+ word both scans): Dan 14:27, 28-42 / John 7:1-13. [colitur_keys]'s own
+ "ef-passiontide-0-tuesday" branch above excludes lectio's ini value for
+ this one weekday (it is Holy Tuesday's Mass, not Passion Tuesday's --
+ see that branch's own comment for the full account) rather than
+ translating it, so this is the ONLY source of a citation for
+ "ef-passiontide-1-tuesday" at all -- not a widen, not a correction of
+ an existing translated value. *)
+let passion_tuesday_entry =
+ [ (slug_or_die "ef-passiontide-1-tuesday", pair ~first:"Dan 14:27, 28-42" ~gospel:"John 7:1-13") ]
+
(* Important 3(a), fix round 1 (coordinator review): the fixed Nativity-
Octave days (RG 91 entry 17, 29-31 December, colitur's own
[ef-nativity-octave-day-{5,6,7}]) have a DIRECT formulary in the
Missal, not merely a resolvable-by-walk-back gap -- "Diebus infra
- octavam Nativitatis Domini, II classis": Tit. 3,4-7 / Luc. 2,15-20
+ octavam Nativitatis Domini, II classis": Titus 3:4-7 / Luke 2:15-20
(docs/research/scan1.txt:6281-6329, scan2.txt:6900-6960, both word for
word), and each specific date's own rubric points straight at it
("Die 29 decembris... Missa Puer natus est nobis, ut supra [28]",
@@ -278,7 +349,7 @@ let holy_week_entries =
REJECTED, and recorded so it is not re-attempted: the FIRST guess tried
here gave these three slugs [ef-holy-name-sunday]'s own citation
- instead (Gal. 4,1-7 / Luc. 2,33-40, "Dominica infra octavam Nativitatis
+ instead (Gal 4:1-7 / Luke 2:33-40, "Dominica infra octavam Nativitatis
Domini" -- a DIFFERENT heading, for the Sunday specifically, not the
weekdays). Measured against the real fixture and reverted: lectio's own
citation for these dates is not uniform across years (2005-12-29 reads
@@ -307,7 +378,7 @@ let holy_week_entries =
the same RG 67/69 gap data/ef/expected-divergences-missalemeum.sexp's
own M11 already tracks from a different differential layer. *)
let nativity_octave_entries =
- let cs = pair ~first:"Tit. 3, 4-7" ~gospel:"Luc. 2, 15-20" in
+ let cs = pair ~first:"Titus 3:4-7" ~gospel:"Luke 2:15-20" in
List.map (fun n -> (slug_or_die (Printf.sprintf "ef-nativity-octave-day-%d" n), cs)) [ 5; 6; 7 ]
(* One more colitur-only slug, DERIVED from [ef-holy-name-sunday]'s own
@@ -358,13 +429,29 @@ let sha256 path =
slug it ever actually produces. [assert_reachable] then requires every
key this generator is about to EMIT to be a member of that set (an
emitted key that is not a real Temporal_ef slug is dead data,
- unreachable by any caller -- exactly the shape both Critical findings
- had), dying loudly and naming every offender if not. Separately,
- informational only, it prints every real Temporal_ef slug that has NO
- entry in the final table -- not an error (most such gaps are the
- correctly-unproper ferias step 3 already resolves, task-5-report.md's
- own "297 of 304" measurement), but a standing audit log a human reader
- can check against that same report rather than trusting silence.
+ unreachable by any caller), dying loudly and naming every offender if
+ not.
+
+ CORRECTED, fix round 2 (coordinator review): the paragraph above used
+ to claim this check "is exactly the shape both Critical findings had".
+ Wrong for one of the two, and worth being precise about the limit: this
+ assertion catches an emitted KEY that names no real office (Critical 2,
+ the Lent Ember dead keys) -- it cannot catch, and did not catch, a real,
+ live key carrying the WRONG VALUE (Critical 1, fix round 1's own Holy
+ Week widening: "ef-passiontide-2-monday" etc were all perfectly
+ reachable slugs, just paired with Passion week's citations instead of
+ Holy Week's own). A reachability sweep is a name check, not a content
+ check; nothing in this generator verifies citation VALUES against the
+ Missal except the human cross-referencing docs/research/scan1.txt and
+ scan2.txt line by line, which is why every hand-authored entry in this
+ file states its own two-scan citation.
+
+ Separately, informational only, [assert_reachable] also prints every
+ real Temporal_ef slug that has NO entry in the final table -- not an
+ error (most such gaps are the correctly-unproper ferias step 3 already
+ resolves, task-5-report.md's own "297 of 304" measurement), but a
+ standing audit log a human reader can check against that same report
+ rather than trusting silence.
1583 is deliberately NOT the sweep's start: the full 1583-9999 domain is
the KERNEL's own contract, not this rite-specific bootstrap tool's --
@@ -427,8 +514,8 @@ let () =
let secs = parse_ini src in
let translated = List.concat_map convert secs in
let entries =
- translated @ vigil_entries @ holy_week_entries @ nativity_octave_entries
- @ derived_entries translated
+ translated @ vigil_entries @ holy_week_entries @ passion_tuesday_entry
+ @ nativity_octave_entries @ derived_entries translated
in
assert_reachable entries;
let lect =
@@ -444,7 +531,9 @@ let () =
; generator against a newer lectio and commit the diff instead. Every\n\
; emitted key is asserted, at generation time, to be a slug\n\
; Rite_ef.Temporal_ef actually computes (see this generator's own\n\
- ; [assert_reachable]) -- a dead key cannot ship silently again.\n\
+ ; [assert_reachable]) -- a dead key cannot ship silently again (a\n\
+ ; wrongly-VALUED live key still can; [assert_reachable]'s own comment\n\
+ ; says why this check cannot be widened to catch that).\n\
;\n\
; Source: %s\n\
; SHA-256: %s\n\
@@ -452,14 +541,18 @@ let () =
; Temporal_ef vocabulary via [colitur_keys]; %d hand-authored from a\n\
; second source file [vigil_entries]; %d hand-authored directly from\n\
; the Missal [holy_week_entries]; %d hand-authored directly from the\n\
- ; Missal [nativity_octave_entries]; %d derived from an already-\n\
- ; translated entry above rather than re-typed [derived_entries] --\n\
- ; see this generator's own comments on all five). Regenerate with:\n\
+ ; Missal, one entry [passion_tuesday_entry]; %d hand-authored directly\n\
+ ; from the Missal [nativity_octave_entries]; %d derived from an\n\
+ ; already-translated entry above rather than re-typed\n\
+ ; [derived_entries] -- see this generator's own comments on all six).\n\
+ ; Regenerate with:\n\
; eval $(opam env) && dune exec tools/bootstrap_lectionary.exe -- %s %s\n"
src (sha256 src) (List.length entries) (List.length secs) (List.length vigil_entries)
- (List.length holy_week_entries) (List.length nativity_octave_entries)
+ (List.length holy_week_entries) (List.length passion_tuesday_entry)
+ (List.length nativity_octave_entries)
(List.length entries - List.length translated - List.length vigil_entries
- - List.length holy_week_entries - List.length nativity_octave_entries)
+ - List.length holy_week_entries - List.length passion_tuesday_entry
+ - List.length nativity_octave_entries)
src dst;
Sexplib.Sexp.output_hum oc (Lectionary.sexp_of_t lect);
output_char oc '\n';