summaryrefslogtreecommitdiff
path: root/test/test_golden.ml
blob: ba25d8a4ad0361004d361b8e0e688cdb14ddd618 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
(* Task 17: golden pins -- validation layer 5 of the design spec's five
   (colitur CLAUDE.md "Validation" section; layers 1-4 -- Types, Property,
   the lectio differential, the missalemeum oracle -- are already built and
   green: test_validate.ml, test_differential.ml, test_oracle.ml).

   *** THE ONE RULE THIS FILE IS BUILT AROUND ***

   A golden test blesses whatever it is given. Every other layer derives its
   expectation from something independent of colitur's own output -- an
   invariant, a sibling engine, a published calendar oracle. This layer's
   expectation comes from ME, so every literal string below was checked by
   hand, against sources OUTSIDE this codebase, BEFORE it was typed in here
   -- never copied from a `colitur day` run and then rationalised:

   1. The three Easter dates this file pins (1598-03-22, 1666-04-25,
      2038-04-25) were independently computed by hand using the standard
      Gauss/Meeus Gregorian Easter algorithm (the same algorithm register
      §0's "first Sunday on/after the ecclesiastical full moon on/after 21
      March" reduces to), NOT read off Computus.gregorian_easter or the CLI.
      All three matched colitur's own output exactly on the first attempt.
   2. EVERY weekday asserted below was independently cross-checked against
      `date -d <iso-date> +%A` (GNU coreutils, glibc's own proleptic-
      Gregorian calendar arithmetic -- a completely separate implementation
      from anything in this repository) before being written into a literal
      string. All matched.
   3. Every precedence/transfer/commemoration outcome was traced by hand
      against its RG citation (quoted or paraphrased in each test's own
      comment) and against the actual `band`/`disposition`/`admit`/
      `transfer_target` logic in precedence_ef.ml, not merely observed to
      "look plausible". Where the primary text alone does not fully settle
      an outcome (the 2008 Annunciation/Joseph tie, see that test's own
      comment), this is said explicitly rather than papered over with a
      confident-sounding citation.

   *** WHY [describe] BUILDS ONE STRING PER DAY ***

   Every assertion below compares ONE formatted line against a single
   expected literal, rather than five or six separate field checks. This is
   deliberate: a one-line diff on failure still names the day (it is the
   first token) and shows exactly which field changed (Alcotest's own diff
   highlights the differing substring) -- "prove the pins have teeth" in the
   brief's own words -- while keeping each test's body to one literal per
   date instead of five, which is what makes 30-odd pinned dates reviewable
   at all. [describe]'s own five components (season/week/slug/rank/colour,
   commemorations, transferred_in, transferred_out) are exactly the fields
   {!Colitur_kernel.Liturgical_day.t} promises never to lose (that type's own
   doc comment) -- nothing is cherry-picked to make a case look cleaner than
   it is. *)

module Cal = Colitur_kernel.Calendar
module Layer = Colitur_kernel.Layer
module Overlay = Colitur_kernel.Overlay
module LD = Colitur_kernel.Liturgical_day
module Slug = Colitur_kernel.Slug
module Date = Colitur_kernel.Date
module Cel = Colitur_kernel.Celebration
module Colour = Colitur_kernel.Colour
module Citation = Colitur_kernel.Citation
module Temporal = Colitur_kernel.Temporal
module Prec = Colitur_kernel.Precedence
module V = Rite_ef.Vocab_ef

(* Same relative paths every other suite in this directory uses (dune test
   runs from _build/default/test/). *)
let sanctoral_path = "../data/ef/sanctoral.sexp"
let adjustments_path = "../data/ef/adjustments.sexp"
let lectionary_path = "../data/ef/lectionary.sexp"
let commons_path = "../data/ef/commons.sexp"

(* Loaded once at module init, same convention test_validate.ml's own
   [real_ef_layer] uses (not test_oracle.ml/test_differential.ml's
   per-test-case reload, which recomputes a whole 46-year or 2-year sweep
   per call for reasons specific to those files -- loading the immutable
   layer itself has no such per-call cost and nothing here mutates it). *)
let real_ef_layer =
  match Layer.load V.rank_of_sexp sanctoral_path with
  | Error e -> failwith (Printf.sprintf "%s: failed to load: %s" sanctoral_path e)
  | Ok layer -> (
      match Overlay.load V.rank_of_sexp adjustments_path with
      | Error e -> failwith (Printf.sprintf "%s: failed to load: %s" adjustments_path e)
      | Ok overlay ->
          let layer, diagnostics = Overlay.apply layer overlay in
          if diagnostics <> [] then
            failwith
              (Printf.sprintf "unexpected overlay diagnostics: %s"
                 (String.concat "; " (List.map Overlay.diagnostic_to_string diagnostics)));
          layer)

(* [Rite_ef.context] takes [~lectionary] (fix round 1, coordinator review) --
   caller-supplied, same as [real_ef_layer] above. *)
let real_ef_lectionary =
  match Colitur_kernel.Lectionary.load lectionary_path with
  | Error e -> failwith (Printf.sprintf "%s: failed to load: %s" lectionary_path e)
  | Ok l -> l

(* The Commons (data/ef/commons.sexp) travel the same caller-supplied seam
   as the lectionary above, and [~commons] is required rather than defaulted
   so that no caller can silently run with none. CORRECTED, Task 9 fix
   round 1 (coordinator review): this used to claim "nothing in layers 3-5
   compares reading citations" -- stale since Task 8 (layer 3) and Task 9
   (layer 4). THIS layer (5, golden pins) is the one that still does not:
   this file's own [describe] carries no citation field at all, so a rite
   quietly missing its Commons would still be invisible here specifically,
   even though layers 3/4 would now catch it. Loaded here even where this
   file asserts nothing about readings, so that the rite under test is the
   same one bin/main.ml assembles. *)
let real_ef_commons =
  match Rite_ef.Lectionary_ef.Commons.load commons_path with
  | Error e -> failwith (Printf.sprintf "%s: failed to load: %s" commons_path e)
  | Ok c -> c

let real_ef_rite = Rite_ef.context ~lectionary:real_ef_lectionary ~commons:real_ef_commons

let mk y m d = match Date.make ~year:y ~month:m ~day:d with Ok t -> t | Error e -> failwith e

(* [Cal.day] recomputes its whole liturgical year on every call (calendar.mli
   -- "pure, no cache"); acceptable here, same as test_rite_ef.ml's own use
   of [Cal.year] for a handful of dates, not a hot loop. *)
let fetch y m d = Cal.day real_ef_rite real_ef_layer (mk y m d)

let slug_s (c : V.rank Cel.t) = Slug.to_string c.Cel.slug
let rank_s (c : V.rank Cel.t) = V.rank_to_string c.Cel.rank
let colour_s (c : V.rank Cel.t) = Colour.to_string c.Cel.colour
let subject_s (c : V.rank Cel.t) = Colitur_kernel.Subject.to_string c.Cel.subject
let la = Colitur_kernel.Lang.of_string_exn "la"
let name_la_s (c : V.rank Cel.t) = match Colitur_kernel.Names.find c.Cel.names la with Some n -> n | None -> "-"
let privilege_s = function Prec.Privileged -> "privileged" | Prec.Ordinary -> "ordinary"

(* fix round 1 (coordinator finding 4): [subject] added. Two of the three
   original Holy Family pins (2026-01-11, 2024-01-07) were byte-identical
   before and after temporal_ef.ml's own fix -- reverting the feature failed
   only the 2030-01-13 collision pin, because [describe] omitted the ONE
   field the change touches on an ordinary (non-collision) year. Vacuity
   flavour #1 ("an assertion already true before the code under test ran"),
   not #8 ("promoted from actual output") -- the OTHER nine fields' values
   were independently sourced and remain so; only this tenth was missing.

   The five fields {!Colitur_kernel.Liturgical_day.t} promises are never
   silently lost, folded into one comparable line -- see the file header for
   why one string, not five checks.

   ef-triduum-litanies task (Gap 1), same lesson applied pre-emptively rather
   than caught after the fact: [name_la] added for the SAME reason [subject]
   was -- a names-only change (the Sacred Triduum's own Latin identity, RG 91
   entry 2) would otherwise be invisible to every pin whose rank/colour/slug
   are already correct (this project's own catalogued vacuity flavour #1,
   "an assertion already true before the code under test ran" -- this task's
   own hazard, stated in its brief). Read off [Cel.names]'s own Latin entry
   (the same field/language {!Cel.of_list}'s callers in temporal_ef.ml use
   for Holy Family/Holy Name/the Triduum -- no temporal-cycle candidate
   currently carries any OTHER language), "-" for none, matching [week]'s
   own None-as-"-" convention immediately above. *)
let describe (day : (V.season, V.rank) LD.t) =
  let t = day.LD.temporal in
  let week = match t.Temporal.week with Some n -> string_of_int n | None -> "-" in
  let comms =
    List.map (fun (c, p) -> Printf.sprintf "%s:%s" (slug_s c) (privilege_s p)) day.LD.commemorations
    |> List.sort compare |> String.concat ","
  in
  let tin = match day.LD.transferred_in with None -> "-" | Some c -> slug_s c in
  let tout =
    List.map (fun (c, d) -> Printf.sprintf "%s->%s" (slug_s c) (Date.to_iso8601 d)) day.LD.transferred_out
    |> List.sort compare |> String.concat ","
  in
  Printf.sprintf "%s %s season=%s week=%s slug=%s rank=%s colour=%s subject=%s name_la=%s comms=[%s] in=%s out=[%s]"
    (Date.to_iso8601 day.LD.date)
    (Date.weekday_to_string t.Temporal.weekday)
    (V.season_to_string t.Temporal.season)
    week (slug_s day.LD.observed) (rank_s day.LD.observed) (colour_s day.LD.observed)
    (subject_s day.LD.observed) (name_la_s day.LD.observed) comms tin tout

let check ~msg y m d expected = Alcotest.(check string) msg expected (describe (fetch y m d))

(* ---- Task 10: the reading citations ----

   A SEPARATE formatter rather than widening [describe] itself, and the
   reason is this file's own opening rule, not convenience. Widening
   [describe] would invalidate all ~30 expected literals above at once, and
   the only practical way to restore them would be to run the suite and paste
   back whatever colitur printed -- "copied from a `colitur day` run and then
   rationalised", the exact thing the header forbids. Restoring them
   HONESTLY would mean hand-verifying 30 further Masses against the scans,
   which is a task of its own and not this one.

   So the trade is stated rather than hidden: the pins above stay
   citation-blind, and the pins below carry citations for one day per chain
   step, every literal hand-verified against the photographic scans BEFORE
   being typed here (each pin's own comment carries its scan line numbers).
   That division is defensible because layer 2 now covers what breadth would
   have bought: {!Colitur_kernel.Validate}'s own citation checks assert the
   SHAPE of every day's citations in every year 1583-9999 -- far more days
   than 30 pins could -- so what layer 5 still owes is specific VALUES on the
   distinct routes, which is exactly what these six are. *)
let describe_readings (day : (V.season, V.rank) LD.t) =
  let part_ref p =
    match
      List.find_opt (fun (c : Citation.t) -> c.Citation.part = p) day.LD.citations
    with
    | Some c -> c.Citation.reference
    | None -> "-"
  in
  Printf.sprintf "%s slug=%s first=%s gospel=%s"
    (Date.to_iso8601 day.LD.date)
    (slug_s day.LD.observed)
    (part_ref Citation.First) (part_ref Citation.Gospel)

let check_readings ~msg y m d expected =
  Alcotest.(check string) msg expected (describe_readings (fetch y m d))

let omitted_has (day : (V.season, V.rank) LD.t) slug =
  List.exists (fun (c, _) -> slug_s c = slug) day.LD.omitted

(* ------------------------------------------------------------------ *)
(* Easter extremes (register §0; independently Gauss-computed, see file
   header point 1). extreme_years in test_validate.ml already pins that
   1598/1666 are the true earliest/latest within 1583..2500 -- this file
   pins what the RESOLVED DAY looks like there, not merely that Easter
   lands on the expected date. *)
(* ------------------------------------------------------------------ *)

(* 1598: earliest Gregorian Easter possible, 22 March -- hand-verified via
   Gauss's algorithm (a = 2, b = 15, c = 98, ..., h = 0, l = 0, m = 0 ->
   22 March), independent of Computus.gregorian_easter. RG 76 (Paschaltide
   begins with the Vigil Mass) + RG 91 entry 1 (Easter Sunday, I class) +
   RG 119(b) (white from the Vigil Mass) fix season/rank/colour; the day
   before (Holy Saturday, still Passiontide, violet per RG 128) is pinned
   alongside it to prove the season boundary itself falls in the right
   place, not merely that 22 March is white. *)
let test_easter_extreme_1598 () =
  check ~msg:"1598-03-21 Holy Saturday: still Passiontide, violet (RG 128)" 1598 3 21
    "1598-03-21 saturday season=passiontide week=2 slug=ef-passiontide-2-saturday rank=class-1 colour=violet subject=lord \
     name_la=Sabbato sancto comms=[] in=- out=[]";
  check ~msg:"1598-03-22 earliest possible Easter (Gauss-verified): Paschaltide begins, white, class-1" 1598 3
    22
    "1598-03-22 sunday season=paschaltide week=1 slug=ef-easter-sunday rank=class-1 colour=white subject=temporal name_la=- comms=[] in=- \
     out=[]"

(* 1666: latest Gregorian Easter possible, 25 April -- hand-verified via
   Gauss's algorithm (a = 13, b = 16, c = 66, ..., h = 29, l = 5, m = 0 ->
   25 April). Same citations as 1598 above.

   ef-major-litanies task: 25 April = Easter Sunday is RG 80's own FIRST
   trigger condition ("si vero eo die occurrit dominica Paschatis...
   transferuntur in sequentem feriam III") -- this pin, already the
   project's own hand-verified latest-Easter witness, now ALSO carries the
   transfer this rule requires: [major-litanies] departs 25 April for
   27 April (Easter+2, "the following Tuesday" -- Precedence_ef.transfer_
   target's own Litanies branch has the full citation). Nothing else about
   this day changes: [observed]/[comms]/[in] are exactly as before,
   proving the Litanies departure is additive, not disruptive, to the
   day Easter itself owns outright. *)
let test_easter_extreme_1666 () =
  check ~msg:"1666-04-24 Holy Saturday: still Passiontide, violet (RG 128)" 1666 4 24
    "1666-04-24 saturday season=passiontide week=2 slug=ef-passiontide-2-saturday rank=class-1 colour=violet subject=lord \
     name_la=Sabbato sancto comms=[] in=- out=[]";
  check ~msg:"1666-04-25 latest possible Easter (Gauss-verified): Paschaltide begins, white, class-1" 1666 4
    25
    "1666-04-25 sunday season=paschaltide week=1 slug=ef-easter-sunday rank=class-1 colour=white subject=temporal name_la=- comms=[] in=- \
     out=[major-litanies->1666-04-27]";
  (* The transfer's own target, positively pinned (not merely inferred from
     [out] above): an ordinary Easter-octave Tuesday, I class, white (RG
     119 -- white "a Missa Vigiliae paschalis usque ad Missam vigiliae
     Pentecostis"), with the Litanies as its own sole, privileged
     commemoration (RG 109(f); RG 111(a), "I class: none save one
     privileged" -- {!PE.admit}'s own [Class1] case). No [transferred_in]
     printed: [Liturgical_day.transferred_in] only ever names a candidate
     that went on to WIN the day (calendar.ml's own [build_day]), and the
     Litanies structurally never can (RG 81, Commemoration_only) -- this is
     the SAME asymmetry {!Colitur_kernel.Calendar}'s own [settled_at] fix
     (this task) exists to get right on the OTHER side of the ledger. *)
  check ~msg:"1666-04-27 Easter+2: the Litanies' own transfer target, commemorated and privileged" 1666 4 27
    "1666-04-27 tuesday season=paschaltide week=1 slug=ef-easter-1-tuesday rank=class-1 colour=white subject=temporal \
     name_la=- comms=[major-litanies:privileged] in=- out=[]"

(* 2038: the brief's "late modern case" -- Easter also 25 April (Gauss-
   verified: a = 5, b = 20, c = 38, ..., h = 29, l = 5, m = 0 -> 25 April,
   the SAME extreme as 1666, reached independently), chosen because it is a
   near-term year rather than a 17th-century one.

   ORIGINAL EXCLUSION, now RESOLVED (ef-rebootstrap, 2026-08-12): this
   comment used to say 2038-03-06 (+sts-felicitas-perpetua), 2038-03-08
   (+john-of-god) and 2038-03-09 (+frances-rome) were deliberately left
   unpinned because they were WRONG -- three sanctoral entries bootstrapped
   from lectio carried a `Commemoration_only` status that should have been a
   real III-class Feast. That defect was in lectio's own generator, not
   fixable without touching lectio; lectio's generator has SINCE been fixed
   (source SHA-256 6a25e634... -> 1b303ef2...) and colitur re-bootstrapped
   from it, so this specific exclusion reason no longer holds. These three
   dates, plus three more of the same shape (2008-04-02/04/05), are now
   pinned below in [test_iii_class_feast_wins_class4_feria] -- the deferred
   pin this comment always pointed at, not a new finding. Kept as a
   temporal-cycle-only test regardless (the two concerns -- Easter-extreme
   arithmetic and a sanctoral occurrence rule -- stay separate pins even
   though nothing forces that any more). *)
(* ef-major-litanies task: 2038 is this file's own worked example for the
   OTHER RG 80 trigger shape checked elsewhere in this task (Easter Monday
   = 25 April, e.g. 2011) landing on the identical target (Easter+2) --
   here Easter SUNDAY itself is 25 April, so the "following Tuesday" is
   two days later, 27 April, not one; both shapes converge on Easter+2,
   never on Easter+1 or Easter+3 (precedence_ef.ml's own [transfer_target]
   Litanies branch has the full RG 80 citation and the arithmetic for
   both shapes). *)
let test_easter_extreme_2038_late_modern () =
  check ~msg:"2038-04-24 Holy Saturday: still Passiontide, violet (RG 128)" 2038 4 24
    "2038-04-24 saturday season=passiontide week=2 slug=ef-passiontide-2-saturday rank=class-1 colour=violet subject=lord \
     name_la=Sabbato sancto comms=[] in=- out=[]";
  check ~msg:"2038-04-25 late-modern instance of the latest possible Easter (Gauss-verified)" 2038 4 25
    "2038-04-25 sunday season=paschaltide week=1 slug=ef-easter-sunday rank=class-1 colour=white subject=temporal name_la=- comms=[] in=- \
     out=[major-litanies->2038-04-27]";
  check ~msg:"2038-04-27 Easter+2: the Litanies' own transfer target, commemorated and privileged" 2038 4 27
    "2038-04-27 tuesday season=paschaltide week=1 slug=ef-easter-1-tuesday rank=class-1 colour=white subject=temporal \
     name_la=- comms=[major-litanies:privileged] in=- out=[]"

(* ------------------------------------------------------------------ *)
(* Annunciation transfer, 25 March inside Holy Week (register §4, RG 96
   Attamen (a)). 2016: Easter = 27 March (Gauss-independent of this file's
   own concern -- already covered by extreme-year hand-checks above; 2016 is
   an ordinary year, trusted via the property sweep + the weekday
   cross-check below), so 25 March 2016 falls on GOOD FRIDAY itself --
   literally inside Holy Week, not merely "impeded by the Easter octave" the
   way the 1598/2008/1666 cases are. Independently confirmed: `date -d
   2016-03-25 +%A` = Friday, `date -d 2016-03-27 +%A` = Sunday (Easter). *)
let test_annunciation_transfer_inside_holy_week_2016 () =
  (* RG 91 entry 2 (Sacred Triduum) outranks entry 11 (an ordinary universal
     I-class feast) outright -- Good Friday wins, Annunciation is
     Transfer-disposed (RG 95: only I-class feasts transfer). RG 23 (I-class
     ferias admit no commemoration except one privileged) leaves nothing on
     the day itself: register's own note on RG 23 says this branch "never
     actually reaches a live case" for exactly this reason. *)
  check ~msg:"2016-03-25 Good Friday: Triduum outranks the Annunciation (RG91 entry 2 > 11), no commemoration"
    2016 3 25
    "2016-03-25 friday season=passiontide week=2 slug=ef-passiontide-2-friday rank=class-1 colour=black subject=lord \
     name_la=Feria VI in Passione et Morte Domini comms=[] in=- out=[annunciation-of-the-blessed-virgin-mary->2016-04-04]";
  (* The general RG 96 walk from 26 March would still be inside the Triduum,
     the Easter octave (all I class, entry 2/10) -- carrying the feast PAST
     Easter -- so RG 96 Attamen (a)'s condition fires: sedes propria = Monday
     after Low Sunday = Easter + 8 = 27 Mar + 8 = 4 April. *)
  check ~msg:"2016-04-04 Easter+8 (Monday after Low Sunday): RG96 Attamen(a) sedes propria fires" 2016 4 4
    "2016-04-04 monday season=paschaltide week=2 slug=annunciation-of-the-blessed-virgin-mary rank=class-1 \
     colour=white subject=saint name_la=- comms=[] in=annunciation-of-the-blessed-virgin-mary out=[]"

(* ------------------------------------------------------------------ *)
(* 2008: the double transfer (register §4, "cases worth adding"). Easter =
   23 March 2008 (trusted via the property sweep, not independently
   Gauss-checked a fourth time -- the algorithm is already hand-verified
   above; what this case adds is the TWO-CANDIDATE collision, not a fourth
   Easter-date check). Independently confirmed: `date -d 2008-03-19 +%A` =
   Wednesday, `-03-31` = Monday, `-04-01` = Tuesday. *)
let test_annunciation_joseph_double_transfer_2008 () =
  (* St Joseph (19 March, I class, universal, band 11 -- same table entry as
     the Annunciation) falls on Wednesday of Holy Week (RG 91 entry 7: an
     I-class feria, band 7, above 11) -- impeded, Transfer-disposed. *)
  check ~msg:"2008-03-19 Wednesday of Holy Week outranks Joseph (RG91 entry 7 > 11), no commemoration" 2008 3
    19
    "2008-03-19 wednesday season=passiontide week=2 slug=ef-passiontide-2-wednesday rank=class-1 colour=violet subject=temporal \
     name_la=- comms=[] in=- out=[joseph-spouse-of-the-bl-virgin-mary->2008-04-01]";
  (* Both Joseph and the Annunciation are band 11 (register: neither is
     Immaculate Conception/Assumption, band 4; both are ordinary universal
     I-class feasts). RG 96 Attamen (a) is CONDITIONAL, so it is evaluated
     for BOTH: the Annunciation's own general walk from 26 March would cross
     the Easter octave and land past Easter, so its exception fires --
     Easter + 8 = 23 Mar + 8 = 31 March, its NAMED sedes propria. Joseph has
     no such named exception; its own general walk (from 20 March) also
     crosses the octave and, absent Annunciation, would land on the SAME 31
     March. WEAKER-THAN-IT-LOOKS, stated honestly: RG 97-98's plain text
     ("the higher in the table is kept") does not itself distinguish two
     candidates tied at the SAME table entry (11) the way it does for a
     genuine dignity difference -- the engine breaks this specific tie by
     slug (calendar.ml's own [compare_deferred]: alphabetical, an
     engineering convention, not itself an RG citation), and
     "annunciation-of-the-blessed-virgin-mary" < "joseph-spouse-of-the-bl-
     virgin-mary" is exactly why Annunciation claims 31 March first in
     [place_transfers]'s per-round sort. A textual argument beyond the bare
     tie-break does exist -- the Annunciation's own Attamen clause names its
     target as a "sedes propria" (proper seat), a positively-assigned day,
     where Joseph is merely wherever the generic RG96 search happens to
     land -- but this is this report's own reading of the primary text, not
     something the register states outright, so it is offered as
     corroboration, not as the citation carrying the outcome. *)
  check ~msg:"2008-03-31 Easter+8: Annunciation's named sedes propria (RG96 Attamen a) claims the day first"
    2008 3 31
    "2008-03-31 monday season=paschaltide week=2 slug=annunciation-of-the-blessed-virgin-mary rank=class-1 \
     colour=white subject=saint name_la=- comms=[] in=annunciation-of-the-blessed-virgin-mary out=[]";
  (* Joseph's own general RG96 walk, finding 31 March already claimed
     (occupant reports Class1 there, still blocking per [is_blocking]),
     continues one more day to 1 April -- RG 97-98's "in order": the second
     I-class candidate to reach an already-occupied target keeps searching
     rather than displacing the first. *)
  check ~msg:"2008-04-01: Joseph's RG96 walk continues past the now-claimed 31 March (RG97-98 'in order')" 2008
    4 1
    "2008-04-01 tuesday season=paschaltide week=2 slug=joseph-spouse-of-the-bl-virgin-mary rank=class-1 \
     colour=white subject=saint name_la=- comms=[] in=joseph-spouse-of-the-bl-virgin-mary out=[]"

(* ------------------------------------------------------------------ *)
(* RG 96 Attamen (a) is CONDITIONAL ("quando est transferendum post
   Pascha") -- pinning both sides so the condition itself, not merely the
   exception's existence, is under test. All six weekdays independently
   confirmed via `date -d`. *)
(* ------------------------------------------------------------------ *)

(* Before Easter: 2057 (Easter 22 Apr), 2007 and 2012 (both Easter 8 Apr).
   In each, 25 March is a Sunday impeding the Annunciation (Lent III in
   2057; Passion Sunday, Dominica I Passionis, in 2007/2012), and the
   GENERAL RG96 walk lands on 26 March -- an ordinary III-class feria, band
   22/25, well before Easter -- so the condition never fires: no named
   exception, just the plain walk. Each arrival day's own displaced feria is
   RG25/RG109(e)-privileged (an ordinary Lent/Passiontide feria, impeded,
   must be commemorated) -- a second, independent confirmation of the
   RG24/25/RG109(e) mandate this file also pins directly below at 1900/1902,
   in a different code path (an ARRIVING transferred feast's displaced
   office, not a plain sanctoral winner's). *)
let test_annunciation_exception_not_triggered_general_walk_suffices () =
  check ~msg:"2057-03-26: general RG96 walk suffices (target before Easter, 22 Apr) -- no Attamen(a) exception"
    2057 3 26
    "2057-03-26 monday season=lent week=3 slug=annunciation-of-the-blessed-virgin-mary rank=class-1 \
     colour=white subject=saint name_la=- comms=[ef-lent-3-monday:privileged] in=annunciation-of-the-blessed-virgin-mary out=[]";
  check ~msg:"2007-03-26: general RG96 walk suffices (target before Easter, 8 Apr) -- no Attamen(a) exception"
    2007 3 26
    "2007-03-26 monday season=passiontide week=1 slug=annunciation-of-the-blessed-virgin-mary rank=class-1 \
     colour=white subject=saint name_la=- comms=[ef-passiontide-1-monday:privileged] in=annunciation-of-the-blessed-virgin-mary out=[]";
  check ~msg:"2012-03-26: general RG96 walk suffices (target before Easter, 8 Apr) -- no Attamen(a) exception"
    2012 3 26
    "2012-03-26 monday season=passiontide week=1 slug=annunciation-of-the-blessed-virgin-mary rank=class-1 \
     colour=white subject=saint name_la=- comms=[ef-passiontide-1-monday:privileged] in=annunciation-of-the-blessed-virgin-mary out=[]"

(* After Easter: 2016 (already pinned above, Easter+8 = 4 April) and 2024
   (Easter 31 March, Easter+8 = 8 April). In both, the general walk would
   cross Easter, so Attamen(a) fires and lands the feast on the Monday after
   Low Sunday exactly. Both arrival days are Paschaltide-2 (Class4
   ferias) -- RG26 (IV-class ferias never commemorated), so unlike the
   "before" trio above, no commemoration is left behind; a second,
   independent confirmation of RG26 in yet another code path. *)
let test_annunciation_exception_triggered_when_walk_would_cross_easter () =
  check
    ~msg:
      "2024-04-08: Easter+8 (Easter=31 Mar): RG96 Attamen(a) fires, arrival day is Class4 so no commemoration \
       (RG26)"
    2024 4 8
    "2024-04-08 monday season=paschaltide week=2 slug=annunciation-of-the-blessed-virgin-mary rank=class-1 \
     colour=white subject=saint name_la=- comms=[] in=annunciation-of-the-blessed-virgin-mary out=[]"
(* 2016's own pin (test_annunciation_transfer_inside_holy_week_2016) is the
   other half of this pair -- not repeated here to avoid asserting the same
   date twice. *)

(* ------------------------------------------------------------------ *)
(* 2011-07-04, the Precious Blood (register §4/§6, differential C10).
   Sacred Heart (Easter + 68, RG 91 entry 3) coincides with the fixed 1 July
   feast of the Most Precious Blood (entry 11) in 2011 (Easter = 24 April,
   so Easter + 68 = 1 July). Entry 3 outranks entry 11 outright (RG 97-98:
   the higher in the table is kept) -- no tie, unlike the 2008 case above.
   The general RG96 walk from 2 July must skip Visitation (2 July, II
   class, entry 19) and the ordinary Sunday (3 July, entry 15, II class)
   before landing on 4 July, an ordinary Class4 feria. All four weekdays
   independently confirmed via `date -d`. *)
let test_precious_blood_transfer_2011 () =
  check ~msg:"2011-07-01: Sacred Heart (RG91 entry 3) outranks the fixed Precious Blood (entry 11) outright"
    2011 7 1
    "2011-07-01 friday season=time-after-pentecost week=2 slug=ef-sacred-heart rank=class-1 colour=white subject=temporal \
     name_la=- comms=[] in=- out=[precious-blood-of-our-lord-jesus-christ->2011-07-04]";
  check ~msg:"2011-07-02: the RG96 walk's first blocked day (Visitation, II class) -- Precious Blood not here"
    2011 7 2
    "2011-07-02 saturday season=time-after-pentecost week=2 slug=visitation-of-the-blessed-virgin-mary \
     rank=class-2 colour=white subject=saint name_la=- comms=[processus-and-martinian:ordinary] in=- out=[]";
  check ~msg:"2011-07-03: the RG96 walk's second blocked day (an ordinary Sunday, II class) -- still not here"
    2011 7 3
    "2011-07-03 sunday season=time-after-pentecost week=3 slug=ef-time-after-pentecost-sunday-3 rank=class-2 \
     colour=green subject=temporal name_la=- comms=[] in=- out=[]";
  check ~msg:"2011-07-04: the first non-I/II-class day (RG96) -- the Precious Blood lands here" 2011 7 4
    "2011-07-04 monday season=time-after-pentecost week=3 slug=precious-blood-of-our-lord-jesus-christ \
     rank=class-1 colour=red subject=lord name_la=- comms=[] in=precious-blood-of-our-lord-jesus-christ out=[]"

(* ------------------------------------------------------------------ *)
(* All Souls falling on a Sunday (register §4, RG96 Attamen (b), primary-
   source-verified: "quando occurrit cum dominica, transfertur ... in feriam
   II sequentem"). 2025: All Souls (2 Nov) falls on an ordinary Sunday.
   Weekdays independently confirmed via `date -d`. *)
let test_all_souls_on_a_sunday_2025 () =
  (* RG91 entry 8's own parenthetical ("yields to an occurring Sunday") plus
     Attamen (b): the Sunday is simply observed, and RG94 ("a fixed-day
     commemoration is not carried with a transferred feast") means the
     transferred All Souls leaves nothing behind on the Sunday itself. *)
  check ~msg:"2025-11-02: All Souls yields to the occurring Sunday (RG91 entry 8), no commemoration (RG94)" 2025
    11 2
    "2025-11-02 sunday season=time-after-pentecost week=21 slug=ef-time-after-pentecost-sunday-21 rank=class-2 \
     colour=green subject=temporal name_la=- comms=[] in=- out=[commemoration-of-all-souls->2025-11-03]";
  check ~msg:"2025-11-03: All Souls transfers to the following Monday, its sedes propria (RG96 Attamen b)" 2025
    11 3
    "2025-11-03 monday season=time-after-pentecost week=21 slug=commemoration-of-all-souls rank=class-1 \
     colour=black subject=saint name_la=- comms=[] in=commemoration-of-all-souls out=[]"

(* ------------------------------------------------------------------ *)
(* Christmas falling on a Sunday (RG91 entry 1: Nativity, Easter, Pentecost
   are I class WITH THEIR OWN OCTAVE, listed first in the whole table --
   temporal_ef.ml assigns 25 December the Nativity office unconditionally,
   so unlike a Feast of the Lord merely REPLACING an occurring Sunday (RG
   16(a), register §6), there is no Sunday-of-Advent/-per-annum candidate to
   even contest here: 25 December is never, in the EF temporal cycle, an
   "ordinary Sunday" in the first place. 2022: `date -d 2022-12-25 +%A` =
   Sunday, independently confirmed. *)
let test_christmas_on_a_sunday_2022 () =
  check ~msg:"2022-12-25 falling on a Sunday: the Nativity (RG91 entry 1) takes the day outright, no contest"
    2022 12 25
    "2022-12-25 sunday season=christmastide week=- slug=ef-nativity rank=class-1 colour=white subject=temporal name_la=- comms=[] in=- \
     out=[]"

(* ------------------------------------------------------------------ *)
(* Holy Thursday is white (register §3b, RG 128(b) + RG 122, Task 16) while
   the surrounding Passiontide days stay violet -- the case the brief flags
   because colitur and lectio previously AGREED on violet (both wrong), so
   the lectio differential was structurally incapable of ever catching this;
   only a golden pin (or the missalemeum oracle, layer 4) can. 2026: Easter
   5 April (trusted via the property sweep). Weekdays independently
   confirmed via `date -d`. *)
let test_holy_thursday_is_white_2026 () =
  check ~msg:"2026-04-01 (Wednesday of Holy Week): ordinary Passiontide violet (RG128)" 2026 4 1
    "2026-04-01 wednesday season=passiontide week=2 slug=ef-passiontide-2-wednesday rank=class-1 colour=violet subject=temporal \
     name_la=- comms=[] in=- out=[]";
  check
    ~msg:
      "2026-04-02 Holy Thursday: white (RG128(b)'s named exception + RG122's affirmative statement), not \
       Passiontide's violet"
    2026 4 2
    "2026-04-02 thursday season=passiontide week=2 slug=ef-passiontide-2-thursday rank=class-1 colour=white subject=lord \
     name_la=Feria V in Cena Domini comms=[] in=- out=[]";
  check ~msg:"2026-04-03 Good Friday: back to violet (RG128) -- Thursday's white is a one-day exception" 2026 4
    3
    "2026-04-03 friday season=passiontide week=2 slug=ef-passiontide-2-friday rank=class-1 colour=black subject=lord \
     name_la=Feria VI in Passione et Morte Domini comms=[] in=- out=[]"

(* ------------------------------------------------------------------ *)
(* Advent/Lent Ember ferias are commemorated when impeded (RG 24: "si vero
   impediuntur, commemorari debent" -- MUST be commemorated) while IV-class
   ferias never are (RG 26: "quae nunquam commemorantur") -- the fix-round-1
   F1/F2 finding, a Critical bug caught late precisely because no golden pin
   existed for it before now. Weekdays independently confirmed via
   `date -d`. *)
(* ------------------------------------------------------------------ *)

let test_ember_ferias_commemorated_when_impeded () =
  (* 1900-12-21: Advent Ember Friday (RG91 entry 18, II class) impeded by St
     Thomas (II class, universal, entry 16 -- Thomas outranks an II-class
     feria only because entry 16 < 18 in the table). RG24 makes the Ember
     feria's commemoration MANDATORY, and RG109(e) (corrected, fix round 1)
     makes it PRIVILEGED, not merely eligible. *)
  check ~msg:"1900-12-21: Advent Ember Friday, impeded, MUST be commemorated (RG24) and is privileged (RG109e)"
    1900 12 21
    "1900-12-21 friday season=advent week=3 slug=thomas rank=class-2 colour=red subject=saint \
     name_la=- comms=[ef-advent-ember-fri:privileged] in=- out=[]";
  (* 1902-02-22: Lent Ember Saturday (RG91 entry 18, II class) impeded by
     the Chair of St Peter (II class, universal, entry 16). Same RG24/
     RG109(e) mandate. ALSO, ef-holyname-rg110 task: RG 110's own
     inseparable Peter/Paul commemoration -- "in Officio ... S. Petri semper
     fit commemoratio S. Pauli" -- fires unconditionally whenever
     [chair-of-st-peter] is observed, uncapped by RG 111's own admission
     count, so `+paul` now rides alongside the Ember feria's own
     commemoration rather than being displaced by it (precedence_ef.ml's own
     [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.

     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 Chair of St Peter is WHITE, \
              named by date in RG120(f)'s own enumeration of white ('Cathedrae S. Petri (22 februarii)'). \
              THIS PIN WAS RIGHT AND WAS EDITED TO ACCOMMODATE A WRONG CHANGE: the ef-sanctoral-audit task \
              set it to red on RG124(b), whose 'in eorum die natalicio' qualifier does not reach the Chair, \
              and rewrote this assertion rather than treating its failure as evidence. Restored by the \
              fix-round review. A pin that resists a change is evidence, not an obstacle"
    1902 2 22
    "1902-02-22 saturday season=lent week=1 slug=chair-of-st-peter rank=class-2 colour=white subject=saint \
     name_la=- comms=[ef-lent-ember-sat:privileged,paul:ordinary] in=- out=[]"

let test_iv_class_ferias_never_commemorated () =
  (* 2026-07-22: an ordinary Time-after-Pentecost feria (Class4, RG91 entry
     28) impeded by St Mary Magdalene (III class). RG26 ("IV-class ferias
     are NEVER commemorated") means the displaced feria is dropped outright
     -- present in [omitted], never in [commemorations]. Asserting only
     "comms=[]" would be vacuous if there had been no losing candidate at
     all (one of this project's own catalogued vacuity flavours); the
     [omitted_has] check below proves a real candidate existed and was
     actively excluded, not merely absent. *)
  let d1 = fetch 2026 7 22 in
  Alcotest.(check bool) "2026-07-22: the displaced IV-class feria is in [omitted], proving RG26 actually fired"
    true
    (omitted_has d1 "ef-time-after-pentecost-8-wednesday");
  Alcotest.(check string) "2026-07-22: Mary Magdalene observed, no commemoration (RG26)"
    "2026-07-22 wednesday season=time-after-pentecost week=8 slug=mary-magdalene rank=class-3 colour=white subject=saint \
     name_la=- comms=[] in=- out=[]"
    (describe d1);
  let d2 = fetch 2026 8 10 in
  Alcotest.(check bool) "2026-08-10: the displaced IV-class feria is in [omitted], proving RG26 actually fired"
    true
    (omitted_has d2 "ef-time-after-pentecost-11-monday");
  Alcotest.(check string) "2026-08-10: St Lawrence observed, no commemoration (RG26)"
    "2026-08-10 monday season=time-after-pentecost week=11 slug=lawrence rank=class-2 colour=red subject=saint name_la=- comms=[] in=- \
     out=[]"
    (describe d2)

(* ------------------------------------------------------------------ *)
(* RG 111(b): a II-class SUNDAY admits only a commemoration "de festo II
   classis" -- a rank restriction, not "the best available ordinary
   candidate". 9 August falling on a Sunday: `romanus` (Class3,
   commemoration-only, register §6 notes his very existence is
   questionable, but that is irrelevant here -- RG111(b) excludes him on
   RANK alone, regardless of that open question) has no standing for the
   day's single slot. The brief names four recurring years (2009, 2015,
   2020, 2026); two are pinned here (2009 for an older-era instance, 2026
   for a current one) -- 2015/2020 were hand-checked via the CLI and found
   identical in shape (same slug family, same `romanus` exclusion, only the
   week number differs), so pinning all four would repeat the same
   assertion four times without exercising a different code path; rejected
   as duplicative. *)
let test_ii_class_sunday_admits_only_ii_class_commemoration () =
  let d2009 = fetch 2009 8 9 in
  Alcotest.(check bool) "2009-08-09: romanus (Class3) is offered and excluded, proving RG111(b) actually fired"
    true
    (omitted_has d2009 "romanus");
  Alcotest.(check string) "2009-08-09: the Sunday observed, no commemoration at all (RG111(b) rank floor)"
    "2009-08-09 sunday season=time-after-pentecost week=10 slug=ef-time-after-pentecost-sunday-10 rank=class-2 \
     colour=green subject=temporal name_la=- comms=[] in=- out=[]"
    (describe d2009);
  let d2026 = fetch 2026 8 9 in
  Alcotest.(check bool) "2026-08-09: romanus (Class3) is offered and excluded, proving RG111(b) actually fired"
    true
    (omitted_has d2026 "romanus");
  Alcotest.(check string) "2026-08-09: the Sunday observed, no commemoration at all (RG111(b) rank floor)"
    "2026-08-09 sunday season=time-after-pentecost week=11 slug=ef-time-after-pentecost-sunday-11 rank=class-2 \
     colour=green subject=temporal name_la=- comms=[] in=- out=[]"
    (describe d2026)

(* ------------------------------------------------------------------ *)
(* RG 16(a) (register §6.0, Caput III "De Dominicis", primary text quoted in
   this task's own report): "festum Domini I aut II classis, in dominica II
   classis occurrens, ... de dominica, proinde, nulla fit commemoratio" -- a
   Feast of the Lord, I or II class, impeding a II-class Sunday, takes the
   Sunday's own place with no commemoration of the Sunday at all, unlike
   every OTHER impeded II-class Sunday (RG 109(a)/RG 111(b), the
   [test_ii_class_sunday_admits_only_ii_class_commemoration] pin above).
   Two real dates, both directions, exactly the pair the task brief asks
   for. *)
(* ------------------------------------------------------------------ *)

(* 2028-08-06: the Transfiguration (6 Aug, II class, calendarium "IN
   TRANSFIGURATIONE D. N. I. C.") falls on an ordinary Time-after-Pentecost
   Sunday. `date -d 2028-08-06 +%A` = Sunday, independently confirmed. Also
   the exact date register §6.0's own reproduction names as a live instance
   of the bug this pin closes -- before this fix `colitur day` admitted
   "+ef-time-after-pentecost-sunday-9" here.

   CORRECTED, fix round 1 (IMPORTANT finding, item 2): this pin used to
   expect Pope Sixtus II et al. (6 August's own unrelated Class3 saint,
   Commemoration_only) admitted as an ordinary commemoration in the freed
   slot -- promoted from `colitur day`'s own actual output without
   consulting either the primary text or the oracle first (the exact
   vacuous-pin flavour the review caught, and this file's own header warns
   against). Re-derived properly this time:
   - PRIMARY TEXT: RG 16(a) itself says the winning Feast of the Lord holds
     the Sunday's place cum omnibus iuribus et privilegiis -- WITH ALL the
     Sunday's own rights and privileges. RG 111(b) is one of those: in
     dominicis II classis, una tantum admittitur commemoratio, SCILICET DE
     FESTO II CLASSIS -- a Sunday's own slot is reserved for a Class2
     candidate specifically. The day is still a dominica II classis for
     this purpose even though the Transfiguration, not the Sunday, is
     [observed] -- so Sixtus (Class3) has no standing for the slot at all,
     the same as on any other II-class Sunday (contrast
     [test_ii_class_sunday_admits_only_ii_class_commemoration] above,
     romanus, Class3, excluded the identical way on an ORDINARY II-class
     Sunday).
   - ORACLE, independently fetched (missalemeum's own /api/v5/calendar
     JSON, not the small-model webpage summary, which proved unreliable for
     this exact question): 2023-08-06 (a Sunday) -- title Transfiguration of
     Our Lord, commemorations empty, displaced Pope Sixtus II, Felicissimus
     and Agapitus, Martyrs -- Sixtus displaced, not commemorated. Control,
     2026-08-06 (a Thursday, no Sunday collision) -- Sixtus present in
     commemorations. Being a Sunday is exactly what excludes him.

   Deliberately NOT a bare "comms=[]" pin (this project's own catalogued
   vacuity flavour: a count/emptiness assertion that would pass even if
   nothing had ever been offered): [omitted_has] first proves the Sunday
   really WAS a candidate that reached [disposition] and was actively
   excluded, not merely never constructed. *)
let test_rg16a_lord_feast_no_sunday_commemoration_2028 () =
  let d = fetch 2028 8 6 in
  Alcotest.(check bool)
    "2028-08-06: the impeded Sunday is in [omitted], proving RG16(a) actively fired (not merely never offered)"
    true
    (omitted_has d "ef-time-after-pentecost-sunday-9");
  Alcotest.(check string)
    "2028-08-06: the Transfiguration observed; NO commemoration at all -- RG16(a)'s own \"cum omnibus iuribus \
     et privilegiis\" keeps the day a dominica II classis for RG111(b), so Sixtus (Class3) has no standing \
     either, oracle-confirmed"
    "2028-08-06 sunday season=time-after-pentecost week=9 slug=transfiguration-of-our-lord rank=class-2 \
     colour=white subject=lord name_la=- comms=[] in=- out=[]"
    (describe d)

(* Holy Family (RG 17(b), scan-verified, docs/research/rules-register.md
   §4/§6.0): "festum S. Familiae Iesu, Mariae, Ioseph, celebrandum dominica
   prima post Epiphaniam" -- first Sunday after Epiphany, II class, EVERY
   year, taking the Sunday's own place "cum omnibus iuribus et privilegiis"
   (RG 17(b)'s own closing clause, the same formula RG 16(a) uses above).
   Three shapes, each independently verified against the primary text
   BEFORE being typed in here, per this file's own header rule:

   1. AN ORDINARY YEAR (2026, 11 January): no fixed-date competitor in the
      7-13 January window besides St Hyginus (11 Jan, Class3,
      Commemoration_only per data/ef/sanctoral.sexp) -- excluded from the
      day's single Sunday slot outright by RG 111(b)'s own rank floor ("de
      festo II classis"), the SAME mechanism test_precedence_ef.ml's own
      "II-class Sunday admits only a de-festo-II-classis commemoration" pin
      already proves for a different date -- so comms=[] is not itself new
      ground, only Holy Family's own slug/rank/colour/season are.
      `date -d 2026-01-11 +%A` = Sunday, independently confirmed.
   2. THE 13-JANUARY COLLISION (2030, the latest possible date -- Epiphany
      itself a Sunday, 6 January): the fixed Commemoration of the Baptism
      of the Lord (13 Jan, Class2, subject Lord, data/ef/sanctoral.sexp)
      would otherwise contest the day directly, not merely lose a
      commemoration slot -- RG 91 entry 14's own "primum mobilia, deinde
      fixa" (precedence_ef.ml's own [entry_14_movable_band]) is what keeps
      Holy Family observed, and RG 112(a) -- directly confirmed by the Holy
      Family Mass propers' own more specific 13-January rubric, both
      photographic scans, word for word: "Si festum S. Familiae occurrerit
      die 13 ianuarii, Missa dicitur de festo S. Familiae, sine
      commemoratione Baptismatis D.N.I.C., et sine commemoratione
      dominicae" -- is what keeps comms=[] rather than admitting the
      Baptism as an ordinary Class2 commemoration. `date -d 2030-01-13
      +%A` = Sunday, independently confirmed; 2030 is one of the seven
      years in the 2005-2050 window this collision occurs (register §6.0's
      own list, test_differential.ml's own C15).
   3. THE EARLIEST POSSIBLE DATE (2024, 7 January -- Epiphany a Saturday):
      the OTHER end of RG 17(b)'s own 7-13 January range, proving the
      window's own boundary, not only its middle and its latest instance.
      No fixed-date competitor exists this early in the window either.
      `date -d 2024-01-07 +%A` = Sunday, independently confirmed.

   All three: season=christmastide (RG 72-73's own 1-13 January boundary,
   unaffected by Holy Family), week=- (Christmastide carries no numbered
   weeks at all, [T.week_origin]'s own [None] case -- unaffected by which
   Sunday of it this is), slug=ef-time-after-epiphany-sunday-1 (unchanged
   from the plain Sunday key this date always carried -- temporal_ef.ml's
   own comment on why RG 17(b) does not need a new lectionary key), rank
   =class-2, colour=white -- AND, fix round 1 (coordinator finding 4),
   subject=lord: the ONE field that actually distinguishes Holy Family from
   an ordinary, unnamed Sunday (every other field above was already
   identical before temporal_ef.ml's own fix -- [describe] originally
   omitted [subject], so two of these three pins would have stayed green
   even with the feature fully reverted; see [describe]'s own comment). *)
let test_holy_family_ordinary_year_2026 () =
  check ~msg:"2026-01-11: Holy Family, ordinary year -- Hyginus (Class3, Commemoration_only) has no \
              standing for the day's II-class-only Sunday slot"
    2026 1 11
    "2026-01-11 sunday season=christmastide week=- slug=ef-time-after-epiphany-sunday-1 rank=class-2 \
     colour=white subject=lord name_la=Sanctae Familiae Iesu, Mariae, Ioseph comms=[] in=- out=[]"

let test_holy_family_excludes_baptism_2030 () =
  check ~msg:"2030-01-13: Holy Family on the latest possible date -- RG91 e14 \"primum mobilia\" keeps it \
              observed over the fixed Baptism, RG112(a) excludes the Baptism entirely, not merely demotes it"
    2030 1 13
    "2030-01-13 sunday season=christmastide week=- slug=ef-time-after-epiphany-sunday-1 rank=class-2 \
     colour=white subject=lord name_la=Sanctae Familiae Iesu, Mariae, Ioseph comms=[] in=- out=[]"

let test_holy_family_earliest_possible_date_2024 () =
  check ~msg:"2024-01-07: Holy Family on the earliest possible date (Epiphany a Saturday)"
    2024 1 7
    "2024-01-07 sunday season=christmastide week=- slug=ef-time-after-epiphany-sunday-1 rank=class-2 \
     colour=white subject=lord name_la=Sanctae Familiae Iesu, Mariae, Ioseph comms=[] in=- out=[]"

(* Most Holy Name of Jesus (RG 17(a), scan-verified, docs/research/rules-
   register.md §6.2/§4): "festum Ss.mi Nominis Iesu, celebrandum dominica
   quae occurrit a die 2 ad 5 ianuarii (secus die 2 ianuarii)" -- unlike Holy
   Family (RG 17(b), immediately above), this window CAN be, and routinely
   is, empty of a Sunday (3,619 of 8,417 years domain-wide) -- the
   calendarium's own fallback, "vel, ea deficiente, die 2 ianuarii", is what
   the SECOND pin below exercises.

   *** THIS FILE'S OWN VACUITY WARNING, APPLIED DIRECTLY ***

   [test_holy_name_sunday_shape_2026] pins a year where the Sunday shape
   fires -- slug/rank/colour/season/comms here are ALL already what
   [T.sunday_slug]'s Christmastide branch computed before this task's fix
   (the identical accident Holy Family's own [describe] note above already
   explains for its shape): an unnamed Sunday and Holy Name of Jesus share
   every one of those fields on this date. Reverting the fix leaves this
   pin GREEN except for [subject] -- the ONE field it actually tests, same
   discipline as Holy Family's own "coordinator finding 4" fix. This pin
   earns its place only by asserting [subject] explicitly, not by pinning
   the whole line uncritically.

   [test_holy_name_fallback_2029] is the pin that actually has teeth against
   the LARGER of the two defects this task closed: before the fix, colitur
   emitted NO Holy Name office at all in a fallback year -- not merely an
   unnamed generic Sunday (Holy Family's own pre-fix shape), but the
   ordinary Christmastide ferial slug (`ef-christmas-1-tuesday`, class-4)
   the task brief's own example names. Reverting the fallback half of the
   fix reddens this pin on EVERY field (slug, rank, subject), not only one --
   the shape this file's header calls "the whole line moved", the opposite
   of the Sunday pin's own narrow, single-field teeth. *)
let test_holy_name_sunday_shape_2026 () =
  check ~msg:"2026-01-04: Holy Name of Jesus, Sunday shape -- oracle-corroborated (missalemeum's own \
              2026-01-04 row: rank 2, white, title \"Holy Name of Jesus\")"
    2026 1 4
    "2026-01-04 sunday season=christmastide week=- slug=ef-holy-name-sunday rank=class-2 colour=white \
     subject=lord name_la=Sanctissimi Nominis Iesu comms=[] in=- out=[]"

let test_holy_name_fallback_2029 () =
  check ~msg:"2029-01-02: Holy Name of Jesus, FALLBACK shape (RG 17(a)'s own \"secus die 2 ianuarii\", the \
              calendarium's \"vel, ea deficiente, die 2 ianuarii\") -- no Sunday falls 2-5 January 2029 \
              (`date -d 2029-01-0{2,3,4,5} +%u` = 2,3,4,5, independently confirmed), so 2 January itself \
              carries the feast; before this task's fix colitur emitted `ef-christmas-1-tuesday class-4` \
              here instead -- a real, II-class office simply missing, not merely misnamed"
    2029 1 2
    "2029-01-02 tuesday season=christmastide week=- slug=ef-holy-name rank=class-2 colour=white \
     subject=lord name_la=Sanctissimi Nominis Iesu comms=[] in=- out=[]"

(* RG 110 (docs/research/rules-register.md §4, Caput XIV), fix round 1
   (coordinator findings F1/F2): two golden pins neither existed before this
   round -- F2 named 30 June's own total absence of golden coverage
   directly ("no golden pin on any 30 June date"), and F1 named the
   shape-(c) collision as a real day this suite otherwise never exercises
   end-to-end against real data (both new [admit_cases] rows are synthetic
   candidates, not `Calendar.day` over the real committed layer).

   *** BOTH PINS ARE BLIND TO COMMEMORATION ORDER -- STATED HONESTLY, NOT
   CLAIMED AS PROOF OF F1's OWN FIX ***

   [describe]'s own `comms` field (this file's header, above) sorts the
   commemoration list alphabetically before rendering
   (`List.sort compare`) -- deliberately, so a golden pin's own STRING
   comparison is not accidentally order-sensitive where nothing textual
   requires it to be. That means this file, like the differential and the
   oracle's own [identity_diff] (test_oracle.ml), CANNOT see whether
   [rg110_additions] emits `chair-of-st-peter, paul` or `paul,
   chair-of-st-peter` -- both sort to the same string. The ONLY place in
   this codebase's test suite that asserts commemoration ORDER at all is
   `test_precedence_ef.ml`'s own [admit_cases] table (`Alcotest.(check
   (list string))`, not a sorted string) -- see precedence_ef.ml's own
   [rg110_additions] comment for the citation this uses to fix the order,
   and CLAUDE.md's "know what each layer cannot see" section for this as a
   permanent, structural limit, not a gap this task's own pins happened not
   to close. These two pins prove PRESENCE and IDENTITY of both
   commemorations on a real, `Calendar.day`-resolved date -- genuine
   ground no other pin in this file currently covers -- not order. *)
let test_rg110_june_30_2026 () =
  check ~msg:"2026-06-30: RG 110's 30-June direction -- In Commemoratione Sancti Pauli Apostoli observed, \
              +commemoration-of-st-peter (data/ef/adjustments.sexp's own `Add` directive) the sole \
              commemoration -- missalemeum shows neither (M19, verdict colitur)"
    2026 6 30
    "2026-06-30 tuesday season=time-after-pentecost week=5 slug=in-commemoratione-sancti-pauli-apostoli \
     rank=class-3 colour=red subject=saint name_la=- comms=[commemoration-of-st-peter:ordinary] in=- out=[]"

let test_rg110_shape_c_2004 () =
  check ~msg:"2004-02-22: RG 110 shape (c) -- Chair of St Peter itself loses the day to an ordinary \
              (Septuagesima, not I-class) II-class Sunday and is admitted only as a commemoration under \
              RG 111(b)'s own rank floor; Paul rides along too, uncapped (`date -d 2004-02-22 +%A` = \
              Sunday, independently confirmed)"
    2004 2 22
    "2004-02-22 sunday season=septuagesima week=3 slug=ef-septuagesima-sunday-3 rank=class-2 colour=violet \
     subject=temporal name_la=- comms=[chair-of-st-peter:ordinary,paul:ordinary] in=- out=[]"

(* 2025-02-02, the other real instance of the SAME pattern -- not a
   contrasting second direction, per this task's fix round 1 (CRITICAL
   finding, item 1, reverted; see register §6.0 for the full account).

   The Purification (2 Feb, calendarium "IN PURIFICATIONE B. MARIAE VIRG.")
   stays [subject = Lord] -- the user has ruled: follow the oracle.
   missalemeum treats it exactly as RG 16(a)'s own "festum Domini" case
   (independently fetched, `/api/v5/calendar` JSON): 2020-02-02 (a Sunday)
   -- `{"title":"Purification of the Blessed Virgin Mary","tags":["IV
   Sunday after Epiphany"],...,"commemorations":[],"displaced":[]}` -- the
   Sunday named only as a TAG (the underlying temporal placement), not
   commemorated at all; matches 2014-02-02 exactly. Contrast, the SAME
   oracle on an ordinary Marian feast (2019-09-08, the Nativity of the
   BVM): `{"title":"XIII Sunday after Pentecost",...,"commemorations":
   [{"title":"Nativity of the Blessed Virgin Mary",...}],...}` -- the
   Sunday observed, the feast merely commemorated, the OPPOSITE pattern --
   proving missalemeum does not treat every Marian feast this way, only the
   Purification specifically. `date -d 2025-02-02 +%A` = Sunday,
   independently confirmed.

   Unlike 2028-08-06, no second sanctoral candidate exists on 2 February
   (data/ef/sanctoral.sexp has exactly one 2 February entry) -- a genuinely
   clean "comms=[]" case, complementing the pair above rather than
   duplicating it. *)
let test_purification_on_a_sunday_2025 () =
  check
    ~msg:
      "2025-02-02: the Purification observed (subject Lord, unretagged -- the oracle's own treatment); NO \
       commemoration of the Sunday, oracle-confirmed"
    2025 2 2
    "2025-02-02 sunday season=time-after-epiphany week=4 slug=purification-of-the-blessed-virgin-mary \
     rank=class-2 colour=white subject=lord name_la=- comms=[] in=- out=[]"

(* ------------------------------------------------------------------ *)
(* RG 91 entry 24 ("Festa III classis, in calendario Ecclesiae universae
   inscripta") outranks entry 28 ("feriae IV classis") -- ef-rebootstrap
   (2026-08-12). Fifteen data/ef/sanctoral.sexp entries, 6 March-5 April,
   were wrongly bootstrapped `rank = commemoration` (Commemoration_only,
   which RG 91's table has no row for at all, register §6.1's own Maurice/
   Thomas finding) where the 1962 calendarium gives each a real class
   number instead; corrected upstream in lectio's own generator and
   re-bootstrapped here (source SHA-256 1b303ef2...). Only 7 of the 15 ever
   land on a date that collides with a plain CLASS-4 feria somewhere in the
   domain -- fact-checked by a full 1583-9999 blast-radius sweep finding
   zero diff rows for the other 8 anywhere in the domain, not assumed.

   CORRECTED (ef-rebootstrap fix round 1, F1): this comment previously gave
   a single reason for the other 8's silence -- "always fall inside Lent/
   Passiontide proper, RG 91 entry 22, which already outranks entry 24" --
   WRONG for 3 of the 8, measured directly against the domain-wide season
   breakdown per slug: `gabriel-the-archangel` (24 Mar) lands in
   PASCHALTIDE 226 times, `john-damascene` (27 Mar) 846 times,
   `john-of-capistrano` (28 Mar) 1108 times -- always at `class-1` there
   (inside the Easter Octave, RG 91 entry 2/10), never a class-4 feria.
   Entry 22 itself is also narrower than "Lent/Passiontide" suggests (its
   own text excludes the Ember days). The TRUE, general shield, checked
   against every (season, rank) pair these 8 slugs' dates ever land in
   domain-wide: whichever office already occupies the date is EITHER
   class-1 or class-2 (an Easter-Octave/Holy-Week/I-class-Sunday day,
   entries 2/6/7/10; a II-class Sunday or Ember feria, entries 15/18) --
   which trivially outranks ANY class-3 candidate regardless of status, and
   also excludes it from commemoration by RG 111(a)/(b)'s own rank floors
   the same way regardless of status -- OR the ordinary Lent/Passiontide
   class-3 feria itself, entry 22, which outranks entry 24 for OBSERVATION
   but (RG 111(c)/(d), no ferial-class gate on an "ordinary" sanctoral
   candidate) admits the losing saint's commemoration identically whether
   `Commemoration_only` or a genuine `Feast` -- so no context these 8 dates
   ever reach anywhere in the domain distinguishes the two statuses in
   either direction, observation or commemoration. Not "always Lent" --
   "always something at entry ≤22 already occupies the day". These are the
   SIX dates the task brief that started this re-bootstrap named as
   producing "a wrong observed office"
   under the OLD data (2008-04-02/04/05, 2038-03-06/08/09) --
   [test_easter_extreme_2038_late_modern]'s own header above is the record
   of why they were deliberately left unpinned until now. *)

let test_iii_class_feast_wins_class4_feria () =
  (* Two [omitted_has] checks, one per season family (Paschaltide,
     Septuagesima), prove the displaced CLASS-4 feria actually reached
     [disposition] and was actively omitted under RG26 ("IV-class ferias
     are never commemorated"), not merely absent because no candidate was
     ever offered -- this file's own established convention (the RG16(a)
     and "IV-class ferias never commemorated" tests above), not repeated
     for all six to avoid duplicating the same proof six times. *)
  let d1 = fetch 2008 4 2 in
  Alcotest.(check bool)
    "2008-04-02: the displaced class-4 Paschaltide feria is in [omitted] (RG26), proving RG91 entry24>28 fired"
    true
    (omitted_has d1 "ef-easter-2-wednesday");
  Alcotest.(check string) "2008-04-02: Francis of Paola (III class, RG91 entry 24) outranks the class-4 feria"
    "2008-04-02 wednesday season=paschaltide week=2 slug=francis-of-paola rank=class-3 colour=white subject=saint name_la=- comms=[] \
     in=- out=[]"
    (describe d1);
  check ~msg:"2008-04-04: Isidore of Seville (III class) outranks the class-4 Paschaltide feria, same shape" 2008
    4 4
    "2008-04-04 friday season=paschaltide week=2 slug=isidore-of-seville rank=class-3 colour=white subject=saint name_la=- comms=[] in=- \
     out=[]";
  check ~msg:"2008-04-05: Vincent Ferrer (III class) outranks the class-4 Paschaltide feria, same shape" 2008 4 5
    "2008-04-05 saturday season=paschaltide week=2 slug=vincent-ferrer rank=class-3 colour=white subject=saint name_la=- comms=[] in=- \
     out=[]";
  let d4 = fetch 2038 3 6 in
  (* ef-bvm-saturday task: 6 March 2038 is a SATURDAY, so the displaced
     temporal candidate is now RG 78's own BVM Saturday office
     ({!Rite_ef.Temporal_ef}'s own [bvm_saturday_names] citation) rather than
     a bare ferial -- but per that citation's own "Slug" paragraph the SLUG
     is deliberately left unchanged (reused, not replaced), precisely so
     this test's own [omitted_has] probe still needs no update: the displaced
     candidate is still named "ef-septuagesima-2-saturday", only its
     colour/subject/name would have differed had it won. RG 91 entry 24
     (Sts Perpetua and Felicity) still outranks it either way -- entry 24 <
     entry 27, both above entry 28 -- so the disposition/win outcome this
     test proves is genuinely unchanged by this task, now more precisely
     entry 24 > entry 27 rather than > 28 for this one date (a Saturday), not
     that the test itself needed touching. *)
  Alcotest.(check bool)
    "2038-03-06: the displaced class-4 feria (RG78's own BVM-Saturday office, \
     for this Saturday) is in [omitted] (RG26), proving RG91 entry24>27 fired"
    true
    (omitted_has d4 "ef-septuagesima-2-saturday");
  Alcotest.(check string)
    "2038-03-06: Sts Perpetua and Felicity (III class, RG91 entry 24) outrank the class-4 feria; RED per their \
     own martyrs' colour, not the season's violet"
    "2038-03-06 saturday season=septuagesima week=2 slug=sts-felicitas-perpetua rank=class-3 colour=red subject=saint name_la=- comms=[] \
     in=- out=[]"
    (describe d4);
  check ~msg:"2038-03-08: John of God (III class) outranks the class-4 Septuagesima feria, same shape" 2038 3 8
    "2038-03-08 monday season=septuagesima week=3 slug=john-of-god rank=class-3 colour=white subject=saint name_la=- comms=[] in=- \
     out=[]";
  check ~msg:"2038-03-09: Frances of Rome (III class) outranks the class-4 Septuagesima feria, same shape" 2038 3
    9
    "2038-03-09 tuesday season=septuagesima week=3 slug=frances-rome rank=class-3 colour=white subject=saint name_la=- comms=[] in=- \
     out=[]"

(* ------------------------------------------------------------------ *)
(* RG 91 entry 27 / RG 78-79 (Caput IX, "De sancta Maria in sabbato") --
   ef-bvm-saturday task, Rite_ef.Temporal_ef's own [bvm_saturday_names]
   citation has the full primary-source argument. Two real-data pins, the
   task brief's own worked example and its mirror-image negative case,
   matching this file's own header rule: EVERY literal below was checked by
   hand against `colitur day 2026` before being typed in here (this task's
   own report has the full session log), never copied from a run and
   rationalised after. *)

(* The task brief's own worked example, checked against the oracle
   (missalemeum) directly: "2026-08-01|4|w|V Mass of the B. V. M. -- Salve,
   Sancta Parens|Saturday_after_IX_Sunday_after_Pentecost|Holy Machabees".
   colitur's own rank (4) and colour (white) now match; the oracle's own
   numbered-Mass title (RG 309(a)'s own "iuxta temporum diversitatem", not
   modelled here -- {!bvm_saturday_names}'s own "Slug" paragraph) and the
   observed day's own English identity remain outside what this comparator
   can see (M18's own limit) -- this pin instead proves what colitur DOES
   compute: white/class-4/subject Bvm/the Latin title, and Holy Machabees
   (real data/ef/sanctoral.sexp entry, Commemoration_only, Class3, 1 August)
   still admitted as an ORDINARY commemoration alongside it, matching the
   oracle's own commemoration list exactly. *)
let test_bvm_saturday_2026 () =
  check ~msg:"2026-08-01: the BVM Saturday office observed, white overriding Time after Pentecost's green, \
              Holy Machabees commemorated -- matches the task brief's own oracle example"
    2026 8 1
    "2026-08-01 saturday season=time-after-pentecost week=9 slug=ef-time-after-pentecost-9-saturday \
     rank=class-4 colour=white subject=bvm name_la=Officium sanctae Mariae in sabbato \
     comms=[holy-machabees:ordinary] in=- out=[]"

(* The mirror-image case: RG 78's own protasis is FALSE here (a real feast
   wins the Saturday outright), so the BVM-Saturday-shaped temporal
   candidate must lose and be omitted under RG 26, exactly like the plain
   ferial candidate it replaces always was -- no commemoration at all,
   proving [band]/[disposition] need no special case for this office (the
   design claim the task report makes explicitly). ALSO the one live data
   witness where the winning candidate itself carries [subject = Bvm]
   (data/ef/adjustments.sexp's own `Edit most-holy-name-of-mary
   ((Set_subject Bvm))`) -- proving no Bvm-vs-Bvm confusion with a
   RG112(a)-style subject rule: [omitted_has] below confirms the loser is
   genuinely reported omitted (RG26 fires on [rank = Class4] alone, checked
   ahead of any subject-based branch in {!Rite_ef.Precedence_ef.disposition}),
   not silently swallowed by a same-subject exclusion that does not exist
   for [Bvm] anywhere in this codebase. *)
let test_bvm_saturday_loses_to_most_holy_name_of_mary_2026 () =
  let d = fetch 2026 9 12 in
  Alcotest.(check bool)
    "2026-09-12: the displaced BVM-Saturday-shaped candidate is in [omitted] (RG26), not silently absent"
    true
    (omitted_has d "ef-time-after-pentecost-15-saturday");
  Alcotest.(check string)
    "2026-09-12: Most Holy Name of Mary (III class, subject Bvm, data/ef/adjustments.sexp) outranks the \
     class-4 BVM-Saturday candidate; no commemoration at all"
    "2026-09-12 saturday season=time-after-pentecost week=15 slug=most-holy-name-of-mary rank=class-3 \
     colour=white subject=bvm name_la=- comms=[] in=- out=[]"
    (describe d)

(* RG112(d), fix round 1 (coordinator finding F1): PE.marian_slugs's own
   citation (precedence_ef.ml) has the full RG112(d) argument, including
   the 16 July "Missa dici potest aut ... aut ..." rubric found on both
   photographic scans. 16 July 2033 is a real otherwise-unoccupied
   Saturday; before this fix, [comms] here read
   "[our-lady-of-mt-carmel:ordinary]" (confirmed against real pre-fix
   `colitur day 2033` output). *)
let test_bvm_saturday_excludes_mt_carmel_2033 () =
  let d = fetch 2033 7 16 in
  Alcotest.(check bool)
    "2033-07-16: Mt Carmel is in [omitted] (RG112(d)), not silently dropped from the record" true
    (omitted_has d "our-lady-of-mt-carmel");
  Alcotest.(check string)
    "2033-07-16: the BVM Saturday office observed, white; NO commemoration of Mt Carmel (RG112(d))"
    "2033-07-16 saturday season=time-after-pentecost week=5 slug=ef-time-after-pentecost-5-saturday \
     rank=class-4 colour=white subject=bvm name_la=Officium sanctae Mariae in sabbato comms=[] in=- out=[]"
    (describe d)

(* ------------------------------------------------------------------ *)
(* RG 80/81/109(f), the Major Litanies (ef-major-litanies task). Every
   date below independently derived from the primary text, not read off a
   `colitur day` run first and rationalised (this file's own header, rule
   3): Easter's own date for each civil year comes from Computus (already
   independently Gauss-cross-checked by this file's own extreme-year
   pins), 25 April's Easter-offset and weekday follow arithmetically from
   that, and the RULE applied at each offset (RG 80's transfer condition;
   RG 91 entries 15/16 for the Sunday-vs-Mark band comparison; RG 111(b)'s
   privilege-overrides-ordinary clause) is quoted at
   Rite_ef.Precedence_ef's own [disposition]/[admit]/[transfer_target] and
   at data/ef/expected-divergences-missalemeum.sexp's own M5/M20 entries,
   which independently adjudicate the one substantive rubric question here
   (WHO wins the Sunday's single slot) against the missalemeum oracle. *)
(* ------------------------------------------------------------------ *)

(* The ORDINARY case (~97.7% of years, register's own measurement): 25
   April 2026 is a Saturday (independently checked via `date -d
   2026-04-25 +%A`, this file's own header rule 2), Easter+20 (Easter 2026
   = 5 April, itself independently checked the same way against a
   published 2026 Easter date). St Mark (II class, RG 91 entry 16) wins the day outright
   over the ordinary Class4 Paschaltide feria (entry 28); the Litanies,
   Commemoration_only, is the day's ONLY other candidate and is admitted
   uncontested ({!Rite_ef.Precedence_ef.admit}'s [Class2, false] case: one
   slot, table order alone, no rival). This is the SAME date and shape
   data/ef/expected-divergences-missalemeum.sexp's own [M5] entry
   adjudicates against the real missalemeum oracle (verdict colitur, a
   naming-convention difference only -- "The Major Litanies" vs
   missalemeum's own "Pro rogationibus"). *)
let test_major_litanies_ordinary_2026 () =
  check ~msg:"2026-04-25: St Mark observed outright (RG91 e16 > e28); the Major Litanies (RG80/109(f)) ride \
              along uncontested" 2026 4 25
    "2026-04-25 saturday season=paschaltide week=3 slug=mark rank=class-2 colour=red subject=saint name_la=- \
     comms=[major-litanies:privileged] in=- out=[]"

(* The FOUR Sunday-displacement years in 2005-2050 (this task's own
   measurement, register): 25 April is a Sunday, so St Mark (entry 16, II
   class) loses to it exactly as any other II-class feast would (entry 15
   < entry 16), becoming an ordinary commemoration CANDIDATE rather than
   the observed day -- and then loses the day's own single slot to the
   Litanies (RG 111(b): "quæ tamen omittitur si commemoratio privilegiata
   facienda sit" -- the ordinary de-festo-II-classis commemoration is
   DROPPED once a privileged one is due). [omitted_has] on each date below
   confirms Mark is genuinely reported dropped, not silently absent --
   "omitted: admission limit reached", {!Precedence.resolve}'s own generic
   reason for a candidate [disposition] admitted to the contest but
   [admit] then cut. Two different generic Sunday slugs appear (week 4 in
   2010/2021, week 5 in 2027/2032) because Easter falls on a different
   date in each pair -- neither is hand-picked, both are what
   [Temporal_ef.sunday_slug] actually computes. *)
let test_major_litanies_displaces_mark_on_ii_class_sunday () =
  List.iter
    (fun (y, week) ->
      let d = fetch y 4 25 in
      Alcotest.(check bool)
        (Printf.sprintf "%d-04-25: St Mark is in [omitted] (RG111(b)'s privileged-commemoration override), not \
                          silently dropped" y)
        true (omitted_has d "mark");
      Alcotest.(check string)
        (Printf.sprintf "%d-04-25: the Sunday observed (II class); ONLY the Litanies commemorated, Mark \
                          displaced (RG111(b))" y)
        (Printf.sprintf
           "%d-04-25 sunday season=paschaltide week=%d slug=ef-easter-sunday-%d rank=class-2 colour=white \
            subject=temporal name_la=- comms=[major-litanies:privileged] in=- out=[]"
           y week week)
        (describe d))
    [ (2010, 4); (2021, 4); (2027, 5); (2032, 5) ]

(* The OTHER RG 80 trigger shape, complementing 1666/2038 above (Easter
   Sunday itself = 25 April): here Easter MONDAY = 25 April (Easter = 24
   April 2011, independently Gauss-derivable and already the domain's own
   register-cited "Easter Monday" marker, [ef-easter-1-monday]). RG 80's
   "following Tuesday" is one day later this time (26 April, not 27) --
   both shapes converge on the SAME offset, Easter+2, never on a fixed
   civil-date difference. *)
let test_major_litanies_transfer_2011_easter_monday () =
  check ~msg:"2011-04-25 Easter Monday (RG80's SECOND trigger): Litanies depart for the following Tuesday, \
              26 April" 2011 4 25
    "2011-04-25 monday season=paschaltide week=1 slug=ef-easter-1-monday rank=class-1 colour=white \
     subject=temporal name_la=- comms=[] in=- out=[major-litanies->2011-04-26]";
  check ~msg:"2011-04-26 Easter+2: the Litanies land here, commemorated and privileged, exactly one day after \
              origin (not two, unlike the Easter-Sunday shape)" 2011 4 26
    "2011-04-26 tuesday season=paschaltide week=1 slug=ef-easter-1-tuesday rank=class-1 colour=white \
     subject=temporal name_la=- comms=[major-litanies:privileged] in=- out=[]"

(* ---- Task 10: one pin per step of the reading-resolution chain ----

   The chain has four steps, and a citation regression is far easier to
   diagnose when the failing pin names WHICH step broke. Every literal below
   was read out of the photographic scans first (line numbers given per pin),
   then compared with colitur -- not the other way round.

   Step 4 deserves special note: it is the ONE step with no external oracle
   witness of any kind (register §6.7 -- the five Common-routed saints are
   the observed office on just five days in all of 2005-2050, and not one
   falls inside layer 4's own 2026-2027 fixture window; layer 3 sees those
   days but lectio resolves the literal "-"/"-" sentinel there, which is what
   C18 is gated on, so it confirms only that colitur emits SOMETHING). These
   two pins are therefore the first independent confirmation that route has
   ever had, which is also why step 4 gets two of the six rather than one. *)

(* STEP 1 -- a sanctoral proper: the citation lives on the Celebration
   itself, no walk, no Common. 8 March 1938^H2038, "S. Ioannis a Deo Conf.,
   III classis": "Lectio libri Sapientiae. Eccli. 31, 8-11" / "Sequentia
   sancti Evangelii secundum Matthaeum. Mt. 22, 34-46" (scan1.txt:27732-27734
   + 27769-27770; scan2.txt:30698-30699 + 30733-30736; page image p. 485).
   The Missal prints him a FULL Mass with its own assigned Gospel -- the
   Epistle happens to coincide with the Common of a Confessor not a Bishop's,
   but the Gospel does not (that Common has Luke 12:35-40), which is what
   makes this a proper and not a Common, and so a step-1 case rather than a
   step-4 one. NOTE the plan named 2030-01-13 for this step; that date is a
   TEMPORAL day (Holy Family, carried on the generic Sunday slug per
   RG 17(b)) and would not have exercised step 1 at all. *)
let test_readings_step1_sanctoral_proper () =
  check_readings ~msg:"step 1: sanctoral proper (John of God, 8 March)" 2038 3 8
    "2038-03-08 slug=john-of-god first=Ecclus 31:8-11 gospel=Matt 22:34-46"

(* STEP 2 -- a temporal day with its own direct lectionary entry. 23 February
   2026 is "Feria II post dominicam I in Quadragesima" (heading, scan1.txt:
   8137): "Lectio Ezechielis Prophetae. Ezech. 34, 11-16" (scan1.txt:8138-
   8139) / "Sequentia sancti Evangelii secundum Matthaeum. Mt. 25,31-46"
   (scan1.txt:8199). A Lenten feria is the right shape here because Lent's
   ferias each have their OWN Mass -- they never walk back, so a regression
   in step 3 cannot mask a regression in step 2 on this date.
   The book abbreviation is "Ezech", not "Ezek": this pin was first written
   from the scan as "Ezek 34:11-16" and failed, and the difference turned out
   to be notation alone -- colitur's own spelling is in fact the Missal's own
   ("Ezech. 34, 11-16", above). Recorded rather than quietly amended, since a
   failing pin whose only fault is an abbreviation is exactly the kind of
   thing a later reader will otherwise re-litigate. Cross-engine abbreviation
   differences are normalised away in layer 3 (test_differential.ml's own A/B
   normalisation) and so are invisible there; this layer sees them. *)
let test_readings_step2_temporal_proper () =
  check_readings ~msg:"step 2: temporal proper (Lent I Monday)" 2026 2 23
    "2026-02-23 slug=ef-lent-1-monday first=Ezech 34:11-16 gospel=Matt 25:31-46"

(* STEP 3 -- the ferial resumption: a day with NO Mass of its own, which
   therefore repeats its preceding Sunday's. 1 December 2025 is the Monday
   after Advent I, and Advent's ferias (unlike Lent's) have no proper Mass,
   so the citation must be Advent I Sunday's own: "ad Romanos. Rom. 13,
   11-14" (scan1.txt:4878) / "dum Lucam. Luc. 21, 25-33" (scan1.txt:4912).
   That the pinned value equals the SUNDAY's, not the Monday's own, is the
   whole point of the pin. *)
let test_readings_step3_ferial_resumption () =
  check_readings ~msg:"step 3: ferial resumption (Advent I Monday reads Advent I Sunday)" 2025 12 1
    "2025-12-01 slug=ef-advent-1-monday first=Rom 13:11-14 gospel=Luke 21:25-33"

(* STEP 4 (a) -- the Common route. 6 March 2038, "Ss. Perpetuae et
   Felicitatis Martyrum, III classis", whose own entry directs: "Missa Me
   exspectaverunt, de Communi non Virginum I loco [35]" (scan1.txt:27634-
   27635). That Common -- COMMUNE NON VIRGINUM, "Pro Martyre non Virgine"
   (scan1.txt:42560 ff) -- reads "Lectio libri Sapientiae. Eccli. 51, 1-8 et
   12" (scan1.txt:42589-42590) and "Sequentia sancti Evangelii secundum
   Matthaeum. Mt. 13, 44-52" (scan1.txt:42606). 2038 is the only year in
   2005-2050 in which she is the OBSERVED office at all. *)
let test_readings_step4_common_felicitas () =
  check_readings ~msg:"step 4: Common of Non-Virgins I (Perpetua & Felicitas)" 2038 3 6
    "2038-03-06 slug=sts-felicitas-perpetua first=Ecclus 51:1-8, 12 gospel=Matt 13:44-52"

(* STEP 4 (b) -- the SECOND Common, deliberately a different one, so the pin
   pair distinguishes "the Common route works" from "one Common's data is
   right". 9 March 2038, "S. Franciscae Romanae Vid., III classis", directed
   to "Missa Cognovi, de Communi non Virginum" -- the second Mass of that
   same Common, which reads "Lectio libri Sapientiae. Prov. 31, 10-31"
   (scan1.txt:42679) and shares the Gospel "Mt. 13, 44-52" (scan1.txt:42720).
   The shared Gospel with (a) is the Missal's own doing, not a colitur
   collapse -- which is exactly why both Epistles are pinned too. *)
let test_readings_step4_common_frances () =
  check_readings ~msg:"step 4: Common of Non-Virgins II (Frances of Rome)" 2038 3 9
    "2038-03-09 slug=frances-rome first=Prov 31:10-31 gospel=Matt 13:44-52"

(* THE DISPLACED-SUNDAY FERIA -- the case that separates the temporal slug
   from the observed one, and the one the plan singles out. It is pinned
   here as KNOWN-DIVERGENT OUTPUT, deliberately and with the divergence
   named, NOT blessed as correct: this is test_oracle.ml's own M26 shape 2(b)
   (verdict open). 26 October 2026 is a feria of the week whose Sunday was
   displaced by Christ the King (I class, movable, "Dominica ultima
   octobris"). The general mechanism -- a week's ferias follow their OWN
   Sunday's temporal identity even when that Sunday is impeded -- is the
   shape RG 69 establishes for the Nativity Octave, so the displaced ordinary
   Sunday's Mass is what the ferias should read; colitur's step 3 instead
   walks back to the DISPLACING feast and emits Christ the King's own Gospel
   (John 18:33-37, "Art thou a king?"), which is the literal in this pin.
   No Missal-propers rubric specific to Christ the King's own week was found
   (checked: scan1.txt:39140-39163, no rubric precedes that Mass heading,
   unlike Holy Name's and Holy Family's own), so M26 stays verdict open
   rather than being asserted against a citation that does not exist.
   WHEN M26 IS FIXED THIS PIN MUST FAIL -- that is its purpose. Update it
   deliberately then; do not regenerate it.

   Two things this pin established that M26's own note did not record. First,
   BOTH parts come from the displacing feast, not only the Gospel M26 cites:
   the Epistle here is Col 1:12-20, which is Christ the King's own, so step 3
   is transplanting the whole Mass rather than half of it. Second, the
   reference carries a TRAILING PERIOD ("Col 1:12-20."), a blemish inherited
   through the bootstrap from the source data. It is pinned verbatim rather
   than tidied, because this pin's job is to state what colitur actually
   emits today; the period is noted here as a separate, cosmetic data defect
   and is deliberately not fixed under a task about test coverage. Layer 3
   cannot see it (its A/B normalisation strips punctuation before comparing),
   which is why it surfaced here first. *)
let test_readings_displaced_sunday_feria_known_divergent () =
  check_readings
    ~msg:"displaced-Sunday feria: pins M26 shape 2(b)'s KNOWN-WRONG walkback, not correct output"
    2026 10 26
    "2026-10-26 slug=ef-time-after-pentecost-22-monday first=Col 1:12-20. gospel=John 18:33-37"


(* ---- Rogation Wednesday (RG 87/88/89), movable-date-specs task ----

   RG 87, verbatim (scan1.txt:691): "Litaniae minores seu Rogationes, per se,
   assignantur feriis II, III et IV ante festum Ascensionis Domini" -- Monday,
   Tuesday AND Wednesday. colitur built the first two as temporal offices from
   the start; the Wednesday had no channel at all until Date_spec grew
   [Easter_offset], because Easter+38 IS the Ascension Vigil by construction
   and there is no (month, day) a Fixed spec could anchor to.

   RG 88 keeps it out of the Office entirely, so the Vigil remains the
   observed day and this is only ever a commemoration. RG 89 routes it through
   the Major Litanies' rules -- but RG 109's closed list of privileged
   commemorations names only "de Litaniis MAIORIBUS", never minoribus, so by
   RG 107's closing rule it is ORDINARY. That asymmetry is the whole point of
   pinning two years rather than one:

   2024 -- nothing else competes, so the single commemoration RG 111 allows on
   a II-class day is the Rogation itself.
   2026 -- St Robert Bellarmine (III class) is impeded onto the same day and
   takes that one slot instead: RG 113 orders admissions by RG 91's table, and
   a Commemoration_only candidate has no row in it at all (band returns
   [unclassified]), so a genuine feast outranks it.

   Both weekdays independently checked with `date -d`. *)
let test_rogation_wednesday_admitted_2024 () =
  check ~msg:"2024: Rogation Wednesday is the one admitted commemoration" 2024 5 8
    "2024-05-08 wednesday season=paschaltide week=6 slug=ef-ascension-vigil rank=class-2 colour=white subject=temporal name_la=- comms=[rogation-wednesday:ordinary] in=- out=[]"

let test_rogation_wednesday_yields_to_a_feast_2026 () =
  check ~msg:"2026: an impeded III-class feast takes the single slot instead" 2026 5 13
    "2026-05-13 wednesday season=paschaltide week=6 slug=ef-ascension-vigil rank=class-2 colour=white \
     subject=temporal name_la=- comms=[robert-bellarmine:ordinary] in=- out=[]"

(* RG 128, transcribed in docs/research/rules-register.md §3b: violet is used
   for "II/III-class vigils outside Paschaltide". Two of colitur's five vigils
   disagreed with that rule until the ef-oconnell-rubrics branch -- the
   Assumption's (II class, 14 August) was White and St Lawrence's (III class,
   9 August) was Red. Both are outside Paschaltide, so both are violet.

   O'Connell, The Celebration of Mass 4th ed. (1964), section 4(c) states the
   same rule and its footnote 169 names the sole exception -- "the only vigil
   of II class within Eastertide is that of the Ascension and its colour is
   white" -- which colitur already emits correctly (ef-ascension-vigil, white,
   pinned by the two Rogation Wednesday cases just above).

   Pinned in years where each vigil is actually OBSERVED rather than impeded.
   14 August 2025 is a Thursday and the Assumption always keeps 15 August (I
   class, nothing in the universal calendar outranks it), so that pin is
   stable in any year the vigil's own day is free.

   ST LAWRENCE'S PIN MOVED 2025 -> 2027 when RG 33's third omission trigger
   was implemented. Its original comment reasoned only about the vigil's OWN
   weekday -- "9 August 2025 is a Saturday", true, so RG 33's Sunday trigger
   does not fire -- and missed that the rule also looks at the FEAST: 10
   August 2025 is a Sunday, the II-class Sunday reduces St Lawrence to a
   commemoration, and "vel si festum cui praemittitur... ad commemorationem
   reduci contingat" omits the vigil entirely. The day now keeps the BVM
   Saturday Office instead, pinned separately just below. 9 August 2027 is a
   Monday and 10 August 2027 a Tuesday (both `date -d`-checked), so St
   Lawrence keeps his own day there and the vigil genuinely is observed --
   which is what this pin has always been trying to assert about RG 128's
   colour rule. *)
let test_vigil_of_st_lawrence_is_violet_2027 () =
  check ~msg:"RG 128: III-class vigil outside Paschaltide is violet, not red" 2027 8 9
    "2027-08-09 monday season=time-after-pentecost week=12 slug=vigil-of-st-lawrence \
     rank=class-3 colour=violet subject=saint name_la=- comms=[romanus:ordinary] in=- out=[]"

(* RG 33's THIRD omission trigger -- "vel si festum cui praemittitur in alium
   diem transferri aut ad commemorationem reduci contingat" -- on the shipped
   universal calendar, no overlay involved. 10 August 2025 is a Sunday; the
   II-class Sunday outranks St Lawrence's II-class feast (RG 15), so the feast
   is "ad commemorationem reducta" on its own day and the vigil of 9 August is
   "penitus omissa".

   This is the clause's most common live shape by a wide margin: 1 199 of the
   1 744 days it fires on across 1583-9999 are St Lawrence's vigil dropped
   because 10 August fell on a Sunday. The day does not become a bare feria --
   9 August 2025 is a Saturday, so RG 78's votive Office of the BVM takes the
   freed IV-class Saturday, which is why this pin reads white and subject=bvm.
   St Romanus keeps his commemoration either way (RG 111).

   INDEPENDENTLY WITNESSED at the day level, though not by the Ordo this
   project already had. That one (extraordinaryform.org) omits the vigil here
   AND on 2027-08-09 where the clause says to keep it, so it never shows St
   Laurence's vigil at all and corroborates nothing -- a claim an earlier
   version of this comment got wrong in both directions before checking.
   The Latin Mass Society Ordo 2024-2025 for England and Wales does the job:
   9 August 2025 is "OUR LADY on SATURDAY IV Cl W / Commem of St Romanus M",
   no vigil, and 10 August is the II-class Sunday with St Laurence reduced to
   a commemoration -- this pin's exact content. It is discriminating because
   that edition lists six other vigils as the day's office, including the
   Assumption's on 14 August 2025. See C39's own note in
   data/ef/expected-divergences.sexp. *)
let test_rg33_vigil_omitted_when_its_feast_is_commemorated_2025 () =
  check
    ~msg:"RG 33: 10 Aug 2025 is a Sunday, so St Lawrence is only commemorated and his vigil is \
          omitted entirely -- the BVM Saturday Office (RG 78) takes the freed day"
    2025 8 9
    "2025-08-09 saturday season=time-after-pentecost week=8 \
     slug=ef-time-after-pentecost-8-saturday rank=class-4 colour=white subject=bvm \
     name_la=Officium sanctae Mariae in sabbato comms=[romanus:ordinary] in=- out=[]"

(* The same clause's OTHER half, "in alium diem transferri". 24 June 2038 is
   impeded by the Sacred Heart (I class, movable), so the Nativity of St John
   the Baptist is TRANSFERRED under RG 96 and its vigil of 23 June is omitted.
   478 days across the domain take this shape (a further 67 via Corpus
   Christi). Pinned in 2038 because that year already carries an oracle
   fixture, so the divergence this creates is visible to layer 4 as well. *)
let test_rg33_vigil_omitted_when_its_feast_transfers_2038 () =
  check
    ~msg:"RG 33: the Sacred Heart takes 24 June 2038, St John the Baptist transfers, \
          and his vigil of 23 June is omitted entirely"
    2038 6 23
    "2038-06-23 wednesday season=time-after-pentecost week=1 \
     slug=ef-time-after-pentecost-1-wednesday rank=class-4 colour=green subject=temporal \
     name_la=- comms=[] in=- out=[]"

let test_vigil_of_the_assumption_is_violet_2025 () =
  check ~msg:"RG 128: II-class vigil outside Paschaltide is violet, not white" 2025 8 14
    "2025-08-14 thursday season=time-after-pentecost week=9 slug=vigil-of-the-assumption \
     rank=class-2 colour=violet subject=saint name_la=- comms=[eusebius-confessor:ordinary] in=- out=[]"

let suite =
  ( "golden pins (known-tricky years)",
    [ Alcotest.test_case "RG128: St Lawrence's vigil is violet (2027)" `Quick
        test_vigil_of_st_lawrence_is_violet_2027;
      Alcotest.test_case
        "RG33 third trigger: the vigil is omitted when its feast is reduced to a commemoration \
         (2025-08-09, St Lawrence)"
        `Quick test_rg33_vigil_omitted_when_its_feast_is_commemorated_2025;
      Alcotest.test_case
        "RG33 third trigger: the vigil is omitted when its feast is transferred away \
         (2038-06-23, St John the Baptist)"
        `Quick test_rg33_vigil_omitted_when_its_feast_transfers_2038;
      Alcotest.test_case "RG128: the Assumption's vigil is violet (2025)" `Quick
        test_vigil_of_the_assumption_is_violet_2025;
      Alcotest.test_case "Easter extreme: 1598 earliest (22 Mar, Gauss-verified)" `Quick
        test_easter_extreme_1598;
      Alcotest.test_case "Easter extreme: 1666 latest (25 Apr, Gauss-verified)" `Quick
        test_easter_extreme_1666;
      Alcotest.test_case "Easter extreme: 2038 late-modern instance of the latest date" `Quick
        test_easter_extreme_2038_late_modern;
      Alcotest.test_case "Annunciation transfer: 25 March falls inside Holy Week (2016)" `Quick
        test_annunciation_transfer_inside_holy_week_2016;
      Alcotest.test_case "Annunciation/Joseph double transfer (2008)" `Quick
        test_annunciation_joseph_double_transfer_2008;
      Alcotest.test_case "Annunciation RG96 exception NOT triggered: general walk suffices (2057, 2007, 2012)"
        `Quick test_annunciation_exception_not_triggered_general_walk_suffices;
      Alcotest.test_case "Annunciation RG96 exception triggered: walk would cross Easter (2024; see also 2016)"
        `Quick test_annunciation_exception_triggered_when_walk_would_cross_easter;
      Alcotest.test_case "2011-07-04: the Precious Blood transfer" `Quick test_precious_blood_transfer_2011;
      Alcotest.test_case "All Souls falling on a Sunday (2025)" `Quick test_all_souls_on_a_sunday_2025;
      Alcotest.test_case "Christmas falling on a Sunday (2022)" `Quick test_christmas_on_a_sunday_2022;
      Alcotest.test_case "Holy Thursday is white amid violet Passiontide (2026)" `Quick
        test_holy_thursday_is_white_2026;
      Alcotest.test_case "Advent/Lent Ember ferias commemorated when impeded (1900, 1902)" `Quick
        test_ember_ferias_commemorated_when_impeded;
      Alcotest.test_case "IV-class ferias never commemorated (2026)" `Quick test_iv_class_ferias_never_commemorated;
      Alcotest.test_case "RG111(b): a II-class Sunday admits only a de-festo-II-classis commemoration (2009, 2026)"
        `Quick test_ii_class_sunday_admits_only_ii_class_commemoration;
      Alcotest.test_case
        "RG16(a): a Feast of the Lord leaves the impeded Sunday with NO commemoration at all, not even an \
         unrelated saint's own (2028-08-06, rubric + oracle)"
        `Quick test_rg16a_lord_feast_no_sunday_commemoration_2028;
      Alcotest.test_case
        "RG16(a), the Purification (oracle-ruled): observed outright, the Sunday not commemorated (2025-02-02)"
        `Quick test_purification_on_a_sunday_2025;
      Alcotest.test_case "Holy Family (RG17(b)), ordinary year (2026-01-11)" `Quick
        test_holy_family_ordinary_year_2026;
      Alcotest.test_case
        "Holy Family excludes the Baptism entirely on the latest possible date (RG91 e14 + RG112(a), 2030-01-13)"
        `Quick test_holy_family_excludes_baptism_2030;
      Alcotest.test_case "Holy Family, earliest possible date (2024-01-07)" `Quick
        test_holy_family_earliest_possible_date_2024;
      Alcotest.test_case "Holy Name of Jesus (RG17(a)), Sunday shape, subject asserted (2026-01-04)" `Quick
        test_holy_name_sunday_shape_2026;
      Alcotest.test_case
        "Holy Name of Jesus, fallback shape (RG17(a) \"secus die 2 ianuarii\", 2029-01-02 -- no Sunday in \
         the window)"
        `Quick test_holy_name_fallback_2029;
      Alcotest.test_case
        "RG110: 30 June, In Commemoratione S. Pauli Apostoli + commemoration-of-st-peter (2026-06-30)" `Quick
        test_rg110_june_30_2026;
      Alcotest.test_case
        "RG110 shape (c): Chair of St Peter admitted only as a commemoration of an ordinary Sunday, Paul \
         rides along (2004-02-22)"
        `Quick test_rg110_shape_c_2004;
      Alcotest.test_case
        "ef-rebootstrap: RG91 entry24 (III-class feast) outranks entry28 (class-4 feria) -- the six dates \
         deferred since 2038's own Easter-extreme pin (2008-04-02/04/05, 2038-03-06/08/09)"
        `Quick test_iii_class_feast_wins_class4_feria;
      Alcotest.test_case
        "RG91 e27/RG78: the BVM Saturday office wins, Holy Machabees commemorated -- matches the task \
         brief's own oracle example (2026-08-01)"
        `Quick test_bvm_saturday_2026;
      Alcotest.test_case
        "RG91 e27/RG78/RG26: the BVM Saturday office loses to Most Holy Name of Mary (subject Bvm too) \
         and is omitted, no confusion (2026-09-12)"
        `Quick test_bvm_saturday_loses_to_most_holy_name_of_mary_2026;
      Alcotest.test_case
        "RG112(d), fix round 1 (F1): the BVM Saturday office excludes Mt Carmel's own commemoration \
         (2033-07-16)"
        `Quick test_bvm_saturday_excludes_mt_carmel_2033;
      Alcotest.test_case "RG80/109(f): the Major Litanies, ordinary year, St Mark wins outright (2026-04-25)"
        `Quick test_major_litanies_ordinary_2026;
      Alcotest.test_case
        "RG111(b): the Major Litanies displace St Mark's own commemoration on a II-class Sunday (2010, 2021, \
         2027, 2032)"
        `Quick test_major_litanies_displaces_mark_on_ii_class_sunday;
      Alcotest.test_case
        "RG80's second trigger: Easter Monday = 25 April, the Litanies transfer to Easter+2 (2011)" `Quick
        test_major_litanies_transfer_2011_easter_monday;
      Alcotest.test_case "readings step 1: sanctoral proper (John of God, 2038-03-08)" `Quick
        test_readings_step1_sanctoral_proper;
      Alcotest.test_case "readings step 2: temporal proper (Lent I Monday, 2026-02-23)" `Quick
        test_readings_step2_temporal_proper;
      Alcotest.test_case "readings step 3: ferial resumption (Advent I Monday, 2025-12-01)" `Quick
        test_readings_step3_ferial_resumption;
      Alcotest.test_case "readings step 4: Common of Non-Virgins I (2038-03-06)" `Quick
        test_readings_step4_common_felicitas;
      Alcotest.test_case "readings step 4: Common of Non-Virgins II (2038-03-09)" `Quick
        test_readings_step4_common_frances;
      Alcotest.test_case
        "readings: displaced-Sunday feria pins M26 shape 2(b)'s KNOWN-WRONG walkback (2026-10-26)" `Quick
        test_readings_displaced_sunday_feria_known_divergent;
      Alcotest.test_case "RG87: Rogation Wednesday admitted (2024)" `Quick
        test_rogation_wednesday_admitted_2024;
      Alcotest.test_case "RG87/113: Rogation Wednesday yields to an impeded feast (2026)" `Quick
        test_rogation_wednesday_yields_to_a_feast_2026
    ] )