aboutsummaryrefslogtreecommitdiff
path: root/man
Commit message (Collapse)AuthorAgeFilesLines
* feat(examples): ship two real local calendars, with their limits statedLukasz Kasprzak2026-08-181-0/+30
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Poland and the Benedictines, in the flat INI form, installed beside the invented diocesan example. poland.ini -- 17 entries from the Calendarium Perpetuum pro Dioecesium Poloniae (1964), promulgated under Rubricarum instructum and applying to the 1962 Missal. Transcribed from a published Polish EF ordo that names that same calendar, cross-checked against missalemeum's supplement page for the two formularies 1964 added (13 and 15 July). Date, Latin name and class were read from the source for every entry; three classes could not be recovered from the PDF's column layout and are marked INFERRED where they appear, with the reasoning. The header carries an edition warning that is a real trap here: the Proprium Poloniae of 1921 and 1934 is still bound into many missals and carries an outdated arrangement of dioceses and ranks. It is not this calendar, and it is exactly the kind of plausible wrong-edition source that has cost this project time before. benedictine.ini -- two entries, and the comment explaining why is the point of shipping it. Counted across the Norcia ordo: 86 "I cl.", 43 "II cl.", ZERO "III cl." and ZERO "IV cl.", against 103 "Semidup." and ~96 "Dup.". The monastic rite uses the Roman classes at the top and the older Duplex / Semiduplex grades below, exactly where the Roman calendar has III and IV class. colitur's rank vocabulary cannot express those, so most Benedictine propers cannot be written here at all -- a mismatch of vocabularies between related rites, not a gap in the data. Rather than invent a Duplex -> Class3 mapping the source never states, that file ships only what its ordo gives in Roman terms and lists roughly two dozen excluded feasts BY NAME, so the omission is visible instead of silent. Both headers say plainly that they are examples and not authorities: they are transcriptions from published ordines, none of the five test layers can vouch for either, and both should be checked against the reader's own ordo. The cram test asserts only what we control -- that they parse, convert and apply -- and says so. They also demonstrate the precedence engine on real data: the Benedictine Transitus is I class and takes 21 March with the Lenten feria commemorated, and Maurus is II class and takes 15 January with Paul the First Hermit commemorated.
* docs(man): explain how an overlay is actually appliedLukasz Kasprzak2026-08-181-0/+65
| | | | | | | | | | | | | | | | | | | | | | | | | The format was documented; the pipeline was not. The commonest surprise when writing a local calendar is 'my feast does not appear', and it is almost never a loading failure -- which means the existing docs answered a question nobody was asking. Four stages, named: load, merge, resolve, emit. The one that matters is resolve, and the point it makes explicit is that overlay entries take part in precedence on EQUAL TERMS. A local feast is not privileged for being local; it competes under RG 91's table exactly as a universal one does. Three concrete causes of a missing feast, each of which came up while testing this branch: outranked by the day it lands on, a date structurally occupied (an easter+60 feast can never appear, because Corpus Christi is Easter+60 and is I class), or a commemoration past RG 111's limit for the day's class. And the diagnostic rule -- if check says the file loaded and every directive found its target, the answer is stage 3, so raise the class or move the date. Also records why the shipped adjustments are applied first and why that is not configurable: replacing rather than layering would silently drop the inseparable Peter/Paul commemoration, the Major Litanies, St Barbara and Rogation Wednesday. Overriding one deliberately by naming its slug is a different thing from losing it by accident.
* feat(overlay): a flat INI front end, which verifies its own outputLukasz Kasprzak2026-08-182-0/+101
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | A convenience format for calendars that add a few local feasts and drop one or two universal entries. Section names are slugs, a [overlay] section carries the id, and status/subject/layer default so the common case -- an ordinary local saint's feast -- says only what distinguishes it. It is a FRONT DOOR, not a second data model. It parses to exactly the Overlay.t the S-expression form parses to, and everything downstream is the same code on the same values; a test asserts an INI overlay and its hand-written sexp equivalent produce identical Overlay.t values. It is also deliberately less expressive -- Add, Suppress and single-field Edit only -- and refuses Replace, multi-field edits and citation edits BY NAME rather than dropping them silently. Anything it cannot say is a reason to write sexp. Little of this is new machinery: tools/bootstrap_sanctoral.ml has parsed INI and mapped it to celebrations since the sanctoral was bootstrapped from lectio. The dates needed extending, since that mapping handled only MM-DD; the flat forms are easter+N/easter-N and mon/day/nth, with nth negative to count from the end. `colitur convert` is a separate step rather than --overlay sniffing the extension, so the author can read what their INI became. When a date form was mistyped, "what did the engine actually get" is the question, and an invisible transpile cannot answer it. The conversion verifies its own output: the emitted text is parsed back with the same function that loads an overlay and must equal what the INI denoted, or nothing is written. That is the point of the module. A transpiler emitting valid-but-wrong sexp is the failure a convenience format invites, and `colitur check` could never catch it -- the output would parse cleanly and mean something else. That check was WRONG on the first attempt, in exactly the way it exists to prevent. It re-serialised the parsed value instead of parsing the text being returned, so it verified t -> sexp -> t, which is true by construction and proves nothing. Found by mutation: corrupting the renderer to emit a different overlay id sailed through and exited 0. It now parses the returned text, the mutation is caught with exit 2, and two tests fail under it where none did before.
* docs(man): colitur-overlay(5), the overlay format in fullLukasz Kasprzak2026-08-182-1/+264
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The overlay format was documented in three partial places -- a paragraph in colitur(1), a block in --help, and the comments inside the shipped example -- none of which was a reference. Someone writing a diocesan calendar had to read all three and infer the rest. Section 5 because an overlay is a thing a user AUTHORS rather than a command they run: it belongs beside fstab(5), not in man1. Covers every directive and every field edit, the six required fields and the two optional ones, all three date specifications including the signed Easter_offset and the negative nth, three worked examples, and the caveats. The subject field gets a note explaining that it is not decoration -- it decides whether a feast displaces an occurring Sunday under RG 16(a). Two things it says that the code says and the old prose did not. There is no Set_status and no Set_date among the field edits, deliberately: changing an entry's status or its date makes it a different celebration rather than an edited one, so Replace is the right directive and the change stays visible in `colitur check` output. And a local feast missing from output has usually LOST its day under the general rubrics rather than failed to load -- the engine applies precedence to overlay entries exactly as to universal ones, which is the first thing an author hits and was written down nowhere. Writing it caught a documentation bug before it shipped: a first draft listed a Set_status edit that does not exist and omitted Remove_name that does. Every documented edit is now cross-checked against overlay.mli. Linked from colitur(1)'s SEE ALSO and its OVERLAYS section, and from --help. The Makefile installs it into man5, removes it on uninstall, and the man and doc targets lint both pages.
* docs: point --help and the man page at the new overlay workflowLukasz Kasprzak2026-08-181-3/+42
| | | | | | | | | | | | | Both listed check and new-overlay among the commands but neither told a reader how they fit together, which is the part that makes them useful. The overlay sections now carry the four-step loop -- new-overlay, edit, check, run -- and state what check does not do, since its name invites a stronger reading than it earns. Also documents what the format now permits: citations and layer optional, the signed Easter_offset, the negative nth counting from the end of the month, and the legal values of each of the six required fields, which previously appeared only in the shipped example.
* feat(cli): colitur check and colitur new-overlayLukasz Kasprzak2026-08-181-0/+21
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Writing a local calendar had no feedback loop. An overlay is applied, not validated -- that stays true, and the five test layers still cannot vouch for a user's file -- but before this the only way to learn whether yours did what you meant was to generate a year of output and grep for your own slug, and the only way to see that a directive matched nothing was to notice a warning scroll past among 365 lines. `check` loads each overlay, applies it to the real shipped calendar, and reports the directive counts, the slug each one targets, and any directive that found no target. It exits 2 when a file fails to load or a directive matched nothing, so it composes into a Makefile or a pre-commit hook rather than merely being readable. It is applied to the SHIPPED calendar and not to an empty layer on purpose: against an empty one every Suppress would fail trivially and the check would be worthless. It answers three narrow questions -- does the file parse, does every directive find its target, what does the merged result contain. It does not validate a calendar against the rubrics and cannot, and both the help text and the man page say so rather than letting the name imply more than it does. `new-overlay` prints a starter to stdout for redirection, rather than writing a file where it likes. Every value in it is a placeholder that will appear in `day` output if left unedited, so a half-finished overlay is visible rather than silently inert, and it documents the three date shapes and the legal values for each closed field inline. load_ef_layer now returns its diagnostics instead of printing them: day and readings still want them on stderr beside a year of output, while check wants them on stdout, attributed to the overlay that produced them, and counted. Printing at the source made the second impossible. The cram test round-trips new-overlay through check rather than pinning the template line by line -- editing its prose should not fail a test, but a syntax error in it still must.
* feat(data): ship a worked example overlay calendarLukasz Kasprzak2026-08-181-2/+13
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | `--overlay` shipped last week with nothing to point at. The only example was a test fixture, explicitly labelled invented and living where no user would find it, so "colitur supports local calendars" was true but undemonstrated. data/ef/examples/diocesan-example.sexp is installed to <prefix>/share/colitur/examples/ and is runnable documentation: every celebration in it is invented, with example- slugs that cannot collide, and its header explains the four directives, the three date shapes, and the field vocabularies before showing any of them. It demonstrates the range deliberately rather than the minimum: a fixed-date local patron with its own propers; a movable dedication on the first Sunday of October, I class because a church's own dedication anniversary is I class in that church and at III it would lose to the Sunday every year; an Easter-relative commemoration; an Edit raising a universal feast for local use; and a Suppress shown commented out, because suppressing a universal feast is a real liturgical act and an example should not invite it casually. Two things the header says plainly, since this is the one file that lets someone change what colitur computes: an overlay is applied ON TOP of the shipped calendar and never instead of it, and an overlay is applied, NOT validated -- the five test layers assert things about the shipped data and cannot vouch for a user's file. A test asserts it loads against the real EF rank vocabulary, still demonstrates at least four directives, and applies with NO diagnostics. The last matters most: a directive naming a slug the universal calendar lacks would warn, and an example that warns teaches the wrong lesson. It deliberately does not pin which days it produces -- those celebrations are invented, and pinning their dates would make an illustrative file behave like calendar data.
* feat(cli): --overlay FILE, so a local calendar can be suppliedLukasz Kasprzak2026-08-171-0/+76
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The Overlay algebra has been built and tested since Plan 2, and Date_spec grew movable variants last commit, but there was still no way to hand colitur a file: bin/main.ml applied exactly one overlay, the shipped data/ef/adjustments.sexp, with the path hardcoded. This is the plumbing. --overlay is repeatable and ordered, and applies ON TOP of the shipped adjustments rather than instead of them. That ordering is the whole point: adjustments.sexp carries RG 110's own 30 June companion, the Major Litanies, St Barbara and Rogation Wednesday, and a user file that replaced it would silently drop all four while appearing merely to add a local feast. Overlay.merge is last-writer-wins, so a local calendar can still override a universal entry deliberately, by naming its slug. The dispatch needed real argument parsing. It matched Sys.argv as an exact array, which does not survive a repeatable flag -- two --overlay arguments are a different array shape from one, and each further flag would multiply the patterns again. Flags are now stripped first and the remaining words matched as command plus year. Hand-rolled, because the dependency list is frozen and this is fifteen lines. One defect found by the cram suite on the first run: the unknown-option guard rejected --help and --version, having excluded only their short forms. Refused, not ignored, on easter and temporal. Neither reads sanctoral data -- temporal deliberately runs the cycle before any layer exists -- so accepting the flag there and silently doing nothing is the failure mode this project refuses everywhere else. A file that fails to load is fatal, exactly as the shipped overlay is; a directive naming a slug that does not exist warns on stderr and the run continues. The second matters more for a user file than for ours: a typo in a diocesan calendar should say so rather than quietly do nothing. test/fixtures/overlay-example-diocesan.sexp is a worked example and is labelled as invented, not calendar data. It carries both shapes a real local calendar needs: a fixed-date patron, and a dedication on "the first Sunday of October" -- which is the case the Date_spec work existed to unlock and which lands on 4 October in 2026, 3 October in 2027, 1 October in 2028. The dedication is I class because a church's own dedication anniversary is I class in that church; at III class it lost to the II-class Sunday every year, which a first draft demonstrated correctly and uselessly. The man page gains an OVERLAYS section saying plainly that an overlay is applied, NOT validated: the five test layers assert things about the shipped calendar and none of them can vouch for a file the user supplies. This is the first feature that lets someone change what colitur computes, and that distinction should not have to be inferred.
* feat(cli): a Makefile, a man page, and --helpLukasz Kasprzak2026-08-171-0/+219
Three things the project had no answer for: how to install it without knowing dune, where to read about it, and what it does when asked. Makefile, same shape as lectio's -- PREFIX ?= $(HOME)/.local, BINDIR, MANDIR, and the '## '-comment help target -- so the two siblings are driven the same way. Every recipe wraps dune in `opam exec --`, which is the actual point of having one here: `make build` works from a plain shell with no `eval $(opam env)` first. install goes through `dune install` rather than a hand-rolled copy, because the binary finds its calendar data relative to its own path; the man page is installed separately to share/man/man1, matching lectio. install and uninstall were both run against a scratch prefix and checked: uninstall leaves zero files behind. PREFIX defaults to ~/.local because that is where lectio installs and where it actually lives on this machine, so colitur lands on an existing PATH with no shell change. An earlier install this session went to ~/opt/colitur, which was me over-applying a rule meant for third-party tools to one of the author's own projects; it has been removed rather than left as a second, staler binary competing on PATH. man/colitur.1 documents the four commands, both output formats and why they differ, COLITUR_DATA_DIR and its refusal to fall back, the data resolution order, exit statuses, and -- deliberately -- the limitations: EF only, Epistle and Gospel only with the chants unbuilt and rejected rather than guessed, and the BVM Saturday Mass-selection gap. A man page that only lists what works is half a man page. Renders clean under `groff -ww -z`, no warnings. --help prints to stdout and exits 0; a usage error prints one line to stderr and exits 2. That is the Unix convention rather than a preference: asking for help succeeded and should be pipeable, being invoked wrongly did not and must not pollute stdout. Both directions are asserted in cli.t, along with a loop confirming every command the help text advertises is one the dispatch actually accepts -- the check that catches help drifting away from the code.