<feed xmlns='http://www.w3.org/2005/Atom'>
<title>colitur.git/schema, branch main</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=main</id>
<link rel='self' href='https://git.labunix.xyz/colitur.git/atom?h=main'/>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/'/>
<updated>2026-08-19T13:29:33Z</updated>
<entry>
<title>feat(render): names reach the view and every emitter</title>
<updated>2026-08-19T13:29:33Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-19T13:29:33Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/commit/?id=6d367ab8e90f6e713d262a7f19fb908b28d4796a'/>
<id>urn:sha1:6d367ab8e90f6e713d262a7f19fb908b28d4796a</id>
<content type='text'>
The view's name is now the RESOLVED display string and slug is untouched,
so machine formats carry both -- a script keeps the stable key, a human
reads the name.

name is a plain string, not a lang-keyed object. That removes the
shadowing hazard outright: a dotted {{name.la}} used to fall back
WHOLESALE to the enclosing month's name.la and print Ianuarius on every
unnamed day, which is how a printed booklet came to show the month where
the feast belonged.

weekday, season, rank and colour all gain localised companions, because a
calendar in a language needs more than feast names, and templates gain a
term vocabulary so fixed strings need no template edit to translate.

Asserted over a whole year: no day renders its slug as its name.

Beyond the brief's own code sample:

- comm_value's own name is now resolved through the same lang.celebration
  table too (not only the observed day's), because Task 8's own ordo
  template interpolates a plain {{name}} inside {{#comms}} -- an Obj there
  would render silently blank. A commemoration slug without Latin
  coverage still degrades to the slug, same as everywhere else in this
  system; that is a lang/la.ini DATA gap (113 of 327 sanctoral slugs,
  measured), not a regression this task introduced.

- bin/main.ml's emit/table/publish call sites needed ~lang to compile at
  all, which is collateral from the of_days signature change, not this
  task's own file list. Rather than pass Lang.raw and ship the very
  slug-as-name defect this branch exists to fix, they load the shipped
  Latin table by the same probe order data_dir() already uses -- a
  deliberate, commented BRIDGE that Task 6 replaces wholesale with real
  --lang/--raw/config resolution. bin/dune gained colitur_naming
  accordingly.

- test/cli.t needed two related fixes to stay green: the CSV header/row
  example, and a table/LaTeX escaping demonstration that relied on the
  kernel's own English name for Sts Peter &amp; Paul -- gone from the view
  now that name resolves through lang tables only, and the Missal's own
  Latin spells the feast with et, never an ampersand. Escaping itself is
  still proved live on 2035 data in test_emit.ml.

- Both schemas gained the new day/week/top-level keys (season_name,
  weekday, rank_name, colour_name, term, weekday_headings, month_num,
  month_name), not only the name shape change; schema/colitur-v1.xsd
  verified against real emitted XML via xmllint (make check-schema).

Render/golden's 9 cases (the shipped ordo/grid templates, all six
flavours) now fail as expected: their old {{name.la}} / {{#name}}...
idiom finds nothing on a plain string. That is Tasks 8/9's own scope to
rewrite, per the plan's own pre-flight conflict scan -- not fixed here,
and not silently pinned by regenerating goldens off broken output.

495 tests run (490 + 5 new), 486 pass; the 9 failures are exactly
Render/golden's ordo/grid cases.
</content>
</entry>
<entry>
<title>docs(render): template reference, install rules, typesetting check</title>
<updated>2026-08-19T08:32:44Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-19T08:32:33Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/commit/?id=b90678e560808dd788fa7d7eb319d93a83005db4'/>
<id>urn:sha1:b90678e560808dd788fa7d7eb319d93a83005db4</id>
<content type='text'>
colitur-templates.5 documents the four syntax forms, the six flavours
and their escaping, and the full view-model field reference. It states
plainly that there are no partials, no raw form and no expression
evaluation -- a template is data, never a program. It documents two
real hazards found during this build, not theoretical ones: the
outward scope fallback silently shadowing an inner name/num key with
an outer one of the same name (with the safe {{#name}}...{{^la}}
idiom), and the engine's lack of host-comment awareness (a {{...}}
inside a LaTeX %, groff .\" or HTML &lt;!-- --&gt; comment is still parsed
as a tag).

It also states the limitation rather than hiding it: AsciiDoc and
Markdown are not escaped, so a feast name containing * or _ renders as
emphasis.

templates/ and schema/ now install into &lt;prefix&gt;/share/colitur/,
matching data/ef/, via new install stanzas; colitur-templates.5
installs to man5 beside colitur-overlay.5. Verified against a scratch
prefix: the installed binary resolves both from the prefix, not the
source tree, when run from an unrelated working directory.

make check-templates typesets every shipped template through pdflatex
and groff when they are installed, and prints SKIPPED loudly when they
are not. Golden tests prove templates render; only this proves they
typeset. A silent skip would read as a pass.

Fixed a real doc/help drift while here: bin/main.ml's --help still said
--overlay was accepted on day and readings only, three commands out of
date (emit, table/render and publish all accept it too), disagreeing
with the man page's own OVERLAYS section, which carried the identical
stale line. Both are corrected; --overlay's own behaviour is unchanged.
</content>
</entry>
<entry>
<title>feat(render): XML emitter and schema</title>
<updated>2026-08-19T06:57:42Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-19T06:57:42Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/commit/?id=544836cf0f6373a0a753d0953ce7e60fd96337af'/>
<id>urn:sha1:544836cf0f6373a0a753d0953ce7e60fd96337af</id>
<content type='text'>
Element-per-field; attributes carry identity only and there is no mixed
content, so a consumer's XPath never has to distinguish the two.

Schema validation is an opt-in make check-schema via xmllint, not an
in-suite assertion: validating XSD needs an XML library and the
dependency list is frozen. It prints SKIPPED loudly when xmllint is
absent, because a silent skip reads as a pass. The suite asserts
well-formedness properties directly instead.

This corrects the design spec, which claimed in-test validation.
</content>
</entry>
<entry>
<title>feat(render): CSV and JSON emitters, and the published contract</title>
<updated>2026-08-19T06:41:23Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-19T06:41:23Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/commit/?id=063d4058346f7dc03eb0aa5ba394b9a872d3f1e3'/>
<id>urn:sha1:063d4058346f7dc03eb0aa5ba394b9a872d3f1e3</id>
<content type='text'>
Both consume the VIEW, not the kernel, so every emitter and every
template describe exactly the same fields -- there is one vocabulary,
not five.

CSV is RFC 4180: a field with a comma is quoted. That is live on real
data, not hypothetical -- 'St. Joseph, Spouse of the Bl. Virgin Mary'
would otherwise split into two columns.

JSON is hand-rolled because the dependency list is frozen and escaping
is the only subtlety. Control characters below 0x20 are \u-escaped per
RFC 8259 section 7.

There are no numbers in the view, deliberately: a consumer never has to
guess whether week is 2 or "2".

schema/day-v1.json pins the shape. Once a phone subscribes or a site
fetches this, it is a promise to strangers -- adding a field is minor,
renaming one means /v2/.
</content>
</entry>
</feed>
