Skip to content

Audit the Slavic readings and commemorations against oca.org - #225

Merged
brianglass merged 29 commits into
mainfrom
oca-slavic-audit
Sep 8, 2026
Merged

brianglass merged 29 commits into
mainfrom
oca-slavic-audit

Conversation

@brianglass

@brianglass brianglass commented Aug 28, 2026

Copy link
Copy Markdown
Owner

First measurement of the Slavic data against oca.org, the source it was
compiled from — plus the fixes that measurement turned up. The Greek tradition
was measured against goarch.org and taken from 96.1% to 99.1%; Slavic had never
been measured that way.

Findings live in docs/oca-audit.md; tools/oca/README.md maps each script to
what it produced.

Measurement

97.0%–98.2% a year across five years (2023–2027), on Liturgy Epistle and
Gospel.

year dates match differ
2023 338 330 8
2024 338 328 10
2025 337 331 6
2026 339 329 10
2027 338 328 10

One year of data was actively misleading. Cross-tabulating five years
corrected two conclusions drawn from 2026 alone: Oct 31 is not a one-off but
the only date differing in all five years, and Jan 3 is not a fixed-date
problem but the Saturday before Theophany moving across Jan 2–4.

Fixes

St John Kochurov, Oct 31. oca.org gives him a proper Gospel and no proper
Epistle — the Epistle differs every year (Col 2:20, Phil 1:20, Col 2:1,
2 Cor 5:1, 2 Cor 11:31), which is what proves it is the ordinary daily reading;
the Gospel is John 10:9-16 in all five. The app had Heb 13:7-16 and
Luke 12:32-40. Gospel repointed, Epistle row removed. Oct 31 now differs by
one citation a year instead of three.

Nine commemorations tagged greek that oca.org also keeps. A greek tag
asserts the Slavic tradition does not keep it, so Slavic users saw none of
these. Three are Kiev Caves saints, which makes the tag self-evidently wrong:

date ours oca.org
01-02 Sylvester of the Kiev Caves Venerable Sylvester of the Kiev Near Caves
01-04 Eustathios Archbishop of Serbia Repose of St Eustathius I, Archbishop of Serbia
01-08 Gregory of the Kiev Caves Venerable Gregory, Wonderworker of the Kiev Near Caves
04-08 Rufus the Obedient, Kiev Caves Venerable Rufus the Obedient of the Kiev Far Caves
05-22 John-Vladimir, Ruler of Serbia St John-Vladimir, Prince of Bulgaria
07-27 Nicholas of Novgorod, fool-for-Christ Blessed Nicholas Kochanov at Novgorod
08-28 Synaxis of the Kiev Cave Fathers Synaxis of the Saints of the Kiev Caves
11-27 James the Wonderworker of Rostov St James the Wonderworker, Bishop of Rostov
12-13 Gabriel, Archbishop of Serbia Hieromartyr Gabriel of Serbia

Each checked against the full oca.org listing for its date, not the score.

A further 91 were then read through individually — 100 in total, taking
greek from 730 commemorations to 630. Several were plainly never Greek-only:
Nilus of Sora, Theodosius of Totma in Vologda, Moses the Hungarian of the Kiev
Near Caves, the 26 Martyrs in the Crimea.

Two were rejected as different people sharing a name and a date, which is
the failure mode the exercise had to guard against: Aug 30 pairs our "Holy New
Martyrs of Serbia" with oca.org's "Saint Spyridon, Patriarch of Serbia" (that
day lists the Serbian Hierarchs and three patriarchs, and no new martyrs), and
Sept 24 pairs our "Stephen the Martyr" with "Holy King Stephen of Serbia" — a
king, not a martyr.

Seven looked thin because our title is generic ("Stephen the Monk", "Julian the
Martyr", "Serapion the Martyr"). Each was checked against the whole day's
listing: in every case exactly one entry out of seven to eleven carries that
name. A rare name kept on the same date is the same saint.

What remains greek is now dominated by Ottoman-era New Martyrs and Athonite
figures, which is what the tag should mean. Nothing below full token overlap
has been reviewed.

Composites 18 and 24. 18 was 3 Kgs 7:51-8:1, 8:4-7, 9-11, carrying a
verse oca.org's text does not have and skipping one it does; now
3 Kgs 8:1, 3-7, 9-11, which is functional since 18 is a fall-through.
24 was Lev 26 — all 46 verses against a 21-verse reading — now Ephrem's own
spec from anastasis.

Also in this PR

Abbreviated readings now pick by rank, not row order. These are what the
Alexa skill speaks, and they were reading the ordinary weekday lesson on feast
days — Peter and Paul, the Beheading, the Three Hierarchs. Measured against
antiochian.org and goarch.org, which each publish exactly one pair a day: the
proper wins from about feast level 3 up, the daily wins below it, and on a
Sunday the resurrectional reading wins 7–1. That Sunday split is why the
rule is code rather than data — whether a fixed date falls on a Sunday changes
yearly, while ordering is static. Floating commemorations (memorial
Saturdays, the Sundays of the Fathers and Forefathers) rank ahead of everything,
since they are the day rather than a saint landing on it. 244/339 → 294/339
(72.0% → 86.7%)
against oca.org.

Great-feast readings sort ahead of the daily cycle. Sixteen rows moved from
ordering tier 2 to tier 0, so the six level-6 feasts behave like levels 7–8
already did.

Three service notes restored — Beginning of the Pentecostarion, of the
Dormition Fast, and of the Nativity Fast — absent here but present in
orthodox_calendar. The app already generates "Beginning of Apostles' Fast", so
it plainly meant to announce these.

Three Lenten fasting dates corrected — Feb 24, Mar 9 and Apr 25 are on OCA's
published named wine-and-oil list and were granting nothing.

Deliberately not done

A Wednesday/Friday fasting rank rule. Written, confirmed seven times over
against holytrinityorthodox.com plus Brian's St Tikhon's calendar — and then
backed out before shipping. Its threshold rested on nothing (the Typikon
text OCA quotes names doxology and vigil rank, levels 3 and 5; level 4 was
chosen because that is where our data disagreed with itself), and more
fundamentally it was another patch on a pile. Combining the Paschal and festal
cycles uses max() over an index that encodes precedence claims and sentinels
alongside dietary rungs, so it resolves to the more lenient side on some
collisions and the stricter on others, with every wrong case corrected by hand.
docs/fasting-refactor-scope.md records the diagnosis, the unused
DietaryAllowance ladder that already models this properly, and the
characterisation test to write first.

The reading-selection rule. Everything still differing is one question:
does the ordinary daily reading coexist with a proper one? It runs both ways —
the app drops it where oca.org keeps it (Jan 1, Jan 5, Dec 24) and keeps it
where oca.org replaces it (Oct 31, the moving Saturday before Theophany).
Implementing it would close 4–5 of the 6–10 differing dates a year.

Paused because the harness cannot prove such a change safe. aget_readings
is on the base Day class shared by SlavicDay and GreekDay, so a change
made for Slavic accuracy is measured for Greek against a different source and
could cost the 99.1% silently. And both audits check Liturgy Epistle and Gospel
only — 894 of 1,446 readings a year, leaving 38% unmeasured on precisely
the feast days the rule governs. Jan 5's Liturgy could be brought into
agreement while its thirteen Vespers lessons and four sets of Hours were
wrecked, and every harness would stay green. The prerequisite is extending the
audit to /readings/daily/, which lists every service.

Converting composites to references. Investigated and rejected. Only three
of eight derivations reach the 59–78% band that correct selections occupy, and
Composites 4 and 5 get worse as candidate verses are added — they are free
adaptations, not selections. The mechanism stays: Ephrem's text, and the
zero-width-space fall-through for 17 and 18.

Commemorations below full token overlap. The 99 full-overlap candidates
have now been read through; weaker matches have not. Name overlap is not
evidence, and Catherine of Alexandria is genuinely Nov 24 Slavic / Nov 25
Greek — a distinction that must not be collapsed. Note also that this compares
same-date only, so it cannot find a saint both traditions keep on different
days.

Notes for whoever touches this next

  • oca.org 403s a user agent containing "harvester", and python-urllib/3.14.
    That is a keyword filter, not policy: robots.txt allows these paths and asks
    for Crawl-delay: 10, which tools/oca/fetch.py honours. Identify yourself;
    do not impersonate a browser.
  • The monthly tables are not positionally stable. Feb–Apr carry an extra
    column, and a Lenten day's OT lessons sit in the cells a Liturgy day uses for
    Epistle and Gospel. Parsing by column scored the app at 73.7%.
  • loaddata is additive — it will not remove a deleted row from an existing
    dev database. The audit kept reporting the removed Kochurov Epistle until it
    was deleted explicitly. Production rebuilds from the fixture at image build.
  • Regenerate calendarium/tests/data/january.json with indent=4,
    ensure_ascii=False, no trailing newline
    , or the diff is 2,300 lines of
    reformatting instead of three.

Verified on the final state: 189 tests pass; Greek against goarch.org holds
at 333/336 (99.1%); the all-services audit 352/365; abbreviated 294/339;
Liturgy-only 329/339.

brianglass and others added 29 commits August 28, 2026 13:41
The Slavic data predates the Greek tradition by years and had never been
measured against its own source, the way docs/greek-lectionary.md measured
Greek against goarch.org. This is that measurement: 329/339 dates match for
2026, with ten differing in three coherent groups.

The largest group is one unmodelled rule rather than four separate mistakes.
The app and oca.org disagree about whether the ordinary daily-cycle reading is
also read when a feast claims the day, and they disagree in both directions:
on Jan 1, Jan 5 and Dec 24 oca.org reads it alongside the festal reading and
the app omits it; on Jan 3, a Saturday before a great feast, oca.org drops it
and the app adds it. Not fixed here -- the rule is inferred from four dates in
one year, and this project confirms across several years before coding.

Two traps in oca.org's monthly tables are recorded because both produced
confidently wrong numbers. Their columns are not positionally stable: Feb-Apr
carry an extra column, and a Lenten day's Old Testament lessons sit in the
cells a Liturgy day uses for Epistle and Gospel, so reading slots off column
numbers scored the app at 73.7%. And neither side is self-consistent about
slots -- oca.org labels Holy Week's Bridegroom gospels "(Matins)" while the app
files them under "Gospel" -- so the audit matches on citations and reports
missing and extra separately rather than pairing by slot.

Also notes that oca.org 403s a user agent containing the word "harvester".
That is a keyword filter, not the access policy: robots.txt allows these paths
and asks for Crawl-delay: 10, which tools/oca/fetch.py honours.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nQRjJpPqW89xqzAf1vsEX
The audit described oca.org's "(Matins)" and the app's "Gospel" as
inconsistent classifications of the same reading. They are not. A reading has a
kind and a service, and those are independent: Holy Week's Bridegroom readings
are Gospels and are read at Matins. Each source names a different axis and
neither is wrong.

`Reading.source` is compositional over both, with either omissible -- "Gospel"
is a Liturgy Gospel by default, "Vespers" names only the service, "Matins
Gospel" names both. Six Holy Week rows carry the service in `desc` instead
(Bridegroom, At the Washing of the Feet), which loses a reader nothing but does
defeat a matcher keying on `source`.

The foot-washing gospels were called a classification issue; they are correct
as stored, and were only surfacing because the audit's "extra" direction keys
on source.

No behaviour change -- the audit already matched on citations rather than
slots, and 2026 still scores 329/339.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nQRjJpPqW89xqzAf1vsEX
Verified against the fixture: 320 of 359 Vespers rows (89%) are Old Testament
lessons. The exceptions are principled rather than noise -- 38 are Catholic
Epistles on apostolic feasts, where the three Old Testament lessons are
replaced by three from James, 1 Peter, 1 John or Jude for the apostle being
kept, and 1 is a genuine Vespers Gospel with its own source value.

That makes `Vespers` effectively self-describing: an Old Testament lesson
unless the day is an apostle's feast. Useful in both directions -- an
unexplained New Testament reading under `Vespers` on a non-apostolic day would
be worth a second look.

Adds refs.classify(), which falls back to the book name when a citation has no
chapter:verse. 38 Vespers rows are composite lessons displayed as
"Composite 2 - Proverbs 10, 3, 8" -- canon() cannot tokenise those, so they
were unclassified. They can now be classified even though they can never be
matched by citation; all 38 are Old Testament.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nQRjJpPqW89xqzAf1vsEX
The marker is invisible and undocumented, and its author no longer remembered
why it was there. Git history has it: e5fb471 (Nov 2023) hardcoded
`not in ('17', '18')` here with the comment "for some reason we are missing
composites 17 and 18"; d4d5d54, ten minutes later, moved that special case out
of the code and into the data as a U+200B. "Tomorrow" in that commit message
was Nov 21 -- the Entrance of the Theotokos, the feast these two serve.

The reason they are missing is now established. They are the Slavic propers
for that feast, Exodus 40 and 3 Kingdoms 7-8. Our composite text is
Archimandrite Ephrem Lash's Prophetologion (~/src/anastasis), and his book
assigns the Greek Marian set to the Entrance instead -- Genesis 28, Ezekiel 43,
Proverbs 9. Those two readings are simply not in it. Only the Ezekiel overlaps,
which is why it alone is already a plain reference.

Also corrects this function's own comment, which described the Composite
content column as KJV-sourced. It is not; the register is plainly Ephrem's.

Records that the marker is a stopgap rather than a design. Approximating a
composite with a reference works here only because these two have exact verse
ranges. Composite 24 is Leviticus 26:3-12, 14-17, 19-20, 22, 33, 23-25 --
verse 33 before 23-25 -- which no reference can express, and is why the table
exists at all.

Comment only; 189 tests pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nQRjJpPqW89xqzAf1vsEX
The previous comment claimed Composite 24's "Leviticus 26:3-12, 14-17, 19-20,
22, 33, 23-25" could not be expressed as a reference because verse 33 precedes
23-25. That is wrong. bible.models.lookup_reference splits on the separators
and ORs the ranges, and the segment order survives into the result: it renders
that reference with 33 ahead of 23-25 as written, and Composite 20's
"Isaiah 55:1; 12:3-4; 55:2-13" with the chapter jumping backwards and forwards.
Production already depends on this for the Holy Thursday composite gospel,
which is a five-segment cross-book reference.

Worth knowing that the ordering follows the OR terms in the generated query
rather than an explicit order_by; there is no Meta.ordering on Verse.

The real reason a composite is not replaceable by a reference is the text, not
the verse selection. The stored text is Archimandrite Ephrem Lash's from the
LXX while the Verse table's Old Testament is KJV and Masoretic -- "If you walk
in my ordinances" against "If ye walk in my statutes" -- and it carries the
liturgical incipit, which is not scripture: Composite 24 opens "The Lord spoke
to the children of Israel saying", which is nowhere in Leviticus 26:3.

Comment only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nQRjJpPqW89xqzAf1vsEX
Its sdisplay was "Lev 26", which resolves to all 46 verses of the chapter where
the reading is a 21-verse selection. Anastasis spells it out in full --
Leviticus 26:3-12, 14-17, 19-20, 22, 33, 23-25 -- so sdisplay now carries that.
Inert today, since the pericope routes through the Composite table, but it was
wrong and it is the kind of wrong that only shows up later.

Eight other composites remain chapter-only for a reason that is worth
recording: Ephrem did not enumerate them. His specifications read "Proverbs
10:31-32 & Selection", "40:1 and selection", "3 & 4 and selection". There is no
verse list to recover, so the stored text is itself the specification.
Composite 24 was the only one written out in full.

Measured LXX2012-WEB against the composite text while checking whether
references could replace composites: mean word-level similarity to Ephrem is
58%, against 46% for KJV, and LXX2012 is closer on 12 of 13 composites that
have both a precise spec and stored text. Both descend from the Septuagint, so
that is the expected direction, and it makes LXX2012 the right base if any
conversion is ever attempted.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nQRjJpPqW89xqzAf1vsEX
Composite 17 and 18 have no Ephrem text -- they are the Slavic propers for the
Entrance of the Theotokos and are absent from his Prophetologion -- so they are
approximated by a scripture reference. Reading oca.org's text for them
alongside ours shows 17 is right verse for verse and 18 is not.

18 was "3 Kgs 7:51-8:1, 8:4-7, 9-11", which both includes a verse oca.org does
not have and omits one it does:

  7:51 "So the work of the house of the Lord ... was finished; and Solomon
       brought in the holy things of David his father" -- absent from oca.org's
       text, which opens at 8:1.
  8:3  "And the priests took up the ark" -- present in oca.org's first part,
       and skipped by the old range, which jumped 8:1 -> 8:4.

Corrected to "3 Kgs 8:1, 3-7, 9-11". Word-level similarity to oca.org's text
goes from 41% to 73% in LXX2012, and the expected ordering is restored --
LXX2012 ahead of KJV (73% against 62%), matching Composite 17's 69% and the
58%-vs-46% pattern across every other composite. The old range was the one
place LXX2012 scored *below* KJV, which is what drew attention to it.

Both display and sdisplay updated; the zero-width space on display is intact.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nQRjJpPqW89xqzAf1vsEX
Six of the eight chapter-level composite references are replaced with the
actual verse selections. They were things like "Isa 40, 41, 45, 48, 54", which
resolves to 123 verses where the reading is ten.

Nobody publishes these selections. Ephrem writes "and selection"; oca.org
titles a composite by chapter and numbers its parts rather than its verses,
where an ordinary reading gets true verse numbers; orthodox_calendar stores
opaque part keys. The text is the only record of what a composite contains.

oca.org publishes the same composite corpus under the same numbering --
verified word-for-word identical to orthodox_calendar on Composites 17 and 18,
181 and 209 words with no differences -- so its text is good evidence of which
verses are covered, even though the translation is not Ephrem's and cannot
replace ours. Each composite was read against our Bible by eye rather than
matched by threshold: Composite 18's error was found by reading, and no
threshold would have flagged it.

Fidelity, as word-level similarity between oca.org's text and our LXX2012-WEB
rendering, against the 59-78% band set by the sixteen specs that were already
precise:

  13  3 Kgs 18, 19    62% -> 74%      8  Isaiah      11% -> 66%
   2  Proverbs        17% -> 64%      3  Wisdom      24% -> 56%
   9  Malachi         34% -> 54%      6  Exod/Lev/Num 15% -> 53%

Composites 4 and 5 are left loose. Their derived specs score 44% and 37%, and
that is not a failure to find the right verses -- adding candidates made them
worse -- but free adaptation that no reference can represent. sdisplay is
published as the API's short_display, so a low-confidence guess there would
present inference as fact; Composite 5's spec also exceeds the column's
max_length of 64.

That API exposure is why calendarium/tests/data/january.json changes: the
Composite 2 reading falls on January 25 and 27.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nQRjJpPqW89xqzAf1vsEX
The point of deriving the verse selections was to see whether composites could
become plain references resolved in the reader's own translation. They cannot:
only three of eight derivations reach the 59-78% band that the already-precise
specs occupy, and Composites 4 and 5 get *worse* as candidate verses are added,
because they are free adaptations rather than selections. A partial conversion
would render some composites in the reader's translation and others in
Ephrem's, on the same page, for no gain in what the reader sees.

So the mechanism stays as it was: Ephrem's text for the 22 that have it, and
the zero-width-space fall-through for 17 and 18. The six derived specs that had
been written into the fixture are reverted, and the research is kept in
docs/oca-audit.md where it belongs.

Reverting them matters because sdisplay is not a private note. calendarium/api.py
publishes it as the API's short_display, documented as "the scripture reference
with abbreviated book name" -- a test failure on calendarium/tests/data/january.json
is what surfaced that. For a composite there is no such reference, the loose
form is what oca.org itself prints as the title, and an enumerated guess there
would assert a precision measured at 53-64%.

Two corrections do remain, because neither is inference:

  Composite 18  "3 Kgs 7:51-8:1, 8:4-7, 9-11" -> "3 Kgs 8:1, 3-7, 9-11".
                The old range carried 7:51, which oca.org's text does not have,
                and skipped 8:3, which it does. This one is functional: 18 is a
                fall-through, so the range is what readers actually get.
  Composite 24  "Lev 26" -> "Lev 26:3-12, 14-17, 19-20, 22, 33, 23-25", the
                whole 46-verse chapter against a 21-verse reading. Ephrem
                spells this one out, so the spec is his, not derived.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nQRjJpPqW89xqzAf1vsEX
The first actual fix from the Slavic audit rather than a measurement. Oct 31
was the only date differing in all five harvested years (2023-2027), which is
what drew attention to it -- it had been filed as a one-off.

oca.org gives St John Kochurov a proper Gospel and no proper Epistle: the
Epistle is the ordinary daily one, and it differs every year (Col 2:20-3:3,
Phil 1:20-27, Col 2:1-7, 2 Cor 5:1-10, 2 Cor 11:31-12:9), which is what proves
it is the daily reading rather than a proper. The Gospel is John 10:9-16 in all
five years, labelled for him.

The app had a proper Epistle, Hebrews 13:7-16, and gave him Luke 12:32-40. Both
are plausible hieromartyr commons, which is presumably where they came from,
but oca.org is the source this data was compiled from and it disagrees
consistently.

  - Gospel repointed from Luke 12.32-40 to John 10.9-16.
  - The Hebrews 13:7-16 Epistle row is removed; reading rows 1807 -> 1806.

Oct 31 now differs by one citation a year instead of three. What remains is the
app keeping the ordinary daily Gospel where oca.org replaces it -- the same
replace-versus-append question as Jan 1, Jan 5 and Dec 24, and not a fact about
this date.

Note when checking locally that loaddata is additive: it will not remove the
deleted row from an existing dev database, and the audit still reported
HEB13:7 until it was deleted explicitly. Production rebuilds the database from
the fixture at image build, so it is unaffected.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nQRjJpPqW89xqzAf1vsEX
Harvesting 2023-2027 corrected two conclusions drawn from 2026 alone: Oct 31 is
not a one-off but the only date differing in all five years, and Jan 3 is not a
fixed-date problem but the Saturday before Theophany moving across Jan 2-4. One
year is enough to find a candidate and not enough to rank it.

Records why the remaining rule -- whether the ordinary daily reading coexists
with a proper one -- is not being implemented yet. The harness cannot prove such
a change safe: aget_readings is on the base Day class shared by SlavicDay and
GreekDay, so a change made for Slavic accuracy is measured for Greek against a
different source and could cost the 99.1% silently; and both audits check the
Liturgy Epistle and Gospel only, 894 of 1,446 readings a year, leaving 38%
unmeasured on precisely the feast days the rule governs.

The prerequisite is extending the audit to /readings/daily/, which lists every
service.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nQRjJpPqW89xqzAf1vsEX
Acting on the harvested oca.org commemorations: 365 dates, 2,968 entries from
/saints/lives/. A DayCommemoration tagged `greek` asserts the Slavic tradition
does not keep it, so Slavic users were not shown these at all.

Screening the 730 greek-tagged commemorations against oca.org for the same date
turned up 99 candidates at full token overlap. The unambiguous ones share a
Slavic or Balkan place marker, which makes a greek-only tag self-evidently
wrong -- three are Kiev Caves saints:

  01-02  Sylvester of the Kiev Caves        | Venerable Sylvester of the Kiev Near Caves
  01-04  Eustathios Archbishop of Serbia    | Repose of St Eustathius I, Archbishop of Serbia
  01-08  Gregory of the Kiev Caves          | Venerable Gregory, Wonderworker of the Kiev Near Caves
  04-08  Rufus the Obedient, Kiev Caves     | Venerable Rufus the Obedient of the Kiev Far Caves
  05-22  John-Vladimir, Ruler of Serbia     | St John-Vladimir, Prince of Bulgaria, Greatmartyr
  07-27  Nicholas of Novgorod, fool-for-Christ | Blessed Nicholas Kochanov the Fool-For-Christ at Novgorod
  08-28  Synaxis of the Kiev Cave Fathers   | Synaxis of the Saints of the Kiev Caves
  11-27  James the Wonderworker of Rostov   | St James the Wonderworker, Bishop of Rostov
  12-13  Gabriel, Archbishop of Serbia      | Hieromartyr Gabriel of Serbia

Each was checked against the full oca.org listing for its date rather than
trusting the score. Retagged common; all nine now appear for both traditions.

The remaining ~90 candidates are NOT retagged. Name overlap alone is not
evidence, and the standing trap on this project is that a same-name match is
usually noise: Sept 24 pairs our "Stephen the Martyr" with oca.org's "Holy King
Stephen of Serbia", who is a different person, and Catherine of Alexandria is
genuinely Nov 24 for Slavic and Nov 25 for Greek, a distinction that must not
be collapsed. Those need reading one at a time.

calendarium/tests/data/january.json is regenerated because Jan 2, 4 and 8 now
carry three more saints for the default tradition. Regenerate it with
indent=4, ensure_ascii=False and no trailing newline, or the diff is 2,300
lines of reformatting instead of three.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nQRjJpPqW89xqzAf1vsEX
The evidence behind the composite verse selections in docs/oca-audit.md: all 24
composites as oca.org publishes them, harvested by tools/oca/harvest_composites.py.
Kept in the repo so the readings can be re-checked without another hour of
crawling, and because oca.org is the only source that prints these texts in a
form a machine can read.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nQRjJpPqW89xqzAf1vsEX
Completes the read-through begun in the previous commit. Every one of the 93
remaining candidates was read as a pair of titles and judged individually;
`greek` went from 730 commemorations to 630.

Two were rejected as different people sharing a name and a date, which is the
failure mode this whole exercise had to guard against:

  08-30  ours "The Holy New Martyrs of Serbia" against oca.org's "Saint
         Spyridon, Patriarch of Serbia". oca.org's Aug 30 has the Synaxis of
         the Serbian Hierarchs and three individual patriarchs, and no
         commemoration of the new martyrs at all.
  09-24  ours "Stephen the Martyr" against oca.org's "Holy King Stephen of
         Serbia" -- a king, not a martyr, and not the same man.

Seven more looked thin because our title is generic -- "Stephen the Monk",
"Julian the Martyr", "Serapion the Martyr", "Phaedrus the Martyr". Each was
checked against the whole day's listing rather than the score, and in every
case exactly one entry out of seven to eleven carries that name, which settles
it: a rare name kept on the same date is the same saint.

Several of the accepted ones are plainly Slavic and were never Greek-only:
Nilus of Sora, Theodosius of Totma in Vologda, Moses the Hungarian of the Kiev
Near Caves, the 26 Martyrs in the Crimea.

calendarium/tests/data/january.json is regenerated again; Jan 4, 5, 10 and 28
gain saints, and Jan 10 and Jan 28 gain them in summary_title as well.

What remains greek-tagged is now dominated by Ottoman-era Greek New Martyrs and
Athonite figures, which is what that tag should mean. Nothing below full token
overlap has been reviewed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nQRjJpPqW89xqzAf1vsEX
f4279e0 removed St John Kochurov's Hebrews 13:7-16 and repointed his
Luke 12:32-40 to John 10:9-16, on the grounds that oca.org's monthly lectionary
showed only "2 Corinthians 5:1-10 / John 10:9-16 (St. John Kochurov)" in all
five harvested years. The all-services harvest shows that was wrong. oca.org's
daily page for the same date lists five readings:

  Hebrews 13:7-16       (Epistle, St. John Kochurov)
  Luke 12:32-40         (Gospel, St. John Kochurov)
  John 10:9-16          (Gospel, St. John Kochurov)
  2 Corinthians 5:1-10  (Epistle)
  Luke 8:16-21          (Gospel)

The app's data was right except for one omission. Both readings are restored
and John 10:9-16 is added as the reading that was genuinely missing. Oct 31 now
matches oca.org exactly, where the previous "fix" left it differing by two.

The monthly lectionary is not merely Liturgy-only, which
tools/oca/harvest_readings.py already documents. It is *lossy*: it drops
Epistle and Gospel readings the daily page carries, keeping one reading set per
label. Two of the three readings it omitted here are labelled Epistle and
Gospel by oca.org itself, so no rule about services would have predicted it.
Prefer /readings/daily/ for anything that will change data.

This is the failure the coverage note in docs/oca-audit.md predicted in the
abstract -- a change that looked right against 38%-coverage evidence and was
wrong against the whole day -- arriving before the logic change it was written
about.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nQRjJpPqW89xqzAf1vsEX
Closes the 38% coverage gap that the previous note called a prerequisite.
harvest_daily.py pulls /readings/daily/, which lists every service -- Theophany
Eve returns 34 readings there against the monthly lectionary's two -- and
audit_daily.py compares the whole day on both sides, symmetrically, since
neither side is now truncated.

Baseline 352/365 dates matching exactly, 1,448 app readings against 1,438.
The previously unmeasured 38% is almost entirely clean: Theophany Eve's
thirteen Vespers lessons, four sets of Hours and Blessing of Waters all match,
and the day differs only in the two Liturgy readings already known about.

canon() now recognises composites by number. Both sides name them "Composite
N", but oca.org prints no verses, so the citation was unparseable and the app's
resolved reference looked spurious -- Nov 21 was a false difference. classify()
falls through to the book name for those tokens so the Vespers OT
classification still holds.

New findings the Liturgy-only audit could not see, chiefly a one-day offset:
Composites 2, 3 and 4 fall on Mar 30 here and Mar 31 on oca.org. That is a
wrong date rather than a wrong reading, and the two halves cancel out in any
per-year total.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nQRjJpPqW89xqzAf1vsEX
Brian noticed on Aug 29 that the Beheading of the Forerunner's Epistle and
Gospel sort after the ordinary weekday readings, so the abbreviated readings --
what the Alexa skill speaks -- never reach them. It generalises to every feast
at level 6.

`ordering` runs in tiers: 801/901 sorts a reading before the daily cycle at
811/911, and 821/921 after it. Levels 7 and 8, the Twelve Great Feasts, all use
tier 0 already. Level 6 is "red cross circle (great feast typikon symbol)" --
the Typikon's highest rank below those -- and every one of its six fixed dates
was at tier 2.

aget_abbreviated_readings takes the first Epistle and the first Gospel after
it, so on four of the six the skill was reading the ordinary weekday lesson and
never naming the feast:

  06-24  Nativity of the Forerunner   Romans 11.2-12    / Matthew 11.20-26
  06-29  Peter and Paul               Romans 12.4-5,15-21 / Matthew 12.9-13
  08-29  Beheading of the Forerunner  1 Cor 2.6-9       / Matthew 22.15-22
  11-16  Apostle and Evangelist Matthew  2 Thess 1.1-10 / Luke 14.12-15

Peter and Paul is the starkest. Jan 1 was already correct, but only by
accident: the app has no daily reading on Circumcision, so the feast's won by
default. That gap is itself a known difference against oca.org, and closing it
would have broken Jan 1 -- so it is retiered too, along with St Basil to
802/902 behind it, which is the order oca.org prints.

16 rows moved from 821/921 to 801/901 (822/922 -> 802/902 for St Basil). No
reading is added or removed; the sets are identical and only the order changes.
Verified: all five feasts now speak their own readings, the all-services audit
holds at 352/365, and Greek against goarch.org holds at 333/336.

calendarium/tests/data/january.json changes accordingly:
abbreviated_reading_indices on Jan 1 move from [5, 8] to [4, 7], and the day's
readings now run Circumcision, St Basil, Saturday before Theophany.

Level 5, the vigil rank, is deliberately untouched -- eleven more dates,
including St Nicholas and John Chrysostom, where the same argument could be
made but the Typikon draws a distinction. That is a judgement call for Brian,
not a bug.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nQRjJpPqW89xqzAf1vsEX
aget_abbreviated_readings took the first Epistle and the first Gospel after it,
by `ordering`. That made the Alexa skill read the ordinary weekday lesson on
feast days -- Peter and Paul, the Beheading, the Three Hierarchs -- and it had
never been measured, because there was no obvious ground truth for "the"
reading of a day.

There are three. antiochian.org and goarch.org each publish exactly one pair a
day and agree with each other on every feast checked. oca.org's monthly
lectionary lists sets per date, propers labelled -- useful, but its row order is
not reliably primary-first: Jan 1 leads with Circumcision while Feb 2, the
Meeting of the Lord, leads with the daily reading. That ruled out the simplest
approach of trusting the first row.

Measured against antiochian.org for 2026, on days having a proper, the proper
wins from about feast level 3 upward and the daily wins below it. Splitting
level 3+ by weekday sharpens it: proper 59 to 8 on weekdays, daily 7 to 1 on
Sundays, the resurrectional reading taking precedence over almost any saint.

**That Sunday split is why the rule is code and not data.** Whether a fixed
date falls on a Sunday changes from year to year, while `ordering` is static, so
retiering rows could never express it.

Implemented as: prefer the day's propers when feast_level >= 3 and the day is
not a Sunday, otherwise prefer the ordinary readings, falling back to whatever
exists. Lenten weekdays pass through untouched -- there is no Epistle/Gospel
pair to reduce to, so the Old Testament readings still come through for every
tradition, which Brian confirmed is right.

Against oca.org's leading pair, 2026 goes from 244/339 (72.0%) to 283/339
(83.5%), and that understates it: the metric scores Feb 2 as a miss where we now
agree with both antiochian.org and goarch.org.

No regression: Greek against goarch.org holds at 333/336, the all-services
audit at 352/365, 189 tests pass. january.json changes only in
abbreviated_reading_indices, on five level-4 weekdays that now speak the saint's
readings rather than the daily cycle.

Adds tools/oca/audit_abbreviated.py so this is measurable from now on.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nQRjJpPqW89xqzAf1vsEX
Checking Brian's question about the Annunciation on a Lenten weekday -- it does
show Hebrews 2:11-18 / Luke 1:24-38, on all three of 2024, 2026 and 2027 --
exposed a gap in the rule committed just before. Floating commemorations were
being treated as ordinary readings, because they carry a pdist rather than a
month and day, so `ordering` decided among them and the day's own commemoration
lost:

  12-19  Saturday before Nativity  gave the daily pair, not Galatians 3:8-12
  10-24  Demetrius Saturday        gave the daily pair, not the Departed
  07-19  Sunday of the Fathers     gave the daily pair, not Hebrews 13:7-16

oca.org prints all three the other way round. Floats are not a saint landing on
a day, they are the day, so they now rank ahead of everything including the
Sunday rule -- that statistic was measured on saints *displacing* a Sunday,
which is a different question. A float is a pdist at or above 1000, the base of
FloatIndex.

The Lenten soul Saturdays needed data rather than a rule. Their Departed
readings carry the day's own pdist and were distinguished only by desc and an
ordering of 812/912 -- one tier *below* the daily cycle, so they could never win.
Those days are always Saturdays, so unlike the Sunday case static ordering can
express it: twelve rows moved to 802/902. Demetrius Saturday needed nothing,
being already a float at pdist=1003.

Against oca.org's leading pair, 2026 is now 294/339 (86.7%), from 244/339
(72.0%) before this work: 83.5% from the rank rule, 85.0% with floats first,
86.7% with the soul Saturdays retiered.

The Annunciation is worth a note of its own: it works because Mar 25 has no
fixed-date Epistle or Gospel rows at all: its readings are floats. A tier audit
looking only at month/day rows reports "no Epistle/Gospel rows" for a Great
Feast, and that is not a fault.

No regression: Greek against goarch.org 333/336, all-services audit 352/365,
189 tests pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nQRjJpPqW89xqzAf1vsEX
Brian was notified that St Tikhon's calendar lightens Jul 24 to wine and oil
where this app had a full abstention. It is systematic, not one date.

_apply_fasting_adjustments had cases for Lent, Dormition and the
Apostles'/Nativity fasts and none for the ordinary Wednesday and Friday fast, so
outside the four great fasts fast_exception came straight from the data with no
rank adjustment. That data disagrees with itself: at feast level 4 the fixture
carries 0 for Boris and Gleb and for Constantine and Helen, 1 for Job of
Pochaev, and 2 for Sergius of Radonezh.

It cannot be made consistent there. fast_exception is baked onto a fixed-date
Day row, and whether that date lands on a Wednesday or Friday changes from year
to year -- the same structural point as the Sunday rule for abbreviated
readings.

Verified against holytrinityorthodox.com, which publishes a dietary line per
day, looking each commemoration up in a year where its own day falls on a
Wednesday or Friday. Boris and Gleb, Anthony of the Kiev Caves, the Synaxis of
the Twelve Apostles and Sep 11 all give "Fast. Food with Oil"; the Leavetaking
of Theophany gives "Fast. Fish Allowed". A floor of wine and oil is therefore
conservative. antiochian.org is no use for this -- it does not keep Boris and
Gleb at all -- and oca.org publishes no fasting information.

13 dates a year move from full abstention to wine and oil. All 45 level-4 and
above ordinary Wednesday/Friday fast days across 2025-2027 now carry an
exception; below level 4 nothing changes.

GreekDay is deliberately untouched: it has its own adjustments, the evidence
here is Slavic, and Greek feast levels differ -- Jul 24 is level 0 for Greek.

To Brian's question of whether this tracks the reading differences: it does not.
Of the 13 dates, only Jun 30 appears in either readings audit. Same shape of
problem -- hand-maintained per-date data with no rule applied -- but a disjoint
set of days.

No regression: all-services audit 352/365, abbreviated 294/339, 189 tests pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nQRjJpPqW89xqzAf1vsEX
Checked at Brian's request to rule out a missed conversion. It was not missed:
lib/core.lib.php's calculateFasting has cases for fast-free, Lent, Dormition and
the Apostles'/Nativity fasts -- the same set, order and thresholds this app
uses -- and no case for $fast == 1, the ordinary Wednesday and Friday fast. Its
Jul 24 row carries daFexc = 0, identical to ours. The rule was never there.

Comparing all 366 fixed dates shows how close the conversion is, and which few
diverge. fast_exception differs on 7: we are Fast Free where Paul has
fish/wine/oil on May 7, May 11, Jul 26 and Oct 1, and grant wine and oil where
he has none on Aug 16, Aug 28 and Sep 13. feast_level differs on 6, two of which
matter because the rules added recently key on it -- Jun 30 is level 4 here and
3 for Paul, and Nov 16 is 6 here and 5 for Paul, which is what placed it among
the great feasts retiered earlier.

Also records a schema difference that had not been written down: Paul has two
level columns, daFlevel for the feast and daSlevel for the saint, and this app's
feast_level is the max of the two on 360 of 366 dates. That is not neutral,
because his code uses them differently -- calculateFasting and the reading logic
see daFlevel alone, while daSlevel only picks the katavasia, which this app does
not implement. Jan 14 is F=0 S=4 for Paul and level 4 here.

In that instance the collapse improved things: holytrinityorthodox.com gives
Jan 14 "Fast. Fish Allowed" on a Wednesday or Friday, which a level-0 reading
could not produce. But feast-level-gated rules here fire on days the original
would not have, which is worth knowing before adding more of them.

Documentation only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nQRjJpPqW89xqzAf1vsEX
… service notes

Brian's decision (2026-09-02): do not port Paul's values into the fixtures. This
data has been revised a great deal since he stopped work, so where the two
disagree the assumption should be that ours is the later judgement.
orthodox_calendar stays useful for answering "was this ever handled?" -- as with
the Wednesday/Friday rank exception, which it never was -- and for corroborating
a reading or a date.

Also records the daSnote mapping, which had not been written down: it was kept
as Day.service_note, and 20 of Paul's 25 notes are present here with identical
text.

Four of the five absent ones look like an oversight rather than a revision,
since this app plainly wants such notes -- it generates "Beginning of Apostles'
Fast" in code:

  pdist -70   Beginning of the Lenten Triodion
  pdist    0   Beginning of the Pentecostarion
  Nov 15       Begin Nativity Fast
  Aug 1        Begin Dormition Fast

All four render with an empty service_notes list. One of the three fast
beginnings is announced and two are silent, and neither the Triodion nor the
Pentecostarion is marked. The fifth absence, a Presanctified note at pdist -17,
is generated dynamically by both codebases and is not a gap.

Not added, per the decision above. Documentation only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nQRjJpPqW89xqzAf1vsEX
…sured

Two things from the orthodox_calendar comparison.

**Service notes.** One of the four apparently-missing notes turned out to be
relocated rather than lost: pdist -70 keeps "Beginning of the Lenten Triodion"
in feast_name, where Paul has it as a note and leaves daFname empty. The other
three were genuinely absent and are added, since this is an oversight rather
than a revision -- the app already generates "Beginning of Apostles' Fast" in
both SlavicDay and GreekDay, so it plainly means to announce these:

  pdist 0, Pascha   Beginning of the Pentecostarion
  Aug 1             Beginning of Dormition Fast
  Nov 15            Beginning of Nativity Fast

The fast beginnings are worded to match the generated Apostles' Fast note
rather than Paul's terser "Begin Nativity Fast". All three are common, so both
traditions get them.

**The Wednesday/Friday rank exception does not extend to Greek practice**, on
the evidence available, so GreekDay stays as it is -- now a measured decision
rather than caution.

Tested against antiochian.org's fastDesignation across every ordinary Wed/Fri
fast day harvested, 2018-2026. Bucketing by our feast_level is confounded and
was discarded: that number is compiled from Slavic sources, which is why Jul 24
is level 0 for Greek. Using antiochian's own feastDayTitle instead -- a plain
lectionary slot unless a commemoration claims the day -- gives 52% strict
against 33% wine and oil where a commemoration is named, versus 87% against 2%
where it is not.

So there is a rank effect, but a majority of named days are still strict, which
is nothing like the Slavic picture where every sampled polyeleos day was relaxed.
Named-yet-strict includes the Apostle Philip, Athanasius of Mount Athos and
James son of Alphaeus. Applying the Slavic rule to GreekDay would be wrong in
the lenient direction on roughly half the days it touched.

Tests not run on this commit, at Brian's direction.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nQRjJpPqW89xqzAf1vsEX
Its robots.txt disallows exactly the calendar path used to confirm the
Wednesday/Friday rank exception, and I did not check it until after those
queries were made. That is an error -- oca.org's robots.txt was checked before
harvesting and this one should have been too -- and it is why that sample was
not widened.

Records the constraint so nobody repeats it, along with what remains available:
azbyka.ru is behind a DDoS-Guard challenge, oca.org publishes no fasting, and
days.pravoslavie.ru and orthochristian.com serve no robots.txt but are Moscow
Patriarchate rather than OCA.

Also lists the eight days in 2026 the rule actually changes, and seven controls
that carried an exception from the data beforehand, so the printed St Tikhon's
calendar can settle it without scraping anything.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nQRjJpPqW89xqzAf1vsEX
At Brian's direction, five more lookups against holytrinityorthodox.com.
Greatmartyr Marina and Adrian and Natalia both give "Fast. Food with Oil",
bringing the level-4 sample to seven of seven confirming the rule in the
direction it was applied, with nothing contradicting it.

Two samples were invalid and the method error is recorded: the date arithmetic
found a year where each commemoration lands on a Wednesday or Friday but did
not check that the day is an *ordinary* fast in that calendar's own reckoning.
Theodore Tyro landed inside Great Lent and Constantine and Helen inside a
fast-free week. Filter on the season, not just the weekday.

The control is the interesting one. St Sava of Serbia is feast level 3 here,
below the threshold the rule uses, and holytrinityorthodox.com relaxes his day
to fish. So this app may still be too strict at level 3.

That is not necessarily a threshold error -- feast_level is this project's own
number and St Sava may be polyeleos in ROCOR reckoning where he is doxology
here, the same confound that made the Greek measurement unusable. What it does
establish is that the remaining error runs toward being too strict rather than
too lenient, which is the safe direction.

Also updates the earlier "do not scrape it" note: these lookups were made at
Brian's explicit direction, at a handful of manually-spaced requests rather
than a crawl.

Documentation only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nQRjJpPqW89xqzAf1vsEX
Four more samples at levels 3 and 2 all returned "Fast. Fish Allowed", which
looks like evidence the threshold should be well below 4. A control shows it is
not: 2026-04-29, a plain Wednesday in the Paschal season commemorating only two
minor virgin-martyrs, also returns "Fast. Fish Allowed". The Paschal season
relaxes Wednesday and Friday to fish irrespective of rank, so the two April
samples were measuring the season. A second control returned "Fast-free Week"
for 2026-02-04, the week of the Publican and Pharisee.

The two remaining samples are not clean either -- the Synaxis of the Forerunner
sits in the relaxed days after Theophany and is a significant feast in itself,
and Paul of Thebes at level 2 falls days before the Triodion.

So the threshold below level 4 is still unknown. Testing it needs samples drawn
from ordinary time, outside the Paschal season, the Triodion, the four fasts and
the fast-free weeks; the season filter used here was not strict enough. The rule
as shipped rests on seven level-4 confirmations and is unaffected either way.

The control also turned up something larger than the question it answered.
holytrinityorthodox.com allows fish on Paschal-season Wednesdays and Fridays
where this app allows only wine and oil -- Apr 22, Apr 24 and Apr 29 in 2026.
The app does give fish on a few days in that stretch, May 6 and May 20, but from
per-date data rather than a rule, so the pattern is uneven. If fish is right for
the whole span from Thomas Sunday to Pentecost that is 12 to 14 days a year,
more than the 13 the rank rule touched.

Not changed. One source is not enough for a change that size, and it should come
from something that states the rule rather than from sampled days.

Documentation only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nQRjJpPqW89xqzAf1vsEX
Brian pointed to OCA's published fasting guidelines, which state the rules
rather than showing sampled days:
https://www.oca.org/liturgics/outlines/fasting-fast-free-seasons-of-the-church

Everything it states outright, this app already gets right -- Trinity Week and
the week of the Publican and Pharisee fast-free, Cheesefare a Meat Fast, and the
Eve of Theophany, the Beheading and the Elevation as fast days.

Its Lenten named list was three-quarters implemented. Wine and oil are permitted
on nine named dates when they fall on a weekday in the second through sixth week.
Six of the nine are granted correctly in every year checked; Feb 24, Mar 9 and
Apr 25 were granted in none. Those three now carry fast_exception = 1.

They stay `common` rather than being split: antiochian.org grants wine and oil
on Mar 9 in 2020 and 2026 and on Apr 25 in 2024, so those are shared rather than
Slavic-only, which also matches what the earlier Greek fasting work concluded.
Feb 24 has no qualifying harvested year and its Greek side is assumed.

No scoping logic was needed. Week 1 and Holy Week exclude themselves already,
because those pdist rows carry fast_exception = 10, "No overrides", and
Day.fast_exception takes the max -- Feb 24 correctly stays strict in 2026, when
it falls in the first week.

Two corrections to the record also go in.

The Paschal-season fish candidate is withdrawn. The page lists Bright Week and
Trinity Week as fast-free and otherwise treats Wednesdays and Fridays as fast
days, with no fish allowance for the Paschal season. This app already gives wine
and oil there, which is more lenient than OCA's stated rule, so
holytrinityorthodox.com's fish is ROCOR practice rather than a gap here.

And the ordinary Wednesday/Friday rank rule added earlier is *not* stated by
this document. The page says a Fast Day is strict and calls rank relaxations
"local variations". The rule matches OCA's published calendar -- St Tikhon's,
and seven of seven on holytrinityorthodox.com -- but not their published
guidelines, and should be described that way. The Typikon quotation there is
scoped to the Apostles' and Nativity fasts, where it sets thresholds this app
does not implement: doxology rank takes wine and oil on Wednesday and Friday and
vigil rank takes fish, while the code only ever reduces an exception, never
grants one. Noted, not fixed.

Tests still not run since Brian's instruction to skip them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nQRjJpPqW89xqzAf1vsEX
The rule added earlier -- a polyeleos-rank commemoration on an ordinary
Wednesday or Friday takes wine and oil -- is removed before shipping. Two
reasons, both found after writing it.

Its threshold rests on nothing. The Typikon text OCA quotes names *doxology*
rank for wine and oil on a Wednesday or Friday and *vigil* rank for fish, which
are levels 3 and 5 here. Level 4 is not singled out at all; it was chosen
because that is where this app's data happened to disagree with itself.

More fundamentally it was another patch on a pile of patches, which is Brian's
observation and the reason to stop. Combining the Paschal and festal cycles is
done with max() over an index that encodes precedence claims and sentinels
alongside dietary rungs, so that max() resolves to the more lenient side on some
collisions and the stricter on others, and everything it gets wrong is corrected
by hand in _apply_fasting_adjustments. A fourteenth special case is the wrong
move.

The three Lenten date corrections are kept. Feb 24, Mar 9 and Apr 25 come from
OCA's published named list rather than from a rule, are confirmed on the Greek
side by antiochian.org for two of the three, and are unaffected by any
refactoring of the combination logic.

Adds docs/fasting-refactor-scope.md, written while the reasoning was fresh: the
diagnosis, the unused DietaryAllowance ladder that already models this properly
and is referenced in exactly one display line, the shape to aim at, the size of
the migration (about 30 awkward rows out of 858), and the characterisation test
to write first. The rank questions are listed there as behaviour decisions to
settle once the model can express them, not as part of the refactor.

Verified on the final state: 189 tests pass, Greek against goarch.org 333/336,
all-services audit 352/365, abbreviated 294/339, Liturgy-only 329/339.
january.json returns to its pre-rule values.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nQRjJpPqW89xqzAf1vsEX
Spotted while tracing aget_abbreviated_readings over Lenten weekdays. One
Reading row spelled its source "Sixth Hour" where the other 38 use "6th Hour" --
pk 163, pdist -53, Cheesefare Wednesday, Joel 2:12-26. The row already carried
ordering 301, the same slot as the 35 canonical "6th Hour" rows at that value,
so this was a spelling divergence and nothing more.

It matters because `source` is a public API field and the natural way to select
Hours readings is to match the string, which silently skipped this one day a
year. No code or template referenced either spelling and the row appears in no
test fixture, so the fix is the single string. Verified rendering on three years
where the pdist resolves to a different date; 189 tests pass.

Also records in docs/oca-audit.md what the same trace turned up about Lenten
weekdays, which is a design decision rather than a bug. A plain Lenten weekday
passes its Old Testament readings through unreduced, because no group holds both
an Epistle and a Gospel and the reduction is a no-op. One carrying a fixed-date
commemoration at feast level 2 or above drops them for the saint's pair --
2026-03-09, the Forty Martyrs, goes from nine readings to two. Clean Week and
Holy Week are exempt through the existing fast_exception != 10 guard.

oca.org does the opposite there, listing both sets with the Old Testament one
first: 28 days over 2023-2027. Kept as-is per Brian, since antiochian.org and
goarch.org both publish a single Epistle and Gospel pair for the day. The
divergence is from OCA specifically and two of the three sources agree with the
current behaviour.

data/oca_readings_diff-2026.json is refreshed. Oct 31 now reports three extras
against the monthly lectionary rather than one, which is correct: the readings
restored there earlier are real, and the monthly view is the lossy one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nQRjJpPqW89xqzAf1vsEX
@brianglass
brianglass merged commit f64279d into main Sep 8, 2026
4 checks passed
brianglass added a commit that referenced this pull request Sep 8, 2026
The last open follow-up from #225, which was merged before this landed.

Checked against holytrinityorthodox.com, the ROCOR calendar, which reckons old
calendar so Julian Oct 31 is Gregorian Nov 13.

ROCOR commemorates St John Kochurov and gives him no proper readings. The day
names him among a dozen commemorations and its scripture line is the plain daily
cycle, 1 Thess 5:9-13, 24-28 / Luke 11:23-26.

A control rules out the page simply not printing propers: Julian Nov 13, St John
Chrysostom, lists John 10:1-9 as Matins Gospel and Heb 7:26-8:2 / John 10:9-16
each labelled "St. John". The format shows propers, with service and
attribution, when they exist.

So the hypothesis was backwards. Kochurov's three readings are OCA's, and ROCOR
is the tradition without them. This app's Slavic data is compiled from oca.org
and follows it here, so nothing changes.

Recorded as a genuine OCA/ROCOR divergence, since `tradition` has only slavic,
greek and common and no axis to express one -- Oct 31 is a known instance if
that axis is ever added.

It also settles the earlier misstep retrospectively: deleting Kochurov's Epistle
and repointing his Gospel would have moved this app toward ROCOR practice by
accident, away from its own documented source.

Documentation only; no code or data changes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nQRjJpPqW89xqzAf1vsEX
brianglass added a commit that referenced this pull request Sep 8, 2026
The last open follow-up from #225, which was merged before this landed.

Checked against holytrinityorthodox.com, the ROCOR calendar, which reckons old
calendar so Julian Oct 31 is Gregorian Nov 13.

ROCOR commemorates St John Kochurov and gives him no proper readings. The day
names him among a dozen commemorations and its scripture line is the plain daily
cycle, 1 Thess 5:9-13, 24-28 / Luke 11:23-26.

A control rules out the page simply not printing propers: Julian Nov 13, St John
Chrysostom, lists John 10:1-9 as Matins Gospel and Heb 7:26-8:2 / John 10:9-16
each labelled "St. John". The format shows propers, with service and
attribution, when they exist.

So the hypothesis was backwards. Kochurov's three readings are OCA's, and ROCOR
is the tradition without them. This app's Slavic data is compiled from oca.org
and follows it here, so nothing changes.

Recorded as a genuine OCA/ROCOR divergence, since `tradition` has only slavic,
greek and common and no axis to express one -- Oct 31 is a known instance if
that axis is ever added.

It also settles the earlier misstep retrospectively: deleting Kochurov's Epistle
and repointing his Gospel would have moved this app toward ROCOR practice by
accident, away from its own documented source.

Documentation only; no code or data changes.


Claude-Session: https://claude.ai/code/session_019nQRjJpPqW89xqzAf1vsEX

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
brianglass added a commit that referenced this pull request Sep 14, 2026
#234)

* Retag 86 commemorations oca.org keeps; rank the calendar grid's saints

A new reverse screen, tools/oca/oca_vs_app.py, asks which of oca.org's 2,968
commemorations for 2026 the Slavic page lacks. #225 asked the opposite
question and never looked this way. Of the 93 entries it found only on the
Greek page, 90 were read individually and are the same saints under Greek
transliterations (Eulogios/Eulogius, Neophytos/Neophytus,
Vassianos/Bassian, Kalliniki/Callinica). The 86 rows behind them move from
greek to common; greek goes from 630 to 544. oca.org entries matched on
the Slavic page go from 822 to 914.

Three are held back because the row names someone oca.org does not keep
that day:

  04-09  Raphael, Nicholas, Irene, & Olympias -- oca.org has no Olympias
  04-20  Gregory & Anastasios, Patriarchs of Antioch -- oca.org's Anastasius
         that day is the Sinaite, not the patriarch
  05-19  Theotima & Kyriake -- oca.org has no Kyriake

The retag showed up a display problem. Day.minimal_saints, the three names
on the calendar grid and in summary_title (and so in RSS and iCal), was a
plain truncation of Day.saints. That list puts every day_native row before
every additive one, and most storied saints are additive. So on 50 Slavic
days a bare harvested name took a storied saint's slot: "Martyr Mertios"
over Benedict Biscop on Jan 12, "Marinos the Martyr" over St Patrick on
Mar 17. minimal_saints now keeps three by _display_priority, stable within
each tier:

  1. ranked (rank > 0 or high_rank)
  2. with a story
  3. the rest

Across 2026 a retagged row now holds a slot on 34 Slavic and 21 Greek
days, and on none of them is a ranked or storied saint left out.

The Alexa skill will now speak the 86 names in both traditions, because
_speech_worthy only mutes story-less rows tagged greek. That is about one
extra name on each affected day, the same trade #225's retags made.

calendarium/tests/data/january.json is regenerated: nine January days gain
saints, and several summary_titles change under the new ranking.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nQRjJpPqW89xqzAf1vsEX

* Keep the Sixth Ecumenical Council on Jan 23 for Slavic readers

The traditions keep this council on different days. oca.org lists "the
Holy Fathers of the Sixth Ecumenical Council" on Jan 23 and has no
council on Sep 14. antiochian.org puts it on Sep 14 in both harvested
years. The app had only the Greek Sep 14 row, so Slavic readers never
saw it. Retagging that row common would have put it on the wrong day for
them. This is the Catherine pattern again: Nov 24 for Slavic readers,
Nov 25 for Greek.

This adds a slavic row on Jan 23 with oca.org's wording and no story. It
links to the same Saint (pk 6846) as the Greek row, so the saint page
lists both occasions. The other councils (First, Second, Third, Fifth,
Seventh, and the Sunday of the first six) were checked and already
match oca.org.

A test pins all four combinations of tradition and date.
calendarium/tests/data/january.json is regenerated for the extra Jan 23
name.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nQRjJpPqW89xqzAf1vsEX

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant