| name | code-driven-cluster-migration |
| description | Step-by-step procedure for migrating a legacy Ember-based Matter server cluster in src/app/clusters/ to the code-driven pattern. Use this skill when converting an existing cluster. All rules from code-driven-cluster-development still apply; this skill adds the migration-specific workflow on top.
|
Code-Driven Cluster Migration
1. When to Invoke This Skill
Invoke this skill when you are converting an existing cluster under
src/app/clusters/<folder>/ that does not yet contain a
CodegenIntegration.cpp file (note: the folder naming convention is
inconsistent — it may be <name>-server or simply <name>). If the cluster is
already migrated, use code-driven-cluster-development instead.
All rules from code-driven-cluster-development apply to the converted cluster.
This skill specifies the additional migration workflow: PR structure, backward
compatibility, and integration wiring.
2. The Three-PR Migration Workflow
Conversion must be split into at least three pull requests to ensure
reviewability. Mixing these phases is a common cause for review rejection.
| PR | Contents | Validation Method |
|---|
| 1 | File renames and compat-stub creation only. | git diff --color-moved |
| 2 | Code reordering / anonymous-namespace moves only. | git diff --color-moved=dimmed-zebra |
| 3 | Substantive conversion: new class, integration, tests, regen. | Functional review + ninja check |
3. Phase 0: Discovery & Strategy
3.1 Baseline & Map Blast Radius
- Inventory Attributes: Identify Mandatory (
M), Optional (O), Fixed
(F), and Nullable attributes from the cluster XML and generated
Attributes.h.
- Map Consumers: Search
examples/ for references to the legacy
<name>-server.h or class <Name>Server. This determines your
backward-compat strategy.
3.2 Backward-Compat Strategy
The standard approach preserves full include and API compatibility:
- CodegenIntegration.cpp: Keep any legacy public APIs alive here as thin
forwarders to the new cluster class. This is the preferred location for all
backward-compat shims.
- Legacy header (
<name>-server.h): If it existed, update it to
#include the new CodegenIntegration.h. This preserves include-path
compatibility for application code without duplicating logic.
- Breaking changes: If direct Ember RAM writes (
Attributes::X::Set) are
used by apps and cannot be shimmed, you must update those apps in PR #3
and document the change in a cluster README.md.
3.3 Pick a Reference PR
Identify the "shape" of your cluster and study the corresponding reference PR's
diff end-to-end.
| Your cluster looks like... | Reference PR |
|---|
| Nullable measurement + min/max/tolerance | #71424 Relative Humidity |
| Mandatory list + delegate + commands | #43471 Actions |
| Multi-instance (per-endpoint state) | #43720 Closure Dimension |
| Singleton, node-scoped | #40422 Basic Information |
| Command-heavy with delegate | #42331 Chime |
| Writable scalar + features | #42968 Switch |
| Runtime-only (no Ember defaults) | #71552 Flow Measurement |
| Identify/timer-driven | #41232 Identify |
4. Implementation Phases
4.1 Phase 1: Renames (PR #1)
- Rename
.cpp and .h to PascalCase (<Name>Cluster.cpp).
- Create a one-line compat stub at the old header path.
- Update
BUILD.gn with renamed files. No logic changes.
4.2 Phase 2: Moves (PR #2)
- Move helpers to anonymous namespaces or group command handlers. No logic
changes.
4.3 Phase 3: The Conversion (PR #3)
- Strip Shell: Remove
emberAf... calls and
MatterPostAttributeChangeCallback.
- Implement Class: Follow the patterns in
code-driven-cluster-development.
- Integration Layer: Create
CodegenIntegration.cpp to bridge ZAP defaults
to your new cluster.
- ZAP/Build Updates:
- Update
src/app/common/templates/config-data.yaml (add to
CodeDrivenClusters).
- Update
src/app/zap-templates/zcl/zcl.json
(attributeAccessInterfaceAttributes).
- Regeneration: Run
scripts/tools/zap_regen_all.py and commit all
generated files.
5. Unit Testing with ClusterTester
The ClusterTester helper class (in
src/app/server-cluster/testing/ClusterTester.h) is mandatory for testing
code-driven clusters. It abstracts TLV handling, memory management for views,
and fabric context.
5.1 Key Capabilities
- Automatic TLV Handling: Encodes C++ types for writes/commands and
decodes for reads.
- Memory Safety: Maintains ownership of TLV data for views (like
CharSpan or List) during the test scope.
- Type Safety: Uses generated types (e.g.,
Attributes::MyAttr::TypeInfo)
to ensure spec alignment.
5.2 Core Patterns
- Attributes: Use
tester.ReadAttribute(Id, outValue) and
tester.WriteAttribute(Id, inValue).
- Commands: Use
tester.Invoke(requestStruct). It returns an
InvokeResult containing both status and response data.
- Side Effects:
- Verify events via
tester.GetNextGeneratedEvent().
- Verify dirty markings (reporting) via
tester.GetDirtyList().
- Fabric Scoping: Use
tester.SetFabricIndex(idx) to simulate actions
from specific fabrics.
6. Ensuring Spec Compliance
Tests must not just "pass" — they must prove the cluster adheres to the Matter
Specification and Test Plans.
6.1 Consult the Source of Truth
Use the matter-specification-access skill to obtain and read the latest
specification and test plans.
- Validate Semantics: Ensure
ReadAttribute and WriteAttribute handle
constraints (nullable, min/max, fixed) exactly as defined in the spec.
- Mirror Test Plans: Use the
.adoc test plans (e.g.,
src/cluster/<name>.adoc in chip-test-plans repo) as the blueprint for
your unit tests. If a test plan requires a ConstraintError for a specific
value, your unit test must assert exactly that.
6.2 Mandatory Test Coverage
- Attribute Constraints: Below min, at min, at max, above max.
- Nullable Handling: Null sentinel rejection (e.g.,
0xFFFF for uint16)
for nullable numeric types.
- Feature Gating: Verify that optional attributes/commands are only
present when the corresponding FeatureMap bits are set (using
Attributes()
/ AcceptedCommands()).
7. Ember → Code-Driven Mapping Reference
| Ember / Legacy Construct | Code-Driven Equivalent |
|---|
emberAfReadAttribute | Member access + encoder.Encode |
Attributes::X::Set | cluster->SetX(value) (Forwarded) |
PreAttributeChangedCallback | Validation logic in WriteAttribute |
PostAttributeChangeCallback | Automatic via SetAttributeValue |
class FooServer : public AAI | class <Name>Cluster : public DefaultServerCluster |
8. Common Review Findings (Migration Specific)
- Mixed PRs: Never combine renames with logic changes.
- Missing Regen: Forgetting to commit files changed by
zap_regen_all.py
breaks the build for other apps.
- Double Notification: Migrated clusters often have redundant
NotifyAttributeChanged calls; use SetAttributeValue to avoid this.
- Incorrect Fallbacks: When reading ZAP defaults in
CodegenIntegration,
ensure you handle read failures with safe, spec-compliant defaults.
- Missing README: If you break backward compatibility, a
README.md
explaining the migration for app developers is mandatory.
9. References
- Implementation Patterns:
.agents/skills/code-driven-cluster-development/SKILL.md
- Migration Guide:
docs/guides/migrating_ember_cluster_to_code_driven.md