checks / unit (pull_request) Successful in 7m13s
Second base move while this card was open:559f0eb->a6e64fb. Only CHANGELOG.md conflicted; test_arbitrage.py auto-merged (TARIFF-12 used section 8b, this card uses 10, so the two do not collide). CHANGELOG: both entries under the single ## Unreleased in MERGE ORDER - TARIFF-12's row and section precede this branch's, because it landed first. No version number is picked; PKG-06/07's note stands untouched. WHAT TARIFF-12 CHANGES FOR THIS CARD - written out in decision note §8 rather than left silent, because the question is a fair one. A served plan can now be up to TARIFF_MAX_AGE_S = 3 * TARIFF_REFRESH_S = 5400 s old, where before it was at most one refresh interval, and at() is what answers "what does it cost right now" out of that older plan. * PER-POINT SPAN: unaffected, structurally. Every window is derived from the neighbours' ABSOLUTE start instants - datetimes in a frozen dataclass in a tuple, which cannot move - and _cover_table() runs once at construction over data that cannot change. A plan's AGE is a property of fetched_at/tariff_at, never of a window. The index cannot go stale, which is a small positive under TARIFF-12: a kept plan now lives longer and there is nothing in it that needed recomputing. * ONE-HOUR CAP: unaffected. It bites only at a hole; walking further into a series crosses more windows, each resolved on its own neighbours. * THE TAIL: reachable for the first time, and §2's choice survives it. At the shipped 48 h horizon the tail is ~46.5 h beyond anything a 90-minute-stale plan can be asked about. But price_horizon_h is int(1,72), so a 1 h horizon is a SUPPORTED configuration, and a 90-minute serve-stale window against a 1 h series puts `now` past the end. Under the chosen rule a quarter-hourly 1 h series ends at :45, its window closes on the hour, at() raises Absence, _act() idles and §11.4 sends the site to the static profile. Under the REJECTED flat one-hour tail the same stale plan would have traded against the :45 print for up to 45 minutes after the interval it describes had ended. So TARIFF-12 does not weaken the decision - it is the first thing that makes it reachable, and it selects the same answer. The rule should be re-read if TARIFF_MAX_AGE_S is ever widened again. * TariffPlan.from_feed is a pure predicate on `origin` and TARIFF-12 keeps or replaces a WHOLE PLAN OBJECT. It never splices two reads into one series, so it cannot hand at() an unsorted or duplicated point list. RE-GRADED ON THIS BASE, not carried over: all 29 test files green; app/price.py byte-identical to the TARIFF-10 merge (90d8ca31f7c0109d, 85953 B) because TARIFF-12 does not touch it; M1 re-driven at +68 B, red on the same 24 named checks across both files; M3 re-driven at ZERO BYTES, red on the same 4. Each purged before the run and reverted with the fingerprint re-checked, from a card-specific harness at /home/homelab/.cache/t14-mut/ with a mktemp backup inside it. This branch contributes zero diff to app/main.py and app/static_tariff.py. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BwRLw7eKYj2byVdwB6kbjU