From bd7a41b5b83a5fc18e6169327c9176eee2d54a2b Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Thu, 13 Aug 2026 08:07:17 +0200 Subject: test(oracle): compare the observed day's identity, not only rank/colour Layer 4 (test_oracle.ml, vs missalemeum) compared the observed celebration's rank and colour and stopped there -- it never checked whether the observed day is actually the RIGHT one. Holy Family (11 January 2026: oracle title "The Holy Family: Jesus, Mary & Joseph") is rank 2/white on both sides purely by coincidence -- an ordinary, unnamed Sunday and Holy Family share both -- so this layer has been silently blind to the missing feast since it was built. Adds Observed_identity_mismatch/Observed_identity_unresolved, following the shape and honesty of yesterday's commemoration-identity comparison (Comm_identity_mismatch/Comm_identity_unresolved): colitur identifies its own observed day by slug, missalemeum by an English title, and the bridge is Celebration.t.names, read directly off LD.observed. That name is only ever set for a SANCTORAL-origin celebration -- a TEMPORAL-origin one (an ordinary Sunday, a feria, a movable named feast) carries none, so most of the 730 days in the fixture window land in Observed_identity_unresolved, not a clean match. That is counted and reported, never silently skipped -- the same discipline this file already applies to an unresolvable commemoration. WIP, expected red: 399 days now show observed-identity-unresolved with nothing in the allow-list naming it, and five of the six existing M-series entries whose own diff shape is checked by exact list equality (M1, M2, M3, M13, M16) lose their match now that this axis widens their diff set. Landed on its own, ahead of any production fix, so it is the regression net the following commits are checked against rather than a change bundled in alongside the fix they enable. The next commits widen those five entries' own diff shapes and add the new bucket's own cited allow-list entry, then build Holy Family itself. --- test/test_oracle.ml | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 2 deletions(-) (limited to 'test/test_oracle.ml') diff --git a/test/test_oracle.ml b/test/test_oracle.ml index a86fd76..ae5c396 100644 --- a/test/test_oracle.ml +++ b/test/test_oracle.ml @@ -3,6 +3,20 @@ layer 4 of the design spec's five (colitur CLAUDE.md "Validation" section; layer 3, the lectio differential, is test_differential.ml, already green). + *** WIP, 2026-08-13, branch ef-rg112-rg110: this file is EXPECTED RED + right now. [Observed_identity_mismatch]/[Observed_identity_unresolved] + (below) are new -- this layer used to compare the observed day's rank and + colour and stop there, never whether it is actually the RIGHT day (Holy + Family, 11 January 2026, is rank 2/white on both sides purely by + coincidence -- an ordinary unnamed Sunday and Holy Family share both). + Landing the strengthened comparator FIRST, red, before touching any + production code, is the point: it is the regression net the rest of this + task's fix commits are checked against, not a change bundled in alongside + the fix. The very next commits make it green again (data/ef/expected- + divergences-missalemeum.sexp's own M-series entries below, and this + file's own [layer_m_reason] widening) -- see that file and + docs/research/rules-register.md for the running account. + *** WHY THIS LAYER EXISTS, AND WHAT IT CAN DO THAT THE LECTIO DIFFERENTIAL CANNOT (test_differential.ml's own header comment, limit 1) *** @@ -280,6 +294,15 @@ type colitur_row = { c_date : string; c_rank : int; c_colour : char; + c_observed_slug : string; + c_observed_name : string option; + (** colitur's own resolved English name for the OBSERVED celebration -- + [None] for a TEMPORAL-origin day (an ordinary Sunday, a feria, a + movable named feast: {!Rite_ef.Temporal_ef}'s own [build] never + sets [names], the SAME gap this file's header already documents + for commemorations, see [Comm_identity_unresolved]), the same + [Names.find ... en] read [c_commemorations] below already uses, + applied to [LD.observed] instead of a commemoration candidate. *) c_commemorations : (string * int * Cel.status * string option) list; (** slug, rank, status, English name *) } @@ -321,6 +344,8 @@ let colitur_rows_2026_2027 () = { c_date = Date.to_iso8601 day.LD.date; c_rank = rank_to_int cel.Cel.rank; c_colour = colour_to_char cel.Cel.colour; + c_observed_slug = Slug.to_string cel.Cel.slug; + c_observed_name = Names.find cel.Cel.names en; c_commemorations = commemorations } :: !rows @@ -335,7 +360,15 @@ let colitur_rows_2026_2027 () = (* header for the mapping and its exact limits. *) (* ---------------------------------------------------------------------- *) -type field = Rank | Colour_f | Comm_presence | Comm_count | Comm_identity_mismatch | Comm_identity_unresolved +type field = + | Rank + | Colour_f + | Comm_presence + | Comm_count + | Comm_identity_mismatch + | Comm_identity_unresolved + | Observed_identity_mismatch + | Observed_identity_unresolved let field_name = function | Rank -> "rank" @@ -344,6 +377,40 @@ let field_name = function | Comm_count -> "commemoration-count" | Comm_identity_mismatch -> "commemoration-identity-mismatch" | Comm_identity_unresolved -> "commemoration-identity-unresolved" + | Observed_identity_mismatch -> "observed-identity-mismatch" + | Observed_identity_unresolved -> "observed-identity-unresolved" + +(* CORRECTING the gap this file's own header names (added under this task, + branch ef-rg112-rg110): the axes above all compare the day's OBSERVED + celebration's rank and colour ([Rank]/[Colour_f]) and the admitted + COMMEMORATIONS' identity ([Comm_identity_*]) -- nothing until now compared + the OBSERVED celebration's own identity, WHICH day is actually being kept, + only what class/colour it happens to carry. Holy Family (11 January 2026: + oracle title "The Holy Family: Jesus, Mary & Joseph", colitur's own + observed slug the plain "ef-time-after-epiphany-sunday-1", BOTH rank 2 and + colour white on both sides) is exactly the shape this axis exists to + catch -- rank and colour already agreed by coincidence (an ordinary, + unnamed II-class Sunday and Holy Family share both), so nothing above + ever saw a difference. Same mapping and the same honesty about its limit + as [identity_diff] just above: colitur identifies a day by SLUG, + missalemeum by an English TITLE string, and the bridge is + {!Celebration.t}.names, read directly off [LD.observed] by + [colitur_rows_2026_2027] into [c_observed_name] -- resolvable for a + SANCTORAL-origin observed day (a saint's feast winning the day outright), + NEVER for a TEMPORAL-origin one (an ordinary Sunday, a feria, a movable + named feast -- {!Rite_ef.Temporal_ef}'s own [build] never sets [names]). + [Observed_identity_unresolved] is that TEMPORAL-origin case: counted and + reported, never silently skipped, the same discipline + [Comm_identity_unresolved] already established and the brief demands + again here ("a day whose observed identity cannot be resolved must be a + counted, allow-listed outcome, never a silent skip"). Unlike commemoration + identity, this axis needs no presence/count gate first -- there is always + exactly one observed day on each side, no list-length ambiguity to + resolve before a title comparison is even meaningful. *) +let observed_identity_diff (c : colitur_row) (o : oracle_row) = + match c.c_observed_name with + | None -> Some Observed_identity_unresolved + | Some name -> if String.equal name o.o_title then None else Some Observed_identity_mismatch (* Identity (Task B): only even attempted once presence AND count already agree (both non-empty, same length) -- see this file's own header, @@ -378,7 +445,8 @@ let diff_fields (c : colitur_row) (o : oracle_row) = (if c_has && o_has && List.length c.c_commemorations <> List.length o.o_commemorations then Some Comm_count else None); - identity_diff c o + identity_diff c o; + observed_identity_diff c o ] (* ---------------------------------------------------------------------- *) -- cgit v1.3 From e5cafdd222a05032ae4f3c0ea07fe0eeda47c145 Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Thu, 13 Aug 2026 08:21:39 +0200 Subject: test(oracle): green -- widen five entries, add M18 for observed identity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit layer_m_reason's own five entries whose diff shape was checked by EXACT list equality (M1, M2, M3, M13, M16) lost their match once the observed-identity axis widened every row's diff set two commits ago. Each is widened the same way commemoration-identity was already widened for M13 once before: the axis now ALSO fires alongside their existing citation, same root cause, same id -- M1/M3 (temporal-origin observed day, no name), M2 (same, plus five Paschaltide dates that used to match cleanly and now surface for the first time), M13 (Joseph, sanctoral, now a genuine title MISMATCH, not merely unresolved), M16 (the temporal Passiontide feria observed there). M18 covers the remainder: 373 of 730 days whose observed celebration is temporal-origin and so carries no English name at all -- the overwhelming majority of the window, including Holy Family's own two dates in it (2026-01-11, 2027-01-10), counted here rather than shown as a false Matched. Gated on the diff SHAPE alone (this file's own convention for a population this size, the same reasoning M2's own title-substring predicate already uses instead of a date list), verdict unresolvable -- the same fourth verdict M15 already established for the identical limit on the commemoration axis. Building an English name onto the temporal cycle is Plan 4's own scope, register §6. Teeth, per the brief: temporarily corrupted hilary's own English name in data/ef/sanctoral.sexp (14 January 2026, an OBSERVED sanctoral day, rank and colour left untouched) and reran. Reddened immediately: 2026-01-14: observed-identity-mismatch differ -- colitur=(rank=3 colour=w ...) oracle=(rank=3 colours=[w] title="St. Hilary" ...) rank and colour agree on both sides -- exactly the shape the pre-strengthening comparator would have slept through completely. Reverted (git diff on the data file is empty) before this commit. --- data/ef/expected-divergences-missalemeum.sexp | 22 ++++++- test/test_oracle.ml | 83 +++++++++++++++++++++++---- 2 files changed, 92 insertions(+), 13 deletions(-) (limited to 'test/test_oracle.ml') diff --git a/data/ef/expected-divergences-missalemeum.sexp b/data/ef/expected-divergences-missalemeum.sexp index 9cff236..d7a1c8c 100644 --- a/data/ef/expected-divergences-missalemeum.sexp +++ b/data/ef/expected-divergences-missalemeum.sexp @@ -35,6 +35,19 @@ ; primary-source effort, not defaulted past -- see each entry's own note ; and the task report for the full search. ; +; M18 -- ef-rg112-rg110 task (Holy Family, RG 17(b)): test_oracle.ml's own +; comparator was strengthened to compare the OBSERVED day's identity, not +; only its rank and colour (the gap that let the missing Holy Family go +; unnoticed by this layer for as long as it did -- rank 2/white on both +; sides purely by coincidence). [M18] is [verdict unresolvable], the SAME +; fourth verdict as M15, for the SAME reason: colitur's own resolved name is +; [None] for any TEMPORAL-origin observed day, the large majority of this +; window. M1, M2, M3, M13 and M16's own subsets were each widened to absorb +; this axis where it fires alongside their pre-existing citation (see each +; entry's own note) rather than being folded into M18's own count, which +; covers only rows where [Observed_identity_unresolved] is the SOLE +; disagreement. +; ; FIVE MORE REMOVED, ef-rebootstrap (2026-08-12), same shape as M17's own ; removal above -- not re-adjudicated to a different verdict, just gone, ; because the underlying divergence stopped occurring: M4 (second @@ -102,8 +115,8 @@ ((id M2) (citation "RG 91 entry 27 (\"Officium sanctae Mariae in sabbato\") -- register §4") (verdict missalemeum) - (note "Every otherwise-unoccupied IV-class Saturday should carry the votive Office of the BVM (white; missalemeum's own titles cycle \"I\"..\"V Mass of the B. V. M. -- Salve, Sancta Parens\"). precedence_ef.ml's [band] already has an entry-27 comment acknowledging this row exists (corrected attribution, final fix wave: this note previously said temporal_ef.ml, but [band] is precedence_ef.ml's own function), but [Temporal_ef.temporal] never actually CONSTRUCTS this office -- an unimpeded Time-after-Epiphany/-Pentecost Saturday gets a bare ferial slug and season green instead. A genuine feature gap, not a citation dispute; tracked in register §6, not built in this task.") - (expected_rows 17)) + (note "Every otherwise-unoccupied IV-class Saturday should carry the votive Office of the BVM (white; missalemeum's own titles cycle \"I\"..\"V Mass of the B. V. M. -- Salve, Sancta Parens\"). precedence_ef.ml's [band] already has an entry-27 comment acknowledging this row exists (corrected attribution, final fix wave: this note previously said temporal_ef.ml, but [band] is precedence_ef.ml's own function), but [Temporal_ef.temporal] never actually CONSTRUCTS this office -- an unimpeded Time-after-Epiphany/-Pentecost Saturday gets a bare ferial slug and season green instead. A genuine feature gap, not a citation dispute; tracked in register §6, not built in this task. REFRESHED (ef-rg112-rg110 task): expected_rows moved 17 -> 22, for the SAME reason M1/M3/M16 below move too -- the new observed-identity axis (test_oracle.ml's own header) now ALSO fires on 5 Paschaltide BVM-Saturday dates that used to match CLEANLY (colour already white there, RG 119, so the pre-existing [Colour_f]-only check never even reached these 5 rows; M2's own comment already noted \"every Paschaltide occurrence... never reaches this predicate at all\" -- no longer true once [Observed_identity_unresolved] alone is enough to reach it). Not a new divergence, the identical feature gap surfacing on a second axis -- derived from the comparator's own failure output (test_layer_m_counts_match_citations), not hand-counted.") + (expected_rows 22)) ((id M3) (citation "RG 87 (Minor Litanies/Rogations, Mon/Tue before Ascension) -- the SAME citation as the lectio allow-list's own C8 (data/ef/expected-divergences.sexp)") (verdict colitur) @@ -149,3 +162,8 @@ (verdict missalemeum) (note "27 March 2026 (Friday of Passion Week): colitur admits \"St. John Damascene\" (a real Class3 universal feast that genuinely falls that day, data/ef/sanctoral.sexp's own john-damascene entry, 27 March); missalemeum shows \"For Our Lady of the Seven Sorrows\" instead. The SAME standing gap M13 already names: the Seven-Sorrows-of-Passion-Friday commemoration is confirmed real in the primary text but not implemented anywhere in temporal_ef.ml, so colitur has no candidate for it and falls back to whichever ordinary Class3 saint actually wins that Friday. Verdict missalemeum: colitur is missing a real, primary-cited office, not differently opinioned. Register §6 open item (the same one M13's own note already tracks). Only 2026 shows here as an IDENTITY mismatch -- 2027's Friday of Passion Week IS 19 March, M13's own date, where identity comparison cannot even be reached (M13's own rank/colour mismatch already excludes that day from count-matched identity comparison, folded into M13's own widened subset above). NOTE for whoever builds the office (fix round 1, coordinator finding 7): 27 March 2026 is a III-class day, where RG 111(d) admits TWO commemorations -- yet missalemeum admits only the Seven Sorrows and DISPLACES John Damascene entirely, not merely drops him to second place. A naive implementation (one more ordinary III-class commemoration competing for the day's two slots) would give colitur two commemorations where missalemeum shows one; whatever privilege the Seven Sorrows carries must itself explain the exclusion, not just win admission.") (expected_rows 1)) + ((id M18) + (citation "test_oracle.ml's own header, \"COMMEMORATION IDENTITY\" section, restated for the OBSERVED axis (ef-rg112-rg110 task) -- not an RG citation, a comparator LIMIT: Rite_ef.Temporal_ef never sets an English [names] field on any candidate it builds, the SAME limit M15 already names for a commemoration, now visible for the OBSERVED day itself now that the observed-identity axis exists") + (verdict unresolvable) + (note "373 of the 730 days in this window -- the overwhelming majority of days whose observed celebration is TEMPORAL-origin (an ordinary Sunday, a feria, a movable named feast including Holy Family itself), which never carries an English name on colitur's side. Not a rubric dispute and not a data gap either engine is wrong about -- a LIMIT of this comparator, honestly counted rather than silently passed, per the brief's own explicit instruction (\"a day whose observed identity cannot be resolved must be a counted, allow-listed outcome, never a silent skip\"). Gated on the diff SHAPE alone (exactly, and only, observed-identity-unresolved), not a literal date list the way every other entry in this file is -- at this population size a date list would itself be the \"pattern that could silently widen\" this file's own header warns against, for the opposite reason a date RANGE is normally risky: the predicate (colitur's own resolved name is [None]) is the precise, falsifiable evidence, the same shape M2's own title-substring predicate already uses instead of enumerating dates, just keyed on presence-of-a-name rather than a title string. Building an English name onto every temporal-cycle candidate (a data/lectionary-bootstrap task, Plan 4, out of this task's scope -- register §6, the same open item M15's own note already tracks) would resolve this entry to Matched for every one of these days it does not instead expose as a REAL divergence (Holy Family's own two dates in this window, 2026-01-11 and 2027-01-10, are counted here, not separately -- the name comparison genuinely cannot distinguish \"Holy Family\" from any other unnamed Sunday, so proving Holy Family's own correctness rests on the golden pins and the differential's own C15, not this axis). Derived directly from the comparator's own failure output (test_layer_m_counts_match_citations), not hand-counted first and cross-checked after: 373.") + (expected_rows 373)) diff --git a/test/test_oracle.ml b/test/test_oracle.ml index ae5c396..7068af9 100644 --- a/test/test_oracle.ml +++ b/test/test_oracle.ml @@ -726,25 +726,86 @@ let m16_dates = [ "2026-03-27" ] entry needed at all -- removed, not re-adjudicated to a different verdict, since there is no longer a divergence to name. *) +(* M18 -- ef-rg112-rg110 task: the gap this file's own header now names under + "COMMEMORATION IDENTITY", restated for the OBSERVED axis. colitur's own + observed celebration carries an English name ({!Celebration.t}.names) + ONLY for a SANCTORAL-origin day (a saint's feast winning outright); a + TEMPORAL-origin one -- an ordinary Sunday, a feria, a movable named feast + including Holy Family itself -- never does ({!Rite_ef.Temporal_ef}'s own + [build] has no [en "..."] string anywhere in it, this file's own header + again). That is the OVERWHELMING majority of days in any calendar (saints' + feasts are common, but far from every day), so this is a single + STRUCTURAL predicate -- [Observed_identity_unresolved] alone, nothing + else disagreeing -- not a literal date list the way every other entry in + this file is: at 374 of the 730 days in this window, a list would be + exactly the "range/pattern that could silently widen" this file's own + header explicitly avoids elsewhere, for the OPPOSITE reason a range is + normally risky here -- the predicate itself (colitur's own name is + [None]) is the precise, falsifiable evidence, the same shape [M2]'s own + title-substring predicate already uses instead of a date list, just keyed + on presence-of-a-name rather than a title string. NOT a rubric dispute + and NOT a data gap either engine is wrong about -- a LIMIT of this + comparator itself, honestly counted rather than silently passed, per the + brief's own explicit instruction ("a day whose observed identity cannot + be resolved must be a counted, allow-listed outcome, never a silent + skip"), the SAME discipline [M15] already established for an unresolvable + COMMEMORATION, applied here for the first time to the OBSERVED day. Fixing + this at the root (giving every temporal-cycle candidate an English name) + is a data/lectionary-bootstrap task (Plan 4), not something this + comparator can do for itself -- register §6 tracks it, the same open item + [M15]'s own note already points to. *) let layer_m_reason (c : colitur_row) (o : oracle_row) diffs = if diffs = [] then None - else if List.mem c.c_date m1_dates && subset diffs [ Rank; Colour_f; Comm_presence ] then Some "M1" - else if contains_substring o.o_title ~needle:bvm_saturday_title && diffs = [ Colour_f ] then Some "M2" - else if List.mem c.c_date m3_dates && diffs = [ Colour_f ] then Some "M3" + (* M1's own subset widened (this task): colitur's observed day on both + dates is TEMPORAL-origin (the Sunday itself, RG33's own point -- the + vigil should never have displaced it), so it now ALSO carries + [Observed_identity_unresolved] -- the same root cause the file's other + widened entries below share, restated for this one's own shape. *) + else if List.mem c.c_date m1_dates + && subset diffs [ Rank; Colour_f; Comm_presence; Observed_identity_unresolved ] + then Some "M1" + (* M2's own subset widened (this task): every BVM-Saturday date's observed + celebration is temporal-origin (the ordinary Saturday feria colitur + computes, not the distinct Office of the BVM missalemeum's title + names -- M2's own gap), so [Observed_identity_unresolved] now fires + alongside [Colour_f] where it used to fire alone, AND alone on its own + on every Paschaltide occurrence M2's own comment already says never + reached this predicate before (colour already agrees there; only the + new axis has anything left to say). *) + else if contains_substring o.o_title ~needle:bvm_saturday_title + && subset diffs [ Colour_f; Observed_identity_unresolved ] + then Some "M2" + (* M3's own subset widened (this task): the Rogation Monday feria colitur + observes is temporal-origin, same root cause as M1/M2 above. *) + else if List.mem c.c_date m3_dates && subset diffs [ Colour_f; Observed_identity_unresolved ] then + Some "M3" else if List.mem c.c_date m5_dates && diffs = [ Comm_presence ] then Some "M5" else if List.mem c.c_date m8_dates && diffs = [ Comm_presence ] then Some "M8" else if List.mem c.c_date m10_dates && diffs = [ Comm_presence ] then Some "M10" else if List.mem c.c_date m11_dates && diffs = [ Comm_presence ] then Some "M11" else if List.mem c.c_date m12_dates && diffs = [ Comm_count ] then Some "M12" - (* M13's own subset widened (Task B): the same underlying gap (the Seven - Sorrows commemoration is never constructed at all) now ALSO surfaces - as [Comm_identity_unresolved] on this date, since colitur's own - admitted commemoration there is temporal-origin (no English name) -- - one root cause, one id, both axes it touches. *) - else if List.mem c.c_date m13_dates && subset diffs [ Rank; Colour_f; Comm_identity_unresolved ] then - Some "M13" + (* M13's own subset widened AGAIN (this task, following the SAME pattern + Task B's own widening comment below records): colitur's observed + celebration here is Joseph, SANCTORAL-origin, so its name IS + resolvable -- and disagrees with missalemeum's own title outright + ([Observed_identity_mismatch], not [_unresolved]), the SAME underlying + gap (the Seven Sorrows commemoration is never constructed, so colitur + has no candidate to observe OR commemorate in its place) now visible + on a THIRD axis. *) + else if List.mem c.c_date m13_dates + && subset diffs [ Rank; Colour_f; Comm_identity_unresolved; Observed_identity_mismatch ] + then Some "M13" else if List.mem c.c_date m15_dates && diffs = [ Comm_identity_unresolved ] then Some "M15" - else if List.mem c.c_date m16_dates && diffs = [ Comm_identity_mismatch ] then Some "M16" + (* M16's own subset widened (this task): colitur's observed celebration + here is the temporal Passiontide feria (John Damascene is only a + COMMEMORATION on this Feast-status-losing day -- band picks the + temporal candidate outright at Class3 rank, register's own account of + this date), so [Observed_identity_unresolved] now fires alongside the + pre-existing [Comm_identity_mismatch]. *) + else if List.mem c.c_date m16_dates + && subset diffs [ Comm_identity_mismatch; Observed_identity_unresolved ] + then Some "M16" + else if diffs = [ Observed_identity_unresolved ] then Some "M18" else None (* ---------------------------------------------------------------------- *) -- cgit v1.3 From 9df26e2e91646925c5977d4bab22c0d510d3b3c1 Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Thu, 13 Aug 2026 09:22:36 +0200 Subject: test(oracle): fix round 1 (F8) -- correct the M18 headline, name the blind spot precisely The full breakdown, precisely: of 730 days, 331 are resolved (330 matching, 1 mismatched -- M13, Joseph vs the Seven Sorrows), 399 unresolved -- 373 in M18's own bucket, 26 absorbed inside M1/M2/M3/ M16's own widened subsets where the axis fires alongside their pre-existing citation. M18's own note previously stated only its own 373 without this fuller context. Also names the blind spot the axis exists to close in exact terms, not just "the observed day's identity": a TEMPORAL-origin observed day silently replaced by a DIFFERENT temporal-origin observed day of the SAME rank and colour -- exactly Holy Family's own shape, and the reason every prior layer stayed green through the whole gap. States explicitly what the count pin does and does not prove: it guards against the 373/399 population moving silently in either direction, but cannot say which specific day moved or why. --- CLAUDE.md | 8 ++++- data/ef/expected-divergences-missalemeum.sexp | 2 +- test/test_oracle.ml | 52 ++++++++++++++++----------- 3 files changed, 40 insertions(+), 22 deletions(-) (limited to 'test/test_oracle.ml') diff --git a/CLAUDE.md b/CLAUDE.md index 20f3ebf..8915bc6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -124,7 +124,13 @@ against missalemeum; layer 5 pins ~30 dates. fix below: resolvable only for a SANCTORAL-origin observed day; a TEMPORAL-origin one (an ordinary Sunday, a feria, a movable named feast — 373 of 730 days in the 2026–2027 window) is `Observed_identity_unresolved`, - counted and allow-listed (`M18`), never silently skipped. Teeth proved and + counted and allow-listed (`M18`), never silently skipped. Full breakdown: + 331 of 730 days resolved (330 matching, 1 mismatched — Joseph vs the Seven + Sorrows, `M13`), 399 unresolved (373 in `M18`, 26 absorbed inside four + other entries' own widened subsets). The blind spot, precisely: a + TEMPORAL-origin observed day silently replaced by a DIFFERENT + temporal-origin observed day of the SAME rank and colour — exactly Holy + Family's own shape. Teeth proved and reverted: corrupting one sanctoral saint's own English name on an OBSERVED day (rank/colour untouched) reddened the suite immediately with an `observed-identity-mismatch` and nothing else — the exact shape the diff --git a/data/ef/expected-divergences-missalemeum.sexp b/data/ef/expected-divergences-missalemeum.sexp index d7a1c8c..15f9cf2 100644 --- a/data/ef/expected-divergences-missalemeum.sexp +++ b/data/ef/expected-divergences-missalemeum.sexp @@ -165,5 +165,5 @@ ((id M18) (citation "test_oracle.ml's own header, \"COMMEMORATION IDENTITY\" section, restated for the OBSERVED axis (ef-rg112-rg110 task) -- not an RG citation, a comparator LIMIT: Rite_ef.Temporal_ef never sets an English [names] field on any candidate it builds, the SAME limit M15 already names for a commemoration, now visible for the OBSERVED day itself now that the observed-identity axis exists") (verdict unresolvable) - (note "373 of the 730 days in this window -- the overwhelming majority of days whose observed celebration is TEMPORAL-origin (an ordinary Sunday, a feria, a movable named feast including Holy Family itself), which never carries an English name on colitur's side. Not a rubric dispute and not a data gap either engine is wrong about -- a LIMIT of this comparator, honestly counted rather than silently passed, per the brief's own explicit instruction (\"a day whose observed identity cannot be resolved must be a counted, allow-listed outcome, never a silent skip\"). Gated on the diff SHAPE alone (exactly, and only, observed-identity-unresolved), not a literal date list the way every other entry in this file is -- at this population size a date list would itself be the \"pattern that could silently widen\" this file's own header warns against, for the opposite reason a date RANGE is normally risky: the predicate (colitur's own resolved name is [None]) is the precise, falsifiable evidence, the same shape M2's own title-substring predicate already uses instead of enumerating dates, just keyed on presence-of-a-name rather than a title string. Building an English name onto every temporal-cycle candidate (a data/lectionary-bootstrap task, Plan 4, out of this task's scope -- register §6, the same open item M15's own note already tracks) would resolve this entry to Matched for every one of these days it does not instead expose as a REAL divergence (Holy Family's own two dates in this window, 2026-01-11 and 2027-01-10, are counted here, not separately -- the name comparison genuinely cannot distinguish \"Holy Family\" from any other unnamed Sunday, so proving Holy Family's own correctness rests on the golden pins and the differential's own C15, not this axis). Derived directly from the comparator's own failure output (test_layer_m_counts_match_citations), not hand-counted first and cross-checked after: 373.") + (note "373 of the 730 days in this window carry this shape alone (M1/M2/M3/M16 absorb a further 26 where it fires ALONGSIDE their own pre-existing citation -- see each entry's own widened note; 373+26=399 is the axis's own full unresolved population). CORRECTED, fix round 1 (coordinator finding 8) -- the full breakdown, precisely: of 730 days, 331 are RESOLVED (colitur's own observed celebration carries a name) -- 330 resolved-and-MATCHING, 1 resolved-and-MISMATCHED (M13, Joseph vs the Seven Sorrows, already its own entry above) -- and 399 are UNRESOLVED (colitur's own name is [None]), split between this entry's own 373 and the 26 absorbed elsewhere. The BLIND SPOT this axis exists to close, stated precisely: a TEMPORAL-origin observed day silently replaced by a DIFFERENT temporal-origin observed day of the SAME RANK AND COLOUR -- exactly the shape that hid Holy Family from every layer before this task (rank 2/white on both sides, purely coincidental). This count PIN is what stands guard against that population growing silently in either direction: an implementation change that made MORE days temporal-origin-and-unresolvable, or fewer, changes 373 and fails the pin, even though the axis itself cannot say WHICH specific day moved or why. The overwhelming majority of days whose observed celebration is TEMPORAL-origin (an ordinary Sunday, a feria, a movable named feast including Holy Family itself) never carry an English name on colitur's side -- not a rubric dispute and not a data gap either engine is wrong about, a LIMIT of this comparator, honestly counted rather than silently passed, per the brief's own explicit instruction (\"a day whose observed identity cannot be resolved must be a counted, allow-listed outcome, never a silent skip\"). Gated on the diff SHAPE alone (exactly, and only, observed-identity-unresolved), not a literal date list the way every other entry in this file is -- at this population size a date list would itself be the \"pattern that could silently widen\" this file's own header warns against, for the opposite reason a date RANGE is normally risky: the predicate (colitur's own resolved name is [None]) is the precise, falsifiable evidence, the same shape M2's own title-substring predicate already uses instead of enumerating dates, just keyed on presence-of-a-name rather than a title string. Building an English name onto every temporal-cycle candidate (a data/lectionary-bootstrap task, Plan 4, out of this task's scope -- register §6, the same open item M15's own note already tracks) would resolve this entry to Matched for every one of these days it does not instead expose as a REAL divergence (Holy Family's own two dates in this window, 2026-01-11 and 2027-01-10, are counted here, not separately -- the name comparison genuinely cannot distinguish \"Holy Family\" from any other unnamed Sunday, so proving Holy Family's own correctness rests on the golden pins and the differential's own C15, not this axis). Derived directly from the comparator's own failure output (test_layer_m_counts_match_citations), not hand-counted first and cross-checked after: 373.") (expected_rows 373)) diff --git a/test/test_oracle.ml b/test/test_oracle.ml index 7068af9..35d4501 100644 --- a/test/test_oracle.ml +++ b/test/test_oracle.ml @@ -733,26 +733,38 @@ let m16_dates = [ "2026-03-27" ] TEMPORAL-origin one -- an ordinary Sunday, a feria, a movable named feast including Holy Family itself -- never does ({!Rite_ef.Temporal_ef}'s own [build] has no [en "..."] string anywhere in it, this file's own header - again). That is the OVERWHELMING majority of days in any calendar (saints' - feasts are common, but far from every day), so this is a single - STRUCTURAL predicate -- [Observed_identity_unresolved] alone, nothing - else disagreeing -- not a literal date list the way every other entry in - this file is: at 374 of the 730 days in this window, a list would be - exactly the "range/pattern that could silently widen" this file's own - header explicitly avoids elsewhere, for the OPPOSITE reason a range is - normally risky here -- the predicate itself (colitur's own name is - [None]) is the precise, falsifiable evidence, the same shape [M2]'s own - title-substring predicate already uses instead of a date list, just keyed - on presence-of-a-name rather than a title string. NOT a rubric dispute - and NOT a data gap either engine is wrong about -- a LIMIT of this - comparator itself, honestly counted rather than silently passed, per the - brief's own explicit instruction ("a day whose observed identity cannot - be resolved must be a counted, allow-listed outcome, never a silent - skip"), the SAME discipline [M15] already established for an unresolvable - COMMEMORATION, applied here for the first time to the OBSERVED day. Fixing - this at the root (giving every temporal-cycle candidate an English name) - is a data/lectionary-bootstrap task (Plan 4), not something this - comparator can do for itself -- register §6 tracks it, the same open item + again). CORRECTED, fix round 1 (coordinator finding 8) -- the precise + breakdown: of this window's 730 days, 331 are RESOLVED (330 matching, 1 + mismatched, [M13]); 399 are UNRESOLVED, split between this entry's own + 373 (the axis's sole disagreement) and 26 absorbed inside [M1]/[M2]/[M3]/ + [M16]'s own widened subsets, where it fires ALONGSIDE their pre-existing + citation. The BLIND SPOT this axis exists to close, stated precisely: a + TEMPORAL-origin observed day silently replaced by a DIFFERENT + temporal-origin observed day of the SAME RANK AND COLOUR -- exactly the + shape that hid Holy Family from every layer before this task. This is + the OVERWHELMING majority of days in any calendar (saints' feasts are + common, but far from every day), so this is a single STRUCTURAL + predicate -- [Observed_identity_unresolved] alone, nothing else + disagreeing -- not a literal date list the way every other entry in this + file is: at this population size, a list would be exactly the + "range/pattern that could silently widen" this file's own header + explicitly avoids elsewhere, for the OPPOSITE reason a range is normally + risky here -- the predicate itself (colitur's own name is [None]) is the + precise, falsifiable evidence, the same shape [M2]'s own title-substring + predicate already uses instead of a date list, just keyed on + presence-of-a-name rather than a title string. NOT a rubric dispute and + NOT a data gap either engine is wrong about -- a LIMIT of this comparator + itself, honestly counted rather than silently passed, per the brief's own + explicit instruction ("a day whose observed identity cannot be resolved + must be a counted, allow-listed outcome, never a silent skip"), the SAME + discipline [M15] already established for an unresolvable COMMEMORATION, + applied here for the first time to the OBSERVED day. The count pin below + ([test_layer_m_counts_match_citations]) is what stands guard against this + population growing (or shrinking) silently -- it cannot say WHICH day + moved or why, only that the total did. Fixing this at the root (giving + every temporal-cycle candidate an English name) is a data/lectionary- + bootstrap task (Plan 4), not something this comparator can do for itself + -- register §6 tracks it, the same open item [M15]'s own note already points to. *) let layer_m_reason (c : colitur_row) (o : oracle_row) diffs = if diffs = [] then None -- cgit v1.3