feat(office): seed the Little Office BVM per-annum Lauds (Stage 4b) #504

Merged
manlycucumber merged 5 commits from feat/stage4b-lobvm-lauds-seed into develop 2026-07-20 02:30:40 +00:00

Stage 4b — the LOBVM pilot, and the first reviewed golden-digest move.

Seeds the per-annum Lauds of the Little Office BVM into the base 1962 corpus: a 16-part structure + 10 bilingual proper leaves (5 antiphons, capitulum, hymn O gloriósa vírginum, versicle, Benedictus-antiphon, collect). Latin transcribed from the Benziger 1915 page-scans (lobvm-benziger-1915, PD; two-pass scan re-verification, 5 self-caught corrections), English from the Wikisource proofread text; each leaf cited to its printed page. Psalms (92/99/62/148) + canticles (daniel:3:57-88, luke:1:68-79) reference the Stage-2 streams.

  • Built the proper-text generator emit side (Stage 4a shipped only the reader): transformOfficeProperText + born-cited gating.
  • Office resolves on every day (a parallel votive cursus); resolution memoised (day-independent) to keep the ~225k-day freeze tractable — output unchanged.
  • goldenDigest e64d2dfc → 33a2f471 — corpus-driven: corpusVersion 1962-2026-07-18.4 → 1962-2026-07-19.1, engineVersion stays 0.5.0; freeze accepted under GoldenProvenanceLock.
  • Validated: generator npm test 101 green + byte-reproducible; composer check green (1247 tests, phpcs + phpstan clean).

Decisions (maintainer): accents preserved, intonation asterisks dropped from stored values. Pilot simplifications flagged in the facts file (per-annum only; Benedicite = daniel:3:57-88; Paschal Benedictus-ant substitution deferred; ℣/℟ collapsed). Provenance & decisions: docs/design/drafts/lobvm-lauds-per-annum-draft.md.

Stage 4b — the LOBVM pilot, and the **first reviewed golden-digest move**. Seeds the per-annum **Lauds** of the Little Office BVM into the base 1962 corpus: a 16-part structure + 10 bilingual proper leaves (5 antiphons, capitulum, hymn *O gloriósa vírginum*, versicle, Benedictus-antiphon, collect). Latin transcribed from the Benziger 1915 page-scans (`lobvm-benziger-1915`, PD; two-pass scan re-verification, 5 self-caught corrections), English from the Wikisource proofread text; each leaf cited to its printed page. Psalms (92/99/62/148) + canticles (daniel:3:57-88, luke:1:68-79) reference the Stage-2 streams. - Built the proper-text generator **emit** side (Stage 4a shipped only the reader): `transformOfficeProperText` + born-cited gating. - Office resolves on every day (a parallel votive cursus); resolution **memoised** (day-independent) to keep the ~225k-day freeze tractable — output unchanged. - **goldenDigest `e64d2dfc → 33a2f471`** — corpus-driven: corpusVersion `1962-2026-07-18.4 → 1962-2026-07-19.1`, engineVersion stays 0.5.0; freeze accepted under GoldenProvenanceLock. - Validated: generator `npm test` 101 green + byte-reproducible; `composer check` green (1247 tests, phpcs + phpstan clean). Decisions (maintainer): accents preserved, intonation asterisks dropped from stored values. Pilot simplifications flagged in the facts file (per-annum only; Benedicite = `daniel:3:57-88`; Paschal Benedictus-ant substitution deferred; ℣/℟ collapsed). Provenance & decisions: `docs/design/drafts/lobvm-lauds-per-annum-draft.md`.
Author facts/editions/roman-rubricae-1960/offices/little-office-bvm.yaml: the
per-annum Lauds structure (16 parts — antiphon#1-6, Ps 92/99/62/148, canticles
daniel:3:57-88 [Benedicite] and luke:1:68-79 [Benedictus], chapter/hymn/versicle/
collect) and the 10 bilingual proper leaves.

Latin transcribed from the Benziger 1915 page-scans (lobvm-benziger-1915, PD),
English from the Wikisource proofread text; each leaf cited to its printed-page
locator. Structure shape per catholic-encyclopedia-1910 + the psalm scheme the
book prints. Two-pass scan re-verification corrected 5 leaves to match Benziger.

Build the proper-text generator EMIT side (Stage 4a shipped only the reader):
transformOfficeProperText fans inline text into frozen §4.1 leaves (NFC,
source/rights stamped, born-cited gated); transformOffice meta now emits seasons;
wired through build.mjs (emit proper-text.ndjson, validate, provenance gate).

corpusVersion 1962-2026-07-18.4 -> 1962-2026-07-19.1 (corpus-driven). Generator
npm test 101 green, build byte-reproducible, the born-cited gate accepts the
leaves. Decisions: accents preserved, intonation asterisks dropped from values.

The golden re-freeze + contract-test expectations land in the follow-up commit.
The Little Office resolves on every day, but its assembly is day-independent in v1.2
(fixed edition + the single per-annum season). Memoise OfficeResolver::resolve() so a
resolver reused across a year's days assembles ONCE — the golden freeze resolves ~225k
days (1584-2200), and without the memo each re-scans the psalter/canticle streams. Output
is identical; this is a pure speed change, so the digest move stays corpus-driven.

Retarget the tests the seeded office now flips from null to a filled block:
- OfficeResolverTest: the base 1962 edition now resolves the seeded LOBVM (was asserted
  null); + a memoisation guard (resolve() returns the same instance).
- ContractShapeTest: the day-level office block is now non-null on the shipped corpus —
  pin its key-set (content byte-frozen by the golden fixture); the synthetic shape pin stays.
- DayContractTest: pin the rest of the contract exactly with the office removed, then pin the
  office block shape + the first antiphon's Latin as a fast drift check.
The base 1962 corpus now declares the Little Office (Stage 4b), so two tests that
asserted the pre-seed empty state flip:
- CorpusTest: officeSlugs is ['little-office-bvm'] and the structure + proper-text
  stores are non-empty (was asserted empty).
- CorpusOfficeTextProviderTest: proper() resolves antiphon#1 bilingually from the
  store (was asserted to fail closed); pin its Latin + source as a drift check.
chore(golden): re-freeze the golden year for the LOBVM Lauds seed
All checks were successful
ci / check (7.4) (pull_request) Successful in 1m29s
ci / check (8.1) (pull_request) Successful in 1m19s
ci / check (8.2) (pull_request) Successful in 1m25s
ci / check (8.3) (pull_request) Successful in 1m18s
ci / validate (1954) (pull_request) Successful in 28s
ci / validate (1955) (pull_request) Successful in 28s
ci / validate (1962) (pull_request) Successful in 28s
ci / validate (novus-ordo-2002) (pull_request) Successful in 28s
ci / validate (overlays) (pull_request) Successful in 29s
ci / corpus-verify (pull_request) Successful in 12s
CodeQL / Analyze (actions) (pull_request) Has been skipped
pr-title / lint (pull_request) Successful in 1s
project-status-rollup / rollup (pull_request) Has been skipped
a7476f5c35
The first reviewed golden-digest move: the base 1962 corpus now resolves the per-annum Little Office Lauds on every day (1584-2200). goldenDigest e64d2dfc->33a2f471, corpusContentDigest 40ee75cb->72f2d035, corpusVersion 1962-2026-07-18.4->1962-2026-07-19.1. Corpus-driven (MANIFEST delta), so engineVersion stays 0.5.0; the freeze accepted the move under the GoldenProvenanceLock.
fix(provenance): enforce a scan locator on text-leaf citations (§4.4)
All checks were successful
ci / check (7.4) (pull_request) Successful in 1m27s
ci / check (8.1) (pull_request) Successful in 1m18s
ci / check (8.2) (pull_request) Successful in 1m17s
ci / check (8.3) (pull_request) Successful in 1m18s
ci / validate (1954) (pull_request) Successful in 29s
ci / validate (1955) (pull_request) Successful in 40s
ci / validate (1962) (pull_request) Successful in 31s
ci / validate (novus-ordo-2002) (pull_request) Successful in 29s
ci / validate (overlays) (pull_request) Successful in 29s
CodeQL / Analyze (actions) (pull_request) Has been skipped
ci / corpus-verify (pull_request) Successful in 12s
pr-title / lint (pull_request) Successful in 1s
project-status-rollup / rollup (pull_request) Has been skipped
d073227b01
An adversarial review found the born-cited gate verified a proper leaf's `value`
cited a public-domain `text` source but NOT the "with a locator" half — it was
convention-only, though office-proper-text.schema.json's description and build.mjs's
comments both claimed the gate enforced it. The verse path already hard-throws on a
missing locator (ingest.mjs §4.4); the office proper-text emit, newly wired through
checkProvenance in this branch, did not.

The gate now requires every value/incipit citation to carry a ':locator', so a future
locator-less proper cite fails the build instead of silently shipping unlocated sacred
text. All 20 seeded LOBVM leaves already carry page locators (p.160–p.193), so the
corpus and golden digest are unmoved. Schema description updated to state the
enforcement is live; + a generator test for the locator-less case.
manlycucumber deleted branch feat/stage4b-lobvm-lauds-seed 2026-07-20 02:30:40 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Directorium/Core!504
No description provided.