summaryrefslogtreecommitdiff
path: root/tools/dune
Commit message (Collapse)AuthorAgeFilesLines
* test(fiuv-ordo): add the FIUV universal Ordo as a second, independent witnessLukasz Kasprzak2026-08-221-0/+14
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Wires in the FIUV (Foederatio Internationalis Una Voce) Ordo for 2025-2026 as validation layer seven -- the first UNIVERSAL (not diocesan) source in this project, and the first compiled by someone other than the LMS editions' own Peter Day-Milne (Joseph Shaw, FIUV President). tools/extract_fiuv_ordo.ml hand-parses pdftotext's -layout dump of the Latin-language ordo (no Str/regex, frozen deps). Three real parsing traps were found and fixed by tracing actual false results against the raw source, not assumed in advance: - "Gloria" is also the first word of "Gloria Patri" (the psalm doxology), which can appear INSIDE the very Mass clause being scanned (Good Friday: "...omittuntur ps. Iudica me et Gloria Patri), Gloria, sine Credo..."). Guarded by rejecting any "Gloria" hit whose next token is "Patri". - Christmas Day's own rubric uses the PLURAL "Missae"/"Missae" ("tres Missae celebrantur..."), never the singular "Missa" every other real day anchors on -- silently produced gloria=None/credo=None on the one most doctrinally unambiguous day in the calendar until traced and fixed, guarded in turn against "Missae defunctorum" (an unrelated Requiem-prohibition notice appearing at the end of many day-blocks). - The Vespers boundary (bounding the primary Mass option before a second "Vel Missa" alternative or before Vespers information) missed the ligature spelling "VESPERÆ", the dominant form in this source, letting a whole day's trailing prose leak into the captured [praef] field on the task brief's own worked example day (19 September). A distinct second stop-marker bug: the malformed trailing entry this source shares structurally with the LMS PDFs (a stray, mislabelled duplicate of 1 January) was initially over-matched by a generic "first token is 4 digits" heuristic, which false-positived on an indulgence-decree date citation wrapped mid-sentence in October ("...20 augusti / 1885, 26 augusti 1886..."), truncating the extraction to 308 of 400 real days. Fixed by anchoring on the tail entry's own distinctive "1st Jan" opening instead. Five opposite-prediction Creed probes are re-verified by the tool itself against its own extracted rows (a failed probe is a hard exit, not a printed claim) before the fixture is written. test/test_fiuv_ordo.ml compares the Creed against colitur across all 399 comparable days (Holy Saturday excluded, no Mass that day) -- CLEAN, zero unexplained divergences, a genuine second confirmation of {!Rite_ef.Rubrics_ef.creed} independent of the LMS lineage. [praef], [te_deum] and [class_] (including the FIUV ordo's own "III cl." vs "III cl. (Priv.)" Cum Sanctissima convention, stated on its title page) are captured into the fixture but deliberately not compared -- colitur has no Gloria/preface predicate yet, and its default configuration models no Cum Sanctissima overlay to compare the class convention against. All 634 tests green (dune test), 635 with the exhaustive sweep and make check. No lib/ file touched. Claude-Session: https://claude.ai/code/session_017ZBxCCRM2ojnBupp3SBxV9
* test(ef): the LMS Ordo as a sixth validation layerLukasz Kasprzak2026-08-221-0/+13
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | An independent lineage -- neither Divinum Officium, missalemeum nor lectio -- covering the Mass formulary and the Creed. Characterised before use, per the discipline the extraordinaryform.org Ordo's blanket St Lawrence vigil gap taught this session: five opposite- prediction Creed probes (Sunday/feria, octave-override vs plain II-class, I-class) all confirmed against the raw extracted text before a single divergence was adjudicated, recorded in the fixture's own provenance header. tools/extract_lms_ordo.ml hand-parses pdftotext -layout output (no Str/regex, frozen deps) into test/fixtures/lms-ordo-2024-2025.sexp, 400 day-rows (2024-11-27..2025-12-31). Gl/Cr print per Mass-option, not per day; the extractor takes the first pair in reading order, which the source's own layout guarantees is the universal entry's, since a diocesan variant always follows it, never precedes it. The Ordo's I-V roman numeral ("Missae de sancta Maria in sabbato", RG 309(a)) is matched whole-line only, never by substring -- "V Mass of BVM" is a substring of "IV Mass of BVM". test/test_lms_ordo.ml compares two axes 1:1 by date: - the Creed (RG 475-476): 399 of 400 days comparable (Good Friday has no Mass at all, asserted as the only such day); one genuine divergence, adjudicated to the ORDO, not colitur -- All Souls' Day 2025-11-03 is a Requiem Mass, and colitur's own creed predicate has no notion of one, a pre-existing scope gap rubrics_ef.ml already documented in its own header before this task existed. - the BVM-Saturday seasonal Mass selection, the addendum's own "sharper check": all 14 Votive days in the window match their season's own numeral exactly, both directions, independently re-derived from RG 309(a) rather than calling colitur's private function. - the formulary override, for the three sources it can honestly discriminate (Proper 179, Preceding_sunday 66, Common 2, all clean); Own_slug (139) is excluded with evidence, not silently -- several ferial slugs carry citations byte-identical to their Sunday's own, so the Ordo's override line cannot distinguish the two representations. data/ef/expected-divergences-lms.sexp carries the one adjudicated entry (L1), cited to RG 476(f). Mutation-tested: inverting RG 475(a)'s Sunday clause reddens 10 existing Rubrics_ef unit tests AND this new layer's own comparator, independently, on a data source none of the other five layers touch. Reverted after confirming. No lib/ changes. day/readings/rubrics output re-confirmed byte- identical (1583, 2026, 2038, 9999) after this task; all three gates green (dune test, exhaustive sweep, make check).
* fix(tools): harden check_citations.py against its own self-poisoning bugLukasz Kasprzak2026-08-191-0/+19
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Reproduced the defect: reintroducing the exact historical citation bug (pointing class-1's citation back at LT.txt:12459, the value a prior fix round corrected away from) made the tool report "147 citations checked, 0 look wrong". The mechanism was that the corrective comment documenting the old bug quotes the wrong historical value, and the checker pooled every quoted phrase from the whole surrounding comment block, so citing the wrong line matched the comment explaining why it was wrong. Four changes: 1. The word pool for a citation is now scoped to the entry(ies) it is attached to only -- never to quoted text elsewhere in the comment. This is the direct fix for the self-poisoning bug. 2. A citation whose pool has fewer than two distinctive words (Latin liturgical headings are short and stopword-heavy) cannot discriminate the right line from a wrong nearby one. Such a citation is now reported CANNOT VERIFY and fails the target, instead of silently passing. 3. The blanket +-2-line tolerance is gone. A bare "LT.txt:N" is checked at line N only; a heading that genuinely wraps must say so explicitly as "LT.txt:N-M". The allowance moves into the data, where it is visible. 4. The tool gets its own test suite, tools/test_check_citations.py, with a synthetic fixture covering: a correct citation, off-by-one and off-by-three mismatches, an explicit wrap range, a degenerate pool, a PATTERN-marked entry with no citation, and a dedicated regression test for the self-poisoning case itself. Wired into `dune test` via a new (rule (alias runtest) ...) in tools/dune (a plain (test ...) stanza cannot run a Python script), so it runs with the rest of the suite, not only as a `make` target. Added a --file/--lt-file override to check_citations.py so the tool (and its own tests) can point at a fixture without touching the real lang/la.ini or docs/research/LT.txt. Confirmed the "SKIPPED, exit 0" behaviour for a missing docs/research/LT.txt is unchanged. tools/__pycache__/ (a stray artefact of this script, previously untracked and ungitignored) is now in .gitignore. Measured against the current lang/la.ini (another task is still landing its sanctoral entries on this branch): 15 of 275 citations now look wrong and 42 more cannot be verified, both far above the 0 the unhardened tool reported. Not fixed here -- the data pass is separate, once the sanctoral entries land.
* test(differential): fix round 1 -- Holy Week, Lent Ember, RG 69Lukasz Kasprzak2026-08-151-1/+7
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Two Critical findings, both real: Holy Week (including the entire Sacred Triduum) was reading Passion Week's Masses -- the original colitur_keys widened lectio's single, conflated ini section into both weeks instead of Passion week alone. Narrowed to Passion week only; Holy Week's own four cleanest days (Monday, Tuesday, Thursday, Saturday) are hand-authored directly from the Missal, verified twice against both scans (C23); Holy Wednesday and Good Friday are genuinely multi-lesson liturgies with no single Epistle slot and are deliberately left unanswered, not guessed (C24). The Lenten Ember days were still dead keys: Temporal_ef computes ef-lent-ember-{wed,fri,sat}, lectio's ini names the same data ef-lent-1-{wednesday,friday,saturday}, and colitur_keys never translated it -- both engines independently fell through to the same wrong ferial answer, so even the differential stayed green (C22, a genuine lectio bug recorded for upstream). Fixed the instance and the class: colitur_keys gained the rename, and tools/bootstrap_lectionary.ml gained assert_reachable, sweeping Rite_ef.Temporal_ef.temporal at generation time so a translation table entry naming a slug colitur never computes dies loudly instead of shipping silently, plus an informational log of every real slug with no entry at all. C21 ("colitur's step 3 cannot reach a correct answer for the fixed Christmastide slugs") is retired, superseded rather than merely patched: the Nativity-Octave days now have their own direct Missal formulary (Tit. 3,4-7/Luc. 2,15-20, "Diebus infra octavam Nativitatis Domini"), resolved at step 2, not step 3 at all. That surfaced a real RG 69 gap underneath it: a Sunday landing 26-31 December should keep its own Office, not the fixed weekday placeholder, which Temporal_ef does not currently distinguish. C6's verdict is re-opened (open, not colitur) for the 19 of its 138 rows where this applies directly, and a new entry C25 (57 rows) records the same gap propagating one hop further through step 3 into Holy Name week. Both are out of this task's own scope -- a behaviour change to a shared, multi-round-reviewed kernel-adjacent function -- and recorded, not fixed, per the coordinator's own instruction. C6's own note is rewritten in full rather than layered with another correction; C18's stale trailing count is fixed to match its own pin; the Vigil-misapplication risk flagged as a Minor closed itself once the Nativity-Octave fix landed, confirmed by inspection rather than assumed. dune test: 358/358. COLITUR_EXHAUSTIVE_SWEEP=1 dune test --force: 359/359.
* data(ef): bootstrap the temporal lectionary from lectioLukasz Kasprzak2026-08-141-0/+8
| | | | | | | 119 entries, Epistle + Gospel only. The generator refuses a section carrying exactly one of the two: an Epistle without a Gospel is malformed and wants investigating, not shipping. SHA-256 of the source INI is in the provenance header, as with sanctoral.sexp.
* data(ef): bootstrap the 1962 sanctoral from lectioLukasz Kasprzak2026-08-121-0/+10
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.