<feed xmlns='http://www.w3.org/2005/Atom'>
<title>colitur.git/test/test_colitur.ml, branch v0.8.0</title>
<subtitle>deterministic OCaml engine to compute and validate liturgical calendars for multiple rites, template-driven output to year 9999</subtitle>
<id>https://git.labunix.xyz/colitur.git/atom?h=v0.8.0</id>
<link rel='self' href='https://git.labunix.xyz/colitur.git/atom?h=v0.8.0'/>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/'/>
<updated>2026-08-20T13:44:29Z</updated>
<entry>
<title>feat(citation): the Sigla facade, total by construction</title>
<updated>2026-08-20T13:44:29Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-20T13:44:29Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/commit/?id=987175105ead0ea78b960c0e638a81c6fd2384cf'/>
<id>urn:sha1:987175105ead0ea78b960c0e638a81c6fd2384cf</id>
<content type='text'>
format never raises and returns an unparseable citation unchanged, so a
gap degrades to today's behaviour rather than to a crash. The coverage
test asserts separately that no shipped citation takes that path.

verbatim is what --raw uses: an identity name table would still reformat
punctuation and renumber, which would break byte-exact diffing against
lectio.

The existing citation-coverage walk (1970-2070) now also drives a
round-trip check in the same pass: parse -&gt; render -&gt; parse must reach
the same structure, using Book.default_spelling for names (Book.to_string
returns the internal id, which is not a registered token and cannot be
read back). Proved with a mutation: changing the default style's
part_sep to a separator the parser does not accept reddened the
round-trip case on 31 multi-part citations and nothing else; reverted.
</content>
</entry>
<entry>
<title>feat(citation): render a parsed citation in a configurable style</title>
<updated>2026-08-20T13:09:49Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-20T13:09:49Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/commit/?id=a2cbb6b85e79fbc59b0879362c0f853757d51c07'/>
<id>urn:sha1:a2cbb6b85e79fbc59b0879362c0f853757d51c07</id>
<content type='text'>
A style is a set of format strings, so punctuation convention is data.

Values are unquoted here rather than in Overlay_ini: that parser trims
every value and is shared with overlays and [defaults], so teaching it
about quotes would change behaviour this feature has no business
changing.
</content>
</entry>
<entry>
<title>test(citation): assert every emitted citation parses</title>
<updated>2026-08-20T13:03:02Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-20T13:03:02Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/commit/?id=f8c746736fe269ef6e0c4377f58bbb6b1f2fc07a'/>
<id>urn:sha1:f8c746736fe269ef6e0c4377f58bbb6b1f2fc07a</id>
<content type='text'>
Walks 1970-2070 and parses every citation the engine emits. Nothing
validated citation spellings before this, which is why seven books
carried two spellings unnoticed.

Mutation-proved: removing the 'Isa.' spelling from the book table
reddens this test naming that citation.
</content>
</entry>
<entry>
<title>feat(citation): parse citations into structure</title>
<updated>2026-08-20T12:59:03Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-20T12:59:03Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/commit/?id=56e36188c6afc208b96b15b9a1d3d62bbbe2f705'/>
<id>urn:sha1:56e36188c6afc208b96b15b9a1d3d62bbbe2f705</id>
<content type='text'>
The parsed form is a book and a LIST of chapter-parts: the data cites
across chapters and lists disjoint verse ranges within one.

Two rules the shipped data forces and that are not obvious: a
semicolon-separated part may inherit the previous chapter rather than
restate it, and a chapter may be separated from its verses by a comma.
</content>
</entry>
<entry>
<title>feat(citation): canonical book ids and tradition mapping</title>
<updated>2026-08-20T12:40:12Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-20T12:40:12Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/commit/?id=405910d2fd245e7a11e09eecb8c6fffb68d2169c'/>
<id>urn:sha1:405910d2fd245e7a11e09eecb8c6fffb68d2169c</id>
<content type='text'>
Seven books arrive in two spellings, inherited from lectio's ini and
ultimately from Divinum Officium. Collapse them onto one id here rather
than editing generated data.

Naming and renumbering are kept apart: a tradition decides which book an
id denotes, a language file decides what it is called.
</content>
</entry>
<entry>
<title>feat(lang): Latin temporal names from the Missal</title>
<updated>2026-08-19T12:12:52Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-19T12:12:52Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/commit/?id=f00f7a66d073815249e94cec1b6ef10539d773e7'/>
<id>urn:sha1:f00f7a66d073815249e94cec1b6ef10539d773e7</id>
<content type='text'>
Every entry is transcribed from the 1962 Missal's own propers headings in
docs/research/LT.txt and cites where it came from; names constructed by
following a neighbouring pattern are marked as such, so a reader can tell
transcription from inference.

The coverage test is the point of this commit. It walks every day of
2020-2045 and fails naming any slug with no Latin name -- the test that
would have caught the original defect, where a printed booklet said
ef-septuagesima-sunday-2 because nothing asserted that names exist.

The three Triduum names reuse the exact strings temporal_ef.ml already
carries, so the engine and the language file cannot disagree.
</content>
</entry>
<entry>
<title>feat(naming): the config file</title>
<updated>2026-08-19T11:38:54Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-19T11:38:54Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/commit/?id=b26089630a61a54060c86ec26be6dc4fe5418b12'/>
<id>urn:sha1:b26089630a61a54060c86ec26be6dc4fe5418b12</id>
<content type='text'>
Owns precedence and provenance and nothing else, and never reads the
filesystem, so it is as testable as the language table.

resolve returns the value AND its source, because a setting that silently
comes from a file the user forgot about is worse than no setting at all --
config --show can then say where each effective value came from.

overlay accumulates rather than last-wins: a user has more than one.

An unknown key is reported, never fatal. A config written for a newer
colitur must still work on an older one, but silently dropping a line the
user wrote is how a typo becomes invisible.
</content>
</entry>
<entry>
<title>feat(naming): the language table</title>
<updated>2026-08-19T11:34:53Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-19T11:34:53Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/commit/?id=45bcbde502e07ad73b96039fcbb62471a3c94781'/>
<id>urn:sha1:45bcbde502e07ad73b96039fcbb62471a3c94781</id>
<content type='text'>
Maps strings to strings and nothing else -- no calendars, no dates, no
filesystem. That is what lets every command use it without the kernel
learning about presentation.

Every lookup is total, and a miss returns THE KEY rather than the empty
string. A partial translation is therefore usable from its first line,
and the fully-degraded case is exactly today's output (bare slugs) rather
than a blank page.

--raw is a real identity table, not a special case threaded through every
call site: one value the whole program passes around.

Reuses Overlay_ini's INI reader rather than growing a second one that
would drift in its comment, quoting and trimming rules; parse_sections is
exposed in the .mli for that, with no behaviour change.

Fixes one defect found while running the brief's own tests rather than
transcribing them blind: weekday's internal lookup key is an English
day-name word (month's is already the numeral string), so on a miss it
echoed that word instead of the documented numeral, breaking both the
0=Sunday convention and Lang.raw's own identity contract for weekday.
weekday/month now fall back to string_of_int n directly on a miss
instead of through get's generic echo-the-search-key path; month is
byte-identical since its key already equals string_of_int n.
</content>
</entry>
<entry>
<title>feat(templates): ordo booklet in six flavours, pinned by goldens</title>
<updated>2026-08-19T07:47:41Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-19T07:47:41Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/commit/?id=27a8fbdf738f7fdb4c9a42ec99f08855dbed440b'/>
<id>urn:sha1:27a8fbdf738f7fdb4c9a42ec99f08855dbed440b</id>
<content type='text'>
LaTeX and groff are the print paths; HTML carries a print stylesheet;
AsciiDoc, Markdown and plain text are the plain-consumer paths.

AsciiDoc and Markdown use flavour none, and say so in a comment: their
metacharacters are context-dependent and escaping them aggressively
produces worse output than not escaping. The consequence is real and
documented -- a feast name containing * renders as emphasis.

Golden tests pin all six byte-for-byte for 2027. They prove the
templates RENDER, not that they TYPESET; compiling needs TeX and groff,
which is Task 13's opt-in make check-templates.
</content>
</entry>
<entry>
<title>feat(render): iCalendar emitter, RFC 5545</title>
<updated>2026-08-19T07:09:36Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-19T07:09:36Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/commit/?id=6abc243e5d40bd60174a3fb1affc9517b0ac5617'/>
<id>urn:sha1:6abc243e5d40bd60174a3fb1affc9517b0ac5617</id>
<content type='text'>
Not a template job: folding, escaping, exclusive DTEND and stable UIDs
are rules a logic-less template cannot enforce, and each fails silently
in a subscriber's client rather than loudly at generation.

  DTEND is EXCLUSIVE for an all-day event (section 3.6.1). Wrong here
  shows every event a day short, everywhere.
  UIDs are YYYYMMDD-&lt;rite&gt;@colitur and stable across regenerations
  (section 3.8.4.7). Wrong here duplicates the whole year in every
  subscriber's phone, months later.
  Every line is CRLF-terminated and folded at 75 octets (section 3.1).
  No RRULE: a liturgical calendar is not a recurrence rule. Asserted, so
  nobody optimises it later.

DTSTAMP is a parameter, not a clock read. RFC 5545 requires it and the
obvious implementation reads the wall clock -- which violates the
kernel's determinism rule and would make two feeds from identical data
differ byte-for-byte, defeating reproducible builds and any reviewable
diff on a published tree.

Corrected one test literal against real engine output: DTSTAMP is a
per-VEVENT property (section 3.8.7.2), not calendar-level, so the
default-value line count is 365 (every event), not 1.

Mutation-tested: a non-exclusive DTEND reddens the suite.
</content>
</entry>
</feed>
