Add new AddStatus overloads using ClusterStatusCode (#33904)

* Add new AddStatus overloads using ClusterStatusCode

- CommandHandler and WriteHandler did not have a way to cleanly just `AddStatus` with
  a cluster-specific status code (to eventually harmonize handling methods to
  always return just a ClusterStatusCode).

This PR:

- Introduces an `AddStatus` overload for `ClusterStatusCode` to CommandHandler
  and WriteHandler.
- Removes the unimplemented `WriteClient::Shutdown` method.
- Adds notes to the `AddClusterSpecificSuccess` and `AddClusterSpecificFailure`.
- Removes implicit conversion from `ClusterStatusCode` to `Status`
  - Was never used and was error-prone/lossy

Fixes #31120

Testing done:
- Updated necessary unit tests.
- Added unit tests for cluster specific statuses on writes.
- Integration tests still pass.

* Restyled by clang-format

* Improve type safety of test

* Address review comments

- Make StatusIB initializable from ClusterStatusCode
- Clean-ups requested
- CommandResponseHelper loses the error-prone cluster-specific-code on success
  (can be added back if ever needed).

* Restyled by clang-format

---------

Co-authored-by: Restyled.io <commits@restyled.io>
diff --git a/src/app/CommandHandler.h b/src/app/CommandHandler.h
index 6c2af0e..4771a45 100644
--- a/src/app/CommandHandler.h
+++ b/src/app/CommandHandler.h
@@ -25,6 +25,7 @@
 #include <lib/support/CodeUtils.h>
 #include <lib/support/IntrusiveList.h>
 #include <lib/support/logging/CHIPLogging.h>
+#include <protocols/interaction_model/StatusCode.h>
 
 namespace chip {
 namespace app {
@@ -116,21 +117,51 @@
 
     /**
      * Adds the given command status and returns any failures in adding statuses (e.g. out
-     * of buffer space) to the caller
+     * of buffer space) to the caller. `context` is an optional (if not nullptr)
+     * debug string to include in logging.
      */
     virtual CHIP_ERROR FallibleAddStatus(const ConcreteCommandPath & aRequestCommandPath,
-                                         const Protocols::InteractionModel::Status aStatus, const char * context = nullptr) = 0;
+                                         const Protocols::InteractionModel::ClusterStatusCode & aStatus,
+                                         const char * context = nullptr) = 0;
+    CHIP_ERROR FallibleAddStatus(const ConcreteCommandPath & aRequestCommandPath, const Protocols::InteractionModel::Status aStatus,
+                                 const char * context = nullptr)
+    {
+        return FallibleAddStatus(aRequestCommandPath, Protocols::InteractionModel::ClusterStatusCode{ aStatus }, context);
+    }
 
     /**
-     * Adds a status when the caller is unable to handle any failures. Logging is performed
-     * and failure to register the status is checked with VerifyOrDie.
+     * Adds an IM global or Cluster status when the caller is unable to handle any failures. Logging is performed
+     * and failure to register the status is checked with VerifyOrDie. `context` is an optional (if not nullptr)
+     * debug string to include in logging.
      */
-    virtual void AddStatus(const ConcreteCommandPath & aCommandPath, const Protocols::InteractionModel::Status aStatus,
-                           const char * context = nullptr) = 0;
+    virtual void AddStatus(const ConcreteCommandPath & aRequestCommandPath,
+                           const Protocols::InteractionModel::ClusterStatusCode & aStatus, const char * context = nullptr) = 0;
+    void AddStatus(const ConcreteCommandPath & aRequestCommandPath, const Protocols::InteractionModel::Status aStatus,
+                   const char * context = nullptr)
+    {
+        AddStatus(aRequestCommandPath, Protocols::InteractionModel::ClusterStatusCode{ aStatus }, context);
+    }
 
-    virtual CHIP_ERROR AddClusterSpecificSuccess(const ConcreteCommandPath & aRequestCommandPath, ClusterStatus aClusterStatus) = 0;
+    /**
+     * Sets the response to indicate Success with a cluster-specific status code `aClusterStatus` included.
+     *
+     * NOTE: For regular success, what you want is AddStatus/FailibleAddStatus(aRequestCommandPath,
+     * InteractionModel::Status::Success).
+     */
+    virtual CHIP_ERROR AddClusterSpecificSuccess(const ConcreteCommandPath & aRequestCommandPath, ClusterStatus aClusterStatus)
+    {
+        return FallibleAddStatus(aRequestCommandPath,
+                                 Protocols::InteractionModel::ClusterStatusCode::ClusterSpecificSuccess(aClusterStatus));
+    }
 
-    virtual CHIP_ERROR AddClusterSpecificFailure(const ConcreteCommandPath & aRequestCommandPath, ClusterStatus aClusterStatus) = 0;
+    /**
+     * Sets the response to indicate Failure with a cluster-specific status code `aClusterStatus` included.
+     */
+    virtual CHIP_ERROR AddClusterSpecificFailure(const ConcreteCommandPath & aRequestCommandPath, ClusterStatus aClusterStatus)
+    {
+        return FallibleAddStatus(aRequestCommandPath,
+                                 Protocols::InteractionModel::ClusterStatusCode::ClusterSpecificFailure(aClusterStatus));
+    }
 
     /**
      * GetAccessingFabricIndex() may only be called during synchronous command
diff --git a/src/app/CommandHandlerImpl.cpp b/src/app/CommandHandlerImpl.cpp
index 926e5b3..d57fab2 100644
--- a/src/app/CommandHandlerImpl.cpp
+++ b/src/app/CommandHandlerImpl.cpp
@@ -19,6 +19,7 @@
 
 #include <access/AccessControl.h>
 #include <app-common/zap-generated/cluster-objects.h>
+#include <app/MessageDef/StatusIB.h>
 #include <app/RequiredPrivilege.h>
 #include <app/StatusResponse.h>
 #include <app/util/MatterCallbacks.h>
@@ -30,6 +31,7 @@
 #include <lib/support/TypeTraits.h>
 #include <messaging/ExchangeContext.h>
 #include <platform/LockTracker.h>
+#include <protocols/interaction_model/StatusCode.h>
 #include <protocols/secure_channel/Constants.h>
 
 namespace chip {
@@ -592,11 +594,11 @@
     return TryAddingResponse([&]() -> CHIP_ERROR { return TryAddStatusInternal(aCommandPath, aStatus); });
 }
 
-void CommandHandlerImpl::AddStatus(const ConcreteCommandPath & aCommandPath, const Protocols::InteractionModel::Status aStatus,
-                                   const char * context)
+void CommandHandlerImpl::AddStatus(const ConcreteCommandPath & aCommandPath,
+                                   const Protocols::InteractionModel::ClusterStatusCode & status, const char * context)
 {
 
-    CHIP_ERROR error = FallibleAddStatus(aCommandPath, aStatus, context);
+    CHIP_ERROR error = FallibleAddStatus(aCommandPath, status, context);
 
     if (error != CHIP_NO_ERROR)
     {
@@ -610,33 +612,37 @@
     }
 }
 
-CHIP_ERROR CommandHandlerImpl::FallibleAddStatus(const ConcreteCommandPath & path, const Protocols::InteractionModel::Status status,
+CHIP_ERROR CommandHandlerImpl::FallibleAddStatus(const ConcreteCommandPath & path,
+                                                 const Protocols::InteractionModel::ClusterStatusCode & status,
                                                  const char * context)
 {
-    if (status != Status::Success)
+    if (!status.IsSuccess())
     {
         if (context == nullptr)
         {
             context = "no additional context";
         }
 
-        ChipLogError(DataManagement,
-                     "Endpoint=%u Cluster=" ChipLogFormatMEI " Command=" ChipLogFormatMEI " status " ChipLogFormatIMStatus " (%s)",
-                     path.mEndpointId, ChipLogValueMEI(path.mClusterId), ChipLogValueMEI(path.mCommandId),
-                     ChipLogValueIMStatus(status), context);
+        if (status.HasClusterSpecificCode())
+        {
+            ChipLogError(DataManagement,
+                         "Endpoint=%u Cluster=" ChipLogFormatMEI " Command=" ChipLogFormatMEI " status " ChipLogFormatIMStatus
+                         " ClusterSpecificCode=%u (%s)",
+                         path.mEndpointId, ChipLogValueMEI(path.mClusterId), ChipLogValueMEI(path.mCommandId),
+                         ChipLogValueIMStatus(status.GetStatus()), static_cast<unsigned>(status.GetClusterSpecificCode().Value()),
+                         context);
+        }
+        else
+        {
+            ChipLogError(DataManagement,
+                         "Endpoint=%u Cluster=" ChipLogFormatMEI " Command=" ChipLogFormatMEI " status " ChipLogFormatIMStatus
+                         " (%s)",
+                         path.mEndpointId, ChipLogValueMEI(path.mClusterId), ChipLogValueMEI(path.mCommandId),
+                         ChipLogValueIMStatus(status.GetStatus()), context);
+        }
     }
 
-    return AddStatusInternal(path, StatusIB(status));
-}
-
-CHIP_ERROR CommandHandlerImpl::AddClusterSpecificSuccess(const ConcreteCommandPath & aCommandPath, ClusterStatus aClusterStatus)
-{
-    return AddStatusInternal(aCommandPath, StatusIB(Status::Success, aClusterStatus));
-}
-
-CHIP_ERROR CommandHandlerImpl::AddClusterSpecificFailure(const ConcreteCommandPath & aCommandPath, ClusterStatus aClusterStatus)
-{
-    return AddStatusInternal(aCommandPath, StatusIB(Status::Failure, aClusterStatus));
+    return AddStatusInternal(path, StatusIB{ status });
 }
 
 CHIP_ERROR CommandHandlerImpl::PrepareInvokeResponseCommand(const ConcreteCommandPath & aResponseCommandPath,
diff --git a/src/app/CommandHandlerImpl.h b/src/app/CommandHandlerImpl.h
index db7c851..55e356b 100644
--- a/src/app/CommandHandlerImpl.h
+++ b/src/app/CommandHandlerImpl.h
@@ -29,6 +29,7 @@
 #include <messaging/Flags.h>
 #include <protocols/Protocols.h>
 #include <protocols/interaction_model/Constants.h>
+#include <protocols/interaction_model/StatusCode.h>
 #include <system/SystemPacketBuffer.h>
 #include <system/TLVPacketBufferBackingStore.h>
 
@@ -114,15 +115,16 @@
     /**************** CommandHandler interface implementation ***********************/
 
     using CommandHandler::AddResponseData;
+    using CommandHandler::AddStatus;
+    using CommandHandler::FallibleAddStatus;
 
     void FlushAcksRightAwayOnSlowCommand() override;
 
-    CHIP_ERROR FallibleAddStatus(const ConcreteCommandPath & aRequestCommandPath, const Protocols::InteractionModel::Status aStatus,
+    CHIP_ERROR FallibleAddStatus(const ConcreteCommandPath & aRequestCommandPath,
+                                 const Protocols::InteractionModel::ClusterStatusCode & aStatus,
                                  const char * context = nullptr) override;
-    void AddStatus(const ConcreteCommandPath & aCommandPath, const Protocols::InteractionModel::Status aStatus,
+    void AddStatus(const ConcreteCommandPath & aCommandPath, const Protocols::InteractionModel::ClusterStatusCode & aStatus,
                    const char * context = nullptr) override;
-    CHIP_ERROR AddClusterSpecificSuccess(const ConcreteCommandPath & aRequestCommandPath, ClusterStatus aClusterStatus) override;
-    CHIP_ERROR AddClusterSpecificFailure(const ConcreteCommandPath & aRequestCommandPath, ClusterStatus aClusterStatus) override;
 
     CHIP_ERROR AddResponseData(const ConcreteCommandPath & aRequestCommandPath, CommandId aResponseCommandId,
                                const DataModel::EncodableToTLV & aEncodable) override;
diff --git a/src/app/CommandResponseHelper.h b/src/app/CommandResponseHelper.h
index a0209ac..9d8112f 100644
--- a/src/app/CommandResponseHelper.h
+++ b/src/app/CommandResponseHelper.h
@@ -21,6 +21,7 @@
 #include <app/CommandHandler.h>
 #include <app/ConcreteCommandPath.h>
 #include <protocols/interaction_model/Constants.h>
+#include <protocols/interaction_model/StatusCode.h>
 
 namespace chip {
 namespace app {
@@ -38,35 +39,27 @@
         mCommandHandler->AddResponse(mCommandPath, aResponse);
         mSentResponse = true;
         return CHIP_NO_ERROR;
-    };
+    }
 
-    CHIP_ERROR Success(ClusterStatus aClusterStatus)
+    CHIP_ERROR Success()
     {
-        CHIP_ERROR err = mCommandHandler->AddClusterSpecificSuccess(mCommandPath, aClusterStatus);
-        if (err == CHIP_NO_ERROR)
-        {
-            mSentResponse = true;
-        }
+        CHIP_ERROR err = mCommandHandler->FallibleAddStatus(mCommandPath, Protocols::InteractionModel::Status::Success);
+        mSentResponse  = (err == CHIP_NO_ERROR);
         return err;
     }
 
     CHIP_ERROR Failure(Protocols::InteractionModel::Status aStatus)
     {
         CHIP_ERROR err = mCommandHandler->FallibleAddStatus(mCommandPath, aStatus);
-        if (err == CHIP_NO_ERROR)
-        {
-            mSentResponse = true;
-        }
+        mSentResponse  = (err == CHIP_NO_ERROR);
         return err;
     }
 
     CHIP_ERROR Failure(ClusterStatus aClusterStatus)
     {
-        CHIP_ERROR err = mCommandHandler->AddClusterSpecificFailure(mCommandPath, aClusterStatus);
-        if (err == CHIP_NO_ERROR)
-        {
-            mSentResponse = true;
-        }
+        CHIP_ERROR err = mCommandHandler->FallibleAddStatus(
+            mCommandPath, Protocols::InteractionModel::ClusterStatusCode::ClusterSpecificFailure(aClusterStatus));
+        mSentResponse = (err == CHIP_NO_ERROR);
         return err;
     }
 
diff --git a/src/app/MessageDef/StatusIB.h b/src/app/MessageDef/StatusIB.h
index f5ade6c..0813704 100644
--- a/src/app/MessageDef/StatusIB.h
+++ b/src/app/MessageDef/StatusIB.h
@@ -34,6 +34,7 @@
 #include <lib/support/CodeUtils.h>
 #include <lib/support/logging/CHIPLogging.h>
 #include <protocols/interaction_model/Constants.h>
+#include <protocols/interaction_model/StatusCode.h>
 #include <protocols/secure_channel/Constants.h>
 
 namespace chip {
@@ -41,10 +42,24 @@
 struct StatusIB
 {
     StatusIB() = default;
-    StatusIB(Protocols::InteractionModel::Status imStatus) : mStatus(imStatus) {}
-    StatusIB(Protocols::InteractionModel::Status imStatus, ClusterStatus clusterStatus) :
+    explicit StatusIB(Protocols::InteractionModel::Status imStatus) : mStatus(imStatus) {}
+
+    explicit StatusIB(Protocols::InteractionModel::Status imStatus, ClusterStatus clusterStatus) :
         mStatus(imStatus), mClusterStatus(clusterStatus)
     {}
+
+    explicit StatusIB(const Protocols::InteractionModel::ClusterStatusCode & statusCode) : mStatus(statusCode.GetStatus())
+    {
+        // NOTE: Cluster-specific codes are only valid on SUCCESS/FAILURE IM status (7.10.7. Status Codes)
+        chip::Optional<ClusterStatus> clusterStatus = statusCode.GetClusterSpecificCode();
+        if (clusterStatus.HasValue())
+        {
+            mStatus        = statusCode.IsSuccess() ? Protocols::InteractionModel::Status::Success
+                                                    : Protocols::InteractionModel::Status::Failure;
+            mClusterStatus = clusterStatus;
+        }
+    }
+
     explicit StatusIB(CHIP_ERROR error) { InitFromChipError(error); }
 
     enum class Tag : uint8_t
diff --git a/src/app/WriteClient.h b/src/app/WriteClient.h
index 90f0f3a..b67bf4e 100644
--- a/src/app/WriteClient.h
+++ b/src/app/WriteClient.h
@@ -228,12 +228,6 @@
      */
     CHIP_ERROR SendWriteRequest(const SessionHandle & session, System::Clock::Timeout timeout = System::Clock::kZero);
 
-    /**
-     *  Shutdown the WriteClient. This terminates this instance
-     *  of the object and releases all held resources.
-     */
-    void Shutdown();
-
 private:
     friend class TestWriteInteraction;
     friend class InteractionModelEngine;
diff --git a/src/app/WriteHandler.cpp b/src/app/WriteHandler.cpp
index 068f1f6..aadb59a 100644
--- a/src/app/WriteHandler.cpp
+++ b/src/app/WriteHandler.cpp
@@ -21,6 +21,7 @@
 #include <app/AttributeAccessInterfaceRegistry.h>
 #include <app/InteractionModelEngine.h>
 #include <app/MessageDef/EventPathIB.h>
+#include <app/MessageDef/StatusIB.h>
 #include <app/StatusResponse.h>
 #include <app/WriteHandler.h>
 #include <app/reporting/Engine.h>
@@ -28,6 +29,7 @@
 #include <app/util/ember-compatibility-functions.h>
 #include <credentials/GroupDataProvider.h>
 #include <lib/support/TypeTraits.h>
+#include <protocols/interaction_model/StatusCode.h>
 
 namespace chip {
 namespace app {
@@ -315,7 +317,7 @@
             // it with Busy status code.
             (dataAttributePath.IsListItemOperation() && !IsSameAttribute(mProcessingAttributePath, dataAttributePath)))
         {
-            err = AddStatus(dataAttributePath, StatusIB(Status::Busy));
+            err = AddStatusInternal(dataAttributePath, StatusIB(Status::Busy));
             continue;
         }
 
@@ -353,7 +355,7 @@
         if (err != CHIP_NO_ERROR)
         {
             mWriteResponseBuilder.GetWriteResponses().Rollback(backup);
-            err = AddStatus(dataAttributePath, StatusIB(err));
+            err = AddStatusInternal(dataAttributePath, StatusIB(err));
         }
 
         DataModelCallbacks::GetInstance()->AttributeOperation(DataModelCallbacks::OperationType::Write,
@@ -616,22 +618,23 @@
     return status;
 }
 
-CHIP_ERROR WriteHandler::AddStatus(const ConcreteDataAttributePath & aPath, const Protocols::InteractionModel::Status aStatus)
+CHIP_ERROR WriteHandler::AddStatus(const ConcreteDataAttributePath & aPath,
+                                   const Protocols::InteractionModel::ClusterStatusCode & aStatus)
 {
-    return AddStatus(aPath, StatusIB(aStatus));
+    return AddStatusInternal(aPath, StatusIB{ aStatus });
 }
 
 CHIP_ERROR WriteHandler::AddClusterSpecificSuccess(const ConcreteDataAttributePath & aPath, ClusterStatus aClusterStatus)
 {
-    return AddStatus(aPath, StatusIB(Status::Success, aClusterStatus));
+    return AddStatus(aPath, Protocols::InteractionModel::ClusterStatusCode::ClusterSpecificSuccess(aClusterStatus));
 }
 
 CHIP_ERROR WriteHandler::AddClusterSpecificFailure(const ConcreteDataAttributePath & aPath, ClusterStatus aClusterStatus)
 {
-    return AddStatus(aPath, StatusIB(Status::Failure, aClusterStatus));
+    return AddStatus(aPath, Protocols::InteractionModel::ClusterStatusCode::ClusterSpecificFailure(aClusterStatus));
 }
 
-CHIP_ERROR WriteHandler::AddStatus(const ConcreteDataAttributePath & aPath, const StatusIB & aStatus)
+CHIP_ERROR WriteHandler::AddStatusInternal(const ConcreteDataAttributePath & aPath, const StatusIB & aStatus)
 {
     AttributeStatusIBs::Builder & writeResponses   = mWriteResponseBuilder.GetWriteResponses();
     AttributeStatusIB::Builder & attributeStatusIB = writeResponses.CreateAttributeStatus();
diff --git a/src/app/WriteHandler.h b/src/app/WriteHandler.h
index 907aa04..1884654 100644
--- a/src/app/WriteHandler.h
+++ b/src/app/WriteHandler.h
@@ -31,6 +31,7 @@
 #include <messaging/Flags.h>
 #include <protocols/Protocols.h>
 #include <protocols/interaction_model/Constants.h>
+#include <protocols/interaction_model/StatusCode.h>
 #include <system/SystemPacketBuffer.h>
 #include <system/TLVPacketBufferBackingStore.h>
 
@@ -100,11 +101,14 @@
     CHIP_ERROR ProcessAttributeDataIBs(TLV::TLVReader & aAttributeDataIBsReader);
     CHIP_ERROR ProcessGroupAttributeDataIBs(TLV::TLVReader & aAttributeDataIBsReader);
 
-    CHIP_ERROR AddStatus(const ConcreteDataAttributePath & aPath, const Protocols::InteractionModel::Status aStatus);
+    CHIP_ERROR AddStatus(const ConcreteDataAttributePath & aPath, const Protocols::InteractionModel::ClusterStatusCode & aStatus);
+    CHIP_ERROR AddStatus(const ConcreteDataAttributePath & aPath, const Protocols::InteractionModel::Status aStatus)
+    {
+        return AddStatus(aPath, Protocols::InteractionModel::ClusterStatusCode{ aStatus });
+    }
 
-    CHIP_ERROR AddClusterSpecificSuccess(const ConcreteDataAttributePath & aAttributePathParams, uint8_t aClusterStatus);
-
-    CHIP_ERROR AddClusterSpecificFailure(const ConcreteDataAttributePath & aAttributePathParams, uint8_t aClusterStatus);
+    CHIP_ERROR AddClusterSpecificSuccess(const ConcreteDataAttributePath & aAttributePathParams, ClusterStatus aClusterStatus);
+    CHIP_ERROR AddClusterSpecificFailure(const ConcreteDataAttributePath & aAttributePathParams, ClusterStatus aClusterStatus);
 
     FabricIndex GetAccessingFabricIndex() const;
 
@@ -166,7 +170,7 @@
     // ProcessGroupAttributeDataIBs.
     CHIP_ERROR DeliverFinalListWriteEndForGroupWrite(bool writeWasSuccessful);
 
-    CHIP_ERROR AddStatus(const ConcreteDataAttributePath & aPath, const StatusIB & aStatus);
+    CHIP_ERROR AddStatusInternal(const ConcreteDataAttributePath & aPath, const StatusIB & aStatus);
 
 private:
     // ExchangeDelegate
diff --git a/src/app/data-model-interface/InvokeResponder.h b/src/app/data-model-interface/InvokeResponder.h
index 54ae9ba..9890c3b 100644
--- a/src/app/data-model-interface/InvokeResponder.h
+++ b/src/app/data-model-interface/InvokeResponder.h
@@ -138,7 +138,7 @@
     {
         if (mCompleteState != CompleteState::kComplete)
         {
-            mWriter->Complete(Protocols::InteractionModel::Status::Failure);
+            mWriter->Complete(StatusIB{ Protocols::InteractionModel::Status::Failure });
         }
     }
 
diff --git a/src/app/tests/TestStatusIB.cpp b/src/app/tests/TestStatusIB.cpp
index 8da643d..2280616 100644
--- a/src/app/tests/TestStatusIB.cpp
+++ b/src/app/tests/TestStatusIB.cpp
@@ -161,4 +161,24 @@
     EXPECT_NE(invalid_argument, StatusIB(CHIP_NO_ERROR));
 }
 
+TEST_F(TestStatusIB, ConversionsFromClusterStatusCodeWork)
+{
+    StatusIB successWithCode{ ClusterStatusCode::ClusterSpecificSuccess(123u) };
+    EXPECT_EQ(successWithCode.mStatus, Status::Success);
+    EXPECT_TRUE(successWithCode.IsSuccess());
+    ASSERT_TRUE(successWithCode.mClusterStatus.HasValue());
+    EXPECT_EQ(successWithCode.mClusterStatus.Value(), 123u);
+
+    StatusIB failureWithCode{ ClusterStatusCode::ClusterSpecificFailure(42u) };
+    EXPECT_EQ(failureWithCode.mStatus, Status::Failure);
+    EXPECT_FALSE(failureWithCode.IsSuccess());
+    ASSERT_TRUE(failureWithCode.mClusterStatus.HasValue());
+    EXPECT_EQ(failureWithCode.mClusterStatus.Value(), 42u);
+
+    StatusIB imStatusInClusterStatusCode{ ClusterStatusCode{ Status::ConstraintError } };
+    EXPECT_EQ(imStatusInClusterStatusCode.mStatus, Status::ConstraintError);
+    EXPECT_FALSE(imStatusInClusterStatusCode.IsSuccess());
+    EXPECT_FALSE(imStatusInClusterStatusCode.mClusterStatus.HasValue());
+}
+
 } // namespace
diff --git a/src/controller/tests/data_model/TestCommands.cpp b/src/controller/tests/data_model/TestCommands.cpp
index adc6bd0..b6d39ae 100644
--- a/src/controller/tests/data_model/TestCommands.cpp
+++ b/src/controller/tests/data_model/TestCommands.cpp
@@ -37,6 +37,7 @@
 #include <lib/support/logging/CHIPLogging.h>
 #include <messaging/tests/MessagingContext.h>
 #include <protocols/interaction_model/Constants.h>
+#include <protocols/interaction_model/StatusCode.h>
 
 using TestContext = chip::Test::AppContext;
 
@@ -144,11 +145,13 @@
         }
         else if (responseDirective == kSendSuccessStatusCodeWithClusterStatus)
         {
-            apCommandObj->AddClusterSpecificSuccess(aCommandPath, kTestSuccessClusterStatus);
+            apCommandObj->AddStatus(
+                aCommandPath, Protocols::InteractionModel::ClusterStatusCode::ClusterSpecificSuccess(kTestSuccessClusterStatus));
         }
         else if (responseDirective == kSendErrorWithClusterStatus)
         {
-            apCommandObj->AddClusterSpecificFailure(aCommandPath, kTestFailureClusterStatus);
+            apCommandObj->AddStatus(
+                aCommandPath, Protocols::InteractionModel::ClusterStatusCode::ClusterSpecificFailure(kTestFailureClusterStatus));
         }
         else if (responseDirective == kAsync)
         {
diff --git a/src/controller/tests/data_model/TestWrite.cpp b/src/controller/tests/data_model/TestWrite.cpp
index 28f5797..e0f2c19 100644
--- a/src/controller/tests/data_model/TestWrite.cpp
+++ b/src/controller/tests/data_model/TestWrite.cpp
@@ -22,6 +22,7 @@
 #include <app-common/zap-generated/cluster-objects.h>
 #include <app/AttributeValueDecoder.h>
 #include <app/InteractionModelEngine.h>
+#include <app/WriteClient.h>
 #include <app/tests/AppTestContext.h>
 #include <controller/WriteInteraction.h>
 #include <lib/core/ErrorStr.h>
@@ -43,15 +44,20 @@
 constexpr DataVersion kRejectedDataVersion = 1;
 constexpr DataVersion kAcceptedDataVersion = 5;
 
+constexpr uint8_t kExampleClusterSpecificSuccess = 11u;
+constexpr uint8_t kExampleClusterSpecificFailure = 12u;
+
 enum ResponseDirective
 {
     kSendAttributeSuccess,
     kSendAttributeError,
     kSendMultipleSuccess,
     kSendMultipleErrors,
+    kSendClusterSpecificSuccess,
+    kSendClusterSpecificFailure,
 };
 
-ResponseDirective responseDirective;
+ResponseDirective gResponseDirective = kSendAttributeSuccess;
 
 } // namespace
 
@@ -78,7 +84,7 @@
     if (aPath.mClusterId == Clusters::UnitTesting::Id &&
         aPath.mAttributeId == Attributes::ListStructOctetString::TypeInfo::GetAttributeId())
     {
-        if (responseDirective == kSendAttributeSuccess)
+        if (gResponseDirective == kSendAttributeSuccess)
         {
             if (!aPath.IsListOperation() || aPath.mListOp == ConcreteDataAttributePath::ListOperation::ReplaceAll)
             {
@@ -153,20 +159,22 @@
         return CHIP_NO_ERROR;
     }
 
+    // Boolean attribute of unit testing cluster triggers "multiple errors" case.
     if (aPath.mClusterId == Clusters::UnitTesting::Id && aPath.mAttributeId == Attributes::Boolean::TypeInfo::GetAttributeId())
     {
-        InteractionModel::Status status;
-        if (responseDirective == kSendMultipleSuccess)
+        InteractionModel::ClusterStatusCode status{ Protocols::InteractionModel::Status::InvalidValue };
+
+        if (gResponseDirective == kSendMultipleSuccess)
         {
             status = InteractionModel::Status::Success;
         }
-        else if (responseDirective == kSendMultipleErrors)
+        else if (gResponseDirective == kSendMultipleErrors)
         {
             status = InteractionModel::Status::Failure;
         }
         else
         {
-            return CHIP_ERROR_INCORRECT_STATE;
+            VerifyOrDie(false);
         }
 
         for (size_t i = 0; i < 4; ++i)
@@ -177,8 +185,69 @@
         return CHIP_NO_ERROR;
     }
 
+    if (aPath.mClusterId == Clusters::UnitTesting::Id && aPath.mAttributeId == Attributes::Int8u::TypeInfo::GetAttributeId())
+    {
+        InteractionModel::ClusterStatusCode status{ Protocols::InteractionModel::Status::InvalidValue };
+        if (gResponseDirective == kSendClusterSpecificSuccess)
+        {
+            status = InteractionModel::ClusterStatusCode::ClusterSpecificSuccess(kExampleClusterSpecificSuccess);
+        }
+        else if (gResponseDirective == kSendClusterSpecificFailure)
+        {
+            status = InteractionModel::ClusterStatusCode::ClusterSpecificFailure(kExampleClusterSpecificFailure);
+        }
+        else
+        {
+            VerifyOrDie(false);
+        }
+
+        aWriteHandler->AddStatus(aPath, status);
+        return CHIP_NO_ERROR;
+    }
+
     return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
 }
+
+class SingleWriteCallback : public WriteClient::Callback
+{
+public:
+    explicit SingleWriteCallback(ConcreteAttributePath path) : mPath(path) {}
+
+    void OnResponse(const WriteClient * apWriteClient, const ConcreteDataAttributePath & aPath, StatusIB attributeStatus) override
+    {
+
+        if (aPath.MatchesConcreteAttributePath(mPath))
+        {
+            mPathWasReponded = true;
+            mPathStatus      = attributeStatus;
+        }
+    }
+
+    void OnError(const WriteClient * apWriteClient, CHIP_ERROR aError) override
+    {
+        (void) apWriteClient;
+        mLastChipError = aError;
+    }
+
+    void OnDone(WriteClient * apWriteClient) override
+    {
+        (void) apWriteClient;
+        mOnDoneCalled = true;
+    }
+
+    bool WasDone() const { return mOnDoneCalled; }
+    bool PathWasResponded() const { return mOnDoneCalled; }
+    CHIP_ERROR GetLastChipError() const { return mLastChipError; }
+    StatusIB GetPathStatus() const { return mPathStatus; }
+
+private:
+    ConcreteAttributePath mPath;
+    bool mOnDoneCalled        = false;
+    CHIP_ERROR mLastChipError = CHIP_NO_ERROR;
+    bool mPathWasReponded     = false;
+    StatusIB mPathStatus;
+};
+
 } // namespace app
 } // namespace chip
 
@@ -209,6 +278,12 @@
         }
     }
 
+    void ResetCallback() { mSingleWriteCallback.reset(); }
+
+    void PrepareWriteCallback(ConcreteAttributePath path) { mSingleWriteCallback = std::make_unique<SingleWriteCallback>(path); }
+
+    SingleWriteCallback * GetWriteCallback() { return mSingleWriteCallback.get(); }
+
 protected:
     // Performs setup for each individual test in the test suite
     void SetUp() { mpContext->SetUp(); }
@@ -217,7 +292,10 @@
     void TearDown() { mpContext->TearDown(); }
 
     static TestContext * mpContext;
+
+    std::unique_ptr<SingleWriteCallback> mSingleWriteCallback;
 };
+
 TestContext * TestWrite::mpContext = nullptr;
 
 TEST_F(TestWrite, TestDataResponse)
@@ -236,7 +314,7 @@
         i++;
     }
 
-    responseDirective = kSendAttributeSuccess;
+    gResponseDirective = kSendAttributeSuccess;
 
     // Passing of stack variables by reference is only safe because of synchronous completion of the interaction. Otherwise, it's
     // not safe to do so.
@@ -274,7 +352,7 @@
         i++;
     }
 
-    responseDirective = kSendAttributeSuccess;
+    gResponseDirective = kSendAttributeSuccess;
 
     // Passing of stack variables by reference is only safe because of synchronous completion of the interaction. Otherwise, it's
     // not safe to do so.
@@ -314,7 +392,7 @@
         i++;
     }
 
-    responseDirective = kSendAttributeSuccess;
+    gResponseDirective = kSendAttributeSuccess;
 
     // Passing of stack variables by reference is only safe because of synchronous completion of the interaction. Otherwise, it's
     // not safe to do so.
@@ -353,7 +431,7 @@
         i++;
     }
 
-    responseDirective = kSendAttributeError;
+    gResponseDirective = kSendAttributeError;
 
     // Passing of stack variables by reference is only safe because of synchronous completion of the interaction. Otherwise, it's
     // not safe to do so.
@@ -419,7 +497,7 @@
     size_t successCalls = 0;
     size_t failureCalls = 0;
 
-    responseDirective = kSendMultipleSuccess;
+    gResponseDirective = kSendMultipleSuccess;
 
     // Passing of stack variables by reference is only safe because of synchronous completion of the interaction. Otherwise, it's
     // not safe to do so.
@@ -446,7 +524,7 @@
     size_t successCalls = 0;
     size_t failureCalls = 0;
 
-    responseDirective = kSendMultipleErrors;
+    gResponseDirective = kSendMultipleErrors;
 
     // Passing of stack variables by reference is only safe because of synchronous completion of the interaction. Otherwise, it's
     // not safe to do so.
@@ -467,4 +545,74 @@
     EXPECT_EQ(mpContext->GetExchangeManager().GetNumActiveExchanges(), 0u);
 }
 
+TEST_F(TestWrite, TestWriteClusterSpecificStatuses)
+{
+    auto sessionHandle = mpContext->GetSessionBobToAlice();
+
+    // Cluster-specific success code case
+    {
+        gResponseDirective = kSendClusterSpecificSuccess;
+
+        this->ResetCallback();
+        this->PrepareWriteCallback(
+            ConcreteAttributePath{ kTestEndpointId, Clusters::UnitTesting::Id, Clusters::UnitTesting::Attributes::Int8u::Id });
+
+        SingleWriteCallback * writeCb = this->GetWriteCallback();
+
+        WriteClient writeClient(&mpContext->GetExchangeManager(), this->GetWriteCallback(), Optional<uint16_t>::Missing());
+        AttributePathParams attributePath{ kTestEndpointId, Clusters::UnitTesting::Id,
+                                           Clusters::UnitTesting::Attributes::Int8u::Id };
+        constexpr uint8_t attributeValue = 1u;
+        ASSERT_EQ(writeClient.EncodeAttribute(attributePath, attributeValue), CHIP_NO_ERROR);
+        ASSERT_EQ(writeClient.SendWriteRequest(sessionHandle), CHIP_NO_ERROR);
+
+        mpContext->DrainAndServiceIO();
+
+        EXPECT_TRUE(writeCb->WasDone());
+        EXPECT_TRUE(writeCb->PathWasResponded());
+        EXPECT_EQ(writeCb->GetLastChipError(), CHIP_NO_ERROR);
+
+        StatusIB pathStatus = writeCb->GetPathStatus();
+        EXPECT_EQ(pathStatus.mStatus, Protocols::InteractionModel::Status::Success);
+        ASSERT_TRUE(pathStatus.mClusterStatus.HasValue());
+        EXPECT_EQ(pathStatus.mClusterStatus.Value(), kExampleClusterSpecificSuccess);
+
+        EXPECT_EQ(chip::app::InteractionModelEngine::GetInstance()->GetNumActiveWriteHandlers(), 0u);
+        EXPECT_EQ(mpContext->GetExchangeManager().GetNumActiveExchanges(), 0u);
+    }
+
+    // Cluster-specific failure code case
+    {
+        gResponseDirective = kSendClusterSpecificFailure;
+
+        this->ResetCallback();
+        this->PrepareWriteCallback(
+            ConcreteAttributePath{ kTestEndpointId, Clusters::UnitTesting::Id, Clusters::UnitTesting::Attributes::Int8u::Id });
+
+        SingleWriteCallback * writeCb = this->GetWriteCallback();
+
+        WriteClient writeClient(&mpContext->GetExchangeManager(), this->GetWriteCallback(), Optional<uint16_t>::Missing());
+        AttributePathParams attributePath{ kTestEndpointId, Clusters::UnitTesting::Id,
+                                           Clusters::UnitTesting::Attributes::Int8u::Id };
+
+        constexpr uint8_t attributeValue = 2u;
+        ASSERT_EQ(writeClient.EncodeAttribute(attributePath, attributeValue), CHIP_NO_ERROR);
+        ASSERT_EQ(writeClient.SendWriteRequest(sessionHandle), CHIP_NO_ERROR);
+
+        mpContext->DrainAndServiceIO();
+
+        EXPECT_TRUE(writeCb->WasDone());
+        EXPECT_TRUE(writeCb->PathWasResponded());
+        EXPECT_EQ(writeCb->GetLastChipError(), CHIP_NO_ERROR);
+
+        StatusIB pathStatus = writeCb->GetPathStatus();
+        EXPECT_EQ(pathStatus.mStatus, Protocols::InteractionModel::Status::Failure);
+        ASSERT_TRUE(pathStatus.mClusterStatus.HasValue());
+        EXPECT_EQ(pathStatus.mClusterStatus.Value(), kExampleClusterSpecificFailure);
+
+        EXPECT_EQ(chip::app::InteractionModelEngine::GetInstance()->GetNumActiveWriteHandlers(), 0u);
+        EXPECT_EQ(mpContext->GetExchangeManager().GetNumActiveExchanges(), 0u);
+    }
+}
+
 } // namespace
diff --git a/src/protocols/interaction_model/StatusCode.h b/src/protocols/interaction_model/StatusCode.h
index b004b4e..9326c86 100644
--- a/src/protocols/interaction_model/StatusCode.h
+++ b/src/protocols/interaction_model/StatusCode.h
@@ -18,6 +18,8 @@
 #pragma once
 
 #include <limits>
+#include <type_traits>
+
 #include <stdint.h>
 
 #include <lib/core/CHIPConfig.h>
@@ -58,10 +60,6 @@
  * are the components of a StatusIB, used in many IM actions, in a
  * way which allows both of them to carry together.
  *
- * This can be used everywhere a `Status` is used, but it is lossy
- * to the cluster-specific code if used in place of `Status` when
- * the cluster-specific code is set.
- *
  * This class can only be directly constructed from a `Status`. To
  * attach a cluster-specific-code, please use the `ClusterSpecificFailure()`
  * and `ClusterSpecificSuccess()` factory methods.
@@ -75,13 +73,13 @@
     ClusterStatusCode(const ClusterStatusCode & other)             = default;
     ClusterStatusCode & operator=(const ClusterStatusCode & other) = default;
 
-    bool operator==(const ClusterStatusCode & other)
+    bool operator==(const ClusterStatusCode & other) const
     {
         return (this->mStatus == other.mStatus) && (this->HasClusterSpecificCode() == other.HasClusterSpecificCode()) &&
             (this->GetClusterSpecificCode() == other.GetClusterSpecificCode());
     }
 
-    bool operator!=(const ClusterStatusCode & other) { return !(*this == other); }
+    bool operator!=(const ClusterStatusCode & other) const { return !(*this == other); }
 
     ClusterStatusCode & operator=(const Status & status)
     {
@@ -99,7 +97,7 @@
      *             (e.g. chip::app::Clusters::AdministratorCommissioning::CommissioningWindowStatusEnum::kWindowNotOpen)
      * @return a ClusterStatusCode instance properly configured.
      */
-    template <typename T>
+    template <typename T, typename std::enable_if_t<std::is_enum<T>::value, bool> = true>
     static ClusterStatusCode ClusterSpecificFailure(T cluster_specific_code)
     {
         static_assert(std::numeric_limits<std::underlying_type_t<T>>::max() <= std::numeric_limits<ClusterStatus>::max(),
@@ -107,6 +105,11 @@
         return ClusterStatusCode(Status::Failure, chip::to_underlying(cluster_specific_code));
     }
 
+    static ClusterStatusCode ClusterSpecificFailure(ClusterStatus cluster_specific_code)
+    {
+        return ClusterStatusCode(Status::Failure, cluster_specific_code);
+    }
+
     /**
      * @brief Builder for a cluster-specific success status code.
      *
@@ -116,7 +119,7 @@
      *             (e.g. chip::app::Clusters::AdministratorCommissioning::CommissioningWindowStatusEnum::kBasicWindowOpen)
      * @return a ClusterStatusCode instance properly configured.
      */
-    template <typename T>
+    template <typename T, typename std::enable_if_t<std::is_enum<T>::value, bool> = true>
     static ClusterStatusCode ClusterSpecificSuccess(T cluster_specific_code)
     {
         static_assert(std::numeric_limits<std::underlying_type_t<T>>::max() <= std::numeric_limits<ClusterStatus>::max(),
@@ -124,6 +127,11 @@
         return ClusterStatusCode(Status::Success, chip::to_underlying(cluster_specific_code));
     }
 
+    static ClusterStatusCode ClusterSpecificSuccess(ClusterStatus cluster_specific_code)
+    {
+        return ClusterStatusCode(Status::Success, cluster_specific_code);
+    }
+
     /// @return true if the core Status associated with this ClusterStatusCode is the one for success.
     bool IsSuccess() const { return mStatus == Status::Success; }
 
@@ -143,9 +151,6 @@
         return mClusterSpecificCode;
     }
 
-    // Automatic conversions to common types, using the status code alone.
-    operator Status() const { return mStatus; }
-
 private:
     ClusterStatusCode() = delete;
     ClusterStatusCode(Status status, ClusterStatus cluster_specific_code) :
diff --git a/src/protocols/interaction_model/tests/TestStatusCode.cpp b/src/protocols/interaction_model/tests/TestStatusCode.cpp
index 2be78c4..3183d68 100644
--- a/src/protocols/interaction_model/tests/TestStatusCode.cpp
+++ b/src/protocols/interaction_model/tests/TestStatusCode.cpp
@@ -40,26 +40,22 @@
     // Basic usage as a Status.
     {
         ClusterStatusCode status_code_success{ Status::Success };
-        EXPECT_EQ(status_code_success, Status::Success);
         EXPECT_EQ(status_code_success.GetStatus(), Status::Success);
         EXPECT_FALSE(status_code_success.HasClusterSpecificCode());
         EXPECT_EQ(status_code_success.GetClusterSpecificCode(), chip::NullOptional);
         EXPECT_TRUE(status_code_success.IsSuccess());
 
         ClusterStatusCode status_code_failure{ Status::Failure };
-        EXPECT_EQ(status_code_failure, Status::Failure);
         EXPECT_EQ(status_code_failure.GetStatus(), Status::Failure);
         EXPECT_FALSE(status_code_failure.HasClusterSpecificCode());
         EXPECT_FALSE(status_code_failure.IsSuccess());
 
         ClusterStatusCode status_code_unsupported_ep{ Status::UnsupportedEndpoint };
-        EXPECT_EQ(status_code_unsupported_ep, Status::UnsupportedEndpoint);
         EXPECT_EQ(status_code_unsupported_ep.GetStatus(), Status::UnsupportedEndpoint);
         EXPECT_FALSE(status_code_unsupported_ep.HasClusterSpecificCode());
         EXPECT_FALSE(status_code_unsupported_ep.IsSuccess());
 
         ClusterStatusCode status_code_invalid_in_state{ Status::InvalidInState };
-        EXPECT_EQ(status_code_invalid_in_state, Status::InvalidInState);
         EXPECT_EQ(status_code_invalid_in_state.GetStatus(), Status::InvalidInState);
         EXPECT_FALSE(status_code_invalid_in_state.HasClusterSpecificCode());
         EXPECT_FALSE(status_code_invalid_in_state.IsSuccess());
@@ -74,13 +70,13 @@
     // Cluster-specific usage.
     {
         ClusterStatusCode status_code_success = ClusterStatusCode::ClusterSpecificSuccess(RobotoClusterStatus::kSauceSuccess);
-        EXPECT_EQ(status_code_success, Status::Success);
+        EXPECT_EQ(status_code_success.GetStatus(), Status::Success);
         EXPECT_TRUE(status_code_success.HasClusterSpecificCode());
         EXPECT_EQ(status_code_success.GetClusterSpecificCode(), static_cast<uint8_t>(RobotoClusterStatus::kSauceSuccess));
         EXPECT_TRUE(status_code_success.IsSuccess());
 
         ClusterStatusCode status_code_failure = ClusterStatusCode::ClusterSpecificFailure(RobotoClusterStatus::kSandwichError);
-        EXPECT_EQ(status_code_failure, Status::Failure);
+        EXPECT_EQ(status_code_failure.GetStatus(), Status::Failure);
         EXPECT_TRUE(status_code_failure.HasClusterSpecificCode());
         EXPECT_EQ(status_code_failure.GetClusterSpecificCode(), static_cast<uint8_t>(RobotoClusterStatus::kSandwichError));
         EXPECT_FALSE(status_code_failure.IsSuccess());