| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Validation layer 5: fourteen hand-verified pins in test/test_golden.ml,
wired into the suite via test/test_colitur.ml. Each pinned day was computed
and checked against its RG citation before being written down, never
transcribed from `colitur day` output:
- Easter extremes 1598 (earliest, 22 Mar), 1666 (latest, 25 Apr) and 2038
(late-modern instance of the same latest date) -- all three independently
computed by hand via the Gauss/Meeus Gregorian Easter algorithm, not read
off Computus.gregorian_easter. extreme_years in test_validate.ml already
established 1598/1666 as the true 1583-2500 extremes (correcting a stale
1818/2038 comment); this file pins the resolved DAY there, not just the
date.
- Annunciation transfer, 25 March inside Holy Week (2016: 25 March is
literally Good Friday) and the double transfer with St Joseph (2008:
RG96's Attamen(a) claims Easter+8 for the Annunciation first, Joseph's
own RG96 walk continues past it to the next day).
- The RG96 Attamen(a) exception's own CONDITION pinned on both sides: 2057,
2007, 2012 (general walk suffices, lands before Easter, no exception) vs
2016/2024 (walk would cross Easter, Easter+8 fires).
- 2011-07-04, the Precious Blood transfer (Sacred Heart outranks it
outright on 1 July; the RG96 walk skips Visitation and a Sunday before
landing on 4 July).
- All Souls falling on a Sunday (2025, RG96 Attamen(b)) and Christmas
falling on a Sunday (2022, RG91 entry 1 -- no contest, since 25 December
is never an ordinary Sunday candidate in the EF temporal cycle).
- Holy Thursday's white amid violet Passiontide (2026, RG128(b)/RG122) --
the case colitur and lectio previously agreed was violet, so the
differential could never have caught it; only a golden pin or the
missalemeum oracle can.
- Advent/Lent Ember ferias commemorated when impeded (1900, 1902, RG24 +
RG109(e)) contrasted with IV-class ferias never commemorated (2026,
RG26) and RG111(b)'s Sunday rank floor (2009, 2026) -- all three assert
the actual displaced/excluded candidate is present in `omitted`, not just
that `commemorations` is empty, so a day with no losing candidate at all
could not pass vacuously.
2038 is deliberately NOT pinned day-by-day: register item F4 confirms
2038-03-06/08/09 are wrong (a lectio bootstrap-generator defect, not
fixable here). Only the Easter-week days, untouched by that bug, are
pinned; the exclusion is stated in the test's own comment, not silent.
Every weekday asserted was independently cross-checked against `date -d
<iso> +%A` (glibc, wholly outside this codebase) before being written down.
Precedence outcomes were traced against precedence_ef.ml's own band/
disposition/admit/transfer_target, not merely observed to look plausible;
where the primary text alone doesn't fully settle an outcome (the 2008
Annunciation/Joseph tie-break, both landing at RG91 table entry 11), the
test's own comment says so rather than overclaiming a citation.
Perturbation-tested: temporarily broke Holy Thursday's white-colour special
case in temporal_ef.ml, confirmed the golden test failed with a clear
day-and-field diff, reverted.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Validation layer 4: an oracle harness against missalemeum (Divinum
Officium data), independent of the lectio bootstrap chain colitur's
own sanctoral data comes from -- the only layer that can catch an
error inherited from that bootstrap, and the only one that can
validate commemorations at all (the lectio differential explicitly
excludes them, per its own header comment).
tools/extract_missalemeum_oracle.py shapes the fixture from lectio's
sources/snapshot.tar.gz outside the test (no JSON library in this
project's frozen deps, same reasoning test_differential.ml's own
fixture already documents). test/fixtures/missalemeum-ef-2026-2027
.txt (730 days, SHA-256 pinned and asserted) + its own .provenance
note record exactly how to regenerate it.
test_oracle.ml compares three axes the oracle actually supports:
rank, colour (SET MEMBERSHIP -- 14 of 730 days carry two colours,
e.g. rose+violet on Gaudete/Laetare, which independently vindicates
this project's own rose reading against lectio's violet-only one,
recorded in the register), and commemoration presence/count. Slug
identity is deliberately out of scope (needs a title->slug mapping,
the data audit's own business, not the automated comparator's).
Of 730 days, 688 matched cleanly outright. The remaining 42 are all
named in data/ef/expected-divergences-missalemeum.sexp (14 cited
entries, M1-M14): most are genuine primary-source-confirmed findings
this task adjudicated and fixed in the two preceding commits (RG 33,
RG 109/111, Holy Thursday's colour); the rest are real, cited,
deferred feature/data gaps (RG 91 entry 27's BVM-Saturday office, RG
110's inseparable Peter/Paul commemoration, four sanctoral entries
missing from lectio's own source) or genuine oracle-side artifacts
-- honestly verdicted against whichever side this task's own
primary-source research actually backs, never defaulted to colitur.
One entry (M13, St Joseph vs the Friday of Passion Week 2027) is
verdict open: adjudicated as unresolved after real search effort,
not guessed past.
The data audit: every sanctoral entry the comparison flagged was
hand-checked against the 1962 calendarium, plus a 20-entry
deterministic random control sample (seed 20260812) drawn
independently of the flagged set. The control sample caught two
entries (benedict, frances-rome) marked Commemoration_only in the
bootstrapped data when the primary calendarium lists them as plain
III-class feasts with their own Office -- traced to lectio's own
source, not fixable here, and reported as a signal (10% of a random
sample) rather than a blanket claim. Coverage recorded honestly in
docs/research/rules-register.md's own three buckets: confirmed by
oracle (200/322), confirmed by hand (23/322, 2 of them wrong),
unverified (115/322) -- the unverified bucket stated explicitly
rather than left implicit.
Harness teeth demonstrated and reverted (not committed): a fixture
rank/colour edit on a previously-clean day fails both the checksum
pin and the no-unexplained-differences assertion independently; an
expected_rows drift on the allow-list fails the citation-count
assertion. Both captured with their exact failure messages, both
reverted before this commit.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Validation layer 3 (design spec's five): compares colitur's real day-by-day
EF output against lectio (sibling project, Go), 2005-2050, one line per
civil day. Of 16801 day-pairs, 11206 already agree on the seven leading
columns; the 5595 that don't resolve into exactly 25 distinct field-diff
signatures, all triaged.
Three strictly separate layers, per the controller's ruling (the brief's
single flat allow-list assumed a handful of differences, not 5595):
- Layer A (test_differential.ml, norm_season/norm_slug): vocabulary. An
explicit, closed table of naming synonyms with no liturgical substance
(lectio's easter/christmas vs colitur's paschaltide/christmastide; a
handful of slugs that are two names for the identical office). No
wildcards -- every entry is a literal string pair.
- Layer B (strip_epiphany_index): numbering. The one slug family whose
lectio/colitur index offset is not a constant (Time-after-Epiphany week
numbering, register 3c#5) has its embedded digit stripped to a common
form on both sides before comparing; rank and colour stay fully compared.
- Layer C (data/ef/expected-divergences.sexp): the cited allow-list. Ten
genuine liturgical disagreements, each citing its RG paragraph and
naming which engine is right (always colitur, verified against the
Missal/register, never against lectio's own behaviour). This is the
only layer permitted to cover a difference in rank, colour, or which
celebration is observed. Five extend or restate register 3c's already-
documented divergences (season boundary, Sunday I-class, Advent Ember
ferias, Rogations); five are new, found and adjudicated in this task
(Lent Ember days, the Nativity Octave, Ember-day-vs-saint precedence,
the St Joseph transfer off a Lent Sunday, and the 2011 Sacred Heart /
Precious Blood / Visitation collision). expected_rows on each entry is
an exact regression pin, asserted by the test, not documentation.
13 January (register 6's long-open "Baptism of the Lord" item) is
confirmed empirically fixed already -- Task 11's sanctoral wiring closed
it before this task started -- so it is not allow-listed; the only
residual difference there is the season boundary already covered by C1.
Two stated limits carried from the brief (commemorations are not
comparable; lectio's own EF oracle asserts season only, 2025-2026 only,
so a rank/colour difference is not presumptive evidence against colitur)
plus a third found during this task (the week column is a display
convention on both sides, not a liturgical fact, and is not compared at
all) are documented in the test file's own doc comment.
Fixture: test/fixtures/lectio-ef-2005-2050.txt, committed as plain text
(1.4 MB), generated by lectio commit 2386a45; provenance recorded in the
sibling .provenance file. Colitur's side is recomputed fresh from the
library on every run, through the same Calendar/Rite_ef pipeline
`colitur day` uses, not the compiled binary.
Proved the harness has teeth by two reverted perturbations: a genuine
colour difference injected into a fully-covered fixture row fails the
"no unexplained differences" check with the exact mismatched row printed;
a one-row drift in an allow-list entry's expected_rows fails the count
check independently, showing it is not merely a duplicate of the first
assertion. 236/236 tests green, clean-build verified, deterministic
across OCAMLRUNPARAM=R.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
search_from could walk up to 400 days past origin before Calendar's own
~start ~stop clamp is ever consulted, and nothing stopped it probing
occupant on a date past 31 December 9999 -- occupant chains through the
real EF rite's temporal, which calls Computus.gregorian_easter, not
total outside 1583..9999 (it Date.makes and failwiths on Error).
Not reachable with the shipped sanctoral data alone, but reachable
through the project's own primary extension path: an overlay adding an
I-class feast on 25 December leaves nothing but Class2 Nativity-octave
days for the rest of civil year 9999, so the unguarded search reached 1
January of year 10000 and crashed there with 'computus: year 10000 out
of range 1583..9999'. 9999 is an in-range year and the kernel's contract
is 'never raises on in-range input'.
search_from now also stops, without probing occupant again, once it
passes Date's own domain ceiling -- the same 'return a finite date, let
Calendar's own out-of-range handling record it, never pretend to have
found something admissible' contract the existing step-count guard
already follows.
Two new tests, both mutation-verified to actually reproduce the crash
when the guard is removed (see the task report): a precedence_ef.ml unit
test using the real Temporal_ef.temporal as occupant (a synthetic
occupant can never discriminate this, since it never calls Computus
itself), and a Calendar-level integration test reproducing the exact
overlay-based scenario the review found.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Convert lectio's tridentine-calendar.ini (322 entries) into
data/ef/sanctoral.sexp via a validating OCaml converter, tools/
bootstrap_sanctoral.ml, rather than a hand-written script: every field is
built through Slug.of_string, Colour.of_string and Vocab_ef.rank_of_string,
so the emitted sexp is valid by construction.
Two conversion decisions, both documented rather than buried:
- subject defaults to Subject.Saint, overriding Celebration.make's kernel
default of Subject.Temporal, for the 316 entries with no explicit class;
- rank = commemoration maps to status = Commemoration_only with an inferred
Class3 (not a citation -- it is what the 1960 reform reduced most simple
feasts from), recorded as an open item in the rules register for the
oracle to adjudicate.
Every celebration is tagged layer = Precedence_ef.universal_layer, the
provenance id RG 91's band classifier reads to tell the universal calendar
from proper/indult data.
The generated file carries a provenance header: source path, its SHA-256,
and the UTC conversion date, so re-bootstrapping against a newer lectio is
reproducible and diffable. Output is byte-identical across runs.
test/test_sanctoral_ef.ml loads the file through Layer.load and checks the
counts independently derived from the source INI (322 entries, 114
Commemoration_only, 12 Class1, no Subject.Temporal, every date resolves in
a leap year), plus two named spot-checks against the INI's own text -- one
entry with an explicit class field, one commemoration -- so a passing count
cannot hide the wrong 322 entries having been converted.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Precedence_ef.band transcribes RG 91's 28-entry Table of Precedence
(rules-register.md ยง4) for the EF rite: given a day's context and a
candidate celebration, returns the table's own entry number, 1-28
(I class 1-13, II class 14-21, III class 22-26, IV class 27-28); lower
wins. Every branch carries its entry number and register citation in a
comment, checked in the table's own numeric order.
Two entries are transcribed as the register states them even though
they invert the pattern the rest of the table follows: at III class,
23 (particular calendars) outranks 24 (universal), the reverse of how
11/12 and 14/16/19/20 rank a universal feast ahead of a proper one at
I and II class.
Sanctoral-origin, layer-decided entries (11-13, 14/16/19/20, 23/24)
follow the brief's structural insight: a celebration whose layer is
not the universal base is an overlay -- proper, or indult if its
layer id also carries the indult prefix. Neither the universal-layer
id nor the indult prefix is an RG citation; both are colitur's own
data-modelling convention, exposed from the module so whichever task
loads the real EF sanctoral overlays can align to them.
Vigils (21, 26) are read off the temporal cycle's own -vigil slug
suffix rather than gated on origin, since a II/III-class vigil can be
either temporal-origin (Ascension, already produced by temporal_ef) or
sanctoral-origin (a saint's vigil, no task has loaded yet); Ember days
(part of entry 18) are read off temporal_ef's own ember slug prefixes
rather than re-derived, since the September anchor is independently
flagged there as one of the more contested dates in the calendar.
A candidate shape the table has no row for (e.g. a Class1 vigil that
is not Nativity or Pentecost, or a Class4 candidate marked as a vigil
-- RG 91 has no IV-class vigil either) returns a dedicated unclassified
sentinel (max_int) rather than being folded into a same-rank entry it
does not belong to.
test_precedence_ef.ml is table-driven: one Alcotest.test_case per RG
91 entry (55 rows total, several entries covered by more than one
named day so a single missed offset cannot hide behind a passing
sibling), each date computed from Computus.gregorian_easter rather
than hand-typed, so an arithmetic slip cannot pass by accident.
|
| |
|
|
|
|
|
|
|
|
|
| |
Transfers make per-date resolution impossible to do correctly: resolving 25
March can push a feast onto 26 March, and RG 97-98 has coinciding I-class
feasts transfer in table order, which needs global knowledge. So year computes
a whole liturgical year in one pass and day indexes into it. Pure, no cache, no
mutable state.
This commit resolves each day but does not yet place deferred transfers; they
are recorded with a reason. Task 6 adds the placement pass.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
Three rite-supplied functions, not one: band (who wins, RG 91), disposition
(what happens to the loser, RG 92-95) and admit (how many commemorations are
admitted, RG 111). The loser's fate depends on the loser's own rank, so
conflating them would resist extension.
resolve takes the temporal candidate separately from the sanctoral list, which
makes it total by construction. Every candidate lands in exactly one of
observed, commemorations, deferred or omitted -- nothing is dropped silently,
which is what makes the no-celebration-lost invariant checkable.
|
| |
|
|
|
|
|
|
|
|
|
| |
Coverage, season contiguity and completeness, Sunday-aligned week numbering,
slug well-formedness, weekday agreement and vocabulary closure.
Checks run over a liturgical year rather than a civil one, since Christmastide
straddles January and would otherwise appear to recur.
Run against EF temporal for landmark years, both Easter extremes, and 200
random years across 1583..9998 -- the property layer is how confidence reaches
past the oracle horizon.
|
| |
|
|
|
|
|
| |
Eight seasons in canonical liturgical-year order, each carrying its RG
citation, and the four classes. season_slug_word is deliberately distinct from
season_to_string: slugs are lectionary keys adopted verbatim from lectio,
which calls Paschaltide 'easter' and Christmastide 'christmas'.
|
| |
|
|
|
|
|
| |
Entries sort by slug so equal layers serialise identically. The by-date index
is built once per layer rather than per year, since fixed dates are
year-independent; a full-domain sweep would otherwise rescan every entry for
every day. load turns parse and validation failures into result.
|
| |
|
|
|
|
|
| |
Names is an open language-keyed assoc kept in canonical order so equal name
sets serialise identically. Citation carries references only, never text.
Date_spec ships the one form the EF sanctoral needs; 29 February is
constructible and resolves to None in common years.
|
| |
|
|
|
|
| |
Both parse through a smart constructor returning result, and both hand-write
t_of_sexp so a malformed value in a data file is rejected at load rather than
silently accepted -- deriving the converter would have bypassed validation.
|
| |
|
|
|
|
| |
The six liturgical colours and the Lord/BVM/saint/temporal distinction are
common to both Roman forms, so they are shared closed variants rather than
rite-parametric. Subject is so named because class is an OCaml keyword.
|
| |
|
|
|
|
|
| |
Anonymous Gregorian (Meeus/Jones/Butcher) for OF+EF; Meeus Julian mapped to the
proleptic-Gregorian date for future eastern rites. Verified vs known dates
(2000/2024-27, 1583; Orthodox 2023/24) and EXHAUSTIVELY over 1583..9999: every
Easter is a Sunday in [Mar22,Apr25].
|
| |
|
|
|
|
|
| |
Hinnant civil<->days rep (1970-epoch rata die); make validates month/day and
the 1583..9999 domain; of_rata/add_days are total arithmetic. Weekday, compare.
Tested: known weekdays, leap boundaries, rejects; qcheck round-trip / add-inverse
/ weekday-cycle properties over random in-range dates.
|
|
|
OCaml 5.2.0 local switch; colitur_kernel library, a colitur executable stub,
and an alcotest+qcheck test runner. AGPL LICENSE, README, generated colitur.opam.
|