Skip to content

fix(node): filter getDefaults() to conformant members - #4380

Draft
lboue wants to merge 3 commits into
matter-js:mainfrom
lboue:fix/commodity-tariff-randomization-type-default
Draft

fix(node): filter getDefaults() to conformant members#4380
lboue wants to merge 3 commits into
matter-js:mainfrom
lboue:fix/commodity-tariff-randomization-type-default

Conversation

@lboue

@lboue lboue commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Type of change

  • 🐛 Fix — corrects a defect or wrong behavior
  • Feature — adds new functionality or capability

Description

Supersedes #4375, which was correctly closed: that first pass hand-edited the generated
commodity-tariff.element.ts to drop RandomizationType's default: 0/"None" — but that default is
spec-mandated (Application Cluster Specification § 9.12.5.10, DayEntryStruct field table: RandomizationType's
Fallback column is None), so removing it "fixed" the crash by breaking the spec-correct read fallback for
when Randomization is supported and the field is omitted.

Actual root cause: ValuePatcher.ts's getDefaults() calls supervisor.membersOf(schema) with no
conformance filter, so it considers every declared member's default regardless of whether that member is
even conformant given the currently active features. Writing a dayEntries list entry that omits
RandomizationType synthesizes its default (None) even when Randomization is unsupported — where the
field can't exist at all — then rejects that very same synthesized value for violating the conformance gate
it was never asked to satisfy:

Conformance "[RNDM]": Matter does not allow you to set this attribute

SelectDefaultValue() (@matter/model) already guards this correctly with scope.hasOperationalSupport(member);
StructManager.ts's mandatory-default synthesis path already calls membersOf(member, { conformance: "conformant" })
for the same reason. getDefaults() has its own, separate member-default computation that skipped that
filter. This only surfaces on the list-of-struct write path — a bare, non-list struct attribute with the
identical field shape (e.g. CurrentDayEntry) doesn't trip it.

Fix: pass { conformance: "conformant" } to membersOf() in getDefaults(), matching the filter already
used elsewhere. No model files touched; commodity-tariff.element.ts's default: 0 stays as originally
declared.

Backing evidence

(Log attached on #4374, from the real-world crash in a Matterbridge plugin.) Two tests in
CommodityTariffServerTest.ts cover both sides: Randomization unsupported (the crash — omitting
RandomizationType must be accepted) and Randomization supported (omitting it must still read back as the
spec's None fallback, not undefined) — both fail without this fix, both pass with it.

Checklist

  • I understand the code I am submitting and can explain how it works (AI policy)
  • Tests added or updated to cover the change
  • npm test passes — ran @matter/model's build, @matter/node's full test/behaviors/** suite (391 tests, including both new regression tests) and @matter/general's suite (1330/1330).
  • npm run format-verify and npm run lint pass (both run at the repo root, not just on the touched files)
  • CHANGELOG updated

lboue and others added 3 commits August 31, 2026 00:40
…onType

DayEntryStruct.RandomizationType declared both conformance "[RNDM]" (only
allowed when the Randomization feature is supported) and default: 0. Writing
a dayEntries list entry that omits RandomizationType auto-filled it with that
default before validating conformance, then rejected the very same fill:

  Conformance "[RNDM]": Matter does not allow you to set this attribute

A bare (non-list) struct attribute with the identical field shape (e.g.
CurrentDayEntry) did not trip this — the auto-fill-then-reject only happens
along the list-of-struct write path (Endpoint.setStateOf -> supervisor.patch
-> StructManager), not a plain state property assignment.

Per the spec text for both RandomizationOffset and RandomizationType (Matter
1.6 Application Cluster Spec, cluster section 9.12.5.10.4-5, as transcribed
in commodity-tariff.resource.ts): "If this field is not indicated,
randomization shall use the value in the Default* attribute." Absence is
spec-meaningful (fall back to the cluster's own DefaultRandomizationType),
not "value is None" — RandomizationOffset already has no default in the
model, matching that; RandomizationType's default: 0 was inconsistent with
its sibling field and collapsed that fallback semantic even when
Randomization was enabled, on top of causing the conformance crash when it
wasn't.

Root-caused and reported against Luligu/matterbridge#625 (their
ElectricalUtilityMeter.addElectricalMeter()'s energyTariff option hits this
exact path). Full crash log and analysis in the linked issue.

Related issue: matter-js#4374

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…default

The actual bug isn't in the model: the spec's own DayEntryStruct field table
(Application Cluster Spec § 9.12.5.10, page 798-800 of 23-27350-010) gives
RandomizationType a Fallback of "None" — matching what CHIP's data model XML
encodes (default="None") and what the matter.js model already had. My first
pass (previous commit) removed that default to make the crash go away, but
that also removes the spec-mandated fallback for when Randomization *is*
supported and a day entry omits the field.

Root cause is in ValuePatcher.ts's getDefaults(): it calls
`supervisor.membersOf(schema)` with no conformance filter, so it considers
every declared member's `default" regardless of whether that member is even
conformant given the currently active features. SelectDefaultValue() (in
@matter/model) already guards this correctly with
`scope.hasOperationalSupport(member)`; getDefaults() has its own, separate
member-default computation that skipped that check. The fix passes
`{ conformance: "conformant" }` to `membersOf()`, matching the filter
StructManager.ts already uses for the (working) mandatory-default synthesis
path.

Restores commodity-tariff.element.ts's `default: 0` and adds a second test
confirming the fix preserves the spec's "None" fallback once Randomization
is enabled and the field is omitted, alongside the existing regression test
for the conformance-disabled crash.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant