[EGC / Commodity Price] PeriodEnd == PeriodStart accepted, forecast ordering and bounds unenforced, and two TC_SEPRTestBase defects that hide it

Author: Apollon77Created Sep 16, 2026Updated Sep 17, 2026
Labelsenergy-seprenergy-egc

Verified on master (99a81bd3298). Six items: four about the time bounds on forecast/period data in Electrical Grid Conditions and Commodity Price, two test-base defects that hide them, and one reference-app arithmetic defect.

# What Why it matters
1 PeriodEnd == PeriodStart is accepted in both servers A controller gets a zero-length period where the spec mandates strict >
2 Cross-entry forecast time-ordering is enforced by nobody A controller cannot rely on forecast entries arriving in time order
3 EGC's kMaxForecastEntries is declared and never used ForecastConditions can exceed its max 56, so a client with a 56-entry buffer overruns
4 §9.9.6.4 is enforced nowhere A controller sees a non-empty PriceForecast alongside a null CurrentPrice.PeriodEnd
5 Two test-base defects The cert harness mis-evaluates a conformant device
6 The reference app's price components do not sum to the price Anyone copying the sample data ships a device that breaks §9.9.5.3.6

1 — PeriodEnd == PeriodStart is accepted in both servers

09-13-electrical-grid-conditions-cluster.md:75 gives PeriodEnd the constraint min (PeriodStart + 1) and :89 states "This SHALL be greater than PeriodStart." 09-09-commodity-price-cluster.md:105 is identical. Strict > is mandated.

  • EGC: electrical-grid-conditions-server.cpp:132 rejects only periodStart > periodEnd, so equality passes.
  • Commodity Price: the same defect survived the SCI rewrite, inverted — CommodityPriceCluster.cpp:93: VerifyOrReturnError(price.periodEnd.IsNull() || (price.periodStart <= price.periodEnd.Value()), ConstraintError).

2 — Cross-entry forecast time-ordering is enforced by nobody

The ForecastConditions / PriceForecast ordering SHALLs are implemented in no server and asserted by no test base (TC_SEPRTestBase.py:41-60, TC_EGCTestBase.py:36-52 check only list-ness, type and max-56). They survive only as a -1 fudge in the reference apps' trigger data: ElectricalGridConditionsEventTriggers.cpp:105-106 and CommodityPriceEventTriggers.cpp:115-116, both currentStart + k30MinsInSeconds - 1.

Note Commodity Price is now covered for the per-entry bound since the rewrite — PriceStorage::Set (CommodityPriceCluster.cpp:116-135) calls ValidatePrice per entry. EGC's SetForecastConditions (:165-174) still validates nothing per entry.

3 — EGC's kMaxForecastEntries is declared and never used

electrical-grid-conditions-server.h:32-33 declares it; nothing reads it, so ForecastConditions' max 56 is unenforced. Commodity Price's own copy is used, at CommodityPriceCluster.cpp:442.

4 — §9.9.6.4 ("if CurrentPrice.PeriodEnd is null then PriceForecast SHALL be empty") is enforced nowhere

SetCurrentPrice and SetForecast stay fully independent. Neither inspects the other's stored state.

5 — Two test-base defects that mis-evaluate a conformant device

These are bugs in the harness, not the servers, and they produce false results:

  • TC_SEPRTestBase.py:98/:102-103 test if struct.price is not NullValue: for optional fields, so an absent optional field reads as present. The assertion at :106 (assert_true(bPriceIncluded or bPriceLevelIncluded, ...)) can then never fail. A conformant DUT supplying only one of Price/PriceLevel is mis-evaluated. The correct idiom (is not None) is used nearby at :110 and :122, so the file disagrees with itself.
  • TC_SEPRTestBase.py:86-87 asserts assert_less_equal(struct.periodStart, now_time_epoch_s, ...) with the failure message "PeriodStart must not be in the past", while the comment at :84 says "verify that the PeriodStart is in the past". The message states the opposite of the assertion, so whoever debugs a failure here is sent the wrong way.

6 — The reference app's own price components do not sum to the price

§9.9.5.3.6 requires the components to sum to the Price. CommodityPriceEventTriggers.cpp:50 sets amount = 15916; :62 and :66 derive amount * 95 / 100 = 15120 and amount * 5 / 100 = 795, summing to 15915. Integer truncation leaves it one minor unit short, and nothing checks the rule. The same arithmetic repeats at :126/:130.

Related: #38581 (EGC deep-check and memory lifetime), #40272 (Commodity Tariff validation).

Found during a Matter 1.6.0 specification-versus-implementation review.

Source: project-chip/connectedhomeip