summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-08-26 00:18:16 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-08-26 00:18:16 +0200
commit85c1f194c03634eaa905f0bfd630fe23b5f53e59 (patch)
treeb003bd26bebd44c61514fc652250889ce56c1bef
parent0994587b3694bb87d9a8ee32d60babf25c784acf (diff)
downloadcolitur-85c1f194c03634eaa905f0bfd630fe23b5f53e59.tar.gz
colitur-85c1f194c03634eaa905f0bfd630fe23b5f53e59.zip
feat(of): post-2002 GRC amendment overlays, one file per decree
Adds data/of/amendments/*.sexp: 13 overlay files, one per decree, carrying the General Roman Calendar's amendments since the 2002 typical edition (docs/research/of/PROVENANCE-amendments.md's own 15-item inventory, items 10-12 sharing one decree; items 2 and 3, though issued the same day, carry different protocol numbers and are counted as two decrees). 14 Add directives (Padre Pio, Juan Diego, Guadalupe, John XXIII, John Paul II, Mary Mother of the Church, Paul VI, Loreto, Faustina Kowalska, Gregory of Narek, John of Avila, Hildegard of Bingen, Teresa of Calcutta, John Henry Newman) and 2 Edit directives (Mary Magdalene's rank memoria->festum; Martha/Mary/Lazarus's name widened, same slug, same date and rank -- a continuation of the existing memorial per the decree's own text, not a suppression and replacement). Mary, Mother of the Church is the one movable entry (Easter_offset 50, the Monday after Pentecost) -- verified against the decree's own text and against Colitur_kernel.Computus.gregorian_easter in three real years, no kernel change needed. data/of/calendar-2002.sexp is untouched, per spec sec4.1. Adds test/test_amendments_of.ml: every file parses, each has a pinned SHA-256, applying all 13 in decree order yields 222 entries (208 base + 14 Add) with an empty Overlay.merge diagnostics list, and Mary Mother of the Church resolves to the Monday after Pentecost in three real years. Two Precedence_of interactions are recorded, not fixed (out of scope for a data-only task): a movable Memoria_obligatoria can land on an existing fixed one (verified for 13 June 2011), a genuine band tie Normae n. 14 does not address and Precedence_of.ml does not implement; and adding Hildegard of Bingen changes which of two ad-libitum memorials on 17 September Precedence.resolve reports as observed, by alphabetical tie-break alone.
-rw-r--r--data/of/amendments/001-padre-pio.sexp39
-rw-r--r--data/of/amendments/002-juan-diego-cuauhtlatoatzin.sexp40
-rw-r--r--data/of/amendments/003-our-lady-of-guadalupe.sexp41
-rw-r--r--data/of/amendments/004-john-xxiii-john-paul-ii.sexp50
-rw-r--r--data/of/amendments/005-mary-magdalene-rank.sexp49
-rw-r--r--data/of/amendments/006-mary-mother-of-the-church.sexp76
-rw-r--r--data/of/amendments/007-paul-vi.sexp31
-rw-r--r--data/of/amendments/008-our-lady-of-loreto.sexp27
-rw-r--r--data/of/amendments/009-faustina-kowalska.sexp34
-rw-r--r--data/of/amendments/010-narek-avila-hildegard.sexp76
-rw-r--r--data/of/amendments/011-martha-mary-lazarus.sexp38
-rw-r--r--data/of/amendments/012-teresa-of-calcutta.sexp30
-rw-r--r--data/of/amendments/013-john-henry-newman.sexp47
-rw-r--r--test/dune13
-rw-r--r--test/test_amendments_of.ml217
-rw-r--r--test/test_colitur.ml1
16 files changed, 809 insertions, 0 deletions
diff --git a/data/of/amendments/001-padre-pio.sexp b/data/of/amendments/001-padre-pio.sexp
new file mode 100644
index 0000000..27758ee
--- /dev/null
+++ b/data/of/amendments/001-padre-pio.sexp
@@ -0,0 +1,39 @@
+; data/of/amendments/001-padre-pio.sexp -- ADDS St Pius of Pietrelcina
+; (Padre Pio) to the General Roman Calendar.
+;
+; Decree: "Ex uberi terra", Congregation for Divine Worship and the
+; Discipline of the Sacraments, 26 June 2002. Signed Marius Marini,
+; Subsecretarius.
+; Published: Notitiae 38 (2002), 549-550 (annexed liturgical texts run
+; 551-554). Absent from AAS 2002-2006 (checked directly, not merely
+; unindexed -- docs/research/of/PROVENANCE-amendments.md item 1).
+;
+; What it does: inscribes 23 September, memoria (obligatory -- the
+; decree's own operative sentence, "in posterum memoria Sancti Pii de
+; Pietrelcina, presbyteri, quotannis... celebretur", carries no "ad
+; libitum" qualifier; confirmed directly against the decree's own text,
+; Notitiae 38 (2002) 551, line 1643 of the extracted transcript).
+;
+; Absent from the printed 2002 Missal's own Calendarium (23 September is
+; blank in data/of/calendar-2002.sexp's source PDF) despite Pio's
+; canonization (16 June 2002) preceding this decree by ten days -- a
+; genuine post-typical-edition addition, not a transcription gap.
+;
+; `en`/slug reused from lectio's own roman-calendar.ini
+; ([pio-of-pietrelcina-padre-pio-priest], date 09-23, rank memorial,
+; class saint, colour white) -- an engineering-key/cross-check reuse only,
+; per calendar-2002.sexp's own header discipline; `la` taken directly from
+; the decree's own calendarium table row ("S. Pii de Pietrelcina,
+; presbyteri", Notitiae 38 (2002) 551).
+((id of-amend-01-padre-pio)
+ (directives
+ ((Add
+ ((date (Fixed (month 9) (day 23)))
+ (cel
+ ((slug pio-of-pietrelcina-padre-pio-priest)
+ (names
+ ((en "Saint Pio of Pietrelcina (Padre Pio), priest")
+ (la "S. Pii de Pietrelcina, presbyteri")))
+ (rank Memoria_obligatoria) (status Feast) (colour White)
+ (subject Saint) (citations ()) (layer of-universal)))))
+ )))
diff --git a/data/of/amendments/002-juan-diego-cuauhtlatoatzin.sexp b/data/of/amendments/002-juan-diego-cuauhtlatoatzin.sexp
new file mode 100644
index 0000000..0efca17
--- /dev/null
+++ b/data/of/amendments/002-juan-diego-cuauhtlatoatzin.sexp
@@ -0,0 +1,40 @@
+; data/of/amendments/002-juan-diego-cuauhtlatoatzin.sexp -- ADDS St Juan
+; Diego Cuauhtlatoatzin to the General Roman Calendar.
+;
+; Decree: "Reverentia atque cultus", Congregation for Divine Worship and
+; the Discipline of the Sacraments, 28 September 2002, Prot. n. 1828/02/L.
+; Signed Georgius A. Card. Medina Estevez (Praefectus) + Franciscus Pius
+; Tamburrino (Archiepiscopus a Secretis). A DIFFERENT protocol number from
+; 003-our-lady-of-guadalupe.sexp's own decree (Prot. N. 1622/02/L) --
+; issued the same day and published in the same Notitiae article, but two
+; separate juridical instruments, hence two separate files per this
+; project's own "exactly one decree per file" discipline.
+; Published: Notitiae 40 (2004), 196 (part of a combined article, pp.
+; 194-201). Absent from AAS 2002-2006 (checked directly).
+;
+; What it does: inscribes 9 December, memoria ad libitum, extending to the
+; universal calendar a devotion previously particular to Mexico (a 25
+; March 1999 CDW decree, Notitiae 35 (1999) 227-247, out of this
+; inventory's scope: it predates the 2002 baseline and was never
+; universal). Absent from the printed 2002 Missal's own Calendarium
+; (9 December is blank).
+;
+; `en`/`la`/slug/rank/colour/subject reused from lectio's own
+; roman-calendar.ini ([juan-diego], date 12-09, rank optional, class
+; saint, colour white) -- an engineering-key/cross-check reuse only, per
+; calendar-2002.sexp's own header discipline. `la` matches the decree's
+; own calendarium table row ("S. Ioannis Didaci Cuahtlatoatzin";
+; lectio's own transcription carries a stray "[*]" footnote marker not
+; present in the decree text itself, dropped here).
+((id of-amend-02-juan-diego)
+ (directives
+ ((Add
+ ((date (Fixed (month 12) (day 9)))
+ (cel
+ ((slug juan-diego)
+ (names
+ ((en "Saint Juan Diego")
+ (la "S. Ioannis Didaci Cuahtlatoatzin")))
+ (rank Memoria_ad_libitum) (status Feast) (colour White)
+ (subject Saint) (citations ()) (layer of-universal)))))
+ )))
diff --git a/data/of/amendments/003-our-lady-of-guadalupe.sexp b/data/of/amendments/003-our-lady-of-guadalupe.sexp
new file mode 100644
index 0000000..141e9b7
--- /dev/null
+++ b/data/of/amendments/003-our-lady-of-guadalupe.sexp
@@ -0,0 +1,41 @@
+; data/of/amendments/003-our-lady-of-guadalupe.sexp -- ADDS Our Lady of
+; Guadalupe to the General Roman Calendar (universal extension).
+;
+; Decree: "Beatam Mariam Virginem", Congregation for Divine Worship and
+; the Discipline of the Sacraments, 28 September 2002, Prot. N.
+; 1622/02/L. Same signatories, same day and same Notitiae article as
+; 002-juan-diego-cuauhtlatoatzin.sexp's own decree, but a DIFFERENT
+; protocol number -- a second, separate juridical instrument, hence its
+; own file.
+; Published: Notitiae 40 (2004), 197. Absent from AAS 2002-2006 (checked
+; directly).
+;
+; What it does: inscribes 12 December, memoria ad libitum, as a
+; UNIVERSAL entry -- this is the first General-Roman-Calendar action for
+; Guadalupe; a 25 March 1999 CDW decree had already published Spanish-
+; language texts for a particular-calendar Feast (Mexico and, per
+; Ecclesia in America, the Americas), which is why 12 December is still
+; blank in the 2002 typical edition's own printed Calendarium despite
+; already being kept locally at the time. That 1999 grant is out of this
+; inventory's scope (particular, not universal, and pre-dates the 2002
+; baseline).
+;
+; `en`/`la`/slug/rank/colour/subject reused from lectio's own
+; roman-calendar.ini ([our-lady-of-guadalupe], date 12-12, rank optional,
+; class bvm, colour white) -- an engineering-key/cross-check reuse only,
+; per calendar-2002.sexp's own header discipline. `la` matches the
+; decree's own calendarium table row ("Beatae Mariae Virginis de
+; Guadalupe"; lectio's own transcription carries a stray "[*]" footnote
+; marker not present in the decree text itself, dropped here).
+((id of-amend-03-guadalupe)
+ (directives
+ ((Add
+ ((date (Fixed (month 12) (day 12)))
+ (cel
+ ((slug our-lady-of-guadalupe)
+ (names
+ ((en "Our Lady of Guadalupe")
+ (la "Beatae Mariae Virginis de Guadalupe")))
+ (rank Memoria_ad_libitum) (status Feast) (colour White)
+ (subject Bvm) (citations ()) (layer of-universal)))))
+ )))
diff --git a/data/of/amendments/004-john-xxiii-john-paul-ii.sexp b/data/of/amendments/004-john-xxiii-john-paul-ii.sexp
new file mode 100644
index 0000000..2760313
--- /dev/null
+++ b/data/of/amendments/004-john-xxiii-john-paul-ii.sexp
@@ -0,0 +1,50 @@
+; data/of/amendments/004-john-xxiii-john-paul-ii.sexp -- ADDS St John XXIII
+; and St John Paul II to the General Roman Calendar (one decree, two
+; entries).
+;
+; Decree: 29 May 2014, Prot. N. 309/14. Signed Antonio Card. Canizares
+; Llovera (Praefectus) + Arthur Roche (Archiepiscopus a Secretis).
+; Following the joint canonization of 27 April 2014.
+;
+; Published: NOT FOUND IN AAS -- checked exhaustively (yearly indices
+; 2014-2024, and the four individual monthly AAS issues June-Sept 2014,
+; all read directly with no hit; docs/research/of/PROVENANCE-amendments.md
+; item 4 names this the weakest-sourced entry in the whole inventory).
+; Rests on the Vatican decree webpage alone:
+; http://www.vatican.va/roman_curia/congregations/ccdds/documents/rc_con_ccdds_doc_20140529_decreto-calendario-generale-gxxiii-gpii_en.html
+; -- re-fetched directly for this task (2026-08-26), which confirms the
+; operative sentence: "the Supreme Pontiff Francis... has decreed that
+; the celebrations of Pope Saint John XXIII and Pope Saint John Paul II
+; should be inserted into the General Roman Calendar, to be observed
+; annually with the rank of Optional Memorial, the first on 11 October,
+; the second on 22 October."
+;
+; What it does: two new memoria ad libitum entries, 11 October (John
+; XXIII) and 22 October (John Paul II).
+;
+; `en`/`la`/slug/rank/colour/subject all reused from lectio's own
+; roman-calendar.ini ([john-xxiii-pope] date 10-11, [john-paul-ii-pope]
+; date 10-22, both rank optional, class saint, colour white) -- an
+; engineering-key/cross-check reuse only, per calendar-2002.sexp's own
+; header discipline.
+((id of-amend-04-john-xxiii-john-paul-ii)
+ (directives
+ ((Add
+ ((date (Fixed (month 10) (day 11)))
+ (cel
+ ((slug john-xxiii-pope)
+ (names
+ ((en "Saint John XXIII, pope")
+ (la "S. Ioannis XXIII, papae")))
+ (rank Memoria_ad_libitum) (status Feast) (colour White)
+ (subject Saint) (citations ()) (layer of-universal)))))
+ (Add
+ ((date (Fixed (month 10) (day 22)))
+ (cel
+ ((slug john-paul-ii-pope)
+ (names
+ ((en "Saint John Paul II, pope")
+ (la "S. Ioannis Pauli II, papae")))
+ (rank Memoria_ad_libitum) (status Feast) (colour White)
+ (subject Saint) (citations ()) (layer of-universal)))))
+ )))
diff --git a/data/of/amendments/005-mary-magdalene-rank.sexp b/data/of/amendments/005-mary-magdalene-rank.sexp
new file mode 100644
index 0000000..e867d58
--- /dev/null
+++ b/data/of/amendments/005-mary-magdalene-rank.sexp
@@ -0,0 +1,49 @@
+; data/of/amendments/005-mary-magdalene-rank.sexp -- RANK CHANGE: St Mary
+; Magdalene, memoria -> festum.
+;
+; Decree: "Sanctae Mariae Magdalenae", Congregation for Divine Worship and
+; the Discipline of the Sacraments, 3 June 2016, Prot. n. 708/2015. Signed
+; Robertus Card. Sarah (Praefectus) alone.
+; Published: AAS 108 (2016), 798-799.
+;
+; What it does: the day and colour are unchanged (22 July, white); only the
+; grade changes. Verified directly against the decree's own operative
+; sentence (docs/research/of/aas-monthly/2016_luglio.pdf, line 3344-3345 of
+; the extracted transcript): "...celebrationem Sanctae Mariae Magdalenae
+; Calendario Romano generali posthac inscribendam esse gradu festi loco
+; memoriae, sicut nunc habetur" -- "the celebration of St Mary Magdalene is
+; henceforth to be inscribed in the General Roman Calendar with the rank of
+; FEAST IN PLACE OF MEMORIAL, as it now stands." The decree also adds a
+; proper Preface to the Missal; colitur's calendar data carries no Preface
+; field, so that half of the decree has no representation here.
+;
+; This is an EDIT, not an Add: the 2002 Missal already carries the entry
+; (data/of/calendar-2002.sexp's own "mary-magdalene", 22 July,
+; Memoria_obligatoria -- Task 1's own lectio cross-check flagged exactly
+; this rank disagreement and deliberately left it for this task). Editing
+; in place, rather than Suppress+Add under a new slug, keeps the same
+; entity's identity stable across the amendment for anything that may come
+; to reference it by slug (a future lectionary, an overlay, a template).
+;
+; Precedence_of interaction (checked, not merely assumed): Festum and
+; Memoria_obligatoria are both NON-Sollemnitas ranks, so this edit does not
+; cross the Sollemnitas boundary -- Precedence_of.disposition's losing-case
+; answer for this candidate stays Omit either way (only a losing Sollemnitas
+; is ever Transferred). Precedence_of.band does move this candidate from
+; Tabula III.10 (band 100) to Tabula II.7 (band 70) -- a real precedence
+; strengthening on paper, but with ZERO effect on which day is OBSERVED
+; under the shipped universal calendar: 22 July carries no other
+; universal-calendar entry (verified -- grep of calendar-2002.sexp finds
+; exactly one (month 7) (day 22) block), so Mary Magdalene already won that
+; date outright at band 100 and continues to at band 70; an ordinary per
+; annum Sunday still outranks her either way (band 60 < 70 < 100 -- a
+; Festum of a SAINT, Tabula II.7, is not a Festum OF THE LORD, Tabula II.5
+; band 50, the only Festum grade that would beat a Sunday). The change
+; becomes live only if a future PROPER (diocesan/religious) overlay adds a
+; band-80-or-lower competitor to 22 July, or if a future obligatory
+; memorial elsewhere ties her old band-100 slot -- neither exists in the
+; shipped data today.
+((id of-amend-05-mary-magdalene)
+ (directives
+ ((Edit mary-magdalene ((Set_rank Festum)))
+ )))
diff --git a/data/of/amendments/006-mary-mother-of-the-church.sexp b/data/of/amendments/006-mary-mother-of-the-church.sexp
new file mode 100644
index 0000000..8fc6d64
--- /dev/null
+++ b/data/of/amendments/006-mary-mother-of-the-church.sexp
@@ -0,0 +1,76 @@
+; data/of/amendments/006-mary-mother-of-the-church.sexp -- ADDS the
+; Blessed Virgin Mary, Mother of the Church, to the General Roman
+; Calendar. The one genuinely MOVABLE amendment in this inventory.
+;
+; Decree: Congregation for Divine Worship and the Discipline of the
+; Sacraments, 11 February 2018, Prot. N. 1037/2017. Signed Robertus Card.
+; Sarah (Praefectus) + Arturus Roche (Archiepiscopus a Secretis).
+; Published: AAS 110 (2018), 437-438 (decree text continues past 438;
+; index cites 437 as the start page).
+;
+; What it does, verified directly against the decree's own operative
+; sentence (docs/research/of/aas-monthly/2018_marzo.pdf, line 2434-2437 of
+; the extracted transcript): "Summus autem Pontifex Franciscus... decrevit
+; ut memoriam B. Mariae Virginis, Ecclesiae Matris, in Calendarium
+; Romanum inscribendam esse Feria II post Pentecosten et quotannis
+; celebrandam" -- inscribes a MEMORIA (obligatory: no "ad libitum"
+; qualifier anywhere in the operative sentence) on "Feria II post
+; Pentecosten", the MONDAY AFTER PENTECOST.
+;
+; Date arithmetic, verified rather than trusted: Pentecost is Easter+49
+; (the 50th day of the Paschal season inclusive of Easter itself), so the
+; following Monday is Easter+50. Date_spec.Easter_offset 50 is exactly
+; this; kernel-level and rite-agnostic (built for EF's Rogation
+; Wednesday), no code change needed. See test_amendments_of.ml's own
+; test_mater_ecclesiae_monday_after_pentecost, which re-derives the
+; calendar date from Colitur_kernel.Computus.gregorian_easter in three
+; real years rather than trusting this offset's own arithmetic.
+;
+; The day carries no other GRC celebration to displace on any FIXED date
+; -- but the decree's own destination is a MOVABLE one, so this is not
+; quite true every year: see the finding recorded below.
+;
+; `la` name taken directly from the decree's own text ("B. Mariae
+; Virginis, Ecclesiae Matris", expanded here to "Beatae Mariae Virginis,
+; Ecclesiae Matris" to match calendar-2002.sexp's own convention of
+; writing out abbreviations in `la` names); `en` is this task's own
+; translation (no lectio entry exists for this decree -- it postdates
+; lectio's own last GRC sync).
+;
+; *** FINDING, worth surfacing rather than silently shipping: a real,
+; unresolved Precedence_of interaction. *** Easter+50 ranges over 11 May
+; - 14 June across the Gregorian Easter window (22 March - 25 April).
+; Several FIXED Memoria_obligatoria entries already sit inside that
+; window in data/of/calendar-2002.sexp: 26 May (Philip Neri), 1 June
+; (Justin Martyr), 3 June (Charles Lwanga & companions), 5 June
+; (Boniface), 11 June (Barnabas), 13 June (Anthony of Padua). Verified
+; computationally (Meeus/Jones/Butcher Gregorian Easter, 1583-2100) that
+; Easter+50 DOES land on each of those dates in real years -- e.g. 2011
+; (Easter 24 April) puts Mary, Mother of the Church squarely on 13 June,
+; Anthony of Padua's own day. On such a year both candidates band at
+; Tabula III.10 (100, both universal Memoria_obligatoria), a genuine tie
+; Precedence_of.band does not and cannot break: Precedence.resolve's own
+; tie-break (Slug.compare) then picks whichever slug sorts first
+; alphabetically as the sole OBSERVED day, silently omitting the other
+; for that year -- not a crash, not a diagnostic, just a deterministic
+; but liturgically arbitrary pick. Normae universales n. 14's own text
+; (docs/research/of/normae-universales-2002-transcription.md) resolves the
+; AD-LIBITUM/AD-LIBITUM case explicitly ("una tantum celebrari potest,
+; omissis ceteris") but is silent on two OBLIGATORY memorials colliding;
+; pastoral practice generally reduces both to ad libitum for that one
+; year, which Precedence_of.ml implements nowhere. Recorded here as a
+; real, load-bearing gap -- not fixed, out of this data-only task's own
+; scope, and not unique to this entry (any future obligatory-memorial
+; overlay entry on a fixed May/June date has the identical exposure).
+((id of-amend-06-mary-mother-of-the-church)
+ (directives
+ ((Add
+ ((date (Easter_offset 50))
+ (cel
+ ((slug mary-mother-of-the-church)
+ (names
+ ((en "The Blessed Virgin Mary, Mother of the Church")
+ (la "Beatae Mariae Virginis, Ecclesiae Matris")))
+ (rank Memoria_obligatoria) (status Feast) (colour White)
+ (subject Bvm) (citations ()) (layer of-universal)))))
+ )))
diff --git a/data/of/amendments/007-paul-vi.sexp b/data/of/amendments/007-paul-vi.sexp
new file mode 100644
index 0000000..1342e3b
--- /dev/null
+++ b/data/of/amendments/007-paul-vi.sexp
@@ -0,0 +1,31 @@
+; data/of/amendments/007-paul-vi.sexp -- ADDS St Paul VI, pope, to the
+; General Roman Calendar.
+;
+; Decree: Congregation for Divine Worship and the Discipline of the
+; Sacraments, 25 January 2019. Signed Robertus Card. Sarah (Praefectus).
+; (Protocol number not visible in the extracted text near the signature
+; block -- not chased further, the AAS citation is unambiguous.)
+; Published: AAS 111 (2019), 212-213.
+;
+; What it does: inscribes 29 May, memoria ad libitum. Verified directly
+; against the decree's own calendarium table row
+; (docs/research/of/aas-monthly/2019_febbraio.pdf, line 3888): "...
+; celebrationem sancti Pauli VI, papae, die 29 [maii]..." -- title "S.
+; Pauli VI, papae".
+;
+; No lectio entry exists for this decree; `en`/`la` are this task's own
+; rendering, following the decree's own Latin title and
+; calendar-2002.sexp's own convention for other popes (e.g.
+; "john-xxiii-pope").
+((id of-amend-07-paul-vi)
+ (directives
+ ((Add
+ ((date (Fixed (month 5) (day 29)))
+ (cel
+ ((slug paul-vi-pope)
+ (names
+ ((en "Saint Paul VI, pope")
+ (la "S. Pauli VI, papae")))
+ (rank Memoria_ad_libitum) (status Feast) (colour White)
+ (subject Saint) (citations ()) (layer of-universal)))))
+ )))
diff --git a/data/of/amendments/008-our-lady-of-loreto.sexp b/data/of/amendments/008-our-lady-of-loreto.sexp
new file mode 100644
index 0000000..1ddcb91
--- /dev/null
+++ b/data/of/amendments/008-our-lady-of-loreto.sexp
@@ -0,0 +1,27 @@
+; data/of/amendments/008-our-lady-of-loreto.sexp -- ADDS Our Lady of
+; Loreto to the General Roman Calendar.
+;
+; Decree: Congregation for Divine Worship and the Discipline of the
+; Sacraments, 7 October 2019, Prot. N. 404/19. Signed Robertus Card. Sarah
+; (Praefectus) + Arturus Roche (Archiepiscopus a Secretis).
+; Published: AAS 111 (2019), 1789-1790.
+;
+; What it does: inscribes 10 December, memoria ad libitum. Verified
+; directly against the decree's own annexed calendarium table row
+; (docs/research/of/aas-monthly/2019_novembre.pdf, line 4718): "10 B.
+; Mariae Virginis de Loreto".
+;
+; No lectio entry exists for this decree; `en`/`la` are this task's own
+; rendering, `la` matching the calendarium row above verbatim.
+((id of-amend-08-our-lady-of-loreto)
+ (directives
+ ((Add
+ ((date (Fixed (month 12) (day 10)))
+ (cel
+ ((slug our-lady-of-loreto)
+ (names
+ ((en "Our Lady of Loreto")
+ (la "B. Mariae Virginis de Loreto")))
+ (rank Memoria_ad_libitum) (status Feast) (colour White)
+ (subject Bvm) (citations ()) (layer of-universal)))))
+ )))
diff --git a/data/of/amendments/009-faustina-kowalska.sexp b/data/of/amendments/009-faustina-kowalska.sexp
new file mode 100644
index 0000000..5d302cf
--- /dev/null
+++ b/data/of/amendments/009-faustina-kowalska.sexp
@@ -0,0 +1,34 @@
+; data/of/amendments/009-faustina-kowalska.sexp -- ADDS St Faustina
+; Kowalska, virgin, to the General Roman Calendar.
+;
+; Decree: Congregation for Divine Worship and the Discipline of the
+; Sacraments, 18 May 2020, Prot. N. 229/20. Signed Robertus Card. Sarah
+; (Praefectus) + Arturus Roche (Archiepiscopus a Secretis).
+; Published: AAS 112 (2020), 595-596.
+;
+; What it does: inscribes 5 October, memoria ad libitum. Verified directly
+; against the decree's own calendarium table row
+; (docs/research/of/aas-monthly/2020_giugno.pdf, line 4114): "5 S.
+; Faustinae Kowalska, virginis".
+;
+; A lead that did NOT hold up (docs/research/of/PROVENANCE-amendments.md
+; item 9): a devotional website claims an April 2005 addition; this
+; decree's own text unambiguously has Pope Francis deciding the insertion
+; in 2020, contradicting that claim, which most likely conflates a
+; particular-calendar action with this universal one. Recorded to explain
+; why this file's date is 2020, not 2005.
+;
+; No lectio entry exists for this decree; `en`/`la` are this task's own
+; rendering, `la` matching the calendarium row above verbatim.
+((id of-amend-09-faustina-kowalska)
+ (directives
+ ((Add
+ ((date (Fixed (month 10) (day 5)))
+ (cel
+ ((slug faustina-kowalska-virgin)
+ (names
+ ((en "Saint Faustina Kowalska, virgin")
+ (la "S. Faustinae Kowalska, virginis")))
+ (rank Memoria_ad_libitum) (status Feast) (colour White)
+ (subject Saint) (citations ()) (layer of-universal)))))
+ )))
diff --git a/data/of/amendments/010-narek-avila-hildegard.sexp b/data/of/amendments/010-narek-avila-hildegard.sexp
new file mode 100644
index 0000000..378d3ba
--- /dev/null
+++ b/data/of/amendments/010-narek-avila-hildegard.sexp
@@ -0,0 +1,76 @@
+; data/of/amendments/010-narek-avila-hildegard.sexp -- ADDS three Doctors
+; of the Church to the General Roman Calendar: St Gregory of Narek, St
+; John of Avila, St Hildegard of Bingen. ONE decree, three entries --
+; docs/research/of/PROVENANCE-amendments.md's own items 10-12.
+;
+; Decree: Congregation for Divine Worship and the Discipline of the
+; Sacraments, 25 January 2021, Prot. N. 40/21. Signed Robertus Card. Sarah
+; (Praefectus) + Arturus Roche (Archiepiscopus a Secretis).
+; Published: AAS 113 (2021), 225-226.
+;
+; What it does: inscribes three memoria-ad-libitum entries -- 27 February
+; (Narek), 10 May (Avila), 17 September (Hildegard). All three were
+; declared Doctors of the Church years earlier (Avila/Hildegard 7 October
+; 2012, Narek 21 February 2015); the GRC inscription followed 6-9 years
+; later. Verified directly against the decree's own calendarium table
+; rows (docs/research/of/aas-monthly/2021_febbraio.pdf):
+; line 2170: "27 S. Gregorii Narecensis, abbatis et Ecclesiae doctoris"
+; line 2300: "10 S. Ioannis De Avila, presbyteri et Ecclesiae doctoris"
+; line 2432: "17 S. Hildegardis Bingensis, virginis et Ecclesiae
+; doctoris"
+;
+; No lectio entries exist for this decree; `en`/`la` are this task's own
+; rendering, `la` matching the calendarium rows above verbatim.
+;
+; *** FINDING: 17 September already carries a universal entry ***
+; (calendar-2002.sexp's own "robert-bellarmine-bishop-and-doctor",
+; Memoria_ad_libitum, White) -- verified: `grep '(month 9) (day 17)'`
+; finds exactly those two blocks once this file is applied. Both are
+; ordinary optional memorials, so Normae universales n. 14's own text
+; ("Si eodem die plures inscribuntur in calendario memoriae ad libitum,
+; una tantum celebrari potest, omissis ceteris" -- if several optional
+; memorials are inscribed on the same day, only one may be celebrated,
+; the rest omitted) governs correctly and is NOT a defect. But
+; Precedence_of's tie-break for two same-band candidates is
+; Slug.compare, and "hildegard-of-bingen-virgin-and-doctor" sorts BEFORE
+; "robert-bellarmine-bishop-and-doctor" -- so applying this overlay
+; changes which candidate `Precedence.resolve` reports as OBSERVED on
+; every 17 September, from Robert Bellarmine to Hildegard. Both are
+; legitimate choices under n. 14 (the celebrant elects either), but
+; colitur's single-observed-day model cannot express "elective": it
+; reports one deterministically, and this file changes which one. Not a
+; new architectural gap -- 9 October already carries two ad-libitum
+; entries in the shipped 2002 base (Denis & companions, John Leonardi)
+; with the identical shape, undisturbed here (see
+; 013-john-henry-newman.sexp's own note: adding a third candidate there
+; leaves Denis, already alphabetically first, unchanged as observed).
+((id of-amend-10-narek-avila-hildegard)
+ (directives
+ ((Add
+ ((date (Fixed (month 2) (day 27)))
+ (cel
+ ((slug gregory-of-narek-abbot-and-doctor)
+ (names
+ ((en "Saint Gregory of Narek, abbot and doctor")
+ (la "S. Gregorii Narecensis, abbatis et Ecclesiae doctoris")))
+ (rank Memoria_ad_libitum) (status Feast) (colour White)
+ (subject Saint) (citations ()) (layer of-universal)))))
+ (Add
+ ((date (Fixed (month 5) (day 10)))
+ (cel
+ ((slug john-of-avila-priest-and-doctor)
+ (names
+ ((en "Saint John of Avila, priest and doctor")
+ (la "S. Ioannis De Avila, presbyteri et Ecclesiae doctoris")))
+ (rank Memoria_ad_libitum) (status Feast) (colour White)
+ (subject Saint) (citations ()) (layer of-universal)))))
+ (Add
+ ((date (Fixed (month 9) (day 17)))
+ (cel
+ ((slug hildegard-of-bingen-virgin-and-doctor)
+ (names
+ ((en "Saint Hildegard of Bingen, virgin and doctor")
+ (la "S. Hildegardis Bingensis, virginis et Ecclesiae doctoris")))
+ (rank Memoria_ad_libitum) (status Feast) (colour White)
+ (subject Saint) (citations ()) (layer of-universal)))))
+ )))
diff --git a/data/of/amendments/011-martha-mary-lazarus.sexp b/data/of/amendments/011-martha-mary-lazarus.sexp
new file mode 100644
index 0000000..b9365ca
--- /dev/null
+++ b/data/of/amendments/011-martha-mary-lazarus.sexp
@@ -0,0 +1,38 @@
+; data/of/amendments/011-martha-mary-lazarus.sexp -- RENAME / scope-widen:
+; the existing 29 July obligatory memorial of St Martha alone becomes
+; Sts Martha, Mary and Lazarus.
+;
+; Decree: Congregation for Divine Worship and the Discipline of the
+; Sacraments, 26 January 2021, Prot. N. 35/21. Signed Robertus Card. Sarah
+; (Praefectus) + Arturus Roche (Archiepiscopus a Secretis).
+; Published: AAS 113 (2021), 236-237.
+;
+; What it does, verified directly against the decree's own text and its
+; annexed calendarium table row (docs/research/of/aas-monthly/
+; 2021_febbraio.pdf): "Dicasterii propositis, disposuit ut memoria
+; sanctorum Marthae, Mariae et Lazari in Calendario Romano generali die 29
+; iulii inscriberetur" (line 2576-2577); the table row itself (line 2604):
+; "29 Ss. Marthae, Mariae et Lazari ... Memoria". Date and rank are
+; UNCHANGED (29 July, memoria obligatoria, inherited from the existing
+; entry, not newly stated); only the celebration's NAME (and its proper
+; texts, out of colitur's scope) changes, resolving a long Latin-tradition
+; ambiguity over "which Mary" that the reformed Roman Martyrology had
+; already settled.
+;
+; DIRECTIVE CHOICE: Edit, not Suppress+Add. This is the SAME entity
+; continuing to exist under the SAME date/rank/colour/subject -- only its
+; display name widens. Edit's Set_name field-edit expresses exactly a
+; rename without disturbing anything else, and keeps the existing slug
+; ("martha") stable for any future overlay or lectionary entry that might
+; already key off it -- a Suppress+Add under a new slug would force every
+; such reference to be rewritten for no liturgical reason (the decree
+; itself treats this as a continuation of the same memorial, "memoria...
+; inscriberetur" on the SAME date the memorial already occupied, not a
+; suppression of one and creation of another). Rank/colour/subject/status
+; are deliberately NOT touched by this Edit -- they were already correct.
+((id of-amend-11-martha-mary-lazarus)
+ (directives
+ ((Edit martha
+ ((Set_name en "Saints Martha, Mary and Lazarus")
+ (Set_name la "Ss. Marthae, Mariae et Lazari")))
+ )))
diff --git a/data/of/amendments/012-teresa-of-calcutta.sexp b/data/of/amendments/012-teresa-of-calcutta.sexp
new file mode 100644
index 0000000..e8ca63c
--- /dev/null
+++ b/data/of/amendments/012-teresa-of-calcutta.sexp
@@ -0,0 +1,30 @@
+; data/of/amendments/012-teresa-of-calcutta.sexp -- ADDS St Teresa of
+; Calcutta (Mother Teresa), virgin, to the General Roman Calendar.
+;
+; Decree: Dicastery for Divine Worship and the Discipline of the
+; Sacraments, 24 December 2024, Prot. N. 703/24. Signed Arturus Card.
+; Roche (Praefectus -- the Dicastery renamed in 2022) + Victorius
+; Franciscus Viola, O.F.M. (Archiepiscopus a Secretis). First publicised
+; via a Vatican press bulletin (11 February 2025), ahead of AAS.
+; Published: AAS 117 (2025), 256-257 -- found directly in the March 2025
+; monthly issue; no compiled 2025 index yet exists.
+;
+; What it does: inscribes 5 September, memoria ad libitum. Verified
+; directly against the decree's own calendarium table row
+; (docs/research/of/aas-monthly-2025/marzo.pdf, line 1280): "5 S.
+; Teresiae de Calcutta, virginis".
+;
+; No lectio entry exists for this decree; `en`/`la` are this task's own
+; rendering, `la` matching the calendarium row above verbatim.
+((id of-amend-12-teresa-of-calcutta)
+ (directives
+ ((Add
+ ((date (Fixed (month 9) (day 5)))
+ (cel
+ ((slug teresa-of-calcutta-virgin)
+ (names
+ ((en "Saint Teresa of Calcutta, virgin")
+ (la "S. Teresiae de Calcutta, virginis")))
+ (rank Memoria_ad_libitum) (status Feast) (colour White)
+ (subject Saint) (citations ()) (layer of-universal)))))
+ )))
diff --git a/data/of/amendments/013-john-henry-newman.sexp b/data/of/amendments/013-john-henry-newman.sexp
new file mode 100644
index 0000000..043f09a
--- /dev/null
+++ b/data/of/amendments/013-john-henry-newman.sexp
@@ -0,0 +1,47 @@
+; data/of/amendments/013-john-henry-newman.sexp -- ADDS St John Henry
+; Newman, priest and doctor, to the General Roman Calendar. The most
+; recent amendment in this inventory.
+;
+; Decree: Dicastery for Divine Worship and the Discipline of the
+; Sacraments, 9 November 2025, Prot. N. 760/25. Signed Arturus Card.
+; Roche (Praefectus) + Victorius Franciscus Viola. Made public via
+; Vatican commentary/press on 3 February 2026 -- under Pope Leo XIV, per
+; the decree's own preamble, the first entry in this whole inventory
+; issued under a different pope from Francis.
+; Published: AAS 118 (2026), 260-262 -- the single most recent item in
+; the inventory, found in the February 2026 monthly issue.
+;
+; What it does: inscribes 9 October, memoria ad libitum. Verified
+; directly against the decree's own calendarium table row
+; (docs/research/of/aas-monthly-2026/febbraio.pdf, line 4915): "9 S.
+; Ioannis Henrici Newman, presbyteri et Ecclesiae doctoris". Newman was
+; never ordained a bishop (he was made a Cardinal-Deacon), hence
+; "presbyteri", priest, not "episcopi".
+;
+; No lectio entry exists for this decree; `en`/`la` are this task's own
+; rendering, `la` matching the calendarium row above verbatim.
+;
+; *** NOTE: 9 October already carries TWO universal entries ***
+; (calendar-2002.sexp's own "denis-and-companions-martyrs" and
+; "john-leonardi-priest", both Memoria_ad_libitum) -- this is not a new
+; situation this file creates (Task 1's own base data already shipped two
+; ad-libitum candidates sharing that date under Normae n. 14's "one may
+; be celebrated" rule) and does not change what `Precedence.resolve`
+; reports as observed: alphabetically, "denis-and-companions-martyrs" <
+; "john-henry-newman-priest-and-doctor" < "john-leonardi-priest", so
+; Denis remains the reported observed day both before and after this
+; overlay applies. Contrast 010-narek-avila-hildegard.sexp's own 17
+; September finding, where the equivalent addition DOES flip the
+; alphabetical winner.
+((id of-amend-13-john-henry-newman)
+ (directives
+ ((Add
+ ((date (Fixed (month 10) (day 9)))
+ (cel
+ ((slug john-henry-newman-priest-and-doctor)
+ (names
+ ((en "Saint John Henry Newman, priest and doctor")
+ (la "S. Ioannis Henrici Newman, presbyteri et Ecclesiae doctoris")))
+ (rank Memoria_ad_libitum) (status Feast) (colour White)
+ (subject Saint) (citations ()) (layer of-universal)))))
+ )))
diff --git a/test/dune b/test/dune
index 9af9900..c5bfe07 100644
--- a/test/dune
+++ b/test/dune
@@ -5,6 +5,19 @@
../data/ef/sanctoral.sexp
../data/ef/adjustments.sexp
../data/of/calendar-2002.sexp
+ ../data/of/amendments/001-padre-pio.sexp
+ ../data/of/amendments/002-juan-diego-cuauhtlatoatzin.sexp
+ ../data/of/amendments/003-our-lady-of-guadalupe.sexp
+ ../data/of/amendments/004-john-xxiii-john-paul-ii.sexp
+ ../data/of/amendments/005-mary-magdalene-rank.sexp
+ ../data/of/amendments/006-mary-mother-of-the-church.sexp
+ ../data/of/amendments/007-paul-vi.sexp
+ ../data/of/amendments/008-our-lady-of-loreto.sexp
+ ../data/of/amendments/009-faustina-kowalska.sexp
+ ../data/of/amendments/010-narek-avila-hildegard.sexp
+ ../data/of/amendments/011-martha-mary-lazarus.sexp
+ ../data/of/amendments/012-teresa-of-calcutta.sexp
+ ../data/of/amendments/013-john-henry-newman.sexp
../lang/la.ini
../lang/en.ini
../lang/traditions.ini
diff --git a/test/test_amendments_of.ml b/test/test_amendments_of.ml
new file mode 100644
index 0000000..1ed21d9
--- /dev/null
+++ b/test/test_amendments_of.ml
@@ -0,0 +1,217 @@
+(* Task 2 (2026-08-25-colitur-of-phases-3-5): the post-2002 decree overlays.
+ data/of/calendar-2002.sexp (Task 1) transcribes the 2002 typical edition
+ and is never edited again -- every subsequent Calendarium Romanum Generale
+ change is its own overlay file under data/of/amendments/, one per decree,
+ per docs/research/of/PROVENANCE-amendments.md (13 decrees, 16
+ celebration-level changes -- the research inventory's own "15 items,
+ numbered 1-15, where items 10-12 are three entries carried by a single
+ decree" collapses to 13 distinct decrees once items 2/3, both dated 28
+ September 2002 but carrying DIFFERENT protocol numbers, are counted as the
+ two separate juridical instruments they are).
+
+ This suite asserts: every file parses; each has a pinned SHA-256;
+ applying all 13 in their own decree-chronological order to the base
+ calendar yields a pinned entry count; Overlay.merge reports NO diagnostic
+ (a diagnostic would mean a directive was transcribed against a slug that
+ does not exist in the base -- the exact silent failure one-file-per-decree
+ exists to prevent); and Mary, Mother of the Church resolves to the Monday
+ after Pentecost in three real, independently-computed years. *)
+
+module L = Colitur_kernel.Layer
+module O = Colitur_kernel.Overlay
+module Cel = Colitur_kernel.Celebration
+module S = Colitur_kernel.Slug
+module DS = Colitur_kernel.Date_spec
+module D = Colitur_kernel.Date
+module Computus = Colitur_kernel.Computus
+module Names = Colitur_kernel.Names
+module Lang = Colitur_kernel.Lang
+module V = Rite_of.Vocab_of
+
+let base_path = "../data/of/calendar-2002.sexp"
+let amendments_dir = "../data/of/amendments/"
+
+(* Decree-chronological order, matching docs/research/of/
+ PROVENANCE-amendments.md's own "Ordered by decree date" -- the numeric
+ filename prefixes already encode this, spelled out here explicitly so the
+ intended application order is visible in the test itself, not merely
+ inferred from a directory listing. Each paired with its SHA-256, pinned
+ 2026-08-26 against the shipped files themselves (`sha256sum
+ data/of/amendments/*.sexp`), not copied from any tool's own stdout. *)
+let files =
+ [ ("001-padre-pio.sexp",
+ "8ddd92f1820a736c7a4f3c27b3181579d6280ed82f0983f80219d523782731f6");
+ ("002-juan-diego-cuauhtlatoatzin.sexp",
+ "01000fe5615894ff82b89a099e1539d6556beccfe1d07d5664362b0126724b09");
+ ("003-our-lady-of-guadalupe.sexp",
+ "a19100769786d110d462805cc4533867c9d56308f8a058f46b257b2b0d6cb3ef");
+ ("004-john-xxiii-john-paul-ii.sexp",
+ "ae24ac41935600d0d11a81072bedce2ba9790b79e2f8192c5d4a08a9a7db4e6e");
+ ("005-mary-magdalene-rank.sexp",
+ "08a19050c21391ee565bc4d5a3ba228e3162875188db4bbc3b7f4b066a60a0c1");
+ ("006-mary-mother-of-the-church.sexp",
+ "8a1dda10c1014bc59c22779f3686c6cce0bf9ff1ec4e504cb912cf2c89319ff9");
+ ("007-paul-vi.sexp",
+ "d7b442b6b6dd6c8e7aca5f7126f7b3e65104191ae84c158e07e9afd0f70edcfc");
+ ("008-our-lady-of-loreto.sexp",
+ "772366fb6c04d1c779109d8b56fa1c0a71ff0129f92349564e1a353b253c2e13");
+ ("009-faustina-kowalska.sexp",
+ "a6bf8a1ab8e3f4aa65bb22d4ff9ba863e5f65865180444f4b143f36aa99e45b3");
+ ("010-narek-avila-hildegard.sexp",
+ "6ca11a7c2e02491b9b96349282c7b1d6fc28c9e4729f678bd54d11a70e43f568");
+ ("011-martha-mary-lazarus.sexp",
+ "c4ede10b24f22def5fd557a3627d1ecaeb5de583f263372a67bccf9ab838f023");
+ ("012-teresa-of-calcutta.sexp",
+ "5646597472f6170a2b56e1f1c4a2ef7e7917cb5c87eb5e3d1f98b8ee1aaae646");
+ ("013-john-henry-newman.sexp",
+ "f6c12d1ead043a609b0086a130ae1a9fc680a8926fe77ccd97756ee747105d7c") ]
+
+let sha256_of_file path =
+ let tmp = Filename.temp_file "colitur_amendments_of_sha256" ".txt" in
+ let cmd = Printf.sprintf "sha256sum %s > %s" (Filename.quote path) (Filename.quote tmp) in
+ (match Sys.command cmd with
+ | 0 -> ()
+ | n -> Alcotest.failf "sha256sum exited %d" n);
+ let ic = open_in tmp in
+ let line = input_line ic in
+ close_in ic;
+ Sys.remove tmp;
+ match String.split_on_char ' ' line with
+ | hash :: _ -> hash
+ | [] -> Alcotest.fail "sha256sum produced no output"
+
+let load_overlay name =
+ let path = amendments_dir ^ name in
+ match O.load V.rank_of_sexp path with
+ | Ok o -> o
+ | Error e -> Alcotest.failf "%s: failed to load: %s" path e
+
+let load_base () =
+ match L.load V.rank_of_sexp base_path with
+ | Ok l -> l
+ | Error e -> Alcotest.failf "%s: failed to load: %s" base_path e
+
+(* Every file parses. *)
+let test_files_parse () =
+ List.iter
+ (fun (name, _) ->
+ let (_ : V.rank O.t) = load_overlay name in
+ ())
+ files
+
+(* Each has a pinned SHA-256. *)
+let test_sha256_pinned () =
+ List.iter
+ (fun (name, expected) ->
+ Alcotest.(check string) (name ^ " SHA-256") expected
+ (sha256_of_file (amendments_dir ^ name)))
+ files
+
+(* Applying all 13, in decree-chronological order, to the base calendar
+ yields a pinned entry count, AND Overlay.merge reports no diagnostic --
+ the silent-no-op check the whole one-file-per-decree discipline exists
+ to enforce (a diagnostic here would mean a directive was transcribed
+ against a slug the base calendar does not actually have). *)
+let test_apply_in_order () =
+ let base = load_base () in
+ let overlays = List.map (fun (name, _) -> load_overlay name) files in
+ let merged, diagnostics = O.merge base overlays in
+ Alcotest.(check (list string)) "no diagnostics -- every directive matched something" []
+ (List.map O.diagnostic_to_string diagnostics);
+ (* 208 base entries + 14 Adds (1+1+1+2+1+1+1+1+3+1+1, files
+ 001/002/003/004/006/007/008/009/010x3/012/013) + 2 Edits (005, 011,
+ no count change) = 222. *)
+ Alcotest.(check int) "208 base + 14 adds = 222 entries" 222 (List.length merged.L.entries)
+
+(* Every new/edited slug this task introduces is actually present after
+ the merge, and the two Edits did not accidentally become Adds (no
+ "slug not present; edit ignored" diagnostic already ruled that out
+ above, but a direct spot-check on the merged layer is cheap insurance
+ against a slug typo that happened to also dodge the diagnostic, e.g. by
+ matching a DIFFERENT, wrong existing slug). *)
+let test_new_slugs_present () =
+ let base = load_base () in
+ let overlays = List.map (fun (name, _) -> load_overlay name) files in
+ let merged, _ = O.merge base overlays in
+ let has slug = L.mem merged (S.of_string_exn slug) in
+ List.iter
+ (fun slug -> Alcotest.(check bool) (slug ^ " present after merge") true (has slug))
+ [ "pio-of-pietrelcina-padre-pio-priest"; "juan-diego"; "our-lady-of-guadalupe";
+ "john-xxiii-pope"; "john-paul-ii-pope"; "mary-mother-of-the-church"; "paul-vi-pope";
+ "our-lady-of-loreto"; "faustina-kowalska-virgin"; "gregory-of-narek-abbot-and-doctor";
+ "john-of-avila-priest-and-doctor"; "hildegard-of-bingen-virgin-and-doctor";
+ "teresa-of-calcutta-virgin"; "john-henry-newman-priest-and-doctor" ];
+ (* The two Edits kept their original slugs. *)
+ match L.find merged (S.of_string_exn "mary-magdalene") with
+ | None -> Alcotest.fail "mary-magdalene: slug lost after Edit"
+ | Some e -> Alcotest.(check bool) "mary-magdalene is now Festum" true (e.L.cel.Cel.rank = V.Festum)
+
+let test_martha_mary_lazarus_renamed () =
+ let base = load_base () in
+ let overlays = List.map (fun (name, _) -> load_overlay name) files in
+ let merged, _ = O.merge base overlays in
+ match L.find merged (S.of_string_exn "martha") with
+ | None -> Alcotest.fail "martha: slug lost after Edit"
+ | Some e ->
+ Alcotest.(check (option string)) "en name widened" (Some "Saints Martha, Mary and Lazarus")
+ (Names.find e.L.cel.Cel.names (Lang.of_string_exn "en"));
+ Alcotest.(check bool) "date unchanged, 29 July" true
+ (e.L.date = (match DS.fixed ~month:7 ~day:29 with Ok d -> d | Error err -> failwith err));
+ Alcotest.(check bool) "rank unchanged, Memoria_obligatoria" true
+ (e.L.cel.Cel.rank = V.Memoria_obligatoria)
+
+(* Mary, Mother of the Church resolves to the Monday after Pentecost --
+ Easter+50 -- in three real, independently-computed years, not merely
+ trusted from the decree's own arithmetic. Pentecost is Easter+49 (the
+ 50th day of the Paschal season inclusive of Easter); the day after is
+ Easter+50. Checked structurally (weekday = Mon, and the resolved date is
+ exactly 50 days after that year's real Gregorian Easter, computed via
+ Colitur_kernel.Computus.gregorian_easter), not by hand-typing expected
+ calendar dates. *)
+let test_mater_ecclesiae_monday_after_pentecost () =
+ let base = load_base () in
+ let overlays = List.map (fun (name, _) -> load_overlay name) files in
+ let merged, _ = O.merge base overlays in
+ let entry =
+ match L.find merged (S.of_string_exn "mary-mother-of-the-church") with
+ | Some e -> e
+ | None -> Alcotest.fail "mary-mother-of-the-church: not found after merge"
+ in
+ Alcotest.(check bool) "date spec is Easter_offset 50" true (entry.L.date = DS.Easter_offset 50);
+ Alcotest.(check bool) "Memoria_obligatoria" true (entry.L.cel.Cel.rank = V.Memoria_obligatoria);
+ Alcotest.(check bool) "white" true (entry.L.cel.Cel.colour = Colitur_kernel.Colour.White);
+ Alcotest.(check bool) "subject Bvm" true (entry.L.cel.Cel.subject = Colitur_kernel.Subject.Bvm);
+ List.iter
+ (fun year ->
+ let easter = Computus.gregorian_easter year in
+ let pentecost = D.add_days easter 49 in
+ let expected = D.add_days easter 50 in
+ Alcotest.(check bool)
+ (Printf.sprintf "%d: Pentecost (Easter+49) is a Sunday" year)
+ true (D.weekday pentecost = D.Sun);
+ let resolved =
+ match DS.resolve entry.L.date ~year ~easter with
+ | Some d -> d
+ | None -> Alcotest.failf "%d: Easter_offset 50 did not resolve" year
+ in
+ Alcotest.(check bool)
+ (Printf.sprintf "%d: resolves to exactly Easter+50" year) true (resolved = expected);
+ Alcotest.(check bool)
+ (Printf.sprintf "%d: resolved date is a Monday" year) true (D.weekday resolved = D.Mon);
+ Alcotest.(check bool)
+ (Printf.sprintf "%d: resolved date is the day after Pentecost" year) true
+ (resolved = D.add_days pentecost 1))
+ [ 2026; 2027; 2035 ]
+
+let suite =
+ ( "Amendments_of (data/of/amendments/*.sexp)",
+ [ Alcotest.test_case "every overlay file parses" `Quick test_files_parse;
+ Alcotest.test_case "SHA-256 pinned per file" `Quick test_sha256_pinned;
+ Alcotest.test_case "apply in decree-chronological order: 222 entries, no diagnostics" `Quick
+ test_apply_in_order;
+ Alcotest.test_case "every new/edited slug is present after merge" `Quick
+ test_new_slugs_present;
+ Alcotest.test_case "Martha/Mary/Lazarus: renamed in place, date/rank unchanged" `Quick
+ test_martha_mary_lazarus_renamed;
+ Alcotest.test_case "Mary, Mother of the Church: Monday after Pentecost, 3 real years" `Quick
+ test_mater_ecclesiae_monday_after_pentecost ] )
diff --git a/test/test_colitur.ml b/test/test_colitur.ml
index c750e75..98e08fb 100644
--- a/test/test_colitur.ml
+++ b/test/test_colitur.ml
@@ -12,6 +12,7 @@ let () =
("sigla", Test_citation.sigla_suite);
Test_config.suite;
Test_overlay.suite; Test_overlay_ini.suite; Test_temporal_ef.suite; Test_temporal_of.suite; Test_litcal_of.suite; Test_precedence_of.suite;
+ Test_amendments_of.suite;
Test_validate.suite;
Test_precedence.suite;
Test_calendar.suite; Test_precedence_ef.suite; Test_sanctoral_ef.suite; Test_calendar_of_data.suite;