aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md6
-rw-r--r--data/ef/expected-divergences-missalemeum.sexp9
-rw-r--r--dune7
-rw-r--r--lib/kernel/precedence.mli18
-rw-r--r--test/test_oracle.ml10
-rw-r--r--test/test_validate.ml13
6 files changed, 48 insertions, 15 deletions
diff --git a/README.md b/README.md
index e5a6c5f..9b11d73 100644
--- a/README.md
+++ b/README.md
@@ -12,10 +12,12 @@ reading citations, correct to year 9999. See the design and rules research under
```sh
opam switch create . 5.2.0 -y # first time: local OCaml switch
-opam install -y dune alcotest qcheck qcheck-alcotest
+opam install -y dune alcotest qcheck qcheck-alcotest sexplib ppx_sexp_conv
dune build
dune test
-dune exec colitur -- easter 2026
+dune exec colitur -- easter 2026 # Easter and its Easter-relative anchors
+dune exec colitur -- temporal 2026 # the EF temporal cycle only, one line per day
+dune exec colitur -- day 2026 # the full resolved EF calendar (temporal + sanctoral)
```
## License
diff --git a/data/ef/expected-divergences-missalemeum.sexp b/data/ef/expected-divergences-missalemeum.sexp
index 5dbe3ed..f50898b 100644
--- a/data/ef/expected-divergences-missalemeum.sexp
+++ b/data/ef/expected-divergences-missalemeum.sexp
@@ -16,9 +16,12 @@
; did not build. Those are honestly verdicted [missalemeum] -- colitur is
; short a feature or a row, not correct -- and each is cross-referenced
; into docs/research/rules-register.md §6 as an open item, not silently
-; absorbed as if colitur were right. One entry (M13) is [verdict open]:
+; absorbed as if colitur were right. TWO entries (M11 and M13) are
+; [verdict open] -- corrected, final fix wave, item 7: this note previously
+; said "one entry (M13)", missing M11 (whose own verdict changed from
+; [colitur] to [open] in fix round 1, see M11's own entry below). Both are
; adjudicated as UNRESOLVED after real primary-source effort, not defaulted
-; past -- see that entry's own note and the task report for the full
+; past -- see each entry's own note and the task report for the full
; search.
;
; [expected_rows] is the exact row count this entry accounts for over the
@@ -38,7 +41,7 @@
((id M2)
(citation "RG 91 entry 27 (\"Officium sanctae Mariae in sabbato\") -- register §4")
(verdict missalemeum)
- (note "Every otherwise-unoccupied IV-class Saturday should carry the votive Office of the BVM (white; missalemeum's own titles cycle \"I\"..\"V Mass of the B. V. M. -- Salve, Sancta Parens\"). temporal_ef.ml's [band] already has an entry-27 comment acknowledging this row exists, but [Temporal_ef.temporal] never actually CONSTRUCTS this office -- an unimpeded Time-after-Epiphany/-Pentecost Saturday gets a bare ferial slug and season green instead. A genuine feature gap, not a citation dispute; tracked in register §6, not built in this task.")
+ (note "Every otherwise-unoccupied IV-class Saturday should carry the votive Office of the BVM (white; missalemeum's own titles cycle \"I\"..\"V Mass of the B. V. M. -- Salve, Sancta Parens\"). precedence_ef.ml's [band] already has an entry-27 comment acknowledging this row exists (corrected attribution, final fix wave: this note previously said temporal_ef.ml, but [band] is precedence_ef.ml's own function), but [Temporal_ef.temporal] never actually CONSTRUCTS this office -- an unimpeded Time-after-Epiphany/-Pentecost Saturday gets a bare ferial slug and season green instead. A genuine feature gap, not a citation dispute; tracked in register §6, not built in this task.")
(expected_rows 17))
((id M3)
(citation "RG 87 (Minor Litanies/Rogations, Mon/Tue before Ascension) -- the SAME citation as the lectio allow-list's own C8 (data/ef/expected-divergences.sexp)")
diff --git a/dune b/dune
index de5051c..8a00001 100644
--- a/dune
+++ b/dune
@@ -12,9 +12,10 @@
; test suite could not have caught this on its own -- it took a genuinely
; clean rebuild to surface it.
;
-; (:standard) keeps whatever `default` would otherwise resolve to (the
-; package's own install artifacts -- executables, libraries) so this ADDS a
-; requirement rather than replacing dune's own default behaviour.
+; (alias_rec install) keeps whatever `default` would otherwise resolve to
+; (the package's own install artifacts -- executables, libraries, reached
+; recursively through every subdirectory's own `install` alias) so this
+; ADDS a requirement rather than replacing dune's own default behaviour.
(alias
(name default)
(deps
diff --git a/lib/kernel/precedence.mli b/lib/kernel/precedence.mli
index d30e5c3..4225eb7 100644
--- a/lib/kernel/precedence.mli
+++ b/lib/kernel/precedence.mli
@@ -38,7 +38,23 @@ type ('s, 'r) rules = {
('r candidate * privilege) list;
(** RG 108-111: how many commemorations are admitted, and in what order;
anything filtered out here is recorded in {!resolution.omitted}, not
- dropped. *)
+ dropped.
+
+ OBLIGATION ON THE IMPLEMENTATION, not enforced by this type: every
+ candidate this function returns must be a value taken UNCHANGED
+ from its input list, never rebuilt (e.g. via a [{ c with ... }]
+ record update, even one that copies every field back unchanged).
+ {!resolve}'s own [omitted] accounting distinguishes an admitted
+ candidate from a dropped one by PHYSICAL equality ([==]) on the
+ candidate value, not structural equality -- a rebuilt record is
+ [=] to the original but not [==], so {!resolve} would then count
+ it as dropped a SECOND time (once because it is genuinely absent
+ from the admitted set, once because its identity no longer
+ matches its own admitted copy), silently double-counting rather
+ than raising. This obligation previously lived only in one rite's
+ own module documentation (Rite_ef.Precedence_ef.admit); stated
+ here because this signature -- not any one rite's implementation
+ of it -- is what an author of the next rite reads. *)
}
(** The outcome of resolving one day's candidates. *)
diff --git a/test/test_oracle.ml b/test/test_oracle.ml
index 152464a..ac8d95d 100644
--- a/test/test_oracle.ml
+++ b/test/test_oracle.ml
@@ -56,9 +56,13 @@
entries; RG 110's inseparable-Peter/Paul commemoration is unimplemented
code, a real feature this task did not build) -- honestly verdicted
[missalemeum] (colitur is short a feature or a row, not right), never
- silently absorbed as if colitur were correct. One entry (M13) is
- [verdict open]: adjudicated as unresolved, not resolved either way --
- the brief's own explicit permission ("say so as an open item") used for
+ silently absorbed as if colitur were correct. TWO entries (M11 and
+ M13) are [verdict open] -- CORRECTED, final fix wave, item 7: this
+ comment previously said "one entry (M13)", missing M11, whose own
+ verdict was changed from [colitur] to [open] in fix round 1 (see M11's
+ own entry below for why) but this summary was never updated to match.
+ Both are adjudicated as unresolved, not resolved either way -- the
+ brief's own explicit permission ("say so as an open item") used for
real, not defaulted past. See the task report for every entry's full
reasoning and primary-source citation. *)
diff --git a/test/test_validate.ml b/test/test_validate.ml
index ee7f288..89053c1 100644
--- a/test/test_validate.ml
+++ b/test/test_validate.ml
@@ -79,9 +79,16 @@ let test_easter_extremes () =
let ys = extreme_years () in
(* Both extremes genuinely occur in 1583..2500 (earliest 1598, latest
1666 -- verified against Computus.gregorian_easter directly, not
- transcribed); requiring just "non-empty" would have passed even if the
- search silently found only one of them (register finding 15). *)
- Alcotest.(check int) "found both extreme years (earliest 22 Mar and latest 25 Apr)" 2 (List.length ys);
+ transcribed). CORRECTED (final fix wave, item 7): this used to assert
+ only [List.length ys = 2], a cardinality check where an identity check
+ was called for -- the comment already named 1598 and 1666, but nothing
+ confirmed [ys] actually contained THOSE two years rather than some
+ other pair the search happened to find first; a version of
+ [extreme_years] that silently found the wrong two years but still
+ found exactly two would have passed this unchanged. Asserting the
+ identities directly is strictly stronger and costs nothing extra. *)
+ Alcotest.(check (list int)) "found exactly 1598 (earliest 22 Mar) and 1666 (latest 25 Apr)"
+ [ 1598; 1666 ] ys;
List.iter check_year ys
(* The confidence-to-9999 core: random years across the whole domain. *)