blob: 709a7117412e0c793750d63b8e3544d6ca9fde27 (
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
|
(** The invariant harness -- validation layer 2. Checks run over a *liturgical*
year, not a civil year: a season that straddles January would otherwise look
as though it recurs. *)
type failure = { year : int; date : string; check : string; detail : string }
val failure_to_string : failure -> string
(** [run vocab ~year_start ~temporal ~anchors ~year] returns every invariant
violation in the liturgical year opening in civil year [year]. An empty
list means the year is clean.
[anchors y] is the rite's own independent restatement of its fixed and
Easter-derived named days for civil year [y], as (expected slug, date)
pairs -- not derived from [temporal] itself, so a drift between the two
is caught rather than invisible. [run] consults both [anchors year] and
[anchors (year + 1)], since a liturgical year straddles two civil years,
and checks only the pairs whose date actually falls within the year
walked.
Total over the whole 1583..9999 domain, including [year] = 9999: the
liturgical year opening there continues into out-of-domain civil year
10000, so the walk is clamped to 31 December 9999 and the checks run
against that truncated final year rather than raising. *)
val run :
('s, 'r) Vocab.t ->
year_start:(int -> Date.t) ->
temporal:(Date.t -> ('s, 'r) Temporal.t) ->
anchors:(int -> (string * Date.t) list) ->
year:int ->
failure list
|