Skip to content

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

Open
brianglass wants to merge 20 commits into
mainfrom
oca-slavic-audit
Open

Audit the Slavic readings and commemorations against oca.org#225
brianglass wants to merge 20 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.

Deliberately not done

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.

189 tests pass.

brianglass and others added 20 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
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