PRICE-02 round 2: require now, undo the test loosening, analyse ties
checks / unit (pull_request) Successful in 47m49s
infra-pin / fresh (pull_request) Successful in 2s

- TariffPlan.arbitrage(now=...) is required (keyword-only, no default); so are
  homeview.trade_verdict/refusal_key's now. Existing test calls pass one.
- test_arbitrage.py: no module-level decide wrapper. Fixtures that probed a
  one-day plan after its extremes now hold two days (as a site does); the
  flap day gets a unique cheapest and dearest hour; the replay breaks its
  ties without direction. The flap bound is <= 4 and the replay check is
  len(_trades) < 365 again. 228 checks pass on the old and the new code.
- test_price02.py part 6: ties (equal quarters, a real 15-minute day,
  alternating hourly prices) and the required now.
- Identity pins: the masked digest is only for the older-tree comparison;
  this tree's own digests are pinned with nothing hidden.
- DECISIONS: the tie analysis with numbers.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
glenn schrooyen
2026-10-22 10:37:43 +02:00
co-authored by Claude
parent 2ff9be710c
commit 891ed7bdd7
14 changed files with 362 additions and 134 deletions
+34 -8
View File
@@ -67,27 +67,53 @@ A, B = "inverter-a", "inverter-b"
# ============================================================================ #
print("1. modes off: byte-identical to 7a00f12 on the trade path")
# Printed on release/1.0 7a00f12 (2026-09-29), goodwe-addon-checks:1, 1080
# cycles each; the trade CHARGEs and DISCHARGEs. PRICE-02 (2026-10-22): the
# digest is `ident.trade_digest()`, which hides two things in the trade
# sentence: the "above the cheapest" figure (the plan now drops price periods
# that have ended, so it is another number once the first cheap period is
# over) and "hour" -> "price period". Re-measured with it on 7a00f12, on
# release/1.0 90bc901 and on this tree: the same four digests.
# cycles each; the trade CHARGEs and DISCHARGEs.
#
# PRICE-02 (2026-10-22) changed two things in the trade sentence and nothing
# else: the plan now drops price periods that have ended, so a sale's "N above
# the cheapest" is another number once the first cheap period is over, and the
# sentences say "price period" for "hour". So there are three pins:
# AT_7A00F12_OLD the digests printed on 7a00f12, sentence included. They no
# longer match, and the check below says so: that is what
# makes the mask necessary.
# AT_7A00F12 the same rows through `ident.trade_digest()`, which hides
# exactly those two things (and only for this comparison with
# an older tree). It equals what 7a00f12 gives through it.
# AT_NOW this tree's own digests, sentence included, with nothing
# hidden: any later change to a command, a sentence or a
# figure moves it. The mask cannot hide a future drift.
AT_7A00F12_OLD = {
"n1": "71245b227ca40bada4f71f57ea6154475f4b0bd60239c9ad4f80bffb0094514c",
"n2": "56cf28010b06765c29e5479abb815d7503d570b39240d82dfc818efa90c93722",
}
AT_7A00F12 = {
"n1": "33886ef03f364852883f0370230e6e04823fbb46a4704f88ca75bf9082d98c01",
"n2": "666a31f41491c22750720524853d95944d5fba395d1d14040c460d71414b275f",
"n1_off": "33886ef03f364852883f0370230e6e04823fbb46a4704f88ca75bf9082d98c01",
"n2_off": "666a31f41491c22750720524853d95944d5fba395d1d14040c460d71414b275f",
}
AT_NOW = {
"n1": "939b3d47bbd64fa50e3d70538f092fb98e5c4bd5ead85a7fc696a1dbb514039e",
"n2": "c51008213dd739e1e5142c236d9a950aa585992393e4af120086f1ba52ff3e0d",
"n1_off": "939b3d47bbd64fa50e3d70538f092fb98e5c4bd5ead85a7fc696a1dbb514039e",
"n2_off": "c51008213dd739e1e5142c236d9a950aa585992393e4af120086f1ba52ff3e0d",
}
_cmd_digest = ident.trade_digest
_rows = {k: ident.run(**ident.SCENARIOS[k])[0] for k in AT_7A00F12}
_got = {k: _cmd_digest(v) for k, v in _rows.items()}
for k in AT_7A00F12:
print(f" {k} {'same' if _got[k] == AT_7A00F12[k] else 'DIFFERS'} "
f"{_got[k][:16]}")
_full = {k: ident.digest(v) for k, v in _rows.items()}
check("with battery modes off (option absent or false), every command, "
"integrator, allocation and trade sentence equals 7a00f12's, at one "
"inverter and at two", _got == AT_7A00F12)
"integrator, allocation and trade action equals 7a00f12's, at one "
"inverter and at two (the sentence masked as above)",
_got == AT_7A00F12)
check("...the mask is needed and nothing more: unmasked, n1 and n2 differ "
"from 7a00f12's printed digests, and unmasked they equal this tree's "
"own pins, so a later change to any sentence or figure fails here",
all(_full[k] != AT_7A00F12_OLD[k] for k in AT_7A00F12_OLD)
and _full == AT_NOW)
_acts = {r[-1][0] for r in _rows["n2"] if r[-1]}
check("...and the runs reach the trade P3 now skips with modes on: a charge "
"and a sale", {arbitrage.CHARGE, arbitrage.DISCHARGE} <= _acts)