feat: ClientNode controller API additions + deprecate legacy controller API - #4091
Open
Apollon77 wants to merge 72 commits into
Open
feat: ClientNode controller API additions + deprecate legacy controller API#4091Apollon77 wants to merge 72 commits into
Apollon77 wants to merge 72 commits into
Conversation
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…lientNode Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…et on CASE reconnect Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…Lifecycle Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Disconnected now means the network client is stopped, not started or disabled, driven by lifecycle.shouldBeOffline instead of !isCommissioned. A decommissioned node is destroyed and no longer drives connection state. Reset the likelyOffline latch on the Disconnected branch so a re-enabled or restarted node does not resume in WaitingForDeviceDiscovery, and set targetState offline in ClientNode.disable() so disabling settles straight to Disconnected without a spurious Reconnecting emission. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… ICD wiring Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ith optional model Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…oval Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR adds a set of controller-facing convenience APIs centered on ClientNode (controller peers) to enable deprecation of the legacy CommissioningController / PairedNode controller surface, while keeping the legacy API functional through the deprecation window.
Changes:
- Added new
ClientNodecontroller conveniences (commissioning-window helpers, commissioned-peer enumeration, seeded/connection-state lifecycle signals). - Added model-scoped cluster name/id resolution helpers and
Behaviors.forCluster(clusterId)for cluster→behavior lookup. - Marked the legacy controller/device APIs in
@project-chip/matter.jsas deprecated for removal in 0.19 and added an initial migration guide + changelog entries.
Reviewed changes
Copilot reviewed 35 out of 35 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| packages/types/test/cluster/ClusterNamingTest.ts | Adds tests for new cluster name↔id helper resolution (including custom MatterModel). |
| packages/types/src/cluster/ClusterHelper.ts | Extends cluster/endpoint name resolution to accept a MatterModel and adds attribute/command/event id↔name helpers. |
| packages/protocol/test/peer/PeerEstablishmentProgressTest.ts | Adds tests for establishment-unresponsive detection logic and MRP-driven behavior. |
| packages/protocol/src/peer/PeerConnection.ts | Adds retransmission observer hook + establishmentUnresponsiveDetector and wires it into CASE establishment. |
| packages/protocol/src/peer/Peer.ts | Exposes peer.establishmentUnresponsive observable for higher layers to react to likely-offline detection. |
| packages/node/test/node/NodeLifecycleSeededTest.ts | Adds tests for NodeLifecycle.isSeeded/seeded latch behavior and listener disposal. |
| packages/node/test/node/client/PeersCommissionedTest.ts | Adds tests for Peers.commissioned filtering behavior. |
| packages/node/test/endpoint/properties/BehaviorsForClusterTest.ts | Adds tests for Behaviors.forCluster(clusterId). |
| packages/node/test/behavior/system/network/ConnectionStateTest.ts | Adds tests for NodeLifecycle connection-state transitions and idempotency. |
| packages/node/test/behavior/system/commissioning/CommissioningWindowTest.ts | Adds tests for basic/enhanced commissioning-window helpers and revoke tolerance. |
| packages/node/src/node/NodeLifecycle.ts | Adds seeded tracking + connection-state enum/observables and convenience accessors. |
| packages/node/src/node/ClientNode.ts | Adds commissioning-window helpers and adjusts disable flow to avoid transient connection-state artifacts. |
| packages/node/src/node/client/Peers.ts | Adds peers.commissioned and seeded evaluation/latching logic for peers. |
| packages/node/src/endpoint/properties/Endpoints.ts | Fixes endpoints.size to avoid double-counting the root endpoint. |
| packages/node/src/endpoint/properties/Behaviors.ts | Adds Behaviors.forCluster(clusterId) lookup. |
| packages/node/src/behavior/system/network/NetworkClient.ts | Adds connection-state recomputation engine and likely-offline latch wiring. |
| packages/node/src/behavior/system/commissioning/CommissioningClient.ts | Adds openBasicCommissioningWindow / openEnhancedCommissioningWindow helpers + revoke tolerance. |
| packages/matter.js/src/PaseCommissioner.ts | Marks legacy commissioner wrapper API deprecated (0.19 removal). |
| packages/matter.js/src/device/PairedNode.ts | Marks legacy PairedNode controller API surface deprecated (0.19 removal). |
| packages/matter.js/src/device/Endpoint.ts | Marks legacy endpoint wrapper API surface deprecated (0.19 removal). |
| packages/matter.js/src/device/DeviceTypes.ts | Marks legacy device types helpers deprecated (0.19 removal). |
| packages/matter.js/src/device/DeviceInformation.ts | Marks legacy device-info wrapper types deprecated (0.19 removal). |
| packages/matter.js/src/device/Device.ts | Marks legacy device wrapper classes deprecated (0.19 removal). |
| packages/matter.js/src/device/ComposedDevice.ts | Marks legacy composed-device wrapper deprecated (0.19 removal). |
| packages/matter.js/src/device/Aggregator.ts | Marks legacy aggregator wrapper deprecated (0.19 removal). |
| packages/matter.js/src/ControllerStore.ts | Marks legacy controller store surface deprecated (0.19 removal). |
| packages/matter.js/src/CommissioningController.ts | Marks legacy controller/discovery APIs deprecated (0.19 removal). |
| packages/matter.js/src/cluster/client/InteractionClient.ts | Marks legacy interaction client and related types deprecated (0.19 removal). |
| packages/matter.js/src/cluster/client/EventClient.ts | Marks legacy event client deprecated (0.19 removal). |
| packages/matter.js/src/cluster/client/DecodedDataReport.ts | Marks legacy decoded-report types/helpers deprecated (0.19 removal). |
| packages/matter.js/src/cluster/client/ClusterClientTypes.ts | Marks legacy cluster client types deprecated (0.19 removal). |
| packages/matter.js/src/cluster/client/ClusterClient.ts | Marks legacy cluster client factory APIs deprecated (0.19 removal). |
| packages/matter.js/src/cluster/client/AttributeClient.ts | Marks legacy attribute client deprecated (0.19 removal). |
| docs/MIGRATION_CONTROLLER_018.md | Adds initial (WIP) migration guide from legacy controller API to ClientNode. |
| CHANGELOG.md | Documents new controller conveniences and legacy API deprecation in WIP changelog. |
…plicate side effects Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…urrent API Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Contributor
|
Tick the box to add this pull request to the merge queue (same as
|
…migration guide Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ation guide Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…te stream Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…fecycle Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…namespace Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Apollon77
marked this pull request as draft
July 17, 2026 11:00
…Node Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e; explicit return type Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…oningController Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…, discovery data - Interim per-project recipe for propagating the fabric label to peers (until the node-management layer lands); UpdateFabricLabel targets the accessing fabric - How to read the negotiated subscription interval (ClientSubscription.maxInterval) - Full mDNS DiscoveryData fields (RI/PH/PI/T/SII/SAI) on discovered nodes and peer.descriptor.discoveryData — no gap vs the legacy discovery record Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…issioning failure - Set discoveryData on the live Peer.descriptor (the CommissioningClient.descriptor write landed after forAddress already bound the peer, so it never took effect) - Delete the node on any start()/commissioningComplete() throw, not only on a non-Ok errorCode, so a failed finalize cannot leave a phantom commissioned peer; keep setStateOf/persist outside the try so a post-success persist failure does not orphan a genuinely commissioned device Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…mmissioned peer completeCommissioning() called forAddress(), which returns the existing node for an already-commissioned address instead of always creating one, and its catch block unconditionally deleted that node on any failure — including a non-throwing NoFailSafe errorCode from a duplicate or retried CommissioningComplete. Guard with a non-creating lookup before forAddress() so an already-commissioned peer is rejected up front instead of reaching the delete path.
…ssioning tests The commissionable re-announcement is an async reaction to sessions.deleted that can settle after the failsafe-destroyed signal, so asserting adSpy.count on failsafeGone alone races under load (intermittent low-rate flake). Await the advertisement itself via a spy-armed promise alongside the existing signal. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e timeout in completeCommissioning Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…oth-or-nothing) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ric is absent Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…dentifier Remove comments that restate the next line or the test title; collapse the triplicated re-announcement-race rationale to one canonical doc + pointers; fix a comment referencing a non-existent completeCommission(). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…contract Clarify that finalizeCommissioning must drive the completion to done and only then resolve (throw to roll back), so commission() reflects the real outcome and the PASE session is not torn down mid-handoff. Update the migration-guide recipe to await completeCommissioning inside the hook, and note in the split-commissioning test why it completes in a second phase (two same-fabric-identity controllers cannot run the nested flow concurrently in one process). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- completeCommissioning now reads the node's structure (isNewlyCommissioned) and subscribes unless opted out, mirroring commission(), so the finalized peer is seeded rather than blind (lifecycle.seeded would otherwise never emit); adds an options arg and a regression assertion - await the MaybePromise from node.act(subscriptionActive) in the shutdown/leave guards - drop redundant `as ServerNode` casts, fix a copy-paste JSDoc on peers.get(), stop Factory.create mutating the caller's options, rename a shadowed destructure - CHANGELOG: correct isSeeded location (ClientNodeLifecycle) and add the missing ClientNode-API entries (completeCommissioning, connection state, forCluster, disable/enable, commissioned view, ClusterLookup) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- cmd commission: omit the passcode from the debug dump entirely (spread+override still flows it through the logged object) so CodeQL's clear-text-logging alert clears - openEnhancedCommissioningWindow: throw a clear error before opening the window if the peer's BasicInformation is not seeded, instead of a cryptic behavior-not-found throw when reading the VID/PID encoded into the QR code - cmd discover: sort the address-fallback dedupe key so re-advertisement in a different order is not treated as a new device Copilot typo (CommissioningController "at least") and the passcode/establishment-observer threads were already correct/fixed on this branch — no change needed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Passing ip without port (or vice versa) silently fell through to discovery commissioning. Reject the partial address instead. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…OGRESS 0.17.7 released without this branch; its entries were sitting under the released heading. Relocate them to a WORK IN PROGRESS section on top. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Summary
Adds the ClientNode-based controller convenience API that closes the gaps blocking deprecation of the legacy
PairedNode/CommissioningController, and deprecates that legacy API for 0.19 removal. Everything is a thin, additive surface over existing machinery; legacyPairedNode/CommissioningControllerstay fully functional through the deprecation period.Implemented from the plan
docs/superpowers/plans/2026-07-14-clientnode-controller-api-additions.md.What's added
Behaviors.forCluster(clusterId)+ a model-scoped cluster name↔id resolver (relocated to@matter/typesClusterHelper, now with an optionalMatterModelparam defaulting to globalMatter).Peers.commissioned— commissioned operational peers (excludes commissionable discoveries and group nodes).NodeLifecycle.isSeeded/seeded— node structure read at least once (BasicInformation present and beyond the root endpoint);seededemits once. Also fixesEndpoints.sizedouble-counting the root endpoint.CommissioningClient+ClientNode:openBasicCommissioningWindow/openEnhancedCommissioningWindow(returns{ manualPairingCode, qrPairingCode }).Peer.establishmentUnresponsive(@matter/protocol) — fires once when a CASE (re)establishment attempt retransmits past the MRP budget; observation-only, retransmission behavior unchanged.NetworkClientexposed viaNodeLifecycle:NodeConnectionState(enum values pinned to legacyNodeStates:Connected=0, Disconnected=1, Reconnecting=2, WaitingForDeviceDiscovery=3),connectionState,connectionStateChanged,isConnected.Disconnected= stopped/not-started/disabled; lifecycle on start isDisconnected → Reconnecting → Connected.Deprecation
The legacy
@project-chip/matter.jscontroller/device API (CommissioningController,PairedNode,Device/Endpoint/Aggregator/ComposedDevice, the cluster clients, and their public members — ~330@deprecatedtags) is marked deprecated for removal in 0.19, pointing atdocs/MIGRATION_CONTROLLER_018.md(WIP guide included here).Testing
Full gate green at HEAD:
build --clean,format-verify,lint, and package suites —@matter/types374/374,@matter/protocol1450/1450,@matter/node1471/1471,@project-chip/matter.js5/5 (ESM/CJS/Web). New test files cover each addition with real-behavior assertions (idempotency, recovery, emit-once, transition ordering).Reviews
Per-task spec+quality reviews (all clean) + a final independent adversarial whole-branch review (Ready to merge, 0 Critical/Important) + a "greg" architectural review (Approve-with-fixes —
ClusterNamingrelocation applied) + a second independent adversarial pass (Ready to merge). The two highest-risk paths — a synchronous signal emitted inside the MRP retransmission path, and reactor re-registration across stop/start — were each traced to correct handling (offline-transaction fault isolation;Reactors.adddedup).Follow-ups (not in this PR)
likelyOfflinetrigger — needs a newPeer-level observable; connection state currently degrades toReconnectingin that window (mDNS still recovers).Peers.#instrumentBasicInformationregisters non-self-disposing.onhandlers (pre-existing) that could accumulate ifclusterInstalledre-emits.MIGRATION_CONTROLLER_018.mdsections (events bus, subscription examples, FAQ) alongside the shell + server migrations.🤖 Generated with Claude Code