feat: regression safety for multi-system support (#72) #457

Merged
manlycucumber merged 3 commits from feature/72-multi-system-regression-safety into develop 2026-07-06 21:01:30 +00:00
manlycucumber commented 2026-07-06 20:59:39 +00:00 (Migrated from github.com)

Closes Epic #72 (regression safety for multi-system support) and its children #73, #74, plus #332 (per-edition precedence & commemoration limits, under #59) — the Core v0.3 wrap that lets new editions land without regressing 1962.

#332 — per-edition commemoration limit in the contract

The contract's top-level commemorationLimit was a hardcoded 1960 static (I/II 1, III/IV 2) — correct for 1962, but wrong for 1954 (three per class) and 1955 (0/1/2 by class, a first-class day admitting none). It is now the per-edition class cap:

  • New PrecedenceRules::commemorationClassLimit() — the unguarded, class-level cap — distinct from the day-specific commemorationLimit() (which still returns 0 on the Triduum, privileged octaves, and first-class vigils, and still drives the selector + resolution trace).
  • The resolver stamps it onto LiturgicalDay; DayContract reads it.
  • The 1960 precedence data equals the old static, so the 1962 golden fixture is byte-identical; 1954 and 1955 now report their own caps.
  • The dead Calendar\CommemorationLimit static (and its test) are removed — the per-edition data is now the single source of truth. PrecedenceTableTest pins all three editions' limits.

#74 — active rubric-system stamp

The top-level edition field already carries the active system's URN (via DayResolver::forEdition), defaults to roman:rubricae-1960, and is backward-compatible — so no new field is added (a twin would be redundant and force a golden re-freeze). It is documented as the active-system stamp in output-contract.md and locked with MultiSystemContractTest, which resolves one first-class feast (the Assumption) under 1962/1954/1955 and asserts the edition stamp and the 1 / 3 / 0 caps side by side, the safe 1962 default, and the public boundary's refusal of an unbuilt edition.

#73 — per-system CI oracle matrix

Oracle tests are tagged @group edition-1962 / edition-1954 / edition-1955 (the historical DO oracle split per edition; cross-edition witnesses carry both groups). The validate CI job is now a matrix over system with fail-fast: false, each leg an independent gate, so a regression in one edition fails only its leg and never masks another.

Verification

  • phpcs clean · PHPStan [OK] · 836 tests green (1 skipped: the live Divinum Officium maintainer oracle) · 1962 golden byte-identical · each edition-<system> oracle group green.
  • No data/ or tools/generator/ changes.
  • 4-dimension adversarial review (find → refute): 2 findings confirmed (stale Calendar\CommemorationLimit references in two design docs) and fixed; 2 refuted.

Maintainer note

The main protection ruleset currently requires a status check named validate; this renames it to validate (1962) / validate (1954) / validate (1955). The ruleset's required-check contexts are realigned to match.

Closes Epic #72 (regression safety for multi-system support) and its children #73, #74, plus #332 (per-edition precedence & commemoration limits, under #59) — the Core v0.3 wrap that lets new editions land without regressing 1962. ## #332 — per-edition commemoration limit in the contract The contract's top-level `commemorationLimit` was a hardcoded 1960 static (I/II 1, III/IV 2) — correct for 1962, but wrong for 1954 (three per class) and 1955 (0/1/2 by class, a first-class day admitting none). It is now the **per-edition class cap**: - New `PrecedenceRules::commemorationClassLimit()` — the unguarded, class-level cap — distinct from the day-specific `commemorationLimit()` (which still returns 0 on the Triduum, privileged octaves, and first-class vigils, and still drives the selector + resolution trace). - The resolver stamps it onto `LiturgicalDay`; `DayContract` reads it. - The 1960 precedence data equals the old static, so **the 1962 golden fixture is byte-identical**; 1954 and 1955 now report their own caps. - The dead `Calendar\CommemorationLimit` static (and its test) are removed — the per-edition data is now the single source of truth. `PrecedenceTableTest` pins all three editions' limits. ## #74 — active rubric-system stamp The top-level `edition` field already carries the active system's URN (via `DayResolver::forEdition`), defaults to `roman:rubricae-1960`, and is backward-compatible — so **no new field is added** (a twin would be redundant and force a golden re-freeze). It is documented as the active-system stamp in `output-contract.md` and locked with `MultiSystemContractTest`, which resolves one first-class feast (the Assumption) under 1962/1954/1955 and asserts the edition stamp and the `1 / 3 / 0` caps side by side, the safe 1962 default, and the public boundary's refusal of an unbuilt edition. ## #73 — per-system CI oracle matrix Oracle tests are tagged `@group edition-1962 / edition-1954 / edition-1955` (the historical DO oracle split per edition; cross-edition witnesses carry both groups). The `validate` CI job is now a **matrix over system with `fail-fast: false`**, each leg an independent gate, so a regression in one edition fails only its leg and never masks another. ## Verification - `phpcs` clean · PHPStan `[OK]` · **836 tests green** (1 skipped: the live Divinum Officium maintainer oracle) · **1962 golden byte-identical** · each `edition-<system>` oracle group green. - No `data/` or `tools/generator/` changes. - 4-dimension adversarial review (find → refute): 2 findings confirmed (stale `Calendar\CommemorationLimit` references in two design docs) and fixed; 2 refuted. ## Maintainer note The `main protection` ruleset currently requires a status check named `validate`; this renames it to `validate (1962)` / `validate (1954)` / `validate (1955)`. The ruleset's required-check contexts are realigned to match.
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!457
No description provided.