Repository: crossplane/crossplane
Pull request #6442: Update core Crossplane controllers to use the new observed generation condition manager
Author: login: n3wscott
Created: 2025-05-09T17:00:06Z | Merged: 2025-05-27T20:21:03Z

### Description of your changes

This updates most reconcilers in crossplane to leverage the condition manager to set the Available condition at the end of reconciliation.

This also updates the helper method inside of the E2E test to report when we bring forward an observed generation on a condition to find where we still need to update conditions and reconcilers.

Fixes https://github.com/crossplane/crossplane/issues/6420

I have: <!--You MUST either [x] check or [ ] ~strike through~ every item.-->

- [x] Read and followed Crossplane's [contribution process].
- [x] Run `earthly +reviewable` to ensure this PR is ready for review.
- [x] Added or updated unit tests.
- [x] Added or updated e2e tests.
~- [ ] Linked a PR or a [docs tracking issue] to [document this change].~
~- [ ] Added `backport release-x.y` labels to auto-backport this PR.~
~- [ ] Followed the [API promotion workflow] if this PR introduces, removes, or promotes an API.~

Need help with this checklist? See the [cheat sheet].

[contribution process]: https://github.com/crossplane/crossplane/tree/main/contributing
[docs tracking issue]: https://github.com/crossplane/docs/issues/new
[document this change]: https://docs.crossplane.io/contribute/contribute
[cheat sheet]: https://github.com/crossplane/crossplane/tree/main/contributing#checklist-cheat-sheet
[API promotion workflow]: https://github.com/crossplane/crossplane/blob/main/contributing/guide-api-promotion.md


Diff:
diff --git a/apis/apiextensions/v1/xrd_types.go b/apis/apiextensions/v1/xrd_types.go
index 7135e1c0995..daa8f8ecaa9 100644
--- a/apis/apiextensions/v1/xrd_types.go
+++ b/apis/apiextensions/v1/xrd_types.go
@@ -230,6 +230,18 @@ type CompositeResourceDefinition struct {
 	Status CompositeResourceDefinitionStatus `json:"status,omitempty"`
 }
 
+// SetConditions delegates to Status.SetConditions.
+// Implements Conditioned.SetConditions.
+func (c *CompositeResourceDefinition) SetConditions(cs ...xpv1.Condition) {
+	c.Status.SetConditions(cs...)
+}
+
+// GetCondition delegates to Status.GetCondition.
+// Implements Conditioned.GetCondition.
+func (c *CompositeResourceDefinition) GetCondition(ct xpv1.ConditionType) xpv1.Condition {
+	return c.Status.GetCondition(ct)
+}
+
 // +kubebuilder:object:root=true
 
 // CompositeResourceDefinitionList contains a list of CompositeResourceDefinitions.
diff --git a/apis/apiextensions/v1alpha1/zz_generated.usage_types.go b/apis/apiextensions/v1alpha1/zz_generated.usage_types.go
index 27d4cc12a3a..10e84941df6 100644
--- a/apis/apiextensions/v1alpha1/zz_generated.usage_types.go
+++ b/apis/apiextensions/v1alpha1/zz_generated.usage_types.go
@@ -104,6 +104,18 @@ type Usage struct {
 	Status UsageStatus `json:"status,omitempty"`
 }
 
+// SetConditions delegates to Status.SetConditions.
+// Implements Conditioned.SetConditions.
+func (u *Usage) SetConditions(c ...xpv1.Condition) {
+	u.Status.SetConditions(c...)
+}
+
+// GetCondition delegates to Status.GetCondition.
+// Implements Conditioned.GetCondition.
+func (u *Usage) GetCondition(ct xpv1.ConditionType) xpv1.Condition {
+	return u.Status.GetCondition(ct)
+}
+
 // +kubebuilder:object:root=true
 
 // UsageList contains a list of Usage.
diff --git a/apis/apiextensions/v1beta1/usage_types.go b/apis/apiextensions/v1beta1/usage_types.go
index 3195be1c749..366e305b567 100644
--- a/apis/apiextensions/v1beta1/usage_types.go
+++ b/apis/apiextensions/v1beta1/usage_types.go
@@ -101,6 +101,18 @@ type Usage struct {
 	Status UsageStatus `json:"status,omitempty"`
 }
 
+// SetConditions delegates to Status.SetConditions.
+// Implements Conditioned.SetConditions.
+func (u *Usage) SetConditions(c ...xpv1.Condition) {
+	u.Status.SetConditions(c...)
+}
+
+// GetCondition delegates to Status.GetCondition.
+// Implements Conditioned.GetCondition.
+func (u *Usage) GetCondition(ct xpv1.ConditionType) xpv1.Condition {
+	return u.Status.GetCondition(ct)
+}
+
 // +kubebuilder:object:root=true
 
 // UsageList contains a list of Usage.
diff --git a/internal/controller/apiextensions/claim/reconciler.go b/internal/controller/apiextensions/claim/reconciler.go
index 29594af6a92..594a6251c86 100644
--- a/internal/controller/apiextensions/claim/reconciler.go
+++ b/internal/controller/apiextensions/claim/reconciler.go
@@ -31,6 +31,7 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/reconcile"
 
 	xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
+	"github.com/crossplane/crossplane-runtime/pkg/conditions"
 	"github.com/crossplane/crossplane-runtime/pkg/errors"
 	"github.com/crossplane/crossplane-runtime/pkg/event"
 	"github.com/crossplane/crossplane-runtime/pkg/logging"
@@ -188,8 +189,9 @@ type Reconciler struct {
 	composite crComposite
 	claim     crClaim
 
-	log    logging.Logger
-	record event.Recorder
+	log        logging.Logger
+	record     event.Recorder
+	conditions conditions.Manager
 }
 
 type crComposite struct {
@@ -289,6 +291,7 @@ func NewReconciler(c client.Client, of resource.CompositeClaimKind, with resourc
 		claim:         defaultCRClaim(c),
 		log:           logging.NewNopLogger(),
 		record:        event.NewNopRecorder(),
+		conditions:    conditions.ObservedGenerationPropagationManager{},
 	}
 
 	for _, ro := range o {
@@ -313,6 +316,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 		log.Debug(errGetClaim, "error", err)
 		return reconcile.Result{}, errors.Wrap(resource.IgnoreNotFound(err), errGetClaim)
 	}
+	status := r.conditions.For(cm)
 
 	record := r.record.WithAnnotations("external-name", meta.GetExternalName(cm))
 	log = log.WithValues(
@@ -325,7 +329,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 	// logging, publishing an event and updating the Synced status condition.
 	if meta.IsPaused(cm) {
 		r.record.Event(cm, event.Normal(reasonPaused, reconcilePausedMsg))
-		cm.SetConditions(xpv1.ReconcilePaused().WithMessage(reconcilePausedMsg))
+		status.MarkConditions(xpv1.ReconcilePaused().WithMessage(reconcilePausedMsg))
 		// If the pause annotation is removed, we will have a chance to
 		// reconcile again and resume and if status update fails, we will
 		// reconcile again to retry to update the status.
@@ -340,7 +344,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 		if err := r.client.Get(ctx, types.NamespacedName{Name: ref.Name}, xr); resource.IgnoreNotFound(err) != nil {
 			err = errors.Wrap(err, errGetComposite)
 			record.Event(cm, event.Warning(reasonBind, err))
-			cm.SetConditions(xpv1.ReconcileError(err))
+			status.MarkConditions(xpv1.ReconcileError(err))
 			return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, cm), errUpdateClaimStatus)
 		}
 	}
@@ -359,7 +363,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 	if ref := xr.GetClaimReference(); meta.WasCreated(xr) && ref != nil && !cmp.Equal(cm.GetReference(), ref) {
 		err := errors.Errorf(errFmtUnbound, xr.GetName(), ref.Name)
 		record.Event(cm, event.Warning(reasonBind, err))
-		cm.SetConditions(xpv1.ReconcileError(err))
+		status.MarkConditions(xpv1.ReconcileError(err))
 		return reconcile.Result{Requeue: false}, errors.Wrap(r.client.Status().Update(ctx, cm), errUpdateClaimStatus)
 	}
 
@@ -374,14 +378,14 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 		}
 		err = errors.Wrap(err, errUpgradeManagedFields)
 		record.Event(cm, event.Warning(reasonBind, err))
-		cm.SetConditions(xpv1.ReconcileError(err))
+		status.MarkConditions(xpv1.ReconcileError(err))
 		return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, cm), errUpdateClaimStatus)
 	}
 
 	if meta.WasDeleted(cm) {
 		log = log.WithValues("deletion-timestamp", cm.GetDeletionTimestamp())
 
-		cm.SetConditions(xpv1.Deleting())
+		status.MarkConditions(xpv1.Deleting())
 		if meta.WasCreated(xr) {
 			requiresForegroundDeletion := false
 			if cdp := cm.GetCompositeDeletePolicy(); cdp != nil && *cdp == xpv1.CompositeDeleteForeground {
@@ -398,7 +402,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 			if err := r.client.Delete(ctx, xr, do); resource.IgnoreNotFound(err) != nil {
 				err = errors.Wrap(err, errDeleteComposite)
 				record.Event(cm, event.Warning(reasonDelete, err))
-				cm.SetConditions(xpv1.ReconcileError(err))
+				status.MarkConditions(xpv1.ReconcileError(err))
 				return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, cm), errUpdateClaimStatus)
 			}
 			if requiresForegroundDeletion {
@@ -413,7 +417,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 		if err := r.claim.UnpublishConnection(ctx, cm, nil); err != nil {
 			err = errors.Wrap(err, errDeleteCDs)
 			record.Event(cm, event.Warning(reasonDelete, err))
-			cm.SetConditions(xpv1.ReconcileError(err))
+			status.MarkConditions(xpv1.ReconcileError(err))
 			return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, cm), errUpdateClaimStatus)
 		}
 
@@ -422,12 +426,12 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 		if err := r.claim.RemoveFinalizer(ctx, cm); err != nil {
 			err = errors.Wrap(err, errRemoveFinalizer)
 			record.Event(cm, event.Warning(reasonDelete, err))
-			cm.SetConditions(xpv1.ReconcileError(err))
+			status.MarkConditions(xpv1.ReconcileError(err))
 			return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, cm), errUpdateClaimStatus)
 		}
 
 		log.Debug("Successfully deleted claim")
-		cm.SetConditions(xpv1.ReconcileSuccess())
+		status.MarkConditions(xpv1.ReconcileSuccess())
 		return reconcile.Result{Requeue: false}, errors.Wrap(r.client.Status().Update(ctx, cm), errUpdateClaimStatus)
 	}
 
@@ -437,7 +441,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 		}
 		err = errors.Wrap(err, errAddFinalizer)
 		record.Event(cm, event.Warning(reasonBind, err))
-		cm.SetConditions(xpv1.ReconcileError(err))
+		status.MarkConditions(xpv1.ReconcileError(err))
 		return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, cm), errUpdateClaimStatus)
 	}
 
@@ -451,7 +455,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 		}
 		err = errors.Wrap(err, errSync)
 		record.Event(cm, event.Warning(reasonBind, err))
-		cm.SetConditions(xpv1.ReconcileError(err))
+		status.MarkConditions(xpv1.ReconcileError(err))
 		return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, cm), errUpdateClaimStatus)
 	}
 
@@ -460,12 +464,12 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 		record.Event(cm, event.Normal(reasonBind, "Successfully bound composite resource"))
 	}
 
-	cm.SetConditions(xpv1.ReconcileSuccess())
+	status.MarkConditions(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)
+		status.MarkConditions(c)
 	}
 
 	if !resource.IsConditionTrue(xr.GetCondition(xpv1.TypeReady)) {
@@ -473,7 +477,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 
 		// We should be watching the composite resource and will have a
 		// request queued if it changes, so no need to requeue.
-		cm.SetConditions(Waiting())
+		status.MarkConditions(Waiting())
 		return reconcile.Result{}, errors.Wrap(r.client.Status().Update(ctx, cm), errUpdateClaimStatus)
 	}
 
@@ -481,7 +485,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 	if err != nil {
 		err = errors.Wrap(err, errPropagateCDs)
 		record.Event(cm, event.Warning(reasonPropagate, err))
-		cm.SetConditions(xpv1.ReconcileError(err))
+		status.MarkConditions(xpv1.ReconcileError(err))
 		return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, cm), errUpdateClaimStatus)
 	}
 	if propagated {
@@ -491,7 +495,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 
 	// We have a watch on both the claim and its composite, so there's no
 	// need to requeue here.
-	cm.SetConditions(xpv1.Available())
+	status.MarkConditions(xpv1.Available())
 	return reconcile.Result{Requeue: false}, errors.Wrap(r.client.Status().Update(ctx, cm), errUpdateClaimStatus)
 }
 
diff --git a/internal/controller/apiextensions/composite/reconciler.go b/internal/controller/apiextensions/composite/reconciler.go
index 8aba73c6167..4d934fcb45e 100644
--- a/internal/controller/apiextensions/composite/reconciler.go
+++ b/internal/controller/apiextensions/composite/reconciler.go
@@ -34,6 +34,7 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/reconcile"
 
 	xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
+	"github.com/crossplane/crossplane-runtime/pkg/conditions"
 	"github.com/crossplane/crossplane-runtime/pkg/errors"
 	"github.com/crossplane/crossplane-runtime/pkg/event"
 	"github.com/crossplane/crossplane-runtime/pkg/feature"
@@ -425,8 +426,9 @@ func NewReconciler(c, uc client.Client, of resource.CompositeKind, opts ...Recon
 		// Dynamic watches are disabled by default.
 		engine: &NopWatchStarter{},
 
-		log:    logging.NewNopLogger(),
-		record: event.NewNopRecorder(),
+		log:        logging.NewNopLogger(),
+		record:     event.NewNopRecorder(),
+		conditions: conditions.ObservedGenerationPropagationManager{},
 	}
 
 	for _, f := range opts {
@@ -453,8 +455,9 @@ type Reconciler struct {
 	engine         WatchStarter
 	watchHandler   handler.EventHandler
 
-	log    logging.Logger
-	record event.Recorder
+	log        logging.Logger
+	record     event.Recorder
+	conditions conditions.Manager
 
 	pollInterval time.Duration
 }
@@ -472,6 +475,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 		log.Debug(errGet, "error", err)
 		return reconcile.Result{}, errors.Wrap(resource.IgnoreNotFound(err), errGet)
 	}
+	status := r.conditions.For(xr)
 
 	log = log.WithValues(
 		"uid", xr.GetUID(),
@@ -483,7 +487,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 	// after logging, publishing an event and updating the SYNC status condition
 	if meta.IsPaused(xr) {
 		r.record.Event(xr, event.Normal(reasonPaused, "Reconciliation is paused via the pause annotation"))
-		xr.SetConditions(xpv1.ReconcilePaused().WithMessage(reconcilePausedMsg))
+		status.MarkConditions(xpv1.ReconcilePaused().WithMessage(reconcilePausedMsg))
 		// If the pause annotation is removed, we will have a chance to reconcile again and resume
 		// and if status update fails, we will reconcile again to retry to update the status
 		return reconcile.Result{}, errors.Wrap(r.client.Status().Update(ctx, xr), errUpdateStatus)
@@ -492,11 +496,11 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 	if meta.WasDeleted(xr) {
 		log = log.WithValues("deletion-timestamp", xr.GetDeletionTimestamp())
 
-		xr.SetConditions(xpv1.Deleting())
+		status.MarkConditions(xpv1.Deleting())
 		if err := r.composite.UnpublishConnection(ctx, xr, nil); err != nil {
 			err = errors.Wrap(err, errUnpublish)
 			r.record.Event(xr, event.Warning(reasonDelete, err))
-			xr.SetConditions(xpv1.ReconcileError(err))
+			status.MarkConditions(xpv1.ReconcileError(err))
 			return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, xr), errUpdateStatus)
 		}
 
@@ -506,12 +510,12 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 			}
 			err = errors.Wrap(err, errRemoveFinalizer)
 			r.record.Event(xr, event.Warning(reasonDelete, err))
-			xr.SetConditions(xpv1.ReconcileError(err))
+			status.MarkConditions(xpv1.ReconcileError(err))
 			return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, xr), errUpdateStatus)
 		}
 
 		log.Debug("Successfully deleted composite resource")
-		xr.SetConditions(xpv1.ReconcileSuccess())
+		status.MarkConditions(xpv1.ReconcileSuccess())
 		return reconcile.Result{Requeue: false}, errors.Wrap(r.client.Status().Update(ctx, xr), errUpdateStatus)
 	}
 
@@ -521,7 +525,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 		}
 		err = errors.Wrap(err, errAddFinalizer)
 		r.record.Event(xr, event.Warning(reasonInit, err))
-		xr.SetConditions(xpv1.ReconcileError(err))
+		status.MarkConditions(xpv1.ReconcileError(err))
 		return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, xr), errUpdateStatus)
 	}
 
@@ -532,7 +536,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 		}
 		err = errors.Wrap(err, errSelectComp)
 		r.record.Event(xr, event.Warning(reasonResolve, err))
-		xr.SetConditions(xpv1.ReconcileError(err))
+		status.MarkConditions(xpv1.ReconcileError(err))
 		return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, xr), errUpdateStatus)
 	}
 	if compRef := xr.GetCompositionReference(); compRef != nil && (orig == nil || *compRef != *orig) {
@@ -549,7 +553,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 		log.Debug(errFetchComp, "error", err)
 		err = errors.Wrap(err, errFetchComp)
 		r.record.Event(xr, event.Warning(reasonCompose, err))
-		xr.SetConditions(xpv1.ReconcileError(err))
+		status.MarkConditions(xpv1.ReconcileError(err))
 		return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, xr), errUpdateStatus)
 	}
 	if rev := xr.GetCompositionRevisionReference(); rev != nil && (origRev == nil || *rev != *origRev) {
@@ -560,7 +564,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 		log.Debug(errValidate, "error", err)
 		err = errors.Wrap(err, errValidate)
 		r.record.Event(xr, event.Warning(reasonCompose, err))
-		xr.SetConditions(xpv1.ReconcileError(err))
+		status.MarkConditions(xpv1.ReconcileError(err))
 		return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, xr), errUpdateStatus)
 	}
 
@@ -571,7 +575,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 		}
 		err = errors.Wrap(err, errConfigure)
 		r.record.Event(xr, event.Warning(reasonCompose, err))
-		xr.SetConditions(xpv1.ReconcileError(err))
+		status.MarkConditions(xpv1.ReconcileError(err))
 		return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, xr), errUpdateStatus)
 	}
 
@@ -594,7 +598,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 			// point to the event.
 			err = errors.Wrap(errors.New(errInvalidResources), errCompose)
 		}
-		xr.SetConditions(xpv1.ReconcileError(err))
+		status.MarkConditions(xpv1.ReconcileError(err))
 
 		meta := r.handleCommonCompositionResult(ctx, res, xr)
 		// We encountered a fatal error. For any custom status conditions that were
@@ -607,7 +611,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 				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)
+				status.MarkConditions(c)
 			}
 		}
 
@@ -624,7 +628,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 	if err := r.engine.StartWatches(ctx, r.controllerName, ws...); err != nil {
 		err = errors.Wrap(err, errWatch)
 		r.record.Event(xr, event.Warning(reasonWatch, err))
-		xr.SetConditions(xpv1.ReconcileError(err))
+		status.MarkConditions(xpv1.ReconcileError(err))
 		return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, xr), errUpdateStatus)
 	}
 
@@ -636,7 +640,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 		}
 		err = errors.Wrap(err, errPublish)
 		r.record.Event(xr, event.Warning(reasonPublish, err))
-		xr.SetConditions(xpv1.ReconcileError(err))
+		status.MarkConditions(xpv1.ReconcileError(err))
 		return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, xr), errUpdateStatus)
 	}
 	if published {
@@ -697,7 +701,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 		}
 	}
 
-	xr.SetConditions(synced, ready)
+	status.MarkConditions(synced, ready)
 
 	// Requeue after the configured poll interval by default. If realtime
 	// compositions is enabled this'll be RequeueAfter: 0, i.e. no requeue.
diff --git a/internal/controller/apiextensions/definition/reconciler.go b/internal/controller/apiextensions/definition/reconciler.go
index e41a5fd3b4d..28fb23646ac 100644
--- a/internal/controller/apiextensions/definition/reconciler.go
+++ b/internal/controller/apiextensions/definition/reconciler.go
@@ -36,6 +36,7 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/handler"
 	"sigs.k8s.io/controller-runtime/pkg/reconcile"
 
+	"github.com/crossplane/crossplane-runtime/pkg/conditions"
 	"github.com/crossplane/crossplane-runtime/pkg/connection"
 	"github.com/crossplane/crossplane-runtime/pkg/controller"
 	"github.com/crossplane/crossplane-runtime/pkg/errors"
@@ -254,8 +255,9 @@ func NewReconciler(ca resource.ClientApplicator, opts ...ReconcilerOption) *Reco
 
 		engine: &NopEngine{},
 
-		log:    logging.NewNopLogger(),
-		record: event.NewNopRecorder(),
+		log:        logging.NewNopLogger(),
+		record:     event.NewNopRecorder(),
+		conditions: conditions.ObservedGenerationPropagationManager{},
 
 		options: apiextensionscontroller.Options{
 			Options: controller.DefaultOptions(),
@@ -281,8 +283,9 @@ type Reconciler struct {
 
 	engine ControllerEngine
 
-	log    logging.Logger
-	record event.Recorder
+	log        logging.Logger
+	record     event.Recorder
+	conditions conditions.Manager
 
 	options apiextensionscontroller.Options
 }
@@ -304,6 +307,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 		log.Debug(errGetXRD, "error", err)
 		return reconcile.Result{}, errors.Wrap(resource.IgnoreNotFound(err), errGetXRD)
 	}
+	status := r.conditions.For(d)
 
 	log = log.WithValues(
 		"uid", d.GetUID(),
@@ -319,7 +323,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 	}
 
 	if meta.WasDeleted(d) {
-		d.Status.SetConditions(v1.TerminatingComposite())
+		status.MarkConditions(v1.TerminatingComposite())
 		if err := r.client.Status().Update(ctx, d); err != nil {
 			log.Debug(errUpdateStatus, "error", err)
 			if kerrors.IsConflict(err) {
@@ -470,7 +474,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 
 	if r.engine.IsRunning(composite.ControllerName(d.GetName())) {
 		log.Debug("Composite resource controller is running")
-		d.Status.SetConditions(v1.WatchingComposite())
+		status.MarkConditions(v1.WatchingComposite())
 		return reconcile.Result{Requeue: false}, errors.Wrap(r.client.Status().Update(ctx, d), errUpdateStatus)
 	}
 
@@ -531,7 +535,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 	log.Debug("Started composite resource controller")
 
 	d.Status.Controllers.CompositeResourceTypeRef = v1.TypeReferenceTo(d.GetCompositeGroupVersionKind())
-	d.Status.SetConditions(v1.WatchingComposite())
+	status.MarkConditions(v1.WatchingComposite())
 	return reconcile.Result{Requeue: false}, errors.Wrap(r.client.Status().Update(ctx, d), errUpdateStatus)
 }
 
diff --git a/internal/controller/apiextensions/offered/reconciler.go b/internal/controller/apiextensions/offered/reconciler.go
index 5985b4f4bc4..11ed847fcdd 100644
--- a/internal/controller/apiextensions/offered/reconciler.go
+++ b/internal/controller/apiextensions/offered/reconciler.go
@@ -35,6 +35,7 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/handler"
 	"sigs.k8s.io/controller-runtime/pkg/reconcile"
 
+	"github.com/crossplane/crossplane-runtime/pkg/conditions"
 	"github.com/crossplane/crossplane-runtime/pkg/connection"
 	"github.com/crossplane/crossplane-runtime/pkg/controller"
 	"github.com/crossplane/crossplane-runtime/pkg/errors"
@@ -227,8 +228,9 @@ func NewReconciler(ca resource.ClientApplicator, opts ...ReconcilerOption) *Reco
 
 		engine: &NopEngine{},
 
-		log:    logging.NewNopLogger(),
-		record: event.NewNopRecorder(),
+		log:        logging.NewNopLogger(),
+		record:     event.NewNopRecorder(),
+		conditions: conditions.ObservedGenerationPropagationManager{},
 
 		options: apiextensionscontroller.Options{
 			Options: controller.DefaultOptions(),
@@ -258,8 +260,9 @@ type Reconciler struct {
 
 	engine ControllerEngine
 
-	log    logging.Logger
-	record event.Recorder
+	log        logging.Logger
+	record     event.Recorder
+	conditions conditions.Manager
 
 	options apiextensionscontroller.Options
 }
@@ -278,6 +281,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 		log.Debug(errGetXRD, "error", err)
 		return reconcile.Result{}, errors.Wrap(resource.IgnoreNotFound(err), errGetXRD)
 	}
+	status := r.conditions.For(d)
 
 	log = log.WithValues(
 		"uid", d.GetUID(),
@@ -293,7 +297,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 	}
 
 	if meta.WasDeleted(d) {
-		d.Status.SetConditions(v1.TerminatingClaim())
+		status.MarkConditions(v1.TerminatingClaim())
 		if err := r.client.Status().Update(ctx, d); err != nil {
 			if kerrors.IsConflict(err) {
 				return reconcile.Result{Requeue: true}, nil
@@ -472,7 +476,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 
 	if r.engine.IsRunning(claim.ControllerName(d.GetName())) {
 		log.Debug("Composite resource claim controller is running")
-		d.Status.SetConditions(v1.WatchingClaim())
+		status.MarkConditions(v1.WatchingClaim())
 		return reconcile.Result{Requeue: false}, errors.Wrap(r.client.Status().Update(ctx, d), errUpdateStatus)
 	}
 
@@ -508,6 +512,6 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 	}
 
 	d.Status.Controllers.CompositeResourceClaimTypeRef = v1.TypeReferenceTo(d.GetClaimGroupVersionKind())
-	d.Status.SetConditions(v1.WatchingClaim())
+	status.MarkConditions(v1.WatchingClaim())
 	return reconcile.Result{Requeue: false}, errors.Wrap(r.client.Status().Update(ctx, d), errUpdateStatus)
 }
diff --git a/internal/controller/apiextensions/usage/reconciler.go b/internal/controller/apiextensions/usage/reconciler.go
index 715cdbcfc5e..8a7e118d0fc 100644
--- a/internal/controller/apiextensions/usage/reconciler.go
+++ b/internal/controller/apiextensions/usage/reconciler.go
@@ -35,6 +35,7 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/reconcile"
 
 	xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
+	"github.com/crossplane/crossplane-runtime/pkg/conditions"
 	"github.com/crossplane/crossplane-runtime/pkg/errors"
 	"github.com/crossplane/crossplane-runtime/pkg/event"
 	"github.com/crossplane/crossplane-runtime/pkg/logging"
@@ -191,8 +192,9 @@ func NewReconciler(mgr manager.Manager, opts ...ReconcilerOption) *Reconciler {
 			selectorResolver: newAPISelectorResolver(kube),
 		},
 
-		log:    logging.NewNopLogger(),
-		record: event.NewNopRecorder(),
+		log:        logging.NewNopLogger(),
+		record:     event.NewNopRecorder(),
+		conditions: conditions.ObservedGenerationPropagationManager{},
 	}
 
 	for _, f := range opts {
@@ -207,8 +209,9 @@ type Reconciler struct {
 
 	usage usageResource
 
-	log    logging.Logger
-	record event.Recorder
+	log        logging.Logger
+	record     event.Recorder
+	conditions conditions.Manager
 
 	pollInterval time.Duration
 }
@@ -229,8 +232,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 
 	// Validate APIVersion of used object provided as input.
 	// We parse this value while indexing the objects, and we need to make sure it is valid.
-	_, err := schema.ParseGroupVersion(u.Spec.Of.APIVersion)
-	if err != nil {
+	if _, err := schema.ParseGroupVersion(u.Spec.Of.APIVersion); err != nil {
 		return reconcile.Result{}, errors.Wrap(err, errParseAPIVersion)
 	}
 
@@ -245,6 +247,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 
 	of := u.Spec.Of
 	by := u.Spec.By
+	status := r.conditions.For(u)
 
 	// Identify used xp composed as an unstructured object.
 	used := composed.New(composed.FromReference(v1.ObjectReference{
@@ -272,15 +275,12 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 				APIVersion: by.APIVersion,
 			}))
 			// Get the using resource
-			err := r.client.Get(ctx, client.ObjectKey{Name: by.ResourceRef.Name}, using)
-			if xpresource.IgnoreNotFound(err) != nil {
+			if err := r.client.Get(ctx, client.ObjectKey{Name: by.ResourceRef.Name}, using); xpresource.IgnoreNotFound(err) != nil {
 				log.Debug(errGetUsing, "error", err)
 				err = errors.Wrap(xpresource.IgnoreNotFound(err), errGetUsing)
 				r.record.Event(u, event.Warning(reasonGetUsing, err))
 				return reconcile.Result{}, err
-			}
-
-			if err == nil {
+			} else if err == nil {
 				// Using resource is still there, so we need to wait for it to be deleted.
 				msg := fmt.Sprintf("Waiting for the using resource (which is a %q named %q) to be deleted.", by.Kind, by.ResourceRef.Name)
 				log.Debug(msg)
@@ -300,17 +300,13 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 		// So, we can proceed with the deletion of the usage.
 
 		// Get the used resource
-		var err error
-		if err = r.client.Get(ctx, client.ObjectKey{Name: of.ResourceRef.Name}, used); xpresource.IgnoreNotFound(err) != nil {
+		if err := r.client.Get(ctx, client.ObjectKey{Name: of.ResourceRef.Name}, used); xpresource.IgnoreNotFound(err) != nil {
 			log.Debug(errGetUsed, "error", err)
 			err = errors.Wrap(err, errGetUsed)
 			r.record.Event(u, event.Warning(reasonGetUsed, err))
 			return reconcile.Result{}, err
-		}
-
-		// Remove the in-use label from the used resource if no other usages
-		// exists.
-		if err == nil {
+		} else if err == nil {
+			// Remove the in-use label from the used resource if no other usages exists.
 			usageList := &v1beta1.UsageList{}
 			if err = r.client.List(ctx, usageList, client.MatchingFields{usage.InUseIndexKey: usage.IndexValueForObject(used.GetUnstructured())}); err != nil {
 				log.Debug(errListUsages, "error", err)
@@ -345,7 +341,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 					// very soon.
 					time.Sleep(2 * time.Second)
 					log.Info("Replaying deletion of the used resource", "apiVersion", used.GetAPIVersion(), "kind", used.GetKind(), "name", used.GetName(), "policy", policy)
-					if err = r.client.Delete(context.Background(), used, client.PropagationPolicy(policy)); err != nil {
+					if err := r.client.Delete(context.Background(), used, client.PropagationPolicy(policy)); err != nil {
 						log.Info("Error when replaying deletion of the used resource", "apiVersion", used.GetAPIVersion(), "kind", used.GetKind(), "name", used.GetName(), "err", err)
 					}
 				}()
@@ -353,7 +349,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 		}
 
 		// Remove the finalizer from the usage
-		if err = r.usage.RemoveFinalizer(ctx, u); err != nil {
+		if err := r.usage.RemoveFinalizer(ctx, u); err != nil {
 			log.Debug(errRemoveFinalizer, "error", err)
 			if kerrors.IsConflict(err) {
 				return reconcile.Result{Requeue: true}, nil
@@ -451,7 +447,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 		}
 	}
 
-	u.Status.SetConditions(xpv1.Available())
+	status.MarkConditions(xpv1.Available())
 
 	// We are only watching the Usage itself but not using or used resources.
 	// So, we need to reconcile the Usage periodically to check if the using
diff --git a/internal/controller/pkg/manager/reconciler.go b/internal/controller/pkg/manager/reconciler.go
index 542ce77bdda..49355344d7f 100644
--- a/internal/controller/pkg/manager/reconciler.go
+++ b/internal/controller/pkg/manager/reconciler.go
@@ -36,6 +36,7 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/reconcile"
 
 	xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
+	"github.com/crossplane/crossplane-runtime/pkg/conditions"
 	"github.com/crossplane/crossplane-runtime/pkg/errors"
 	"github.com/crossplane/crossplane-runtime/pkg/event"
 	"github.com/crossplane/crossplane-runtime/pkg/logging"
@@ -152,11 +153,12 @@ func WithRecorder(er event.Recorder) ReconcilerOption {
 
 // Reconciler reconciles packages.
 type Reconciler struct {
-	client resource.ClientApplicator
-	pkg    Revisioner
-	config xpkg.ConfigStore
-	log    logging.Logger
-	record event.Recorder
+	client     resource.ClientApplicator
+	pkg        Revisioner
+	config     xpkg.ConfigStore
+	log        logging.Logger
+	record     event.Recorder
+	conditions conditions.Manager
 
 	newPackage             func() v1.Package
 	newPackageRevision     func() v1.PackageRevision
@@ -278,9 +280,10 @@ func NewReconciler(mgr ctrl.Manager, opts ...ReconcilerOption) *Reconciler {
 			Client:     mgr.GetClient(),
 			Applicator: resource.NewAPIPatchingApplicator(mgr.GetClient()),
 		},
-		pkg:    NewNopRevisioner(),
-		log:    logging.NewNopLogger(),
-		record: event.NewNopRecorder(),
+		pkg:        NewNopRevisioner(),
+		log:        logging.NewNopLogger(),
+		record:     event.NewNopRecorder(),
+		conditions: conditions.ObservedGenerationPropagationManager{},
 	}
 
 	for _, f := range opts {
@@ -305,12 +308,13 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 		log.Debug(errGetPackage, "error", err)
 		return reconcile.Result{}, errors.Wrap(resource.IgnoreNotFound(err), errGetPackage)
 	}
+	status := r.conditions.For(p)
 
 	// Check the pause annotation and return if it has the value "true"
 	// after logging, publishing an event and updating the SYNC status condition
 	if meta.IsPaused(p) {
 		r.record.Event(p, event.Normal(reasonPaused, reconcilePausedMsg))
-		p.SetConditions(xpv1.ReconcilePaused().WithMessage(reconcilePausedMsg))
+		status.MarkConditions(xpv1.ReconcilePaused().WithMessage(reconcilePausedMsg))
 		// If the pause annotation is removed, we will have a chance to reconcile again and resume
 		// and if status update fails, we will reconcile again to retry to update the status
 		return reconcile.Result{}, errors.Wrap(r.client.Status().Update(ctx, p), errUpdateStatus)
@@ -358,7 +362,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 	pullSecretConfig, pullSecretFromConfig, err := r.config.PullSecretFor(ctx, p.GetResolvedSource())
 	if err != nil {
 		err = errors.Wrap(err, errGetPullConfig)
-		p.SetConditions(v1.Unpacking().WithMessage(err.Error()))
+		status.MarkConditions(v1.Unpacking().WithMessage(err.Error()))
 		_ = r.client.Status().Update(ctx, p)
 
 		r.record.Event(p, event.Warning(reasonImageConfig, err))
@@ -380,7 +384,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 	revisionName, err := r.pkg.Revision(ctx, p, secrets...)
 	if err != nil {
 		err = errors.Wrap(err, errUnpack)
-		p.SetConditions(v1.Unpacking().WithMessage(err.Error()))
+		status.MarkConditions(v1.Unpacking().WithMessage(err.Error()))
 		r.record.Event(p, event.Warning(reasonUnpack, err))
 
 		if updateErr := r.client.Status().Update(ctx, p); updateErr != nil {
@@ -391,7 +395,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 	}
 
 	if revisionName == "" {
-		p.SetConditions(v1.Unpacking().WithMessage("Waiting for unpack to complete"))
+		status.MarkConditions(v1.Unpacking().WithMessage("Waiting for unpack to complete"))
 		r.record.Event(p, event.Normal(reasonUnpack, "Waiting for unpack to complete"))
 		return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, p), errUpdateStatus)
 	}
@@ -474,14 +478,14 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 			// package is already healthy.
 			r.record.Event(p, event.Normal(reasonInstall, "Successfully installed package revision"))
 		}
-		p.SetConditions(v1.Healthy())
+		status.MarkConditions(v1.Healthy())
 	}
 	if prHealthy := pr.GetCondition(v1.TypeHealthy); prHealthy.Status == corev1.ConditionFalse {
-		p.SetConditions(v1.Unhealthy().WithMessage(prHealthy.Message))
+		status.MarkConditions(v1.Unhealthy().WithMessage(prHealthy.Message))
 		r.record.Event(p, event.Warning(reasonInstall, errors.New(errUnhealthyPackageRevision)))
 	}
 	if prHealthy := pr.GetCondition(v1.TypeHealthy); prHealthy.Status == corev1.ConditionUnknown {
-		p.SetConditions(v1.UnknownHealth().WithMessage(prHealthy.Message))
+		status.MarkConditions(v1.UnknownHealth().WithMessage(prHealthy.Message))
 		r.record.Event(p, event.Warning(reasonInstall, errors.New(errUnknownPackageRevisionHealth)))
 	}
 
@@ -547,11 +551,11 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 		}
 	}
 
-	p.SetConditions(v1.Active())
+	status.MarkConditions(v1.Active())
 
 	// If current revision is still not active, the package is inactive.
 	if pr.GetDesiredState() != v1.PackageRevisionActive {
-		p.SetConditions(v1.Inactive().WithMessage("Package is inactive"))
+		status.MarkConditions(v1.Inactive().WithMessage("Package is inactive"))
 	}
 
 	// NOTE(hasheddan): when the first package revision is created for a
diff --git a/internal/controller/pkg/manager/reconciler_test.go b/internal/controller/pkg/manager/reconciler_test.go
index 6dcd5228f47..ad5141a7fbc 100644
--- a/internal/controller/pkg/manager/reconciler_test.go
+++ b/internal/controller/pkg/manager/reconciler_test.go
@@ -32,6 +32,7 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/reconcile"
 
 	commonv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
+	"github.com/crossplane/crossplane-runtime/pkg/conditions"
 	"github.com/crossplane/crossplane-runtime/pkg/errors"
 	"github.com/crossplane/crossplane-runtime/pkg/event"
 	"github.com/crossplane/crossplane-runtime/pkg/logging"
@@ -89,7 +90,8 @@ func TestReconcile(t *testing.T) {
 					client: resource.ClientApplicator{
 						Client: &test.MockClient{MockGet: test.NewMockGetFn(kerrors.NewNotFound(schema.GroupResource{}, ""))},
 					},
-					log: testLog,
+					log:        testLog,
+					conditions: conditions.ObservedGenerationPropagationManager{},
 				},
 			},
 			want: want{
@@ -105,7 +107,8 @@ func TestReconcile(t *testing.T) {
 					client: resource.ClientApplicator{
 						Client: &test.MockClient{MockGet: test.NewMockGetFn(errBoom)},
 					},
-					log: testLog,
+					log:        testLog,
+					conditions: conditions.ObservedGenerationPropagationManager{},
 				},
 			},
 			want: want{
@@ -125,8 +128,9 @@ func TestReconcile(t *testing.T) {
 							MockList: test.NewMockListFn(errBoom),
 						},
 					},
-					log:    testLog,
-					record: event.NewNopRecorder(),
+					log:        testLog,
+					record:     event.NewNopRecorder(),
+					conditions: conditions.ObservedGenerationPropagationManager{},
 				},
 			},
 			want: want{
@@ -157,8 +161,9 @@ func TestReconcile(t *testing.T) {
 							return nil
 						}),
 					},
-					log:    testLog,
-					record: event.NewNopRecorder(),
+					log:        testLog,
+					record:     event.NewNopRecorder(),
+					conditions: conditions.ObservedGenerationPropagationManager{},
 					pkg: &MockRevisioner{
 						MockRevision: NewMockRevisionFn("", errBoom),
 					},
@@ -205,6 +210,7 @@ func TestReconcile(t *testing.T) {
 						MockPullSecretFor: fake.NewMockConfigStorePullSecretForFn("", "", errBoom),
 						MockRewritePath:   fake.NewMockRewritePathFn("", "", nil),
 					},
+					conditions: conditions.ObservedGenerationPropagationManager{},
 				},
 			},
 			want: want{
@@ -244,6 +250,7 @@ func TestReconcile(t *testing.T) {
 						MockPullSecretFor: fake.NewMockConfigStorePullSecretForFn("", "", nil),
 						MockRewritePath:   fake.NewMockRewritePathFn("", "", nil),
 					},
+					conditions: conditions.ObservedGenerationPropagationManager{},
 				},
 			},
 			want: want{
@@ -298,8 +305,9 @@ func TestReconcile(t *testing.T) {
 						MockPullSecretFor: fake.NewMockConfigStorePullSecretForFn("", "", nil),
 						MockRewritePath:   fake.NewMockRewritePathFn("imageConfigName", "new/image/path", nil),
 					},
-					log:    testLog,
-					record: event.NewNopRecorder(),
+					log:        testLog,
+					record:     event.NewNopRecorder(),
+					conditions: conditions.ObservedGenerationPropagationManager{},
 				},
 			},
 			want: want{
@@ -349,8 +357,9 @@ func TestReconcile(t *testing.T) {
 						MockPullSecretFor: fake.NewMockConfigStorePullSecretForFn("", "", nil),
 						MockRewritePath:   fake.NewMockRewritePathFn("", "", nil),
 					},
-					log:    testLog,
-					record: event.NewNopRecorder(),
+					log:        testLog,
+					record:     event.NewNopRecorder(),
+					conditions: conditions.ObservedGenerationPropagationManager{},
 				},
 			},
 			want: want{
@@ -402,8 +411,9 @@ func TestReconcile(t *testing.T) {
 						MockPullSecretFor: fake.NewMockConfigStorePullSecretForFn("", "", nil),
 						MockRewritePath:   fake.NewMockRewritePathFn("", "", nil),
 					},
-					log:    testLog,
-					record: event.NewNopRecorder(),
+					log:        testLog,
+					record:     event.NewNopRecorder(),
+					conditions: conditions.ObservedGenerationPropagationManager{},
 				},
 			},
 			want: want{
@@ -453,8 +463,9 @@ func TestReconcile(t *testing.T) {
 						MockPullSecretFor: fake.NewMockConfigStorePullSecretForFn("", "", nil),
 						MockRewritePath:   fake.NewMockRewritePathFn("", "", nil),
 					},
-					log:    testLog,
-					record: event.NewNopRecorder(),
+					log:        testLog,
+					record:     event.NewNopRecorder(),
+					conditions: conditions.ObservedGenerationPropagationManager{},
 				},
 			},
 			want: want{
@@ -515,8 +526,9 @@ func TestReconcile(t *testing.T) {
 						MockPullSecretFor: fake.NewMockConfigStorePullSecretForFn("", "", nil),
 						MockRewritePath:   fake.NewMockRewritePathFn("", "", nil),
 					},
-					log:    testLog,
-					record: event.NewNopRecorder(),
+					log:        testLog,
+					record:     event.NewNopRecorder(),
+					conditions: conditions.ObservedGenerationPropagationManager{},
 				},
 			},
 			want: want{
@@ -597,8 +609,9 @@ func TestReconcile(t *testing.T) {
 						MockPullSecretFor: fake.NewMockConfigStorePullSecretForFn("", "", nil),
 						MockRewritePath:   fake.NewMockRewritePathFn("", "", nil),
 					},
-					log:    testLog,
-					record: event.NewNopRecorder(),
+					log:        testLog,
+					record:     event.NewNopRecorder(),
+					conditions: conditions.ObservedGenerationPropagationManager{},
 				},
 			},
 			want: want{
@@ -660,8 +673,9 @@ func TestReconcile(t *testing.T) {
 						MockPullSecretFor: fake.NewMockConfigStorePullSecretForFn("", "", nil),
 						MockRewritePath:   fake.NewMockRewritePathFn("", "", nil),
 					},
-					log:    testLog,
-					record: event.NewNopRecorder(),
+					log:        testLog,
+					record:     event.NewNopRecorder(),
+					conditions: conditions.ObservedGenerationPropagationManager{},
 				},
 			},
 			want: want{
@@ -724,8 +738,9 @@ func TestReconcile(t *testing.T) {
 						MockPullSecretFor: fake.NewMockConfigStorePullSecretForFn("", "", nil),
 						MockRewritePath:   fake.NewMockRewritePathFn("", "", nil),
 					},
-					log:    testLog,
-					record: event.NewNopRecorder(),
+					log:        testLog,
+					record:     event.NewNopRecorder(),
+					conditions: conditions.ObservedGenerationPropagationManager{},
 				},
 			},
 			want: want{
@@ -825,8 +840,9 @@ func TestReconcile(t *testing.T) {
 						MockPullSecretFor: fake.NewMockConfigStorePullSecretForFn("", "", nil),
 						MockRewritePath:   fake.NewMockRewritePathFn("", "", nil),
 					},
-					log:    testLog,
-					record: event.NewNopRecorder(),
+					log:        testLog,
+					record:     event.NewNopRecorder(),
+					conditions: conditions.ObservedGenerationPropagationManager{},
 				},
 			},
 			want: want{
@@ -908,8 +924,9 @@ func TestReconcile(t *testing.T) {
 						MockPullSecretFor: fake.NewMockConfigStorePullSecretForFn("", "", nil),
 						MockRewritePath:   fake.NewMockRewritePathFn("", "", nil),
 					},
-					log:    testLog,
-					record: event.NewNopRecorder(),
+					log:        testLog,
+					record:     event.NewNopRecorder(),
+					conditions: conditions.ObservedGenerationPropagationManager{},
 				},
 			},
 			want: want{
@@ -959,8 +976,9 @@ func TestReconcile(t *testing.T) {
 						MockPullSecretFor: fake.NewMockConfigStorePullSecretForFn("", "", nil),
 						MockRewritePath:   fake.NewMockRewritePathFn("", "", nil),
 					},
-					log:    testLog,
-					record: event.NewNopRecorder(),
+					log:        testLog,
+					record:     event.NewNopRecorder(),
+					conditions: conditions.ObservedGenerationPropagationManager{},
 				},
 			},
 			want: want{
@@ -1009,8 +1027,9 @@ func TestReconcile(t *testing.T) {
 						MockPullSecretFor: fake.NewMockConfigStorePullSecretForFn("", "", nil),
 						MockRewritePath:   fake.NewMockRewritePathFn("", "", nil),
 					},
-					log:    testLog,
-					record: event.NewNopRecorder(),
+					log:        testLog,
+					record:     event.NewNopRecorder(),
+					conditions: conditions.ObservedGenerationPropagationManager{},
 				},
 			},
 			want: want{
diff --git a/internal/controller/pkg/resolver/reconciler.go b/internal/controller/pkg/resolver/reconciler.go
index 7825a13fceb..95463f03283 100644
--- a/internal/controller/pkg/resolver/reconciler.go
+++ b/internal/controller/pkg/resolver/reconciler.go
@@ -37,6 +37,7 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/manager"
 	"sigs.k8s.io/controller-runtime/pkg/reconcile"
 
+	"github.com/crossplane/crossplane-runtime/pkg/conditions"
 	"github.com/crossplane/crossplane-runtime/pkg/errors"
 	"github.com/crossplane/crossplane-runtime/pkg/feature"
 	"github.com/crossplane/crossplane-runtime/pkg/fieldpath"
@@ -149,14 +150,15 @@ func WithDowngradesEnabled() ReconcilerOption {
 
 // Reconciler reconciles packages.
 type Reconciler struct {
-	client   client.Client
-	log      logging.Logger
-	lock     resource.Finalizer
-	newDag   internaldag.NewDAGFn
-	fetcher  xpkg.Fetcher
-	config   xpkg.ConfigStore
-	registry string
-	features *feature.Flags
+	client     client.Client
+	log        logging.Logger
+	lock       resource.Finalizer
+	newDag     internaldag.NewDAGFn
+	fetcher    xpkg.Fetcher
+	config     xpkg.ConfigStore
+	registry   string
+	features   *feature.Flags
+	conditions conditions.Manager
 
 	downgradesEnabled bool
 }
@@ -209,11 +211,12 @@ func Setup(mgr ctrl.Manager, o controller.Options) error {
 // NewReconciler creates a new lock dependency reconciler.
 func NewReconciler(mgr manager.Manager, opts ...ReconcilerOption) *Reconciler {
 	r := &Reconciler{
-		client:  mgr.GetClient(),
-		lock:    resource.NewAPIFinalizer(mgr.GetClient(), finalizer),
-		log:     logging.NewNopLogger(),
-		newDag:  internaldag.NewMapDag,
-		fetcher: xpkg.NewNopFetcher(),
+		client:     mgr.GetClient(),
+		lock:       resource.NewAPIFinalizer(mgr.GetClient(), finalizer),
+		log:        logging.NewNopLogger(),
+		newDag:     internaldag.NewMapDag,
+		fetcher:    xpkg.NewNopFetcher(),
+		conditions: conditions.ObservedGenerationPropagationManager{},
 	}
 
 	for _, f := range opts {
@@ -238,6 +241,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 		log.Debug(errGetLock, "error", err)
 		return reconcile.Result{}, errors.Wrap(resource.IgnoreNotFound(err), errGetLock)
 	}
+	status := r.conditions.For(lock)
 
 	// If no packages exist in Lock then we remove finalizer and wait until
 	// a package is added to reconcile again. This allows for cleanup of the
@@ -273,7 +277,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 	implied, err := dag.Init(v1beta1.ToNodes(lock.Packages...))
 	if err != nil {
 		log.Debug(errBuildDAG, "error", err)
-		lock.SetConditions(v1beta1.ResolutionFailed(errors.Wrap(err, errBuildDAG)))
+		status.MarkConditions(v1beta1.ResolutionFailed(errors.Wrap(err, errBuildDAG)))
 		_ = r.client.Status().Update(ctx, lock)
 		return reconcile.Result{}, errors.Wrap(err, errBuildDAG)
 	}
@@ -283,13 +287,13 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 	_, err = dag.Sort()
 	if err != nil {
 		log.Debug(errSortDAG, "error", err)
-		lock.SetConditions(v1beta1.ResolutionFailed(errors.Wrap(err, errSortDAG)))
+		status.MarkConditions(v1beta1.ResolutionFailed(errors.Wrap(err, errSortDAG)))
 		_ = r.client.Status().Update(ctx, lock)
 		return reconcile.Result{}, errors.Wrap(err, errSortDAG)
 	}
 
 	if len(implied) == 0 {
-		lock.SetConditions(v1beta1.ResolutionSucceeded())
+		status.MarkConditions(v1beta1.ResolutionSucceeded())
 		return reconcile.Result{}, errors.Wrap(r.client.Status().Update(ctx, lock), errCannotUpdateStatus)
 	}
 
@@ -301,14 +305,14 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 	depID := dep.Identifier()
 	if !ok {
 		log.Debug(errInvalidDependency, "error", errors.Errorf(errFmtMissingDependency, depID))
-		lock.SetConditions(v1beta1.ResolutionFailed(errors.Errorf(errFmtMissingDependency, depID)))
+		status.MarkConditions(v1beta1.ResolutionFailed(errors.Errorf(errFmtMissingDependency, depID)))
 		return reconcile.Result{}, errors.Wrap(r.client.Status().Update(ctx, lock), errCannotUpdateStatus)
 	}
 
 	ref, err := name.ParseReference(depID, name.WithDefaultRegistry(r.registry))
 	if err != nil {
 		log.Debug(errInvalidDependency, "error", err)
-		lock.SetConditions(v1beta1.ResolutionFailed(errors.Wrap(err, errInvalidDependency)))
+		status.MarkConditions(v1beta1.ResolutionFailed(errors.Wrap(err, errInvalidDependency)))
 		return reconcile.Result{}, errors.Wrap(r.client.Status().Update(ctx, lock), errCannotUpdateStatus)
 	}
 
@@ -318,14 +322,14 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 		l, err := NewPackageList(dep)
 		if err != nil {
 			log.Debug(errGetDependency, "error", err)
-			lock.SetConditions(v1beta1.ResolutionFailed(errors.Wrap(err, errGetDependency)))
+			status.MarkConditions(v1beta1.ResolutionFailed(errors.Wrap(err, errGetDependency)))
 			_ = r.client.Status().Update(ctx, lock)
 			return reconcile.Result{}, errors.Wrap(err, errGetDependency)
 		}
 
 		if err := r.client.List(ctx, l); err != nil {
 			log.Debug(errGetDependency, "error", err)
-			lock.SetConditions(v1beta1.ResolutionFailed(errors.Wrap(err, errGetDependency)))
+			status.MarkConditions(v1beta1.ResolutionFailed(errors.Wrap(err, errGetDependency)))
 			_ = r.client.Status().Update(ctx, lock)
 			return reconcile.Result{}, errors.Wrap(err, errGetDependency)
 		}
@@ -352,7 +356,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 		var addVer string
 		if addVer, err = r.findDependencyVersionToInstall(ctx, dep, log, ref); err != nil {
 			log.Debug(errFindDependency, "error", errors.Wrapf(err, depID, dep.Constraints))
-			lock.SetConditions(v1beta1.ResolutionFailed(errors.Wrap(err, errFindDependency)))
+			status.MarkConditions(v1beta1.ResolutionFailed(errors.Wrap(err, errFindDependency)))
 			_ = r.client.Status().Update(ctx, lock)
 			return reconcile.Result{Requeue: false}, errors.Wrap(err, errFindDependency)
 		}
@@ -361,14 +365,14 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 		// dictating constraints.
 		if addVer == "" {
 			log.Debug(errFindDependencyUpgrade, "error", errors.Errorf(errFmtNoValidVersion, depID, dep.Constraints))
-			lock.SetConditions(v1beta1.ResolutionFailed(errors.Errorf(errFmtNoValidVersion, depID, dep.Constraints)))
+			status.MarkConditions(v1beta1.ResolutionFailed(errors.Errorf(errFmtNoValidVersion, depID, dep.Constraints)))
 			return reconcile.Result{}, errors.Wrap(r.client.Status().Update(ctx, lock), errCannotUpdateStatus)
 		}
 
 		pack, err := NewPackage(dep, addVer, ref)
 		if err != nil {
 			log.Debug(errConstructDependency, "error", err)
-			lock.SetConditions(v1beta1.ResolutionFailed(errors.Wrap(err, errConstructDependency)))
+			status.MarkConditions(v1beta1.ResolutionFailed(errors.Wrap(err, errConstructDependency)))
 			_ = r.client.Status().Update(ctx, lock)
 			return reconcile.Result{}, errors.Wrap(err, errConstructDependency)
 		}
@@ -377,12 +381,12 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 		// it creates.
 		if err := r.client.Create(ctx, pack); err != nil && !kerrors.IsAlreadyExists(err) {
 			log.Debug(errCreateDependency, "error", err)
-			lock.SetConditions(v1beta1.ResolutionFailed(errors.Wrap(err, errCreateDependency)))
+			status.MarkConditions(v1beta1.ResolutionFailed(errors.Wrap(err, errCreateDependency)))
 			_ = r.client.Status().Update(ctx, lock)
 			return reconcile.Result{}, errors.Wrap(err, errCreateDependency)
 		}
 
-		lock.SetConditions(v1beta1.ResolutionSucceeded())
+		status.MarkConditions(v1beta1.ResolutionSucceeded())
 		return reconcile.Result{}, errors.Wrap(r.client.Status().Update(ctx, lock), errCannotUpdateStatus)
 	}
 
@@ -397,7 +401,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 	n, err := dag.GetNode(depID)
 	if err != nil {
 		log.Debug(errInvalidDependency, "error", errors.Errorf(errFmtMissingDependency, depID))
-		lock.SetConditions(v1beta1.ResolutionFailed(errors.Errorf(errFmtMissingDependency, depID)))
+		status.MarkConditions(v1beta1.ResolutionFailed(errors.Errorf(errFmtMissingDependency, depID)))
 		_ = r.client.Status().Update(ctx, lock)
 		return reconcile.Result{}, errors.Errorf(errFmtMissingDependency, depID)
 	}
@@ -405,7 +409,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 	newVer, err := r.findDependencyVersionToUpdate(ctx, ref, installedVersion, n, log)
 	if err != nil {
 		log.Debug(errFindDependencyUpgrade, "error", errors.Wrapf(err, depID, dep.Constraints))
-		lock.SetConditions(v1beta1.ResolutionFailed(errors.Wrap(err, errFindDependencyUpgrade)))
+		status.MarkConditions(v1beta1.ResolutionFailed(errors.Wrap(err, errFindDependencyUpgrade)))
 		_ = r.client.Status().Update(ctx, lock)
 		return reconcile.Result{}, errors.Wrap(err, errFindDependencyUpgrade)
 	}
@@ -419,12 +423,12 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 
 	if err := r.client.Update(ctx, pkg); err != nil {
 		log.Debug(errUpdateDependency, "error", err)
-		lock.SetConditions(v1beta1.ResolutionFailed(errors.Wrap(err, errUpdateDependency)))
+		status.MarkConditions(v1beta1.ResolutionFailed(errors.Wrap(err, errUpdateDependency)))
 		_ = r.client.Status().Update(ctx, lock)
 		return reconcile.Result{}, errors.Wrap(err, errUpdateDependency)
 	}
 
-	lock.SetConditions(v1beta1.ResolutionSucceeded())
+	status.MarkConditions(v1beta1.ResolutionSucceeded())
 	return reconcile.Result{}, errors.Wrap(r.client.Status().Update(ctx, lock), errCannotUpdateStatus)
 }
 
diff --git a/internal/controller/pkg/revision/reconciler.go b/internal/controller/pkg/revision/reconciler.go
index 39abe74a41d..20dc1e623ab 100644
--- a/internal/controller/pkg/revision/reconciler.go
+++ b/internal/controller/pkg/revision/reconciler.go
@@ -37,6 +37,7 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/reconcile"
 
 	xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
+	"github.com/crossplane/crossplane-runtime/pkg/conditions"
 	"github.com/crossplane/crossplane-runtime/pkg/errors"
 	"github.com/crossplane/crossplane-runtime/pkg/event"
 	"github.com/crossplane/crossplane-runtime/pkg/feature"
@@ -277,6 +278,7 @@ type Reconciler struct {
 	config         xpkg.ConfigStore
 	log            logging.Logger
 	record         event.Recorder
+	conditions     conditions.Manager
 	features       *feature.Flags
 	namespace      string
 	serviceAccount string
@@ -467,15 +469,16 @@ func SetupFunctionRevision(mgr ctrl.Manager, o controller.Options) error {
 // NewReconciler creates a new package revision reconciler.
 func NewReconciler(mgr manager.Manager, opts ...ReconcilerOption) *Reconciler {
 	r := &Reconciler{
-		client:    mgr.GetClient(),
-		cache:     xpkg.NewNopCache(),
-		revision:  resource.NewAPIFinalizer(mgr.GetClient(), finalizer),
-		objects:   NewNopEstablisher(),
-		parser:    parser.New(nil, nil),
-		linter:    parser.NewPackageLinter(nil, nil, nil),
-		versioner: version.New(),
-		log:       logging.NewNopLogger(),
-		record:    event.NewNopRecorder(),
+		client:     mgr.GetClient(),
+		cache:      xpkg.NewNopCache(),
+		revision:   resource.NewAPIFinalizer(mgr.GetClient(), finalizer),
+		objects:    NewNopEstablisher(),
+		parser:     parser.New(nil, nil),
+		linter:     parser.NewPackageLinter(nil, nil, nil),
+		versioner:  version.New(),
+		log:        logging.NewNopLogger(),
+		record:     event.NewNopRecorder(),
+		conditions: conditions.ObservedGenerationPropagationManager{},
 	}
 
 	for _, f := range opts {
@@ -500,6 +503,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 		log.Debug(errGetPackageRevision, "error", err)
 		return reconcile.Result{}, errors.Wrap(resource.IgnoreNotFound(err), errGetPackageRevision)
 	}
+	status := r.conditions.For(pr)
 
 	log = log.WithValues(
 		"uid", pr.GetUID(),
@@ -511,7 +515,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 	// after logging, publishing an event and updating the SYNC status condition
 	if meta.IsPaused(pr) {
 		r.record.Event(pr, event.Normal(reasonPaused, reconcilePausedMsg))
-		pr.SetConditions(xpv1.ReconcilePaused().WithMessage(reconcilePausedMsg))
+		status.MarkConditions(xpv1.ReconcilePaused().WithMessage(reconcilePausedMsg))
 		// If the pause annotation is removed, we will have a chance to reconcile again and resume
 		// and if status update fails, we will reconcile again to retry to update the status
 		return reconcile.Result{}, errors.Wrap(r.client.Status().Update(ctx, pr), errUpdateStatus)
@@ -600,7 +604,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 			// Initialize the installed condition if they are not already set to
 			// communicate the status of the package.
 			if pr.GetCondition(v1.TypeHealthy).Status == corev1.ConditionUnknown {
-				pr.SetConditions(v1.AwaitingVerification())
+				status.MarkConditions(v1.AwaitingVerification())
 				return reconcile.Result{}, errors.Wrap(r.client.Status().Update(ctx, pr), "cannot update status with awaiting verification")
 			}
 			return reconcile.Result{}, nil
@@ -619,7 +623,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 	pullSecretConfig, pullSecretFromConfig, err := r.config.PullSecretFor(ctx, pr.GetResolvedSource())
 	if err != nil {
 		err = errors.Wrap(err, errGetPullConfig)
-		pr.SetConditions(v1.Unhealthy().WithMessage(err.Error()))
+		status.MarkConditions(v1.Unhealthy().WithMessage(err.Error()))
 		_ = r.client.Status().Update(ctx, pr)
 
 		r.record.Event(pr, event.Warning(reasonImageConfig, err))
@@ -635,7 +639,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 			log.Debug(errManifestBuilderOptions, "error", err)
 
 			err = errors.Wrap(err, errManifestBuilderOptions)
-			pr.SetConditions(v1.Unhealthy().WithMessage(err.Error()))
+			status.MarkConditions(v1.Unhealthy().WithMessage(err.Error()))
 			_ = r.client.Status().Update(ctx, pr)
 
 			r.record.Event(pr, event.Warning(reasonSync, err))
@@ -687,7 +691,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 				// package revision is already healthy.
 				r.record.Event(pr, event.Normal(reasonSync, "Successfully configured package revision"))
 			}
-			pr.SetConditions(v1.Healthy())
+			status.MarkConditions(v1.Healthy())
 			return reconcile.Result{Requeue: false}, errors.Wrap(r.client.Status().Update(ctx, pr), errUpdateStatus)
 		}
 	}
@@ -732,7 +736,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 	// an error.
 	if rc == nil && pullPolicyNever {
 		err := errors.New(errPullPolicyNever)
-		pr.SetConditions(v1.Unhealthy().WithMessage(err.Error()))
+		status.MarkConditions(v1.Unhealthy().WithMessage(err.Error()))
 		_ = r.client.Status().Update(ctx, pr)
 
 		r.record.Event(pr, event.Warning(reasonParse, err))
@@ -762,7 +766,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 		imgrc, err := r.backend.Init(ctx, bo...)
 		if err != nil {
 			err = errors.Wrap(err, errInitParserBackend)
-			pr.SetConditions(v1.Unhealthy().WithMessage(err.Error()))
+			status.MarkConditions(v1.Unhealthy().WithMessage(err.Error()))
 			_ = r.client.Status().Update(ctx, pr)
 
 			r.record.Event(pr, event.Warning(reasonParse, err))
@@ -805,7 +809,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 	}
 	if err != nil {
 		err = errors.Wrap(err, errParsePackage)
-		pr.SetConditions(v1.Unhealthy().WithMessage(err.Error()))
+		status.MarkConditions(v1.Unhealthy().WithMessage(err.Error()))
 		_ = r.client.Status().Update(ctx, pr)
 
 		r.record.Event(pr, event.Warning(reasonParse, err))
@@ -815,7 +819,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 	// Lint package using package-specific linter.
 	if err := r.linter.Lint(pkg); err != nil {
 		err = errors.Wrap(err, errLintPackage)
-		pr.SetConditions(v1.Unhealthy().WithMessage(err.Error()))
+		status.MarkConditions(v1.Unhealthy().WithMessage(err.Error()))
 		_ = r.client.Status().Update(ctx, pr)
 
 		r.record.Event(pr, event.Warning(reasonLint, err))
@@ -832,7 +836,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 	// we check here to avoid a potential panic on 0 index below.
 	if len(pkg.GetMeta()) != 1 {
 		err = errors.New(errNotOneMeta)
-		pr.SetConditions(v1.Unhealthy().WithMessage(err.Error()))
+		status.MarkConditions(v1.Unhealthy().WithMessage(err.Error()))
 		_ = r.client.Status().Update(ctx, pr)
 
 		r.record.Event(pr, event.Warning(reasonLint, err))
@@ -850,7 +854,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 		}
 
 		err = errors.Wrap(err, errUpdateMeta)
-		pr.SetConditions(v1.Unhealthy().WithMessage(err.Error()))
+		status.MarkConditions(v1.Unhealthy().WithMessage(err.Error()))
 		_ = r.client.Status().Update(ctx, pr)
 
 		r.record.Event(pr, event.Warning(reasonSync, err))
@@ -862,7 +866,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 	if pr.GetIgnoreCrossplaneConstraints() == nil || !*pr.GetIgnoreCrossplaneConstraints() {
 		if err := xpkg.PackageCrossplaneCompatible(r.versioner)(pkgMeta); err != nil {
 			err = errors.Wrap(err, errIncompatible)
-			pr.SetConditions(v1.Unhealthy().WithMessage(err.Error()))
+			status.MarkConditions(v1.Unhealthy().WithMessage(err.Error()))
 
 			r.record.Event(pr, event.Warning(reasonLint, err))
 
@@ -885,7 +889,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 			}
 
 			err = errors.Wrap(err, errResolveDeps)
-			pr.SetConditions(v1.UnknownHealth().WithMessage(err.Error()))
+			status.MarkConditions(v1.UnknownHealth().WithMessage(err.Error()))
 			_ = r.client.Status().Update(ctx, pr)
 
 			r.record.Event(pr, event.Warning(reasonDependencies, err))
@@ -901,7 +905,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 			}
 
 			err = errors.Wrap(err, errPreHook)
-			pr.SetConditions(v1.Unhealthy().WithMessage(err.Error()))
+			status.MarkConditions(v1.Unhealthy().WithMessage(err.Error()))
 			_ = r.client.Status().Update(ctx, pr)
 
 			r.record.Event(pr, event.Warning(reasonSync, err))
@@ -918,7 +922,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 		}
 
 		err = errors.Wrap(err, errEstablishControl)
-		pr.SetConditions(v1.Unhealthy().WithMessage(err.Error()))
+		status.MarkConditions(v1.Unhealthy().WithMessage(err.Error()))
 		_ = r.client.Status().Update(ctx, pr)
 
 		r.record.Event(pr, event.Warning(reasonSync, err))
@@ -950,7 +954,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 			}
 
 			err = errors.Wrap(err, errPostHook)
-			pr.SetConditions(v1.Unhealthy().WithMessage(err.Error()))
+			status.MarkConditions(v1.Unhealthy().WithMessage(err.Error()))
 			_ = r.client.Status().Update(ctx, pr)
 
 			r.record.Event(pr, event.Warning(reasonSync, err))
@@ -964,7 +968,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 		// package revision is already healthy.
 		r.record.Event(pr, event.Normal(reasonSync, "Successfully configured package revision"))
 	}
-	pr.SetConditions(v1.Healthy())
+	status.MarkConditions(v1.Healthy())
 	return reconcile.Result{Requeue: false}, errors.Wrap(r.client.Status().Update(ctx, pr), errUpdateStatus)
 }
 
diff --git a/internal/controller/pkg/signature/reconciler.go b/internal/controller/pkg/signature/reconciler.go
index 678a9de9aa6..5d318175874 100644
--- a/internal/controller/pkg/signature/reconciler.go
+++ b/internal/controller/pkg/signature/reconciler.go
@@ -32,6 +32,7 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/handler"
 	"sigs.k8s.io/controller-runtime/pkg/reconcile"
 
+	"github.com/crossplane/crossplane-runtime/pkg/conditions"
 	"github.com/crossplane/crossplane-runtime/pkg/errors"
 	"github.com/crossplane/crossplane-runtime/pkg/logging"
 	"github.com/crossplane/crossplane-runtime/pkg/ratelimiter"
@@ -118,6 +119,7 @@ type Reconciler struct {
 	serviceAccount string
 	namespace      string
 	registry       string
+	conditions     conditions.Manager
 
 	newRevision func() v1.PackageRevision
 }
@@ -230,8 +232,9 @@ func SetupFunctionRevision(mgr ctrl.Manager, o controller.Options) error {
 // NewReconciler creates a new package reconciler for signature verification.
 func NewReconciler(client client.Client, opts ...ReconcilerOption) *Reconciler {
 	r := &Reconciler{
-		client: client,
-		log:    logging.NewNopLogger(),
+		client:     client,
+		log:        logging.NewNopLogger(),
+		conditions: conditions.ObservedGenerationPropagationManager{},
 	}
 
 	for _, f := range opts {
@@ -256,10 +259,12 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 		}
 
 		log.Debug(errGetRevision, "error", err)
-		pr.SetConditions(v1.VerificationIncomplete(errors.Wrap(err, errGetRevision)))
+		status := r.conditions.For(pr)
+		status.MarkConditions(v1.VerificationIncomplete(errors.Wrap(err, errGetRevision)))
 		_ = r.client.Status().Update(ctx, pr)
 		return reconcile.Result{}, errors.Wrap(err, errGetRevision)
 	}
+	status := r.conditions.For(pr)
 
 	log = log.WithValues(
 		"uid", pr.GetUID(),
@@ -293,7 +298,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 	ic, vc, err := r.config.ImageVerificationConfigFor(ctx, imagePath)
 	if err != nil {
 		log.Debug("Cannot get image verification config", "error", err)
-		pr.SetConditions(v1.VerificationIncomplete(errors.Wrap(err, errGetVerificationConfig)))
+		status.MarkConditions(v1.VerificationIncomplete(errors.Wrap(err, errGetVerificationConfig)))
 		_ = r.client.Status().Update(ctx, pr)
 		return reconcile.Result{}, errors.Wrap(err, errGetVerificationConfig)
 	}
@@ -301,7 +306,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 		// No verification config found for this image, so, we will skip
 		// verification.
 		log.Debug("No signature verification config found for image, skipping verification")
-		pr.SetConditions(v1.VerificationSkipped())
+		status.MarkConditions(v1.VerificationSkipped())
 		pr.ClearAppliedImageConfigRef(v1.ImageConfigReasonVerify)
 		return reconcile.Result{}, errors.Wrap(r.client.Status().Update(ctx, pr), "cannot update package status")
 	}
@@ -314,7 +319,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 	ref, err := name.ParseReference(imagePath, name.WithDefaultRegistry(r.registry))
 	if err != nil {
 		log.Debug("Cannot parse package image reference", "error", err)
-		pr.SetConditions(v1.VerificationIncomplete(errors.Wrap(err, errParseReference)))
+		status.MarkConditions(v1.VerificationIncomplete(errors.Wrap(err, errParseReference)))
 		_ = r.client.Status().Update(ctx, pr)
 		return reconcile.Result{}, errors.Wrap(err, errParseReference)
 	}
@@ -327,7 +332,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 	_, s, err := r.config.PullSecretFor(ctx, imagePath)
 	if err != nil {
 		log.Debug("Cannot get image config pull secret for image", "error", err)
-		pr.SetConditions(v1.VerificationIncomplete(errors.Wrap(err, errGetConfigPullSecret)))
+		status.MarkConditions(v1.VerificationIncomplete(errors.Wrap(err, errGetConfigPullSecret)))
 		_ = r.client.Status().Update(ctx, pr)
 		return reconcile.Result{}, errors.Wrap(err, errGetConfigPullSecret)
 	}
@@ -337,14 +342,14 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 
 	if err = r.validator.Validate(ctx, ref, vc, pullSecrets...); err != nil {
 		log.Debug("Signature verification failed", "error", err)
-		pr.SetConditions(v1.VerificationFailed(ic, err))
+		status.MarkConditions(v1.VerificationFailed(ic, err))
 		if sErr := r.client.Status().Update(ctx, pr); sErr != nil {
 			return reconcile.Result{}, errors.Wrap(sErr, "cannot update status with failed verification")
 		}
 		return reconcile.Result{}, errors.Wrap(err, errFailedVerification)
 	}
 
-	pr.SetConditions(v1.VerificationSucceeded(ic))
+	status.MarkConditions(v1.VerificationSucceeded(ic))
 	return reconcile.Result{}, errors.Wrap(r.client.Status().Update(ctx, pr), "cannot update status with successful verification")
 }
 
diff --git a/test/e2e/funcs/feature.go b/test/e2e/funcs/feature.go
index 5b0d4ef3d40..7067fb92193 100644
--- a/test/e2e/funcs/feature.go
+++ b/test/e2e/funcs/feature.go
@@ -348,6 +348,7 @@ func ResourceHasConditionWithin(d time.Duration, o k8s.Object, cds ...xpv1.Condi
 		desired := strings.Join(reasons, ", ")
 
 		t.Logf("Waiting %s for %s to become %s...", d, identifier(o), desired)
+		ogReport := make(map[string]bool)
 		old := make([]xpv1.Condition, len(cds))
 		match := func(o k8s.Object) bool {
 			u := asUnstructured(o)
@@ -355,10 +356,28 @@ func ResourceHasConditionWithin(d time.Duration, o k8s.Object, cds ...xpv1.Condi
 			_ = fieldpath.Pave(u.Object).GetValueInto("status", &s)
 
 			for i, want := range cds {
+				// Update the wanted observed generation to the latest object generation.
+				want.ObservedGeneration = u.GetGeneration()
+
 				got := s.GetCondition(want.Type)
+
+				// Until https://github.com/crossplane/crossplane/issues/6420 is resolved, crossplane will be in a
+				// transition period. A condition with an observedGeneration of zero means it is not yet being
+				// propagated when setting the conditions. To help that transition, we will move the generation forward
+				// ONLY if it is zero. But we will also log this fact to find it in the logs.
+				if got.ObservedGeneration == 0 {
+					got.ObservedGeneration = u.GetGeneration()
+					key := fmt.Sprintf("%s[%s]", u.GetKind(), got.Type)
+					if !ogReport[key] {
+						ogReport[key] = true
+						t.Logf("https://github.com/crossplane/crossplane/issues/6420: Warning, an unset observedGeneration was artificially updated for %s.status.conditions[%s]", u.GetKind(), got.Type)
+					}
+				}
+
 				if !got.Equal(old[i]) {
 					old[i] = got
-					t.Logf("- CONDITION: %s: %s=%s Reason=%s: %s (%s)", identifier(u), got.Type, got.Status, got.Reason, or(got.Message, `""`), got.LastTransitionTime)
+					t.Logf("- CONDITION: %s[@%d]: %s=%s[@%d] Reason=%s: %s (%s)",
+						identifier(u), u.GetGeneration(), got.Type, got.Status, got.ObservedGeneration, got.Reason, or(got.Message, `""`), got.LastTransitionTime)
 				}
 
 				// do compare modulo message as the message in e2e tests


Reviews:
user: login: n3wscott
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-09T17:02:14Z
user: login: n3wscott
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-09T17:02:54Z
user: login: n3wscott
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-09T17:06:24Z
user: login: n3wscott
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-09T17:07:35Z
user: login: negz
body: Overall LGTM - my comments are mostly about how we change style and approach in the project.

Starting to wonder if our contributing guide needs a section capturing our desired approach to structuring/designing controllers.
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-09T18:31:49Z
user: login: n3wscott
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-09T21:26:27Z
user: login: n3wscott
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-09T21:30:07Z
user: login: negz
body: Wonder if we should merge this today or hold for next release?

I'm assuming we'd get this one controller into v1.20 but not the other core controllers. Which probably doesn't hurt, but maybe better to land them all in the same release?

CC @jbw976 
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-13T01:12:23Z
user: login: negz
body: Thanks @n3wscott! Nice to finally start adding observed generation to our controllers.
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-27T20:20:54Z
user: login: n3wscott
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-27T20:56:39Z

Review comments:
diff_hunk: @@ -207,30 +209,23 @@ type Reconciler struct {
 
 	usage usageResource
 
-	log    logging.Logger
-	record event.Recorder
+	log        logging.Logger
+	record     event.Recorder
+	conditions conditions.Manager
 
 	pollInterval time.Duration
 }
 
 // Reconcile a Usage resource by resolving its selectors, defining ownership
 // relationship, adding a finalizer and handling proper deletion.
-func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reconcile.Result, error) { //nolint:gocognit // Reconcilers are typically complex.
path: internal/controller/apiextensions/usage/reconciler.go
user: login: n3wscott
body: Yes this is scope-creep but I wanted to get the project's take on moving to typed reconciled methods? It is a trivial change and starts us down the path of better pre/post-ample reconciliation logic that is outside of the typed reconciler.
created_at: 2025-05-09T17:02:14Z
author_association: CONTRIBUTOR
diff_hunk: @@ -300,17 +293,13 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 		// So, we can proceed with the deletion of the usage.
 
 		// Get the used resource
-		var err error
-		if err = r.client.Get(ctx, client.ObjectKey{Name: of.ResourceRef.Name}, used); xpresource.IgnoreNotFound(err) != nil {
+		if err := r.client.Get(ctx, client.ObjectKey{Name: of.ResourceRef.Name}, used); xpresource.IgnoreNotFound(err) != nil {
 			log.Debug(errGetUsed, "error", err)
 			err = errors.Wrap(err, errGetUsed)
 			r.record.Event(u, event.Warning(reasonGetUsed, err))
 			return reconcile.Result{}, err
-		}
-
-		// Remove the in-use label from the used resource if no other usages
-		// exists.
-		if err == nil {
path: internal/controller/apiextensions/usage/reconciler.go
user: login: n3wscott
body: Floating if's is bad style.
created_at: 2025-05-09T17:02:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -348,23 +348,44 @@ func ResourceHasConditionWithin(d time.Duration, o k8s.Object, cds ...xpv1.Condi
 		desired := strings.Join(reasons, ", ")
 
 		t.Logf("Waiting %s for %s to become %s...", d, identifier(o), desired)
+		ogReport := make(map[string]bool)
 		old := make([]xpv1.Condition, len(cds))
 		match := func(o k8s.Object) bool {
 			u := asUnstructured(o)
 			s := xpv1.ConditionedStatus{}
 			_ = fieldpath.Pave(u.Object).GetValueInto("status", &s)
 
 			for i, want := range cds {
+				// Update the wanted observed generation to the latest object generation.
+				want.ObservedGeneration = u.GetGeneration()
+
 				got := s.GetCondition(want.Type)
-				if !got.Equal(old[i]) {
+
+				// Until https://github.com/crossplane/crossplane/issues/6420 is resolved, crossplane will be in a
+				// transition period. A condition with an observedGeneration of zero means it is not yet being
+				// propagated when setting the conditions. To help that transition, we will move the generation forward
+				// ONLY if it is zero. But we will also log this fact to find it in the logs.
+				if got.ObservedGeneration == 0 {
+					got.ObservedGeneration = u.GetGeneration()
+					key := fmt.Sprintf("%s[%s]", u.GetKind(), got.Type)
+					if !ogReport[key] {
path: test/e2e/funcs/feature.go
user: login: n3wscott
body: this is to track locally the issue report log line to prevent the logs from being spammed, best effort. 
created_at: 2025-05-09T17:06:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -348,23 +348,44 @@ func ResourceHasConditionWithin(d time.Duration, o k8s.Object, cds ...xpv1.Condi
 		desired := strings.Join(reasons, ", ")
 
 		t.Logf("Waiting %s for %s to become %s...", d, identifier(o), desired)
+		ogReport := make(map[string]bool)
 		old := make([]xpv1.Condition, len(cds))
 		match := func(o k8s.Object) bool {
 			u := asUnstructured(o)
 			s := xpv1.ConditionedStatus{}
 			_ = fieldpath.Pave(u.Object).GetValueInto("status", &s)
 
 			for i, want := range cds {
+				// Update the wanted observed generation to the latest object generation.
+				want.ObservedGeneration = u.GetGeneration()
+
 				got := s.GetCondition(want.Type)
-				if !got.Equal(old[i]) {
+
+				// Until https://github.com/crossplane/crossplane/issues/6420 is resolved, crossplane will be in a
+				// transition period. A condition with an observedGeneration of zero means it is not yet being
+				// propagated when setting the conditions. To help that transition, we will move the generation forward
+				// ONLY if it is zero. But we will also log this fact to find it in the logs.
+				if got.ObservedGeneration == 0 {
+					got.ObservedGeneration = u.GetGeneration()
+					key := fmt.Sprintf("%s[%s]", u.GetKind(), got.Type)
+					if !ogReport[key] {
+						ogReport[key] = true
+						t.Logf("crossplane#6420: Warning, an unset observedGeneration was atrifically updated for %s.status.conditions[%s]", u.GetKind(), got.Type)
+					}
+				}
+
+				// TODO: until https://github.com/crossplane/crossplane-runtime/pull/828 is merged, Equal still ignores observedGeneration.
+				if !got.Equal(old[i]) && got.ObservedGeneration == old[i].ObservedGeneration {
 					old[i] = got
-					t.Logf("- CONDITION: %s: %s=%s Reason=%s: %s (%s)", identifier(u), got.Type, got.Status, got.Reason, or(got.Message, `""`), got.LastTransitionTime)
+					t.Logf("- CONDITION: %s[@%d]: %s=%s[@%d] Reason=%s: %s (%s)",
path: test/e2e/funcs/feature.go
user: login: n3wscott
body: We now log generation and observedGeneration explicitly for the test developers. I expect this to expose issues as we progress with the migration.
created_at: 2025-05-09T17:07:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -207,30 +209,23 @@ type Reconciler struct {
 
 	usage usageResource
 
-	log    logging.Logger
-	record event.Recorder
+	log        logging.Logger
+	record     event.Recorder
+	conditions conditions.Manager
 
 	pollInterval time.Duration
 }
 
 // Reconcile a Usage resource by resolving its selectors, defining ownership
 // relationship, adding a finalizer and handling proper deletion.
-func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reconcile.Result, error) { //nolint:gocognit // Reconcilers are typically complex.
path: internal/controller/apiextensions/usage/reconciler.go
user: login: negz
body: My quick take is that I like it. TIL about `reconcile.AsReconciler`.

That said, can you open an issue to discuss this change please (and revert it here until we agree). I prefer to discuss changes like this in an issue first. An example of such an issue: https://github.com/crossplane/crossplane/issues/4514

The failure modes I'm trying to avoid are:

* Some controllers written Alice style, some written Bob style
* We all agree to change how we build controllers, but never quite roll it out

Arguably the error string issue I linked above has fallen prey to the second failure mode. We agreed to move forward, but the old pattern is so prevalent in the code base that it keeps getting cargo culted. What you're proposing here seems like it could more feasibly be done in one PR to update all controllers, which is nice.
created_at: 2025-05-09T17:55:04Z
author_association: MEMBER
diff_hunk: @@ -300,17 +293,13 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 		// So, we can proceed with the deletion of the usage.
 
 		// Get the used resource
-		var err error
-		if err = r.client.Get(ctx, client.ObjectKey{Name: of.ResourceRef.Name}, used); xpresource.IgnoreNotFound(err) != nil {
+		if err := r.client.Get(ctx, client.ObjectKey{Name: of.ResourceRef.Name}, used); xpresource.IgnoreNotFound(err) != nil {
 			log.Debug(errGetUsed, "error", err)
 			err = errors.Wrap(err, errGetUsed)
 			r.record.Event(u, event.Warning(reasonGetUsed, err))
 			return reconcile.Result{}, err
-		}
-
-		// Remove the in-use label from the used resource if no other usages
-		// exists.
-		if err == nil {
path: internal/controller/apiextensions/usage/reconciler.go
user: login: negz
body: Citation needed. 😄

Please do try to improve style, but avoid comments like this - it's more helpful to explain why the proposed style is an improvement on the current style.

We have [a style guide](https://github.com/crossplane/crossplane/tree/main/contributing#coding-style) for the project, that's essentially a link to Go's style guides. We prefer to defer to authority. "The folks Go project says this is bad style at <link>" is usually better at settling arguments than "I personally think this is bad style because XYZ".

We also have just about every linter known to man enabled in [our linter config](https://github.com/crossplane/crossplane/blob/main/.golangci.yml) to try to avoid needing to discuss style in the first place, but it doesn't catch everything.

https://github.com/crossplane/crossplane/tree/v1.19.1/contributing#scope-errors

This case looks like a grey area in the above guidance. I assume you're making this change to scope the error better. For us 'return early' trumps 'scope errors', but in this specific case the second error block is just as nested either way so I can see how the change would make sense.
created_at: 2025-05-09T18:18:55Z
author_association: MEMBER
diff_hunk: @@ -348,23 +348,44 @@ func ResourceHasConditionWithin(d time.Duration, o k8s.Object, cds ...xpv1.Condi
 		desired := strings.Join(reasons, ", ")
 
 		t.Logf("Waiting %s for %s to become %s...", d, identifier(o), desired)
+		ogReport := make(map[string]bool)
 		old := make([]xpv1.Condition, len(cds))
 		match := func(o k8s.Object) bool {
 			u := asUnstructured(o)
 			s := xpv1.ConditionedStatus{}
 			_ = fieldpath.Pave(u.Object).GetValueInto("status", &s)
 
 			for i, want := range cds {
+				// Update the wanted observed generation to the latest object generation.
+				want.ObservedGeneration = u.GetGeneration()
+
 				got := s.GetCondition(want.Type)
-				if !got.Equal(old[i]) {
+
+				// Until https://github.com/crossplane/crossplane/issues/6420 is resolved, crossplane will be in a
+				// transition period. A condition with an observedGeneration of zero means it is not yet being
+				// propagated when setting the conditions. To help that transition, we will move the generation forward
+				// ONLY if it is zero. But we will also log this fact to find it in the logs.
+				if got.ObservedGeneration == 0 {
+					got.ObservedGeneration = u.GetGeneration()
+					key := fmt.Sprintf("%s[%s]", u.GetKind(), got.Type)
+					if !ogReport[key] {
+						ogReport[key] = true
+						t.Logf("crossplane#6420: Warning, an unset observedGeneration was atrifically updated for %s.status.conditions[%s]", u.GetKind(), got.Type)
path: test/e2e/funcs/feature.go
user: login: negz
body: ```suggestion
						t.Logf("crossplane#6420: Warning, an unset observedGeneration was artificially updated for %s.status.conditions[%s]", u.GetKind(), got.Type)
```

I'd prefer to log the actual issue URL here. I'm not sure everyone would recognize this syntax as referencing a GitHub issue.
created_at: 2025-05-09T18:26:28Z
author_association: MEMBER
diff_hunk: @@ -207,30 +209,23 @@ type Reconciler struct {
 
 	usage usageResource
 
-	log    logging.Logger
-	record event.Recorder
+	log        logging.Logger
+	record     event.Recorder
+	conditions conditions.Manager
 
 	pollInterval time.Duration
 }
 
 // Reconcile a Usage resource by resolving its selectors, defining ownership
 // relationship, adding a finalizer and handling proper deletion.
-func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reconcile.Result, error) { //nolint:gocognit // Reconcilers are typically complex.
path: internal/controller/apiextensions/usage/reconciler.go
user: login: n3wscott
body: reverted and opened https://github.com/crossplane/crossplane/issues/6444
created_at: 2025-05-09T21:26:27Z
author_association: CONTRIBUTOR
diff_hunk: @@ -300,17 +293,13 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 		// So, we can proceed with the deletion of the usage.
 
 		// Get the used resource
-		var err error
-		if err = r.client.Get(ctx, client.ObjectKey{Name: of.ResourceRef.Name}, used); xpresource.IgnoreNotFound(err) != nil {
+		if err := r.client.Get(ctx, client.ObjectKey{Name: of.ResourceRef.Name}, used); xpresource.IgnoreNotFound(err) != nil {
 			log.Debug(errGetUsed, "error", err)
 			err = errors.Wrap(err, errGetUsed)
 			r.record.Event(u, event.Warning(reasonGetUsed, err))
 			return reconcile.Result{}, err
-		}
-
-		// Remove the in-use label from the used resource if no other usages
-		// exists.
-		if err == nil {
path: internal/controller/apiextensions/usage/reconciler.go
user: login: n3wscott
body: If the followup was a pure else, I agree with the linked advice. In this case we are acting on the `if err == nil {` based on an err that was set above but not attached. It is extremely easy to assign a new value to `err` between the first set of statements and the continued clarifications. 
created_at: 2025-05-09T21:30:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -229,8 +232,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 
 	// Validate APIVersion of used object provided as input.
 	// We parse this value while indexing the objects, and we need to make sure it is valid.
-	_, err := schema.ParseGroupVersion(u.Spec.Of.APIVersion)
-	if err != nil {
+	if _, err := schema.ParseGroupVersion(u.Spec.Of.APIVersion); err != nil {
path: internal/controller/apiextensions/usage/reconciler.go
user: login: negz
body: Do you have a linter rule spotting these, or are you just noticing them the old fashioned way?

If there's a linter/rule for golangci-lint can you open a PR to enable it? (Whenever, not urgent.)
created_at: 2025-05-27T20:19:29Z
author_association: MEMBER
diff_hunk: @@ -229,8 +232,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
 
 	// Validate APIVersion of used object provided as input.
 	// We parse this value while indexing the objects, and we need to make sure it is valid.
-	_, err := schema.ParseGroupVersion(u.Spec.Of.APIVersion)
-	if err != nil {
+	if _, err := schema.ParseGroupVersion(u.Spec.Of.APIVersion); err != nil {
path: internal/controller/apiextensions/usage/reconciler.go
user: login: n3wscott
body: just plain old OCD.
created_at: 2025-05-27T20:56:39Z
author_association: CONTRIBUTOR

PR comments:
user: login: jbw976
created_at: 2025-05-13T10:51:21Z
body: yes @negz, it sounds good to me to merge this after we create the 1.20 release branch, and then let this update bake during the v1.21 milestone and also give @n3wscott plenty of time to roll out similar changes to the other core controllers.
author_association: MEMBER
user: login: jbw976
created_at: 2025-05-14T16:57:35Z
body: the 1.20 release branch was created, so we can merge this whenever we feel it's ready!
author_association: MEMBER
user: login: negz
created_at: 2025-05-14T23:05:30Z
body: > we can merge this whenever we feel it's ready!

Think it needs another review first. I only reviewed the Usage controller updates - haven't looked at the rest yet.
author_association: MEMBER