Skip to content

Southbound conformance: PAUSED code, eight-measure health, schema alias, renderable panels - #6

Merged
mbreissi merged 1 commit into
mainfrom
feat/southbound-conformance
Jul 27, 2026
Merged

Southbound conformance: PAUSED code, eight-measure health, schema alias, renderable panels#6
mbreissi merged 1 commit into
mainfrom
feat/southbound-conformance

Conversation

@mbreissi

Copy link
Copy Markdown
Contributor

Applies the amended SOUTHBOUND.md southbound conventions (the core adapter-core-enablers train) to the Modbus reference adapter. Per-item status:

Checklist status

1. PAUSED refusal code — MIGRATED. repoll on a paused instance now refuses with top-level code PAUSED instead of BAD_ARGS. This is a wire-visible breaking change: any client pinning BAD_ARGS for the paused-repoll case must move to PAUSED. Updated together: command_service.repoll(), tests/test_commands.py, docs/reference/messaging-interface.md (error-code table + repoll entry), AGENTS.md invariants, and the DESIGN.md D-M2/D-M3 registers (which previously pinned the old code as the breaking-change record).

2. Eight-measure southbound_health — MIGRATED. Was six measures; now exactly connectionState, publishLatencyMs, pollLatencyMs, readErrors, staleSignals, reconnects, writeErrors, signalsSubscribed.

  • writeErrors (Count, 60, drained on emit like readErrors): DEVICE-PATH failures only. command_service._write_one now splits the encode step from the device write, so allow-list refusals, unresolvable refs, missing values, and caller-side encode errors never count — only a write that passed validation + the allow-list and was then rejected by the device (or lost to a dead link) does. Per-entry reply granularity and the WRITE_FAILED batch semantics are unchanged.
  • signalsSubscribed (Count, 1, gauge): Modbus is a polling adapter, so this is the configured/polled inventory the session serves while connected, 0 while disconnected (ClientMetrics.set_signals_serving, fed from the instance config in device.py).
  • Measure-pinning test (tests/test_health.py) and docs/reference/metrics.md updated in the same change.

3. #/$defs/instance schema alias — CONFORMED-ALREADY. config.schema.json already carries "instance": {"$ref": "#/$defs/device"} (landed on main in b9834ef, PR #5). No change.

4. Panel descriptor floor — MIGRATED. The trio existed but was below the renderable floor (bare signalGrid/treeBrowser, summary with a fields key, commandSummary with an actions key). Now: summary.rows (label/value), commandSummary.verbs, signalGrid with signalsVerb and subscriptionsVerb (both → sb/signals; the second is a descriptor-compat alias the shipped console reads — no sb/subscriptions wire verb exists) + readVerbsb/read, treeBrowser with browseVerb/mode: "hierarchical"/rootRef: "root", and widget-level scope: "instance" on every command-backed widget. No widget advertises a writeVerb (none did before either). Kept the repo's existing view set/ids (overview/signals/diagnostics). Scope note: the diagnostics panel's pre-existing keyValueList widget is this repo's own extra view — kept (with an id/title), not expanded, since the floor doesn't cover it.

5. Hierarchical sb/browse — MIGRATED. The repo registered a treeBrowser panel but sb/browse served only pages. Added the hierarchical form per the python template reference, over the same configured inventory the paged mode serves: presence of ref selects it; ref: "root" returns the device node with contains refs to the (flat) signal inventory; a signal id is a known leaf (refs: []); an unknown ref is BAD_ARGS; depth clamps 1..4, maxRefs 1..1000; mixing ref/depth/maxRefs with cursor/max, or depth/maxRefs without ref, is BAD_ARGS. Paged behavior unchanged.

Validation

  • Baseline (pre-change) on the worktree: 121 passed, coverage 95.47% — green.
  • After: 130 passed, coverage 95.67% (gate 90%, pyproject.toml addopts; python -m pytest from the repo's own venv with the editable sibling edgecommons lib per CLAUDE.md).
  • No gate lowered; 9 tests added (PAUSED refusal, hierarchical browse root/leaf/clamps/mixing, writeErrors device-path vs policy/encode semantics, panel floor assertions).

Follow-ups (out of scope per the migration spec)

  • Migrating legacy .../control/* topics to the UNS inbox (standing Phase-5 item) — N/A here; this adapter already serves the UNS command inbox.
  • Adopting registerScoped/setCommandAvailability (requires a core version bump).

🤖 Generated with Claude Code

https://claude.ai/code/session_014iQDskU2E1HLStYHoZrSTn

… panels, hierarchical browse

Applies the amended SOUTHBOUND.md conventions (core adapter-core-enablers train):

- repoll while paused now refuses with the top-level code PAUSED instead of
  BAD_ARGS (wire-visible change; docs/AGENTS/DESIGN registers updated).
- southbound_health emits the exact eight-measure set: writeErrors (device-path
  write failures only, drained on emit like readErrors; the encode step is split
  from the device write so caller-side errors never count) and signalsSubscribed
  (the served configured/polled inventory while connected, 0 while disconnected)
  join the previous six.
- The panel trio is raised to the renderable descriptor floor: summary.rows,
  commandSummary.verbs (replacing fields/actions), signalGrid with signalsVerb +
  subscriptionsVerb (-> sb/signals) + readVerb, treeBrowser with browseVerb /
  mode hierarchical / rootRef / widget-level instance scope; no writeVerb.
- sb/browse gains the hierarchical treeBrowser mode over the same configured
  inventory: presence of ref selects it, depth/maxRefs clamp to 1..4 / 1..1000,
  mixing with cursor/max (or depth/maxRefs without ref) is BAD_ARGS.

The #/$defs/instance schema alias already landed on main (b9834ef).

130 tests pass, coverage 95.67% (gate 90%).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iQDskU2E1HLStYHoZrSTn
@mbreissi
mbreissi marked this pull request as ready for review July 27, 2026 16:56
@mbreissi
mbreissi merged commit 975378c into main Jul 27, 2026
2 checks passed
@mbreissi
mbreissi deleted the feat/southbound-conformance branch July 27, 2026 16:56
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