diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-08-21 14:42:30 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-08-21 14:42:30 +0200 |
| commit | 12b97761019cfa02ca0da8a5fb50ef815d07685c (patch) | |
| tree | 11e7306a1a5de2f5226eea06282515ff38b226d5 /test/test_precedence_ef.ml | |
| parent | 0eae32f67145065b0e4960650ae06c293fd9f272 (diff) | |
| download | colitur-12b97761019cfa02ca0da8a5fb50ef815d07685c.tar.gz colitur-12b97761019cfa02ca0da8a5fb50ef815d07685c.zip | |
feat(ef): implement RG 33's third omission trigger
RG 33 omits a II/III-class vigil in three cases: it falls on a Sunday, it
falls on a I-class feast, "vel si festum cui praemittitur in alium diem
transferri aut ad commemorationem reduci contingat". Only the first two
were built; the third was recorded in precedence_ef.ml as unimplemented on
the grounds that no witness existed in the shipped data. That reasoning was
wrong, and the rule fires on 1 744 days across 1583-9999.
Both halves of the clause reduce to one observable question -- is the feast
the OBSERVED office on the following day (RG 34 puts it there) -- so the
kernel asks it once per candidate, after place_transfers has settled the
year. No fixed point is needed: a vigil is a candidate only on its own day,
never on its feast's, so suppressing it cannot change what the next day
observes. Precedence.rules gains vigil_feast, which returns the slug of the
feast a vigil precedes; the kernel cannot infer that itself, because only
two of the five vigil/feast pairs share a slug stem.
Blast radius, measured pre-change binary vs HEAD over the whole domain and
classified: 1 744 days, three shapes, zero unexplained. 1 199 are the feast
reduced to a commemoration (10 August on a Sunday, St Lawrence); 478 and 67
are the feast transferred under RG 96 after the Sacred Heart or Corpus
Christi takes its day. The Assumption's and the Ascension's vigils never
qualify -- their I-class feasts always keep their own day.
Independently witnessed, which is unusual here. The published Ordo -- the
only witness outside the Divinum Officium -> missalemeum -> lectio lineage
-- omits St Lawrence's vigil on 2025-08-09, agreeing with colitur against
both engines. That date had been read earlier as an Ordo gap; the Ordo was
right, and correcting the misreading is what surfaced this clause. On
2027-08-09 the feast does keep its day and the Ordo omits a vigil colitur
correctly keeps, which is a genuine Ordo gap.
Allow-lists: C39 (lectio, 10 rows) and a 2038 oracle class citing the
register, the 2026-2027 window having no instance. The golden pin asserting
St Lawrence's vigil is violet moved 2025 -> 2027; its own comment had
reasoned about the vigil's weekday and missed that RG 33 also looks at the
feast's. Two new pins cover both shapes of the clause.
The vigil/feast table is built with Slug.of_string_exn: mutation testing
showed that of_string plus Result.to_option turns a typo into None, which
this hook's contract reads as "not a vigil", switching the rule off in
silence. Two tests assert the table against the shipped data in both
directions.
Diffstat (limited to 'test/test_precedence_ef.ml')
| -rw-r--r-- | test/test_precedence_ef.ml | 10 |
1 files changed, 7 insertions, 3 deletions
diff --git a/test/test_precedence_ef.ml b/test/test_precedence_ef.ml index 3f3e070..6bd7bfd 100644 --- a/test/test_precedence_ef.ml +++ b/test/test_precedence_ef.ml @@ -370,7 +370,7 @@ let test_all_souls_yields_to_sunday () = in let all_souls = cand ~origin:P.Sanctoral ~layer:PE.universal_layer "ef-all-souls" in let rules = - { P.band = (fun c cd -> PE.band c cd); + { P.vigil_feast = (fun _ -> None); band = (fun c cd -> PE.band c cd); disposition = (fun ~winner:_ ~loser:_ -> P.Omit); admit = (fun ~observed:_ ~temporal:_ cs -> List.map (fun (c, p, (_ : int)) -> (c, p)) cs) } in @@ -1443,7 +1443,11 @@ let test_admit_uses_precedence_not_slug_when_they_disagree () = [rules] deliberately reused, not rebuilt per test, since it is always the same three real functions. *) -let real_rules = { P.band = PE.band; disposition = PE.disposition; admit = PE.admit } +(* The real EF ruleset, including RG 33's third-trigger hook -- a stub here + would make any test built on [real_rules] blind to it. *) +let real_rules = + { P.vigil_feast = PE.vigil_feast; band = PE.band; disposition = PE.disposition; + admit = PE.admit } (* I-class day, zero admitted: the strongest form of "does not vanish" -- EVERY commemoration due is dropped (RG 111: "none save one privileged", @@ -1929,7 +1933,7 @@ let test_all_souls_transfers_end_to_end () = in let all_souls = cand ~origin:P.Sanctoral ~layer:PE.universal_layer "ef-all-souls" in let rules = - { P.band = PE.band; disposition = PE.disposition; + { P.vigil_feast = (fun _ -> None); band = PE.band; disposition = PE.disposition; admit = (fun ~observed:_ ~temporal:_ cs -> List.map (fun (c, p, (_ : int)) -> (c, p)) cs) } in let resolution = P.resolve rules day_ctx ~temporal:sunday ~sanctoral:[ all_souls ] in |
