Repository: crossplane/crossplane

Issue:
Crossplane is fighting with other XR condition owners when composition fails
<!--
Thank you for helping to improve Crossplane!

Please be sure to search for open issues before raising a new one. We use issues
for bug reports and feature requests. Please find us at https://slack.crossplane.io
for questions, support, and discussion.
-->

### What happened?
<!--
Please let us know what behaviour you expected and how Crossplane diverged from
that behaviour.
-->

Compare https://github.com/crossplane/crossplane/pull/5450/files#r1951097798:

> There might be other parties in the system that own conditions. This loop here will make Crossplane fight with them. Crossplane sets them to Unknown, the other controller update them to whatever they think is right.

### How can we reproduce it?

Run a controller with a condition added to an XR. Then make the compose function fail, e.g. by having a pipeline step with an unknown function.

Then the controller and Crossplane fight over that 3rdparty condition.

<!--
Help us to reproduce your bug as succinctly and precisely as possible. Artifacts
such as example manifests or a script that triggers the issue are highly
appreciated!
-->

### What environment did it happen in?
Crossplane version: 1.18.2

<!--
Include at least the version or commit of Crossplane you were running. Consider
also including your:

* Cloud provider or hardware configuration
* Kubernetes version (use `kubectl version`)
* Kubernetes distribution (e.g. Tectonic, GKE, OpenShift)
* OS (e.g. from /etc/os-release)
* Kernel (e.g. `uname -a`)
-->


Discussion:
@github-actions: Crossplane does not currently have enough maintainers to address every issue and pull request. This issue has been automatically marked as `stale` because it has had no activity in the last 90 days. It will be closed in 14 days if no further activity occurs. Leaving a comment **starting with** `/fresh` will mark this issue as not stale.

Solution patch:
diff --git a/apis/apiextensions/fn/proto/v1beta1/run_function.pb.go b/apis/apiextensions/fn/proto/v1beta1/run_function.pb.go
index ef5f1a8de..20a9660b6 100644
--- a/apis/apiextensions/fn/proto/v1beta1/run_function.pb.go
+++ b/apis/apiextensions/fn/proto/v1beta1/run_function.pb.go
@@ -150,6 +150,113 @@ func (Severity) EnumDescriptor() ([]byte, []int) {
 	return file_apiextensions_fn_proto_v1beta1_run_function_proto_rawDescGZIP(), []int{1}
 }
 
+// Target of Function results and conditions.
+type Target int32
+
+const (
+	// If the target is unspecified, the result targets the composite resource.
+	Target_TARGET_UNSPECIFIED Target = 0
+	// Target the composite resource. Results that target the composite resource
+	// should include detailed, advanced information.
+	Target_TARGET_COMPOSITE Target = 1
+	// Target the composite and the claim. Results that target the composite and
+	// the claim should include only end-user friendly information.
+	Target_TARGET_COMPOSITE_AND_CLAIM Target = 2
+)
+
+// Enum value maps for Target.
+var (
+	Target_name = map[int32]string{
+		0: "TARGET_UNSPECIFIED",
+		1: "TARGET_COMPOSITE",
+		2: "TARGET_COMPOSITE_AND_CLAIM",
+	}
+	Target_value = map[string]int32{
+		"TARGET_UNSPECIFIED":         0,
+		"TARGET_COMPOSITE":           1,
+		"TARGET_COMPOSITE_AND_CLAIM": 2,
+	}
+)
+
+func (x Target) Enum() *Target {
+	p := new(Target)
+	*p = x
+	return p
+}
+
+func (x Target) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (Target) Descriptor() protoreflect.EnumDescriptor {
+	return file_apiextensions_fn_proto_v1beta1_run_function_proto_enumTypes[2].Descriptor()
+}
+
+func (Target) Type() protoreflect.EnumType {
+	return &file_apiextensions_fn_proto_v1beta1_run_function_proto_enumTypes[2]
+}
+
+func (x Target) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use Target.Descriptor instead.
+func (Target) EnumDescriptor() ([]byte, []int) {
+	return file_apiextensions_fn_proto_v1beta1_run_function_proto_rawDescGZIP(), []int{2}
+}
+
+type Status int32
+
+const (
+	Status_STATUS_CONDITION_UNSPECIFIED Status = 0
+	Status_STATUS_CONDITION_UNKNOWN     Status = 1
+	Status_STATUS_CONDITION_TRUE        Status = 2
+	Status_STATUS_CONDITION_FALSE       Status = 3
+)
+
+// Enum value maps for Status.
+var (
+	Status_name = map[int32]string{
+		0: "STATUS_CONDITION_UNSPECIFIED",
+		1: "STATUS_CONDITION_UNKNOWN",
+		2: "STATUS_CONDITION_TRUE",
+		3: "STATUS_CONDITION_FALSE",
+	}
+	Status_value = map[string]int32{
+		"STATUS_CONDITION_UNSPECIFIED": 0,
+		"STATUS_CONDITION_UNKNOWN":     1,
+		"STATUS_CONDITION_TRUE":        2,
+		"STATUS_CONDITION_FALSE":       3,
+	}
+)
+
+func (x Status) Enum() *Status {
+	p := new(Status)
+	*p = x
+	return p
+}
+
+func (x Status) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (Status) Descriptor() protoreflect.EnumDescriptor {
+	return file_apiextensions_fn_proto_v1beta1_run_function_proto_enumTypes[3].Descriptor()
+}
+
+func (Status) Type() protoreflect.EnumType {
+	return &file_apiextensions_fn_proto_v1beta1_run_function_proto_enumTypes[3]
+}
+
+func (x Status) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use Status.Descriptor instead.
+func (Status) EnumDescriptor() ([]byte, []int) {
+	return file_apiextensions_fn_proto_v1beta1_run_function_proto_rawDescGZIP(), []int{3}
+}
+
 // A RunFunctionRequest requests that the Composition Function be run.
 type RunFunctionRequest struct {
 	state         protoimpl.MessageState
@@ -468,6 +575,9 @@ type RunFunctionResponse struct {
 	Context *structpb.Struct `protobuf:"bytes,4,opt,name=context,proto3,oneof" json:"context,omitempty"`
 	// Requirements that must be satisfied for this Function to run successfully.
 	Requirements *Requirements `protobuf:"bytes,5,opt,name=requirements,proto3" json:"requirements,omitempty"`
+	// Status conditions to be applied to the composite resource. Conditions may also
+	// optionally be applied to the composite resource's associated claim.
+	Conditions []*Condition `protobuf:"bytes,6,rep,name=conditions,proto3" json:"conditions,omitempty"`
 }
 
 func (x *RunFunctionResponse) Reset() {
@@ -537,6 +647,13 @@ func (x *RunFunctionResponse) GetRequirements() *Requirements {
 	return nil
 }
 
+func (x *RunFunctionResponse) GetConditions() []*Condition {
+	if x != nil {
+		return x.Conditions
+	}
+	return nil
+}
+
 // RequestMeta contains metadata pertaining to a RunFunctionRequest.
 type RequestMeta struct {
 	state         protoimpl.MessageState
@@ -1017,6 +1134,11 @@ type Result struct {
 	Severity Severity `protobuf:"varint,1,opt,name=severity,proto3,enum=apiextensions.fn.proto.v1beta1.Severity" json:"severity,omitempty"`
 	// Human-readable details about the result.
 	Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"`
+	// Optional PascalCase, machine-readable reason for this result. If omitted,
+	// the value will be ComposeResources.
+	Reason *string `protobuf:"bytes,3,opt,name=reason,proto3,oneof" json:"reason,omitempty"`
+	// The resources this result targets.
+	Target *Target `protobuf:"varint,4,opt,name=target,proto3,enum=apiextensions.fn.proto.v1beta1.Target,oneof" json:"target,omitempty"`
 }
 
 func (x *Result) Reset() {
@@ -1065,6 +1187,113 @@ func (x *Result) GetMessage() string {
 	return ""
 }
 
+func (x *Result) GetReason() string {
+	if x != nil && x.Reason != nil {
+		return *x.Reason
+	}
+	return ""
+}
+
+func (x *Result) GetTarget() Target {
+	if x != nil && x.Target != nil {
+		return *x.Target
+	}
+	return Target_TARGET_UNSPECIFIED
+}
+
+// Status condition to be applied to the composite resource. Condition may also
+// optionally be applied to the composite resource's associated claim. For
+// detailed information on proper usage of status conditions, please see
+// https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties.
+type Condition struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Type of condition in PascalCase.
+	Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"`
+	// Status of the condition.
+	Status Status `protobuf:"varint,2,opt,name=status,proto3,enum=apiextensions.fn.proto.v1beta1.Status" json:"status,omitempty"`
+	// Reason contains a programmatic identifier indicating the reason for the
+	// condition's last transition. Producers of specific condition types may
+	// define expected values and meanings for this field, and whether the values
+	// are considered a guaranteed API. The value should be a PascalCase string.
+	// This field may not be empty.
+	Reason string `protobuf:"bytes,3,opt,name=reason,proto3" json:"reason,omitempty"`
+	// Message is a human readable message indicating details about the
+	// transition. This may be an empty string.
+	Message *string `protobuf:"bytes,4,opt,name=message,proto3,oneof" json:"message,omitempty"`
+	// The resources this condition targets.
+	Target *Target `protobuf:"varint,5,opt,name=target,proto3,enum=apiextensions.fn.proto.v1beta1.Target,oneof" json:"target,omitempty"`
+}
+
+func (x *Condition) Reset() {
+	*x = Condition{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_apiextensions_fn_proto_v1beta1_run_function_proto_msgTypes[13]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Condition) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Condition) ProtoMessage() {}
+
+func (x *Condition) ProtoReflect() protoreflect.Message {
+	mi := &file_apiextensions_fn_proto_v1beta1_run_function_proto_msgTypes[13]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Condition.ProtoReflect.Descriptor instead.
+func (*Condition) Descriptor() ([]byte, []int) {
+	return file_apiextensions_fn_proto_v1beta1_run_function_proto_rawDescGZIP(), []int{13}
+}
+
+func (x *Condition) GetType() string {
+	if x != nil {
+		return x.Type
+	}
+	return ""
+}
+
+func (x *Condition) GetStatus() Status {
+	if x != nil {
+		return x.Status
+	}
+	return Status_STATUS_CONDITION_UNSPECIFIED
+}
+
+func (x *Condition) GetReason() string {
+	if x != nil {
+		return x.Reason
+	}
+	return ""
+}
+
+func (x *Condition) GetMessage() string {
+	if x != nil && x.Message != nil {
+		return *x.Message
+	}
+	return ""
+}
+
+func (x *Condition) GetTarget() Target {
+	if x != nil && x.Target != nil {
+		return *x.Target
+	}
+	return Target_TARGET_UNSPECIFIED
+}
+
 var File_apiextensions_fn_proto_v1beta1_run_function_proto protoreflect.FileDescriptor
 
 var file_apiextensions_fn_proto_v1beta1_run_function_proto_rawDesc = []byte{
@@ -1147,7 +1376,7 @@ var file_apiextensions_fn_proto_v1beta1_run_function_proto_rawDesc = []byte{
 	0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x61,
 	0x70, 0x69, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x66, 0x6e, 0x2e,
 	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65,
-	0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x22, 0xf0, 0x02,
+	0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x22, 0xbb, 0x03,
 	0x0a, 0x13, 0x52, 0x75, 0x6e, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73,
 	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x01, 0x20,
 	0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x61, 0x70, 0x69, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69,
@@ -1170,120 +1399,161 @@ var file_apiextensions_fn_proto_v1beta1_run_function_proto_rawDesc = []byte{
 	0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x66, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74,
 	0x6f, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72,
 	0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x0c, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d,
-	0x65, 0x6e, 0x74, 0x73, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74,
-	0x22, 0x1f, 0x0a, 0x0b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x12,
-	0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61,
-	0x67, 0x22, 0xee, 0x01, 0x0a, 0x0c, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e,
-	0x74, 0x73, 0x12, 0x69, 0x0a, 0x0f, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x72, 0x65, 0x73, 0x6f,
-	0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x40, 0x2e, 0x61, 0x70,
-	0x69, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x66, 0x6e, 0x2e, 0x70,
-	0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x71,
-	0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x45, 0x78, 0x74, 0x72, 0x61, 0x52,
-	0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, 0x65,
-	0x78, 0x74, 0x72, 0x61, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x1a, 0x73, 0x0a,
-	0x13, 0x45, 0x78, 0x74, 0x72, 0x61, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x45,
-	0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28,
-	0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x46, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18,
-	0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x61, 0x70, 0x69, 0x65, 0x78, 0x74, 0x65, 0x6e,
-	0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x66, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76,
-	0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53,
-	0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02,
-	0x38, 0x01, 0x22, 0xc3, 0x01, 0x0a, 0x10, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53,
-	0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x70, 0x69, 0x5f, 0x76,
-	0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x70,
-	0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64,
-	0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x1f, 0x0a, 0x0a,
-	0x6d, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
-	0x48, 0x00, 0x52, 0x09, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x50, 0x0a,
-	0x0c, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x04, 0x20,
-	0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x61, 0x70, 0x69, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69,
+	0x65, 0x6e, 0x74, 0x73, 0x12, 0x49, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f,
+	0x6e, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x61, 0x70, 0x69, 0x65, 0x78,
+	0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x66, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74,
+	0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42,
+	0x0a, 0x0a, 0x08, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x1f, 0x0a, 0x0b, 0x52,
+	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61,
+	0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x22, 0xee, 0x01, 0x0a,
+	0x0c, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x69, 0x0a,
+	0x0f, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73,
+	0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x40, 0x2e, 0x61, 0x70, 0x69, 0x65, 0x78, 0x74, 0x65,
+	0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x66, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e,
+	0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d,
+	0x65, 0x6e, 0x74, 0x73, 0x2e, 0x45, 0x78, 0x74, 0x72, 0x61, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72,
+	0x63, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, 0x65, 0x78, 0x74, 0x72, 0x61, 0x52,
+	0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x1a, 0x73, 0x0a, 0x13, 0x45, 0x78, 0x74, 0x72,
+	0x61, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12,
+	0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65,
+	0x79, 0x12, 0x46, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
+	0x32, 0x30, 0x2e, 0x61, 0x70, 0x69, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73,
+	0x2e, 0x66, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61,
+	0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74,
+	0x6f, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xc3, 0x01,
+	0x0a, 0x10, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74,
+	0x6f, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
+	0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x70, 0x69, 0x56, 0x65, 0x72, 0x73,
+	0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x1f, 0x0a, 0x0a, 0x6d, 0x61, 0x74, 0x63, 0x68,
+	0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x09, 0x6d,
+	0x61, 0x74, 0x63, 0x68, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x50, 0x0a, 0x0c, 0x6d, 0x61, 0x74, 0x63,
+	0x68, 0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b,
+	0x2e, 0x61, 0x70, 0x69, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x66,
+	0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e,
+	0x4d, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x0b, 0x6d,
+	0x61, 0x74, 0x63, 0x68, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x42, 0x07, 0x0a, 0x05, 0x6d, 0x61,
+	0x74, 0x63, 0x68, 0x22, 0x99, 0x01, 0x0a, 0x0b, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x61, 0x62,
+	0x65, 0x6c, 0x73, 0x12, 0x4f, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x01, 0x20,
+	0x03, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x61, 0x70, 0x69, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69,
 	0x6f, 0x6e, 0x73, 0x2e, 0x66, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x31, 0x62,
 	0x65, 0x74, 0x61, 0x31, 0x2e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73,
-	0x48, 0x00, 0x52, 0x0b, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x42,
-	0x07, 0x0a, 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x22, 0x99, 0x01, 0x0a, 0x0b, 0x4d, 0x61, 0x74,
-	0x63, 0x68, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x4f, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65,
-	0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x61, 0x70, 0x69, 0x65, 0x78,
+	0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61,
+	0x62, 0x65, 0x6c, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e,
+	0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22,
+	0x5a, 0x0a, 0x0c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x12,
+	0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61,
+	0x67, 0x12, 0x30, 0x0a, 0x03, 0x74, 0x74, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19,
+	0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
+	0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x03, 0x74, 0x74, 0x6c,
+	0x88, 0x01, 0x01, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x74, 0x74, 0x6c, 0x22, 0x8b, 0x02, 0x0a, 0x05,
+	0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x46, 0x0a, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69,
+	0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x61, 0x70, 0x69, 0x65, 0x78,
 	0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x66, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-	0x6f, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x4c,
-	0x61, 0x62, 0x65, 0x6c, 0x73, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72,
-	0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62,
-	0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18,
-	0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61,
-	0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
-	0x3a, 0x02, 0x38, 0x01, 0x22, 0x5a, 0x0a, 0x0c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
-	0x4d, 0x65, 0x74, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28,
-	0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x30, 0x0a, 0x03, 0x74, 0x74, 0x6c, 0x18, 0x02, 0x20,
-	0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
-	0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00,
-	0x52, 0x03, 0x74, 0x74, 0x6c, 0x88, 0x01, 0x01, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x74, 0x74, 0x6c,
-	0x22, 0x8b, 0x02, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x46, 0x0a, 0x09, 0x63, 0x6f,
-	0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e,
-	0x61, 0x70, 0x69, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x66, 0x6e,
-	0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52,
-	0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69,
-	0x74, 0x65, 0x12, 0x52, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18,
-	0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x61, 0x70, 0x69, 0x65, 0x78, 0x74, 0x65, 0x6e,
-	0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x66, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76,
-	0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x52, 0x65, 0x73,
-	0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, 0x72, 0x65, 0x73,
-	0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x1a, 0x66, 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72,
-	0x63, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18,
-	0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x3e, 0x0a, 0x05, 0x76, 0x61,
-	0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x61, 0x70, 0x69, 0x65,
-	0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x66, 0x6e, 0x2e, 0x70, 0x72, 0x6f,
-	0x74, 0x6f, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75,
-	0x72, 0x63, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xb2,
-	0x02, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x33, 0x0a, 0x08, 0x72,
-	0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e,
-	0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
-	0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
-	0x12, 0x6e, 0x0a, 0x12, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64,
-	0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3f, 0x2e, 0x61,
-	0x70, 0x69, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x66, 0x6e, 0x2e,
-	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65,
-	0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f,
-	0x6e, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x11, 0x63,
-	0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73,
-	0x12, 0x3b, 0x0a, 0x05, 0x72, 0x65, 0x61, 0x64, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32,
-	0x25, 0x2e, 0x61, 0x70, 0x69, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e,
-	0x66, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31,
-	0x2e, 0x52, 0x65, 0x61, 0x64, 0x79, 0x52, 0x05, 0x72, 0x65, 0x61, 0x64, 0x79, 0x1a, 0x44, 0x0a,
-	0x16, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x74, 0x61, 0x69,
-	0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01,
-	0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c,
-	0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a,
-	0x02, 0x38, 0x01, 0x22, 0x68, 0x0a, 0x06, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x44, 0x0a,
-	0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32,
-	0x28, 0x2e, 0x61, 0x70, 0x69, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e,
-	0x66, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31,
-	0x2e, 0x53, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x52, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72,
-	0x69, 0x74, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02,
-	0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2a, 0x3f, 0x0a,
-	0x05, 0x52, 0x65, 0x61, 0x64, 0x79, 0x12, 0x15, 0x0a, 0x11, 0x52, 0x45, 0x41, 0x44, 0x59, 0x5f,
-	0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0e, 0x0a,
-	0x0a, 0x52, 0x45, 0x41, 0x44, 0x59, 0x5f, 0x54, 0x52, 0x55, 0x45, 0x10, 0x01, 0x12, 0x0f, 0x0a,
-	0x0b, 0x52, 0x45, 0x41, 0x44, 0x59, 0x5f, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x10, 0x02, 0x2a, 0x63,
-	0x0a, 0x08, 0x53, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x12, 0x18, 0x0a, 0x14, 0x53, 0x45,
-	0x56, 0x45, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49,
-	0x45, 0x44, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x45, 0x56, 0x45, 0x52, 0x49, 0x54, 0x59,
-	0x5f, 0x46, 0x41, 0x54, 0x41, 0x4c, 0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, 0x53, 0x45, 0x56, 0x45,
-	0x52, 0x49, 0x54, 0x59, 0x5f, 0x57, 0x41, 0x52, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x13,
-	0x0a, 0x0f, 0x53, 0x45, 0x56, 0x45, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x4e, 0x4f, 0x52, 0x4d, 0x41,
-	0x4c, 0x10, 0x03, 0x32, 0x91, 0x01, 0x0a, 0x15, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e,
-	0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x78, 0x0a,
-	0x0b, 0x52, 0x75, 0x6e, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x32, 0x2e, 0x61,
-	0x70, 0x69, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x66, 0x6e, 0x2e,
-	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x75,
-	0x6e, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
-	0x1a, 0x33, 0x2e, 0x61, 0x70, 0x69, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73,
+	0x6f, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72,
+	0x63, 0x65, 0x52, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x65, 0x12, 0x52, 0x0a,
+	0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b,
+	0x32, 0x34, 0x2e, 0x61, 0x70, 0x69, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73,
 	0x2e, 0x66, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61,
-	0x31, 0x2e, 0x52, 0x75, 0x6e, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73,
-	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x46, 0x5a, 0x44, 0x67, 0x69, 0x74, 0x68, 0x75,
-	0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x6f, 0x73, 0x73, 0x70, 0x6c, 0x61, 0x6e, 0x65,
-	0x2f, 0x63, 0x72, 0x6f, 0x73, 0x73, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x73,
-	0x2f, 0x61, 0x70, 0x69, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x66,
-	0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x62,
-	0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
+	0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
+	0x73, 0x1a, 0x66, 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x45, 0x6e,
+	0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x3e, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02,
+	0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x61, 0x70, 0x69, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73,
+	0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x66, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x31,
+	0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x05,
+	0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xb2, 0x02, 0x0a, 0x08, 0x52, 0x65,
+	0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x33, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72,
+	0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
+	0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63,
+	0x74, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x6e, 0x0a, 0x12, 0x63,
+	0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c,
+	0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3f, 0x2e, 0x61, 0x70, 0x69, 0x65, 0x78, 0x74,
+	0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x66, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
+	0x65, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x74, 0x61,
+	0x69, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x11, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63,
+	0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x3b, 0x0a, 0x05, 0x72,
+	0x65, 0x61, 0x64, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x25, 0x2e, 0x61, 0x70, 0x69,
+	0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x66, 0x6e, 0x2e, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x61, 0x64,
+	0x79, 0x52, 0x05, 0x72, 0x65, 0x61, 0x64, 0x79, 0x1a, 0x44, 0x0a, 0x16, 0x43, 0x6f, 0x6e, 0x6e,
+	0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x45, 0x6e, 0x74,
+	0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20,
+	0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xe0,
+	0x01, 0x0a, 0x06, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x44, 0x0a, 0x08, 0x73, 0x65, 0x76,
+	0x65, 0x72, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x28, 0x2e, 0x61, 0x70,
+	0x69, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x66, 0x6e, 0x2e, 0x70,
+	0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x53, 0x65, 0x76,
+	0x65, 0x72, 0x69, 0x74, 0x79, 0x52, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x12,
+	0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x06, 0x72, 0x65, 0x61,
+	0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x06, 0x72, 0x65, 0x61,
+	0x73, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x43, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74,
+	0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x61, 0x70, 0x69, 0x65, 0x78, 0x74, 0x65,
+	0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x66, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e,
+	0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x01,
+	0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f,
+	0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65,
+	0x74, 0x22, 0xf2, 0x01, 0x0a, 0x09, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12,
+	0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74,
+	0x79, 0x70, 0x65, 0x12, 0x3e, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20,
+	0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x61, 0x70, 0x69, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69,
+	0x6f, 0x6e, 0x73, 0x2e, 0x66, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x31, 0x62,
+	0x65, 0x74, 0x61, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61,
+	0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x07, 0x6d,
+	0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x07,
+	0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x88, 0x01, 0x01, 0x12, 0x43, 0x0a, 0x06, 0x74, 0x61,
+	0x72, 0x67, 0x65, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x61, 0x70, 0x69,
+	0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x66, 0x6e, 0x2e, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x54, 0x61, 0x72, 0x67,
+	0x65, 0x74, 0x48, 0x01, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x88, 0x01, 0x01, 0x42,
+	0x0a, 0x0a, 0x08, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x09, 0x0a, 0x07, 0x5f,
+	0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2a, 0x3f, 0x0a, 0x05, 0x52, 0x65, 0x61, 0x64, 0x79, 0x12,
+	0x15, 0x0a, 0x11, 0x52, 0x45, 0x41, 0x44, 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49,
+	0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x52, 0x45, 0x41, 0x44, 0x59, 0x5f,
+	0x54, 0x52, 0x55, 0x45, 0x10, 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x52, 0x45, 0x41, 0x44, 0x59, 0x5f,
+	0x46, 0x41, 0x4c, 0x53, 0x45, 0x10, 0x02, 0x2a, 0x63, 0x0a, 0x08, 0x53, 0x65, 0x76, 0x65, 0x72,
+	0x69, 0x74, 0x79, 0x12, 0x18, 0x0a, 0x14, 0x53, 0x45, 0x56, 0x45, 0x52, 0x49, 0x54, 0x59, 0x5f,
+	0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x12, 0x0a,
+	0x0e, 0x53, 0x45, 0x56, 0x45, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x46, 0x41, 0x54, 0x41, 0x4c, 0x10,
+	0x01, 0x12, 0x14, 0x0a, 0x10, 0x53, 0x45, 0x56, 0x45, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x57, 0x41,
+	0x52, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x53, 0x45, 0x56, 0x45, 0x52,
+	0x49, 0x54, 0x59, 0x5f, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x10, 0x03, 0x2a, 0x56, 0x0a, 0x06,
+	0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x12, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54,
+	0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x14,
+	0x0a, 0x10, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x4f, 0x53, 0x49,
+	0x54, 0x45, 0x10, 0x01, 0x12, 0x1e, 0x0a, 0x1a, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x5f, 0x43,
+	0x4f, 0x4d, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x45, 0x5f, 0x41, 0x4e, 0x44, 0x5f, 0x43, 0x4c, 0x41,
+	0x49, 0x4d, 0x10, 0x02, 0x2a, 0x7f, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x20,
+	0x0a, 0x1c, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x43, 0x4f, 0x4e, 0x44, 0x49, 0x54, 0x49,
+	0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00,
+	0x12, 0x1c, 0x0a, 0x18, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x43, 0x4f, 0x4e, 0x44, 0x49,
+	0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x01, 0x12, 0x19,
+	0x0a, 0x15, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x43, 0x4f, 0x4e, 0x44, 0x49, 0x54, 0x49,
+	0x4f, 0x4e, 0x5f, 0x54, 0x52, 0x55, 0x45, 0x10, 0x02, 0x12, 0x1a, 0x0a, 0x16, 0x53, 0x54, 0x41,
+	0x54, 0x55, 0x53, 0x5f, 0x43, 0x4f, 0x4e, 0x44, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x46, 0x41,
+	0x4c, 0x53, 0x45, 0x10, 0x03, 0x32, 0x91, 0x01, 0x0a, 0x15, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69,
+	0x6f, 0x6e, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12,
+	0x78, 0x0a, 0x0b, 0x52, 0x75, 0x6e, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x32,
+	0x2e, 0x61, 0x70, 0x69, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x66,
+	0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e,
+	0x52, 0x75, 0x6e, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65,
+	0x73, 0x74, 0x1a, 0x33, 0x2e, 0x61, 0x70, 0x69, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f,
+	0x6e, 0x73, 0x2e, 0x66, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x31, 0x62, 0x65,
+	0x74, 0x61, 0x31, 0x2e, 0x52, 0x75, 0x6e, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52,
+	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x46, 0x5a, 0x44, 0x67, 0x69, 0x74,
+	0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x6f, 0x73, 0x73, 0x70, 0x6c, 0x61,
+	0x6e, 0x65, 0x2f, 0x63, 0x72, 0x6f, 0x73, 0x73, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x61, 0x70,
+	0x69, 0x73, 0x2f, 0x61, 0x70, 0x69, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73,
+	0x2f, 0x66, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61,
+	0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -1298,71 +1568,78 @@ func file_apiextensions_fn_proto_v1beta1_run_function_proto_rawDescGZIP() []byte
 	return file_apiextensions_fn_proto_v1beta1_run_function_proto_rawDescData
 }
 
-var file_apiextensions_fn_proto_v1beta1_run_function_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
-var file_apiextensions_fn_proto_v1beta1_run_function_proto_msgTypes = make([]protoimpl.MessageInfo, 20)
+var file_apiextensions_fn_proto_v1beta1_run_function_proto_enumTypes = make([]protoimpl.EnumInfo, 4)
+var file_apiextensions_fn_proto_v1beta1_run_function_proto_msgTypes = make([]protoimpl.MessageInfo, 21)
 var file_apiextensions_fn_proto_v1beta1_run_function_proto_goTypes = []interface{}{
 	(Ready)(0),                  // 0: apiextensions.fn.proto.v1beta1.Ready
 	(Severity)(0),               // 1: apiextensions.fn.proto.v1beta1.Severity
-	(*RunFunctionRequest)(nil),  // 2: apiextensions.fn.proto.v1beta1.RunFunctionRequest
-	(*Credentials)(nil),         // 3: apiextensions.fn.proto.v1beta1.Credentials
-	(*CredentialData)(nil),      // 4: apiextensions.fn.proto.v1beta1.CredentialData
-	(*Resources)(nil),           // 5: apiextensions.fn.proto.v1beta1.Resources
-	(*RunFunctionResponse)(nil), // 6: apiextensions.fn.proto.v1beta1.RunFunctionResponse
-	(*RequestMeta)(nil),         // 7: apiextensions.fn.proto.v1beta1.RequestMeta
-	(*Requirements)(nil),        // 8: apiextensions.fn.proto.v1beta1.Requirements
-	(*ResourceSelector)(nil),    // 9: apiextensions.fn.proto.v1beta1.ResourceSelector
-	(*MatchLabels)(nil),         // 10: apiextensions.fn.proto.v1beta1.MatchLabels
-	(*ResponseMeta)(nil),        // 11: apiextensions.fn.proto.v1beta1.ResponseMeta
-	(*State)(nil),               // 12: apiextensions.fn.proto.v1beta1.State
-	(*Resource)(nil),            // 13: apiextensions.fn.proto.v1beta1.Resource
-	(*Result)(nil),              // 14: apiextensions.fn.proto.v1beta1.Result
-	nil,                         // 15: apiextensions.fn.proto.v1beta1.RunFunctionRequest.ExtraResourcesEntry
-	nil,                         // 16: apiextensions.fn.proto.v1beta1.RunFunctionRequest.CredentialsEntry
-	nil,                         // 17: apiextensions.fn.proto.v1beta1.CredentialData.DataEntry
-	nil,                         // 18: apiextensions.fn.proto.v1beta1.Requirements.ExtraResourcesEntry
-	nil,                         // 19: apiextensions.fn.proto.v1beta1.MatchLabels.LabelsEntry
-	nil,                         // 20: apiextensions.fn.proto.v1beta1.State.ResourcesEntry
-	nil,                         // 21: apiextensions.fn.proto.v1beta1.Resource.ConnectionDetailsEntry
-	(*structpb.Struct)(nil),     // 22: google.protobuf.Struct
-	(*durationpb.Duration)(nil), // 23: google.protobuf.Duration
+	(Target)(0),                 // 2: apiextensions.fn.proto.v1beta1.Target
+	(Status)(0),                 // 3: apiextensions.fn.proto.v1beta1.Status
+	(*RunFunctionRequest)(nil),  // 4: apiextensions.fn.proto.v1beta1.RunFunctionRequest
+	(*Credentials)(nil),         // 5: apiextensions.fn.proto.v1beta1.Credentials
+	(*CredentialData)(nil),      // 6: apiextensions.fn.proto.v1beta1.CredentialData
+	(*Resources)(nil),           // 7: apiextensions.fn.proto.v1beta1.Resources
+	(*RunFunctionResponse)(nil), // 8: apiextensions.fn.proto.v1beta1.RunFunctionResponse
+	(*RequestMeta)(nil),         // 9: apiextensions.fn.proto.v1beta1.RequestMeta
+	(*Requirements)(nil),        // 10: apiextensions.fn.proto.v1beta1.Requirements
+	(*ResourceSelector)(nil),    // 11: apiextensions.fn.proto.v1beta1.ResourceSelector
+	(*MatchLabels)(nil),         // 12: apiextensions.fn.proto.v1beta1.MatchLabels
+	(*ResponseMeta)(nil),        // 13: apiextensions.fn.proto.v1beta1.ResponseMeta
+	(*State)(nil),               // 14: apiextensions.fn.proto.v1beta1.State
+	(*Resource)(nil),            // 15: apiextensions.fn.proto.v1beta1.Resource
+	(*Result)(nil),              // 16: apiextensions.fn.proto.v1beta1.Result
+	(*Condition)(nil),           // 17: apiextensions.fn.proto.v1beta1.Condition
+	nil,                         // 18: apiextensions.fn.proto.v1beta1.RunFunctionRequest.ExtraResourcesEntry
+	nil,                         // 19: apiextensions.fn.proto.v1beta1.RunFunctionRequest.CredentialsEntry
+	nil,                         // 20: apiextensions.fn.proto.v1beta1.CredentialData.DataEntry
+	nil,                         // 21: apiextensions.fn.proto.v1beta1.Requirements.ExtraResourcesEntry
+	nil,                         // 22: apiextensions.fn.proto.v1beta1.MatchLabels.LabelsEntry
+	nil,                         // 23: apiextensions.fn.proto.v1beta1.State.ResourcesEntry
+	nil,                         // 24: apiextensions.fn.proto.v1beta1.Resource.ConnectionDetailsEntry
+	(*structpb.Struct)(nil),     // 25: google.protobuf.Struct
+	(*durationpb.Duration)(nil), // 26: google.protobuf.Duration
 }
 var file_apiextensions_fn_proto_v1beta1_run_function_proto_depIdxs = []int32{
-	7,  // 0: apiextensions.fn.proto.v1beta1.RunFunctionRequest.meta:type_name -> apiextensions.fn.proto.v1beta1.RequestMeta
-	12, // 1: apiextensions.fn.proto.v1beta1.RunFunctionRequest.observed:type_name -> apiextensions.fn.proto.v1beta1.State
-	12, // 2: apiextensions.fn.proto.v1beta1.RunFunctionRequest.desired:type_name -> apiextensions.fn.proto.v1beta1.State
-	22, // 3: apiextensions.fn.proto.v1beta1.RunFunctionRequest.input:type_name -> google.protobuf.Struct
-	22, // 4: apiextensions.fn.proto.v1beta1.RunFunctionRequest.context:type_name -> google.protobuf.Struct
-	15, // 5: apiextensions.fn.proto.v1beta1.RunFunctionRequest.extra_resources:type_name -> apiextensions.fn.proto.v1beta1.RunFunctionRequest.ExtraResourcesEntry
-	16, // 6: apiextensions.fn.proto.v1beta1.RunFunctionRequest.credentials:type_name -> apiextensions.fn.proto.v1beta1.RunFunctionRequest.CredentialsEntry
-	4,  // 7: apiextensions.fn.proto.v1beta1.Credentials.credential_data:type_name -> apiextensions.fn.proto.v1beta1.CredentialData
-	17, // 8: apiextensions.fn.proto.v1beta1.CredentialData.data:type_name -> apiextensions.fn.proto.v1beta1.CredentialData.DataEntry
-	13, // 9: apiextensions.fn.proto.v1beta1.Resources.items:type_name -> apiextensions.fn.proto.v1beta1.Resource
-	11, // 10: apiextensions.fn.proto.v1beta1.RunFunctionResponse.meta:type_name -> apiextensions.fn.proto.v1beta1.ResponseMeta
-	12, // 11: apiextensions.fn.proto.v1beta1.RunFunctionResponse.desired:type_name -> apiextensions.fn.proto.v1beta1.State
-	14, // 12: apiextensions.fn.proto.v1beta1.RunFunctionResponse.results:type_name -> apiextensions.fn.proto.v1beta1.Result
-	22, // 13: apiextensions.fn.proto.v1beta1.RunFunctionResponse.context:type_name -> google.protobuf.Struct
-	8,  // 14: apiextensions.fn.proto.v1beta1.RunFunctionResponse.requirements:type_name -> apiextensions.fn.proto.v1beta1.Requirements
-	18, // 15: apiextensions.fn.proto.v1beta1.Requirements.extra_resources:type_name -> apiextensions.fn.proto.v1beta1.Requirements.ExtraResourcesEntry
-	10, // 16: apiextensions.fn.proto.v1beta1.ResourceSelector.match_labels:type_name -> apiextensions.fn.proto.v1beta1.MatchLabels
-	19, // 17: apiextensions.fn.proto.v1beta1.MatchLabels.labels:type_name -> apiextensions.fn.proto.v1beta1.MatchLabels.LabelsEntry
-	23, // 18: apiextensions.fn.proto.v1beta1.ResponseMeta.ttl:type_name -> google.protobuf.Duration
-	13, // 19: apiextensions.fn.proto.v1beta1.State.composite:type_name -> apiextensions.fn.proto.v1beta1.Resource
-	20, // 20: apiextensions.fn.proto.v1beta1.State.resources:type_name -> apiextensions.fn.proto.v1beta1.State.ResourcesEntry
-	22, // 21: apiextensions.fn.proto.v1beta1.Resource.resource:type_name -> google.protobuf.Struct
-	21, // 22: apiextensions.fn.proto.v1beta1.Resource.connection_details:type_name -> apiextensions.fn.proto.v1beta1.Resource.ConnectionDetailsEntry
-	0,  // 23: apiextensions.fn.proto.v1beta1.Resource.ready:type_name -> apiextensions.fn.proto.v1beta1.Ready
-	1,  // 24: apiextensions.fn.proto.v1beta1.Result.severity:type_name -> apiextensions.fn.proto.v1beta1.Severity
-	5,  // 25: apiextensions.fn.proto.v1beta1.RunFunctionRequest.ExtraResourcesEntry.value:type_name -> apiextensions.fn.proto.v1beta1.Resources
-	3,  // 26: apiextensions.fn.proto.v1beta1.RunFunctionRequest.CredentialsEntry.value:type_name -> apiextensions.fn.proto.v1beta1.Credentials
-	9,  // 27: apiextensions.fn.proto.v1beta1.Requirements.ExtraResourcesEntry.value:type_name -> apiextensions.fn.proto.v1beta1.ResourceSelector
-	13, // 28: apiextensions.fn.proto.v1beta1.State.ResourcesEntry.value:type_name -> apiextensions.fn.proto.v1beta1.Resource
-	2,  // 29: apiextensions.fn.proto.v1beta1.FunctionRunnerService.RunFunction:input_type -> apiextensions.fn.proto.v1beta1.RunFunctionRequest
-	6,  // 30: apiextensions.fn.proto.v1beta1.FunctionRunnerService.RunFunction:output_type -> apiextensions.fn.proto.v1beta1.RunFunctionResponse
-	30, // [30:31] is the sub-list for method output_type
-	29, // [29:30] is the sub-list for method input_type
-	29, // [29:29] is the sub-list for extension type_name
-	29, // [29:29] is the sub-list for extension extendee
-	0,  // [0:29] is the sub-list for field type_name
+	9,  // 0: apiextensions.fn.proto.v1beta1.RunFunctionRequest.meta:type_name -> apiextensions.fn.proto.v1beta1.RequestMeta
+	14, // 1: apiextensions.fn.proto.v1beta1.RunFunctionRequest.observed:type_name -> apiextensions.fn.proto.v1beta1.State
+	14, // 2: apiextensions.fn.proto.v1beta1.RunFunctionRequest.desired:type_name -> apiextensions.fn.proto.v1beta1.State
+	25, // 3: apiextensions.fn.proto.v1beta1.RunFunctionRequest.input:type_name -> google.protobuf.Struct
+	25, // 4: apiextensions.fn.proto.v1beta1.RunFunctionRequest.context:type_name -> google.protobuf.Struct
+	18, // 5: apiextensions.fn.proto.v1beta1.RunFunctionRequest.extra_resources:type_name -> apiextensions.fn.proto.v1beta1.RunFunctionRequest.ExtraResourcesEntry
+	19, // 6: apiextensions.fn.proto.v1beta1.RunFunctionRequest.credentials:type_name -> apiextensions.fn.proto.v1beta1.RunFunctionRequest.CredentialsEntry
+	6,  // 7: apiextensions.fn.proto.v1beta1.Credentials.credential_data:type_name -> apiextensions.fn.proto.v1beta1.CredentialData
+	20, // 8: apiextensions.fn.proto.v1beta1.CredentialData.data:type_name -> apiextensions.fn.proto.v1beta1.CredentialData.DataEntry
+	15, // 9: apiextensions.fn.proto.v1beta1.Resources.items:type_name -> apiextensions.fn.proto.v1beta1.Resource
+	13, // 10: apiextensions.fn.proto.v1beta1.RunFunctionResponse.meta:type_name -> apiextensions.fn.proto.v1beta1.ResponseMeta
+	14, // 11: apiextensions.fn.proto.v1beta1.RunFunctionResponse.desired:type_name -> apiextensions.fn.proto.v1beta1.State
+	16, // 12: apiextensions.fn.proto.v1beta1.RunFunctionResponse.results:type_name -> apiextensions.fn.proto.v1beta1.Result
+	25, // 13: apiextensions.fn.proto.v1beta1.RunFunctionResponse.context:type_name -> google.protobuf.Struct
+	10, // 14: apiextensions.fn.proto.v1beta1.RunFunctionResponse.requirements:type_name -> apiextensions.fn.proto.v1beta1.Requirements
+	17, // 15: apiextensions.fn.proto.v1beta1.RunFunctionResponse.conditions:type_name -> apiextensions.fn.proto.v1beta1.Condition
+	21, // 16: apiextensions.fn.proto.v1beta1.Requirements.extra_resources:type_name -> apiextensions.fn.proto.v1beta1.Requirements.ExtraResourcesEntry
+	12, // 17: apiextensions.fn.proto.v1beta1.ResourceSelector.match_labels:type_name -> apiextensions.fn.proto.v1beta1.MatchLabels
+	22, // 18: apiextensions.fn.proto.v1beta1.MatchLabels.labels:type_name -> apiextensions.fn.proto.v1beta1.MatchLabels.LabelsEntry
+	26, // 19: apiextensions.fn.proto.v1beta1.ResponseMeta.ttl:type_name -> google.protobuf.Duration
+	15, // 20: apiextensions.fn.proto.v1beta1.State.composite:type_name -> apiextensions.fn.proto.v1beta1.Resource
+	23, // 21: apiextensions.fn.proto.v1beta1.State.resources:type_name -> apiextensions.fn.proto.v1beta1.State.ResourcesEntry
+	25, // 22: apiextensions.fn.proto.v1beta1.Resource.resource:type_name -> google.protobuf.Struct
+	24, // 23: apiextensions.fn.proto.v1beta1.Resource.connection_details:type_name -> apiextensions.fn.proto.v1beta1.Resource.ConnectionDetailsEntry
+	0,  // 24: apiextensions.fn.proto.v1beta1.Resource.ready:type_name -> apiextensions.fn.proto.v1beta1.Ready
+	1,  // 25: apiextensions.fn.proto.v1beta1.Result.severity:type_name -> apiextensions.fn.proto.v1beta1.Severity
+	2,  // 26: apiextensions.fn.proto.v1beta1.Result.target:type_name -> apiextensions.fn.proto.v1beta1.Target
+	3,  // 27: apiextensions.fn.proto.v1beta1.Condition.status:type_name -> apiextensions.fn.proto.v1beta1.Status
+	2,  // 28: apiextensions.fn.proto.v1beta1.Condition.target:type_name -> apiextensions.fn.proto.v1beta1.Target
+	7,  // 29: apiextensions.fn.proto.v1beta1.RunFunctionRequest.ExtraResourcesEntry.value:type_name -> apiextensions.fn.proto.v1beta1.Resources
+	5,  // 30: apiextensions.fn.proto.v1beta1.RunFunctionRequest.CredentialsEntry.value:type_name -> apiextensions.fn.proto.v1beta1.Credentials
+	11, // 31: apiextensions.fn.proto.v1beta1.Requirements.ExtraResourcesEntry.value:type_name -> apiextensions.fn.proto.v1beta1.ResourceSelector
+	15, // 32: apiextensions.fn.proto.v1beta1.State.ResourcesEntry.value:type_name -> apiextensions.fn.proto.v1beta1.Resource
+	4,  // 33: apiextensions.fn.proto.v1beta1.FunctionRunnerService.RunFunction:input_type -> apiextensions.fn.proto.v1beta1.RunFunctionRequest
+	8,  // 34: apiextensions.fn.proto.v1beta1.FunctionRunnerService.RunFunction:output_type -> apiextensions.fn.proto.v1beta1.RunFunctionResponse
+	34, // [34:35] is the sub-list for method output_type
+	33, // [33:34] is the sub-list for method input_type
+	33, // [33:33] is the sub-list for extension type_name
+	33, // [33:33] is the sub-list for extension extendee
+	0,  // [0:33] is the sub-list for field type_name
 }
 
 func init() { file_apiextensions_fn_proto_v1beta1_run_function_proto_init() }
@@ -1527,6 +1804,18 @@ func file_apiextensions_fn_proto_v1beta1_run_function_proto_init() {
 				return nil
 			}
 		}
+		file_apiextensions_fn_proto_v1beta1_run_function_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Condition); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
 	}
 	file_apiextensions_fn_proto_v1beta1_run_function_proto_msgTypes[0].OneofWrappers = []interface{}{}
 	file_apiextensions_fn_proto_v1beta1_run_function_proto_msgTypes[1].OneofWrappers = []interface{}{
@@ -1538,13 +1827,15 @@ func file_apiextensions_fn_proto_v1beta1_run_function_proto_init() {
 		(*ResourceSelector_MatchLabels)(nil),
 	}
 	file_apiextensions_fn_proto_v1beta1_run_function_proto_msgTypes[9].OneofWrappers = []interface{}{}
+	file_apiextensions_fn_proto_v1beta1_run_function_proto_msgTypes[12].OneofWrappers = []interface{}{}
+	file_apiextensions_fn_proto_v1beta1_run_function_proto_msgTypes[13].OneofWrappers = []interface{}{}
 	type x struct{}
 	out := protoimpl.TypeBuilder{
 		File: protoimpl.DescBuilder{
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_apiextensions_fn_proto_v1beta1_run_function_proto_rawDesc,
-			NumEnums:      2,
-			NumMessages:   20,
+			NumEnums:      4,
+			NumMessages:   21,
 			NumExtensions: 0,
 			NumServices:   1,
 		},
diff --git a/apis/apiextensions/fn/proto/v1beta1/run_function.proto b/apis/apiextensions/fn/proto/v1beta1/run_function.proto
index 2a6e0b1ef..18060fb3c 100644
--- a/apis/apiextensions/fn/proto/v1beta1/run_function.proto
+++ b/apis/apiextensions/fn/proto/v1beta1/run_function.proto
@@ -121,6 +121,10 @@ message RunFunctionResponse {
 
   // Requirements that must be satisfied for this Function to run successfully.
   Requirements requirements = 5;
+
+  // Status conditions to be applied to the composite resource. Conditions may also
+  // optionally be applied to the composite resource's associated claim.
+  repeated Condition conditions = 6;
 }
 
 // RequestMeta contains metadata pertaining to a RunFunctionRequest.
@@ -243,6 +247,13 @@ message Result {
 
   // Human-readable details about the result.
   string message = 2;
+
+  // Optional PascalCase, machine-readable reason for this result. If omitted,
+  // the value will be ComposeResources.
+  optional string reason = 3;
+
+  // The resources this result targets.
+  optional Target target = 4;
 }
 
 // Severity of Function results.
@@ -262,4 +273,54 @@ enum Severity {
   // Normal results are emitted as normal events and debug logs associated
   // with the composite resource.
   SEVERITY_NORMAL = 3;
-}
\ No newline at end of file
+}
+
+// Target of Function results and conditions.
+enum Target {
+  // If the target is unspecified, the result targets the composite resource.
+  TARGET_UNSPECIFIED = 0;
+
+  // Target the composite resource. Results that target the composite resource
+  // should include detailed, advanced information.
+  TARGET_COMPOSITE = 1;
+
+  // Target the composite and the claim. Results that target the composite and
+  // the claim should include only end-user friendly information.
+  TARGET_COMPOSITE_AND_CLAIM = 2;
+}
+
+// Status condition to be applied to the composite resource. Condition may also
+// optionally be applied to the composite resource's associated claim. For
+// detailed information on proper usage of status conditions, please see
+// https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties.
+message Condition {
+  // Type of condition in PascalCase.
+  string type = 1;
+
+  // Status of the condition.
+  Status status = 2;
+
+  // Reason contains a programmatic identifier indicating the reason for the
+  // condition's last transition. Producers of specific condition types may
+  // define expected values and meanings for this field, and whether the values
+  // are considered a guaranteed API. The value should be a PascalCase string.
+  // This field may not be empty.
+  string reason = 3;
+
+  // Message is a human readable message indicating details about the
+  // transition. This may be an empty string.
+  optional string message = 4;
+
+  // The resources this condition targets.
+  optional Target target = 5;
+}
+
+enum Status {
+  STATUS_CONDITION_UNSPECIFIED = 0;
+
+  STATUS_CONDITION_UNKNOWN = 1;
+
+  STATUS_CONDITION_TRUE = 2;
+
+  STATUS_CONDITION_FALSE = 3;
+}
diff --git a/go.mod b/go.mod
index 9b2287b96..4a107f8dd 100644
--- a/go.mod
+++ b/go.mod
@@ -9,7 +9,7 @@ require (
 	github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24
 	github.com/Masterminds/semver v1.5.0
 	github.com/alecthomas/kong v0.9.0
-	github.com/crossplane/crossplane-runtime v1.17.0-rc.0.0.20240520203451-fc036618ffd8
+	github.com/crossplane/crossplane-runtime v1.17.0-rc.0.0.20240628014613-063a0273907b
 	github.com/docker/docker v25.0.5+incompatible
 	github.com/docker/go-connections v0.5.0
 	github.com/emicklei/dot v1.6.2
@@ -120,7 +120,7 @@ require (
 	github.com/aws/aws-sdk-go-v2/service/sso v1.12.10 // indirect
 	github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.10 // indirect
 	github.com/aws/aws-sdk-go-v2/service/sts v1.19.0 // indirect
-	github.com/aws/smithy-go v1.20.2
+	github.com/aws/smithy-go v1.20.2 // indirect
 	github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20230510185313-f5e39e5f34c7 // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/cespare/xxhash/v2 v2.2.0 // indirect
@@ -134,11 +134,11 @@ require (
 	github.com/docker/docker-credential-helpers v0.8.2
 	github.com/docker/go-units v0.5.0 // indirect
 	github.com/emicklei/go-restful/v3 v3.11.0 // indirect
-	github.com/evanphx/json-patch v5.6.0+incompatible // indirect
+	github.com/evanphx/json-patch v5.9.0+incompatible // indirect
 	github.com/evanphx/json-patch/v5 v5.9.0 // indirect
 	github.com/fatih/color v1.16.0 // indirect
 	github.com/fsnotify/fsnotify v1.7.0 // indirect
-	github.com/go-logr/logr v1.4.1 // indirect
+	github.com/go-logr/logr v1.4.2 // indirect
 	github.com/go-logr/stdr v1.2.2 // indirect
 	github.com/go-logr/zapr v1.3.0 // indirect
 	github.com/go-openapi/jsonpointer v0.19.6 // indirect
diff --git a/go.sum b/go.sum
index f3109f562..f5dd9f816 100644
--- a/go.sum
+++ b/go.sum
@@ -125,8 +125,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
 github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
-github.com/crossplane/crossplane-runtime v1.17.0-rc.0.0.20240520203451-fc036618ffd8 h1:4OtdWor2ixE1Nk+96//Knf63wRiyKFnxFcJx4iM+WY0=
-github.com/crossplane/crossplane-runtime v1.17.0-rc.0.0.20240520203451-fc036618ffd8/go.mod h1:aZ1ODIvtOPFFVZ9oo3qVH/MQHuim24qWg2Tj6n+AIf4=
+github.com/crossplane/crossplane-runtime v1.17.0-rc.0.0.20240628014613-063a0273907b h1:XNYG9Px6WsZ8OvfZ/hPDtnglK2jAmmkox1/JUZjXE9I=
+github.com/crossplane/crossplane-runtime v1.17.0-rc.0.0.20240628014613-063a0273907b/go.mod h1:saPoKGl3PfzzL8Q6PH+/cIjD0ssrmHW/gmiqstWy+0Q=
 github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
 github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
 github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0=
@@ -169,8 +169,8 @@ github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc
 github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
 github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U=
-github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
+github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls=
+github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
 github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4=
 github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg=
 github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ=
@@ -195,8 +195,8 @@ github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod
 github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys=
 github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY=
 github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
-github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
-github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
+github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
 github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
 github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
 github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
diff --git a/internal/controller/apiextensions/claim/reconciler.go b/internal/controller/apiextensions/claim/reconciler.go
index be4cc7d05..d94c9883a 100644
--- a/internal/controller/apiextensions/claim/reconciler.go
+++ b/internal/controller/apiextensions/claim/reconciler.go
@@ -473,6 +473,12 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 
 	cm.SetConditions(xpv1.ReconcileSuccess())
 
+	// Copy any custom status conditions from the XR to the claim.
+	for _, cType := range xr.GetClaimConditionTypes() {
+		c := xr.GetCondition(cType)
+		cm.SetConditions(c)
+	}
+
 	if !resource.IsConditionTrue(xr.GetCondition(xpv1.TypeReady)) {
 		record.Event(cm, event.Normal(reasonBind, "Composite resource is not yet ready"))
 
diff --git a/internal/controller/apiextensions/composite/composition_functions.go b/internal/controller/apiextensions/composite/composition_functions.go
index d3b214c65..d44210a04 100644
--- a/internal/controller/apiextensions/composite/composition_functions.go
+++ b/internal/controller/apiextensions/composite/composition_functions.go
@@ -34,6 +34,7 @@ import (
 	"k8s.io/apimachinery/pkg/util/json"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
+	xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
 	"github.com/crossplane/crossplane-runtime/pkg/errors"
 	"github.com/crossplane/crossplane-runtime/pkg/event"
 	"github.com/crossplane/crossplane-runtime/pkg/meta"
@@ -291,7 +292,8 @@ func (c *FunctionComposer) Compose(ctx context.Context, xr *composite.Unstructur
 	// The Function pipeline starts with empty desired state.
 	d := &v1beta1.State{}
 
-	events := []event.Event{}
+	events := []TargetedEvent{}
+	conditions := []TargetedCondition{}
 
 	// The Function context starts empty...
 	fctx := &structpb.Struct{Fields: map[string]*structpb.Value{}}
@@ -397,22 +399,58 @@ func (c *FunctionComposer) Compose(ctx context.Context, xr *composite.Unstructur
 		// We intentionally discard/ignore this after the last Function runs.
 		fctx = rsp.GetContext()
 
+		for _, c := range rsp.GetConditions() {
+			var status corev1.ConditionStatus
+			switch c.GetStatus() {
+			case v1beta1.Status_STATUS_CONDITION_TRUE:
+				status = corev1.ConditionTrue
+			case v1beta1.Status_STATUS_CONDITION_FALSE:
+				status = corev1.ConditionFalse
+			case v1beta1.Status_STATUS_CONDITION_UNKNOWN, v1beta1.Status_STATUS_CONDITION_UNSPECIFIED:
+				status = corev1.ConditionUnknown
+			}
+
+			conditions = append(conditions, TargetedCondition{
+				Condition: xpv1.Condition{
+					Type:               xpv1.ConditionType(c.GetType()),
+					Status:             status,
+					LastTransitionTime: metav1.Now(),
+					Reason:             xpv1.ConditionReason(c.GetReason()),
+					Message:            c.GetMessage(),
+				},
+				Target: convertTarget(c.GetTarget()),
+			})
+		}
+
 		// Results of fatal severity stop the Composition process. Other results
 		// are accumulated to be emitted as events by the Reconciler.
 		for _, rs := range rsp.GetResults() {
+			reason := event.Reason(rs.GetReason())
+			if reason == "" {
+				reason = reasonCompose
+			}
+
+			e := TargetedEvent{Target: convertTarget(rs.GetTarget())}
+
 			switch rs.GetSeverity() {
 			case v1beta1.Severity_SEVERITY_FATAL:
-				return CompositionResult{}, errors.Errorf(errFmtFatalResult, fn.Step, rs.GetMessage())
+				return CompositionResult{Events: events, Conditions: conditions}, errors.Errorf(errFmtFatalResult, fn.Step, rs.GetMessage())
 			case v1beta1.Severity_SEVERITY_WARNING:
-				events = append(events, event.Warning(reasonCompose, errors.Errorf("Pipeline step %q: %s", fn.Step, rs.GetMessage())))
+				e.Event = event.Warning(reason, errors.New(rs.GetMessage()))
+				e.Detail = fmt.Sprintf("Pipeline step %q", fn.Step)
 			case v1beta1.Severity_SEVERITY_NORMAL:
-				events = append(events, event.Normal(reasonCompose, fmt.Sprintf("Pipeline step %q: %s", fn.Step, rs.GetMessage())))
+				e.Event = event.Normal(reason, rs.GetMessage())
+				e.Detail = fmt.Sprintf("Pipeline step %q", fn.Step)
 			case v1beta1.Severity_SEVERITY_UNSPECIFIED:
 				// We could hit this case if a Function was built against a newer
 				// protobuf than this build of Crossplane, and the new protobuf
 				// introduced a severity that we don't know about.
-				events = append(events, event.Warning(reasonCompose, errors.Errorf("Pipeline step %q returned a result of unknown severity (assuming warning): %s", fn.Step, rs.GetMessage())))
+				e.Event = event.Warning(reason, errors.Errorf("Pipeline step %q returned a result of unknown severity (assuming warning): %s", fn.Step, rs.GetMessage()))
+				// Explicitly target only the XR, since we're including information
+				// about an exceptional, unexpected state.
+				e.Target = CompositionTargetComposite
 			}
+			events = append(events, e)
 		}
 	}
 
@@ -531,7 +569,10 @@ func (c *FunctionComposer) Compose(ctx context.Context, xr *composite.Unstructur
 				// We mark the resource as not synced, so that once we get to
 				// decide the XR's Synced condition, we can set it to false if
 				// any of the resources didn't sync successfully.
-				events = append(events, event.Warning(reasonCompose, errors.Wrapf(err, errFmtApplyCD, name)))
+				events = append(events, TargetedEvent{
+					Event:  event.Warning(reasonCompose, errors.Wrapf(err, errFmtApplyCD, name)),
+					Target: CompositionTargetComposite,
+				})
 				// NOTE(phisco): here we behave differently w.r.t. the native
 				// p&t composer, as we respect the readiness reported by
 				// functions, while there we defaulted to also set ready false
@@ -572,7 +613,7 @@ func (c *FunctionComposer) Compose(ctx context.Context, xr *composite.Unstructur
 		return CompositionResult{}, errors.Wrap(err, errApplyXRStatus)
 	}
 
-	return CompositionResult{ConnectionDetails: d.GetComposite().GetConnectionDetails(), Composed: resources, Events: events}, nil
+	return CompositionResult{ConnectionDetails: d.GetComposite().GetConnectionDetails(), Composed: resources, Events: events, Conditions: conditions}, nil
 }
 
 // ComposedFieldOwnerName generates a unique field owner name
@@ -935,3 +976,10 @@ func (u *PatchingManagedFieldsUpgrader) Upgrade(ctx context.Context, obj client.
 		return errors.Wrap(resource.IgnoreNotFound(u.client.Patch(ctx, obj, client.RawPatch(types.JSONPatchType, p))), "cannot clear field managers")
 	}
 }
+
+func convertTarget(t v1beta1.Target) CompositionTarget {
+	if t == v1beta1.Target_TARGET_COMPOSITE_AND_CLAIM {
+		return CompositionTargetCompositeAndClaim
+	}
+	return CompositionTargetComposite
+}
diff --git a/internal/controller/apiextensions/composite/composition_pt.go b/internal/controller/apiextensions/composite/composition_pt.go
index 45e8c7fcc..293952528 100644
--- a/internal/controller/apiextensions/composite/composition_pt.go
+++ b/internal/controller/apiextensions/composite/composition_pt.go
@@ -183,7 +183,7 @@ func (c *PTComposer) Compose(ctx context.Context, xr *composite.Unstructured, re
 		}
 	}
 
-	events := make([]event.Event, 0)
+	events := make([]TargetedEvent, 0)
 
 	// We optimistically render all composed resources that we are able to with
 	// the expectation that any that we fail to render will subsequently have
@@ -213,17 +213,26 @@ func (c *PTComposer) Compose(ctx context.Context, xr *composite.Unstructured, re
 
 		rendered := true
 		if err := RenderFromCompositeAndEnvironmentPatches(r, xr, req.Environment, ta.Template.Patches); err != nil {
-			events = append(events, event.Warning(reasonCompose, errors.Wrapf(err, errFmtRenderFromCompositePatches, name)))
+			events = append(events, TargetedEvent{
+				Event:  event.Warning(reasonCompose, errors.Wrapf(err, errFmtRenderFromCompositePatches, name)),
+				Target: CompositionTargetComposite,
+			})
 			rendered = false
 		}
 
 		if err := RenderComposedResourceMetadata(r, xr, ResourceName(ptr.Deref(ta.Template.Name, ""))); err != nil {
-			events = append(events, event.Warning(reasonCompose, errors.Wrapf(err, errFmtRenderMetadata, name)))
+			events = append(events, TargetedEvent{
+				Event:  event.Warning(reasonCompose, errors.Wrapf(err, errFmtRenderMetadata, name)),
+				Target: CompositionTargetComposite,
+			})
 			rendered = false
 		}
 
 		if err := c.composed.GenerateName(ctx, r); err != nil {
-			events = append(events, event.Warning(reasonCompose, errors.Wrapf(err, errFmtGenerateName, name)))
+			events = append(events, TargetedEvent{
+				Event:  event.Warning(reasonCompose, errors.Wrapf(err, errFmtGenerateName, name)),
+				Target: CompositionTargetComposite,
+			})
 			rendered = false
 		}
 
@@ -274,7 +283,10 @@ func (c *PTComposer) Compose(ctx context.Context, xr *composite.Unstructured, re
 				// run again the composition after some other resource is
 				// created or updated successfully. So, we emit a warning event
 				// and move on.
-				events = append(events, event.Warning(reasonCompose, errors.Wrapf(err, errFmtApplyComposed, ptr.Deref(t.Name, fmt.Sprintf("%d", i+1)))))
+				events = append(events, TargetedEvent{
+					Event:  event.Warning(reasonCompose, errors.Wrapf(err, errFmtApplyComposed, ptr.Deref(t.Name, fmt.Sprintf("%d", i+1)))),
+					Target: CompositionTargetComposite,
+				})
 				// We unset the cd here so that we don't try to observe it
 				// later. This will also mean we report it as not ready and not
 				// synced. Resulting in the XR being reported as not ready nor
diff --git a/internal/controller/apiextensions/composite/reconciler.go b/internal/controller/apiextensions/composite/reconciler.go
index 47c13396c..64d36b373 100644
--- a/internal/controller/apiextensions/composite/reconciler.go
+++ b/internal/controller/apiextensions/composite/reconciler.go
@@ -24,9 +24,11 @@ import (
 	"strconv"
 	"time"
 
+	corev1 "k8s.io/api/core/v1"
 	kerrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime/schema"
+	"k8s.io/apimachinery/pkg/types"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/handler"
 	"sigs.k8s.io/controller-runtime/pkg/reconcile"
@@ -38,6 +40,7 @@ import (
 	"github.com/crossplane/crossplane-runtime/pkg/meta"
 	"github.com/crossplane/crossplane-runtime/pkg/reconciler/managed"
 	"github.com/crossplane/crossplane-runtime/pkg/resource"
+	"github.com/crossplane/crossplane-runtime/pkg/resource/unstructured/claim"
 	"github.com/crossplane/crossplane-runtime/pkg/resource/unstructured/composed"
 	"github.com/crossplane/crossplane-runtime/pkg/resource/unstructured/composite"
 
@@ -72,6 +75,8 @@ const (
 	errInvalidResources       = "some resources were invalid, check events"
 	errRenderCD               = "cannot render composed resource"
 	errSyncResources          = "cannot sync composed resources"
+	errGetClaim               = "cannot get referenced claim"
+	errParseClaimRef          = "cannot parse claim reference"
 
 	reconcilePausedMsg = "Reconciliation (including deletion) is paused via the pause annotation"
 )
@@ -86,6 +91,11 @@ const (
 	reasonPaused  event.Reason = "ReconciliationPaused"
 )
 
+// Condition reasons.
+const (
+	reasonFatalError xpv1.ConditionReason = "FatalError"
+)
+
 // ControllerName returns the recommended name for controllers that use this
 // package to reconcile a particular kind of composite resource.
 func ControllerName(name string) string {
@@ -185,7 +195,48 @@ type CompositionRequest struct {
 type CompositionResult struct {
 	Composed          []ComposedResource
 	ConnectionDetails managed.ConnectionDetails
-	Events            []event.Event
+	Events            []TargetedEvent
+	Conditions        []TargetedCondition
+}
+
+// A CompositionTarget is the target of a composition event or condition.
+type CompositionTarget string
+
+// Composition event and condition targets.
+const (
+	CompositionTargetComposite         CompositionTarget = "Composite"
+	CompositionTargetCompositeAndClaim CompositionTarget = "CompositeAndClaim"
+)
+
+// A TargetedEvent represents an event produced by the composition process. It
+// can target either the XR only, or both the XR and the claim.
+type TargetedEvent struct {
+	event.Event
+	Target CompositionTarget
+	// Detail about the event to be included in the composite resource event but
+	// not the claim.
+	Detail string
+}
+
+// AsEvent produces the base event.
+func (e *TargetedEvent) AsEvent() event.Event {
+	return event.Event{Type: e.Type, Reason: e.Reason, Message: e.Message, Annotations: e.Annotations}
+}
+
+// AsDetailedEvent produces an event with additional detail if available.
+func (e *TargetedEvent) AsDetailedEvent() event.Event {
+	if e.Detail == "" {
+		return e.AsEvent()
+	}
+	msg := fmt.Sprintf("%s: %s", e.Detail, e.Message)
+	return event.Event{Type: e.Type, Reason: e.Reason, Message: msg, Annotations: e.Annotations}
+}
+
+// A TargetedCondition represents a condition produced by the composition
+// process. It can target either the XR only, or both the XR and the claim.
+type TargetedCondition struct {
+	xpv1.Condition
+	Target CompositionTarget
 }
 
 // A Composer composes (i.e. creates, updates, or deletes) resources given the
@@ -608,6 +659,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 		if kerrors.IsConflict(err) {
 			return reconcile.Result{Requeue: true}, nil
 		}
+
 		err = errors.Wrap(err, errCompose)
 		r.record.Event(xr, event.Warning(reasonCompose, err))
 		if kerrors.IsInvalid(err) {
@@ -621,6 +673,22 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 			err = errors.Wrap(errors.New(errInvalidResources), errCompose)
 		}
 		xr.SetConditions(xpv1.ReconcileError(err))
+
+		meta := r.handleCommonCompositionResult(ctx, res, xr)
+		// We encountered a fatal error. For any custom status conditions that were
+		// not received due to the fatal error, mark them as unknown.
+		for _, c := range xr.GetConditions() {
+			if xpv1.IsSystemConditionType(c.Type) {
+				continue
+			}
+			if !meta.conditionTypesSeen[c.Type] {
+				c.Status = corev1.ConditionUnknown
+				c.Reason = reasonFatalError
+				c.Message = "A fatal error occurred before the status of this condition could be determined."
+				xr.SetConditions(c)
+			}
+		}
+
 		return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, xr), errUpdateStatus)
 	}
 
@@ -654,16 +722,9 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 		r.record.Event(xr, event.Normal(reasonPublish, "Successfully published connection details"))
 	}
 
-	warnings := 0
-	for _, e := range res.Events {
-		if e.Type == event.TypeWarning {
-			warnings++
-		}
-		log.Debug(e.Message)
-		r.record.Event(xr, e)
-	}
+	meta := r.handleCommonCompositionResult(ctx, res, xr)
 
-	if warnings == 0 {
+	if meta.numWarningEvents == 0 {
 		// We don't consider warnings severe enough to prevent the XR from being
 		// considered synced (i.e. severe enough to return a ReconcileError) but
 		// they are severe enough that we probably shouldn't say we successfully
@@ -736,3 +797,74 @@ func getComposerResourcesNames(cds []ComposedResource) []string {
 	}
 	return names
 }
+
+type compositionResultMeta struct {
+	numWarningEvents   int
+	conditionTypesSeen map[xpv1.ConditionType]bool
+}
+
+func (r *Reconciler) handleCommonCompositionResult(ctx context.Context, res CompositionResult, xr *composite.Unstructured) compositionResultMeta {
+	log := r.log.WithValues(
+		"uid", xr.GetUID(),
+		"version", xr.GetResourceVersion(),
+		"name", xr.GetName(),
+	)
+
+	cm, err := getClaimFromXR(ctx, r.client, xr)
+	if err != nil {
+		log.Debug(errGetClaim, "error", err)
+	}
+
+	numWarningEvents := 0
+	for _, e := range res.Events {
+		if e.Event.Type == event.TypeWarning {
+			numWarningEvents++
+		}
+
+		detailedEvent := e.AsDetailedEvent()
+		log.Debug(detailedEvent.Message)
+		r.record.Event(xr, detailedEvent)
+
+		if e.Target == CompositionTargetCompositeAndClaim && cm != nil {
+			r.record.Event(cm, e.AsEvent())
+		}
+	}
+
+	conditionTypesSeen := make(map[xpv1.ConditionType]bool)
+	for _, c := range res.Conditions {
+		if xpv1.IsSystemConditionType(c.Condition.Type) {
+			// Do not let users update system conditions.
+			continue
+		}
+		conditionTypesSeen[c.Condition.Type] = true
+		xr.SetConditions(c.Condition)
+		if c.Target == CompositionTargetCompositeAndClaim {
+			// We can ignore the error as it only occurs if given a system condition.
+			_ = xr.SetClaimConditionTypes(c.Condition.Type)
+		}
+	}
+
+	return compositionResultMeta{
+		numWarningEvents:   numWarningEvents,
+		conditionTypesSeen: conditionTypesSeen,
+	}
+}
+
+func getClaimFromXR(ctx context.Context, c client.Client, xr *composite.Unstructured) (*claim.Unstructured, error) {
+	if xr.GetClaimReference() == nil {
+		return nil, nil
+	}
+
+	gv, err := schema.ParseGroupVersion(xr.GetClaimReference().APIVersion)
+	if err != nil {
+		return nil, errors.Wrap(err, errParseClaimRef)
+	}
+
+	claimGVK := gv.WithKind(xr.GetClaimReference().Kind)
+	cm := claim.New(claim.WithGroupVersionKind(claimGVK))
+	claimNN := types.NamespacedName{Namespace: xr.GetClaimReference().Namespace, Name: xr.GetClaimReference().Name}
+	if err := c.Get(ctx, claimNN, cm); err != nil {
+		return nil, errors.Wrap(err, errGetClaim)
+	}
+	return cm, nil
+}
diff --git a/internal/xcrd/schemas.go b/internal/xcrd/schemas.go
index d8fb00917..1d21b5cfd 100644
--- a/internal/xcrd/schemas.go
+++ b/internal/xcrd/schemas.go
@@ -356,6 +356,15 @@ func CompositeResourceStatusProps() map[string]extv1.JSONSchemaProps {
 				"lastPublishedTime": {Type: "string", Format: "date-time"},
 			},
 		},
+		"claimConditionTypes": {
+			Type:      "array",
+			XListType: ptr.To("set"),
+			Items: &extv1.JSONSchemaPropsOrArray{
+				Schema: &extv1.JSONSchemaProps{
+					Type: "string",
+				},
+			},
+		},
 	}
 }
 



Test patch:
diff --git a/cmd/crank/beta/validate/validate_test.go b/cmd/crank/beta/validate/validate_test.go
index 9b6511a39..cb252df13 100644
--- a/cmd/crank/beta/validate/validate_test.go
+++ b/cmd/crank/beta/validate/validate_test.go
@@ -526,6 +526,15 @@ func TestConvertToCRDs(t *testing.T) {
 																},
 															},
 														},
+														"claimConditionTypes": {
+															Type:      "array",
+															XListType: ptr.To("set"),
+															Items: &extv1.JSONSchemaPropsOrArray{
+																Schema: &extv1.JSONSchemaProps{
+																	Type: "string",
+																},
+															},
+														},
 														"connectionDetails": {
 															Type: "object",
 															Properties: map[string]extv1.JSONSchemaProps{
@@ -869,6 +878,15 @@ func TestConvertToCRDs(t *testing.T) {
 																},
 															},
 														},
+														"claimConditionTypes": {
+															Type:      "array",
+															XListType: ptr.To("set"),
+															Items: &extv1.JSONSchemaPropsOrArray{
+																Schema: &extv1.JSONSchemaProps{
+																	Type: "string",
+																},
+															},
+														},
 														"connectionDetails": {
 															Type: "object",
 															Properties: map[string]extv1.JSONSchemaProps{
@@ -1113,6 +1131,15 @@ func TestConvertToCRDs(t *testing.T) {
 																},
 															},
 														},
+														"claimConditionTypes": {
+															Type:      "array",
+															XListType: ptr.To("set"),
+															Items: &extv1.JSONSchemaPropsOrArray{
+																Schema: &extv1.JSONSchemaProps{
+																	Type: "string",
+																},
+															},
+														},
 														"connectionDetails": {
 															Type: "object",
 															Properties: map[string]extv1.JSONSchemaProps{
diff --git a/internal/controller/apiextensions/claim/reconciler_test.go b/internal/controller/apiextensions/claim/reconciler_test.go
index f8a1ff899..919bae6de 100644
--- a/internal/controller/apiextensions/claim/reconciler_test.go
+++ b/internal/controller/apiextensions/claim/reconciler_test.go
@@ -535,6 +535,93 @@ func TestReconcile(t *testing.T) {
 				r: reconcile.Result{Requeue: false},
 			},
 		},
+		"ClaimConditions": {
+			reason: "We should copy custom conditions from the XR if seen in the claimConditions array.",
+			args: args{
+				client: &test.MockClient{
+					MockGet: test.NewMockGetFn(nil, func(obj client.Object) error {
+						switch o := obj.(type) {
+						case *claim.Unstructured:
+							// We won't try to get an XR unless the claim
+							// references one.
+							o.SetResourceReference(&corev1.ObjectReference{Name: "cool-composite"})
+							// The system conditions are already set.
+							o.SetConditions(xpv1.ReconcileSuccess())
+							o.SetConditions(xpv1.Available())
+							// Database was marked as creating in a prior reconciliation.
+							o.SetConditions(xpv1.Condition{
+								Type:   "DatabaseReady",
+								Status: corev1.ConditionFalse,
+								Reason: "Creating",
+							})
+						case *composite.Unstructured:
+							// Pretend the XR exists and is available.
+							o.SetCreationTimestamp(now)
+							o.SetClaimReference(&claim.Reference{})
+							o.SetConditions(xpv1.Available())
+							o.SetConditions(
+								// Database has become ready.
+								xpv1.Condition{
+									Type:   "DatabaseReady",
+									Status: corev1.ConditionTrue,
+									Reason: "Available",
+								},
+								// Bucket is a new condition.
+								xpv1.Condition{
+									Type:   "BucketReady",
+									Status: corev1.ConditionFalse,
+									Reason: "Creating",
+								},
+								// Internal condition should not be copied over as it is not in
+								// claimConditions.
+								xpv1.Condition{
+									Type:   "InternalSync",
+									Status: corev1.ConditionFalse,
+									Reason: "Syncing",
+								},
+							)
+							// Database and Bucket are claim conditions so they should be
+							// copied over.
+							o.SetClaimConditionTypes("DatabaseReady", "BucketReady")
+						}
+						return nil
+					}),
+					MockStatusUpdate: WantClaim(t, NewClaim(func(cm *claim.Unstructured) {
+						// Check that we set our status condition.
+						cm.SetResourceReference(&corev1.ObjectReference{Name: "cool-composite"})
+						cm.SetConnectionDetailsLastPublishedTime(&now)
+						cm.SetConditions(xpv1.ReconcileSuccess())
+						cm.SetConditions(xpv1.Available())
+						cm.SetConditions(
+							// Database condition should have been updated to show ready.
+							xpv1.Condition{
+								Type:   "DatabaseReady",
+								Status: corev1.ConditionTrue,
+								Reason: "Available",
+							},
+							// Bucket condition should have been created.
+							xpv1.Condition{
+								Type:   "BucketReady",
+								Status: corev1.ConditionFalse,
+								Reason: "Creating",
+							},
+						)
+					})),
+				},
+				opts: []ReconcilerOption{
+					WithClaimFinalizer(resource.FinalizerFns{
+						AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil },
+					}),
+					WithCompositeSyncer(CompositeSyncerFn(func(_ context.Context, _ *claim.Unstructured, _ *composite.Unstructured) error { return nil })),
+					WithConnectionPropagator(ConnectionPropagatorFn(func(_ context.Context, _ resource.LocalConnectionSecretOwner, _ resource.ConnectionSecretOwner) (propagated bool, err error) {
+						return true, nil
+					})),
+				},
+			},
+			want: want{
+				r: reconcile.Result{Requeue: false},
+			},
+		},
 	}
 
 	for name, tc := range cases {
diff --git a/internal/controller/apiextensions/composite/composition_functions_test.go b/internal/controller/apiextensions/composite/composition_functions_test.go
index 062c71f4b..40aafeef1 100644
--- a/internal/controller/apiextensions/composite/composition_functions_test.go
+++ b/internal/controller/apiextensions/composite/composition_functions_test.go
@@ -229,14 +229,57 @@ func TestFunctionCompose(t *testing.T) {
 			},
 		},
 		"FatalFunctionResultError": {
-			reason: "We should return any fatal function results as an error",
+			reason: "We should return any fatal function results as an error. Any conditions returned by the function should be passed up. Any results returned by the function prior to the fatal result should be passed up.",
 			params: params{
 				r: FunctionRunnerFn(func(_ context.Context, _ string, _ *v1beta1.RunFunctionRequest) (rsp *v1beta1.RunFunctionResponse, err error) {
-					r := &v1beta1.Result{
-						Severity: v1beta1.Severity_SEVERITY_FATAL,
-						Message:  "oh no",
-					}
-					return &v1beta1.RunFunctionResponse{Results: []*v1beta1.Result{r}}, nil
+					return &v1beta1.RunFunctionResponse{
+						Results: []*v1beta1.Result{
+							// This result should be passed up as it was sent before the fatal
+							// result. The reason should be defaulted. The target should be
+							// defaulted.
+							{
+								Severity: v1beta1.Severity_SEVERITY_NORMAL,
+								Message:  "A result before the fatal result with the default Reason.",
+							},
+							// This result should be passed up as it was sent before the fatal
+							// result. The reason should be kept. The target should be kept.
+							{
+								Severity: v1beta1.Severity_SEVERITY_NORMAL,
+								Reason:   ptr.To("SomeReason"),
+								Message:  "A result before the fatal result with a specific Reason.",
+								Target:   v1beta1.Target_TARGET_COMPOSITE_AND_CLAIM.Enum(),
+							},
+							// The fatal result
+							{
+								Severity: v1beta1.Severity_SEVERITY_FATAL,
+								Message:  "oh no",
+							},
+							// This result should not be passed up as it was sent after the
+							// fatal result.
+							{
+								Severity: v1beta1.Severity_SEVERITY_NORMAL,
+								Message:  "a result after the fatal result",
+							},
+						},
+						Conditions: []*v1beta1.Condition{
+							// A condition returned by the function with only the minimum
+							// necessary values.
+							{
+								Type:   "DatabaseReady",
+								Status: v1beta1.Status_STATUS_CONDITION_FALSE,
+								Reason: "Creating",
+							},
+							// A condition returned by the function with all optional values
+							// given.
+							{
+								Type:    "DeploymentReady",
+								Status:  v1beta1.Status_STATUS_CONDITION_TRUE,
+								Reason:  "Available",
+								Message: ptr.To("The deployment is ready."),
+								Target:  v1beta1.Target_TARGET_COMPOSITE_AND_CLAIM.Enum(),
+							},
+						},
+					}, nil
 				}),
 				o: []FunctionComposerOption{
 					WithCompositeConnectionDetailsFetcher(ConnectionDetailsFetcherFn(func(_ context.Context, _ resource.ConnectionSecretOwner) (managed.ConnectionDetails, error) {
@@ -264,6 +307,51 @@ func TestFunctionCompose(t *testing.T) {
 			},
 			want: want{
 				err: errors.Errorf(errFmtFatalResult, "run-cool-function", "oh no"),
+				res: CompositionResult{
+					Events: []TargetedEvent{
+						// The event with minimum values.
+						{
+							Event: event.Event{
+								Type:    "Normal",
+								Reason:  "ComposeResources",
+								Message: "A result before the fatal result with the default Reason.",
+							},
+							Detail: "Pipeline step \"run-cool-function\"",
+							Target: CompositionTargetComposite,
+						},
+						// The event that provides all possible values.
+						{
+							Event: event.Event{
+								Type:    "Normal",
+								Reason:  "SomeReason",
+								Message: "A result before the fatal result with a specific Reason.",
+							},
+							Detail: "Pipeline step \"run-cool-function\"",
+							Target: CompositionTargetCompositeAndClaim,
+						},
+					},
+					Conditions: []TargetedCondition{
+						// The condition with minimum values.
+						{
+							Condition: xpv1.Condition{
+								Type:   "DatabaseReady",
+								Status: "False",
+								Reason: "Creating",
+							},
+							Target: CompositionTargetComposite,
+						},
+						// The condition that provides all possible values.
+						{
+							Condition: xpv1.Condition{
+								Type:    "DeploymentReady",
+								Status:  "True",
+								Reason:  "Available",
+								Message: "The deployment is ready.",
+							},
+							Target: CompositionTargetCompositeAndClaim,
+						},
+					},
+				},
 			},
 		},
 		"RenderComposedResourceMetadataError": {
@@ -620,6 +708,30 @@ func TestFunctionCompose(t *testing.T) {
 								Severity: v1beta1.Severity_SEVERITY_UNSPECIFIED,
 								Message:  "A result of unspecified severity",
 							},
+							{
+								Severity: v1beta1.Severity_SEVERITY_NORMAL,
+								Reason:   ptr.To("SomeReason"),
+								Message:  "A result with all values explicitly set.",
+								Target:   v1beta1.Target_TARGET_COMPOSITE_AND_CLAIM.Enum(),
+							},
+						},
+						Conditions: []*v1beta1.Condition{
+							// A condition returned by the function with only the minimum
+							// necessary values.
+							{
+								Type:   "DatabaseReady",
+								Status: v1beta1.Status_STATUS_CONDITION_FALSE,
+								Reason: "Creating",
+							},
+							// A condition returned by the function with all optional values
+							// given.
+							{
+								Type:    "DeploymentReady",
+								Status:  v1beta1.Status_STATUS_CONDITION_TRUE,
+								Reason:  "Available",
+								Message: ptr.To("The deployment is ready."),
+								Target:  v1beta1.Target_TARGET_COMPOSITE_AND_CLAIM.Enum(),
+							},
 						},
 					}
 					return rsp, nil
@@ -692,21 +804,62 @@ func TestFunctionCompose(t *testing.T) {
 					ConnectionDetails: managed.ConnectionDetails{
 						"from": []byte("function-pipeline"),
 					},
-					Events: []event.Event{
+					Events: []TargetedEvent{
+						{
+							Event: event.Event{
+								Type:    "Normal",
+								Reason:  "ComposeResources",
+								Message: "A normal result",
+							},
+							Detail: "Pipeline step \"run-cool-function\"",
+							Target: CompositionTargetComposite,
+						},
+						{
+							Event: event.Event{
+								Type:    "Warning",
+								Reason:  "ComposeResources",
+								Message: "A warning result",
+							},
+							Detail: "Pipeline step \"run-cool-function\"",
+							Target: CompositionTargetComposite,
+						},
 						{
-							Type:    "Normal",
-							Reason:  "ComposeResources",
-							Message: "Pipeline step \"run-cool-function\": A normal result",
+							Event: event.Event{
+								Type:    "Warning",
+								Reason:  "ComposeResources",
+								Message: "Pipeline step \"run-cool-function\" returned a result of unknown severity (assuming warning): A result of unspecified severity",
+							},
+							Target: CompositionTargetComposite,
+						},
+						{
+							Event: event.Event{
+								Type:    "Normal",
+								Reason:  "SomeReason",
+								Message: "A result with all values explicitly set.",
+							},
+							Detail: "Pipeline step \"run-cool-function\"",
+							Target: CompositionTargetCompositeAndClaim,
 						},
+					},
+					Conditions: []TargetedCondition{
+						// The condition with minimum values.
 						{
-							Type:    "Warning",
-							Reason:  "ComposeResources",
-							Message: "Pipeline step \"run-cool-function\": A warning result",
+							Condition: xpv1.Condition{
+								Type:   "DatabaseReady",
+								Status: "False",
+								Reason: "Creating",
+							},
+							Target: CompositionTargetComposite,
 						},
+						// The condition that provides all possible values.
 						{
-							Type:    "Warning",
-							Reason:  "ComposeResources",
-							Message: "Pipeline step \"run-cool-function\" returned a result of unknown severity (assuming warning): A result of unspecified severity",
+							Condition: xpv1.Condition{
+								Type:    "DeploymentReady",
+								Status:  "True",
+								Reason:  "Available",
+								Message: "The deployment is ready.",
+							},
+							Target: CompositionTargetCompositeAndClaim,
 						},
 					},
 				},
@@ -785,6 +938,13 @@ func TestFunctionCompose(t *testing.T) {
 									Severity: v1beta1.Severity_SEVERITY_UNSPECIFIED,
 									Message:  "A result of unspecified severity",
 								},
+								{
+									// If the severity is unknown, the target should be force set
+									// to target only the XR.
+									Severity: v1beta1.Severity_SEVERITY_UNSPECIFIED,
+									Message:  "A result of unspecified severity targeting the claim should be forced to only target the XR.",
+									Target:   v1beta1.Target_TARGET_COMPOSITE_AND_CLAIM.Enum(),
+								},
 							},
 							Requirements: requirements,
 						}
@@ -893,21 +1053,42 @@ func TestFunctionCompose(t *testing.T) {
 					ConnectionDetails: managed.ConnectionDetails{
 						"from": []byte("function-pipeline"),
 					},
-					Events: []event.Event{
+					Events: []TargetedEvent{
+						{
+							Event: event.Event{
+								Type:    "Normal",
+								Reason:  "ComposeResources",
+								Message: "A normal result",
+							},
+							Detail: "Pipeline step \"run-cool-function\"",
+							Target: CompositionTargetComposite,
+						},
 						{
-							Type:    "Normal",
-							Reason:  "ComposeResources",
-							Message: "Pipeline step \"run-cool-function\": A normal result",
+							Event: event.Event{
+								Type:    "Warning",
+								Reason:  "ComposeResources",
+								Message: "A warning result",
+							},
+							Detail: "Pipeline step \"run-cool-function\"",
+							Target: CompositionTargetComposite,
 						},
 						{
-							Type:    "Warning",
-							Reason:  "ComposeResources",
-							Message: "Pipeline step \"run-cool-function\": A warning result",
+							Event: event.Event{
+								Type:    "Warning",
+								Reason:  "ComposeResources",
+								Message: "Pipeline step \"run-cool-function\" returned a result of unknown severity (assuming warning): A result of unspecified severity",
+							},
+							Target: CompositionTargetComposite,
 						},
+						// If the severity is unknown, the target should be force set
+						// to target only the XR.
 						{
-							Type:    "Warning",
-							Reason:  "ComposeResources",
-							Message: "Pipeline step \"run-cool-function\" returned a result of unknown severity (assuming warning): A result of unspecified severity",
+							Event: event.Event{
+								Type:    "Warning",
+								Reason:  "ComposeResources",
+								Message: "Pipeline step \"run-cool-function\" returned a result of unknown severity (assuming warning): A result of unspecified severity targeting the claim should be forced to only target the XR.",
+							},
+							Target: CompositionTargetComposite,
 						},
 					},
 				},
diff --git a/internal/controller/apiextensions/composite/composition_pt_test.go b/internal/controller/apiextensions/composite/composition_pt_test.go
index efb8e3059..edc2fb29a 100644
--- a/internal/controller/apiextensions/composite/composition_pt_test.go
+++ b/internal/controller/apiextensions/composite/composition_pt_test.go
@@ -467,8 +467,11 @@ func TestPTCompose(t *testing.T) {
 						},
 					},
 					ConnectionDetails: details,
-					Events: []event.Event{
-						event.Warning(reasonCompose, errors.Wrapf(errBoom, errFmtGenerateName, "uncool-resource")),
+					Events: []TargetedEvent{
+						{
+							Event:  event.Warning(reasonCompose, errors.Wrapf(errBoom, errFmtGenerateName, "uncool-resource")),
+							Target: CompositionTargetComposite,
+						},
 					},
 				},
 			},
diff --git a/internal/controller/apiextensions/composite/reconciler_test.go b/internal/controller/apiextensions/composite/reconciler_test.go
index 6d68d15c1..4fa0dce53 100644
--- a/internal/controller/apiextensions/composite/reconciler_test.go
+++ b/internal/controller/apiextensions/composite/reconciler_test.go
@@ -18,6 +18,7 @@ package composite
 
 import (
 	"context"
+	"fmt"
 	"testing"
 	"time"
 
@@ -26,6 +27,7 @@ import (
 	corev1 "k8s.io/api/core/v1"
 	kerrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/runtime/schema"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/reconcile"
@@ -36,6 +38,7 @@ import (
 	"github.com/crossplane/crossplane-runtime/pkg/meta"
 	"github.com/crossplane/crossplane-runtime/pkg/reconciler/managed"
 	"github.com/crossplane/crossplane-runtime/pkg/resource"
+	"github.com/crossplane/crossplane-runtime/pkg/resource/unstructured/claim"
 	"github.com/crossplane/crossplane-runtime/pkg/resource/unstructured/composed"
 	"github.com/crossplane/crossplane-runtime/pkg/resource/unstructured/composite"
 	"github.com/crossplane/crossplane-runtime/pkg/test"
@@ -446,7 +449,12 @@ func TestReconcile(t *testing.T) {
 					})),
 					WithComposer(ComposerFn(func(_ context.Context, _ *composite.Unstructured, _ CompositionRequest) (CompositionResult, error) {
 						return CompositionResult{
-							Events: []event.Event{event.Warning("Warning", errBoom)},
+							Events: []TargetedEvent{
+								{
+									Event:  event.Warning("Warning", errBoom),
+									Target: CompositionTargetComposite,
+								},
+							},
 						}, nil
 					})),
 					WithConnectionPublishers(managed.ConnectionPublisherFns{
@@ -714,6 +722,588 @@ func TestReconcile(t *testing.T) {
 				r: reconcile.Result{RequeueAfter: defaultPollInterval},
 			},
 		},
+		"CustomEventsAndConditions": {
+			reason: "We should emit custom events and set custom conditions that were returned by the composer on both the composite resource and the claim.",
+			args: args{
+				client: &test.MockClient{
+					MockGet: test.NewMockGetFn(nil, func(obj client.Object) error {
+						if xr, ok := obj.(*composite.Unstructured); ok {
+							// non-nil claim ref to trigger claim Get()
+							xr.SetClaimReference(&claim.Reference{})
+							return nil
+						}
+						if cm, ok := obj.(*claim.Unstructured); ok {
+							claim.New(claim.WithGroupVersionKind(schema.GroupVersionKind{})).DeepCopyInto(cm)
+							return nil
+						}
+						return nil
+					}),
+					MockStatusUpdate: WantComposite(t, NewComposite(func(cr resource.Composite) {
+						cr.SetCompositionReference(&corev1.ObjectReference{})
+						cr.SetConditions(
+							xpv1.Condition{
+								Type:    "DatabaseReady",
+								Status:  corev1.ConditionTrue,
+								Reason:  "Available",
+								Message: "This is a condition for database availability.",
+							},
+							xpv1.Condition{
+								Type:               "InternalSync",
+								Status:             corev1.ConditionTrue,
+								LastTransitionTime: metav1.Time{},
+								Reason:             "SyncSuccess",
+								Message:            "This is a condition representing an internal sync process.",
+								ObservedGeneration: 0,
+							},
+							xpv1.ReconcileSuccess(),
+							xpv1.Available(),
+						)
+						cr.(*composite.Unstructured).SetClaimConditionTypes("DatabaseReady")
+						cr.SetClaimReference(&claim.Reference{})
+					})),
+				},
+				opts: []ReconcilerOption{
+					WithRecorder(newTestRecorder(
+						eventArgs{
+							Kind: compositeKind,
+							Event: event.Event{
+								Type:        event.Type(corev1.EventTypeNormal),
+								Reason:      "SelectComposition",
+								Message:     "Successfully selected composition: ",
+								Annotations: map[string]string{},
+							},
+						},
+						eventArgs{
+							Kind: compositeKind,
+							Event: event.Event{
+								Type:   event.TypeNormal,
+								Reason: "DatabaseAvailable",
+								// The composite should have the "Pipeline step" prefix.
+								Message:     "Pipeline step \"some-function\": This is an event for database availability.",
+								Annotations: map[string]string{},
+							},
+						},
+						eventArgs{
+							Kind: claimKind,
+							Event: event.Event{
+								Type:   event.TypeNormal,
+								Reason: "DatabaseAvailable",
+								// The claim should not have the "Pipeline step" prefix.
+								Message:     "This is an event for database availability.",
+								Annotations: map[string]string{},
+							},
+						},
+						eventArgs{
+							Kind: compositeKind,
+							Event: event.Event{
+								Type:        event.TypeNormal,
+								Reason:      "SyncSuccess",
+								Message:     "Pipeline step \"some-function\": Internal sync was successful.",
+								Annotations: map[string]string{},
+							},
+						},
+						eventArgs{
+							Kind: compositeKind,
+							Event: event.Event{
+								Type:        event.Type(corev1.EventTypeNormal),
+								Reason:      "ComposeResources",
+								Message:     "Successfully composed resources",
+								Annotations: map[string]string{},
+							},
+						},
+					)),
+					WithCompositeFinalizer(resource.NewNopFinalizer()),
+					WithCompositionSelector(CompositionSelectorFn(func(_ context.Context, cr resource.Composite) error {
+						cr.SetCompositionReference(&corev1.ObjectReference{})
+						return nil
+					})),
+					WithCompositionRevisionFetcher(CompositionRevisionFetcherFn(func(_ context.Context, _ resource.Composite) (*v1.CompositionRevision, error) {
+						return &v1.CompositionRevision{}, nil
+					})),
+					WithCompositionRevisionValidator(CompositionRevisionValidatorFn(func(_ *v1.CompositionRevision) error { return nil })),
+					WithConfigurator(ConfiguratorFn(func(_ context.Context, _ resource.Composite, _ *v1.CompositionRevision) error {
+						return nil
+					})),
+					WithComposer(ComposerFn(func(_ context.Context, _ *composite.Unstructured, _ CompositionRequest) (CompositionResult, error) {
+						return CompositionResult{
+							Composed:          []ComposedResource{},
+							ConnectionDetails: cd,
+							Events: []TargetedEvent{
+								{
+									Event: event.Event{
+										Type:        event.TypeNormal,
+										Reason:      "DatabaseAvailable",
+										Message:     "This is an event for database availability.",
+										Annotations: map[string]string{},
+									},
+									Detail: "Pipeline step \"some-function\"",
+									Target: CompositionTargetCompositeAndClaim,
+								},
+								{
+									Event: event.Event{
+										Type:        event.TypeNormal,
+										Reason:      "SyncSuccess",
+										Message:     "Internal sync was successful.",
+										Annotations: map[string]string{},
+									},
+									Detail: "Pipeline step \"some-function\"",
+									Target: CompositionTargetComposite,
+								},
+							},
+							Conditions: []TargetedCondition{
+								{
+									Condition: xpv1.Condition{
+										Type:    "DatabaseReady",
+										Status:  corev1.ConditionTrue,
+										Reason:  "Available",
+										Message: "This is a condition for database availability.",
+									},
+									Target: CompositionTargetCompositeAndClaim,
+								},
+								{
+									Condition: xpv1.Condition{
+										Type:               "InternalSync",
+										Status:             corev1.ConditionTrue,
+										LastTransitionTime: metav1.Time{},
+										Reason:             "SyncSuccess",
+										Message:            "This is a condition representing an internal sync process.",
+										ObservedGeneration: 0,
+									},
+									Target: CompositionTargetComposite,
+								},
+							},
+						}, nil
+					})),
+				},
+			},
+			want: want{
+				r: reconcile.Result{RequeueAfter: defaultPollInterval},
+			},
+		},
+		"CustomEventsAndConditionFatal": {
+			reason: "In the case of a fatal result from the composer, we should set all custom conditions that were seen. If any custom conditions were not seen, they should be marked as Unknown. The error message should be emitted as an event to the composite but not the claim.",
+			args: args{
+				client: &test.MockClient{
+					MockGet: test.NewMockGetFn(nil, func(obj client.Object) error {
+						if xr, ok := obj.(*composite.Unstructured); ok {
+							// non-nil claim ref to trigger claim Get()
+							xr.SetClaimReference(&claim.Reference{})
+							xr.SetConditions(xpv1.Condition{
+								Type:    "DatabaseReady",
+								Status:  corev1.ConditionTrue,
+								Reason:  "Available",
+								Message: "This is a condition for database availability.",
+							})
+							xr.SetClaimConditionTypes("DatabaseReady")
+							return nil
+						}
+						if cm, ok := obj.(*claim.Unstructured); ok {
+							claim.New(claim.WithGroupVersionKind(schema.GroupVersionKind{})).DeepCopyInto(cm)
+							return nil
+						}
+						return nil
+					}),
+					MockStatusUpdate: WantComposite(t, NewComposite(func(cr resource.Composite) {
+						cr.SetCompositionReference(&corev1.ObjectReference{})
+
+						cr.SetConditions(
+							xpv1.Condition{
+								Type:    "DatabaseReady",
+								Status:  corev1.ConditionUnknown,
+								Reason:  "FatalError",
+								Message: "A fatal error occurred before the status of this condition could be determined.",
+							},
+							xpv1.ReconcileError(fmt.Errorf("cannot compose resources: %w", errBoom)),
+							xpv1.Condition{
+								Type:               "InternalSync",
+								Status:             corev1.ConditionTrue,
+								LastTransitionTime: metav1.Time{},
+								Reason:             "SyncSuccess",
+								Message:            "This is a condition representing an internal sync process.",
+								ObservedGeneration: 0,
+							},
+							xpv1.Condition{
+								Type:               "BucketReady",
+								Status:             corev1.ConditionTrue,
+								LastTransitionTime: metav1.Time{},
+								Reason:             "Available",
+								Message:            "This is a condition for bucket availability.",
+								ObservedGeneration: 0,
+							},
+						)
+
+						cr.(*composite.Unstructured).SetClaimConditionTypes(
+							"DatabaseReady",
+							"BucketReady",
+						)
+						cr.SetClaimReference(&claim.Reference{})
+					})),
+				},
+				opts: []ReconcilerOption{
+					WithRecorder(newTestRecorder(
+						eventArgs{
+							Kind: compositeKind,
+							Event: event.Event{
+								Type:        event.Type(corev1.EventTypeNormal),
+								Reason:      "SelectComposition",
+								Message:     "Successfully selected composition: ",
+								Annotations: map[string]string{},
+							},
+						},
+						eventArgs{
+							Kind:  compositeKind,
+							Event: event.Warning("ComposeResources", fmt.Errorf("cannot compose resources: %w", errBoom)),
+						},
+						eventArgs{
+							Kind: compositeKind,
+							Event: event.Event{
+								Type:    event.TypeNormal,
+								Reason:  "DatabaseAvailable",
+								Message: "Pipeline step \"some-function\": This is an event for database availability.",
+							},
+						},
+						eventArgs{
+							Kind: claimKind,
+							Event: event.Event{
+								Type:   event.TypeNormal,
+								Reason: "DatabaseAvailable",
+								// The claim should not have the "Pipeline step" prefix.
+								Message: "This is an event for database availability.",
+							},
+						},
+						eventArgs{
+							Kind: compositeKind,
+							Event: event.Event{
+								Type:   event.TypeNormal,
+								Reason: "SyncSuccess",
+								// The composite should have the "Pipeline step" prefix.
+								Message:     "Pipeline step \"some-function\": Internal sync was successful.",
+								Annotations: map[string]string{},
+							},
+						},
+						eventArgs{
+							Kind: compositeKind,
+							Event: event.Event{
+								Type:   event.TypeNormal,
+								Reason: "EventNoDetail",
+								// The composite should not have the prefix as it had an empty
+								// detail.
+								Message:     "This event should not contain a detail prefix.",
+								Annotations: map[string]string{},
+							},
+						},
+					)),
+					WithCompositeFinalizer(resource.NewNopFinalizer()),
+					WithCompositionSelector(CompositionSelectorFn(func(_ context.Context, cr resource.Composite) error {
+						cr.SetCompositionReference(&corev1.ObjectReference{})
+						return nil
+					})),
+					WithCompositionRevisionFetcher(CompositionRevisionFetcherFn(func(_ context.Context, _ resource.Composite) (*v1.CompositionRevision, error) {
+						return &v1.CompositionRevision{}, nil
+					})),
+					WithCompositionRevisionValidator(CompositionRevisionValidatorFn(func(_ *v1.CompositionRevision) error { return nil })),
+					WithConfigurator(ConfiguratorFn(func(_ context.Context, _ resource.Composite, _ *v1.CompositionRevision) error {
+						return nil
+					})),
+					WithComposer(ComposerFn(func(_ context.Context, _ *composite.Unstructured, _ CompositionRequest) (CompositionResult, error) {
+						return CompositionResult{
+							Composed:          []ComposedResource{},
+							ConnectionDetails: cd,
+							Events: []TargetedEvent{
+								{
+									Event: event.Event{
+										Type:    event.TypeNormal,
+										Reason:  "DatabaseAvailable",
+										Message: "This is an event for database availability.",
+									},
+									Detail: "Pipeline step \"some-function\"",
+									Target: CompositionTargetCompositeAndClaim,
+								},
+								{
+									Event: event.Event{
+										Type:        event.TypeNormal,
+										Reason:      "SyncSuccess",
+										Message:     "Internal sync was successful.",
+										Annotations: map[string]string{},
+									},
+									Detail: "Pipeline step \"some-function\"",
+									Target: CompositionTargetComposite,
+								},
+								{
+									Event: event.Event{
+										Type:        event.TypeNormal,
+										Reason:      "EventNoDetail",
+										Message:     "This event should not contain a detail prefix.",
+										Annotations: map[string]string{},
+									},
+									Target: CompositionTargetComposite,
+								},
+							},
+							Conditions: []TargetedCondition{
+								{
+									Condition: xpv1.Condition{
+										Type:               "InternalSync",
+										Status:             corev1.ConditionTrue,
+										LastTransitionTime: metav1.Time{},
+										Reason:             "SyncSuccess",
+										Message:            "This is a condition representing an internal sync process.",
+										ObservedGeneration: 0,
+									},
+									Target: CompositionTargetComposite,
+								},
+								{
+									Condition: xpv1.Condition{
+										Type:               "BucketReady",
+										Status:             corev1.ConditionTrue,
+										LastTransitionTime: metav1.Time{},
+										Reason:             "Available",
+										Message:            "This is a condition for bucket availability.",
+										ObservedGeneration: 0,
+									},
+									Target: CompositionTargetCompositeAndClaim,
+								},
+							},
+						}, errBoom
+					})),
+				},
+			},
+			want: want{
+				r: reconcile.Result{Requeue: true},
+			},
+		},
+		"CustomConditionUpdate": {
+			reason: "Custom conditions should be updated if they already exist. Additionally, if a condition already exists in the status but was not included in the response, it should remain in the status.",
+			args: args{
+				client: &test.MockClient{
+					MockGet: test.NewMockGetFn(nil, func(obj client.Object) error {
+						if xr, ok := obj.(*composite.Unstructured); ok {
+							// non-nil claim ref to trigger claim Get()
+							xr.SetClaimReference(&claim.Reference{})
+							// The database condition already exists on the XR.
+							xr.SetConditions(xpv1.Condition{
+								Type:    "DatabaseReady",
+								Status:  corev1.ConditionTrue,
+								Reason:  "Available",
+								Message: "This is a condition for database availability.",
+							})
+							// The bucket began in a non-ready state.
+							xr.SetConditions(xpv1.Condition{
+								Type:    "BucketReady",
+								Status:  corev1.ConditionFalse,
+								Reason:  "Creating",
+								Message: "Waiting for bucket to be created.",
+							})
+
+							xr.SetClaimConditionTypes("DatabaseReady", "BucketReady")
+							return nil
+						}
+						if cm, ok := obj.(*claim.Unstructured); ok {
+							claim.New(claim.WithGroupVersionKind(schema.GroupVersionKind{})).DeepCopyInto(cm)
+							return nil
+						}
+						return nil
+					}),
+					MockStatusUpdate: WantComposite(t, NewComposite(func(cr resource.Composite) {
+						cr.SetCompositionReference(&corev1.ObjectReference{})
+						cr.SetConditions(
+							// The database condition should exist even though it was not seen
+							// during this reconcile.
+							xpv1.Condition{
+								Type:    "DatabaseReady",
+								Status:  corev1.ConditionTrue,
+								Reason:  "Available",
+								Message: "This is a condition for database availability.",
+							},
+							// The bucket condition should be updated to reflect the latest
+							// condition which is available.
+							xpv1.Condition{
+								Type:               "BucketReady",
+								Status:             corev1.ConditionTrue,
+								LastTransitionTime: metav1.Time{},
+								Reason:             "Available",
+								Message:            "This is a condition for bucket availability.",
+								ObservedGeneration: 0,
+							},
+							xpv1.Condition{
+								Type:               "InternalSync",
+								Status:             corev1.ConditionTrue,
+								LastTransitionTime: metav1.Time{},
+								Reason:             "SyncSuccess",
+								Message:            "This is a condition representing an internal sync process.",
+								ObservedGeneration: 0,
+							},
+							xpv1.ReconcileSuccess(),
+							xpv1.Available(),
+						)
+						cr.(*composite.Unstructured).SetClaimConditionTypes(
+							// The database claim condition should exist even though it was
+							// not seen during this reconcile.
+							"DatabaseReady",
+							"BucketReady",
+						)
+						cr.SetClaimReference(&claim.Reference{})
+					})),
+				},
+				opts: []ReconcilerOption{
+					WithRecorder(newTestRecorder(
+						eventArgs{
+							Kind: compositeKind,
+							Event: event.Event{
+								Type:        event.Type(corev1.EventTypeNormal),
+								Reason:      "SelectComposition",
+								Message:     "Successfully selected composition: ",
+								Annotations: map[string]string{},
+							},
+						},
+						eventArgs{
+							Kind: compositeKind,
+							Event: event.Event{
+								Type:        event.Type(corev1.EventTypeNormal),
+								Reason:      "ComposeResources",
+								Message:     "Successfully composed resources",
+								Annotations: map[string]string{},
+							},
+						},
+					)),
+					WithCompositeFinalizer(resource.NewNopFinalizer()),
+					WithCompositionSelector(CompositionSelectorFn(func(_ context.Context, cr resource.Composite) error {
+						cr.SetCompositionReference(&corev1.ObjectReference{})
+						return nil
+					})),
+					WithCompositionRevisionFetcher(CompositionRevisionFetcherFn(func(_ context.Context, _ resource.Composite) (*v1.CompositionRevision, error) {
+						return &v1.CompositionRevision{}, nil
+					})),
+					WithCompositionRevisionValidator(CompositionRevisionValidatorFn(func(_ *v1.CompositionRevision) error { return nil })),
+					WithConfigurator(ConfiguratorFn(func(_ context.Context, _ resource.Composite, _ *v1.CompositionRevision) error {
+						return nil
+					})),
+					WithComposer(ComposerFn(func(_ context.Context, _ *composite.Unstructured, _ CompositionRequest) (CompositionResult, error) {
+						return CompositionResult{
+							Composed:          []ComposedResource{},
+							ConnectionDetails: cd,
+							Events:            []TargetedEvent{},
+							Conditions: []TargetedCondition{
+								// The database condition is not added to the XR again.
+								{
+									Condition: xpv1.Condition{
+										Type:               "InternalSync",
+										Status:             corev1.ConditionTrue,
+										LastTransitionTime: metav1.Time{},
+										Reason:             "SyncSuccess",
+										Message:            "This is a condition representing an internal sync process.",
+										ObservedGeneration: 0,
+									},
+									Target: CompositionTargetComposite,
+								},
+								// The bucket is now ready.
+								{
+									Condition: xpv1.Condition{
+										Type:               "BucketReady",
+										Status:             corev1.ConditionTrue,
+										LastTransitionTime: metav1.Time{},
+										Reason:             "Available",
+										Message:            "This is a condition for bucket availability.",
+										ObservedGeneration: 0,
+									},
+									Target: CompositionTargetCompositeAndClaim,
+								},
+							},
+						}, nil
+					})),
+				},
+			},
+			want: want{
+				r: reconcile.Result{RequeueAfter: defaultPollInterval},
+			},
+		},
+		"CustomEventsFailToGetClaim": {
+			reason: "We should emit custom events that were returned by the composer. If we cannot get the claim, we should just emit events for the composite and continue as normal.",
+			args: args{
+				client: &test.MockClient{
+					MockGet: test.NewMockGetFn(nil, func(obj client.Object) error {
+						if xr, ok := obj.(*composite.Unstructured); ok {
+							// non-nil claim ref to trigger claim Get()
+							xr.SetClaimReference(&claim.Reference{})
+							return nil
+						}
+						if _, ok := obj.(*claim.Unstructured); ok {
+							// something went wrong when getting the claim
+							return errBoom
+						}
+						return nil
+					}),
+					MockStatusUpdate: WantComposite(t, NewComposite(func(cr resource.Composite) {
+						cr.SetCompositionReference(&corev1.ObjectReference{})
+						cr.SetConditions(xpv1.ReconcileSuccess(), xpv1.Available())
+						cr.SetClaimReference(&claim.Reference{})
+					})),
+				},
+				opts: []ReconcilerOption{
+					WithRecorder(newTestRecorder(
+						eventArgs{
+							Kind: compositeKind,
+							Event: event.Event{
+								Type:        event.Type(corev1.EventTypeNormal),
+								Reason:      "SelectComposition",
+								Message:     "Successfully selected composition: ",
+								Annotations: map[string]string{},
+							},
+						},
+						eventArgs{
+							Kind: compositeKind,
+							Event: event.Event{
+								Type:        event.TypeNormal,
+								Reason:      "DatabaseAvailable",
+								Message:     "Pipeline step \"some-function\": This is an event for database availability.",
+								Annotations: map[string]string{},
+							},
+						},
+						eventArgs{
+							Kind: compositeKind,
+							Event: event.Event{
+								Type:        event.Type(corev1.EventTypeNormal),
+								Reason:      "ComposeResources",
+								Message:     "Successfully composed resources",
+								Annotations: map[string]string{},
+							},
+						},
+					)),
+					WithCompositeFinalizer(resource.NewNopFinalizer()),
+					WithCompositionSelector(CompositionSelectorFn(func(_ context.Context, cr resource.Composite) error {
+						cr.SetCompositionReference(&corev1.ObjectReference{})
+						return nil
+					})),
+					WithCompositionRevisionFetcher(CompositionRevisionFetcherFn(func(_ context.Context, _ resource.Composite) (*v1.CompositionRevision, error) {
+						return &v1.CompositionRevision{}, nil
+					})),
+					WithCompositionRevisionValidator(CompositionRevisionValidatorFn(func(_ *v1.CompositionRevision) error { return nil })),
+					WithConfigurator(ConfiguratorFn(func(_ context.Context, _ resource.Composite, _ *v1.CompositionRevision) error {
+						return nil
+					})),
+					WithComposer(ComposerFn(func(_ context.Context, _ *composite.Unstructured, _ CompositionRequest) (CompositionResult, error) {
+						return CompositionResult{
+							Composed:          []ComposedResource{},
+							ConnectionDetails: cd,
+							Events: []TargetedEvent{
+								{
+									Event: event.Event{
+										Type:        event.TypeNormal,
+										Reason:      "DatabaseAvailable",
+										Message:     "This is an event for database availability.",
+										Annotations: map[string]string{},
+									},
+									Detail: "Pipeline step \"some-function\"",
+									Target: CompositionTargetCompositeAndClaim,
+								},
+							},
+						}, nil
+					})),
+				},
+			},
+			want: want{
+				r: reconcile.Result{RequeueAfter: defaultPollInterval},
+			},
+		},
 	}
 
 	for name, tc := range cases {
@@ -727,6 +1317,12 @@ func TestReconcile(t *testing.T) {
 			if diff := cmp.Diff(tc.want.r, got, test.EquateErrors()); diff != "" {
 				t.Errorf("\n%s\nr.Reconcile(...): -want, +got:\n%s", tc.reason, diff)
 			}
+
+			if tr, ok := r.record.(*testRecorder); ok {
+				if diff := cmp.Diff(tr.Want, tr.Got, test.EquateErrors()); diff != "" {
+					t.Errorf("\n%s\nr.Reconcile(...): -want events, +got events:\n%s", tc.reason, diff)
+				}
+			}
 		})
 	}
 }
@@ -830,3 +1426,41 @@ func TestFilterToXRPatches(t *testing.T) {
 		})
 	}
 }
+
+// Test types.
+const (
+	compositeKind = "Composite"
+	claimKind     = "Claim"
+)
+
+// testRecorder allows asserting event creation.
+type testRecorder struct {
+	Want []eventArgs
+	Got  []eventArgs
+}
+
+type eventArgs struct {
+	Kind  string
+	Event event.Event
+}
+
+func (r *testRecorder) Event(obj runtime.Object, e event.Event) {
+	var kind string
+	switch obj.(type) {
+	case *composite.Unstructured:
+		kind = compositeKind
+	case *claim.Unstructured:
+		kind = claimKind
+	}
+	r.Got = append(r.Got, eventArgs{Kind: kind, Event: e})
+}
+
+func (r *testRecorder) WithAnnotations(_ ...string) event.Recorder {
+	return r
+}
+
+func newTestRecorder(expected ...eventArgs) *testRecorder {
+	return &testRecorder{
+		Want: expected,
+	}
+}
diff --git a/internal/controller/pkg/revision/establisher_test.go b/internal/controller/pkg/revision/establisher_test.go
index e33485845..d550a896c 100644
--- a/internal/controller/pkg/revision/establisher_test.go
+++ b/internal/controller/pkg/revision/establisher_test.go
@@ -20,7 +20,6 @@ import (
 	"context"
 	"testing"
 
-	"github.com/aws/smithy-go/ptr"
 	"github.com/google/go-cmp/cmp"
 	"github.com/google/go-cmp/cmp/cmpopts"
 	admv1 "k8s.io/api/admissionregistration/v1"
@@ -32,6 +31,7 @@ import (
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/runtime/schema"
 	"k8s.io/apimachinery/pkg/types"
+	"k8s.io/utils/ptr"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
 	xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
@@ -697,7 +697,7 @@ func TestAPIEstablisherReleaseObjects(t *testing.T) {
 							for _, ref := range o.GetOwnerReferences() {
 								if ref.Kind == "ProviderRevision" && ref.UID == "some-unique-uid-2312" {
 									found = true
-									if ptr.ToBool(ref.Controller) {
+									if ptr.Deref(ref.Controller, false) {
 										t.Errorf("expected controller to be false, got %t", *ref.Controller)
 									}
 								}
diff --git a/internal/xcrd/crd_test.go b/internal/xcrd/crd_test.go
index 91323d1fc..60b6dd549 100644
--- a/internal/xcrd/crd_test.go
+++ b/internal/xcrd/crd_test.go
@@ -466,6 +466,15 @@ func TestForCompositeResource(t *testing.T) {
 														},
 													},
 												},
+												"claimConditionTypes": {
+													Type:      "array",
+													XListType: ptr.To("set"),
+													Items: &extv1.JSONSchemaPropsOrArray{
+														Schema: &extv1.JSONSchemaProps{
+															Type: "string",
+														},
+													},
+												},
 												"connectionDetails": {
 													Type: "object",
 													Properties: map[string]extv1.JSONSchemaProps{
@@ -783,6 +792,15 @@ func TestForCompositeResource(t *testing.T) {
 														},
 													},
 												},
+												"claimConditionTypes": {
+													Type:      "array",
+													XListType: ptr.To("set"),
+													Items: &extv1.JSONSchemaPropsOrArray{
+														Schema: &extv1.JSONSchemaProps{
+															Type: "string",
+														},
+													},
+												},
 												"connectionDetails": {
 													Type: "object",
 													Properties: map[string]extv1.JSONSchemaProps{
@@ -1050,6 +1068,15 @@ func TestForCompositeResource(t *testing.T) {
 														},
 													},
 												},
+												"claimConditionTypes": {
+													Type:      "array",
+													XListType: ptr.To("set"),
+													Items: &extv1.JSONSchemaPropsOrArray{
+														Schema: &extv1.JSONSchemaProps{
+															Type: "string",
+														},
+													},
+												},
 												"connectionDetails": {
 													Type: "object",
 													Properties: map[string]extv1.JSONSchemaProps{
@@ -1333,6 +1360,15 @@ func TestForCompositeResource(t *testing.T) {
 														},
 													},
 												},
+												"claimConditionTypes": {
+													Type:      "array",
+													XListType: ptr.To("set"),
+													Items: &extv1.JSONSchemaPropsOrArray{
+														Schema: &extv1.JSONSchemaProps{
+															Type: "string",
+														},
+													},
+												},
 												"connectionDetails": {
 													Type: "object",
 													Properties: map[string]extv1.JSONSchemaProps{
@@ -1626,6 +1662,15 @@ func TestForCompositeResource(t *testing.T) {
 														},
 													},
 												},
+												"claimConditionTypes": {
+													Type:      "array",
+													XListType: ptr.To("set"),
+													Items: &extv1.JSONSchemaPropsOrArray{
+														Schema: &extv1.JSONSchemaProps{
+															Type: "string",
+														},
+													},
+												},
 												"connectionDetails": {
 													Type: "object",
 													Properties: map[string]extv1.JSONSchemaProps{
@@ -2119,6 +2164,15 @@ func TestForCompositeResourceClaim(t *testing.T) {
 														},
 													},
 												},
+												"claimConditionTypes": {
+													Type:      "array",
+													XListType: ptr.To("set"),
+													Items: &extv1.JSONSchemaPropsOrArray{
+														Schema: &extv1.JSONSchemaProps{
+															Type: "string",
+														},
+													},
+												},
 												"connectionDetails": {
 													Type: "object",
 													Properties: map[string]extv1.JSONSchemaProps{
@@ -2405,6 +2459,15 @@ func TestForCompositeResourceClaim(t *testing.T) {
 														},
 													},
 												},
+												"claimConditionTypes": {
+													Type:      "array",
+													XListType: ptr.To("set"),
+													Items: &extv1.JSONSchemaPropsOrArray{
+														Schema: &extv1.JSONSchemaProps{
+															Type: "string",
+														},
+													},
+												},
 												"connectionDetails": {
 													Type: "object",
 													Properties: map[string]extv1.JSONSchemaProps{
@@ -2705,6 +2768,15 @@ func TestForCompositeResourceClaimEmptyXrd(t *testing.T) {
 												},
 											},
 										},
+										"claimConditionTypes": {
+											Type:      "array",
+											XListType: ptr.To("set"),
+											Items: &extv1.JSONSchemaPropsOrArray{
+												Schema: &extv1.JSONSchemaProps{
+													Type: "string",
+												},
+											},
+										},
 										"connectionDetails": {
 											Type: "object",
 											Properties: map[string]extv1.JSONSchemaProps{