feat(office): assemble the office-structure tree into the day office key (Stage 3) #502

Merged
manlycucumber merged 1 commit from feat/stage3-office-assembler into develop 2026-07-19 05:00:57 +00:00

Stage 3 of the v1.2 Office/text layer: the DO-format resolver + PHP bilingual assembler that resolve the Stage-1 office-structure tree into the day-level office key (office-text-layer.md §4/§5; #100/#102/#104/#105/#106).

Provable zero-axis-movement. No office is seeded in the base 1962 corpus, so the office key stays null on every day and goldenDigest / corpusContentDigest / corpusVersion / engineVersion are all UNMOVED (the lock file is byte-identical; both freezes zero-diff on the workspace). Stage 4 (LOBVM) is the first digest-mover.

What lands — 10 new internal src/Office/ classes: OfficePartPathResolver (#100/#102), OfficeAssembler (#104/#105), the OfficeTextProvider seam + CorpusOfficeTextProvider, and the TextLeaf/Availability/AssembledPart/AssembledOffice value objects. Wired into DayResolver/LiturgicalDay/DayContract mirroring the fasting axis (#106). Per-string provenance is structural (no leaf without source+rights, no part without a Latin leaf, no full leaf without a value → withhold-never-leak). The office block + part-node shapes get their own ContractShapeTest pin (§5.2); the 16a withhold path is exercised end-to-end.

Deferred to Stage 4 (data, not machinery): the per-office proper-text store and the date-driven seasonal-form selection.

Validationcomposer check GREEN (phpcs + phpstan + 1237 phpunit / 145,799 assertions / 1 skip); both freeze-* scripts zero-diff; api-surface re-frozen additively for LiturgicalDay::office(). Adversarial 4-lens review (clean-room/provenance, byte-stability, correctness, spec-honesty): 0 confirmed findings; two low docblock-precision claims tightened.

Stage 3 of the v1.2 Office/text layer: the DO-format resolver + PHP bilingual assembler that resolve the Stage-1 office-structure tree into the day-level `office` key (office-text-layer.md §4/§5; #100/#102/#104/#105/#106). **Provable zero-axis-movement.** No office is seeded in the base 1962 corpus, so the `office` key stays null on every day and `goldenDigest` / `corpusContentDigest` / `corpusVersion` / `engineVersion` are all UNMOVED (the lock file is byte-identical; both freezes zero-diff on the workspace). Stage 4 (LOBVM) is the first digest-mover. **What lands** — 10 new internal `src/Office/` classes: `OfficePartPathResolver` (#100/#102), `OfficeAssembler` (#104/#105), the `OfficeTextProvider` seam + `CorpusOfficeTextProvider`, and the `TextLeaf`/`Availability`/`AssembledPart`/`AssembledOffice` value objects. Wired into `DayResolver`/`LiturgicalDay`/`DayContract` mirroring the fasting axis (#106). Per-string provenance is structural (no leaf without source+rights, no part without a Latin leaf, no full leaf without a value → withhold-never-leak). The office block + part-node shapes get their own `ContractShapeTest` pin (§5.2); the 16a withhold path is exercised end-to-end. **Deferred to Stage 4** (data, not machinery): the per-office proper-text store and the date-driven seasonal-form selection. **Validation** — `composer check` GREEN (phpcs + phpstan + 1237 phpunit / 145,799 assertions / 1 skip); both `freeze-*` scripts zero-diff; api-surface re-frozen additively for `LiturgicalDay::office()`. Adversarial 4-lens review (clean-room/provenance, byte-stability, correctness, spec-honesty): 0 confirmed findings; two low docblock-precision claims tightened.
feat(office): assemble the office-structure tree into the reserved day office key (Stage 3)
All checks were successful
ci / check (7.4) (pull_request) Successful in 1m1s
ci / check (8.1) (pull_request) Successful in 57s
ci / check (8.2) (pull_request) Successful in 58s
ci / check (8.3) (pull_request) Successful in 59s
ci / validate (1954) (pull_request) Successful in 31s
ci / validate (1955) (pull_request) Successful in 30s
ci / validate (1962) (pull_request) Successful in 32s
ci / validate (overlays) (pull_request) Successful in 30s
ci / corpus-verify (pull_request) Successful in 12s
CodeQL / Analyze (actions) (pull_request) Has been skipped
pr-title / lint (pull_request) Successful in 1s
ci / validate (novus-ordo-2002) (pull_request) Successful in 29s
project-status-rollup / rollup (pull_request) Has been skipped
fd26918057
Build the office resolver + bilingual text assembler that resolve the Stage-1
office-structure tree into the day-level `office` key (office-text-layer.md §4/§5,
#100/#102/#104/#105/#106). No office is seeded in the base 1962 corpus, so the key
stays null on every day and the golden digest is UNMOVED — a provable zero-axis
change; Stage 4 (LOBVM) is the first digest-mover.

New engine classes under src/Office/ (internal, like the Stage-1 office VOs):
  - OfficePartPathResolver (#100/#102) mints one OfficePartPath per part from the
    office identity — bare for a singleton (hour, part-type), #n-ordinalled for
    repeats; DO section keys stay interop aliases only.
  - OfficeAssembler (#104/#105) walks the structure in order and fills each part
    bilingually via an OfficeTextProvider, dispatching psalter -> psalm stream,
    scripture -> canticle stream, else the proper textRef / minted path.
  - CorpusOfficeTextProvider expands ps:<n>[:v[-v]] / <book>:<ch>[:v[-v]] from the
    Stage-2 streams, each locale joined from its own edition so the psalter #114
    divergence never misaligns them.
  - TextLeaf / Availability / AssembledPart / AssembledOffice value objects: the
    frozen §4.1 leaf {value|incipit, availability, source, rights}. Provenance is
    structural — no leaf without source+rights, no part without a Latin leaf, no full
    leaf without a value — so a missing/unlicensed source can only WITHHOLD, never leak.

Wiring (#106) mirrors the fasting axis: OfficeResolver is built in
DayResolver::forEdition, its resolve() result is a new LiturgicalDay ctor arg, and
DayContract reads $day->office()?->toArray() in place of the null literal. The office
is decided purely by the edition-declares gate (StructureAttributes::has), never by
the occurrence arrays (§3.4). The office block + part-node shapes get their own
ContractShapeTest pin (§5.2); the withhold path (16a) is exercised end-to-end.

Deferred to Stage 4 (data, not machinery): the per-office proper-text store and the
date-driven seasonal-form selection. api-surface re-frozen for LiturgicalDay::office().
manlycucumber changed title from Stage 3: resolve the office-structure tree into the day office key via a bilingual assembler to feat(office): assemble the office-structure tree into the day office key (Stage 3) 2026-07-19 05:00:15 +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!502
No description provided.