aboutsummaryrefslogtreecommitdiff
path: root/test/test_differential.ml
diff options
context:
space:
mode:
Diffstat (limited to 'test/test_differential.ml')
-rw-r--r--test/test_differential.ml196
1 files changed, 89 insertions, 107 deletions
diff --git a/test/test_differential.ml b/test/test_differential.ml
index 4d6aab0..62c7a48 100644
--- a/test/test_differential.ml
+++ b/test/test_differential.ml
@@ -51,9 +51,20 @@
*** THE THREE-LAYER DESIGN (controller ruling, Task 15 dispatch) ***
- Of 16801 day-pairs (2005-2050), 11206 already match on the seven columns.
- Of the 5595 that don't, exactly 25 distinct (field-diff) signatures cover
- all of them (task-15-class-summary.txt). They resolve into three strictly
+ REFRESHED (2026-08-12, task 2026-08-12-colitur-rg16a, branch ef-rebootstrap):
+ the fixture was regenerated from lectio's CURRENT HEAD (3b32c00), which has
+ fixed seven EF calendar defects since the fixture was first pinned (commit
+ 2386a45) -- see the fixture's own provenance note for the exact commits and
+ defect list. Of 16801 day-pairs (2005-2050), 11826 now already match on the
+ raw seven columns (up from roughly 11201 against the stale fixture); of the
+ 4975 that don't, only 10 distinct RAW (field-diff, including the
+ never-compared week column) signatures cover all of them, independently
+ recomputed against this fixture and colitur's current output, not copied
+ from the original task-15-class-summary.txt (which described the OLD
+ fixture and is stale). After Layers A and B below strip the week column and
+ the vocabulary/numbering artifacts, only 540 of those 4975 remain genuine
+ differences -- all 540 explained by Layer C (368 + 138 + 31 + 3, see each
+ entry's own citation), 0 unexplained. They resolve into three strictly
separate layers:
- Layer A (this file's [norm_season]/[norm_slug]): vocabulary. A
@@ -67,7 +78,7 @@
done even where it would shorten the table.
- Layer B ([strip_epiphany_index]): numbering. The ONE case in the whole
- 5595 where a slug's embedded index genuinely cannot be reconciled by a
+ 4975 where a slug's embedded index genuinely cannot be reconciled by a
literal table (Time-after-Epiphany's non-constant offset, limit 3
above) -- both sides' embedded week digit is stripped to a common
family+weekday form before comparing, while rank and colour (which
@@ -76,37 +87,48 @@
family would still be caught by everything except the digit itself.
- Layer C (data/ef/expected-divergences.sexp, matched by
- [layer_c_reason] below): the CITED allow-list. Eleven genuine liturgical
- disagreements (C11 added by Task 16's oracle work, below), each citing
- its RG paragraph and stating which engine is right (always colitur,
- verified against the Missal/register, never against lectio's own
- behaviour -- "lectio does it differently" is not itself a justification
- anywhere in this file). This is the ONLY layer that may cover a
- difference in rank, colour, or which celebration is observed; A and B
- never do (enforced structurally below: A/B only ever
- touch the season/slug fields, and Layer C's predicates each require an
- exact, narrow field-diff SET, not "anything goes").
+ [layer_c_reason] below): the CITED allow-list. Each entry cites its RG
+ paragraph and states which engine is right (always colitur, verified
+ against the Missal/register, never against lectio's own behaviour --
+ "lectio does it differently" is not itself a justification anywhere in
+ this file). This is the ONLY layer that may cover a difference in rank,
+ colour, or which celebration is observed; A and B never do (enforced
+ structurally below: A/B only ever touch the season/slug fields, and
+ Layer C's predicates each require an exact, narrow field-diff SET, not
+ "anything goes").
- 13 January (register §6's long-open "Baptism of the Lord" item) is
- EMPIRICALLY CONFIRMED FIXED, not separately allow-listed, ONLY in the 39
- of 46 years it is NOT a Sunday: colitur's slug/rank/colour equal
- lectio's fixture exactly there (the sanctoral wiring landed in Task 11's
- "13 Jan now resolves to the Baptism of the Lord" review note), and the
- only residual difference on those 39 rows is season (covered by Layer
- C's C1, the Jan 6-13 boundary).
+ REFRESHED (2026-08-12, ef-rebootstrap fixture regeneration): lectio's own
+ fix wave (2386a45 -> 3b32c00, provenance note has the full commit list)
+ independently fixed the exact defects nine of the thirteen entries this
+ file used to carry were about -- C2, C3, C4, C5, C7, C10, C11, C12 and C13
+ all now match ZERO rows against the refreshed fixture and were CLOSED
+ (removed from data/ef/expected-divergences.sexp and from
+ [layer_c_reason] below), each with its citation preserved and the closure
+ recorded in docs/research/rules-register.md, not silently deleted -- see
+ the report for the full before/after account. C9's old shape ("St Joseph
+ observed ON a Lent Sunday", lectio's own defect 4) also closed the same
+ way, but a NARROWER, different divergence involving the same saint
+ survived the refresh and was re-cited as a new entry, C14 (below), not
+ folded back into the old C9 id (the two are different rules; conflating
+ them would be exactly the "count proving cardinality where identity was
+ required" vacuity flavour this project's harnesses are specifically
+ checked against). Three entries survive unchanged in kind: C1 (368 rows,
+ up from 361 -- the 7 rows C13 used to carve out fold back into C1's own
+ shape now that lectio's slug matches there too, see C1's own note), C6
+ (138 rows, unchanged count but a narrower shape -- lectio fixed the RANK
+ this entry used to also cover, leaving only the SLUG naming difference,
+ see C6's own note) and C8 (31 rows, wholly unchanged -- lectio still
+ computes no Rogation days at all).
- CORRECTED (ef-rebootstrap fix round 1, F2): this note previously said
- "in all 46 years" and "the only residual difference is season" without
- qualification -- WRONG once `commemoration-of-the-baptism-of-the-lord`
- became `subject = Lord` (ef-rebootstrap): on the remaining 7 years (13
- January itself falls on a Sunday: 2008, 2013, 2019, 2030, 2036, 2041,
- 2047), RG16(a) now makes colitur observe the Baptism itself where it
- used to observe the Sunday -- the OBSERVED SLUG differs from lectio's
- fixture too, not merely colour. Allow-listed separately as C13
- (`data/ef/expected-divergences.sexp`), whose own note has the full
- account, including a live cross-check against lectio's CURRENT engine
- (not the committed fixture, which predates lectio's own equivalent
- fix) -- see the report. *)
+ 13 January (register §6's long-open "Baptism of the Lord" item) is
+ EMPIRICALLY CONFIRMED FIXED, not separately allow-listed, in ALL 46 years
+ again as of this refresh: lectio's own fix wave independently fixed its
+ equivalent of the same RG16(a) defect the now-closed C13 used to allow-
+ list (its slug now matches colitur's `commemoration-of-the-baptism-of-
+ the-lord` on all 7 of the Sunday years too, not only the 39 non-Sunday
+ ones), so the residual difference on every one of the 46 rows is season
+ alone -- covered by C1, the Jan 6-13 boundary, with no separate entry
+ needed any more. *)
module Cal = Colitur_kernel.Calendar
module Layer = Colitur_kernel.Layer
@@ -136,7 +158,7 @@ let allow_list_path = "../data/ef/expected-divergences.sexp"
deliberately, after re-running the exact command the provenance note
names -- never by copying the actual value back in to make a mismatch
pass, which would defeat the point of pinning it at all. *)
-let fixture_sha256 = "2ca3eeeda4e7a0406c4d004c1b2003fc0df671aca9af18a1b506543a721c8bac"
+let fixture_sha256 = "fc4832f1bc1bdfd322e808ae4929487628981792c23f959c1764c8eaedbfaf7f"
(* Same technique tools/bootstrap_sanctoral.ml already uses for this exact
purpose (that file's own comment: shelling out to the system's
@@ -396,15 +418,21 @@ let diff_fields (l : row) (c : row) =
let subset xs ys = List.for_all (fun x -> List.mem x ys) xs
let day_of_date date = int_of_string (String.sub date 8 2)
-let advent_feria_slug slug =
- List.exists
- (fun wk -> List.exists (fun wd -> String.equal slug (Printf.sprintf "ef-advent-%d-%s" wk wd)) weekdays)
- [ 3; 4 ]
-
-let sunday_iclass_slugs =
- [ "ef-advent-sunday-2"; "ef-advent-sunday-4"; "ef-lent-sunday-1"; "ef-lent-sunday-2"; "ef-lent-sunday-3" ]
-
-let rose_sunday_slugs = [ "ef-advent-sunday-3"; "ef-lent-sunday-4" ]
+(* ef-rebootstrap fixture refresh (2026-08-12): C2, C3, C4, C5, C7, C10, C11,
+ C12 and C13's own predicates/helper bindings (sunday_iclass_slugs,
+ rose_sunday_slugs, advent_feria_slug, fixed_iii_class_reclassified_slugs,
+ jan13_lord_sunday_dates_2005_2050, and the inline C7/C10/C11 date/slug
+ checks) are REMOVED here, not left dead -- each matched zero rows against
+ the refreshed fixture (lectio's own fix wave, 2386a45 -> 3b32c00,
+ independently fixed the exact defects these entries were about), so their
+ citations moved to docs/research/rules-register.md (closure record, with
+ the RG paragraph preserved) and their sexp rows to nothing --
+ data/ef/expected-divergences.sexp no longer declares those ids at all.
+ Removing a predicate strengthens this harness the same way removing its
+ sexp row does: if any of these nine shapes ever reappears (a regression in
+ colitur, or lectio moving again), it now surfaces as UNEXPLAINED instead
+ of being silently re-absorbed by a citation whose own divergence no
+ longer exists. *)
(* Fix round 1, finding 1: C1 and C6 (below) originally gated on calendar
date alone, with no slug/slug-family check -- unlike every other entry
@@ -427,40 +455,21 @@ 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" ]
-(* C12 (ef-rebootstrap, 2026-08-12): the exact 15 slugs data/ef/sanctoral.sexp
- re-bootstrapped from `rank = commemoration` to a real class-3 feast (the
- source's own SHA-256 moved 6a25e634... -> 1b303ef2...) -- a literal list,
- not a date-range or slug-prefix pattern, per the SAME discipline the
- comment above this one already states for C1/C6 (fix round 1, finding 1):
- a slug outside this exact set must fail through to [None], not be
- silently absorbed by a citation about a different set of saints. Only 7
- of the 15 are ever actually reachable here (the other 8 never collide
- with a plain class-4 feria anywhere in 2005-2050), but the guard is
- written against the full set colitur's own data now carries, not the
- narrower subset this one 46-year window happens to exercise. *)
-let fixed_iii_class_reclassified_slugs =
- [ "benedict"; "cyril-of-jerusalem"; "forty-holy-martyrs-of-sebaste"; "frances-rome";
- "francis-of-paola"; "gabriel-the-archangel"; "gregory-the-great"; "isidore-of-seville";
- "john-damascene"; "john-of-capistrano"; "john-of-god"; "patrick"; "sts-felicitas-perpetua";
- "thomas-aquinas"; "vincent-ferrer" ]
-
-(* C13 (ef-rebootstrap fix round 1, F2): the 7 years 13 January falls on a
- Sunday within 2005-2050 -- independently re-derived, not copied from a
- prior figure: `date -d <year>-01-13 +%A` = Sunday in exactly these 7
- (2005-2050 is 46 years; 13 January cycles through the 7 weekdays with
- the usual leap-year irregularity, landing on Sunday this many times).
- A LITERAL list, not a predicate on [jan_6_13_slug] (which already, and
- correctly, admits this exact slug for the other 39 non-Sunday years in
- the window) -- checked BEFORE C1 below in the if/elif chain specifically
- so it intercepts these 7 dates first; leaving C1's own guard as it was
- would have let [jan_6_13_slug]'s pre-existing admission of this slug
- silently absorb them into C1's count instead, the exact gap fix round 1
- found: C1's own [expected_rows] is a bare row COUNT, blind to WHICH of
- two different diff shapes produced it, so a real shape change here
- (colitur's own OBSERVED CELEBRATION on these 7 dates, not merely its
- colour) could move underneath the pin without the pin ever moving. *)
-let jan13_lord_sunday_dates_2005_2050 =
- [ "2008-01-13"; "2013-01-13"; "2019-01-13"; "2030-01-13"; "2036-01-13"; "2041-01-13"; "2047-01-13" ]
+(* 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,
+ where St Joseph's (19 March, I class) RG 96 transfer walk is congested
+ enough by Passiontide/Holy Week/the Easter octave to cross Easter AND
+ collide with the Annunciation's own transferred "sedes propria" (RG 96
+ Attamen (a), Monday after Low Sunday) -- independently re-derived from
+ this fixture and colitur's own output (`grep joseph-spouse` both sides,
+ filtered to rows with a real post-Layer-A/B diff), not copied from the
+ register's prior "2008, 2035, 2046" prose. A LITERAL date list, not a
+ slug predicate, per the SAME discipline C12/C13 already used (a plain
+ "involves this slug" predicate is exactly what let the old C9 silently
+ cover two textually-unrelated divergences at once -- see C14's own sexp
+ note for the full account of why that was wrong). *)
+let joseph_annunciation_collision_dates_2005_2050 = [ "2008-04-01"; "2035-04-03"; "2046-04-03" ]
(* [layer_c_reason l c diffs] returns the [data/ef/expected-divergences.sexp]
[id] this row-pair's remaining (post Layer A/B) diff set belongs to, or
@@ -468,50 +477,23 @@ let jan13_lord_sunday_dates_2005_2050 =
let layer_c_reason (l : row) (c : row) diffs =
let m = month_of_date l.date and d = day_of_date l.date in
if diffs = [] then None
- else if List.mem l.date jan13_lord_sunday_dates_2005_2050 && diffs = [ Season; Slug_f; Colour_f ] then
- Some "C13"
else if
m = 1 && d >= 6 && d <= 13
&& subset diffs [ Season; Colour_f; Slug_f ]
&& (not (List.mem Slug_f diffs) || jan_6_13_slug c.slug)
then Some "C1"
- else if List.mem c.slug sunday_iclass_slugs && diffs = [ Rank ] then Some "C2"
- else if List.mem c.slug rose_sunday_slugs && subset diffs [ Rank; Colour_f ] then Some "C3"
- else if advent_feria_slug c.slug && diffs = [ Rank ] then Some "C4"
- else if starts_with ~prefix:"ef-lent-ember-" c.slug && subset diffs [ Slug_f; Rank ] then Some "C5"
else if
m = 12
&& (d = 29 || d = 30 || d = 31)
&& subset diffs [ Slug_f; Rank ]
&& (not (List.mem Slug_f diffs) || List.mem c.slug nativity_octave_day_slugs)
then Some "C6"
- else if (String.equal c.slug "matthew" || String.equal c.slug "thomas") && subset diffs [ Slug_f; Colour_f ]
- then Some "C7"
else if
(String.equal c.slug "ef-rogation-monday" || String.equal c.slug "ef-rogation-tuesday")
&& subset diffs [ Season; Slug_f; Colour_f ]
then Some "C8"
- else if
- (String.equal l.slug "joseph-spouse-of-the-bl-virgin-mary"
- || String.equal c.slug "joseph-spouse-of-the-bl-virgin-mary")
- && subset diffs [ Season; Slug_f; Rank; Colour_f ]
- then Some "C9"
- else if
- (String.equal l.date "2011-07-02" || String.equal l.date "2011-07-04")
- && subset diffs [ Slug_f; Rank; Colour_f ]
- then Some "C10"
- else if String.equal c.slug "ef-passiontide-2-thursday" && diffs = [ Colour_f ] then Some "C11"
- (* RG16(a) task, fix round 1 (CRITICAL finding): a C12 entry briefly lived
- here, allow-listing 2 February on a Sunday against lectio, when
- `data/ef/adjustments.sexp` retagged the Purification `Bvm`. REVERTED --
- the user has ruled: follow the oracle (register §6.0 has the full
- account). The Purification is `Lord` again, colitur observes it
- outright on those six 2-February Sundays, matching lectio's own
- `class = lord` exactly -- no divergence to allow-list. That C12 was
- removed, not merely unused -- superseded by the DIFFERENT C12 below,
- ef-rebootstrap (2026-08-12), reusing the freed id. *)
- else if List.mem c.slug fixed_iii_class_reclassified_slugs && subset diffs [ Slug_f; Rank; Colour_f ]
- then Some "C12"
+ else if List.mem l.date joseph_annunciation_collision_dates_2005_2050 && subset diffs [ Slug_f; Rank ] then
+ Some "C14"
else None
(* ---------------------------------------------------------------------- *)
@@ -591,7 +573,7 @@ let test_dates_align () =
in
Alcotest.(check (list (pair string string))) "no misaligned dates" [] mismatched
-(* The core assertion: every one of the 5595 raw differences is either
+(* The core assertion: every one of the 4975 raw differences is either
normalised away (Layers A/B) or named in the cited allow-list (Layer C).
Nothing else is permitted to pass silently. *)
let test_no_unexplained_differences () =