Skip to content

feat(node-manager): endpointGroupMembership ItemKind (Phase 2b-2b)#3965

Merged
Apollon77 merged 2 commits into
node-managerfrom
node-manager-phase2b2b
Jun 23, 2026
Merged

feat(node-manager): endpointGroupMembership ItemKind (Phase 2b-2b)#3965
Apollon77 merged 2 commits into
node-managerfrom
node-manager-phase2b2b

Conversation

@Apollon77

Copy link
Copy Markdown
Collaborator

Phase 2b-2b of Node Manager. Adds the fifth reconciler ItemKind, endpointGroupMembership, completing the group-provisioning chain (keyset → groupKeyMap → membership).

What

  • GroupMembershipItemKind — per-endpoint Groups cluster (0x0004) membership. Resolves a per-endpoint command target (node.endpoints.for(ep).commandsOf(GroupsClient)) like BindingItemKind, command-based like GroupKeyItemKind:
    • applyAddGroup({groupId, groupName ?? ""}), always-write (idempotent); groupName sent but not verified.
    • verifyGetGroupMembership({groupList:[groupId]}), presence-only (member iff groupId ∈ response.groupList).
    • removeRemoveGroup({groupId}); NotFound treated as success. Never RemoveAllGroups.
    • capacity — from subscription cache: limit = maxGroupsPerFabric ?? 4, used = groupTable.length.
    • priority PRIORITY_BANDS.membership (30); recoverable = Timeout/Busy.
  • Registered in ReconcilerBehavior.initialize(). No engine changes — reuses the mutex-queue reconcile path + planActions/executeActions.

New mechanic vs prior kinds

Groups commands return application status in the response payload (AddGroupResponse.status), not as a thrown StatusResponseError (KeySetWrite had no payload). So apply/remove inspect response.status and re-throw non-SUCCESS as StatusResponseError(msg, status), routing through the executor's extractStatusCoderecoverable → retry/drop uniformly. ResourceExhausted → drop; Timeout/Busy → retry.

Device dependency: GroupsServer.addGroup returns UnsupportedAccess unless the group is already keyset-mapped — the priority bands keyset(10) < group(20) < membership(30) enforce that order in one reconcile pass.

Tests

  • 12 unit tests (fake peer: status-payload branches, NotFound→success, endpoint-absent paths, capacity-from-cache).
  • 3 integration scenarios on a commissioned OnOffLightSwitchDevice.with(GroupsServer) (ep1): apply → member; behind-the-back removal → verify pass re-applies; deletePending → removed. All assert real device groupTable state.

Gates

build --clean ✓ · format ✓ · lint ✓ · node-manager 75/75 · node 1227/1227 (no regression). Whole-branch review: zero Critical/Important.

Base = node-manager (sub-PR; CI runs on umbrella #3948 → main).

🤖 Generated with Claude Code

Apollon77 and others added 2 commits June 23, 2026 19:33
Implements GroupMembershipItemKind — the fifth reconciler kind — which
provisions peer endpoint group membership via the Groups cluster
AddGroup/RemoveGroup/GetGroupMembership commands. Non-success statuses
in response payloads are re-thrown as StatusResponseError so the
engine's retry/drop machinery works uniformly. Capacity is read from
the subscription-cached GroupKeyManagement state (no live I/O).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Register GroupMembershipItemKind in ReconcilerBehavior.initialize() after
GroupKeyMapItemKind (priority order: keyset < group < membership). Add
three-scenario integration test (apply, behind-back re-apply, removeIntent).

API-drift notes:
- DesiredStateBehavior uses removeIntent(), not deleteIntent() (brief was wrong).
- GroupsServer.removeGroup calls assertRemoteActor, so the behind-back removal
  test mutates groupTable directly (same idiom as GroupKeyIntegrationTest).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mergify

mergify Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Tick the box to add this pull request to the merge queue (same as @mergifyio queue).

  • Queue this pull request

@Apollon77 Apollon77 merged commit bd73f7d into node-manager Jun 23, 2026
2 checks passed
@Apollon77 Apollon77 deleted the node-manager-phase2b2b branch June 23, 2026 18:00
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