summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-08-14 12:00:24 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-08-14 12:00:24 +0200
commitbf326ab963f46c42dc1a3b5141f2022bed81756d (patch)
tree398519ccbf0b495c59ee5be78597ba6acd732eaa
parent2af482c7293d821f10c3bbc96b7913d658c5839c (diff)
downloadcolitur-bf326ab963f46c42dc1a3b5141f2022bed81756d.tar.gz
colitur-bf326ab963f46c42dc1a3b5141f2022bed81756d.zip
data(ef): sanctoral audit — one genuine gap, 21 wrong colours (RG124)
Full re-extraction of the primary CALENDARIUM (both photographic scans, missale-romanum-1962.pdf and Missale Romanum 1962_text.pdf, rendered page images read directly rather than trusting either PDF's own OCR text layer) against all 327 base sanctoral entries plus the two prior overlay Adds. Both scans agree throughout; no OCR ambiguity required adjudication this round. Add `barbara` (4 December): both scans carry a Commemoratio nested under Peter Chrysologus's own entry, absent from data/ef/sanctoral.sexp AND from lectio's own tridentine-calendar.ini (confirmed directly) — the same upstream-gap shape as the five entries the ef-rebootstrap task already added (agnes-secundo/boniface-martyr/eusebius-confessor/evaristus/ theodore). Correct 21 sanctoral colours against RG 124 ("De coloribus paramentorum"), scan-verified word for word: 124(b) red for an Apostle/Evangelist's own feast day except John's (27 December, already correct); 124(e) red for a Martyr's own martyrdom; RG 117's five-colour residual otherwise white. Every one of the 21 is a confirmed lectio/missalemeum bootstrap defect — checked individually against lectio's own ini, several of whose Polish name fields already contradict that same record's own colour tag (e.g. `martha`: "Dziewicy", Virgin, no "Męczennicy", yet `colour = red`) — the identical root cause and mechanism the `eusebius-confessor` fix already established. Colour is confirmed read nowhere in Precedence_ef's resolution logic (grep), so these are output-correctness fixes with zero observed-office blast radius; three (apollinaris/josaphat/chair-of-st- peter) additionally disagree with missalemeum's own oracle, not only with lectio, so they are adjudicated from RG 124's text directly rather than by which engine happens to agree. Both corrections applied via data/ef/adjustments.sexp's established Overlay mechanism (data/ef/sanctoral.sexp stays generated, untouched). Pinned: test_rite_ef.ml (two new tests, verified to fail against the pre-fix data); test_differential.ml's new C18 predicate (526 rows, 2005-2050) and test_oracle.ml's new M21/M22 predicates (20 + 2 rows, 2026-2027), each with data/ef/expected-divergences{,-missalemeum}.sexp entries citing RG124 or the matching upstream-gap precedent; test_golden.ml's 1902-02-22 pin corrected (chair-of-st-peter was white in the stale pin, RG124(b) makes it red). All 329 tests green, including the full 1583-9999 exhaustive sweep. Full method, per-entry adjudication table and corrected bucket counts: docs/research/rules-register.md (gitignored) and .superpowers/sdd/2026-08-12-colitur-rg16a/sanctoral-audit-report.md (gitignored).
-rw-r--r--data/ef/adjustments.sexp94
-rw-r--r--data/ef/expected-divergences-missalemeum.sexp12
-rw-r--r--data/ef/expected-divergences.sexp5
-rw-r--r--test/test_differential.ml22
-rw-r--r--test/test_golden.ml18
-rw-r--r--test/test_oracle.ml51
-rw-r--r--test/test_rite_ef.ml86
7 files changed, 283 insertions, 5 deletions
diff --git a/data/ef/adjustments.sexp b/data/ef/adjustments.sexp
index a5e7c6f..48957ef 100644
--- a/data/ef/adjustments.sexp
+++ b/data/ef/adjustments.sexp
@@ -246,6 +246,69 @@
; against any external Polish liturgical reference -- flagged here rather
; than presented as sourced, the same honesty this file's own
; `commemoration-of-st-peter` entry gives its own colour field above.
+;
+; ef-sanctoral-audit (2026-08-14, branch ef-sanctoral-audit): a full
+; extraction of the CALENDARIUM (both photographic scans, missale-romanum-
+; 1962.pdf and Missale Romanum 1962_text.pdf, pdftoppm-rendered page images
+; read directly, not their pre-existing OCR text layers, which the task
+; brief's own worked examples show are unreliable for exactly this kind of
+; check) against all 327 base entries plus the two prior overlay Adds.
+; Method and full findings: docs/research/rules-register.md's own audit
+; rewrite (bucket recomputation) and .superpowers/sdd/2026-08-12-colitur-
+; rg16a/sanctoral-audit-report.md. Two shapes of finding follow.
+;
+; `Add barbara` -- a genuine DATA GAP, the same shape as the ef-rebootstrap
+; task's five (agnes-secundo/boniface-martyr/eusebius-confessor/evaristus/
+; theodore, above): both scans, 4 December, carry a second line under S.
+; Petri Chrysologi's own entry, "Commemoratio S. Barbarae Virg. et Mart."
+; -- a real, standing commemoration, absent from `data/ef/sanctoral.sexp`
+; AND from lectio's own `tridentine-calendar.ini` (checked directly: its
+; 12-04 block holds only `peter-chrysologus`, then jumps straight to
+; `sabbas`/12-05) -- not a colitur bootstrap miss, the same upstream gap
+; shape the ef-rebootstrap task's own five entries already documented.
+; Rank `Class3`: bootstrapped by the SAME convention every other
+; `Commemoration_only` entry in this file uses (see the rank-inference note
+; below) -- the calendarium gives a bare "Commemoratio", no class numeral,
+; for any commemoration, hers included. Colour Red: RG 124(e), scan-
+; verified word for word (both photographic scans, "XVIII - De coloribus
+; paramentorum", "C) De colore rubro"): "adhibetur color ruber in Officio
+; et Missa de festis: ... e) Sanctorum Martyrum, quorum colitur aut
+; martyrium..." -- red for the feast of a Martyr's own martyrdom, and the
+; calendarium's own line names her "Virg. et Mart." Names: English a plain
+; translation of "Barbarae"; Polish a plain vernacular rendering -- neither
+; independently primary-sourced, the same honesty this file's own
+; `commemoration-of-st-peter` entry already gives its names.
+;
+; Sixteen `Edit ... ((Set_colour ...))` directives -- confirmed lectio/
+; missalemeum bootstrap DATA DEFECTS, the identical shape and root cause as
+; `eusebius-confessor` above (checked individually against
+; `~/git/projects/lectio/internal/caldata/tridentine-calendar.ini`: every
+; one of these 17 slugs' wrong colour is already wrong AT THE SOURCE, not
+; introduced by colitur's own bootstrap step -- e.g. `martha`'s own Polish
+; field there already reads "św. Marty, Dziewicy" (Virgin, no "Męczennicy"
+; suffix), contradicting that SAME ini record's own `colour = red`). Two
+; rules, both scan-verified word for word against RG 124 ("XVIII - De
+; coloribus paramentorum", missale-romanum-1962.pdf, both photographic
+; scans agree): (124.b) "adhibetur color ruber in Officio et Missa de
+; festis: ... Sanctorum Apostolorum et Evangelistarum, in eorum die
+; natalicio, EXCEPTO FESTO S. IOANNIS (27 decembris)" -- red for an
+; Apostle/Evangelist's OWN feast day, John's 27 December the one named
+; exception (already correctly White in the base data, untouched here);
+; (124.e) "Sanctorum Martyrum, quorum colitur aut martyrium" -- red for a
+; Martyr's own martyrdom. Every plain Confessor/Bishop/Virgin entry below
+; (no "Mart."/"Mm." in the calendarium's own line, not an Apostle) is
+; White by RG 117's five-colour residual (not itself independently
+; RG-124-cited the way the red corrections are, since 124 enumerates the
+; RED exceptions, not a white default) -- flagged as inferred-by-category,
+; the same distinction the design spec draws throughout. `peter` (25
+; January, a commemoration of the Apostle nested under `conversion-of-st-
+; paul`) is corrected by CONSISTENCY with this file's own established
+; precedent, not a fresh RG 124 citation of its own: `paul`'s sibling
+; commemoration (22 February, under `chair-of-st-peter`) already carries
+; Red in the base bootstrap, and `commemoration-of-st-peter` above (30
+; June) was deliberately set Red "to match the host day's own Red" by an
+; earlier task -- the identical RG 110 Peter/Paul-pair shape, so `peter`
+; is the one inconsistent survivor, not a newly-discovered rule.
((id ef-adjustments)
(directives
((Suppress vigil-of-christmas)
@@ -265,4 +328,33 @@
((slug major-litanies)
(names ((en "The Major Litanies") (pl "Litanie Większe")))
(rank Class4) (status Commemoration_only) (colour Violet)
- (subject Saint) (citations ()) (layer ef-universal))))))))
+ (subject Saint) (citations ()) (layer ef-universal)))))
+ (Add
+ ((date (Fixed (month 12) (day 4)))
+ (cel
+ ((slug barbara)
+ (names
+ ((en "St. Barbara") (pl "\197\155w. Barbary, Dziewicy i M\196\153czennicy")))
+ (rank Class3) (status Commemoration_only) (colour Red)
+ (subject Saint) (citations ()) (layer ef-universal)))))
+ (Edit prisca ((Set_colour Red)))
+ (Edit conversion-of-st-paul ((Set_colour Red)))
+ (Edit peter ((Set_colour Red)))
+ (Edit chair-of-st-peter ((Set_colour Red)))
+ (Edit vitus ((Set_colour Red)))
+ (Edit margaret ((Set_colour Red)))
+ (Edit apollinaris ((Set_colour Red)))
+ (Edit agapitus ((Set_colour Red)))
+ (Edit josaphat ((Set_colour Red)))
+ (Edit john-of-san-fecundo ((Set_colour White)))
+ (Edit ephrem-of-syria ((Set_colour White)))
+ (Edit julia-of-falconieri ((Set_colour White)))
+ (Edit john-gualbert ((Set_colour White)))
+ (Edit camillus-de-lellis ((Set_colour White)))
+ (Edit jerome-emiliani ((Set_colour White)))
+ (Edit liborii ((Set_colour White)))
+ (Edit martha ((Set_colour White)))
+ (Edit alphonsus-liguori ((Set_colour White)))
+ (Edit augustine ((Set_colour White)))
+ (Edit rose-of-lima ((Set_colour White)))
+ (Edit mark-i ((Set_colour White))))))
diff --git a/data/ef/expected-divergences-missalemeum.sexp b/data/ef/expected-divergences-missalemeum.sexp
index 96d6405..ebc2cf9 100644
--- a/data/ef/expected-divergences-missalemeum.sexp
+++ b/data/ef/expected-divergences-missalemeum.sexp
@@ -195,8 +195,18 @@
((id M15)
(citation "docs/research/rules-register.md §4's own \"THE MAPPING, AND EXACTLY WHAT IT CANNOT RESOLVE\" note (test_oracle.ml's own header) -- not an RG citation, a comparator LIMIT: Rite_ef.Temporal_ef never sets an English [names] field on any candidate it builds (temporal_ef.ml has no \"en ...\" string anywhere in it)")
(verdict unresolvable)
- (note "20 rows where at least one of colitur's own admitted commemorations is TEMPORAL-origin -- there is no English name on colitur's side to compare against missalemeum's title text at all, so identity cannot be attempted for the WHOLE row, only reported as unresolved (test_oracle.ml's own [identity_diff]: any unresolved name bails the whole comparison out, regardless of how many others resolve cleanly). Not a rubric dispute and not a data gap either engine is wrong about. 19 of the 20 rows carry exactly ONE admitted commemoration, an impeded Advent or Lent feria, each reading as a plain prose description of the SAME temporal ferial slot colitur independently computes for that date (e.g. 2 Dec 2026: oracle \"Feria IV after I Sunday of Advent\" against colitur's own ef-advent-1-wednesday; 4 Dec 2027: oracle \"Sabbato after I Sunday of Advent\" against ef-advent-1-saturday) -- suggestive corroboration, recorded for a human reader, NOT claimed as a verified match: this comparator has no mechanism to verify English prose against an opaque slug. ADDED, ef-holyname-rg110 task: 2027-02-22 -- the closed M12's own date (immediately above this entry's own former position), now admitting TWO commemorations (RG 110's own [paul], sanctoral-origin and resolvable, AND the pre-existing [ef-lent-2-monday], temporal-origin and not) -- newly reaches identity comparison only because RG 110 fixed the [Comm_count] mismatch that used to mask it, and lands on this SAME pre-existing limit, not a new one. Fixing this at the root (an English name on every temporal-cycle candidate) is a data/lectionary-bootstrap task, not something this comparator can do for itself -- register §6, neighbouring the RG 113 tie-break section, records it as an open item. The 20 dates: 2026-02-24, 2026-03-19, 2026-03-25, 2026-12-02, 2026-12-03, 2026-12-04, 2026-12-07, 2026-12-11, 2026-12-21, 2027-02-22, 2027-02-24, 2027-12-02, 2027-12-03, 2027-12-04, 2027-12-06, 2027-12-07, 2027-12-11, 2027-12-13, 2027-12-16, 2027-12-21.")
+ (note "17 rows (was 20 before the ef-sanctoral-audit task; three moved to new entries below, not re-adjudicated) where at least one of colitur's own admitted commemorations is TEMPORAL-origin -- there is no English name on colitur's side to compare against missalemeum's title text at all, so identity cannot be attempted for the WHOLE row, only reported as unresolved (test_oracle.ml's own [identity_diff]: any unresolved name bails the whole comparison out, regardless of how many others resolve cleanly). Not a rubric dispute and not a data gap either engine is wrong about. All 17 carry exactly ONE admitted commemoration, an impeded Advent or Lent feria, each reading as a plain prose description of the SAME temporal ferial slot colitur independently computes for that date (e.g. 2 Dec 2026: oracle \"Feria IV after I Sunday of Advent\" against colitur's own ef-advent-1-wednesday) -- suggestive corroboration, recorded for a human reader, NOT claimed as a verified match: this comparator has no mechanism to verify English prose against an opaque slug. (The former double-commemoration exception, 2027-02-22 -- RG 110's [paul] alongside the pre-existing temporal Lenten feria -- is one of the three rows REMOVED below, not part of the 17.) REMOVED, ef-sanctoral-audit task (2026-08-14), not re-adjudicated: 2026-12-04 and 2027-12-04 now also admit `barbara` (data/ef/adjustments.sexp's own new `Add`), so both carry a [Comm_count] diff instead and fall to [M22] below; 2027-02-22 now also carries a [Colour_f] diff (`chair-of-st-peter`'s own corrected colour, RG124b) and falls to [M21] below -- all three are still genuinely unresolvable on THIS axis, just no longer matched by THIS entry's own exact-shape predicate (`diffs = [ Comm_identity_unresolved ]`, [test_oracle.ml]'s own [layer_m_reason]), which never re-fires once a second diff joins it. Fixing this at the root (an English name on every temporal-cycle candidate) is a data/lectionary-bootstrap task, not something this comparator can do for itself -- register §6, neighbouring the RG 113 tie-break section, records it as an open item. The 17 dates: 2026-02-24, 2026-03-19, 2026-03-25, 2026-12-02, 2026-12-03, 2026-12-07, 2026-12-11, 2026-12-21, 2027-02-24, 2027-12-02, 2027-12-03, 2027-12-06, 2027-12-07, 2027-12-11, 2027-12-13, 2027-12-16, 2027-12-21.")
+ (expected_rows 17))
+ ((id M21)
+ (citation "RG 124 (\"XVIII - De coloribus paramentorum\", \"C) De colore rubro\"), both photographic scans word for word: 124(b) \"adhibetur color ruber in Officio et Missa de festis: ... Sanctorum Apostolorum et Evangelistarum, in eorum die natalicio, excepto festo S. Ioannis (27 decembris)\"; 124(e) \"Sanctorum Martyrum, quorum colitur aut martyrium\"; ef-sanctoral-audit task")
+ (verdict colitur)
+ (note "The SAME closed 14-slug colour correction data/ef/expected-divergences.sexp's own C18 documents (its note has the full per-slug reasoning), now checked against this window's own missalemeum oracle rather than the 2005-2050 lectio fixture. 20 rows across 2026-2027 where colitur's own (corrected) observed colour is not in missalemeum's own [colours] set. Three of the twenty -- `apollinaris` (23 July, both years), `josaphat` (14 November, both years) and `chair-of-st-peter` (22 February, both years) -- disagree with missalemeum TOO, not merely with lectio: a second, independent source repeating the same defect (Apollinaris and Josaphat are each a martyr-bishop, RG 124(e); Peter's Chair is an Apostle's own feast day, RG 124(b)), so this is decided by the primary text itself, not by which engine happens to agree with colitur. [Comm_identity_unresolved] is admitted alongside on 22 February specifically (both years): that date's OTHER admitted commemoration is the ordinary Lenten feria RG 110 rides in alongside Paul, TEMPORAL-origin and already unresolved for identity -- the SAME pre-existing M15 gap, a different axis this entry's own citation does not speak to, so it is allowed by the subset check rather than folded in. Derived directly from the OCaml comparator's own failure output (test_layer_m_counts_match_citations), not hand-counted first and cross-checked after: 20.")
(expected_rows 20))
+ ((id M22)
+ (citation "The same lectio/missalemeum upstream-gap shape M1/M3/M8/M10/M19 above already document (\"missalemeum does not implement X\"); data/ef/adjustments.sexp's own `Add barbara` comment has the full RG 124(e)/calendarium citation for the entry itself")
+ (verdict colitur)
+ (note "`barbara` (4 December, data/ef/adjustments.sexp's own new `Add`, ef-sanctoral-audit task) is a genuine DATA GAP in missalemeum too: both years' own 4 December row shows only the Advent feria's own temporal commemoration, Barbara absent entirely, not even listed as displaced -- missalemeum never constructed a candidate for her either, the identical absence lectio's own tridentine-calendar.ini has. Identity-gated the same way [M19] is (coordinator finding F2, that entry's own note): the predicate requires colitur's own admitted set to be exactly the temporal feria plus `barbara`, not merely that some [Comm_count] diff exists on this date. Derived directly from the OCaml comparator's own failure output: 2 (both years in the window).")
+ (expected_rows 2))
((id M16)
(citation "1962 calendarium's March table (same primary text as M13's own citation): \"Feria VI post dominicam I Passionis: Commemoratio septem Dolorum B. Mariae Virg.\" -- a fixed commemoration of Our Lady's Seven Sorrows for the Friday of Passion Week, every year, confirmed real")
(verdict missalemeum)
diff --git a/data/ef/expected-divergences.sexp b/data/ef/expected-divergences.sexp
index f5d5381..75639fc 100644
--- a/data/ef/expected-divergences.sexp
+++ b/data/ef/expected-divergences.sexp
@@ -77,3 +77,8 @@
(verdict colitur)
(note "ef-bvm-saturday task: colitur now builds RG 91 entry 27's votive Office of the BVM on every otherwise-unoccupied IV-class Saturday (Rite_ef.Temporal_ef's own [bvm_saturday_names] citation has the full argument) -- white (RG 431(e) -> RG 121(a) -> RG 120(b); RG 120(b) alone reads \"de festis\" and this Office is not a festum), unconditionally, overriding the season's own colour (violet in Septuagesima, green in Time after Epiphany/Time after Pentecost; Christmastide and ordinary Paschaltide already happened to be white). lectio builds no equivalent office at all -- checked directly against its own tridentine-calendar.ini/generator, no comparable rule exists there, the same genuine upstream gap C15/C16 above document for their own gaps, not a colitur bootstrap miss -- so it keeps showing the plain season colour on every one of these Saturdays. Season, slug and rank all still agree: temporal_ef.ml's own [bvm_saturday_names] citation (\"Slug\" paragraph) deliberately REUSES the ordinary <season>-<week>-saturday ferial slug rather than minting a new one (partly BECAUSE a bespoke uniform slug would itself have broken Colitur_kernel.Validate's own slug-uniqueness invariant, sighted on every otherwise-unoccupied Saturday in a year), so [Colour_f] alone is the complete diff set every one of these rows carries -- never [Slug_f] too. Gated on colitur's own [rank]=class-4, [weekday]=saturday and [colour]=white (test_differential.ml's own [is_bvm_saturday_row]) rather than a date list, unlike C14/C15/C16 above: RG 78's own condition (\"otherwise unoccupied IV-class Saturday\") reduces exactly to this triple, and the population is large (a substantial fraction of all Saturdays in the 2005-2050 window) and entirely formulaic, not a short, individually-interesting list. Derived directly from the OCaml comparator's own failure output (test_layer_c_counts_match_citations), not hand-counted first and cross-checked after.")
(expected_rows 416))
+ ((id C18)
+ (citation "RG 124 (\"XVIII - De coloribus paramentorum\", \"C) De colore rubro\"), both photographic scans word for word: 124(b) \"adhibetur color ruber in Officio et Missa de festis: ... Sanctorum Apostolorum et Evangelistarum, in eorum die natalicio, excepto festo S. Ioannis (27 decembris)\"; 124(e) \"Sanctorum Martyrum, quorum colitur aut martyrium\"; register's own audit rewrite, ef-sanctoral-audit task")
+ (verdict colitur)
+ (note "ef-sanctoral-audit task (2026-08-14): 14 [status = Feast] sanctoral entries bootstrapped from lectio's own tridentine-calendar.ini carry a colour lectio's data gets wrong at the source (checked individually: e.g. `martha`'s own Polish field there already reads \"Dziewicy\" -- Virgin, no \"Męczennicy\" -- yet that SAME record's colour is red) -- corrected via data/ef/adjustments.sexp's own `Edit <slug> ((Set_colour ...))` directives, the same mechanism and root-cause shape `eusebius-confessor` (ef-rebootstrap fix round 1, F3) already used. Two rules: an Apostle/Evangelist's own feast day is red except John's (27 December, already correctly white, untouched); a plain Confessor/Bishop/Virgin (no \"Mart.\"/\"Mm.\" in the calendarium's own line, not an Apostle) is white by RG 117's five-colour residual; a Martyr's own martyrdom is red. `audit_colour_corrected_slugs` in test_differential.ml is the literal 14-slug list (conversion-of-st-paul, chair-of-st-peter, john-of-san-fecundo, ephrem-of-syria, julia-of-falconieri, john-gualbert, camillus-de-lellis, jerome-emiliani, apollinaris, martha, alphonsus-liguori, augustine, rose-of-lima, josaphat) -- every one of these [status = Feast], so it wins its own day most years (rarely impeded) and diffs on [Colour_f] alone against lectio's still-wrong value. A further eight corrections (prisca/peter/vitus/margaret/agapitus/liborii/mark-i, all [Commemoration_only], plus the new `barbara` Add) are NOT in this count: a [Commemoration_only] candidate can never be the OBSERVED day (Precedence.resolve's own design), so this row-level comparator (which reads only the observed day's own record) never has a row to diff for them -- inert to this fixture, not unaffected in truth. Derived directly from the OCaml comparator's own failure output (test_layer_c_counts_match_citations's own `Received`), not hand-counted first and cross-checked after: 526.")
+ (expected_rows 526))
diff --git a/test/test_differential.ml b/test/test_differential.ml
index 5093484..a4e550d 100644
--- a/test/test_differential.ml
+++ b/test/test_differential.ml
@@ -465,6 +465,27 @@ let jan_6_13_slug slug =
let nativity_octave_day_slugs =
[ "ef-nativity-octave-day-5"; "ef-nativity-octave-day-6"; "ef-nativity-octave-day-7" ]
+(* C18 (ef-sanctoral-audit, 2026-08-14): the closed set of colitur
+ [status = Feast] sanctoral slugs whose bootstrapped [colour] was
+ corrected against RG 124 (data/ef/adjustments.sexp's own audit-block
+ comment has the full citation and per-slug reasoning); every one of
+ these still carries lectio's OLD, uncorrected colour, so a row where
+ this is the ONLY colitur candidate observed for its own date diffs on
+ [Colour_f] alone whenever it wins the day (most years -- these are all
+ ordinary universal feasts, rarely impeded). Deliberately a literal list,
+ not a shape predicate, the same discipline C6/C14/C15/C16 already use:
+ a colour diff on any OTHER slug must still surface as unexplained. Eight
+ further audit corrections (prisca/peter/vitus/margaret/agapitus/liborii/
+ mark-i, all Commemoration_only, plus the new `barbara` Add) are NOT
+ listed here and need no predicate: [colitur_rows_2005_2050] emits only
+ the OBSERVED day's own record, and a [Commemoration_only] candidate can
+ never be observed (Precedence.resolve's own design), so their corrected
+ colours have no row in this comparison to explain. *)
+let audit_colour_corrected_slugs =
+ [ "conversion-of-st-paul"; "chair-of-st-peter"; "john-of-san-fecundo"; "ephrem-of-syria";
+ "julia-of-falconieri"; "john-gualbert"; "camillus-de-lellis"; "jerome-emiliani"; "apollinaris";
+ "martha"; "alphonsus-liguori"; "augustine"; "rose-of-lima"; "josaphat" ]
+
(* C14 (ef-rebootstrap fixture refresh, 2026-08-12; replaces the closed C9,
see data/ef/expected-divergences.sexp's own C14 note for the full RG
citation): the exact 3 civil days, across the whole 2005-2050 window,
@@ -620,6 +641,7 @@ let layer_c_reason (l : row) (c : row) diffs =
&& String.equal c.slug "ef-holy-name"
then Some "C16"
else if is_bvm_saturday_row c diffs then Some "C17"
+ else if diffs = [ Colour_f ] && List.mem c.slug audit_colour_corrected_slugs then Some "C18"
else None
(* ---------------------------------------------------------------------- *)
diff --git a/test/test_golden.ml b/test/test_golden.ml
index 7b461bf..61224d3 100644
--- a/test/test_golden.ml
+++ b/test/test_golden.ml
@@ -493,11 +493,23 @@ let test_ember_ferias_commemorated_when_impeded () =
[rg110_additions]). This pin's own [ef-lent-ember-sat:privileged] half
is UNCHANGED from before this task -- only [paul:ordinary] is new,
confirming RG 110 adds a genuinely SEPARATE, uncapped slot rather than
- competing for the Ember feria's own privileged one. *)
+ competing for the Ember feria's own privileged one.
+
+ CORRECTED (ef-sanctoral-audit, 2026-08-14): [chair-of-st-peter]'s own
+ colour was `white` here, matching the pre-audit bootstrap -- WRONG,
+ RG 124(b) ("XVIII - De coloribus paramentorum", both photographic
+ scans, word for word): red is used "in Officio et Missa de festis...
+ Sanctorum Apostolorum et Evangelistarum, in eorum die natalicio,
+ excepto festo S. Ioannis (27 decembris)" -- red for an Apostle's own
+ feast day, no exception for a Chair feast. Fixed via
+ data/ef/adjustments.sexp's own `Edit chair-of-st-peter ((Set_colour
+ Red))`; this pin now asserts the corrected value, catching a
+ regression back to the old one either way. *)
check ~msg:"1902-02-22: Lent Ember Saturday, impeded, MUST be commemorated (RG24) and is privileged (RG109e) -- \
- AND RG110's Paul companion rides alongside it, uncapped"
+ AND RG110's Paul companion rides alongside it, uncapped -- AND Chair of St Peter is red (RG124b), \
+ not lectio's bootstrapped white (ef-sanctoral-audit)"
1902 2 22
- "1902-02-22 saturday season=lent week=1 slug=chair-of-st-peter rank=class-2 colour=white subject=saint \
+ "1902-02-22 saturday season=lent week=1 slug=chair-of-st-peter rank=class-2 colour=red subject=saint \
name_la=- comms=[ef-lent-ember-sat:privileged,paul:ordinary] in=- out=[]"
let test_iv_class_ferias_never_commemorated () =
diff --git a/test/test_oracle.ml b/test/test_oracle.ml
index 3a9e499..cd980aa 100644
--- a/test/test_oracle.ml
+++ b/test/test_oracle.ml
@@ -910,6 +910,52 @@ let m19_commemoration_matches (c : colitur_row) =
| [ (slug, _, _, _) ] -> String.equal slug "commemoration-of-st-peter"
| _ -> false
+(* M21 -- ef-sanctoral-audit task (2026-08-14): the SAME closed 14-slug list
+ test_differential.ml's own [audit_colour_corrected_slugs] uses (RG 124,
+ data/ef/adjustments.sexp's own audit-block comment has the full
+ citation), now checked against THIS window's own oracle. Two of the
+ fourteen (`apollinaris`, 23 July; `josaphat`, 14 November) and one
+ further entry not on the Feast-status list at all (`chair-of-st-peter`,
+ 22 February, a Feast in its own right, RG124(b)) disagree with
+ missalemeum's own colour TOO, not merely lectio's -- a SECOND,
+ independent data source repeating the same defect (Apollinaris and
+ Josaphat are each martyr-bishops, RG 124(e); Peter's Chair is an
+ Apostle's own feast day, RG 124(b) -- both scan-verified word for word,
+ data/ef/adjustments.sexp's own comment), so this is not a case of
+ "missalemeum corroborates colitur, only lectio disagrees": the primary
+ TEXT is what decides it, and missalemeum is simply wrong on these three
+ the same way lectio is on all fourteen. [Comm_identity_unresolved] is
+ allowed alongside on 22 February specifically: that date's OTHER
+ commemoration (the ordinary Lenten feria RG 110 rides in alongside Paul)
+ is TEMPORAL-origin and already unresolved for identity, the same
+ pre-existing gap [M15]/[M18] elsewhere in this file document -- a
+ different axis, not something this entry's own citation explains, so it
+ is admitted by the subset check rather than folded into the citation. *)
+let m21_colour_slugs =
+ [ "conversion-of-st-paul"; "chair-of-st-peter"; "john-of-san-fecundo"; "ephrem-of-syria";
+ "julia-of-falconieri"; "john-gualbert"; "camillus-de-lellis"; "jerome-emiliani"; "apollinaris";
+ "martha"; "alphonsus-liguori"; "augustine"; "rose-of-lima"; "josaphat" ]
+
+(* M22 -- ef-sanctoral-audit task: `barbara` (4 December, data/ef/
+ adjustments.sexp's own new `Add`) is a genuine DATA GAP in missalemeum
+ too, the identical shape M1/M3/M8/M10/M19 above already document (each
+ "missalemeum does not implement X"): both years' own 4 December row
+ shows only the Advent feria's own temporal commemoration, no Barbara at
+ all, not even displaced -- missalemeum never constructed a candidate for
+ her either, the same absence lectio's own tridentine-calendar.ini has
+ (data/ef/adjustments.sexp's own `Add barbara` comment). Identity-gated
+ the same way [M19] is (coordinator finding F2): the predicate requires
+ colitur's own admitted set to be EXACTLY the temporal feria plus
+ `barbara`, not merely that SOME count diff exists on this date, so a
+ future unrelated regression on 4 December cannot silently hide behind
+ this citation. *)
+let m22_dates = [ "2026-12-04"; "2027-12-04" ]
+
+let m22_commemoration_matches (c : colitur_row) =
+ match List.map (fun (slug, _, _, _) -> slug) c.c_commemorations with
+ | [ a; b ] -> (String.equal a "barbara") <> (String.equal b "barbara")
+ | _ -> false
+
(* [_o] (the oracle's own row) is unused now that M2 -- its one reader, via
[o.o_title] -- is closed/removed (see the comment above [m1_dates]):
every remaining predicate below reads only [c]/[diffs]. Kept as a named,
@@ -965,6 +1011,11 @@ let layer_m_reason (c : colitur_row) (_o : oracle_row) diffs =
else if List.mem c.c_date m19_dates && diffs = [ Comm_presence ] && m19_commemoration_matches c then
Some "M19"
else if
+ List.mem c.c_observed_slug m21_colour_slugs && subset diffs [ Colour_f; Comm_identity_unresolved ]
+ then Some "M21"
+ else if List.mem c.c_date m22_dates && diffs = [ Comm_count ] && m22_commemoration_matches c then
+ Some "M22"
+ else if
List.mem c.c_date m20_dates
&& subset diffs [ Comm_identity_mismatch; Observed_identity_unresolved ]
&& m20_commemoration_matches c
diff --git a/test/test_rite_ef.ml b/test/test_rite_ef.ml
index 6cd8709..9b0cb4f 100644
--- a/test/test_rite_ef.ml
+++ b/test/test_rite_ef.ml
@@ -166,6 +166,88 @@ let test_eusebius_confessor_colour_corrected () =
true
(e.Layer.cel.Cel.colour = Colour.White)
+(* ef-sanctoral-audit task (2026-08-14): a full extraction of the primary
+ CALENDARIUM (both photographic scans, page images read directly, not
+ their OCR text layers) against all 327 base entries plus the two prior
+ overlay Adds -- data/ef/adjustments.sexp's own audit-block comment has
+ the full method and RG 124 citation; docs/research/rules-register.md's
+ audit rewrite and .superpowers/sdd/2026-08-12-colitur-rg16a/sanctoral-
+ audit-report.md have the full findings. Same discipline as
+ [test_eusebius_confessor_colour_corrected] above (its own predecessor,
+ same root cause): a typo'd slug or a reverted directive fails THIS test,
+ not only silently changes unread data. One table, all fourteen [Feast]
+ colour corrections plus the eight [Commemoration_only] ones (inert to
+ [Precedence_ef.band]/[admit] today -- colour is read nowhere in either,
+ confirmed by grep -- but real for `colitur day`'s own printed output the
+ moment either entry is ever observed/commemorated). *)
+let test_audit_colours_corrected () =
+ let layer = real_layer () in
+ let check_colour slug expected reason =
+ match Layer.find layer (Slug.of_string_exn slug) with
+ | None -> Alcotest.failf "slug %s not found in the overlaid layer" slug
+ | Some e ->
+ Alcotest.(check bool) (Printf.sprintf "%s: %s" slug reason) true (e.Layer.cel.Cel.colour = expected)
+ in
+ (* RG 124(e): a Martyr's own martyrdom is red -- these seven were White. *)
+ List.iter
+ (fun (slug, reason) -> check_colour slug Colour.Red reason)
+ [ ("prisca", "Virg. et Mart., 18 Jan -- RG124(e)");
+ ("conversion-of-st-paul", "an Apostle's own feast day, 25 Jan -- RG124(b)");
+ ("peter", "Apostle commemoration, 25 Jan -- by consistency, see adjustments.sexp");
+ ("chair-of-st-peter", "an Apostle's own feast day, 22 Feb -- RG124(b)");
+ ("vitus", "Ss. ... Mm., 15 Jun -- RG124(e)");
+ ("margaret", "Virg. et Mart., 20 Jul (comm) -- RG124(e)");
+ ("apollinaris", "Ep. et Mart., 23 Jul -- RG124(e)");
+ ("agapitus", "Mart., 18 Aug -- RG124(e)");
+ ("josaphat", "Ep. et Mart., 14 Nov -- RG124(e)") ];
+ (* RG 117's five-colour residual: a plain Confessor/Bishop/Virgin (no
+ "Mart."/"Mm." in the calendarium's own line, not an Apostle) is white
+ -- these were Red. *)
+ List.iter
+ (fun (slug, reason) -> check_colour slug Colour.White reason)
+ [ ("john-of-san-fecundo", "Conf. only, 12 Jun");
+ ("ephrem-of-syria", "Conf. et Eccl. Doct. only, 18 Jun");
+ ("julia-of-falconieri", "Virg. only, 19 Jun");
+ ("john-gualbert", "Abb. (Conf.) only, 12 Jul");
+ ("camillus-de-lellis", "Conf. only, 18 Jul");
+ ("jerome-emiliani", "Conf. only, 20 Jul");
+ ("liborii", "Ep. et Conf. only, 23 Jul (comm)");
+ ("martha", "Virg. only, 29 Jul");
+ ("alphonsus-liguori", "Ep., Conf. et Eccl. Doct. only, 2 Aug");
+ ("augustine", "Ep., Conf. et Eccl. Doct. only, 28 Aug");
+ ("rose-of-lima", "Virg. only, 30 Aug");
+ ("mark-i", "Papae et Conf. only (Pope Mark I, not the Evangelist), 7 Oct (comm)") ]
+
+(* `barbara` (4 December): a genuine DATA GAP, not a colour correction --
+ present in both photographic scans as a Commemoratio nested under
+ Peter Chrysologus's own entry, absent from data/ef/sanctoral.sexp AND
+ from lectio's own tridentine-calendar.ini (same upstream-gap shape as
+ the ef-rebootstrap task's five: agnes-secundo/boniface-martyr/
+ eusebius-confessor/evaristus/theodore). Added via data/ef/
+ adjustments.sexp's own new `Add` directive. *)
+let test_barbara_added () =
+ let layer = real_layer () in
+ match Layer.find layer (Slug.of_string_exn "barbara") with
+ | None -> Alcotest.fail "slug barbara not found in the overlaid layer (ef-sanctoral-audit Add missing?)"
+ | Some e ->
+ Alcotest.(check bool) "barbara: 4 December" true
+ (match e.Layer.date with Date_spec.Fixed { month = 12; day = 4 } -> true | Date_spec.Fixed _ -> false);
+ Alcotest.(check bool) "barbara: Commemoration_only (no separate feast row in the calendarium, a bare \
+ \"Commemoratio\" nested under Peter Chrysologus)"
+ true (e.Layer.cel.Cel.status = Cel.Commemoration_only);
+ Alcotest.(check bool) "barbara: Class3 (the same bootstrap-inference convention every OTHER \
+ Commemoration_only entry in this file uses -- the calendarium gives no numeral \
+ for any commemoration, hers included)"
+ true (e.Layer.cel.Cel.rank = V.Class3);
+ Alcotest.(check bool) "barbara: Red (RG124(e), \"Virg. et Mart.\")" true
+ (e.Layer.cel.Cel.colour = Colour.Red);
+ Alcotest.(check bool) "barbara: appears as a commemoration on 4 December in real output" true
+ (let days = Cal.year Rite_ef.context layer 2026 in
+ Array.to_list days
+ |> List.exists (fun d ->
+ Date.compare d.LD.date (mk 2026 12 4) = 0
+ && List.exists (fun (c, _) -> slug_of c = "barbara") d.LD.commemorations))
+
(* Coordinator review, finding 2, reproduced through the project's OWN
extension path (an overlay), the same way the reviewer found it: adding
an I-class feast on 25 December (competing against, and losing to, the
@@ -422,6 +504,10 @@ let suite =
`Quick test_rg16a_subject_retag_in_effect;
Alcotest.test_case "ef-rebootstrap fix round 1 (F3): eusebius-confessor is White, not lectio's Red" `Quick
test_eusebius_confessor_colour_corrected;
+ Alcotest.test_case "ef-sanctoral-audit: 21 audit colour corrections are in effect (RG124)" `Quick
+ test_audit_colours_corrected;
+ Alcotest.test_case "ef-sanctoral-audit: barbara (4 Dec) added, a genuine data gap" `Quick
+ test_barbara_added;
Alcotest.test_case "RG96 search does not raise at the domain ceiling (real data)" `Quick
test_transfer_search_does_not_raise_at_domain_ceiling;
Alcotest.test_case "no transfer ever lands inside [Easter, Easter+7], 2005-2050" `Quick