From 199d72163c8aa57f20817e5e54a4ef45dbc011c9 Mon Sep 17 00:00:00 2001 From: Justin Jia Date: Mon, 14 Sep 2026 20:16:38 -0700 Subject: [PATCH 1/2] Track the EWMA vote pre-smoothing + fence-reject carry generation (calc_v17/calc_v16): reproduce votes disclosed cast prices The four public lanes minted a new methodology generation effective 2026-09-14: EWMA vote pre-smoothing (calc.pre_smoothing_half_life_hours, 1 hour) and the fence-reject carry (calc.dynamic_weights.fence_reject_carry), stacked on attendance credit. Track the mints (annex_a_v0_2_calc_v17, annex_a2_v0_3_calc_v17, h100_sxm_v1_calc_v16, h200_sxm_v1_calc_v16) and teach both reproduce modes the new seat law without rerunning the engine's EWMA state: on an armed lane every voting receipt disclosed its exact cast price (smoothed_vote_usd), and the reproduction votes those disclosed casts while the raw print stays evidence. A fence-rejected row carrying the flat carried_vote_from disclosure votes its booked cast at the seat's current fading weight, classifies absent for attendance, and never satisfies the minimum passing panel; a voting row missing the disclosure or carrying an unusable one refuses loudly naming stamp and seat, never a raw-price fallback. Pre-smoothing generations replay byte-identically. The wire shape is pinned cross-repo: tests/fixtures/cross_repo/carried-vote-pin.observation.json is a verbatim production observation (h100_sxm_v1_calc_v16, 2026-09-14T18:30Z) exactly as the index publisher's projector published it; the reproducer lands exactly on the published 3.52045, and a nested carried_vote object is pinned REFUSED. panel_config accepts pre_smoothing_half_life_hours (a number in (0, 2]) and dynamic_weights.fence_reject_carry (strict bool, requires attendance_eta > 0); the unknown-key refusal stays armed around both. The local producer replay refuses smoothing-armed lanes outright: this mirror carries no EWMA vote state, so pricing raw votes under a smoothed methodology_id would be silently wrong. Provider-statistic parameters act upstream of the recorded prints and stay outside reproduction scope. Like the calc_v10 seating commit, this must land WITH the public corpus flip: ./reproduce --collect compares this checkout's methodology_id against the front's current pointer and refuses on mismatch. Co-Authored-By: Claude Fable 5 Signed-off-by: Justin Jia --- CHANGELOG.md | 27 + METHODOLOGY.md | 9 +- README.md | 15 +- config/index_panel_b200.json | 8 +- config/index_panel_b300.json | 8 +- config/index_panel_h100_sxm.json | 8 +- config/index_panel_h200_sxm.json | 8 +- reproduce | 10 +- src/gpu_index/index/panel.py | 57 ++ src/gpu_index/index/panel_config.py | 68 +- src/gpu_index/published/full.py | 197 +++++- src/gpu_index/published/verify.py | 132 +++- .../carried-vote-pin.observation.json | 618 ++++++++++++++++++ tests/unit/test_collect_latest_receipts.py | 8 +- .../unit/test_cross_repo_carried_vote_pin.py | 148 +++++ tests/unit/test_minute_lattice.py | 6 + tests/unit/test_panel_configs.py | 16 +- tests/unit/test_panel_engine.py | 100 +++ tests/unit/test_panel_golden_artifact.py | 2 + tests/unit/test_panel_verify_published.py | 2 + tests/unit/test_published_full.py | 184 ++++++ tests/unit/test_published_verify.py | 298 ++++++++- 22 files changed, 1873 insertions(+), 56 deletions(-) create mode 100644 tests/fixtures/cross_repo/carried-vote-pin.observation.json create mode 100644 tests/unit/test_cross_repo_carried_vote_pin.py diff --git a/CHANGELOG.md b/CHANGELOG.md index c3b7311..43d04eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,33 @@ that produced it; any parameter change touching a published day mints a new methodology_id, and prior versions stay frozen and readable under their own keyspaces. Newest first. +## 2026-09-14 + +EWMA vote pre-smoothing (half-life 1 hour) and fence-reject carry armed on +every public lane (`annex_a_v0_2_calc_v17`, `annex_a2_v0_3_calc_v17`, +`h100_sxm_v1_calc_v16`, `h200_sxm_v1_calc_v16`), effective at the production +promotion recorded in latest.json's `versions.succession`. Each voting +provider's cast price is now the time-based EWMA of its own accepted prints +(`calc_params.pre_smoothing_half_life_hours`); an outlier-rejected print's +provider re-casts its booked smoothed vote as a carried vote +(`calc_params.liveness.fence_reject_carry`). Every voting receipt disclosed its +exact cast price as `smoothed_vote_usd`; fence-reject carried votes carry a +`carried_vote_from` marker beside the unchanged rejected print. Carried votes never +satisfy the minimum passing panel. + +`./reproduce` prices each voting seat at its disclosed cast price on these +generations — it never reruns the smoothing state — and refuses loudly when a +voting receipt omits the disclosure, never falling back to the raw print. +Pre-smoothing versions re-derive byte-identically under the frozen raw-vote +rule. The local producer replay (`--producer`, `--lane`) refuses +smoothing-armed lanes outright: this mirror carries no EWMA vote state, and +pricing raw votes under a smoothed methodology_id would be silently wrong. +Provider-statistic parameters (the population floors of section 6.2 and their +`calc.statistic_params` overrides) act upstream of the recorded prints — they +shape each provider's chosen print before it is published — and are outside +the scope of reproduction from the published record, which starts from the +disclosed prints. + ## 2026-09-08 `./reproduce` now re-derives the as-published history end to end under the version diff --git a/METHODOLOGY.md b/METHODOLOGY.md index b9878f6..7617d67 100644 --- a/METHODOLOGY.md +++ b/METHODOLOGY.md @@ -332,7 +332,9 @@ Where a recorded price is known wrong by rule and the true price was never captu The index is not a weighted average. It is an interquantile mean over votes: the weighted mean of the central third of the vote mass. -Each passing provider casts its full liveness weight three times: at its price and at its price plus and minus its own standard deviation. +Each passing provider casts its full liveness weight three times: at its cast price and at its cast price plus and minus its own standard deviation. + +Since the versioned change effective 2026-09-14, a provider's cast price is the time-based exponentially weighted average of its own accepted prints (half-life 1 hour, `calc_params.pre_smoothing_half_life_hours`), not the raw print itself; carried votes (section 8.6) re-cast the provider's frozen smoothed price. Everything else published for the provider — the chosen print, the outlier-check window, the price history — stays raw: smoothing moves vote centers only. Every voting receipt disclosed its exact cast price as `smoothed_vote_usd`, so any published value remains recomputable from its own receipts without rerunning the smoothing state. ```javascript for each passing provider i: @@ -480,7 +482,7 @@ Q_i = mean of q_{i,h} over the three forwards ### 8.6 Attendance -A provider's weight also reflects whether it shows up. Each scheduled observation marks every provider: 1 if it was read successfully and produced a price (a price held out by the outlier check of section 6.4 still counts as present, since the fence keeps a print out of the index, not out of the attendance record), 0 if it was read successfully and produced none, and unchanged if our own collection or parsing failed, since a provider is never penalized for our failure. +A provider's weight also reflects whether it shows up. Each scheduled observation marks every provider: 1 if it was read successfully and produced a price (a price held out by the outlier check of section 6.4 still counts as present when the provider's own vote priced the index, since the fence keeps a print out of the index, not out of the attendance record; when the fence-reject carry below substituted the vote instead, the provider counts absent — its receipt carries a `carried_vote_from` marker), 0 if it was read successfully and produced none, and unchanged if our own collection or parsing failed, since a provider is never penalized for our failure. The attendance factor `A_i` is the exponentially weighted average of this series over the 90-day regression window, with its own attendance half-life, normalized so a provider present throughout has `A_i` = 1. A newly seated provider's scheduled observations before it joined count as 0, so its first print starts near zero; at the 6-hour half-life, sustained printing reaches full attendance in about two days. @@ -488,6 +490,7 @@ The missing print itself is handled by cause: - Our own collection or parsing failure: the provider's last accepted vote (price, vote sigma, and weight) is carried forward verbatim, and attendance is unchanged. The carried weight is the one recorded at the observation it came from, so it sits outside the weights newly allocated at the current observation, and the published weights of that observation sum to more than one by that amount. A carried price never advances the provider's own price series, so it enters neither the liveness regression nor the vote sigma, and it never counts toward the minimum passing panel. - Provider read, no usable price: attendance falls and the consecutive no-price count advances. The provider's last usable price is carried forward and fades as attendance falls. +- Provider read, price rejected by the outlier check (fence-reject carry, effective 2026-09-14, `calc_params.liveness.fence_reject_carry`): the rejected print publishes unchanged as the provider's record, but the vote cast is the provider's booked smoothed price at its current fading weight, the receipt disclosing `carried_vote_from` (the carried-from observation, with `carry_basis`) beside `smoothed_vote_usd` (the cast price). The carried vote never advances the provider's price series, never counts as attendance, and never counts toward the minimum passing panel. - Hard cutoff: past 96 consecutive observations without a usable price (24 hours), the provider receives no weight and casts no vote until a fresh print advances its state. Exclusion is decided from the pre-observation history, so the first accepted recovery price remains visible as receipt evidence but still carries no weight or vote; it re-admits the provider at the next scheduled observation. Our own failures never advance the count. > **Why attendance?** A new provider should not receive full weight from its first print, and a provider that stops publishing should fade rather than vanish instantly or linger stale. The half-life sets the smooth fade during a temporary absence; the hard cutoff removes persistently absent sources. Entry, fade-out, and recovery all happen without per-provider judgment. @@ -711,6 +714,7 @@ Values identical across the live SKUs. SKU-bound parameters (minimum panel, FX s | Minimum variability | 3% of price | Floor under both sigmas; a frozen price otherwise has sd 0 | | Warm-up | 10 | Observations before the test applies | | Vote sigma window | 90 days | Window for the sigma used in votes and the stability band (section 7.1) | +| Vote pre-smoothing half-life | 1 hour | Each provider's cast price is the time-based EWMA of its own accepted prints (section 7.1); disclosed per receipt as `smoothed_vote_usd`. Effective 2026-09-14 | | Test currency | as quoted | Prevents FX moves ejecting a provider | | Review flag | 15% | Distance from panel average flagging review | | Currency-change confirmation | 3 | Consecutive observations confirming a switch | @@ -734,6 +738,7 @@ Identical on all panels. No per-provider values. | Attendance half-life | 6 hours | How quickly provider-side missing prints lose influence, and returning providers regain it (section 8.6) | | Attendance sensitivity (η) | 0.5 | Strength of attendance in allocation; at 0 the softmax is unchanged. Armed at 0.5 by the versioned change effective 2026-09-01 | | Consecutive no-price limit | 96 observations (24 h) | Hard exclusion after sustained provider-side absence; our own collection failures never count (section 8.6) | +| Fence-reject carry | armed | An outlier-rejected print's provider re-casts its booked smoothed vote, classified carried (section 8.6); disclosed as `calc_params.liveness.fence_reject_carry`. Armed by the versioned change effective 2026-09-14 | | Minimum observations | 10 | Per provider, per window: observations needed before that provider can be scored. Below it the score is undefined | | Minimum variation | 1e-12 | The panel must have moved for prediction to be a meaningful question. Below this variance in what is predicted, the score is undefined rather than computed from nothing. Set above price-rounding noise (roughly 1e-14), below any real movement | | Minimum panel for transition | 5 | Whole-panel, one time: providers that must report at the observation where the index permanently switches to derived weights. Distinct from minimum observations: that asks "does this provider have enough history?", this asks "is this observation broad enough to change the methodology?" | diff --git a/README.md b/README.md index 637f517..5330639 100644 --- a/README.md +++ b/README.md @@ -75,10 +75,17 @@ are labeled `back-calculated`. The raw inputs are disclosed prices and dispersions, recorded currency and FX, upstream status, carry basis, filter verdicts, timing, top-level flags, and -`calc_params`. Published attendance factors, liveness scores, and weights are -comparison outputs, never derivation inputs. Every artifact read is digest -verified. Missing required history or withheld raw inputs cause a full-reproduction -refusal. If `history_path` is absent, the command prints a notice and uses +`calc_params`. On vote-pre-smoothing generations +(`calc_params.pre_smoothing_half_life_hours`, effective 2026-09-14) each voting +receipt also disclosed `smoothed_vote_usd` — the exact cast price the engine +aggregated — and a fence-rejected row that still voted its booked price carries +a `carried_vote_from` marker; the reproduction votes those disclosed cast prices +rather than rerunning the engine's smoothing state, keeps the raw print as +evidence, and refuses loudly if a voting row omits the disclosure (never a +raw-price fallback). Published attendance factors, liveness scores, and +weights are comparison outputs, never derivation inputs. Every artifact read +is digest verified. Missing required history or withheld raw inputs cause a +full-reproduction refusal. If `history_path` is absent, the command prints a notice and uses `current_version` (or the legacy flat record when no version pointer exists). Exit 0: every verifiable value matched. Exit 1 means a mismatch, invalid artifact, diff --git a/config/index_panel_b200.json b/config/index_panel_b200.json index dfa069a..80a9a51 100644 --- a/config/index_panel_b200.json +++ b/config/index_panel_b200.json @@ -78,8 +78,8 @@ } ], "calc": { - "methodology_id": "annex_a2_v0_3_calc_v14", - "description": "calc_v14 attendance-weighted quarter-hour panel mint (annex_a2_v0_3_calc_v14); METHODOLOGY.md is the binding methodology document", + "methodology_id": "annex_a2_v0_3_calc_v17", + "description": "calc_v17 EWMA-pre-smoothed (1h half-life) fence-reject-carry attendance-weighted quarter-hour panel mint (annex_a2_v0_3_calc_v17); METHODOLOGY.md is the binding methodology document", "availability_verified_sources": ["vast"], "min_sources_to_claim": 5, "eligible_tiers": [ @@ -92,6 +92,7 @@ "filter_terms": "recorded_currency", "composite_statistic": "median_ci_votes", "iqm_alpha": 0.16666, + "pre_smoothing_half_life_hours": 1, "vote_sigma_source": "dw_history", "vote_sigma_floor_pct": 3.0, "carry_forward_window_hours": 72, @@ -137,7 +138,8 @@ "attendance_floor": 0.5, "attendance_half_life_hours": 6, "attendance_eta": 0.5, - "no_price_exclusion_hours": 24 + "no_price_exclusion_hours": 24, + "fence_reject_carry": true } }, "members": [ diff --git a/config/index_panel_b300.json b/config/index_panel_b300.json index 9003a54..f9c4277 100644 --- a/config/index_panel_b300.json +++ b/config/index_panel_b300.json @@ -78,8 +78,8 @@ } ], "calc": { - "methodology_id": "annex_a_v0_2_calc_v14", - "description": "calc_v14 attendance-weighted quarter-hour panel mint (annex_a_v0_2_calc_v14); METHODOLOGY.md is the binding methodology document", + "methodology_id": "annex_a_v0_2_calc_v17", + "description": "calc_v17 EWMA-pre-smoothed (1h half-life) fence-reject-carry attendance-weighted quarter-hour panel mint (annex_a_v0_2_calc_v17); METHODOLOGY.md is the binding methodology document", "availability_verified_sources": ["vast"], "min_sources_to_claim": 5, "eligible_tiers": [ @@ -92,6 +92,7 @@ "filter_terms": "recorded_currency", "composite_statistic": "median_ci_votes", "iqm_alpha": 0.16666, + "pre_smoothing_half_life_hours": 1, "vote_sigma_source": "dw_history", "vote_sigma_floor_pct": 3.0, "carry_forward_window_hours": 72, @@ -152,7 +153,8 @@ "attendance_floor": 0.5, "attendance_half_life_hours": 6, "attendance_eta": 0.5, - "no_price_exclusion_hours": 24 + "no_price_exclusion_hours": 24, + "fence_reject_carry": true } }, "members": [ diff --git a/config/index_panel_h100_sxm.json b/config/index_panel_h100_sxm.json index 75fc008..58c4d4a 100644 --- a/config/index_panel_h100_sxm.json +++ b/config/index_panel_h100_sxm.json @@ -71,8 +71,8 @@ "GH200" ], "calc": { - "methodology_id": "h100_sxm_v1_calc_v10", - "description": "calc_v10 attendance-weighted quarter-hour panel mint (h100_sxm_v1_calc_v10); METHODOLOGY.md is the binding methodology document", + "methodology_id": "h100_sxm_v1_calc_v16", + "description": "calc_v16 EWMA-pre-smoothed (1h half-life) fence-reject-carry attendance-weighted quarter-hour panel mint (h100_sxm_v1_calc_v16); METHODOLOGY.md is the binding methodology document", "availability_verified_sources": ["lium", "vast"], "min_sources_to_claim": 5, "eligible_tiers": [ @@ -85,6 +85,7 @@ "filter_terms": "recorded_currency", "composite_statistic": "median_ci_votes", "iqm_alpha": 0.16666, + "pre_smoothing_half_life_hours": 1, "vote_sigma_source": "dw_history", "vote_sigma_floor_pct": 3.0, "carry_forward_window_hours": 72, @@ -134,7 +135,8 @@ "attendance_floor": 0.5, "attendance_half_life_hours": 6, "attendance_eta": 0.5, - "no_price_exclusion_hours": 24 + "no_price_exclusion_hours": 24, + "fence_reject_carry": true } }, "members": [ diff --git a/config/index_panel_h200_sxm.json b/config/index_panel_h200_sxm.json index 36edcf6..ebf2e67 100644 --- a/config/index_panel_h200_sxm.json +++ b/config/index_panel_h200_sxm.json @@ -68,8 +68,8 @@ "H20" ], "calc": { - "methodology_id": "h200_sxm_v1_calc_v10", - "description": "calc_v10 attendance-weighted quarter-hour panel mint (h200_sxm_v1_calc_v10); METHODOLOGY.md is the binding methodology document", + "methodology_id": "h200_sxm_v1_calc_v16", + "description": "calc_v16 EWMA-pre-smoothed (1h half-life) fence-reject-carry attendance-weighted quarter-hour panel mint (h200_sxm_v1_calc_v16); METHODOLOGY.md is the binding methodology document", "availability_verified_sources": [ "lium", "vast" @@ -85,6 +85,7 @@ "filter_terms": "recorded_currency", "composite_statistic": "median_ci_votes", "iqm_alpha": 0.16666, + "pre_smoothing_half_life_hours": 1, "vote_sigma_source": "dw_history", "vote_sigma_floor_pct": 3.0, "carry_forward_window_hours": 72, @@ -134,7 +135,8 @@ "attendance_floor": 0.5, "attendance_half_life_hours": 6, "attendance_eta": 0.5, - "no_price_exclusion_hours": 24 + "no_price_exclusion_hours": 24, + "fence_reject_carry": true } }, "members": [ diff --git a/reproduce b/reproduce index 3fd08d4..c709708 100755 --- a/reproduce +++ b/reproduce @@ -29,9 +29,15 @@ # that version's own history. --version selects one version throughout. # The calculation uses raw disclosed inputs: # per-source prices and dispersions, status/timing facts, filter verdicts, -# carried basis, and published calculation parameters. It derives the +# carried basis, and published calculation parameters — plus, on +# vote-pre-smoothing generations (calc_params. +# pre_smoothing_half_life_hours), each voting receipt's disclosed cast +# price smoothed_vote_usd and the carried_vote_from marker on fence-rejected +# rows: votes price the engine's own disclosed casts, never a rerun of +# its smoothing state, and a voting row missing the disclosure is a +# typed refusal (no raw-price fallback). It derives the # attendance events and factors, liveness scores, weight vector, votes, -# IQM, and final value without consuming any published derived +# IQM, and final value without consuming any other published derived # intermediate. Insufficient observable history is a typed refusal. # # Under --producer, the run replays a local collection record instead diff --git a/src/gpu_index/index/panel.py b/src/gpu_index/index/panel.py index c285b59..f8d52bc 100644 --- a/src/gpu_index/index/panel.py +++ b/src/gpu_index/index/panel.py @@ -754,6 +754,22 @@ def panel_calc_params(config: Dict[str, Any]) -> Dict[str, Any]: if "iqm_alpha" in calc else {} ), + # EWMA vote pre-smoothing (COM-1582, mint 2026-09-14): CONDITIONAL + # like iqm_alpha -- absent means raw vote centers, so every frozen + # predecessor's artifact bytes stay untouched and the D2 fence owns + # the flip. NOTE this engine mirror never RUNS the smoothing: + # compute_observation refuses an armed params set loudly (the + # supported reproduction of a smoothed generation is the published + # record's own disclosed cast prices, gpu_index.published). + **( + { + "pre_smoothing_half_life_hours": float( + calc["pre_smoothing_half_life_hours"] + ) + } + if "pre_smoothing_half_life_hours" in calc + else {} + ), # Vote-sigma source (ruling 2026-08-27): CONDITIONAL exactly like # iqm_alpha and the floor pair -- absent means the legacy # filter-window vote tail (what every already-published artifact @@ -873,6 +889,16 @@ def panel_calc_params(config: Dict[str, Any]) -> Dict[str, Any]: if attendance_minted(dw) else {} ), + # Fence-reject carry (COM-1570): CONDITIONAL sub-knob of the + # attendance triple, embedded only as literal True (the + # upstream emitter rule: absent = fence rejects drop the + # vote, today's bytes; load validation guarantees the strict + # bool and the attendance_eta > 0 precondition). + **( + {"fence_reject_carry": True} + if dw.get("fence_reject_carry") + else {} + ), "fallback_weights": { m["source_id"]: m["weight"] for m in member_params }, @@ -1480,6 +1506,15 @@ def classify_attendance_source(detail: Dict[str, Any]) -> Optional[str]: # Rule D1: chosen exists but no trustworthy filter value -- # a print the provider published that we cannot use. return EVENT_NO_PRICE + if isinstance(detail.get("carried_vote"), dict): + # COM-1570: a fence-rejected print whose VOTE was substituted + # from the state-2 carry book. status/chosen/filter are the + # untouched real print (replay and jump-reference reads need + # them unchanged) -- this disclosure block is the ONLY signal + # that the seat's vote, not its presence, was carried; reads + # absent exactly like the other carry_basis "no_price" + # shapes. + return EVENT_NO_PRICE # Trusted print: accepted, sigma-FENCED, and # currency-mismatch-pending all count PRESENT (the fence holds a # print out of the INDEX, never out of the presence record). @@ -1759,6 +1794,28 @@ def compute_observation( f"be the same law" ) params = calc_params + if "pre_smoothing_half_life_hours" in params or ( + params.get("dynamic_weights") or {} + ).get("fence_reject_carry"): + # COM-1582/COM-1570 fail-closed guard: this mirror carries no + # EWMA vote state and no fence-reject carry path, so running an + # armed lane here would price RAW vote centers (and drop + # fence-rejected seats' carried votes) under a methodology_id + # whose law says otherwise -- a silently plausible wrong index, + # the exact class this engine refuses everywhere else. Smoothed + # generations reproduce from the published record instead: every + # voting receipt disclosed its exact cast price + # (smoothed_vote_usd), which gpu_index.published.verify / + # gpu_index.published.full consume (./reproduce ). + raise ValueError( + f"calc_params for {params.get('methodology_id')!r} carry " + "pre_smoothing_half_life_hours / dynamic_weights." + "fence_reject_carry: this producer mirror does not rerun the " + "EWMA vote pre-smoothing or the fence-reject carry, so it " + "cannot price an armed lane from the raw record -- reproduce " + "smoothed generations from the published record's disclosed " + "cast prices instead (./reproduce , gpu_index.published)" + ) if schedule is None: from gpu_index.index.panel_config import panel_schedule diff --git a/src/gpu_index/index/panel_config.py b/src/gpu_index/index/panel_config.py index fac664e..8a6a995 100644 --- a/src/gpu_index/index/panel_config.py +++ b/src/gpu_index/index/panel_config.py @@ -129,6 +129,11 @@ dynamic_weights.history_days per-source history; requires median_ci_votes, and dw_history requires the dynamic_weights block), + pre_smoothing_half_life_hours? (COM-1582 EWMA vote + pre-smoothing, number in (0, 2] hours -- the 2h + ceiling is the upstream engine's checkpoint- + exactness bound; this repo never reruns the EWMA, + see the validator comment), carry_forward_window_hours? + carry_forward_failure_kinds? (CONDITIONAL pair, both or neither: a member whose raw capture entry @@ -164,7 +169,12 @@ attendance_half_life_hours + attendance_eta + no_price_exclusion_hours -- all three or none, validated by - gpu_index.index.weights.validate_attendance_params; + gpu_index.index.weights.validate_attendance_params, + plus the OPTIONAL COM-1570 sub-knob + fence_reject_carry (strict bool, requires + attendance_eta > 0: a sigma-fence-rejected seat + re-casts its booked vote from the state-2 carry + book); attendance_eta > 0 additionally REQUIRES the carry pair above with no_price_exclusion_hours <= carry_forward_window_hours, METHODOLOGY.md @@ -330,6 +340,7 @@ def panel_schedule(config: Dict[str, Any]) -> PanelSchedule: "filter_terms", "composite_statistic", "iqm_alpha", + "pre_smoothing_half_life_hours", "vote_sigma_source", "manual_verify_pct", "fx_lane", @@ -386,6 +397,11 @@ def panel_schedule(config: Dict[str, Any]) -> PanelSchedule: "attendance_half_life_hours", "attendance_eta", "no_price_exclusion_hours", + # Fence-reject carry (COM-1570; names cross-repo frozen): a + # minted sub-knob of the attendance triple -- strict bool, + # requires attendance_eta > 0. Validated in + # _validate_dynamic_weights. + "fence_reject_carry", } ) _JUMP_SCREEN_KEYS = frozenset( @@ -1085,6 +1101,32 @@ def _positive_floor(value: Any) -> bool: "calc.iqm_alpha requires calc.composite_statistic " f"'median_ci_votes', got {calc.get('composite_statistic')!r}" ) + if "pre_smoothing_half_life_hours" in calc: + # COM-1582 EWMA vote pre-smoothing (mint 2026-09-14): each admitted + # seat's vote price is the time-based EWMA of its own admitted + # prints; carried votes re-cast the frozen smoothed state. The 2h + # CEILING mirrors the upstream engine's exactness bound + # (cross-window EWMA memory must underflow to exact float zero + # inside the lane's 90-day state window, which only holds at + # half_life <= 2h) -- raising it upstream means re-deriving that + # bound, so this mirror pins the same number. No statistic + # coupling: both composite arms consume vote prices. This repo + # does NOT rerun the EWMA: smoothed generations reproduce from + # the published record's disclosed cast prices (smoothed_vote_usd + # -- gpu_index.published.verify / gpu_index.published.full), and + # gpu_index.index.panel.compute_observation refuses an armed lane + # loudly rather than silently price raw votes under it. + half_life = calc["pre_smoothing_half_life_hours"] + if not ( + isinstance(half_life, (int, float)) + and not isinstance(half_life, bool) + and math.isfinite(half_life) + and 0 < half_life <= 2 + ): + raise PanelConfigError( + "calc.pre_smoothing_half_life_hours must be a number in " + f"(0, 2], got {half_life!r}" + ) if "vote_sigma_source" in calc: # Ruling 2026-08-27: WHICH per-source history prices a vote's # stddev. Enum-valued, one home for the vocabulary (gpu_index.index.panel, @@ -1553,6 +1595,30 @@ def _validate_dynamic_weights( validate_attendance_params(dw) except ValueError as exc: raise PanelConfigError(str(exc)) from exc + if "fence_reject_carry" in dw: + # COM-1570: a sigma-fence reject is "we don't trust this print" + # exactly like an uncorroborated jump quarantine -- when armed, + # both unify onto the SAME state-2 carry book/vote (carry_basis + # "no_price"). The knob only makes sense where that book already + # exists, so it inherits the exact attendance-arming + # precondition rather than duplicating it. Strict bool (the + # upstream rule): truthiness would let "false"/1 silently arm + # and 0/null silently disarm. + fence_reject_carry = dw["fence_reject_carry"] + if not isinstance(fence_reject_carry, bool): + raise PanelConfigError( + "calc.dynamic_weights.fence_reject_carry must be a " + f"boolean, got {fence_reject_carry!r}" + ) + if fence_reject_carry and not ( + "attendance_eta" in dw and dw["attendance_eta"] > 0 + ): + raise PanelConfigError( + "calc.dynamic_weights.fence_reject_carry requires " + "calc.dynamic_weights.attendance_eta > 0 -- there is no " + "state-2 carry book to serve a fence-rejected seat's vote " + "on an unarmed lane" + ) min_span_minutes = ( ( max(dw["lookback_horizons_hours"]) diff --git a/src/gpu_index/published/full.py b/src/gpu_index/published/full.py index 8610c95..ec2c25b 100644 --- a/src/gpu_index/published/full.py +++ b/src/gpu_index/published/full.py @@ -22,7 +22,11 @@ new_weight_state, ) from gpu_index.published.artifacts import PublishedRecordError -from gpu_index.published.verify import MIN_DISCLOSURE_WINDOW_DAYS, _history_bound_days +from gpu_index.published.verify import ( + MIN_DISCLOSURE_WINDOW_DAYS, + _carried_vote_disclosed, + _history_bound_days, +) VERDICT_MATCH = "match" VERDICT_MISMATCH = "mismatch" @@ -114,6 +118,53 @@ def _is_number(value: Any) -> bool: ) +def _smoothing_armed(params: dict, *, observed_at: Any) -> bool: + """COM-1582: ``calc_params.pre_smoothing_half_life_hours`` arms the + smoothed seat law for the whole observation. Usability-validated only + (the (0, 2] mint ceiling is lane law, not a reproduce precondition); + absent means the pre-smoothing generations replay byte-identically.""" + if "pre_smoothing_half_life_hours" not in params: + return False + half_life = params["pre_smoothing_half_life_hours"] + if not _is_number(half_life) or half_life <= 0: + raise FullReproductionRefusal( + "invalid_smoothing_params", + f"{observed_at}: calc_params.pre_smoothing_half_life_hours " + f"must be a finite number > 0, got {half_life!r}", + ) + return True + + +def _cast_price(receipt: dict, *, observed_at: Any) -> float: + """The disclosed cast price on a smoothing-armed lane's voting row. + + This reproduction never reruns the engine's EWMA: ``smoothed_vote_usd`` + is the EXACT price the engine aggregated (fresh rows: the seat's + smoothed series advanced by this print; carried flavors: the frozen + smoothed booked price), so the vote prices THAT number and the row's + raw ``price`` stays evidence. A voting row without the disclosure (or + with an unusable one) is a torn artifact: refuse loudly, never fall + back to the raw print (the 2026-09-14T0100 incident posture).""" + source_id = receipt.get("source_id") + cast = receipt.get("smoothed_vote_usd") + if cast is None: + raise FullReproductionRefusal( + "missing_cast_price", + f"{observed_at} {source_id}: the seat votes on a " + "smoothing-armed lane but discloses no smoothed_vote_usd -- " + "the cast price is unknowable, refusing to reproduce (no " + "raw-price fallback)", + ) + if not _is_number(cast) or cast <= 0: + raise FullReproductionRefusal( + "unusable_cast_price", + f"{observed_at} {source_id}: smoothed_vote_usd {cast!r} is " + "not a finite positive number -- torn artifact, refusing to " + "reproduce (no raw-price fallback)", + ) + return float(cast) + + def _dynamic_params(observation: dict) -> tuple[dict, Dict[str, float]]: params = observation.get("calc_params") if not isinstance(params, dict) or not isinstance(params.get("liveness"), dict): @@ -137,7 +188,7 @@ def _dynamic_params(observation: dict) -> tuple[dict, Dict[str, float]]: return dynamic, fallback -def _project_classifier_row(receipt: dict) -> dict: +def _project_classifier_row(receipt: dict, *, smoothing_armed: bool) -> dict: if "upstream_status" not in receipt: raise FullReproductionRefusal( "missing_upstream_status", @@ -154,6 +205,18 @@ def _project_classifier_row(receipt: dict) -> dict: detail["filter"] = {"untrusted_currency": True} else: detail["filter"] = {} + if smoothing_armed and _carried_vote_disclosed(receipt): + # COM-1570 fence-reject carry: the row keeps the real + # rejected print (status/price/verdict untouched), only the + # VOTE was substituted from the carry book -- the classifier + # must read the seat absent, exactly like the engine's own + # carried_vote arm. Armed lanes only: pre-smoothing bytes + # classify unchanged whatever a row happens to carry. The + # corpus flattens the engine block; rebuild the engine shape + # the ported classifier expects. + detail["carried_vote"] = {"from": receipt["carried_vote_from"]} + if isinstance(receipt.get("carry_basis"), str): + detail["carried_vote"]["carry_basis"] = receipt["carry_basis"] if upstream_status == "carried": detail["carried"] = {"carry_basis": receipt.get("carry_basis")} return detail @@ -161,7 +224,14 @@ def _project_classifier_row(receipt: dict) -> dict: def public_attendance_events(observation: dict) -> Dict[str, str]: """Port the engine classifier onto the public receipt vocabulary.""" - rows = [_project_classifier_row(receipt) for receipt in observation["receipts"]] + smoothing_armed = _smoothing_armed( + observation.get("calc_params") or {}, + observed_at=observation.get("observed_at"), + ) + rows = [ + _project_classifier_row(receipt, smoothing_armed=smoothing_armed) + for receipt in observation["receipts"] + ] reason = observation.get("reason") return attendance_events_for_stamp( rows, @@ -176,7 +246,9 @@ def public_attendance_events(observation: dict) -> Dict[str, str]: ) -def _trusted_receipts(observation: dict, manual: set[str]) -> Dict[str, dict]: +def _trusted_receipts( + observation: dict, manual: set[str], *, smoothing_armed: bool = False +) -> Dict[str, dict]: trusted: Dict[str, dict] = {} for receipt in observation["receipts"]: source_id = str(receipt["source_id"]) @@ -188,6 +260,16 @@ def _trusted_receipts(observation: dict, manual: set[str]) -> Dict[str, dict]: "history is withheld", ) continue + if smoothing_armed and _carried_vote_disclosed(receipt): + # COM-1570 fence-reject carry: the real rejected print stays + # on the row, but the engine deliberately never advances the + # weight-series presence record with it (the seat must read + # attendance-ABSENT) -- so this reproduction must not feed + # the weight prints or the carry book from it either. A + # plain fence reject (no disclosure) keeps today's rule: the + # fence holds a print out of the INDEX, never out of the + # presence record. + continue if ( receipt.get("upstream_status") == "ok" and receipt.get("carry_basis") is None @@ -414,6 +496,9 @@ def reproduce_full_history( ) events = public_attendance_events(observation) params = observation["calc_params"] + smoothing_armed = _smoothing_armed( + params, observed_at=observation["observed_at"] + ) dynamic, fallback = _dynamic_params(observation) observation_date = str(observation["observed_at"])[:10] manual = { @@ -421,7 +506,9 @@ def reproduce_full_history( for row in params.get("manual_exclusions", []) if row.get("date") == observation_date } - trusted = _trusted_receipts(observation, manual) + trusted = _trusted_receipts( + observation, manual, smoothing_armed=smoothing_armed + ) obs_stamp = _stamp(observation) attendance = compute_attendance_view( state, @@ -435,14 +522,28 @@ def reproduce_full_history( for source_id in sorted(trusted) if not attendance[source_id]["excluded"] ] - carrying = sorted( + carrying_ids = { str(receipt["source_id"]) for receipt in receipts if receipt.get("upstream_status") == "carried" and receipt.get("carry_basis") == "no_price" and str(receipt["source_id"]) not in manual and not attendance[str(receipt["source_id"])]["excluded"] - ) + } + if smoothing_armed: + # COM-1570 fence-reject carried votes ride the SAME D4 + # fading-weight domain as the state-2 no_price carries: the + # seat is absent from the presence record this stamp, but its + # CURRENT (fading) weight prices its booked vote -- never the + # booked weight (that is the state-3 machinery's rule). + carrying_ids |= { + str(receipt["source_id"]) + for receipt in receipts + if _carried_vote_disclosed(receipt) + and str(receipt["source_id"]) not in manual + and not attendance[str(receipt["source_id"])]["excluded"] + } + carrying = sorted(carrying_ids) block = compute_panel_weights( state, obs_stamp=obs_stamp, @@ -477,24 +578,78 @@ def reproduce_full_history( passing = [] stddevs = {} + carried_vote_count = 0 for receipt in receipts: source_id = str(receipt["source_id"]) - if receipt.get("filter_verdict") != "accepted": + fence_reject_carried = smoothing_armed and _carried_vote_disclosed( + receipt + ) + if ( + receipt.get("filter_verdict") != "accepted" + and not fence_reject_carried + ): continue upstream_status = receipt.get("upstream_status") - if upstream_status == "carried": + carried_voter = False + if fence_reject_carried and upstream_status == "ok": + # COM-1570 fence-reject carry: the rejected verdict judged + # the REAL print the row keeps as evidence; the engine + # cast the seat's booked smoothed vote instead, at its + # CURRENT fading weight (never the booked weight) with + # the BOOKED vote dispersion. carried = carry_book.get(source_id) - assert carried is not None - price = carried["price"] + if carried is None: + raise FullReproductionRefusal( + "insufficient_carry_history", + f"{observation['observed_at']} {source_id}: the public " + "history has no prior accepted raw vote for this " + "fence-reject carry", + ) + weight = derived_weights.get(source_id) + if weight is None: + continue + price = _cast_price( + receipt, observed_at=observation["observed_at"] + ) sd = carried["sd"] + carried_voter = True + elif upstream_status == "carried": + carried = carry_book.get(source_id) + assert carried is not None weight = derived_weights.get(source_id) + if weight is None: + continue + # Smoothing-armed carried rows re-cast the FROZEN smoothed + # state, disclosed on the row itself; pre-smoothing rows + # re-cast the booked raw vote resolved from prior bytes. + price = ( + _cast_price( + receipt, observed_at=observation["observed_at"] + ) + if smoothing_armed + else carried["price"] + ) + sd = carried["sd"] + carried_voter = True elif upstream_status == "ok": - price = receipt.get("price") - sd = receipt.get("sd") weight = block["weights"].get(source_id) + if weight is None: + # A voteless fresh print (e.g. the K_A recovery + # print) never voted upstream and discloses no cast + # price -- the weight gate must run BEFORE any + # cast-price demand. + continue + price = ( + _cast_price( + receipt, observed_at=observation["observed_at"] + ) + if smoothing_armed + else receipt.get("price") + ) + sd = receipt.get("sd") else: continue - if weight is None or not _is_number(price): + if not _is_number(price): continue if not _is_number(sd): raise FullReproductionRefusal( @@ -504,13 +659,25 @@ def reproduce_full_history( ) passing.append((source_id, float(weight), float(price))) stddevs[source_id] = float(sd) + if carried_voter: + carried_vote_count += 1 + # The claim-floor law: carried votes (status-carried re-casts AND + # fence-reject carried votes) may move the median but never keep + # a dying panel lit -- the floor counts OBSERVED voters only on + # the smoothing-armed generations; pre-smoothing bytes keep the + # frozen count untouched. + observed_count = ( + len(passing) - carried_vote_count + if smoothing_armed + else len(passing) + ) composite = ( median_stddev_composite( passing, stddevs, iqm_alpha=float(params.get("iqm_alpha", 0.0)), ) - if len(passing) >= int(params["min_sources_to_publish"]) + if observed_count >= int(params["min_sources_to_publish"]) else None ) derived_value = None if composite is None else composite["value_usd_gpu_hr"] diff --git a/src/gpu_index/published/verify.py b/src/gpu_index/published/verify.py index 553b710..4c8657c 100644 --- a/src/gpu_index/published/verify.py +++ b/src/gpu_index/published/verify.py @@ -18,6 +18,38 @@ the published stability band is the larger distance from the index to the 25th/75th weighted vote percentiles. +On a SMOOTHING-ARMED lane (``calc_params.pre_smoothing_half_life_hours`` +present -- the COM-1582 EWMA vote pre-smoothing generation) the seat law +changes, mirroring the engine's own disclosures rather than rerunning +its EWMA state: + + - every voting receipt disclosed ``smoothed_vote_usd``, the EXACT cast + price the engine aggregated (fresh rows: the seat's smoothed series + advanced by this print; carried flavors: the frozen smoothed booked + price). The recompute votes THAT number; ``price`` stays the raw + print evidence and is never a fallback -- a participating row + missing the disclosure (or carrying an unusable one) is a torn + artifact and refuses loudly (the 2026-09-14T0100 incident posture); + - a status "ok" + filter_verdict "rejected" receipt carrying the + ``carried_vote_from`` disclosure (the flattened COM-1570 + fence_reject_carry marker, with ``carry_basis`` beside it) VOTES: + the sigma fence rejected the fresh print (kept on the row as + ``price`` for the record) and the engine cast the seat's booked + smoothed vote instead. The row's own rejected verdict judged the + PRINT, not the vote, so it does not veto admission; only a + non-empty string reads as a disclosure (anything else reads as + absent -- one presence fence for admission and classification), and + a disclosure only ever admits beside status "ok"; + - carried voters (``upstream_status`` "carried", or the + carried_vote_from arm above) price the composite but never satisfy + the observed floor: a composite exists iff passing count MINUS + carried voters reaches ``min_sources_to_publish`` (the engine's own + claim-floor law). + +Pre-smoothing observations keep the frozen law above byte-identically: +absent the calc knob, ``smoothed_vote_usd``/``carried_vote_from`` are +ignored exactly like any other unfamiliar disclosure field. + The vote/IQM math is IMPORTED from the panel engine (``gpu_index.index.panel.median_stddev_composite``) — the same function that priced the observation — never duplicated here, so this check can @@ -161,6 +193,19 @@ def _finite_number(value: Any) -> bool: ) +def _carried_vote_disclosed(receipt: dict) -> bool: + """The COM-1570 fence-reject carry presence fence, ONE predicate so + admission and carried-classification can never disagree about what + "disclosed" means: the public corpus flattens the engine's carried_vote + block to the receipt-level ``carried_vote_from`` key (an ISO instant; + ``carry_basis`` rides beside it when the engine stated one). A real + disclosure is a non-empty string. Anything else -- number/object/array + dressing -- reads as ABSENT, dropping the seat back onto the plain law, + which fails closed on the fence-rejected verdict.""" + carried_vote_from = receipt.get("carried_vote_from") + return isinstance(carried_vote_from, str) and bool(carried_vote_from) + + def recompute_observation(observation: dict) -> ObservationCheck: """Recompute one published observation from its own receipts and match the published index value and stability band exactly.""" @@ -204,6 +249,19 @@ def recompute_observation(observation: dict) -> ObservationCheck: f"observation {sku} {observed_at} " f"calc_params.min_sources_to_publish is {min_to_publish!r}" ) + # COM-1582: the calc knob arms the smoothed seat law for this whole + # observation (module docstring). Validated for usability only -- the + # (0, 2] mint ceiling is lane law, enforced where lanes load, not a + # reproduce precondition. + smoothing_armed = "pre_smoothing_half_life_hours" in calc_params + if smoothing_armed: + half_life = calc_params["pre_smoothing_half_life_hours"] + if not _finite_number(half_life) or half_life <= 0: + raise PublishedRecordError( + f"observation {sku} {observed_at} " + "calc_params.pre_smoothing_half_life_hours must be a " + f"finite number > 0, got {half_life!r}" + ) receipts = observation.get("receipts") if not isinstance(receipts, list): raise PublishedRecordError( @@ -212,6 +270,7 @@ def recompute_observation(observation: dict) -> ObservationCheck: passing: List[Tuple[str, float, float]] = [] vote_stddevs: Dict[str, float] = {} + carried_voters: List[str] = [] withheld_contributing: List[str] = [] any_withheld = False for index, receipt in enumerate(receipts): @@ -228,17 +287,62 @@ def recompute_observation(observation: dict) -> ObservationCheck: ) if disclosure == "withheld": any_withheld = True - contributing = ( - _receipt_field(receipt, "status", index) == "ok" - and _receipt_field(receipt, "filter_verdict", index) - == "accepted" + # The COM-1570 arm exists ONLY on smoothing-armed observations: + # pre-smoothing bytes replay under the frozen predicate below + # untouched, whatever disclosure fields a row happens to carry. + carried_vote_disclosed = smoothing_armed and _carried_vote_disclosed( + receipt + ) + contributing = _receipt_field(receipt, "status", index) == "ok" and ( + _receipt_field(receipt, "filter_verdict", index) == "accepted" + # The fence-rejected verdict judged the PRINT the row keeps + # for the record, never the booked vote the engine cast + # (the shared seat-admission law) -- the disclosure admits. + or carried_vote_disclosed ) if not contributing: continue if disclosure == "withheld": withheld_contributing.append(source_id) continue - price = _receipt_field(receipt, "price", index) + if smoothing_armed: + # Carried classification rides the SAME predicates the floor + # law needs: the carried_vote arm above, or a status-carried + # upstream seat re-cast onto this stamp (projected status + # "ok" + verdict "accepted", distinguishable only here). + # upstream_status is REQUIRED on an armed lane's voting rows + # -- without it a carried voter would silently satisfy the + # observed floor. + if carried_vote_disclosed or ( + _receipt_field(receipt, "upstream_status", index) + == "carried" + ): + carried_voters.append(source_id) + cast = receipt.get("smoothed_vote_usd") + if cast is None: + # The engine cast a price this artifact does not + # disclose: the exact 2026-09-14T0100 incident shape. + # NEVER the raw print instead -- pricing the rejected or + # pre-smoothing chosen would derive a silently plausible + # wrong index. + raise PublishedRecordError( + f"observation {sku} {observed_at}: receipt " + f"{source_id} votes on a smoothing-armed lane but " + "does not disclose smoothed_vote_usd -- the cast " + "price is unknowable, refusing to reproduce (no " + "raw-price fallback)" + ) + if not _finite_number(cast) or cast <= 0: + raise PublishedRecordError( + f"observation {sku} {observed_at}: receipt " + f"{source_id} discloses unusable smoothed_vote_usd " + f"{cast!r} on a smoothing-armed lane -- torn " + "artifact, refusing to reproduce (no raw-price " + "fallback)" + ) + price = cast + else: + price = _receipt_field(receipt, "price", index) sd = _receipt_field(receipt, "sd", index) weight = _receipt_field(receipt, "weight", index) if not ( @@ -252,7 +356,8 @@ def recompute_observation(observation: dict) -> ObservationCheck: ) # The exact tuple the panel engine fed the vote aggregate: # (source_id, float(weight), price) with the ROUNDED published - # sd as the vote stddev (panel.py compute_observation). + # sd as the vote stddev (panel.py compute_observation). On an + # armed lane price IS the disclosed cast price above. passing.append((source_id, float(weight), price)) vote_stddevs[source_id] = sd @@ -276,14 +381,19 @@ def recompute_observation(observation: dict) -> ObservationCheck: ) # The minimum-panel rule, verbatim from the panel engine: a composite - # exists iff the passing set reaches min_sources_to_publish. + # exists iff the passing set reaches min_sources_to_publish. On a + # smoothing-armed lane the count is OBSERVED voters only -- carried + # votes (status-carried re-casts and COM-1570 fence-reject carries) + # may move the median but never keep a dying panel lit (the engine's + # claim-floor law; carried_voters stays empty pre-smoothing). + observed_count = len(passing) - len(carried_voters) composite = ( median_stddev_composite( passing, vote_stddevs, iqm_alpha=iqm_alpha, ) - if len(passing) >= min_to_publish + if observed_count >= min_to_publish else None ) @@ -299,7 +409,7 @@ def recompute_observation(observation: dict) -> ObservationCheck: if composite is not None: messages.append( f"published no_print but the receipts rebuild a composite " - f"({len(passing)} passing sources >= " + f"({observed_count} observed passing sources >= " f"min_sources_to_publish {min_to_publish}): recomputed " f"value {composite['value_usd_gpu_hr']}" ) @@ -337,8 +447,8 @@ def recompute_observation(observation: dict) -> ObservationCheck: published_value=published_value, published_band=published_band, messages=( - f"published status ok but only {len(passing)} passing " - f"disclosed sources (< min_sources_to_publish " + f"published status ok but only {observed_count} observed " + f"passing disclosed sources (< min_sources_to_publish " f"{min_to_publish}): no composite is recomputable", ), ) diff --git a/tests/fixtures/cross_repo/carried-vote-pin.observation.json b/tests/fixtures/cross_repo/carried-vote-pin.observation.json new file mode 100644 index 0000000..8e77efd --- /dev/null +++ b/tests/fixtures/cross_repo/carried-vote-pin.observation.json @@ -0,0 +1,618 @@ +{ + "schema_version": 1, + "kind": "gpu_price_index_observation", + "sku": "H100", + "methodology_id": "h100_sxm_v1_calc_v16", + "observed_at": "2026-09-14T18:30:00.000Z", + "generated_at": "2026-09-14T23:59:00.000Z", + "input_snapshot_sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "unit": "USD/GPU/hour", + "calc_params": { + "collection_interval": "15-minute", + "index_recomputation": "15-minute", + "minimum_panel_members_to_record": 5, + "min_sources_to_publish": 5, + "eligible_tiers": [ + "on-demand" + ], + "filter_window_observations": 20, + "filter_sigma": 3, + "filter_warmup_observations": 10, + "filter_terms": "recorded_currency", + "manual_verify_pct": 15, + "aggregation": "median_ci_votes", + "composite_statistic": "median_ci_votes", + "iqm_alpha": 0.16666, + "vote_sigma_source": "dw_history", + "vote_sigma_floor_pct": 3, + "fx_source": "ECB", + "fx_max_staleness_days": 7, + "manual_exclusions": [], + "members": [ + { + "source_id": "civo", + "opening_weight": 0.058823529411764705 + }, + { + "source_id": "coreweave", + "opening_weight": 0.058823529411764705 + }, + { + "source_id": "crusoe", + "opening_weight": 0.058823529411764705 + }, + { + "source_id": "digitalocean", + "opening_weight": 0.058823529411764705 + }, + { + "source_id": "hyperbolic", + "opening_weight": 0.058823529411764705 + }, + { + "source_id": "hyperstack", + "opening_weight": 0.058823529411764705 + }, + { + "source_id": "lambda", + "opening_weight": 0.058823529411764705 + }, + { + "source_id": "lium", + "opening_weight": 0.058823529411764705 + }, + { + "source_id": "massedcompute", + "opening_weight": 0.058823529411764705 + }, + { + "source_id": "nebius", + "opening_weight": 0.058823529411764705 + }, + { + "source_id": "runpod", + "opening_weight": 0.058823529411764705 + }, + { + "source_id": "scaleway", + "opening_weight": 0.058823529411764705 + }, + { + "source_id": "tensorpool", + "opening_weight": 0.058823529411764705 + }, + { + "source_id": "together", + "opening_weight": 0.058823529411764705 + }, + { + "source_id": "vast", + "opening_weight": 0.058823529411764705 + }, + { + "source_id": "verda", + "opening_weight": 0.058823529411764705 + }, + { + "source_id": "voltagepark", + "opening_weight": 0.058823529411764705 + } + ], + "liveness": { + "scheme": "predictive_v1", + "lookback_horizons_hours": [ + 6, + 24, + 48 + ], + "forward_horizons_hours": [ + 6, + 24, + 48 + ], + "history_days": 90, + "half_life_days": 30, + "ridge_lambda": 1, + "gamma": 4, + "weight_min": 0.025, + "weight_max": 0.3, + "min_train_samples": 10, + "target_variance_floor": 1e-12, + "switch_min_eligible": 5, + "attendance_floor": 0.5, + "max_abs_log_return": 0.5, + "attendance_half_life_hours": 6, + "attendance_eta": 0.5, + "no_price_exclusion_hours": 24, + "fence_reject_carry": true + }, + "filter_sigma_floor_pct": 3, + "carry_forward_window_hours": 72, + "pre_smoothing_half_life_hours": 1 + }, + "receipts": [ + { + "source_id": "civo", + "liveness_score": 0.132394978, + "upstream_status": "ok", + "gpu_count_basis": 1, + "region": null, + "currency": "USD", + "fx_rate": null, + "sku_identifier": "NVIDIA H100 SXM", + "gpu_variant": "SXM", + "vram_gb": null, + "provider_class": "direct_principal", + "attendance_factor": 1, + "no_price_streak": 0, + "no_price_excluded": false, + "smoothed_vote_usd": 2.99, + "price": 2.99, + "sd": 0.0897, + "weight": 0.062211, + "source_url": "https://www.civo.com/pricing", + "status": "ok", + "filter_verdict": "accepted", + "last_seen": "2026-09-14T18:30:00.000Z", + "attendance_printed": 1822, + "attendance_ratio": 0.997809419, + "attendance_scheduled": 1826, + "price_disclosure": "published" + }, + { + "source_id": "coreweave", + "liveness_score": 0.135351924, + "upstream_status": "ok", + "gpu_count_basis": 8, + "region": "NORTH AMERICA", + "currency": "USD", + "fx_rate": null, + "sku_identifier": "NVIDIA HGX H100", + "gpu_variant": null, + "vram_gb": null, + "provider_class": "direct_principal", + "attendance_factor": 1, + "no_price_streak": 0, + "no_price_excluded": false, + "smoothed_vote_usd": 6.155, + "price": 6.155, + "sd": 0.18465, + "weight": 0.062654, + "source_url": "https://www.coreweave.com/pricing", + "status": "ok", + "filter_verdict": "accepted", + "last_seen": "2026-09-14T18:30:00.000Z", + "attendance_printed": 1722, + "attendance_ratio": 0.943044907, + "attendance_scheduled": 1826, + "price_disclosure": "published" + }, + { + "source_id": "crusoe", + "liveness_score": 0.132474116, + "upstream_status": "ok", + "gpu_count_basis": 1, + "region": null, + "currency": "USD", + "fx_rate": null, + "sku_identifier": "NVIDIA H100 80GB HGX", + "gpu_variant": null, + "vram_gb": 80, + "provider_class": "direct_principal", + "attendance_factor": 1, + "no_price_streak": 0, + "no_price_excluded": false, + "smoothed_vote_usd": 3.9, + "price": 3.9, + "sd": 0.117, + "weight": 0.062223, + "source_url": "https://www.crusoe.ai/cloud/pricing", + "status": "ok", + "filter_verdict": "accepted", + "last_seen": "2026-09-14T18:30:00.000Z", + "attendance_printed": 1822, + "attendance_ratio": 0.997809419, + "attendance_scheduled": 1826, + "price_disclosure": "published" + }, + { + "source_id": "digitalocean", + "liveness_score": 0.132335712, + "upstream_status": "ok", + "gpu_count_basis": 1, + "region": null, + "currency": "USD", + "fx_rate": null, + "sku_identifier": "NVIDIA H100", + "gpu_variant": null, + "vram_gb": null, + "provider_class": "direct_principal", + "attendance_factor": 1, + "no_price_streak": 0, + "no_price_excluded": false, + "smoothed_vote_usd": 4.41, + "price": 4.41, + "sd": 0.1323, + "weight": 0.062202, + "source_url": "https://docs.digitalocean.com/products/droplets/details/pricing/", + "status": "ok", + "filter_verdict": "accepted", + "last_seen": "2026-09-14T18:30:00.000Z", + "attendance_printed": 1819, + "attendance_ratio": 0.996166484, + "attendance_scheduled": 1826, + "price_disclosure": "published" + }, + { + "source_id": "hyperbolic", + "liveness_score": 0.017755116, + "upstream_status": "ok", + "gpu_count_basis": null, + "region": null, + "currency": "USD", + "fx_rate": null, + "sku_identifier": null, + "gpu_variant": null, + "vram_gb": null, + "provider_class": "marketplace", + "attendance_factor": 0.929274887, + "no_price_streak": 0, + "no_price_excluded": false, + "smoothed_vote_usd": 3.536745, + "price": 3.59, + "sd": 0.281059, + "weight": 0.047677, + "source_url": "https://api.hyperbolic.ai/v2/alpha/on-demand/rental-options", + "status": "ok", + "filter_verdict": "accepted", + "last_seen": "2026-09-14T18:30:00.000Z", + "attendance_printed": 1461, + "attendance_ratio": 0.800109529, + "attendance_scheduled": 1826, + "price_disclosure": "published" + }, + { + "source_id": "hyperstack", + "liveness_score": 0.132412992, + "upstream_status": "ok", + "gpu_count_basis": 1, + "region": "EU-heavy", + "currency": "USD", + "fx_rate": null, + "sku_identifier": "NVIDIA H100 SXM", + "gpu_variant": "SXM", + "vram_gb": null, + "provider_class": "direct_principal", + "attendance_factor": 1, + "no_price_streak": 0, + "no_price_excluded": false, + "smoothed_vote_usd": 3.2, + "price": 3.2, + "sd": 0.096, + "weight": 0.062214, + "source_url": "https://www.hyperstack.cloud/gpu-pricing", + "status": "ok", + "filter_verdict": "accepted", + "last_seen": "2026-09-14T18:30:00.000Z", + "attendance_printed": 1822, + "attendance_ratio": 0.997809419, + "attendance_scheduled": 1826, + "price_disclosure": "published" + }, + { + "source_id": "lambda", + "liveness_score": 0.132482079, + "upstream_status": "ok", + "gpu_count_basis": 8, + "region": null, + "currency": "USD", + "fx_rate": null, + "sku_identifier": "NVIDIA H100 SXM", + "gpu_variant": "SXM", + "vram_gb": null, + "provider_class": "direct_principal", + "attendance_factor": 1, + "no_price_streak": 0, + "no_price_excluded": false, + "smoothed_vote_usd": 3.99, + "price": 3.99, + "sd": 0.1197, + "weight": 0.062224, + "source_url": "https://lambda.ai/pricing", + "status": "ok", + "filter_verdict": "accepted", + "last_seen": "2026-09-14T18:30:00.000Z", + "attendance_printed": 1822, + "attendance_ratio": 0.997809419, + "attendance_scheduled": 1826, + "price_disclosure": "published" + }, + { + "source_id": "lium", + "liveness_score": 0.046716606, + "upstream_status": "ok", + "gpu_count_basis": null, + "region": null, + "currency": "USD", + "fx_rate": null, + "sku_identifier": null, + "gpu_variant": null, + "vram_gb": null, + "provider_class": "marketplace", + "attendance_factor": 0.6679, + "no_price_streak": 0, + "no_price_excluded": false, + "smoothed_vote_usd": 1.745, + "price": 1.41, + "sd": 0.550486, + "weight": 0.046587, + "source_url": "https://lium.io/api/executors", + "status": "ok", + "filter_verdict": "accepted", + "last_seen": "2026-09-14T18:30:00.000Z", + "attendance_printed": 1676, + "attendance_ratio": 0.917853231, + "attendance_scheduled": 1826, + "price_disclosure": "published" + }, + { + "source_id": "massedcompute", + "liveness_score": 0.132244875, + "upstream_status": "ok", + "gpu_count_basis": 1, + "region": null, + "currency": "USD", + "fx_rate": null, + "sku_identifier": "H100 SXM5 (80GB)", + "gpu_variant": "SXM", + "vram_gb": 80, + "provider_class": "direct_partnered", + "attendance_factor": 1, + "no_price_streak": 0, + "no_price_excluded": false, + "smoothed_vote_usd": 2.89, + "price": 2.89, + "sd": 0.0867, + "weight": 0.062189, + "source_url": "https://vm.massedcompute.com/pricing", + "status": "ok", + "filter_verdict": "accepted", + "last_seen": "2026-09-14T18:30:00.000Z", + "attendance_printed": 1819, + "attendance_ratio": 0.996166484, + "attendance_scheduled": 1826, + "price_disclosure": "published" + }, + { + "source_id": "nebius", + "liveness_score": 0.133098482, + "upstream_status": "ok", + "gpu_count_basis": 1, + "region": null, + "currency": "USD", + "fx_rate": null, + "sku_identifier": "NVIDIA HGX H100", + "gpu_variant": null, + "vram_gb": null, + "provider_class": "direct_principal", + "attendance_factor": 1, + "no_price_streak": 0, + "no_price_excluded": false, + "smoothed_vote_usd": 3.85, + "price": 3.85, + "sd": 0.1155, + "weight": 0.062316, + "source_url": "https://nebius.com/prices", + "status": "ok", + "filter_verdict": "accepted", + "last_seen": "2026-09-14T18:30:00.000Z", + "attendance_printed": 1814, + "attendance_ratio": 0.993428258, + "attendance_scheduled": 1826, + "price_disclosure": "published" + }, + { + "source_id": "runpod", + "liveness_score": 0.097302038, + "upstream_status": "ok", + "gpu_count_basis": 1, + "region": "global", + "currency": "USD", + "fx_rate": null, + "sku_identifier": "NVIDIA H100 80GB HBM3", + "gpu_variant": null, + "vram_gb": 80, + "provider_class": "neocloud", + "attendance_factor": 1, + "no_price_streak": 0, + "no_price_excluded": false, + "smoothed_vote_usd": 3.49, + "price": 3.49, + "sd": 0.1047, + "weight": 0.057338, + "source_url": "https://api.runpod.io/graphql", + "status": "ok", + "filter_verdict": "accepted", + "last_seen": "2026-09-14T18:30:00.000Z", + "attendance_printed": 1822, + "attendance_ratio": 0.997809419, + "attendance_scheduled": 1826, + "price_disclosure": "published" + }, + { + "source_id": "scaleway", + "liveness_score": 0.133382946, + "upstream_status": "ok", + "gpu_count_basis": 8, + "region": "fr-par-2", + "currency": "EUR", + "fx_rate": 1.1592, + "sku_identifier": "H100-SXM", + "gpu_variant": "SXM", + "vram_gb": null, + "provider_class": "direct_principal", + "attendance_factor": 1, + "no_price_streak": 0, + "no_price_excluded": false, + "smoothed_vote_usd": 3.670375, + "price": 3.670375, + "sd": 0.110111, + "weight": 0.062358, + "source_url": "https://api.scaleway.com/instance/v1/zones/fr-par-2/products/servers", + "status": "ok", + "filter_verdict": "accepted", + "last_seen": "2026-09-14T18:30:00.000Z", + "attendance_printed": 1822, + "attendance_ratio": 0.997809419, + "attendance_scheduled": 1826, + "price_disclosure": "published" + }, + { + "source_id": "tensorpool", + "liveness_score": 0.13231117, + "upstream_status": "ok", + "gpu_count_basis": 1, + "region": null, + "currency": "USD", + "fx_rate": null, + "sku_identifier": "H100 SXM", + "gpu_variant": "SXM", + "vram_gb": null, + "provider_class": "direct_principal", + "attendance_factor": 1, + "no_price_streak": 0, + "no_price_excluded": false, + "smoothed_vote_usd": 1.99, + "price": 1.99, + "sd": 0.0597, + "weight": 0.062199, + "source_url": "https://tensorpool.dev/pricing", + "status": "ok", + "filter_verdict": "accepted", + "last_seen": "2026-09-14T18:30:00.000Z", + "attendance_printed": 1822, + "attendance_ratio": 0.997809419, + "attendance_scheduled": 1826, + "price_disclosure": "published" + }, + { + "source_id": "together", + "liveness_score": 0.108184816, + "upstream_status": "ok", + "gpu_count_basis": 1, + "region": null, + "currency": "USD", + "fx_rate": null, + "sku_identifier": "NVIDIA HGX H100", + "gpu_variant": null, + "vram_gb": null, + "provider_class": "direct_principal", + "attendance_factor": 1, + "no_price_streak": 0, + "no_price_excluded": false, + "smoothed_vote_usd": 3.99, + "price": 3.99, + "sd": 0.1197, + "weight": 0.058777, + "source_url": "https://www.together.ai/pricing", + "status": "ok", + "filter_verdict": "accepted", + "last_seen": "2026-09-14T18:30:00.000Z", + "attendance_printed": 1101, + "attendance_ratio": 0.602957284, + "attendance_scheduled": 1826, + "price_disclosure": "published" + }, + { + "source_id": "vast", + "liveness_score": 0.015588197, + "upstream_status": "ok", + "gpu_count_basis": null, + "region": null, + "currency": "USD", + "fx_rate": null, + "sku_identifier": null, + "gpu_variant": null, + "vram_gb": null, + "provider_class": "marketplace", + "attendance_factor": 0.558271507, + "no_price_streak": 2, + "no_price_excluded": false, + "carry_basis": "no_price", + "smoothed_vote_usd": 4.60857, + "carried_vote_from": "2026-09-14T17:45:00.000Z", + "price": 5.8006, + "sd": 1.8626, + "weight": 0.042425, + "source_url": "https://console.vast.ai/api/v0/bundles/", + "status": "ok", + "filter_verdict": "rejected", + "last_seen": "2026-09-14T18:30:00.000Z", + "attendance_printed": 1535, + "attendance_ratio": 0.840635268, + "attendance_scheduled": 1826, + "price_disclosure": "published" + }, + { + "source_id": "verda", + "liveness_score": 0.132371569, + "upstream_status": "ok", + "gpu_count_basis": 1, + "region": "EU (Nordic DCs)", + "currency": "USD", + "fx_rate": null, + "sku_identifier": "1x H100 SXM5 80GB on-demand", + "gpu_variant": "SXM", + "vram_gb": 80, + "provider_class": "direct_principal", + "attendance_factor": 1, + "no_price_streak": 0, + "no_price_excluded": false, + "smoothed_vote_usd": 3.263622, + "price": 3.266, + "sd": 0.09798, + "weight": 0.062208, + "source_url": "https://verda.com/pricing", + "status": "ok", + "filter_verdict": "accepted", + "last_seen": "2026-09-14T18:30:00.000Z", + "attendance_printed": 1822, + "attendance_ratio": 0.997809419, + "attendance_scheduled": 1826, + "price_disclosure": "published" + }, + { + "source_id": "voltagepark", + "liveness_score": 0.13231117, + "upstream_status": "ok", + "gpu_count_basis": 1, + "region": null, + "currency": "USD", + "fx_rate": null, + "sku_identifier": "h100-sxm5-80gb", + "gpu_variant": "SXM", + "vram_gb": 80, + "provider_class": "direct_principal", + "attendance_factor": 1, + "no_price_streak": 0, + "no_price_excluded": false, + "smoothed_vote_usd": 1.99, + "price": 1.99, + "sd": 0.0597, + "weight": 0.062199, + "source_url": "https://cloud-api.voltagepark.com/api/v1/bare-metal/locations", + "status": "ok", + "filter_verdict": "accepted", + "last_seen": "2026-09-14T18:30:00.000Z", + "attendance_printed": 1822, + "attendance_ratio": 0.997809419, + "attendance_scheduled": 1826, + "price_disclosure": "published" + } + ], + "status": "ok", + "reason": null, + "value_usd_gpu_hr": 3.52045, + "stability_band_usd_gpu_hr": 0.54375, + "restatements": [] +} diff --git a/tests/unit/test_collect_latest_receipts.py b/tests/unit/test_collect_latest_receipts.py index 8193eba..793a133 100644 --- a/tests/unit/test_collect_latest_receipts.py +++ b/tests/unit/test_collect_latest_receipts.py @@ -67,7 +67,7 @@ def _latest(receipts: list[dict]) -> dict: { "sku": "H100", "current_version": 2, - "methodology_id": "h100_sxm_v1_calc_v10", + "methodology_id": "h100_sxm_v1_calc_v16", } ], "observations": [ @@ -79,7 +79,7 @@ def _latest(receipts: list[dict]) -> dict: }, { "sku": "H100", - "methodology_id": "h100_sxm_v1_calc_v10", + "methodology_id": "h100_sxm_v1_calc_v16", "observed_at": "2026-09-01T08:45:00.000Z", "receipts": receipts, }, @@ -271,7 +271,7 @@ def test_exit_codes_are_checked_directly_without_a_pipeline( report = collect_module.CollectionReport( sku="H100", observed_at="2026-09-01T08:45:00.000Z", - methodology_id="h100_sxm_v1_calc_v10", + methodology_id="h100_sxm_v1_calc_v16", checks=tuple( collect_module.SeatCheck("H100", f"source-{i}", state) for i, state in enumerate(checks) @@ -285,7 +285,7 @@ def test_current_pointer_selects_the_latest_methodology(collect_module): current = collect_module._select_latest_observation( # noqa: SLF001 _latest([]), "H100" ) - assert current["methodology_id"] == "h100_sxm_v1_calc_v10" + assert current["methodology_id"] == "h100_sxm_v1_calc_v16" assert current["observed_at"] == "2026-09-01T08:45:00.000Z" diff --git a/tests/unit/test_cross_repo_carried_vote_pin.py b/tests/unit/test_cross_repo_carried_vote_pin.py new file mode 100644 index 0000000..6b26f26 --- /dev/null +++ b/tests/unit/test_cross_repo_carried_vote_pin.py @@ -0,0 +1,148 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2026 Computable +"""The COM-1570 carried-vote CROSS-REPO WIRE-SHAPE PIN. + +``tests/fixtures/cross_repo/carried-vote-pin.observation.json`` is the verbatim +production artifact ``h100_sxm_v1_calc_v16`` 2026-09-14T18:30Z — the first +smoothing-armed generation, whose ``vast`` seat is a live fence-reject carry +(fresh print 5.8006 rejected by the sigma fence; the engine cast the smoothed +booked vote 4.60857 carried from 17:45) — exactly as the index publisher's +projector published it. + +The load-bearing wire shape is FLAT: the publisher flattens the engine's +``carried_vote`` block to the receipt-level ``carried_vote_from`` key, with +``carry_basis`` and the cast (``smoothed_vote_usd``) beside it on a +``status: ok`` + ``filter_verdict: rejected`` receipt. The publisher's own +suite pins the same projection over the same bytes; this suite feeds them to +the reproducer and requires the published value EXACTLY. Whichever side +reshapes the wire goes red in its own suite instead of against the public +reproduce guarantee — and a nested ``carried_vote`` object is pinned REFUSED, +not silently admitted. +""" + +from __future__ import annotations + +import copy +import json +from pathlib import Path + +import pytest + +from gpu_index.published.verify import ( + VERDICT_MATCH, + VERDICT_MISMATCH, + PublishedRecordError, + _carried_vote_disclosed, + recompute_observation, +) + +PIN = ( + Path(__file__).resolve().parents[1] + / "fixtures" + / "cross_repo" + / "carried-vote-pin.observation.json" +) + +PUBLISHED_VALUE = 3.52045 +PUBLISHED_BAND = 0.54375 +VAST_CAST = 4.60857 + + +def _pinned() -> dict: + return json.loads(PIN.read_bytes()) + + +def _vast(observation: dict) -> dict: + (receipt,) = [ + receipt + for receipt in observation["receipts"] + if receipt["source_id"] == "vast" + ] + return receipt + + +def test_pin_bytes_carry_the_flat_fence_reject_disclosure(): + """The wire-shape pin itself: the projected receipt discloses the FLAT + keys — carried_vote_from + carry_basis + the cast — beside the real + rejected print, and no nested object.""" + vast = _vast(_pinned()) + assert vast["status"] == "ok" + assert vast["upstream_status"] == "ok" + assert vast["filter_verdict"] == "rejected" + assert vast["price"] == 5.8006 + assert vast["carried_vote_from"] == "2026-09-14T17:45:00.000Z" + assert vast["carry_basis"] == "no_price" + assert vast["smoothed_vote_usd"] == VAST_CAST + assert "carried_vote" not in vast + assert _carried_vote_disclosed(vast) + + +def test_pin_reproduces_the_published_value_exactly(): + """Happy path, no synthetic edits: the smoothing-armed ballot over the + disclosed casts lands EXACTLY on the engine's published value and band. + A nudged cast misses, so the match is the vote itself, not a plateau.""" + check = recompute_observation(_pinned()) + assert check.verdict == VERDICT_MATCH + assert check.recomputed_value == PUBLISHED_VALUE + assert check.recomputed_band == PUBLISHED_BAND + + nudged = _pinned() + _vast(nudged)["smoothed_vote_usd"] = VAST_CAST + 1.5 + assert recompute_observation(nudged).verdict == VERDICT_MISMATCH + + +def test_dropping_the_flat_disclosure_drops_the_seat_and_reds_the_reproduce(): + """The contrapositive this pin exists for: a projector that stops + flattening the disclosure makes the row read as a plain sigma reject — + the seat's vote drops and the recompute walks away from the published + value. Loud red, never a silently different ballot admitted.""" + stripped = _pinned() + vast = _vast(stripped) + del vast["carried_vote_from"] + del vast["carry_basis"] + del vast["smoothed_vote_usd"] + check = recompute_observation(stripped) + assert check.verdict == VERDICT_MISMATCH + + +def test_nested_object_shape_reads_as_absent(): + """A nested carried_vote object is not the wire shape and is REFUSED as + a disclosure: the presence fence reads it absent, the seat stops voting, + and the reproduce reds — pinning that publisher and reproducer cannot + silently diverge on shape.""" + nested = _pinned() + vast = _vast(nested) + vast["carried_vote"] = { + "from": vast.pop("carried_vote_from"), + "carry_basis": vast.pop("carry_basis"), + "age_minutes": 45, + } + del vast["smoothed_vote_usd"] + assert not _carried_vote_disclosed(vast) + check = recompute_observation(nested) + assert check.verdict == VERDICT_MISMATCH + + +def test_armed_pin_refuses_without_a_cast(): + """Admission proof: the flat disclosure ADMITS the vast vote, so a + missing cast price refuses loudly naming the seat — never a silent + recompute without it.""" + torn = _pinned() + del _vast(torn)["smoothed_vote_usd"] + with pytest.raises(PublishedRecordError, match="vast") as caught: + recompute_observation(torn) + assert "smoothed_vote_usd" in str(caught.value) + + +def test_dressing_on_the_flat_key_reads_as_absent(): + """Number/object/array dressing on carried_vote_from is not a + disclosure: the fence reads it absent and the seat drops back onto the + plain law, which fails closed on the rejected verdict (mismatch, never + admission).""" + for dressing in (45, {"from": "2026-09-14T17:45:00.000Z"}, ["17:45"], ""): + dressed = copy.deepcopy(_pinned()) + vast = _vast(dressed) + vast["carried_vote_from"] = dressing + del vast["smoothed_vote_usd"] + assert not _carried_vote_disclosed(vast) + assert recompute_observation(dressed).verdict == VERDICT_MISMATCH diff --git a/tests/unit/test_minute_lattice.py b/tests/unit/test_minute_lattice.py index 90e9c90..951b211 100644 --- a/tests/unit/test_minute_lattice.py +++ b/tests/unit/test_minute_lattice.py @@ -589,6 +589,12 @@ def test_minute_keyed_lane_refuses_without_live_lever( } ] base["calc"]["methodology_id"] = "h100_sxm_v1_calc_v99" + # This test is about the minute-lane LEVER, not the smoothed + # generation: strip the COM-1582/COM-1570 knobs the shipping config + # carries, or the engine's own does-not-rerun-the-EWMA refusal fires + # first (pinned separately in test_panel_engine). + base["calc"].pop("pre_smoothing_half_life_hours") + base["calc"]["dynamic_weights"].pop("fence_reject_carry") cfg_path = tmp_path / "minute_lane.json" cfg_path.write_text(json.dumps(base)) diff --git a/tests/unit/test_panel_configs.py b/tests/unit/test_panel_configs.py index e9a689f..783d4bc 100644 --- a/tests/unit/test_panel_configs.py +++ b/tests/unit/test_panel_configs.py @@ -51,7 +51,7 @@ LANES = { "config/index_panel_b300.json": { "panel_id": "b300", - "methodology_id": "annex_a_v0_2_calc_v14", + "methodology_id": "annex_a_v0_2_calc_v17", "prefix": "index/b300_basket", "genesis": "2026-08-10", "claim_floor": 5, @@ -63,7 +63,7 @@ }, "config/index_panel_b200.json": { "panel_id": "b200", - "methodology_id": "annex_a2_v0_3_calc_v14", + "methodology_id": "annex_a2_v0_3_calc_v17", "prefix": "index/b200_basket", "genesis": "2026-08-16", "claim_floor": 5, @@ -75,7 +75,7 @@ }, "config/index_panel_h100_sxm.json": { "panel_id": "h100_sxm", - "methodology_id": "h100_sxm_v1_calc_v10", + "methodology_id": "h100_sxm_v1_calc_v16", "prefix": "index/h100_sxm", "genesis": "2026-08-23", "claim_floor": 5, @@ -87,7 +87,7 @@ }, "config/index_panel_h200_sxm.json": { "panel_id": "h200_sxm", - "methodology_id": "h200_sxm_v1_calc_v10", + "methodology_id": "h200_sxm_v1_calc_v16", "prefix": "index/h200_sxm", "genesis": "2026-08-23", "claim_floor": 5, @@ -264,10 +264,18 @@ def test_public_lane_configs_match_the_live_era3_calculation(configs): assert calc["vote_sigma_floor_pct"] == 3.0, rel assert calc["carry_forward_window_hours"] == 72, rel assert calc["carry_forward_failure_kinds"] == ["fetch", "parse"], rel + # EWMA vote pre-smoothing + fence-reject carry (the 2026-09-14 + # generation: calc_v17 / calc_v16 mints). The half-life is CALC + # law this repo never reruns (compute_observation refuses an + # armed lane; smoothed generations reproduce from the published + # record's disclosed cast prices) -- pinned here so the shipped + # configs track the live mints byte-exactly. + assert calc["pre_smoothing_half_life_hours"] == 1, rel dynamic = calc["dynamic_weights"] assert dynamic["attendance_half_life_hours"] == 6, rel assert dynamic["attendance_eta"] == 0.5, rel assert dynamic["no_price_exclusion_hours"] == 24, rel + assert dynamic["fence_reject_carry"] is True, rel def test_migrated_lanes_carry_daily_manual_exclusion_pairs_neutral_reasons( diff --git a/tests/unit/test_panel_engine.py b/tests/unit/test_panel_engine.py index d3281dc..9040f4d 100644 --- a/tests/unit/test_panel_engine.py +++ b/tests/unit/test_panel_engine.py @@ -866,6 +866,106 @@ def _without_dw(c): assert "vote_sigma_source" not in legacy +def _armed_carry_config(): + """A valid attendance-armed config carrying the COM-1570 sub-knob.""" + cfg = _config() + cfg["calc"]["carry_forward_window_hours"] = 24 + cfg["calc"]["carry_forward_failure_kinds"] = ["fetch"] + cfg["calc"]["dynamic_weights"].update( + attendance_half_life_hours=6, + attendance_eta=0.5, + no_price_exclusion_hours=12, + fence_reject_carry=True, + ) + return cfg + + +def test_config_pre_smoothing_half_life_validation_and_conditional_embed(): + """COM-1582 EWMA vote pre-smoothing: a number in (0, 2] (the upstream + engine's checkpoint-exactness ceiling, mirrored verbatim), embedded + CONDITIONALLY like iqm_alpha so knob-less lanes' artifact bytes never + grow. This repo never reruns the EWMA -- compute_observation refuses + an armed params set (pinned below).""" + for bad in (0, -1, 2.5, True, "1", None, float("nan")): + _reject( + "pre_smoothing_half_life_hours", + lambda c, bad=bad: c["calc"].update( + pre_smoothing_half_life_hours=bad + ), + ) + # The unknown-key fence stays armed AROUND the new key (typo class). + _reject( + "unrecognized key", + lambda c: c["calc"].update(pre_smoothing_half_life_hrs=1), + ) + for good in (0.25, 1, 2): + cfg = _config() + cfg["calc"]["pre_smoothing_half_life_hours"] = good + validate_panel_config(cfg) + assert panel_calc_params(cfg)[ + "pre_smoothing_half_life_hours" + ] == float(good) + assert "pre_smoothing_half_life_hours" not in panel_calc_params(_config()) + + +def test_config_fence_reject_carry_validation_and_conditional_embed(): + """COM-1570: strict bool, requires attendance_eta > 0 (there is no + state-2 carry book on an unarmed lane), and the params embed writes + the key only as literal True -- absent/False lanes' bytes are + untouched (the D2 dark contract).""" + _reject( + "fence_reject_carry must be a\\s+boolean", + lambda c: c["calc"]["dynamic_weights"].update(fence_reject_carry=1), + ) + _reject( + "fence_reject_carry requires", + lambda c: c["calc"]["dynamic_weights"].update( + fence_reject_carry=True + ), + ) + # The unknown-key fence stays armed AROUND the new key (typo class). + _reject( + "unrecognized key", + lambda c: c["calc"]["dynamic_weights"].update(fence_carry=True), + ) + armed = _armed_carry_config() + validate_panel_config(armed) + assert ( + panel_calc_params(armed)["dynamic_weights"]["fence_reject_carry"] + is True + ) + dark = _armed_carry_config() + dark["calc"]["dynamic_weights"]["fence_reject_carry"] = False + validate_panel_config(dark) + assert ( + "fence_reject_carry" not in panel_calc_params(dark)["dynamic_weights"] + ) + assert ( + "fence_reject_carry" + not in panel_calc_params(_config())["dynamic_weights"] + ) + + +def test_compute_observation_refuses_smoothing_armed_lanes_loudly(): + """The producer mirror carries no EWMA vote state and no fence-reject + carry path: pricing an armed lane from the raw record would publish + RAW vote centers under a methodology_id whose law says otherwise -- + the silently-inert class this engine refuses everywhere else. The + supported reproduction of a smoothed generation is the published + record's own disclosed cast prices (gpu_index.published).""" + smoothed = _config() + smoothed["calc"]["pre_smoothing_half_life_hours"] = 1 + with pytest.raises(ValueError, match="does not rerun the EWMA"): + _compute(smoothed, _snapshot([]), _state()) + with pytest.raises(ValueError, match="does not rerun the EWMA"): + _compute(_armed_carry_config(), _snapshot([]), _state()) + # The refusal is knob-scoped: the same lane without the knobs runs. + dark = _armed_carry_config() + dark["calc"]["dynamic_weights"]["fence_reject_carry"] = False + payload = _compute(dark, _snapshot([]), _state()) + assert payload["kind"] == "index_panel_composite" + + def test_dw_vote_tail_slice_rules(): """The dw_history vote tail (ruling 2026-08-27), every slice rule on a hand-built prices series: the span is [obs_stamp - window_minutes, diff --git a/tests/unit/test_panel_golden_artifact.py b/tests/unit/test_panel_golden_artifact.py index 46934f0..b80d755 100644 --- a/tests/unit/test_panel_golden_artifact.py +++ b/tests/unit/test_panel_golden_artifact.py @@ -125,6 +125,7 @@ def _frozen_v6_config(tmp_path: Path) -> Path: for key in ( "filter_sigma_floor_pct", "iqm_alpha", + "pre_smoothing_half_life_hours", "vote_sigma_source", "vote_sigma_floor_pct", "carry_forward_window_hours", @@ -135,6 +136,7 @@ def _frozen_v6_config(tmp_path: Path) -> Path: "attendance_half_life_hours", "attendance_eta", "no_price_exclusion_hours", + "fence_reject_carry", ): calc["dynamic_weights"].pop(key) calc["filter_sigma_floor"] = 0.05 diff --git a/tests/unit/test_panel_verify_published.py b/tests/unit/test_panel_verify_published.py index 74f591e..281dd16 100644 --- a/tests/unit/test_panel_verify_published.py +++ b/tests/unit/test_panel_verify_published.py @@ -111,6 +111,7 @@ def _frozen_v6_config(tmp_path: Path) -> Path: for key in ( "filter_sigma_floor_pct", "iqm_alpha", + "pre_smoothing_half_life_hours", "vote_sigma_source", "vote_sigma_floor_pct", "carry_forward_window_hours", @@ -121,6 +122,7 @@ def _frozen_v6_config(tmp_path: Path) -> Path: "attendance_half_life_hours", "attendance_eta", "no_price_exclusion_hours", + "fence_reject_carry", ): calc["dynamic_weights"].pop(key) calc["filter_sigma_floor"] = 0.05 diff --git a/tests/unit/test_published_full.py b/tests/unit/test_published_full.py index fa4d4d4..5f9a0ee 100644 --- a/tests/unit/test_published_full.py +++ b/tests/unit/test_published_full.py @@ -677,3 +677,187 @@ def run(*flags): (root / f"H100/v1/{day_key}").unlink() assert run().returncode == 2 assert run("--receipts").stdout == receipts.stdout + + +# ---------------------------------------- smoothing-armed generations +# COM-1582 EWMA vote pre-smoothing + COM-1570 fence_reject_carry (the +# 2026-09-14 calc_v17/calc_v16 mints). Raw-only reproduction does NOT +# rerun the engine's EWMA: on an armed lane every voting receipt +# disclosed its exact cast price (smoothed_vote_usd), which prices the +# vote; the raw print stays evidence (it still feeds the weight-series +# history, except on fence-reject carries, whose print the engine +# deliberately keeps out of the presence record). + + +def _armed_observation(*, cast_shift=0.11): + observation = _observation() + observation["calc_params"]["pre_smoothing_half_life_hours"] = 1 + observation["calc_params"]["liveness"]["fence_reject_carry"] = True + for receipt in observation["receipts"]: + receipt["smoothed_vote_usd"] = receipt["price"] + cast_shift + return observation + + +def test_full_armed_votes_disclosed_cast_prices_never_raw(): + """Fresh prints stay 1..5 but every seat cast price+0.11: the + derivation prices the casts (3.11/1.1), and a published value equal + to the raw ballot's 3.0 MISMATCHES -- the contrapositive pins that + the vote centers moved off the raw prints.""" + observation = _armed_observation() + observation["value_usd_gpu_hr"] = 3.11 + result = reproduce_full_history([observation], target_date="2026-09-01") + assert result.checks[0].verdict == VERDICT_MATCH + assert result.checks[0].derived_value == 3.11 + assert result.checks[0].derived_band == 1.1 + + raw_published = _armed_observation() + result = reproduce_full_history( + [raw_published], target_date="2026-09-01" + ) + assert result.checks[0].verdict == "mismatch" + assert result.checks[0].derived_value == 3.11 + assert result.checks[0].published_value == 3.0 + + +def _fence_reject_pair(*, min_publish_second=4): + """Two armed stamps; on the second, s2's fresh 9.0 print is + fence-rejected and its booked smoothed vote (3.0, from the first + stamp) is cast instead. The published carry outputs on the row + (sd) and its weight-print terms (currency) are corrupted on purpose: + a raw-only reconstruction must resolve the vote dispersion from the + prior stamp's bytes and must NOT feed the rejected print into the + weight history (the engine keeps the seat out of the presence + record).""" + first = _armed_observation(cast_shift=0.0) + second = copy.deepcopy(first) + second["observed_at"] = "2026-09-01T00:15:00.000Z" + second["calc_params"]["min_sources_to_publish"] = min_publish_second + rejected = second["receipts"][2] + rejected.update( + { + "filter_verdict": "rejected", + "price": 9.0, + "smoothed_vote_usd": 3.0, + # The corpus flattens the engine's carried_vote block (the + # publisher's projection; the cross-repo pin suite holds the + # shape). + "carried_vote_from": "2026-09-01T00:00:00.000Z", + "carry_basis": "no_price", + "sd": 999.0, + "currency": None, + "fx_rate": None, + } + ) + return first, second + + +def test_full_armed_fence_reject_carried_vote_recasts_the_booked_price(): + first, second = _fence_reject_pair() + result = reproduce_full_history( + [first, second], target_date="2026-09-01" + ) + assert [check.verdict for check in result.checks] == [ + VERDICT_MATCH, + VERDICT_MATCH, + ] + # s2 voted its booked 3.0 (never the rejected 9.0, which would move + # the IQM), with the BOOKED dispersion (the row's 999.0 is ignored) + # and its CURRENT weight; the corrupted currency proves the rejected + # print never reached public_weight_print (not a weight-series row). + assert result.checks[1].derived_value == 3.0 + assert result.checks[1].derived_band == 1.1 + + +def test_full_armed_carried_votes_never_satisfy_the_observed_floor(): + """Five seats vote on the second stamp but s2's is a carried vote: + 4 observed < min_sources_to_publish 5 derives NO composite.""" + first, second = _fence_reject_pair(min_publish_second=5) + result = reproduce_full_history( + [first, second], target_date="2026-09-01" + ) + assert result.checks[0].verdict == VERDICT_MATCH + assert result.checks[1].verdict == "mismatch" + assert result.checks[1].derived_value is None + + +def test_full_armed_status_carried_recasts_the_frozen_smoothed_state(): + """A status-carried row disclosed its frozen smoothed state (4.5, + deliberately != its booked raw 3.0): the vote prices the DISCLOSED + cast, with the booked dispersion and the current fading weight -- + corrupting the row's published price/sd changes nothing else.""" + first = _armed_observation(cast_shift=0.0) + second = copy.deepcopy(first) + second["observed_at"] = "2026-09-01T00:15:00.000Z" + # The carried voter never satisfies the observed floor (4 observed). + second["calc_params"]["min_sources_to_publish"] = 4 + carried = second["receipts"][2] + carried.update( + { + "upstream_status": "carried", + "carry_basis": "no_price", + "price": 999.0, + "sd": 999.0, + "smoothed_vote_usd": 4.5, + } + ) + second["value_usd_gpu_hr"] = 3.700018 + second["stability_band_usd_gpu_hr"] = 1.800018 + result = reproduce_full_history( + [first, second], target_date="2026-09-01" + ) + assert [check.verdict for check in result.checks] == [ + VERDICT_MATCH, + VERDICT_MATCH, + ] + assert result.checks[1].derived_value == 3.700018 + assert result.checks[1].derived_band == 1.800018 + + +def test_full_armed_missing_cast_price_refuses_naming_stamp_and_seat(): + observation = _armed_observation() + del observation["receipts"][2]["smoothed_vote_usd"] + with pytest.raises(FullReproductionRefusal) as caught: + reproduce_full_history([observation], target_date="2026-09-01") + assert caught.value.code == "missing_cast_price" + assert "2026-09-01T00:00:00.000Z" in str(caught.value) + assert "s2" in str(caught.value) + + +@pytest.mark.parametrize("unusable", ["3.11", 0, -1, float("inf")]) +def test_full_armed_unusable_cast_price_refuses(unusable): + observation = _armed_observation() + observation["receipts"][2]["smoothed_vote_usd"] = unusable + with pytest.raises(FullReproductionRefusal) as caught: + reproduce_full_history([observation], target_date="2026-09-01") + assert caught.value.code == "unusable_cast_price" + assert "s2" in str(caught.value) + + +@pytest.mark.parametrize("invalid", ["1h", 0, -1, None, True]) +def test_full_invalid_pre_smoothing_half_life_refuses(invalid): + observation = _armed_observation() + observation["calc_params"]["pre_smoothing_half_life_hours"] = invalid + with pytest.raises(FullReproductionRefusal) as caught: + reproduce_full_history([observation], target_date="2026-09-01") + assert caught.value.code == "invalid_smoothing_params" + + +def test_armed_classifier_reads_a_fence_reject_carried_vote_as_absent(): + """The engine's carried_vote arm: the row's status/print stay the + untouched real print, so the disclosure block is the ONLY absence + signal -- and it only exists on armed lanes (the same receipt on a + pre-smoothing observation classifies present, byte-identically to + today).""" + observation = _armed_observation(cast_shift=0.0) + observation["receipts"][2].update( + { + "filter_verdict": "rejected", + "carried_vote_from": "2026-09-01T00:00:00.000Z", + "carry_basis": "no_price", + } + ) + assert public_attendance_events(observation) == {"s2": EVENT_NO_PRICE} + + unarmed = copy.deepcopy(observation) + del unarmed["calc_params"]["pre_smoothing_half_life_hours"] + assert public_attendance_events(unarmed) == {} diff --git a/tests/unit/test_published_verify.py b/tests/unit/test_published_verify.py index 308f572..ffafec5 100644 --- a/tests/unit/test_published_verify.py +++ b/tests/unit/test_published_verify.py @@ -15,7 +15,10 @@ Era note: the b300/b200 lanes ran a 4-slot grid before 2026-08-24, hourly next, and all four public lanes moved to 15 minutes from 2026-08-29. Historical hourly and slot densities are fixture-covered; -the projected era3 case carries the declared IQM alpha explicitly. +the projected era3 case carries the declared IQM alpha explicitly. The +2026-09-14 smoothing-armed generations (EWMA vote pre-smoothing + +fence_reject_carry; disclosed cast prices) are covered in the final +section of this file. """ from __future__ import annotations @@ -430,3 +433,296 @@ def test_invalid_iqm_alpha_refuses_on_insufficient_no_print(): observation["calc_params"]["iqm_alpha"] = 0.75 with pytest.raises(PublishedRecordError, match="calc_params.iqm_alpha"): recompute_observation(observation) + + +# ---------------------------------------- smoothing-armed generations +# COM-1582 EWMA vote pre-smoothing + COM-1570 fence_reject_carry (the +# 2026-09-14 calc_v17/calc_v16 mints). On an armed lane +# (calc_params.pre_smoothing_half_life_hours present) every voting +# receipt disclosed smoothed_vote_usd -- the EXACT cast price the engine +# aggregated -- and the recompute votes that number, never the raw print. +# The expected values below are the upstream extraction pass's pinned +# vectors (oracle: the engine's own median_stddev_composite). + + +def _armed_receipt( + sid, + *, + price, + cast, + sd=0.15, + weight=0.15, + verdict="accepted", + upstream="ok", + carried_vote=None, + disclosure="published", +): + receipt = { + "source_id": sid, + "price_disclosure": disclosure, + "status": "ok", + "upstream_status": upstream, + "filter_verdict": verdict, + "price": price, + "sd": sd, + "weight": weight, + } + if cast is not None: + receipt["smoothed_vote_usd"] = cast + if carried_vote is not None: + # The corpus flattens the engine's carried_vote block to receipt-level + # keys (the publisher's projection; the cross-repo pin suite holds + # the shape). + receipt["carried_vote_from"] = carried_vote["from"] + if "carry_basis" in carried_vote: + receipt["carry_basis"] = carried_vote["carry_basis"] + return receipt + + +def _fence_reject_receipt(sid, *, cast, price=2.09): + """The COM-1570 shape: the REAL rejected print stays on the row as + price, the vote was substituted from the carry book and disclosed.""" + return _armed_receipt( + sid, + price=price, + cast=cast, + verdict="rejected", + carried_vote={ + "from": "2026-09-14T00:45:00.000Z", + "carry_basis": "no_price", + }, + ) + + +def _armed_observation(receipts, *, min_publish=5, value=None, band=None): + return { + "kind": "gpu_price_index_observation", + "sku": "H100", + "observed_at": "2026-09-14T01:00:00.000Z", + "status": "ok", + "reason": None, + "calc_params": { + "aggregation": "median_ci_votes", + "iqm_alpha": 0, + "pre_smoothing_half_life_hours": 1, + "min_sources_to_publish": min_publish, + }, + "value_usd_gpu_hr": value, + "stability_band_usd_gpu_hr": band, + "receipts": receipts, + } + + +def test_armed_lane_votes_disclosed_cast_prices_never_raw(): + """V2: six fresh seats print 7.5 but each cast its EWMA-smoothed + 7.61 -- the recompute must price the disclosed casts exactly, and + voting the raw prints instead reproduces 7.5 != published 7.61 (the + contrapositive proves the vote centers moved).""" + receipts = [ + _armed_receipt(sid, price=7.5, cast=7.61) for sid in "abcdef" + ] + check = recompute_observation( + _armed_observation(receipts, value=7.61, band=0.15) + ) + assert check.verdict == VERDICT_MATCH + assert check.recomputed_value == 7.61 + assert check.recomputed_band == 0.15 + + raw_published = recompute_observation( + _armed_observation(receipts, value=7.5, band=0.15) + ) + assert raw_published.verdict == VERDICT_MISMATCH + assert raw_published.recomputed_value == 7.61 + + +def test_fence_reject_carried_vote_admits_at_its_disclosed_cast_price(): + """V3: seat f's fresh 2.09 print was fence-rejected but the engine + cast its booked smoothed 7.62 -- the row votes 7.62 (never 2.09, + never nothing), is classified carried, and the five observed seats + alone satisfy min_sources_to_publish 5.""" + receipts = [ + _armed_receipt(sid, price=7.5, cast=7.5) for sid in "abcde" + ] + [_fence_reject_receipt("f", cast=7.62)] + check = recompute_observation( + _armed_observation(receipts, value=7.5, band=0.15) + ) + assert check.verdict == VERDICT_MATCH + assert check.recomputed_value == 7.5 + assert check.recomputed_band == 0.15 + + +def test_armed_lane_missing_cast_price_refuses_loudly_naming_the_seat(): + """V4 (the 2026-09-14T0100 sesterce incident shape): a participating + fence-reject row without smoothed_vote_usd = the engine cast a price + this artifact does not disclose. REFUSE -- never price the rejected + 2.09 (that derives 7.575/0.275 != published 7.635/0.215, so the + fallback would be a non-vacuous wrong answer) and never drop the + seat (a different ballot).""" + receipts = [ + _armed_receipt(sid, price=price, cast=price) + for sid, price in zip("abcde", (7.4, 7.5, 7.6, 7.85, 8.0)) + ] + [_fence_reject_receipt("sesterce", cast=None)] + observation = _armed_observation(receipts, value=7.635, band=0.215) + with pytest.raises(PublishedRecordError) as caught: + recompute_observation(observation) + message = str(caught.value) + assert "sesterce" in message + assert "2026-09-14T01:00:00.000Z" in message + assert "smoothed_vote_usd" in message + + +@pytest.mark.parametrize( + "unusable", ["7.62", float("nan"), 0, -1, float("inf")] +) +def test_armed_lane_unusable_cast_price_refuses_loudly(unusable): + """V4's present-but-unusable arm: a malformed disclosure is a torn + artifact, never a silent chosen fall-back.""" + receipts = [ + _armed_receipt(sid, price=7.5, cast=7.5) for sid in "abcde" + ] + [_fence_reject_receipt("sesterce", cast=unusable)] + with pytest.raises(PublishedRecordError, match="sesterce"): + recompute_observation( + _armed_observation(receipts, value=7.635, band=0.215) + ) + + +def test_armed_lane_missing_cast_price_on_a_fresh_seat_refuses_too(): + """The disclosure law covers EVERY voting flavor: an ok+accepted row + on an armed lane without smoothed_vote_usd refuses identically.""" + receipts = [ + _armed_receipt(sid, price=7.5, cast=7.5) for sid in "abcde" + ] + [_armed_receipt("f", price=7.5, cast=None)] + with pytest.raises(PublishedRecordError, match="f"): + recompute_observation( + _armed_observation(receipts, value=7.5, band=0.15) + ) + + +def test_status_carried_seat_recasts_its_frozen_smoothed_state(): + """V5: a carried seat's row keeps the booked raw print (7.9) as + price but disclosed its frozen smoothed state (7.58) -- the vote is + 7.58, classified carried, and the five observed seats satisfy the + floor.""" + receipts = [ + _armed_receipt(sid, price=7.5, cast=7.5) for sid in "abcde" + ] + [_armed_receipt("f", price=7.9, cast=7.58, upstream="carried")] + check = recompute_observation( + _armed_observation(receipts, value=7.5, band=0.15) + ) + assert check.verdict == VERDICT_MATCH + assert check.recomputed_value == 7.5 + assert check.recomputed_band == 0.15 + + +def test_carried_votes_never_satisfy_the_observed_floor(): + """V6: six seats vote but two are fence-reject carries -- only 4 + observed. min_sources_to_publish 5 rebuilds NO composite (a + published ok value mismatches; a published no_print is consistent); + min 4 rebuilds value 7.5 over all six votes.""" + def receipts(): + return [ + _armed_receipt(sid, price=7.5, cast=7.5) for sid in "abcd" + ] + [ + _fence_reject_receipt(sid, cast=7.5) for sid in ("e", "f") + ] + + over_floor = recompute_observation( + _armed_observation(receipts(), min_publish=5, value=7.5, band=0.15) + ) + assert over_floor.verdict == VERDICT_MISMATCH + assert any("4 observed" in m for m in over_floor.messages) + + dark = _armed_observation(receipts(), min_publish=5) + dark["status"] = "no_print" + dark["reason"] = "insufficient_coverage" + assert recompute_observation(dark).verdict == VERDICT_MATCH + + lower_floor = recompute_observation( + _armed_observation(receipts(), min_publish=4, value=7.5, band=0.15) + ) + assert lower_floor.verdict == VERDICT_MATCH + assert lower_floor.recomputed_value == 7.5 + + +def test_pre_smoothing_observations_keep_the_frozen_law_byte_identically(): + """The pin the task demands: WITHOUT the calc knob the disclosures + are ignored like any unfamiliar receipt field -- fresh seats vote + their raw prints, a fence-reject row never votes, and the floor + counts every passer.""" + receipts = [ + _armed_receipt(sid, price=7.5, cast=7.61) for sid in "abcde" + ] + [_fence_reject_receipt("f", cast=7.62)] + observation = _armed_observation(receipts, value=7.5, band=0.15) + del observation["calc_params"]["pre_smoothing_half_life_hours"] + check = recompute_observation(observation) + assert check.verdict == VERDICT_MATCH # raw 7.5 votes, f dropped + assert check.recomputed_value == 7.5 + + smoothed_published = _armed_observation(receipts, value=7.61, band=0.15) + del smoothed_published["calc_params"]["pre_smoothing_half_life_hours"] + assert recompute_observation(smoothed_published).verdict == ( + VERDICT_MISMATCH + ) + + +def test_armed_lane_requires_upstream_status_on_voting_rows(): + """Carried classification (the floor law) reads upstream_status -- + a voting row without it on an armed lane is a torn artifact.""" + receipts = [ + _armed_receipt(sid, price=7.5, cast=7.5) for sid in "abcdef" + ] + del receipts[2]["upstream_status"] + with pytest.raises(PublishedRecordError, match="upstream_status"): + recompute_observation( + _armed_observation(receipts, value=7.5, band=0.15) + ) + + +@pytest.mark.parametrize("invalid", ["1h", -1, 0, None, True, float("nan")]) +def test_invalid_pre_smoothing_half_life_refuses(invalid): + receipts = [ + _armed_receipt(sid, price=7.5, cast=7.5) for sid in "abcdef" + ] + observation = _armed_observation(receipts, value=7.5, band=0.15) + observation["calc_params"]["pre_smoothing_half_life_hours"] = invalid + with pytest.raises( + PublishedRecordError, match="pre_smoothing_half_life_hours" + ): + recompute_observation(observation) + + +def test_withheld_carried_voter_degrades_before_any_cast_price_demand(): + """The disclosure pass nulls a withheld row's prices; a withheld + VOTING row (here a fence-reject carry) degrades the observation to + digest-only exactly like any withheld contributor -- never a + spurious missing-cast-price refusal.""" + withheld = _fence_reject_receipt("f", cast=None) + withheld.update(price_disclosure="withheld", price=None, sd=None) + receipts = [ + _armed_receipt(sid, price=7.5, cast=7.5) for sid in "abcde" + ] + [withheld] + check = recompute_observation( + _armed_observation(receipts, value=7.5, band=0.15) + ) + assert check.verdict == VERDICT_DEGRADED + assert check.withheld_sources == ("f",) + + +def test_non_string_carried_vote_from_dressing_reads_as_absent(): + """The shared presence fence: number/object/array/empty dressing on the + flat carried_vote_from key is NOT a disclosure -- the row falls back + onto the plain law and, being fence-rejected, does not vote (fail + closed). A nested carried_vote object is one of the dressings, pinned + refused.""" + for dressing in ({"from": "2026-09-14T00:45:00.000Z"}, 1, ["no_price"], True, ""): + row = _armed_receipt( + "f", price=2.09, cast=7.62, verdict="rejected" + ) + row["carried_vote_from"] = dressing + receipts = [ + _armed_receipt(sid, price=7.5, cast=7.5) for sid in "abcde" + ] + [row] + check = recompute_observation( + _armed_observation(receipts, value=7.5, band=0.15) + ) + assert check.verdict == VERDICT_MATCH # five observed seats only From c0425709d1d60b82fc15541c3ed701bc579c7c00 Mon Sep 17 00:00:00 2001 From: hobbyistz <293619914+hobbyistz@users.noreply.github.com> Date: Mon, 14 Sep 2026 23:38:15 -0700 Subject: [PATCH 2/2] Use methodology terms in comments and docstrings; shorten the changelog entry Signed-off-by: hobbyistz <293619914+hobbyistz@users.noreply.github.com> --- CHANGELOG.md | 31 +++++-------------- src/gpu_index/index/panel.py | 8 ++--- src/gpu_index/index/panel_config.py | 10 +++--- src/gpu_index/published/full.py | 12 +++---- src/gpu_index/published/verify.py | 16 +++++----- .../unit/test_cross_repo_carried_vote_pin.py | 2 +- tests/unit/test_minute_lattice.py | 2 +- tests/unit/test_panel_engine.py | 6 ++-- tests/unit/test_published_full.py | 2 +- tests/unit/test_published_verify.py | 6 ++-- 10 files changed, 39 insertions(+), 56 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 43d04eb..1332f45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,30 +9,13 @@ their own keyspaces. Newest first. ## 2026-09-14 -EWMA vote pre-smoothing (half-life 1 hour) and fence-reject carry armed on -every public lane (`annex_a_v0_2_calc_v17`, `annex_a2_v0_3_calc_v17`, -`h100_sxm_v1_calc_v16`, `h200_sxm_v1_calc_v16`), effective at the production -promotion recorded in latest.json's `versions.succession`. Each voting -provider's cast price is now the time-based EWMA of its own accepted prints -(`calc_params.pre_smoothing_half_life_hours`); an outlier-rejected print's -provider re-casts its booked smoothed vote as a carried vote -(`calc_params.liveness.fence_reject_carry`). Every voting receipt disclosed its -exact cast price as `smoothed_vote_usd`; fence-reject carried votes carry a -`carried_vote_from` marker beside the unchanged rejected print. Carried votes never -satisfy the minimum passing panel. - -`./reproduce` prices each voting seat at its disclosed cast price on these -generations — it never reruns the smoothing state — and refuses loudly when a -voting receipt omits the disclosure, never falling back to the raw print. -Pre-smoothing versions re-derive byte-identically under the frozen raw-vote -rule. The local producer replay (`--producer`, `--lane`) refuses -smoothing-armed lanes outright: this mirror carries no EWMA vote state, and -pricing raw votes under a smoothed methodology_id would be silently wrong. -Provider-statistic parameters (the population floors of section 6.2 and their -`calc.statistic_params` overrides) act upstream of the recorded prints — they -shape each provider's chosen print before it is published — and are outside -the scope of reproduction from the published record, which starts from the -disclosed prints. +New methodology versions on every public lane: `annex_a_v0_2_calc_v17`, +`annex_a2_v0_3_calc_v17`, `h100_sxm_v1_calc_v16`, `h200_sxm_v1_calc_v16`. Each +provider's vote is now a one-hour exponentially weighted average of its own +accepted prints, a provider whose print is rejected by the outlier fence keeps +voting its last accepted value as a carried vote, and every receipt discloses the +price it cast (`smoothed_vote_usd`). `./reproduce` verifies these versions from the +disclosed cast prices. ## 2026-09-08 diff --git a/src/gpu_index/index/panel.py b/src/gpu_index/index/panel.py index f8d52bc..55f0220 100644 --- a/src/gpu_index/index/panel.py +++ b/src/gpu_index/index/panel.py @@ -754,7 +754,7 @@ def panel_calc_params(config: Dict[str, Any]) -> Dict[str, Any]: if "iqm_alpha" in calc else {} ), - # EWMA vote pre-smoothing (COM-1582, mint 2026-09-14): CONDITIONAL + # EWMA vote pre-smoothing (2026-09-14 generation): CONDITIONAL # like iqm_alpha -- absent means raw vote centers, so every frozen # predecessor's artifact bytes stay untouched and the D2 fence owns # the flip. NOTE this engine mirror never RUNS the smoothing: @@ -889,7 +889,7 @@ def panel_calc_params(config: Dict[str, Any]) -> Dict[str, Any]: if attendance_minted(dw) else {} ), - # Fence-reject carry (COM-1570): CONDITIONAL sub-knob of the + # Fence-reject carry: CONDITIONAL sub-knob of the # attendance triple, embedded only as literal True (the # upstream emitter rule: absent = fence rejects drop the # vote, today's bytes; load validation guarantees the strict @@ -1507,7 +1507,7 @@ def classify_attendance_source(detail: Dict[str, Any]) -> Optional[str]: # a print the provider published that we cannot use. return EVENT_NO_PRICE if isinstance(detail.get("carried_vote"), dict): - # COM-1570: a fence-rejected print whose VOTE was substituted + # a fence-rejected print whose VOTE was substituted # from the state-2 carry book. status/chosen/filter are the # untouched real print (replay and jump-reference reads need # them unchanged) -- this disclosure block is the ONLY signal @@ -1797,7 +1797,7 @@ def compute_observation( if "pre_smoothing_half_life_hours" in params or ( params.get("dynamic_weights") or {} ).get("fence_reject_carry"): - # COM-1582/COM-1570 fail-closed guard: this mirror carries no + # fail-closed guard: this mirror carries no # EWMA vote state and no fence-reject carry path, so running an # armed lane here would price RAW vote centers (and drop # fence-rejected seats' carried votes) under a methodology_id diff --git a/src/gpu_index/index/panel_config.py b/src/gpu_index/index/panel_config.py index 8a6a995..2ae8acd 100644 --- a/src/gpu_index/index/panel_config.py +++ b/src/gpu_index/index/panel_config.py @@ -129,7 +129,7 @@ dynamic_weights.history_days per-source history; requires median_ci_votes, and dw_history requires the dynamic_weights block), - pre_smoothing_half_life_hours? (COM-1582 EWMA vote + pre_smoothing_half_life_hours? (EWMA vote pre-smoothing, number in (0, 2] hours -- the 2h ceiling is the upstream engine's checkpoint- exactness bound; this repo never reruns the EWMA, @@ -170,7 +170,7 @@ no_price_exclusion_hours -- all three or none, validated by gpu_index.index.weights.validate_attendance_params, - plus the OPTIONAL COM-1570 sub-knob + plus the OPTIONAL fence-reject carry sub-knob fence_reject_carry (strict bool, requires attendance_eta > 0: a sigma-fence-rejected seat re-casts its booked vote from the state-2 carry @@ -397,7 +397,7 @@ def panel_schedule(config: Dict[str, Any]) -> PanelSchedule: "attendance_half_life_hours", "attendance_eta", "no_price_exclusion_hours", - # Fence-reject carry (COM-1570; names cross-repo frozen): a + # Fence-reject carry (names frozen in the published record): a # minted sub-knob of the attendance triple -- strict bool, # requires attendance_eta > 0. Validated in # _validate_dynamic_weights. @@ -1102,7 +1102,7 @@ def _positive_floor(value: Any) -> bool: f"'median_ci_votes', got {calc.get('composite_statistic')!r}" ) if "pre_smoothing_half_life_hours" in calc: - # COM-1582 EWMA vote pre-smoothing (mint 2026-09-14): each admitted + # EWMA vote pre-smoothing (2026-09-14 generation): each admitted # seat's vote price is the time-based EWMA of its own admitted # prints; carried votes re-cast the frozen smoothed state. The 2h # CEILING mirrors the upstream engine's exactness bound @@ -1596,7 +1596,7 @@ def _validate_dynamic_weights( except ValueError as exc: raise PanelConfigError(str(exc)) from exc if "fence_reject_carry" in dw: - # COM-1570: a sigma-fence reject is "we don't trust this print" + # a sigma-fence reject is "we don't trust this print" # exactly like an uncorroborated jump quarantine -- when armed, # both unify onto the SAME state-2 carry book/vote (carry_basis # "no_price"). The knob only makes sense where that book already diff --git a/src/gpu_index/published/full.py b/src/gpu_index/published/full.py index ec2c25b..fc2b44a 100644 --- a/src/gpu_index/published/full.py +++ b/src/gpu_index/published/full.py @@ -119,7 +119,7 @@ def _is_number(value: Any) -> bool: def _smoothing_armed(params: dict, *, observed_at: Any) -> bool: - """COM-1582: ``calc_params.pre_smoothing_half_life_hours`` arms the + """``calc_params.pre_smoothing_half_life_hours`` arms the smoothed seat law for the whole observation. Usability-validated only (the (0, 2] mint ceiling is lane law, not a reproduce precondition); absent means the pre-smoothing generations replay byte-identically.""" @@ -144,7 +144,7 @@ def _cast_price(receipt: dict, *, observed_at: Any) -> float: smoothed booked price), so the vote prices THAT number and the row's raw ``price`` stays evidence. A voting row without the disclosure (or with an unusable one) is a torn artifact: refuse loudly, never fall - back to the raw print (the 2026-09-14T0100 incident posture).""" + back to the raw print.""" source_id = receipt.get("source_id") cast = receipt.get("smoothed_vote_usd") if cast is None: @@ -206,7 +206,7 @@ def _project_classifier_row(receipt: dict, *, smoothing_armed: bool) -> dict: else: detail["filter"] = {} if smoothing_armed and _carried_vote_disclosed(receipt): - # COM-1570 fence-reject carry: the row keeps the real + # Fence-reject carry: the row keeps the real # rejected print (status/price/verdict untouched), only the # VOTE was substituted from the carry book -- the classifier # must read the seat absent, exactly like the engine's own @@ -261,7 +261,7 @@ def _trusted_receipts( ) continue if smoothing_armed and _carried_vote_disclosed(receipt): - # COM-1570 fence-reject carry: the real rejected print stays + # Fence-reject carry: the real rejected print stays # on the row, but the engine deliberately never advances the # weight-series presence record with it (the seat must read # attendance-ABSENT) -- so this reproduction must not feed @@ -531,7 +531,7 @@ def reproduce_full_history( and not attendance[str(receipt["source_id"])]["excluded"] } if smoothing_armed: - # COM-1570 fence-reject carried votes ride the SAME D4 + # Fence-reject carried votes ride the SAME D4 # fading-weight domain as the state-2 no_price carries: the # seat is absent from the presence record this stamp, but its # CURRENT (fading) weight prices its booked vote -- never the @@ -592,7 +592,7 @@ def reproduce_full_history( upstream_status = receipt.get("upstream_status") carried_voter = False if fence_reject_carried and upstream_status == "ok": - # COM-1570 fence-reject carry: the rejected verdict judged + # Fence-reject carry: the rejected verdict judged # the REAL print the row keeps as evidence; the engine # cast the seat's booked smoothed vote instead, at its # CURRENT fading weight (never the booked weight) with diff --git a/src/gpu_index/published/verify.py b/src/gpu_index/published/verify.py index 4c8657c..652470e 100644 --- a/src/gpu_index/published/verify.py +++ b/src/gpu_index/published/verify.py @@ -19,7 +19,7 @@ to the 25th/75th weighted vote percentiles. On a SMOOTHING-ARMED lane (``calc_params.pre_smoothing_half_life_hours`` -present -- the COM-1582 EWMA vote pre-smoothing generation) the seat law +present -- the EWMA vote pre-smoothing generation) the seat law changes, mirroring the engine's own disclosures rather than rerunning its EWMA state: @@ -29,9 +29,9 @@ price). The recompute votes THAT number; ``price`` stays the raw print evidence and is never a fallback -- a participating row missing the disclosure (or carrying an unusable one) is a torn - artifact and refuses loudly (the 2026-09-14T0100 incident posture); + artifact and refuses loudly; - a status "ok" + filter_verdict "rejected" receipt carrying the - ``carried_vote_from`` disclosure (the flattened COM-1570 + ``carried_vote_from`` disclosure (the flattened fence-reject carry fence_reject_carry marker, with ``carry_basis`` beside it) VOTES: the sigma fence rejected the fresh print (kept on the row as ``price`` for the record) and the engine cast the seat's booked @@ -194,7 +194,7 @@ def _finite_number(value: Any) -> bool: def _carried_vote_disclosed(receipt: dict) -> bool: - """The COM-1570 fence-reject carry presence fence, ONE predicate so + """The fence-reject carry presence fence, ONE predicate so admission and carried-classification can never disagree about what "disclosed" means: the public corpus flattens the engine's carried_vote block to the receipt-level ``carried_vote_from`` key (an ISO instant; @@ -249,7 +249,7 @@ def recompute_observation(observation: dict) -> ObservationCheck: f"observation {sku} {observed_at} " f"calc_params.min_sources_to_publish is {min_to_publish!r}" ) - # COM-1582: the calc knob arms the smoothed seat law for this whole + # the calc knob arms the smoothed seat law for this whole # observation (module docstring). Validated for usability only -- the # (0, 2] mint ceiling is lane law, enforced where lanes load, not a # reproduce precondition. @@ -287,7 +287,7 @@ def recompute_observation(observation: dict) -> ObservationCheck: ) if disclosure == "withheld": any_withheld = True - # The COM-1570 arm exists ONLY on smoothing-armed observations: + # The fence-reject carry arm exists ONLY on smoothing-armed observations: # pre-smoothing bytes replay under the frozen predicate below # untouched, whatever disclosure fields a row happens to carry. carried_vote_disclosed = smoothing_armed and _carried_vote_disclosed( @@ -321,7 +321,7 @@ def recompute_observation(observation: dict) -> ObservationCheck: cast = receipt.get("smoothed_vote_usd") if cast is None: # The engine cast a price this artifact does not - # disclose: the exact 2026-09-14T0100 incident shape. + # disclose: the 2026-09-14T01:00Z observation shape. # NEVER the raw print instead -- pricing the rejected or # pre-smoothing chosen would derive a silently plausible # wrong index. @@ -383,7 +383,7 @@ def recompute_observation(observation: dict) -> ObservationCheck: # The minimum-panel rule, verbatim from the panel engine: a composite # exists iff the passing set reaches min_sources_to_publish. On a # smoothing-armed lane the count is OBSERVED voters only -- carried - # votes (status-carried re-casts and COM-1570 fence-reject carries) + # votes (status-carried re-casts and fence-reject carries) # may move the median but never keep a dying panel lit (the engine's # claim-floor law; carried_voters stays empty pre-smoothing). observed_count = len(passing) - len(carried_voters) diff --git a/tests/unit/test_cross_repo_carried_vote_pin.py b/tests/unit/test_cross_repo_carried_vote_pin.py index 6b26f26..5f30031 100644 --- a/tests/unit/test_cross_repo_carried_vote_pin.py +++ b/tests/unit/test_cross_repo_carried_vote_pin.py @@ -1,6 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2026 Computable -"""The COM-1570 carried-vote CROSS-REPO WIRE-SHAPE PIN. +"""The carried-vote wire-shape pin. ``tests/fixtures/cross_repo/carried-vote-pin.observation.json`` is the verbatim production artifact ``h100_sxm_v1_calc_v16`` 2026-09-14T18:30Z — the first diff --git a/tests/unit/test_minute_lattice.py b/tests/unit/test_minute_lattice.py index 951b211..d7085b7 100644 --- a/tests/unit/test_minute_lattice.py +++ b/tests/unit/test_minute_lattice.py @@ -590,7 +590,7 @@ def test_minute_keyed_lane_refuses_without_live_lever( ] base["calc"]["methodology_id"] = "h100_sxm_v1_calc_v99" # This test is about the minute-lane LEVER, not the smoothed - # generation: strip the COM-1582/COM-1570 knobs the shipping config + # generation: strip the smoothing and carry knobs the shipping config # carries, or the engine's own does-not-rerun-the-EWMA refusal fires # first (pinned separately in test_panel_engine). base["calc"].pop("pre_smoothing_half_life_hours") diff --git a/tests/unit/test_panel_engine.py b/tests/unit/test_panel_engine.py index 9040f4d..0048ca6 100644 --- a/tests/unit/test_panel_engine.py +++ b/tests/unit/test_panel_engine.py @@ -867,7 +867,7 @@ def _without_dw(c): def _armed_carry_config(): - """A valid attendance-armed config carrying the COM-1570 sub-knob.""" + """A valid attendance-armed config carrying the fence-reject carry sub-knob.""" cfg = _config() cfg["calc"]["carry_forward_window_hours"] = 24 cfg["calc"]["carry_forward_failure_kinds"] = ["fetch"] @@ -881,7 +881,7 @@ def _armed_carry_config(): def test_config_pre_smoothing_half_life_validation_and_conditional_embed(): - """COM-1582 EWMA vote pre-smoothing: a number in (0, 2] (the upstream + """EWMA vote pre-smoothing: a number in (0, 2] (the upstream engine's checkpoint-exactness ceiling, mirrored verbatim), embedded CONDITIONALLY like iqm_alpha so knob-less lanes' artifact bytes never grow. This repo never reruns the EWMA -- compute_observation refuses @@ -909,7 +909,7 @@ def test_config_pre_smoothing_half_life_validation_and_conditional_embed(): def test_config_fence_reject_carry_validation_and_conditional_embed(): - """COM-1570: strict bool, requires attendance_eta > 0 (there is no + """strict bool, requires attendance_eta > 0 (there is no state-2 carry book on an unarmed lane), and the params embed writes the key only as literal True -- absent/False lanes' bytes are untouched (the D2 dark contract).""" diff --git a/tests/unit/test_published_full.py b/tests/unit/test_published_full.py index 5f9a0ee..6a6c10f 100644 --- a/tests/unit/test_published_full.py +++ b/tests/unit/test_published_full.py @@ -680,7 +680,7 @@ def run(*flags): # ---------------------------------------- smoothing-armed generations -# COM-1582 EWMA vote pre-smoothing + COM-1570 fence_reject_carry (the +# EWMA vote pre-smoothing + fence_reject_carry (the # 2026-09-14 calc_v17/calc_v16 mints). Raw-only reproduction does NOT # rerun the engine's EWMA: on an armed lane every voting receipt # disclosed its exact cast price (smoothed_vote_usd), which prices the diff --git a/tests/unit/test_published_verify.py b/tests/unit/test_published_verify.py index ffafec5..2dc3159 100644 --- a/tests/unit/test_published_verify.py +++ b/tests/unit/test_published_verify.py @@ -436,7 +436,7 @@ def test_invalid_iqm_alpha_refuses_on_insufficient_no_print(): # ---------------------------------------- smoothing-armed generations -# COM-1582 EWMA vote pre-smoothing + COM-1570 fence_reject_carry (the +# EWMA vote pre-smoothing + fence_reject_carry (the # 2026-09-14 calc_v17/calc_v16 mints). On an armed lane # (calc_params.pre_smoothing_half_life_hours present) every voting # receipt disclosed smoothed_vote_usd -- the EXACT cast price the engine @@ -480,7 +480,7 @@ def _armed_receipt( def _fence_reject_receipt(sid, *, cast, price=2.09): - """The COM-1570 shape: the REAL rejected print stays on the row as + """The fence-reject carry shape: the REAL rejected print stays on the row as price, the vote was substituted from the carry book and disclosed.""" return _armed_receipt( sid, @@ -552,7 +552,7 @@ def test_fence_reject_carried_vote_admits_at_its_disclosed_cast_price(): def test_armed_lane_missing_cast_price_refuses_loudly_naming_the_seat(): - """V4 (the 2026-09-14T0100 sesterce incident shape): a participating + """V4 (the 2026-09-14T01:00Z observation shape): a participating fence-reject row without smoothed_vote_usd = the engine cast a price this artifact does not disclose. REFUSE -- never price the rejected 2.09 (that derives 7.575/0.275 != published 7.635/0.215, so the