Skip to content

Let a port force $description out on a recovery republish - #10

Merged
dcj merged 1 commit into
mainfrom
fix/force-description-republish
Oct 9, 2026
Merged

dcj merged 1 commit into
mainfrom
fix/force-description-republish

Conversation

@dcj

@dcj dcj commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Fixes #9.

API

Device::forgetDescriptionHash() clears the remembered $description hash of one device, so its next publish() sends $description even if unchanged. clearRetained() now calls it. notifyStructuralChange() keeps its no-op skip.

POSIX port

On every reconnect the demo device calls forgetDescriptionHash() on each device and then publishTree() (was publishStateTree()): each device goes init, $description, values, ready, children first. The port uses a clean session and MQTT reports nothing about the retained store, so it cannot tell a broker restarted without persistence from one that kept everything; a held-publish loss signal alone would miss that case (the restart in the e2e test evicts nothing). The cost is one init/ready cycle and one $description per device per reconnect. A mutex now covers the uptime worker's store and queue, since the reconnect republish reads that property on the main thread.

Docs

  • doc/core.md: new "After a reconnect" section.
  • doc/mqtt.md: esp32-sdk's MqttClientTransport overrides the generic queue_publish() (since esp32-sdk #76), not the Property overload.
  • POSIX README and top-level README: known gap removed or narrowed to esp32-sdk, which does not call the new method yet.

Tests

  • test_device_lifecycle: repeated publishTree() and a no-op notifyStructuralChange() send no $description; after forgetDescriptionHash() both devices send it, then skip again; forgetting one device affects only that device; a failed forced publish is retried.
  • e2e_reconnect: after a mosquitto restart with persistence false, a client that subscribes late receives retained root and child $description. This assertion fails against the previous device code ("no retained ebus/5/e2e-dev/$description after the restart") and passes now (5 of 5 repeats).
  • Local: core CTest (12 suites) with clang++ and g++-16, warnings as errors; POSIX CTest (6 e2e cases); ebus_mqtt and ebus_discovery isolated compiles; esp32-sdk main with lib/ebus_core at this commit: ./ebus-esp32 build (esp32-poe-iso) and ./ebus-esp32 test (13 suites) pass.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Npk6WmkpnBRzWkuhK6uCuz

Device::publish() skips a $description identical to the last one sent,
which blocks every recovery republish after a broker lost its retained
store. Device::forgetDescriptionHash() clears the remembered hash for one
device, so the next publish() sends it; notifyStructuralChange() keeps
its no-op skip.

The POSIX demo device now republishes the whole tree on every reconnect
with the hashes forgotten (was publishStateTree()): with a clean session
it cannot tell whether the broker kept its retained store. The reconnect
e2e test checks that root and child $description are retained again
after a broker restart without persistence. doc/core.md documents the
recovery path; doc/mqtt.md now lists esp32-sdk's MqttClientTransport
under the generic queue_publish() override.

Fixes #9

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Npk6WmkpnBRzWkuhK6uCuz
@dcj
dcj merged commit 0b6faea into main Oct 9, 2026
8 checks passed
@dcj
dcj deleted the fix/force-description-republish branch October 9, 2026 15:06
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.

A recovery republish never restores $description: Device::publish() skips an unchanged hash

1 participant