Skip to content

Adopt core 0.5.0: declared verb scope + keepalive instance state - #9

Merged
mbreissi merged 2 commits into
mainfrom
feat/core-0.5-adoption
Jul 28, 2026
Merged

Adopt core 0.5.0: declared verb scope + keepalive instance state#9
mbreissi merged 2 commits into
mainfrom
feat/core-0.5-adoption

Conversation

@mbreissi

@mbreissi mbreissi commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Adopts the breaking core 0.5.0 scoped-command model and the companion keepalive-instance-state item, one wave (D-SC-1..9).

Pin

edgecommons moves to python-lib/v0.5.0 (release commit a14a328) in requirements.txt and pyproject.toml.

Declared verb scope (D-SC-1..6)

  • All nine verbs — sb/read, sb/write, sb/status, sb/signals, sb/browse, sb/pause, sb/resume, reconnect, repoll — register as commands.register(verb, CommandScope.INSTANCE, handler); every handler is handler(request, addressed_instance). register_scoped no longer exists.
  • The library owns addressing: topic-token and body-instance extraction, the conflict-first BAD_ARGS, and COMPONENT-scope rejection, all before dispatch.
  • routing.resolve_instance drops its topic-authority logic and its body parameter, keeping only the two policies that need configuration knowledge (D-SC-4): the optional-iff-one configured default and NO_SUCH_INSTANCE. No handler reads body["instance"].
  • Wire behavior is unchanged except that the conflict refusal now comes from the library; describe additionally advertises each verb's scope.

Keepalive instance state (D-SC-7)

  • New modbus_adapter/instance_state.py holds the single state model: device_state(paused, connected)ONLINE / PAUSED / BACKOFF, plus CONNECTING for a configured instance whose device has not come up yet.
  • Both surfaces read that one model — the state keepalive's instances[] (via InstanceConnectivity.with_state) and sb/status's new state field — so a pushed keepalive and a pulled status cannot disagree.
  • Link truth wins (the fleet-wide precedence, matching the OPC UA / EtherNet/IP adapters and the templates): PAUSED is reported only while the link is up, so a paused instance whose link is down reads BACKOFF (CONNECTING before its first connect), with the pause still visible in sb/status's paused field. Additive on the wire.
  • This reverses D-M3's deviation ("pause is deliberately not on the keepalive"), which the register now records as adopted.

Docs and register

docs/reference/messaging-interface.md gains the verb Scope column, the resolution-order addressing rules, state on sb/status and on the keepalive element; explanation.md, how-to-guides.md, and sample-configurations.md follow. DESIGN.md adds D-M9 and marks D-M8 superseded; AGENTS.md replaces the instance-routing invariant with the declared-scope and single-state-model invariants.

Validation

python -m pytest (the repo's 90% gate rides addopts): 144 passed, coverage 95.80% — against a local checkout of core at python-lib/v0.5.0. Baseline before the change: 141 passed, 95.74%.

mbreissi added 2 commits July 27, 2026 17:03
Bump the edgecommons pin to python-lib/v0.5.0 and migrate the command
surface to the breaking scoped-command model (D-SC-1..6):

- All nine verbs (sb/read, sb/write, sb/status, sb/signals, sb/browse,
  sb/pause, sb/resume, reconnect, repoll) register through
  register(verb, CommandScope.INSTANCE, handler) with handlers taking
  (request, addressed_instance). register_scoped is gone from the library.
- routing.resolve_instance sheds the topic/body/conflict logic the library
  now owns and keeps only what needs configuration knowledge (D-SC-4): the
  optional-iff-one configured default and NO_SUCH_INSTANCE. No handler
  reads body["instance"] any more.

Populate the state keepalive's instances[] with the instance state
(D-SC-7), reversing D-M3's deviation: a new single state model
(instance_state.device_state) decides ONLINE / PAUSED / BACKOFF /
CONNECTING and feeds both surfaces - the keepalive element via with_state
and sb/status's new state field - so a pushed keepalive and a pulled status
cannot disagree, and a deliberately paused slave is distinguishable from a
stale one.

Docs and the decision register move with the code: the messaging-interface
reference gains a scope column, the resolution-order addressing rules, the
state token on the keepalive and sb/status; DESIGN.md gains D-M9 and marks
D-M8 superseded; AGENTS.md replaces the instance-routing invariant with the
declared-scope and single-state-model invariants.
Match the fleet-wide rule the OPC UA and EtherNet/IP adapters and the
scaffold templates already follow: an instance whose link is down reports
BACKOFF - CONNECTING before its first connect - whether or not sb/pause is
latched, and PAUSED is reported only while the link is up. The pause itself
stays visible on sb/status's paused field, so neither fact is hidden.

device_state flips its precedence accordingly, with the rule stated in the
module docs, the sb/status docstring, the messaging-interface reference, the
D-M9 register entry, and the AGENTS invariant. A new test pins a break while
paused as BACKOFF with paused still true.
@mbreissi
mbreissi merged commit fa049a8 into main Jul 28, 2026
2 checks passed
@mbreissi
mbreissi deleted the feat/core-0.5-adoption branch July 28, 2026 00:28
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