schema-1 1.1.1: a priority with no $settable is locked, not open - #170
Merged
Conversation
Homie 5 defaults the attribute to false and the eBus SDK's description builder emits it only when a property is settable, so a conforming publisher describes a never-backup circuit by omitting `$settable` on `load-shed/priority` rather than by publishing `false`. Both mutations fail against the current permissive default, which reads silence as permission and offers a priority control on exactly the circuits commissioned not to accept one. Two premise tests come with them, because both mutations edit an attribute the producer publishes: every circuit in the capture announces `settable: true` explicitly, so the permissive default was never the thing protecting this fixture, and a capture that stopped announcing it would make the mutations indistinguishable from the shipped state.
`priority_is_settable` read a declared `load-shed/priority` carrying no `$settable` as settable, so a circuit commissioned never-backup reported `is_never_backup=False` and the adapter resolved a write topic for a priority the panel refuses. Homie 5 defaults the attribute to false and a conforming publisher emits it only where a write is accepted, so omission is how a lock is announced -- the same reading the relay already used, and the one the migration guide's `$settable = !never-backup` mapping implies. The justification for the permissive default was firmware that publishes no attribute at all; the vendored capture announces `settable: true` explicitly on every circuit's priority, so no producer here relied on it. With both callers answering absence the same way, `when_unannotated` is a knob nothing turns and goes. The rule it encoded moves to `description.declared_settable`, which is now the only reader of `$settable`: `charge_limit` was asking the same question with a second copy of the answer, and its stricter string handling -- only `"true"` authorises -- is the one kept, since a value that is neither the boolean nor that word has not said what it means.
…ises a write
The sweep behind the priority fix. Adoption and extension answered the
same question with a second expression -- `bool(definition.get(
"settable", False))` -- which agrees on the default and disagrees on
text: Homie attributes travel as strings, and `bool("false")` is True,
so the declaration that most explicitly refuses a write was minting a
set topic for an uncurated device. Both now read
`description.declared_settable`.
`SchemaAdapter.set_circuit_priority_target` documented the rule the
adapter no longer follows, telling every implementer that an absent
`$settable` on a declared `load-shed/priority` means settable. It says
what the attribute's absence actually announces.
The flat adapter needs nothing: it reads `never-backup` as a published
value, not as mutability, and never consults `$settable`.
Only the adapter changed, so only the adapter is released -- RELEASE.md's rule, and the one the root changelog entry would have had to contradict in its own first sentence to justify a core bump for a docstring. `SchemaAdapter.set_circuit_priority_target`'s corrected docstring stays on the branch and rides out with the next release that has a reason of its own; a wrong sentence in the shipped 3.1.1 wheel is worth one release cycle of patience, not a release cycle of its own. Neither floor moves. The adapter asks nothing new of the bootstrap and the bootstrap's extras ask nothing new of the adapter, so every pairing that resolved before still resolves.
Both modules had grown the same assertion over the same capture -- every circuit declares `load-shed/priority` with `settable: true` -- written twice because each was justifying its own mutation. One of them can go, and it is the snapshot module's: the mutations that most need the premise are the adapter's, and a premise asserted beside the thing it is the premise of is the one that gets read when it fails. The survivor takes over the deleted test's two jobs. It carries the non-vacuity guard, now `len(circuits) == 5` in the spelling the relay invariant beside it already uses, so a capture whose circuits stopped matching the type suffix fails here instead of passing an empty loop. And it says what the deleted docstring said: this is the standing answer to the argument the permissive default rested on, firmware that publishes no attribute at all.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
SpanCircuitSnapshot.is_never_backupand the priority write target read a declaredload-shed/prioritythat carries no$settableas locked, where they read it as settable before. Homie 5 defaults the attribute to false; the reference publisher SDK emitssettableonly when true (neverfalse); production captures publish it explicitly on every unlocked property and omit it on locked ones; the migration guide maps never-backup to$settable = !never-backup. The relay already read absence this way — the two now share one reader,description.declared_settable.The sweep behind it: adoption and extension decided settability with
bool(definition.get("settable", False)), so a text"false"(or"1","yes") authorised a write; they use the same reader now.when_unannotatedis gone. TheSchemaAdapter.set_circuit_priority_targetdocstring in core stated the old rule and is corrected on this branch; core is not bumped — the text rides with the next core release.Release:
span-panel-api-schema-11.1.1 only. Floor on core unchanged (>=3.1.0,<4.0).mypy --strict clean (41 files); ruff clean; 1161 passed, 13 skipped.