Skip to content

docs: add the consumer-side contract guide (consuming-a-homie-tree) - #33

Merged
dcj merged 1 commit into
mainfrom
docs/consumer-contract
Aug 7, 2026
Merged

dcj merged 1 commit into
mainfrom
docs/consumer-contract

Conversation

@dcj

@dcj dcj commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Counterpart to doc/building-a-proxy.md, for the controller/subscriber side. Follows #31 / #32.

Why

#31 was filed by a careful integrator who read our observable behaviour, inferred a timing contract from it, and built against the inference. The producer-side ordering defect was real and is fixed in #32, but the deeper problem is that this repo has never stated the asymmetry the convention actually relies on, so the inference looked safe.

A producer SHOULD minimize $state and $description transitions. A consumer MUST react to every $state and $description update, unconditionally.

Those are not two halves of one contract. The first is quality-of-implementation and best-effort; the second is correctness and unconditional.

What it covers

  • Why the asymmetry exists. The four optimizations this SDK already grants itself as a producer (transaction collapsing, content-hash description suppression, deferral inside an open transition, cascade ordering) all change when and whether a message appears, never what is true. A consumer that reconciles is unaffected by all four; a consumer that awaits is broken by at least three.
  • What $state=ready actually means. "My own $description is current", never "my children are present". The rollup reading cannot be made true by any producer, because children are commissioned and decommissioned out of band and there is no moment at which a producer knows it has published all of them. Also documents the one thing a root's state is authoritative for: effective state, keyed on the root rather than the immediate parent.
  • Three failure modes, each with the correct version: the one-shot barrier that passes at startup and stops reconciling; awaiting a $description that the content-hash suppression never sends (the init to ready edge of an empty transition is not suppressed); and inferring order across retained messages, which is exactly the broker-restart case people reason about first.
  • The shape that works, and what Controller already does for you, including the known gap: there is no public tree-complete affordance.

Notes

Documentation only, no code changes. Ships with #32 as 0.18.1.

States the asymmetry the convention relies on and that this repo had never
written down: a producer SHOULD minimize $state/$description transitions
(quality-of-implementation, best-effort), while a consumer MUST react to every
one of them, unconditionally (correctness).

Covers why the asymmetry exists (the four optimizations this SDK already grants
itself as a producer all change WHEN and WHETHER a message appears, never what
is true), what $state=ready does and does not promise, and the three ways
consumers get this wrong: the one-shot barrier that stops reconciling after
startup, awaiting a $description that the content-hash suppression never sends,
and inferring publish order across retained messages.

Prompted by #31, where a careful integrator read our observable behaviour,
inferred a timing contract from it, and built against the inference. The absence
of a stated SHOULD/MUST split is what made that inference look safe.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
@dcj
dcj merged commit f1464ca into main Aug 7, 2026
5 checks passed
@dcj
dcj deleted the docs/consumer-contract branch August 7, 2026 14:10
dcj added a commit that referenced this pull request Aug 7, 2026
…de) (#34)

Pure-fix release. No API change, no ebus-mqtt-client floor bump.

- fix: Device.refresh_tree() now publishes a device's own $state after its
  descendants rather than before, so a device no longer announces ready while
  the children its $description names have published nothing (#31, #32).
  Thanks to @cayossarian.
- docs: doc/consuming-a-homie-tree.md, the consumer-side contract guide (#33).

Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
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