Repository: knative/serving
Pull request #8356 (2020-06-17 17:35:01)

Implement KRShaped on Serving v1alpha1 resource types
<!--
Request Prow to automatically lint any go code in this PR:

/lint
-->
## Proposed Changes

Implements the KRShaped interface for the v1alpha1 serving resources
- Adds unit tests to test the implemented methods

knative-prow-robot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by: *<a href="https://github.com/knative/serving/pull/8356#" title="Author self-approved">shreejad</a>*
To complete the [pull request process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process), please assign **dprotaso**
You can assign the PR to them by writing `/assign @dprotaso` in a comment when ready.

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=knative%2Fserving).

<details open>
Needs approval from an approver in each of these files:

- **[pkg/apis/OWNERS](https://github.com/knative/serving/blob/master/pkg/apis/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["dprotaso"]} -->
shreejad: /assign @whaught 
knative-metrics-robot: The following is the coverage report on the affected files.
Say `/test pull-knative-serving-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[pkg/apis/serving/v1alpha1/configuration_types.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative_serving/8356/pull-knative-serving-go-coverage/1273308319414489089/artifacts/line-cov.html#file1) | Do not exist | 100.0% | 
[pkg/apis/serving/v1alpha1/revision_lifecycle.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative_serving/8356/pull-knative-serving-go-coverage/1273308319414489089/artifacts/line-cov.html#file2) | 90.0% | 90.1% | 0.1
[pkg/apis/serving/v1alpha1/revision_types.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative_serving/8356/pull-knative-serving-go-coverage/1273308319414489089/artifacts/line-cov.html#file3) | Do not exist | 100.0% | 
[pkg/apis/serving/v1alpha1/route_types.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative_serving/8356/pull-knative-serving-go-coverage/1273308319414489089/artifacts/line-cov.html#file5) | Do not exist | 100.0% | 
[pkg/apis/serving/v1alpha1/service_lifecycle.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative_serving/8356/pull-knative-serving-go-coverage/1273308319414489089/artifacts/line-cov.html#file6) | 89.3% | 89.7% | 0.4
[pkg/apis/serving/v1alpha1/service_types.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative_serving/8356/pull-knative-serving-go-coverage/1273308319414489089/artifacts/line-cov.html#file7) | Do not exist | 100.0% | 


Patch:
diff --git a/go.mod b/go.mod
index 66b30ce04..5f2c9397a 100644
--- a/go.mod
+++ b/go.mod
@@ -42,9 +42,9 @@ require (
 	k8s.io/kube-openapi v0.0.0-20200410145947-bcb3869e6f29
 	k8s.io/metrics v0.17.6
 	knative.dev/caching v0.0.0-20200606210318-787aec80f71c
-	knative.dev/networking v0.0.0-20200611155523-bd13675bcf70
-	knative.dev/pkg v0.0.0-20200615224524-dbd32a77f41c
-	knative.dev/test-infra v0.0.0-20200612191923-a4dd434e085b
+	knative.dev/networking v0.0.0-20200617160325-c5b1bddf865c
+	knative.dev/pkg v0.0.0-20200617194224-40c7e164ccd7
+	knative.dev/test-infra v0.0.0-20200617184924-0fc4e2aeef74
 )
 
 // pin the older grpc - see: https://github.com/grpc/grpc-go/issues/3180
diff --git a/go.sum b/go.sum
index 1aecab7b9..7ef9c2112 100644
--- a/go.sum
+++ b/go.sum
@@ -1339,8 +1339,8 @@ knative.dev/caching v0.0.0-20200606210318-787aec80f71c h1:4w9QOkxY1VNH/82LIefx8u
 knative.dev/caching v0.0.0-20200606210318-787aec80f71c/go.mod h1:rWD+0zSqcE7L//jJyt866xtXsOi9hCdB0FoD4w2Rygg=
 knative.dev/eventing-contrib v0.6.1-0.20190723221543-5ce18048c08b/go.mod h1:SnXZgSGgMSMLNFTwTnpaOH7hXDzTFtw0J8OmHflNx3g=
 knative.dev/eventing-contrib v0.11.2/go.mod h1:SnXZgSGgMSMLNFTwTnpaOH7hXDzTFtw0J8OmHflNx3g=
-knative.dev/networking v0.0.0-20200611155523-bd13675bcf70 h1:xS3YAzmfFwmNXFXj6rM2n3FbBHziWvN3FMQmgUKv7bU=
-knative.dev/networking v0.0.0-20200611155523-bd13675bcf70/go.mod h1:PU1dM8mNwh+k3xDBXR54h2fafRgp8pIU5W43dYV7LkA=
+knative.dev/networking v0.0.0-20200617160325-c5b1bddf865c h1:Uv3REjPWLs/mohATHb7G9FZFbH4lkiHGX7IVDz+J+zE=
+knative.dev/networking v0.0.0-20200617160325-c5b1bddf865c/go.mod h1:eypU2utW0QjphexxIWI39NrzDxqrGpMWcZXkx8gM1xM=
 knative.dev/pkg v0.0.0-20191101194912-56c2594e4f11/go.mod h1:pgODObA1dTyhNoFxPZTTjNWfx6F0aKsKzn+vaT9XO/Q=
 knative.dev/pkg v0.0.0-20191111150521-6d806b998379/go.mod h1:pgODObA1dTyhNoFxPZTTjNWfx6F0aKsKzn+vaT9XO/Q=
 knative.dev/pkg v0.0.0-20200207155214-fef852970f43/go.mod h1:pgODObA1dTyhNoFxPZTTjNWfx6F0aKsKzn+vaT9XO/Q=
@@ -1354,10 +1354,11 @@ knative.dev/pkg v0.0.0-20200519155757-14eb3ae3a5a7/go.mod h1:QgNZTxnwpB/oSpNcfnL
 knative.dev/pkg v0.0.0-20200520073958-94316e20e860/go.mod h1:QgNZTxnwpB/oSpNcfnLVlw+WpEwwyKAvJlvR3hgeltA=
 knative.dev/pkg v0.0.0-20200603222317-b79e4a24ca50 h1:sbJMCCtOENPgfE5dUSwBUxZtJzhfqORhExyWFX7L0Fk=
 knative.dev/pkg v0.0.0-20200603222317-b79e4a24ca50/go.mod h1:8IfPj/lpuKHHg82xZCl2wuFZ3BM96To72sN1W8T9wjQ=
-knative.dev/pkg v0.0.0-20200609184032-fef70cc2616b/go.mod h1:rA+FklsrVahwF4a+D63NyHJlzDoAFH81K4J5CYuE3bA=
 knative.dev/pkg v0.0.0-20200611204322-2ddcfef739a2/go.mod h1:rA+FklsrVahwF4a+D63NyHJlzDoAFH81K4J5CYuE3bA=
-knative.dev/pkg v0.0.0-20200615224524-dbd32a77f41c h1:sFGEpJ7MJSaUs9nSOdBHVVxGsOvuFrl4iEHoT4uRAfM=
-knative.dev/pkg v0.0.0-20200615224524-dbd32a77f41c/go.mod h1:rA+FklsrVahwF4a+D63NyHJlzDoAFH81K4J5CYuE3bA=
+knative.dev/pkg v0.0.0-20200616232624-ffb929374a39 h1:wcbxKTDGymyIMI3uUkli5KqvmfBnHLJpTI77QREjC0A=
+knative.dev/pkg v0.0.0-20200616232624-ffb929374a39/go.mod h1:NtvASnZHh7yo6VBfGk7dXdpAK2vJyrDc/+Syk4os8xk=
+knative.dev/pkg v0.0.0-20200617194224-40c7e164ccd7 h1:okYYk80lDA++IV4SqZb53xZX5gTxdZYzkAMihvF7Wss=
+knative.dev/pkg v0.0.0-20200617194224-40c7e164ccd7/go.mod h1:4ty6MSlNjZk5qBaGb3Gt4gopjMD4gRknfTABblcFpQ8=
 knative.dev/sample-controller v0.0.0-20200510050845-bf7c19498b7e/go.mod h1:D2ZDLrR9Dq9LiiVN7TatzI7WMcEPgk1MHbbhgBKE6W8=
 knative.dev/test-infra v0.0.0-20200407185800-1b88cb3b45a5/go.mod h1:xcdUkMJrLlBswIZqL5zCuBFOC22WIPMQoVX1L35i0vQ=
 knative.dev/test-infra v0.0.0-20200505052144-5ea2f705bb55/go.mod h1:WqF1Azka+FxPZ20keR2zCNtiQA1MP9ZB4BH4HuI+SIU=
@@ -1368,9 +1369,11 @@ knative.dev/test-infra v0.0.0-20200519015156-82551620b0a9/go.mod h1:A5b2OAXTOeHT
 knative.dev/test-infra v0.0.0-20200519161858-554a95a37986/go.mod h1:LeNa1Wvn47efeQUkpkn3XG7Fx9Ga+rhAP13SZyjaEGg=
 knative.dev/test-infra v0.0.0-20200522180958-6a0a9b9d893a/go.mod h1:n9eQkzmSNj8BiqNFl1lzoz68D09uMeJfyOjc132Gbik=
 knative.dev/test-infra v0.0.0-20200606045118-14ebc4a42974/go.mod h1://I6IZIF0QDgs5wotU243ZZ5cTpm6/GthayjUenBBc0=
-knative.dev/test-infra v0.0.0-20200610141822-fd009ecf10fe/go.mod h1:vyiP+4ospN/PJgwVzHkjnhlNP4uBPnvwkgb9dPCFSj4=
-knative.dev/test-infra v0.0.0-20200612191923-a4dd434e085b h1:QF1luh24SFfO3llKMl3i1drC6hBBqXCO1D8QV13gkNY=
-knative.dev/test-infra v0.0.0-20200612191923-a4dd434e085b/go.mod h1:+BfrTJpc++rH30gX/C0QY6NT2eYVzycll52uw6CrQnc=
+knative.dev/test-infra v0.0.0-20200615231324-3a016f44102c/go.mod h1:+BfrTJpc++rH30gX/C0QY6NT2eYVzycll52uw6CrQnc=
+knative.dev/test-infra v0.0.0-20200617011927-7220df1de9de h1:6249W/lXfJw84H4loClOfjN4NdYiFt9ZJ4deAeprPQI=
+knative.dev/test-infra v0.0.0-20200617011927-7220df1de9de/go.mod h1:+BfrTJpc++rH30gX/C0QY6NT2eYVzycll52uw6CrQnc=
+knative.dev/test-infra v0.0.0-20200617184924-0fc4e2aeef74 h1:+dA0pgMzoPxUk4hJqMMLvUnBDQw/E/FcLYG7AxteFCo=
+knative.dev/test-infra v0.0.0-20200617184924-0fc4e2aeef74/go.mod h1:+BfrTJpc++rH30gX/C0QY6NT2eYVzycll52uw6CrQnc=
 modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=
 modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
 modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
diff --git a/pkg/activator/handler/metrics.go b/pkg/activator/handler/metrics.go
index 73fc3de7d..812c25ed7 100644
--- a/pkg/activator/handler/metrics.go
+++ b/pkg/activator/handler/metrics.go
@@ -17,6 +17,7 @@ limitations under the License.
 package handler
 
 import (
+	pkgmetrics "knative.dev/pkg/metrics"
 	"knative.dev/serving/pkg/metrics"
 
 	"go.opencensus.io/stats"
@@ -50,7 +51,7 @@ func register() {
 	// Create views to see our measurements. This can return an error if
 	// a previously-registered view has the same name with a different value.
 	// View name defaults to the measure name if unspecified.
-	if err := view.Register(
+	if err := pkgmetrics.RegisterResourceView(
 		&view.View{
 			Description: "Concurrent requests that are routed to Activator",
 			Measure:     requestConcurrencyM,
diff --git a/pkg/apis/autoscaling/v1alpha1/pa_lifecycle.go b/pkg/apis/autoscaling/v1alpha1/pa_lifecycle.go
index 386db59a5..9cec371cd 100644
--- a/pkg/apis/autoscaling/v1alpha1/pa_lifecycle.go
+++ b/pkg/apis/autoscaling/v1alpha1/pa_lifecycle.go
@@ -60,16 +60,12 @@ func (pa *PodAutoscaler) Metric() string {
 	return defaultMetric(pa.Class())
 }
 
-func (pa *PodAutoscaler) annotationInt32(key string) int32 {
+func (pa *PodAutoscaler) annotationInt32(key string) (int32, bool) {
 	if s, ok := pa.Annotations[key]; ok {
-		// no error check: relying on validation
-		i, _ := strconv.ParseInt(s, 10, 32)
-		if i < 0 {
-			return 0
-		}
-		return int32(i)
+		i, err := strconv.ParseInt(s, 10, 32)
+		return int32(i), err == nil
 	}
-	return 0
+	return 0, false
 }
 
 func (pa *PodAutoscaler) annotationFloat64(key string) (float64, bool) {
@@ -86,9 +82,9 @@ func (pa *PodAutoscaler) annotationFloat64(key string) (float64, bool) {
 // Note: min will be ignored if the PA is not reachable
 func (pa *PodAutoscaler) ScaleBounds() (min, max int32) {
 	if pa.Spec.Reachability != ReachabilityUnreachable {
-		min = pa.annotationInt32(autoscaling.MinScaleAnnotationKey)
+		min, _ = pa.annotationInt32(autoscaling.MinScaleAnnotationKey)
 	}
-	max = pa.annotationInt32(autoscaling.MaxScaleAnnotationKey)
+	max, _ = pa.annotationInt32(autoscaling.MaxScaleAnnotationKey)
 
 	return
 }
@@ -146,6 +142,12 @@ func (pa *PodAutoscaler) PanicThresholdPercentage() (percentage float64, ok bool
 	return pa.annotationFloat64(autoscaling.PanicThresholdPercentageAnnotationKey)
 }
 
+// InitialScale returns the initial scale on the revision if present, or false if not present.
+func (pa *PodAutoscaler) InitialScale() (int32, bool) {
+	// The value is validated in the webhook.
+	return pa.annotationInt32(autoscaling.InitialScaleAnnotationKey)
+}
+
 // IsReady returns true if the Status condition PodAutoscalerConditionReady
 // is true and the latest spec has been observed.
 func (pa *PodAutoscaler) IsReady() bool {
diff --git a/pkg/apis/serving/v1alpha1/configuration_lifecycle.go b/pkg/apis/serving/v1alpha1/configuration_lifecycle.go
index 8f21e80ea..b58be41ac 100644
--- a/pkg/apis/serving/v1alpha1/configuration_lifecycle.go
+++ b/pkg/apis/serving/v1alpha1/configuration_lifecycle.go
@@ -24,6 +24,11 @@ import (
 
 var confCondSet = apis.NewLivingConditionSet()
 
+// GetConditionSet retrieves the ConditionSet of the Configuration. Implements the KRShaped interface.
+func (*Configuration) GetConditionSet() apis.ConditionSet {
+	return confCondSet
+}
+
 func (r *Configuration) GetGroupVersionKind() schema.GroupVersionKind {
 	return SchemeGroupVersion.WithKind("Configuration")
 }
diff --git a/pkg/apis/serving/v1alpha1/configuration_types.go b/pkg/apis/serving/v1alpha1/configuration_types.go
index 8678369d0..2d0ecb13e 100644
--- a/pkg/apis/serving/v1alpha1/configuration_types.go
+++ b/pkg/apis/serving/v1alpha1/configuration_types.go
@@ -59,6 +59,9 @@ var (
 
 	// Check that we can create OwnerReferences to a Configuration.
 	_ kmeta.OwnerRefable = (*Configuration)(nil)
+
+	// Check that the type conforms to the duck Knative Resource shape.
+	_ duckv1.KRShaped = (*Configuration)(nil)
 )
 
 // ConfigurationSpec holds the desired state of the Configuration (from the client).
@@ -130,3 +133,8 @@ type ConfigurationList struct {
 
 	Items []Configuration `json:"items"`
 }
+
+// GetStatus retrieves the status of the Configuration. Implements the KRShaped interface.
+func (c *Configuration) GetStatus() *duckv1.Status {
+	return &c.Status.Status
+}
diff --git a/pkg/apis/serving/v1alpha1/revision_lifecycle.go b/pkg/apis/serving/v1alpha1/revision_lifecycle.go
index af8f07d12..811a81193 100644
--- a/pkg/apis/serving/v1alpha1/revision_lifecycle.go
+++ b/pkg/apis/serving/v1alpha1/revision_lifecycle.go
@@ -65,6 +65,11 @@ var revCondSet = apis.NewLivingConditionSet(
 	RevisionConditionContainerHealthy,
 )
 
+// GetConditionSet retrieves the ConditionSet of the Revision. Implements the KRShaped interface.
+func (*Revision) GetConditionSet() apis.ConditionSet {
+	return revCondSet
+}
+
 func (r *Revision) GetGroupVersionKind() schema.GroupVersionKind {
 	return SchemeGroupVersion.WithKind("Revision")
 }
diff --git a/pkg/apis/serving/v1alpha1/revision_types.go b/pkg/apis/serving/v1alpha1/revision_types.go
index 51cf78435..c241604b2 100644
--- a/pkg/apis/serving/v1alpha1/revision_types.go
+++ b/pkg/apis/serving/v1alpha1/revision_types.go
@@ -60,6 +60,9 @@ var (
 
 	// Check that we can create OwnerReferences to a Revision.
 	_ kmeta.OwnerRefable = (*Revision)(nil)
+
+	// Check that the type conforms to the duck Knative Resource shape.
+	_ duckv1.KRShaped = (*Revision)(nil)
 )
 
 // RevisionTemplateSpec describes the data a revision should have when created from a template.
@@ -191,3 +194,8 @@ type RevisionList struct {
 
 	Items []Revision `json:"items"`
 }
+
+// GetStatus retrieves the status of the Revision. Implements the KRShaped interface.
+func (r *Revision) GetStatus() *duckv1.Status {
+	return &r.Status.Status
+}
diff --git a/pkg/apis/serving/v1alpha1/route_lifecycle.go b/pkg/apis/serving/v1alpha1/route_lifecycle.go
index c0aab5547..fd4940805 100644
--- a/pkg/apis/serving/v1alpha1/route_lifecycle.go
+++ b/pkg/apis/serving/v1alpha1/route_lifecycle.go
@@ -32,6 +32,11 @@ var routeCondSet = apis.NewLivingConditionSet(
 	RouteConditionCertificateProvisioned,
 )
 
+// GetConditionSet retrieves the ConditionSet of the Route. Implements the KRShaped interface.
+func (*Route) GetConditionSet() apis.ConditionSet {
+	return routeCondSet
+}
+
 func (r *Route) GetGroupVersionKind() schema.GroupVersionKind {
 	return SchemeGroupVersion.WithKind("Route")
 }
diff --git a/pkg/apis/serving/v1alpha1/route_types.go b/pkg/apis/serving/v1alpha1/route_types.go
index da51e92a8..d4e85ecb6 100644
--- a/pkg/apis/serving/v1alpha1/route_types.go
+++ b/pkg/apis/serving/v1alpha1/route_types.go
@@ -61,6 +61,9 @@ var (
 
 	// Check that we can create OwnerReferences to a Route.
 	_ kmeta.OwnerRefable = (*Route)(nil)
+
+	// Check that the type conforms to the duck Knative Resource shape.
+	_ duckv1.KRShaped = (*Route)(nil)
 )
 
 // TrafficTarget holds a single entry of the routing table for a Route.
@@ -161,3 +164,8 @@ type RouteList struct {
 
 	Items []Route `json:"items"`
 }
+
+// GetStatus retrieves the status of the Route. Implements the KRShaped interface.
+func (r *Route) GetStatus() *duckv1.Status {
+	return &r.Status.Status
+}
diff --git a/pkg/apis/serving/v1alpha1/service_lifecycle.go b/pkg/apis/serving/v1alpha1/service_lifecycle.go
index a93e06a7d..7dda8a4fd 100644
--- a/pkg/apis/serving/v1alpha1/service_lifecycle.go
+++ b/pkg/apis/serving/v1alpha1/service_lifecycle.go
@@ -30,6 +30,11 @@ var serviceCondSet = apis.NewLivingConditionSet(
 	ServiceConditionRoutesReady,
 )
 
+// GetConditionSet retrieves the ConditionSet of the Service. Implements the KRShaped interface.
+func (*Service) GetConditionSet() apis.ConditionSet {
+	return serviceCondSet
+}
+
 // GetGroupVersionKind returns the GetGroupVersionKind.
 func (s *Service) GetGroupVersionKind() schema.GroupVersionKind {
 	return SchemeGroupVersion.WithKind("Service")
diff --git a/pkg/apis/serving/v1alpha1/service_types.go b/pkg/apis/serving/v1alpha1/service_types.go
index 16580a386..2d5aa9133 100644
--- a/pkg/apis/serving/v1alpha1/service_types.go
+++ b/pkg/apis/serving/v1alpha1/service_types.go
@@ -60,6 +60,9 @@ var (
 
 	// Check that we can create OwnerReferences to a Service.
 	_ kmeta.OwnerRefable = (*Service)(nil)
+
+	// Check that the type conforms to the duck Knative Resource shape.
+	_ duckv1.KRShaped = (*Service)(nil)
 )
 
 // ServiceSpec represents the configuration for the Service object. Exactly one
@@ -192,3 +195,8 @@ type ServiceList struct {
 
 	Items []Service `json:"items"`
 }
+
+// GetStatus retrieves the status of the Service. Implements the KRShaped interface.
+func (s *Service) GetStatus() *duckv1.Status {
+	return &s.Status.Status
+}
diff --git a/pkg/autoscaler/metrics/stats_scraper.go b/pkg/autoscaler/metrics/stats_scraper.go
index dc29764f4..45cbaa269 100644
--- a/pkg/autoscaler/metrics/stats_scraper.go
+++ b/pkg/autoscaler/metrics/stats_scraper.go
@@ -102,22 +102,32 @@ type scrapeClient interface {
 	Scrape(url string) (Stat, error)
 }
 
-// cacheDisabledClient is a http client with cache disabled. It is shared by
-// every goruntime for a revision scraper.
-var cacheDisabledClient = &http.Client{
+// noKeepaliveClient is a http client with HTTP Keep-Alive disabled.
+// This client is used in the mesh case since we want to get a new connection -
+// and therefore, hopefully, host - on every scrape of the service.
+var noKeepaliveClient = &http.Client{
 	Transport: &http.Transport{
-		// Do not use the cached connection
 		DisableKeepAlives: true,
 	},
 	Timeout: httpClientTimeout,
 }
 
+// client is a normal http client with HTTP Keep-Alive enabled.
+// This client is used in the direct pod scraping (no mesh) case where we want
+// to take advantage of HTTP Keep-Alive to avoid connection creation overhead
+// between scrapes of the same pod.
+var client = &http.Client{
+	Timeout: httpClientTimeout,
+}
+
 // serviceScraper scrapes Revision metrics via a K8S service by sampling. Which
 // pod to be picked up to serve the request is decided by K8S. Please see
 // https://kubernetes.io/docs/concepts/services-networking/network-policies/
 // for details.
 type serviceScraper struct {
-	sClient  scrapeClient
+	directClient scrapeClient
+	meshClient   scrapeClient
+
 	counter  resources.EndpointsCounter
 	url      string
 	statsCtx context.Context
@@ -131,18 +141,22 @@ type serviceScraper struct {
 // the given Metric is responsible for.
 func NewStatsScraper(metric *av1alpha1.Metric, counter resources.EndpointsCounter,
 	podAccessor resources.PodAccessor, logger *zap.SugaredLogger) (StatsScraper, error) {
-	sClient, err := newHTTPScrapeClient(cacheDisabledClient)
+	directClient, err := newHTTPScrapeClient(client)
 	if err != nil {
 		return nil, err
 	}
-	return newServiceScraperWithClient(metric, counter, podAccessor, sClient, logger)
+	meshClient, err := newHTTPScrapeClient(noKeepaliveClient)
+	if err != nil {
+		return nil, err
+	}
+	return newServiceScraperWithClient(metric, counter, podAccessor, directClient, meshClient, logger)
 }
 
 func newServiceScraperWithClient(
 	metric *av1alpha1.Metric,
 	counter resources.EndpointsCounter,
 	podAccessor resources.PodAccessor,
-	sClient scrapeClient,
+	directClient, meshClient scrapeClient,
 	logger *zap.SugaredLogger) (*serviceScraper, error) {
 	if metric == nil {
 		return nil, errors.New("metric must not be nil")
@@ -150,9 +164,6 @@ func newServiceScraperWithClient(
 	if counter == nil {
 		return nil, errors.New("counter must not be nil")
 	}
-	if sClient == nil {
-		return nil, errors.New("scrape client must not be nil")
-	}
 	revName := metric.Labels[serving.RevisionLabelKey]
 	if revName == "" {
 		return nil, errors.New("no Revision label found for Metric " + metric.Name)
@@ -166,7 +177,8 @@ func newServiceScraperWithClient(
 	}
 
 	return &serviceScraper{
-		sClient:         sClient,
+		directClient:    directClient,
+		meshClient:      meshClient,
 		counter:         counter,
 		url:             urlFromTarget(metric.Spec.ScrapeTarget, metric.ObjectMeta.Namespace),
 		podAccessor:     podAccessor,
@@ -253,7 +265,7 @@ func (s *serviceScraper) scrapePods(readyPods int) (Stat, error) {
 
 				// Scrape!
 				target := "http://" + pods[myIdx] + ":" + portAndPath
-				stat, err := s.sClient.Scrape(target)
+				stat, err := s.directClient.Scrape(target)
 				if err == nil {
 					results <- stat
 					return nil
@@ -388,7 +400,7 @@ func (s *serviceScraper) scrapeService(window time.Duration, readyPods int) (Sta
 // tryScrape runs a single scrape and returns stat if this is a pod that has not been
 // seen before. An error otherwise or if scraping failed.
 func (s *serviceScraper) tryScrape(scrapedPods *sync.Map) (Stat, error) {
-	stat, err := s.sClient.Scrape(s.url)
+	stat, err := s.meshClient.Scrape(s.url)
 	if err != nil {
 		return emptyStat, err
 	}
diff --git a/pkg/autoscaler/scaling/metrics.go b/pkg/autoscaler/scaling/metrics.go
index 3cfa93bfc..f918cf710 100644
--- a/pkg/autoscaler/scaling/metrics.go
+++ b/pkg/autoscaler/scaling/metrics.go
@@ -17,6 +17,7 @@ limitations under the License.
 package scaling
 
 import (
+	pkgmetrics "knative.dev/pkg/metrics"
 	"knative.dev/serving/pkg/metrics"
 
 	"go.opencensus.io/stats"
@@ -70,7 +71,7 @@ func register() {
 	// Create views to see our measurements. This can return an error if
 	// a previously-registered view has the same name with a different value.
 	// View name defaults to the measure name if unspecified.
-	if err := view.Register(
+	if err := pkgmetrics.RegisterResourceView(
 		&view.View{
 			Description: "Number of pods autoscaler wants to allocate",
 			Measure:     desiredPodCountM,
diff --git a/pkg/autoscaler/scaling/multiscaler.go b/pkg/autoscaler/scaling/multiscaler.go
index a7962fe5f..83acb7379 100644
--- a/pkg/autoscaler/scaling/multiscaler.go
+++ b/pkg/autoscaler/scaling/multiscaler.go
@@ -70,6 +70,10 @@ type DeciderSpec struct {
 	StableWindow time.Duration
 	// The name of the k8s service for pod information.
 	ServiceName string
+	// InitialScale is the calculated initial scale of the revision, taking both
+	// revision initial scale and cluster initial scale into account. Revision initial
+	// scale overrides cluster initial scale.
+	InitialScale int32
 }
 
 // DeciderStatus is the current scale recommendation.
diff --git a/pkg/queue/request_metric.go b/pkg/queue/request_metric.go
index 3978b5c7d..dc1a802bb 100644
--- a/pkg/queue/request_metric.go
+++ b/pkg/queue/request_metric.go
@@ -76,7 +76,7 @@ func NewRequestMetricsHandler(next http.Handler,
 	ns, service, config, rev, pod string) (http.Handler, error) {
 	keys := append(metrics.CommonRevisionKeys, metrics.PodTagKey,
 		metrics.ContainerTagKey, metrics.ResponseCodeKey, metrics.ResponseCodeClassKey)
-	if err := view.Register(
+	if err := pkgmetrics.RegisterResourceView(
 		&view.View{
 			Description: "The number of requests that are routed to queue-proxy",
 			Measure:     requestCountM,
@@ -136,7 +136,7 @@ func NewAppRequestMetricsHandler(next http.Handler, b *Breaker,
 	ns, service, config, rev, pod string) (http.Handler, error) {
 	keys := append(metrics.CommonRevisionKeys, metrics.PodTagKey,
 		metrics.ContainerTagKey, metrics.ResponseCodeKey, metrics.ResponseCodeClassKey)
-	if err := view.Register(&view.View{
+	if err := pkgmetrics.RegisterResourceView(&view.View{
 		Description: "The number of requests that are routed to user-container",
 		Measure:     appRequestCountM,
 		Aggregation: view.Count(),
diff --git a/pkg/reconciler/autoscaling/hpa/hpa.go b/pkg/reconciler/autoscaling/hpa/hpa.go
index daca718d1..ee89b83fc 100644
--- a/pkg/reconciler/autoscaling/hpa/hpa.go
+++ b/pkg/reconciler/autoscaling/hpa/hpa.go
@@ -50,14 +50,6 @@ var _ pareconciler.Interface = (*Reconciler)(nil)
 
 func (c *Reconciler) ReconcileKind(ctx context.Context, pa *pav1alpha1.PodAutoscaler) pkgreconciler.Event {
 	logger := logging.FromContext(ctx)
-
-	// We may be reading a version of the object that was stored at an older version
-	// and may not have had all of the assumed defaults specified.  This won't result
-	// in this getting written back to the API Server, but lets downstream logic make
-	// assumptions about defaulting.
-	pa.SetDefaults(ctx)
-
-	pa.Status.InitializeConditions()
 	logger.Debug("PA exists")
 
 	// HPA-class PAs don't yet support scale-to-zero
diff --git a/pkg/reconciler/autoscaling/kpa/kpa.go b/pkg/reconciler/autoscaling/kpa/kpa.go
index 3dbf6b153..ee372a351 100644
--- a/pkg/reconciler/autoscaling/kpa/kpa.go
+++ b/pkg/reconciler/autoscaling/kpa/kpa.go
@@ -71,14 +71,6 @@ var _ pareconciler.Interface = (*Reconciler)(nil)
 func (c *Reconciler) ReconcileKind(ctx context.Context, pa *pav1alpha1.PodAutoscaler) pkgreconciler.Event {
 	logger := logging.FromContext(ctx)
 
-	// We may be reading a version of the object that was stored at an older version
-	// and may not have had all of the assumed defaults specified.  This won't result
-	// in this getting written back to the API Server, but lets downstream logic make
-	// assumptions about defaulting.
-	pa.SetDefaults(ctx)
-
-	pa.Status.InitializeConditions()
-
 	// We need the SKS object in order to optimize scale to zero
 	// performance. It is OK if SKS is nil at this point.
 	sksName := anames.SKS(pa.Name)
diff --git a/pkg/reconciler/autoscaling/kpa/metrics.go b/pkg/reconciler/autoscaling/kpa/metrics.go
index 956e53abf..b84087d1b 100644
--- a/pkg/reconciler/autoscaling/kpa/metrics.go
+++ b/pkg/reconciler/autoscaling/kpa/metrics.go
@@ -17,6 +17,7 @@ limitations under the License.
 package kpa
 
 import (
+	pkgmetrics "knative.dev/pkg/metrics"
 	"knative.dev/serving/pkg/metrics"
 
 	"go.opencensus.io/stats"
@@ -54,7 +55,7 @@ func register() {
 	// Create views to see our measurements. This can return an error if
 	// a previously-registered view has the same name with a different value.
 	// View name defaults to the measure name if unspecified.
-	if err := view.Register(
+	if err := pkgmetrics.RegisterResourceView(
 		&view.View{
 			Description: "Number of pods autoscaler requested from Kubernetes",
 			Measure:     requestedPodCountM,
diff --git a/pkg/reconciler/autoscaling/kpa/resources/decider.go b/pkg/reconciler/autoscaling/kpa/resources/decider.go
index 87cefae0a..9f8566eae 100644
--- a/pkg/reconciler/autoscaling/kpa/resources/decider.go
+++ b/pkg/reconciler/autoscaling/kpa/resources/decider.go
@@ -73,6 +73,19 @@ func MakeDecider(ctx context.Context, pa *v1alpha1.PodAutoscaler, config *autosc
 			PanicThreshold:      panicThreshold,
 			StableWindow:        resources.StableWindow(pa, config),
 			ServiceName:         svc,
+			InitialScale:        GetInitialScale(config, pa),
 		},
 	}
 }
+
+// GetInitialScale returns the calculated initial scale based on the autoscaler
+// ConfigMap and PA initial scale annotation value.
+// TODO(taragu): This function is exported and will be reused in other packages.
+func GetInitialScale(asConfig *autoscalerconfig.Config, pa *v1alpha1.PodAutoscaler) int32 {
+	initialScale := asConfig.InitialScale
+	revisionInitialScale, ok := pa.InitialScale()
+	if !ok || (revisionInitialScale == 0 && !asConfig.AllowZeroInitialScale) {
+		return initialScale
+	}
+	return revisionInitialScale
+}
diff --git a/pkg/reconciler/configuration/configuration.go b/pkg/reconciler/configuration/configuration.go
index f3ddc205b..4a0d8674f 100644
--- a/pkg/reconciler/configuration/configuration.go
+++ b/pkg/reconciler/configuration/configuration.go
@@ -54,13 +54,6 @@ func (c *Reconciler) ReconcileKind(ctx context.Context, config *v1.Configuration
 	logger := logging.FromContext(ctx)
 	recorder := controller.GetEventRecorder(ctx)
 
-	// We may be reading a version of the object that was stored at an older version
-	// and may not have had all of the assumed defaults specified.  This won't result
-	// in this getting written back to the API Server, but lets downstream logic make
-	// assumptions about defaulting.
-	config.SetDefaults(ctx)
-	config.Status.InitializeConditions()
-
 	// First, fetch the revision that should exist for the current generation.
 	lcr, err := c.latestCreatedRevision(config)
 	if errors.IsNotFound(err) {
diff --git a/pkg/reconciler/metric/metric.go b/pkg/reconciler/metric/metric.go
index 000abc3fe..a12e4278f 100644
--- a/pkg/reconciler/metric/metric.go
+++ b/pkg/reconciler/metric/metric.go
@@ -35,9 +35,6 @@ type reconciler struct {
 var _ metricreconciler.Interface = (*reconciler)(nil)
 
 func (r *reconciler) ReconcileKind(ctx context.Context, metric *v1alpha1.Metric) pkgreconciler.Event {
-	metric.SetDefaults(ctx)
-	metric.Status.InitializeConditions()
-
 	if err := r.collector.CreateOrUpdate(metric); err != nil {
 		switch err {
 		case metrics.ErrFailedGetEndpoints:
diff --git a/pkg/reconciler/revision/revision.go b/pkg/reconciler/revision/revision.go
index 19df27cc3..db2018e85 100644
--- a/pkg/reconciler/revision/revision.go
+++ b/pkg/reconciler/revision/revision.go
@@ -128,13 +128,6 @@ func (c *Reconciler) reconcileDigest(ctx context.Context, rev *v1.Revision) erro
 
 func (c *Reconciler) ReconcileKind(ctx context.Context, rev *v1.Revision) pkgreconciler.Event {
 	readyBeforeReconcile := rev.IsReady()
-
-	// We may be reading a version of the object that was stored at an older version
-	// and may not have had all of the assumed defaults specified.  This won't result
-	// in this getting written back to the API Server, but lets downstream logic make
-	// assumptions about defaulting.
-	rev.SetDefaults(ctx)
-	rev.Status.InitializeConditions()
 	c.updateRevisionLoggingURL(ctx, rev)
 
 	phases := []struct {
@@ -160,7 +153,7 @@ func (c *Reconciler) ReconcileKind(ctx context.Context, rev *v1.Revision) pkgrec
 		}
 	}
 
-	readyAfterReconcile := rev.IsReady()
+	readyAfterReconcile := rev.Status.GetCondition(v1.RevisionConditionReady).IsTrue()
 	if !readyBeforeReconcile && readyAfterReconcile {
 		logging.FromContext(ctx).Info("Revision became ready")
 		controller.GetEventRecorder(ctx).Event(
diff --git a/pkg/reconciler/route/route.go b/pkg/reconciler/route/route.go
index 60f18a9a6..09fe04aaa 100644
--- a/pkg/reconciler/route/route.go
+++ b/pkg/reconciler/route/route.go
@@ -104,14 +104,6 @@ func (c *Reconciler) getServices(route *v1.Route) ([]*corev1.Service, error) {
 
 func (c *Reconciler) ReconcileKind(ctx context.Context, r *v1.Route) pkgreconciler.Event {
 	logger := logging.FromContext(ctx)
-
-	// We may be reading a version of the object that was stored at an older version
-	// and may not have had all of the assumed defaults specified.  This won't result
-	// in this getting written back to the API Server, but lets downstream logic make
-	// assumptions about defaulting.
-	r.SetDefaults(ctx)
-	r.Status.InitializeConditions()
-
 	logger.Debugf("Reconciling route: %#v", r)
 
 	// Configure traffic based on the RouteSpec.
diff --git a/pkg/reconciler/serverlessservice/serverlessservice.go b/pkg/reconciler/serverlessservice/serverlessservice.go
index ceb1f79c7..ee20f5e45 100644
--- a/pkg/reconciler/serverlessservice/serverlessservice.go
+++ b/pkg/reconciler/serverlessservice/serverlessservice.go
@@ -69,9 +69,6 @@ func (r *reconciler) ReconcileKind(ctx context.Context, sks *netv1alpha1.Serverl
 		return nil
 	}
 
-	sks.SetDefaults(ctx)
-	sks.Status.InitializeConditions()
-
 	for i, fn := range []func(context.Context, *netv1alpha1.ServerlessService) error{
 		r.reconcilePrivateService, // First make sure our data source is setup.
 		r.reconcilePublicService,
diff --git a/pkg/reconciler/service/service.go b/pkg/reconciler/service/service.go
index 666b2dc7f..12b03e0a0 100644
--- a/pkg/reconciler/service/service.go
+++ b/pkg/reconciler/service/service.go
@@ -63,13 +63,6 @@ var _ ksvcreconciler.Interface = (*Reconciler)(nil)
 func (c *Reconciler) ReconcileKind(ctx context.Context, service *v1.Service) pkgreconciler.Event {
 	logger := logging.FromContext(ctx)
 
-	// We may be reading a version of the object that was stored at an older version
-	// and may not have had all of the assumed defaults specified.  This won't result
-	// in this getting written back to the API Server, but lets downstream logic make
-	// assumptions about defaulting.
-	service.SetDefaults(ctx)
-	service.Status.InitializeConditions()
-
 	config, err := c.config(ctx, logger, service)
 	if err != nil {
 		return err
diff --git a/third_party/cert-manager-0.12.0/net-certmanager.yaml b/third_party/cert-manager-0.12.0/net-certmanager.yaml
index 0c092cf5a..8ce78c9f4 100644
--- a/third_party/cert-manager-0.12.0/net-certmanager.yaml
+++ b/third_party/cert-manager-0.12.0/net-certmanager.yaml
@@ -18,7 +18,7 @@ metadata:
   # These are the permissions needed by the `cert-manager` `Certificate` implementation.
   name: knative-serving-certmanager
   labels:
-    serving.knative.dev/release: "v20200617-65f6d04"
+    serving.knative.dev/release: "v20200618-65f6d04"
     serving.knative.dev/controller: "true"
     networking.knative.dev/certificate-provider: cert-manager
 rules:
@@ -49,7 +49,7 @@ kind: ValidatingWebhookConfiguration
 metadata:
   name: config.webhook.net-certmanager.networking.internal.knative.dev
   labels:
-    serving.knative.dev/release: "v20200617-65f6d04"
+    serving.knative.dev/release: "v20200618-65f6d04"
 webhooks:
 - admissionReviewVersions:
   - v1beta1
@@ -86,7 +86,7 @@ metadata:
   name: net-certmanager-webhook-certs
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200617-65f6d04"
+    serving.knative.dev/release: "v20200618-65f6d04"
 
 ---
 # Copyright 2020 The Knative Authors
@@ -109,7 +109,7 @@ metadata:
   name: config-certmanager
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200617-65f6d04"
+    serving.knative.dev/release: "v20200618-65f6d04"
     networking.knative.dev/certificate-provider: cert-manager
 data:
   _example: |
@@ -156,7 +156,7 @@ metadata:
   name: networking-certmanager
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200617-65f6d04"
+    serving.knative.dev/release: "v20200618-65f6d04"
     networking.knative.dev/certificate-provider: cert-manager
 spec:
   selector:
@@ -168,7 +168,7 @@ spec:
         cluster-autoscaler.kubernetes.io/safe-to-evict: "true"
       labels:
         app: networking-certmanager
-        serving.knative.dev/release: "v20200617-65f6d04"
+        serving.knative.dev/release: "v20200618-65f6d04"
     spec:
       serviceAccountName: controller
       containers:
@@ -208,7 +208,7 @@ kind: Service
 metadata:
   labels:
     app: networking-certmanager
-    serving.knative.dev/release: "v20200617-65f6d04"
+    serving.knative.dev/release: "v20200618-65f6d04"
     networking.knative.dev/certificate-provider: cert-manager
   name: networking-certmanager
   namespace: knative-serving
@@ -245,7 +245,7 @@ metadata:
   name: net-certmanager-webhook
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200617-65f6d04"
+    serving.knative.dev/release: "v20200618-65f6d04"
 spec:
   selector:
     matchLabels:
@@ -258,7 +258,7 @@ spec:
       labels:
         app: net-certmanager-webhook
         role: net-certmanager-webhook
-        serving.knative.dev/release: "v20200617-65f6d04"
+        serving.knative.dev/release: "v20200618-65f6d04"
     spec:
       serviceAccountName: controller
       containers:
@@ -319,7 +319,7 @@ metadata:
   namespace: knative-serving
   labels:
     role: net-certmanager-webhook
-    serving.knative.dev/release: "v20200617-65f6d04"
+    serving.knative.dev/release: "v20200618-65f6d04"
 spec:
   ports:
   - # Define metrics and profiling for them to be accessible within service meshes.
diff --git a/third_party/net-istio.yaml b/third_party/net-istio.yaml
index 568d84baa..f0e585189 100644
--- a/third_party/net-istio.yaml
+++ b/third_party/net-istio.yaml
@@ -18,7 +18,7 @@ metadata:
   # These are the permissions needed by the Istio Ingress implementation.
   name: knative-serving-istio
   labels:
-    serving.knative.dev/release: "v20200617-f2d21fb"
+    serving.knative.dev/release: "v20200618-a1e2b03"
     serving.knative.dev/controller: "true"
     networking.knative.dev/ingress-provider: istio
 rules:
@@ -48,7 +48,7 @@ metadata:
   name: knative-ingress-gateway
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200617-f2d21fb"
+    serving.knative.dev/release: "v20200618-a1e2b03"
     networking.knative.dev/ingress-provider: istio
 spec:
   selector:
@@ -85,7 +85,7 @@ metadata:
   name: cluster-local-gateway
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200617-f2d21fb"
+    serving.knative.dev/release: "v20200618-a1e2b03"
     networking.knative.dev/ingress-provider: istio
 spec:
   selector:
@@ -118,7 +118,7 @@ kind: MutatingWebhookConfiguration
 metadata:
   name: webhook.istio.networking.internal.knative.dev
   labels:
-    serving.knative.dev/release: "v20200617-f2d21fb"
+    serving.knative.dev/release: "v20200618-a1e2b03"
     networking.knative.dev/ingress-provider: istio
 webhooks:
 - admissionReviewVersions:
@@ -154,7 +154,7 @@ kind: ValidatingWebhookConfiguration
 metadata:
   name: config.webhook.istio.networking.internal.knative.dev
   labels:
-    serving.knative.dev/release: "v20200617-f2d21fb"
+    serving.knative.dev/release: "v20200618-a1e2b03"
     networking.knative.dev/ingress-provider: istio
 webhooks:
 - admissionReviewVersions:
@@ -192,7 +192,7 @@ metadata:
   name: istio-webhook-certs
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200617-f2d21fb"
+    serving.knative.dev/release: "v20200618-a1e2b03"
     networking.knative.dev/ingress-provider: istio
 
 ---
@@ -216,7 +216,7 @@ metadata:
   name: config-istio
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200617-f2d21fb"
+    serving.knative.dev/release: "v20200618-a1e2b03"
     networking.knative.dev/ingress-provider: istio
 data:
   _example: |
@@ -286,7 +286,7 @@ metadata:
   name: networking-istio
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200617-f2d21fb"
+    serving.knative.dev/release: "v20200618-a1e2b03"
     networking.knative.dev/ingress-provider: istio
 spec:
   selector:
@@ -302,14 +302,14 @@ spec:
         sidecar.istio.io/inject: "false"
       labels:
         app: networking-istio
-        serving.knative.dev/release: "v20200617-f2d21fb"
+        serving.knative.dev/release: "v20200618-a1e2b03"
     spec:
       serviceAccountName: controller
       containers:
       - name: networking-istio
         # This is the Go import path for the binary that is containerized
         # and substituted here.
-        image: gcr.io/knative-nightly/knative.dev/net-istio/cmd/controller@sha256:56b3796eade3f1c51919aceca3c4d759428577ab3915399e9593a007c5f7fbd9
+        image: gcr.io/knative-nightly/knative.dev/net-istio/cmd/controller@sha256:05c5fd63bdac0f015789fd4798bd682d6c663a7e0689358308f798ed5b3ca362
         resources:
           requests:
             cpu: 30m
@@ -361,7 +361,7 @@ metadata:
   name: istio-webhook
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200617-f2d21fb"
+    serving.knative.dev/release: "v20200618-a1e2b03"
     networking.knative.dev/ingress-provider: istio
 spec:
   selector:
@@ -375,14 +375,14 @@ spec:
       labels:
         app: istio-webhook
         role: istio-webhook
-        serving.knative.dev/release: "v20200617-f2d21fb"
+        serving.knative.dev/release: "v20200618-a1e2b03"
     spec:
       serviceAccountName: controller
       containers:
       - name: webhook
         # This is the Go import path for the binary that is containerized
         # and substituted here.
-        image: gcr.io/knative-nightly/knative.dev/net-istio/cmd/webhook@sha256:18fcc4e2662639e6885b60700539388eca825c7b967c31508e34c69bc529cf97
+        image: gcr.io/knative-nightly/knative.dev/net-istio/cmd/webhook@sha256:2a255fd368dbb4cb01533a0bf9147e148177d18360e1e59cf2cc63f7353f5f1d
         resources:
           requests:
             cpu: 20m
@@ -436,7 +436,7 @@ metadata:
   namespace: knative-serving
   labels:
     role: istio-webhook
-    serving.knative.dev/release: "v20200617-f2d21fb"
+    serving.knative.dev/release: "v20200618-a1e2b03"
     networking.knative.dev/ingress-provider: istio
 spec:
   ports:
diff --git a/vendor/knative.dev/pkg/hack/tools.go b/vendor/knative.dev/pkg/hack/tools.go
index f37c08577..22763c5a8 100644
--- a/vendor/knative.dev/pkg/hack/tools.go
+++ b/vendor/knative.dev/pkg/hack/tools.go
@@ -26,9 +26,7 @@ import (
 	_ "k8s.io/code-generator/cmd/defaulter-gen"
 	_ "k8s.io/code-generator/cmd/informer-gen"
 	_ "k8s.io/code-generator/cmd/lister-gen"
-	_ "k8s.io/kube-openapi/cmd/openapi-gen"
 
 	_ "knative.dev/pkg/codegen/cmd/injection-gen"
-
 	_ "knative.dev/test-infra/scripts"
 )
diff --git a/vendor/knative.dev/pkg/metrics/config.go b/vendor/knative.dev/pkg/metrics/config.go
index b9c5aa29a..d44a22f62 100644
--- a/vendor/knative.dev/pkg/metrics/config.go
+++ b/vendor/knative.dev/pkg/metrics/config.go
@@ -37,39 +37,44 @@ import (
 type metricsBackend string
 
 const (
+	// BackendDestinationKey points to the config map entry key for metrics backend destination.
+	BackendDestinationKey = "metrics.backend-destination"
+	// DomainEnv points to the metrics domain env var.
+	DomainEnv = "METRICS_DOMAIN"
+
 	// The following keys are used to configure metrics reporting.
 	// See https://github.com/knative/serving/blob/master/config/config-observability.yaml
 	// for details.
-	AllowStackdriverCustomMetricsKey    = "metrics.allow-stackdriver-custom-metrics"
-	BackendDestinationKey               = "metrics.backend-destination"
-	ReportingPeriodKey                  = "metrics.reporting-period-seconds"
-	StackdriverCustomMetricSubDomainKey = "metrics.stackdriver-custom-metrics-subdomain"
-	// Stackdriver client configuration keys
-	StackdriverProjectIDKey   = "metrics.stackdriver-project-id"
-	StackdriverGCPLocationKey = "metrics.stackdriver-gcp-location"
-	StackdriverClusterNameKey = "metrics.stackdriver-cluster-name"
-	StackdriverUseSecretKey   = "metrics.stackdriver-use-secret"
+	allowStackdriverCustomMetricsKey = "metrics.allow-stackdriver-custom-metrics"
+	collectorAddressKey              = "metrics.opencensus-address"
+	collectorSecureKey               = "metrics.opencensus-require-tls"
+	reportingPeriodKey               = "metrics.reporting-period-seconds"
 
-	DomainEnv = "METRICS_DOMAIN"
-
-	// Stackdriver is used for Stackdriver backend
-	Stackdriver metricsBackend = "stackdriver"
-	// Prometheus is used for Prometheus backend
-	Prometheus metricsBackend = "prometheus"
-	// OpenCensus is used to export to the OpenCensus Agent / Collector,
-	// which can send to many other services.
-	OpenCensus metricsBackend = "opencensus"
-	// None is used to export, well, nothing.
-	None metricsBackend = "none"
+	// Stackdriver client configuration keys
+	stackdriverClusterNameKey           = "metrics.stackdriver-cluster-name"
+	stackdriverCustomMetricSubDomainKey = "metrics.stackdriver-custom-metrics-subdomain"
+	stackdriverGCPLocationKey           = "metrics.stackdriver-gcp-location"
+	stackdriverProjectIDKey             = "metrics.stackdriver-project-id"
+	stackdriverUseSecretKey             = "metrics.stackdriver-use-secret"
 
 	defaultBackendEnvName = "DEFAULT_METRICS_BACKEND"
-
-	CollectorAddressKey = "metrics.opencensus-address"
-	CollectorSecureKey  = "metrics.opencensus-require-tls"
-
 	defaultPrometheusPort = 9090
 	maxPrometheusPort     = 65535
 	minPrometheusPort     = 1024
+	prometheusPortEnvName = "METRICS_PROMETHEUS_PORT"
+)
+
+// Metrics backend "enum".
+const (
+	// stackdriver is used for Stackdriver backend
+	stackdriver metricsBackend = "stackdriver"
+	// prometheus is used for Prometheus backend
+	prometheus metricsBackend = "prometheus"
+	// openCensus is used to export to the OpenCensus Agent / Collector,
+	// which can send to many other services.
+	openCensus metricsBackend = "opencensus"
+	// none is used to export, well, nothing.
+	none metricsBackend = "none"
 )
 
 type metricsConfig struct {
@@ -141,10 +146,10 @@ type StackdriverClientConfig struct {
 // NewStackdriverClientConfigFromMap creates a stackdriverClientConfig from the given map
 func NewStackdriverClientConfigFromMap(config map[string]string) *StackdriverClientConfig {
 	return &StackdriverClientConfig{
-		ProjectID:   config[StackdriverProjectIDKey],
-		GCPLocation: config[StackdriverGCPLocationKey],
-		ClusterName: config[StackdriverClusterNameKey],
-		UseSecret:   strings.EqualFold(config[StackdriverUseSecretKey], "true"),
+		ProjectID:   config[stackdriverProjectIDKey],
+		GCPLocation: config[stackdriverGCPLocationKey],
+		ClusterName: config[stackdriverClusterNameKey],
+		UseSecret:   strings.EqualFold(config[stackdriverUseSecretKey], "true"),
 	}
 }
 
@@ -183,7 +188,7 @@ func createMetricsConfig(ops ExporterOptions, logger *zap.SugaredLogger) (*metri
 	backend := os.Getenv(defaultBackendEnvName)
 	if backend == "" {
 		// Use Prometheus if DEFAULT_METRICS_BACKEND does not exist or is empty
-		backend = string(Prometheus)
+		backend = string(prometheus)
 	}
 	// Override backend if it is set in the config map.
 	if backendFromConfig, ok := m[BackendDestinationKey]; ok {
@@ -191,18 +196,18 @@ func createMetricsConfig(ops ExporterOptions, logger *zap.SugaredLogger) (*metri
 	}
 	lb := metricsBackend(strings.ToLower(backend))
 	switch lb {
-	case Stackdriver, Prometheus, OpenCensus:
+	case stackdriver, prometheus, openCensus:
 		mc.backendDestination = lb
 	default:
 		return nil, fmt.Errorf("unsupported metrics backend value %q", backend)
 	}
 
-	if mc.backendDestination == OpenCensus {
-		mc.collectorAddress = ops.ConfigMap[CollectorAddressKey]
-		if isSecure := ops.ConfigMap[CollectorSecureKey]; isSecure != "" {
+	if mc.backendDestination == openCensus {
+		mc.collectorAddress = ops.ConfigMap[collectorAddressKey]
+		if isSecure := ops.ConfigMap[collectorSecureKey]; isSecure != "" {
 			var err error
 			if mc.requireSecure, err = strconv.ParseBool(isSecure); err != nil {
-				return nil, fmt.Errorf("invalid %s value %q", CollectorSecureKey, isSecure)
+				return nil, fmt.Errorf("invalid %s value %q", collectorSecureKey, isSecure)
 			}
 
 			if mc.requireSecure {
@@ -214,21 +219,28 @@ func createMetricsConfig(ops ExporterOptions, logger *zap.SugaredLogger) (*metri
 		}
 	}
 
-	if mc.backendDestination == Prometheus {
+	if mc.backendDestination == prometheus {
 		pp := ops.PrometheusPort
 		if pp == 0 {
-			pp = defaultPrometheusPort
+			var err error
+			pp, err = prometheusPort()
+			if err != nil {
+				return nil, fmt.Errorf("failed to determine Prometheus port: %w", err)
+			}
 		}
+
 		if pp < minPrometheusPort || pp > maxPrometheusPort {
-			return nil, fmt.Errorf("invalid port %v, should between %v and %v", pp, minPrometheusPort, maxPrometheusPort)
+			return nil, fmt.Errorf("invalid port %d, should be between %d and %d",
+				pp, minPrometheusPort, maxPrometheusPort)
 		}
+
 		mc.prometheusPort = pp
 	}
 
 	// If stackdriverClientConfig is not provided for stackdriver backend destination, OpenCensus will try to
 	// use the application default credentials. If that is not available, Opencensus would fail to create the
 	// metrics exporter.
-	if mc.backendDestination == Stackdriver {
+	if mc.backendDestination == stackdriver {
 		scc := NewStackdriverClientConfigFromMap(m)
 		mc.stackdriverClientConfig = *scc
 		mc.isStackdriverBackend = true
@@ -236,15 +248,15 @@ func createMetricsConfig(ops ExporterOptions, logger *zap.SugaredLogger) (*metri
 		var err error
 		mc.stackdriverMetricTypePrefix = path.Join(mc.domain, mc.component)
 
-		customMetricsSubDomain := m[StackdriverCustomMetricSubDomainKey]
+		customMetricsSubDomain := m[stackdriverCustomMetricSubDomainKey]
 		if customMetricsSubDomain == "" {
 			customMetricsSubDomain = defaultCustomMetricSubDomain
 		}
 		mc.stackdriverCustomMetricTypePrefix = path.Join(customMetricTypePrefix, customMetricsSubDomain, mc.component)
-		if ascmStr := m[AllowStackdriverCustomMetricsKey]; ascmStr != "" {
+		if ascmStr := m[allowStackdriverCustomMetricsKey]; ascmStr != "" {
 			allowCustomMetrics, err = strconv.ParseBool(ascmStr)
 			if err != nil {
-				return nil, fmt.Errorf("invalid %s value %q", AllowStackdriverCustomMetricsKey, ascmStr)
+				return nil, fmt.Errorf("invalid %s value %q", allowStackdriverCustomMetricsKey, ascmStr)
 			}
 		}
 
@@ -289,15 +301,15 @@ func createMetricsConfig(ops ExporterOptions, logger *zap.SugaredLogger) (*metri
 	// For Prometheus, we will use a lower value since the exporter doesn't
 	// push anything but just responds to pull requests, and shorter durations
 	// do not really hurt the performance and we rely on the scraping configuration.
-	if repStr, ok := m[ReportingPeriodKey]; ok && repStr != "" {
+	if repStr, ok := m[reportingPeriodKey]; ok && repStr != "" {
 		repInt, err := strconv.Atoi(repStr)
 		if err != nil {
-			return nil, fmt.Errorf("invalid %s value %q", ReportingPeriodKey, repStr)
+			return nil, fmt.Errorf("invalid %s value %q", reportingPeriodKey, repStr)
 		}
 		mc.reportingPeriod = time.Duration(repInt) * time.Second
-	} else if mc.backendDestination == Stackdriver {
+	} else if mc.backendDestination == stackdriver {
 		mc.reportingPeriod = 60 * time.Second
-	} else if mc.backendDestination == Prometheus {
+	} else if mc.backendDestination == prometheus {
 		mc.reportingPeriod = 5 * time.Second
 	}
 
@@ -327,6 +339,25 @@ import (
 )`, DomainEnv, DomainEnv))
 }
 
+// prometheusPort returns the TCP port number configured via the environment
+// for the Prometheus metrics exporter if it's set, a default value otherwise.
+// No validation is performed on the port value, other than ensuring that value
+// is a valid port number (16-bit unsigned integer).
+func prometheusPort() (int, error) {
+	ppStr := os.Getenv(prometheusPortEnvName)
+	if ppStr == "" {
+		return defaultPrometheusPort, nil
+	}
+
+	pp, err := strconv.ParseUint(ppStr, 10, 16)
+	if err != nil {
+		return -1, fmt.Errorf("the environment variable %q could not be parsed as a port number: %w",
+			prometheusPortEnvName, err)
+	}
+
+	return int(pp), nil
+}
+
 // JsonToMetricsOptions converts a json string of a
 // ExporterOptions. Returns a non-nil ExporterOptions always.
 func JsonToMetricsOptions(jsonOpts string) (*ExporterOptions, error) {
diff --git a/vendor/knative.dev/pkg/metrics/exporter.go b/vendor/knative.dev/pkg/metrics/exporter.go
index b8f4fd12f..4c2479382 100644
--- a/vendor/knative.dev/pkg/metrics/exporter.go
+++ b/vendor/knative.dev/pkg/metrics/exporter.go
@@ -85,6 +85,26 @@ func UpdateExporterFromConfigMap(component string, logger *zap.SugaredLogger) fu
 	return ConfigMapWatcher(component, nil, logger)
 }
 
+// RegisterResourceView is similar to view.Register(), except that it will
+// register the view across all Resources tracked by the system, rather than
+// simply the default view.
+// this is a placeholder for real implementation in https://github.com/knative/pkg/pull/1392.
+// That PR will introduce a breaking change, as we need to convert some view.Register to RegisterResourceView in
+// all callers, in serving, eventing, and eventing-contrib.
+// Since that PR is huge, a better approach is to introduce the breaking change up front, by creating this
+// passthrough method, and fixing all the callers before merging that PR.
+func RegisterResourceView(views ...*view.View) error {
+	return view.Register(views...)
+}
+
+// UnregisterResourceView is similar to view.unRegister(), except that it will
+// unregister the view across all Resources tracked by the system, rather than
+// simply the default view.
+// this is a placeholder for real implementation in https://github.com/knative/pkg/pull/1392.
+func UnregisterResourceView(views ...*view.View) {
+	view.Unregister(views...)
+}
+
 // ConfigMapWatcher returns a helper func which updates the exporter configuration based on
 // values in the supplied ConfigMap. This method captures a corev1.SecretLister which is used
 // to configure mTLS with the opencensus agent.
@@ -176,11 +196,11 @@ func isNewExporterRequired(newConfig *metricsConfig) bool {
 
 	// If the OpenCensus address has changed, restart the exporter.
 	// TODO(evankanderson): Should we just always restart the opencensus agent?
-	if newConfig.backendDestination == OpenCensus {
+	if newConfig.backendDestination == openCensus {
 		return newConfig.collectorAddress != cc.collectorAddress || newConfig.requireSecure != cc.requireSecure
 	}
 
-	return newConfig.backendDestination == Stackdriver && newConfig.stackdriverClientConfig != cc.stackdriverClientConfig
+	return newConfig.backendDestination == stackdriver && newConfig.stackdriverClientConfig != cc.stackdriverClientConfig
 }
 
 // newMetricsExporter gets a metrics exporter based on the config.
@@ -198,13 +218,13 @@ func newMetricsExporter(config *metricsConfig, logger *zap.SugaredLogger) (view.
 	var err error
 	var e view.Exporter
 	switch config.backendDestination {
-	case OpenCensus:
+	case openCensus:
 		e, err = newOpenCensusExporter(config, logger)
-	case Stackdriver:
+	case stackdriver:
 		e, err = newStackdriverExporter(config, logger)
-	case Prometheus:
+	case prometheus:
 		e, err = newPrometheusExporter(config, logger)
-	case None:
+	case none:
 		e, err = nil, nil
 	default:
 		err = fmt.Errorf("unsupported metrics backend %v", config.backendDestination)
diff --git a/vendor/knative.dev/pkg/metrics/prometheus_exporter.go b/vendor/knative.dev/pkg/metrics/prometheus_exporter.go
index 8c3f462ac..c8a7b73d9 100644
--- a/vendor/knative.dev/pkg/metrics/prometheus_exporter.go
+++ b/vendor/knative.dev/pkg/metrics/prometheus_exporter.go
@@ -21,7 +21,7 @@ import (
 	"net/http"
 	"sync"
 
-	"contrib.go.opencensus.io/exporter/prometheus"
+	prom "contrib.go.opencensus.io/exporter/prometheus"
 	"go.opencensus.io/stats/view"
 	"go.uber.org/zap"
 )
@@ -32,7 +32,7 @@ var (
 )
 
 func newPrometheusExporter(config *metricsConfig, logger *zap.SugaredLogger) (view.Exporter, error) {
-	e, err := prometheus.NewExporter(prometheus.Options{Namespace: config.component})
+	e, err := prom.NewExporter(prom.Options{Namespace: config.component})
 	if err != nil {
 		logger.Errorw("Failed to create the Prometheus exporter.", zap.Error(err))
 		return nil, err
@@ -61,7 +61,7 @@ func resetCurPromSrv() {
 	}
 }
 
-func startNewPromSrv(e *prometheus.Exporter, port int) *http.Server {
+func startNewPromSrv(e *prom.Exporter, port int) *http.Server {
 	sm := http.NewServeMux()
 	sm.Handle("/metrics", e)
 	curPromSrvMux.Lock()
diff --git a/vendor/knative.dev/pkg/metrics/stackdriver_exporter.go b/vendor/knative.dev/pkg/metrics/stackdriver_exporter.go
index e4c2d08dc..5974e6bcf 100644
--- a/vendor/knative.dev/pkg/metrics/stackdriver_exporter.go
+++ b/vendor/knative.dev/pkg/metrics/stackdriver_exporter.go
@@ -21,7 +21,7 @@ import (
 	"path"
 	"sync"
 
-	"contrib.go.opencensus.io/exporter/stackdriver"
+	sd "contrib.go.opencensus.io/exporter/stackdriver"
 	"contrib.go.opencensus.io/exporter/stackdriver/monitoredresource"
 	"go.opencensus.io/metric/metricdata"
 	"go.opencensus.io/stats/view"
@@ -60,7 +60,7 @@ var (
 	// In product usage, this is always set to function newOpencensusSDExporter.
 	// In unit tests this is set to a fake one to avoid calling actual Google API
 	// service.
-	newStackdriverExporterFunc func(stackdriver.Options) (view.Exporter, error)
+	newStackdriverExporterFunc func(sd.Options) (view.Exporter, error)
 
 	// kubeclient is the in-cluster Kubernetes kubeclient, which is lazy-initialized on first use.
 	kubeclient *kubernetes.Clientset
@@ -100,8 +100,8 @@ func init() {
 	kubeclientInitErr = nil
 }
 
-func newOpencensusSDExporter(o stackdriver.Options) (view.Exporter, error) {
-	e, err := stackdriver.NewExporter(o)
+func newOpencensusSDExporter(o sd.Options) (view.Exporter, error) {
+	e, err := sd.NewExporter(o)
 	if err != nil {
 		return nil, err
 	}
@@ -123,7 +123,7 @@ func newStackdriverExporter(config *metricsConfig, logger *zap.SugaredLogger) (v
 		logger.Warnw("Issue configuring Stackdriver exporter client options, no additional client options will be used: ", zap.Error(err))
 	}
 	// Automatically fall back on Google application default credentials
-	e, err := newStackdriverExporterFunc(stackdriver.Options{
+	e, err := newStackdriverExporterFunc(sd.Options{
 		ProjectID:               gm.project,
 		Location:                gm.location,
 		MonitoringClientOptions: co,
@@ -131,7 +131,7 @@ func newStackdriverExporter(config *metricsConfig, logger *zap.SugaredLogger) (v
 		GetMetricPrefix:         mpf,
 		ResourceByDescriptor:    getResourceByDescriptorFunc(config.stackdriverMetricTypePrefix, gm),
 		ReportingInterval:       config.reportingPeriod,
-		DefaultMonitoringLabels: &stackdriver.Labels{},
+		DefaultMonitoringLabels: &sd.Labels{},
 	})
 	if err != nil {
 		logger.Errorw("Failed to create the Stackdriver exporter: ", zap.Error(err))
diff --git a/vendor/knative.dev/pkg/network/transports.go b/vendor/knative.dev/pkg/network/transports.go
index 745e6a5a8..dcb07a175 100644
--- a/vendor/knative.dev/pkg/network/transports.go
+++ b/vendor/knative.dev/pkg/network/transports.go
@@ -96,15 +96,15 @@ func newHTTPTransport(connTimeout time.Duration, disableKeepAlives bool) http.Ro
 	return &http.Transport{
 		// Those match net/http/transport.go
 		Proxy:                 http.ProxyFromEnvironment,
-		MaxIdleConns:          1000,
-		MaxIdleConnsPerHost:   100,
-		IdleConnTimeout:       5 * time.Second,
+		IdleConnTimeout:       90 * time.Second,
 		TLSHandshakeTimeout:   10 * time.Second,
 		ExpectContinueTimeout: 1 * time.Second,
 		DisableKeepAlives:     disableKeepAlives,
 
-		// This is bespoke.
-		DialContext: DialWithBackOff,
+		// Those are bespoke.
+		DialContext:         DialWithBackOff,
+		MaxIdleConns:        1000,
+		MaxIdleConnsPerHost: 100,
 	}
 }
 
diff --git a/vendor/knative.dev/pkg/reconciler/reconcile_common.go b/vendor/knative.dev/pkg/reconciler/reconcile_common.go
index 6a32c06f7..c9f8b5cb8 100644
--- a/vendor/knative.dev/pkg/reconciler/reconcile_common.go
+++ b/vendor/knative.dev/pkg/reconciler/reconcile_common.go
@@ -19,6 +19,7 @@ package reconciler
 import (
 	"context"
 
+	"knative.dev/pkg/apis"
 	duckv1 "knative.dev/pkg/apis/duck/v1"
 	"knative.dev/pkg/logging"
 )
@@ -29,12 +30,20 @@ const failedGenerationBump = "NewObservedGenFailure"
 func PreProcessReconcile(ctx context.Context, resource duckv1.KRShaped) {
 	newStatus := resource.GetStatus()
 
-	if newStatus.ObservedGeneration != resource.GetGeneration() {
-		condSet := resource.GetConditionSet()
-		manager := condSet.Manage(newStatus)
-		// Ensure conditions are initialized before we modify.
-		manager.InitializeConditions()
+	// We may be reading a version of the object that was stored at an older version
+	// and may not have had all of the assumed defaults specified.  This won't result
+	// in this getting written back to the API Server, but lets downstream logic make
+	// assumptions about defaulting.
+	if d, ok := resource.(apis.Defaultable); ok {
+		d.SetDefaults(ctx)
+	}
+
+	// Ensure conditions are initialized before we modify.
+	condSet := resource.GetConditionSet()
+	manager := condSet.Manage(newStatus)
+	manager.InitializeConditions()
 
+	if newStatus.ObservedGeneration != resource.GetGeneration() {
 		// Reset Ready/Successful to unknown. The reconciler is expected to overwrite this.
 		manager.MarkUnknown(condSet.GetTopLevelConditionType(), failedGenerationBump, "unsuccessfully observed a new generation")
 	}
@@ -43,12 +52,12 @@ func PreProcessReconcile(ctx context.Context, resource duckv1.KRShaped) {
 // PostProcessReconcile contains logic to apply after reconciliation of a resource.
 func PostProcessReconcile(ctx context.Context, resource duckv1.KRShaped) {
 	logger := logging.FromContext(ctx)
-	newStatus := resource.GetStatus()
-	mgr := resource.GetConditionSet().Manage(newStatus)
+	status := resource.GetStatus()
+	mgr := resource.GetConditionSet().Manage(status)
 
 	// Bump observed generation to denote that we have processed this
 	// generation regardless of success or failure.
-	newStatus.ObservedGeneration = resource.GetGeneration()
+	status.ObservedGeneration = resource.GetGeneration()
 
 	if rc := mgr.GetTopLevelCondition(); rc == nil {
 		logger.Warn("A reconciliation included no top-level condition")
diff --git a/vendor/knative.dev/pkg/webhook/resourcesemantics/defaulting/defaulting.go b/vendor/knative.dev/pkg/webhook/resourcesemantics/defaulting/defaulting.go
index 75bfa9fd6..d84e6ed9c 100644
--- a/vendor/knative.dev/pkg/webhook/resourcesemantics/defaulting/defaulting.go
+++ b/vendor/knative.dev/pkg/webhook/resourcesemantics/defaulting/defaulting.go
@@ -30,6 +30,7 @@ import (
 	jsonpatch "gomodules.xyz/jsonpatch/v2"
 	admissionv1beta1 "k8s.io/api/admission/v1beta1"
 	admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime/schema"
 	"k8s.io/client-go/kubernetes"
 	admissionlisters "k8s.io/client-go/listers/admissionregistration/v1beta1"
@@ -172,6 +173,12 @@ func (ac *reconciler) reconcileMutatingWebhook(ctx context.Context, caCert []byt
 			continue
 		}
 		webhook.Webhooks[i].Rules = rules
+		webhook.Webhooks[i].NamespaceSelector = &metav1.LabelSelector{
+			MatchExpressions: []metav1.LabelSelectorRequirement{{
+				Key:      "webhooks.knative.dev/exclude",
+				Operator: metav1.LabelSelectorOpDoesNotExist,
+			}},
+		}
 		webhook.Webhooks[i].ClientConfig.CABundle = caCert
 		if webhook.Webhooks[i].ClientConfig.Service == nil {
 			return fmt.Errorf("missing service reference for webhook: %s", wh.Name)
diff --git a/vendor/knative.dev/pkg/webhook/resourcesemantics/validation/reconcile_config.go b/vendor/knative.dev/pkg/webhook/resourcesemantics/validation/reconcile_config.go
index e1b1b0e1e..c17db6281 100644
--- a/vendor/knative.dev/pkg/webhook/resourcesemantics/validation/reconcile_config.go
+++ b/vendor/knative.dev/pkg/webhook/resourcesemantics/validation/reconcile_config.go
@@ -25,6 +25,7 @@ import (
 	"github.com/markbates/inflect"
 	"go.uber.org/zap"
 	admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime/schema"
 	"k8s.io/client-go/kubernetes"
 	admissionlisters "k8s.io/client-go/listers/admissionregistration/v1beta1"
@@ -135,6 +136,12 @@ func (ac *reconciler) reconcileValidatingWebhook(ctx context.Context, caCert []b
 			continue
 		}
 		webhook.Webhooks[i].Rules = rules
+		webhook.Webhooks[i].NamespaceSelector = &metav1.LabelSelector{
+			MatchExpressions: []metav1.LabelSelectorRequirement{{
+				Key:      "webhooks.knative.dev/exclude",
+				Operator: metav1.LabelSelectorOpDoesNotExist,
+			}},
+		}
 		webhook.Webhooks[i].ClientConfig.CABundle = caCert
 		if webhook.Webhooks[i].ClientConfig.Service == nil {
 			return fmt.Errorf("missing service reference for webhook: %s", wh.Name)
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 3dd3630b2..2a6b6c272 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -1200,7 +1200,7 @@ knative.dev/caching/pkg/client/injection/informers/caching/v1alpha1/image/fake
 knative.dev/caching/pkg/client/injection/informers/factory
 knative.dev/caching/pkg/client/injection/informers/factory/fake
 knative.dev/caching/pkg/client/listers/caching/v1alpha1
-# knative.dev/networking v0.0.0-20200611155523-bd13675bcf70
+# knative.dev/networking v0.0.0-20200617160325-c5b1bddf865c
 ## explicit
 knative.dev/networking/pkg/apis/config
 knative.dev/networking/pkg/apis/networking
@@ -1227,7 +1227,7 @@ knative.dev/networking/pkg/client/injection/informers/networking/v1alpha1/server
 knative.dev/networking/pkg/client/injection/reconciler/networking/v1alpha1/serverlessservice
 knative.dev/networking/pkg/client/istio/listers/networking/v1alpha3
 knative.dev/networking/pkg/client/listers/networking/v1alpha1
-# knative.dev/pkg v0.0.0-20200615224524-dbd32a77f41c
+# knative.dev/pkg v0.0.0-20200617194224-40c7e164ccd7
 ## explicit
 knative.dev/pkg/apiextensions/storageversion
 knative.dev/pkg/apiextensions/storageversion/cmd/migrate
@@ -1344,7 +1344,7 @@ knative.dev/pkg/webhook/resourcesemantics/conversion
 knative.dev/pkg/webhook/resourcesemantics/defaulting
 knative.dev/pkg/webhook/resourcesemantics/validation
 knative.dev/pkg/websocket
-# knative.dev/test-infra v0.0.0-20200612191923-a4dd434e085b
+# knative.dev/test-infra v0.0.0-20200617184924-0fc4e2aeef74
 ## explicit
 knative.dev/test-infra/scripts
 # sigs.k8s.io/structured-merge-diff/v2 v2.0.1


Test patch:
diff --git a/pkg/apis/autoscaling/v1alpha1/pa_lifecycle_test.go b/pkg/apis/autoscaling/v1alpha1/pa_lifecycle_test.go
index 353c46d9c..5ab08c904 100644
--- a/pkg/apis/autoscaling/v1alpha1/pa_lifecycle_test.go
+++ b/pkg/apis/autoscaling/v1alpha1/pa_lifecycle_test.go
@@ -667,12 +667,6 @@ func TestScaleBounds(t *testing.T) {
 		max:     "sandwich",
 		wantMin: 0,
 		wantMax: 0,
-	}, {
-		name:    "too small",
-		min:     "-1",
-		max:     "-1",
-		wantMin: 0,
-		wantMax: 0,
 	}}
 
 	for _, tc := range cases {
@@ -1143,3 +1137,37 @@ func TestScaleToZeroPodRetention(t *testing.T) {
 		})
 	}
 }
+
+func TestInitialScale(t *testing.T) {
+	cases := []struct {
+		name   string
+		pa     *PodAutoscaler
+		want   int32
+		wantOK bool
+	}{{
+		name: "nil",
+		pa:   pa(nil),
+	}, {
+		name: "not present",
+		pa:   pa(map[string]string{}),
+	}, {
+		name: "present",
+		pa: pa(map[string]string{
+			autoscaling.InitialScaleAnnotationKey: "2",
+		}),
+		want:   2,
+		wantOK: true,
+	}}
+
+	for _, tc := range cases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, gotOK := tc.pa.InitialScale()
+			if got != tc.want {
+				t.Errorf("InitialScale = %v, want: %v", got, tc.want)
+			}
+			if gotOK != tc.wantOK {
+				t.Errorf("OK = %v, want: %v", gotOK, tc.wantOK)
+			}
+		})
+	}
+}
diff --git a/pkg/apis/serving/v1alpha1/configuration_lifecycle_test.go b/pkg/apis/serving/v1alpha1/configuration_lifecycle_test.go
index 7e4cfbcc6..5789fe6e9 100644
--- a/pkg/apis/serving/v1alpha1/configuration_lifecycle_test.go
+++ b/pkg/apis/serving/v1alpha1/configuration_lifecycle_test.go
@@ -21,6 +21,7 @@ import (
 
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/runtime/schema"
+	"knative.dev/pkg/apis"
 	"knative.dev/pkg/apis/duck"
 	duckv1 "knative.dev/pkg/apis/duck/v1"
 	apistest "knative.dev/pkg/apis/testing"
@@ -45,6 +46,14 @@ func TestConfigurationDuckTypes(t *testing.T) {
 	}
 }
 
+func TestConfigurationGetConditionSet(t *testing.T) {
+	c := &Configuration{}
+
+	if got, want := c.GetConditionSet().GetTopLevelConditionType(), apis.ConditionReady; got != want {
+		t.Errorf("GetTopLevelCondition- Actual:%v, expected:%v", got, want)
+	}
+}
+
 func TestConfigurationIsReady(t *testing.T) {
 	cases := []struct {
 		name    string
diff --git a/pkg/apis/serving/v1alpha1/configuration_types_test.go b/pkg/apis/serving/v1alpha1/configuration_types_test.go
new file mode 100644
index 000000000..c0a536401
--- /dev/null
+++ b/pkg/apis/serving/v1alpha1/configuration_types_test.go
@@ -0,0 +1,34 @@
+/*
+Copyright 2020 The Knative Authors
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+    http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package v1alpha1
+
+import (
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+	duckv1 "knative.dev/pkg/apis/duck/v1"
+)
+
+func TestConfigurationGetStatus(t *testing.T) {
+	status := &duckv1.Status{}
+	configuration := Configuration{
+		Status: ConfigurationStatus{
+			Status: *status,
+		},
+	}
+
+	if !cmp.Equal(configuration.GetStatus(), status) {
+		t.Errorf("configuration.GetStatus() did not return expected status. Actual:%v Expected:%v",
+			configuration.GetStatus(), status)
+	}
+}
diff --git a/pkg/apis/serving/v1alpha1/revision_lifecycle_test.go b/pkg/apis/serving/v1alpha1/revision_lifecycle_test.go
index a391478fc..657f28568 100644
--- a/pkg/apis/serving/v1alpha1/revision_lifecycle_test.go
+++ b/pkg/apis/serving/v1alpha1/revision_lifecycle_test.go
@@ -56,6 +56,14 @@ func TestRevisionDuckTypes(t *testing.T) {
 	}
 }
 
+func TestRevisionGetConditionSet(t *testing.T) {
+	r := &Revision{}
+
+	if got, want := r.GetConditionSet().GetTopLevelConditionType(), apis.ConditionReady; got != want {
+		t.Errorf("GetTopLevelCondition- Actual:%v, expected:%v", got, want)
+	}
+}
+
 func TestIsActivationRequired(t *testing.T) {
 	cases := []struct {
 		name                 string
diff --git a/pkg/apis/serving/v1alpha1/revision_types_test.go b/pkg/apis/serving/v1alpha1/revision_types_test.go
new file mode 100644
index 000000000..3e197acfc
--- /dev/null
+++ b/pkg/apis/serving/v1alpha1/revision_types_test.go
@@ -0,0 +1,34 @@
+/*
+Copyright 2020 The Knative Authors
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+    http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package v1alpha1
+
+import (
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+	duckv1 "knative.dev/pkg/apis/duck/v1"
+)
+
+func TestRevisionGetStatus(t *testing.T) {
+	status := &duckv1.Status{}
+	revision := Revision{
+		Status: RevisionStatus{
+			Status: *status,
+		},
+	}
+
+	if !cmp.Equal(revision.GetStatus(), status) {
+		t.Errorf("revision.GetStatus() did not return expected status. Actual:%v Expected:%v",
+			revision.GetStatus(), status)
+	}
+}
diff --git a/pkg/apis/serving/v1alpha1/route_lifecycle_test.go b/pkg/apis/serving/v1alpha1/route_lifecycle_test.go
index 3ea9e832e..cbd6a9e5a 100644
--- a/pkg/apis/serving/v1alpha1/route_lifecycle_test.go
+++ b/pkg/apis/serving/v1alpha1/route_lifecycle_test.go
@@ -21,6 +21,7 @@ import (
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/runtime/schema"
 	netv1alpha1 "knative.dev/networking/pkg/apis/networking/v1alpha1"
+	"knative.dev/pkg/apis"
 	"knative.dev/pkg/apis/duck"
 	duckv1 "knative.dev/pkg/apis/duck/v1"
 	duckv1alpha1 "knative.dev/pkg/apis/duck/v1alpha1"
@@ -49,6 +50,14 @@ func TestRouteDuckTypes(t *testing.T) {
 	}
 }
 
+func TestRouteGetConditionSet(t *testing.T) {
+	r := &Route{}
+
+	if got, want := r.GetConditionSet().GetTopLevelConditionType(), apis.ConditionReady; got != want {
+		t.Errorf("GetTopLevelCondition- Actual:%v, expected:%v", got, want)
+	}
+}
+
 func TestRouteIsReady(t *testing.T) {
 	cases := []struct {
 		name    string
diff --git a/pkg/apis/serving/v1alpha1/route_types_test.go b/pkg/apis/serving/v1alpha1/route_types_test.go
new file mode 100644
index 000000000..4e5287e84
--- /dev/null
+++ b/pkg/apis/serving/v1alpha1/route_types_test.go
@@ -0,0 +1,34 @@
+/*
+Copyright 2020 The Knative Authors
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+    http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package v1alpha1
+
+import (
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+	duckv1 "knative.dev/pkg/apis/duck/v1"
+)
+
+func TestRouteGetStatus(t *testing.T) {
+	status := &duckv1.Status{}
+	route := Route{
+		Status: RouteStatus{
+			Status: *status,
+		},
+	}
+
+	if !cmp.Equal(route.GetStatus(), status) {
+		t.Errorf("route.GetStatus() did not return expected status. Actual:%v Expected:%v",
+			route.GetStatus(), status)
+	}
+}
diff --git a/pkg/apis/serving/v1alpha1/service_lifecycle_test.go b/pkg/apis/serving/v1alpha1/service_lifecycle_test.go
index 606d8e029..b3e5a78e3 100644
--- a/pkg/apis/serving/v1alpha1/service_lifecycle_test.go
+++ b/pkg/apis/serving/v1alpha1/service_lifecycle_test.go
@@ -66,6 +66,14 @@ func TestServiceGeneration(t *testing.T) {
 	}
 }
 
+func TestServiceGetConditionSet(t *testing.T) {
+	s := &Service{}
+
+	if got, want := s.GetConditionSet().GetTopLevelConditionType(), apis.ConditionReady; got != want {
+		t.Errorf("GetTopLevelCondition- Actual:%v, expected:%v", got, want)
+	}
+}
+
 func TestServiceIsReady(t *testing.T) {
 	cases := []struct {
 		name    string
diff --git a/pkg/apis/serving/v1alpha1/service_types_test.go b/pkg/apis/serving/v1alpha1/service_types_test.go
new file mode 100644
index 000000000..c93eeaa6e
--- /dev/null
+++ b/pkg/apis/serving/v1alpha1/service_types_test.go
@@ -0,0 +1,34 @@
+/*
+Copyright 2020 The Knative Authors
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+    http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package v1alpha1
+
+import (
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+	duckv1 "knative.dev/pkg/apis/duck/v1"
+)
+
+func TestServiceGetStatus(t *testing.T) {
+	status := &duckv1.Status{}
+	service := Service{
+		Status: ServiceStatus{
+			Status: *status,
+		},
+	}
+
+	if !cmp.Equal(service.GetStatus(), status) {
+		t.Errorf("service.GetStatus() did not return expected status. Actual:%v Expected:%v",
+			service.GetStatus(), status)
+	}
+}
diff --git a/pkg/autoscaler/metrics/stats_scraper_test.go b/pkg/autoscaler/metrics/stats_scraper_test.go
index fc439a2a5..f33382912 100644
--- a/pkg/autoscaler/metrics/stats_scraper_test.go
+++ b/pkg/autoscaler/metrics/stats_scraper_test.go
@@ -152,12 +152,6 @@ func TestNewServiceScraperWithClientErrorCases(t *testing.T) {
 		counter:     counter,
 		accessor:    podAccessor,
 		expectedErr: "no Revision label found for Metric test-revision",
-	}, {
-		name:        "Empty scrape client",
-		metric:      metric,
-		counter:     counter,
-		accessor:    podAccessor,
-		expectedErr: "scrape client must not be nil",
 	}, {
 		name:        "Empty counter",
 		metric:      metric,
@@ -170,7 +164,7 @@ func TestNewServiceScraperWithClientErrorCases(t *testing.T) {
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
 			if _, err := newServiceScraperWithClient(test.metric, test.counter,
-				test.accessor, test.client, logger); err != nil {
+				test.accessor, test.client, test.client, logger); err != nil {
 				got := err.Error()
 				want := test.expectedErr
 				if got != want {
@@ -189,7 +183,7 @@ func TestPodDirectScrapeSuccess(t *testing.T) {
 	fake.KubeInformer = kubeinformers.NewSharedInformerFactory(fake.KubeClient, 0)
 
 	client := newTestScrapeClient(testStats, []error{nil})
-	scraper, err := serviceScraperForTest(t, client, true)
+	scraper, err := serviceScraperForTest(t, client, nil /* mesh not used */, true)
 	if err != nil {
 		t.Fatal("serviceScraperForTest:", err)
 	}
@@ -216,7 +210,7 @@ func TestPodDirectScrapeSomeFailButSuccess(t *testing.T) {
 
 	// For 5 pods, we need 4 successes.
 	client := newTestScrapeClient(testStats, []error{nil, nil, errors.New("okay"), nil, nil})
-	scraper, err := serviceScraperForTest(t, client, true)
+	scraper, err := serviceScraperForTest(t, client, nil /* mesh not used */, true)
 	if err != nil {
 		t.Fatal("serviceScraperForTest:", err)
 	}
@@ -248,13 +242,15 @@ func TestPodDirectScrapeNoneSucceed(t *testing.T) {
 	fake.KubeInformer = kubeinformers.NewSharedInformerFactory(fake.KubeClient, 0)
 
 	testStats := testStatsWithTime(4, youngPodCutOffDuration.Seconds() /*youngest*/)
-	client := newTestScrapeClient(testStats, []error{
+	direct := newTestScrapeClient(testStats, []error{
 		// Pods fail.
 		errors.New("okay"), errors.New("okay"), errors.New("okay"), errors.New("okay"),
+	})
+	mesh := newTestScrapeClient(testStats, []error{
 		// Service succeeds.
 		nil, nil, nil, nil,
 	})
-	scraper, err := serviceScraperForTest(t, client, true)
+	scraper, err := serviceScraperForTest(t, direct, mesh, true)
 	if err != nil {
 		t.Fatal("serviceScraperForTest:", err)
 	}
@@ -286,7 +282,7 @@ func TestPodDirectScrapePodsExhausted(t *testing.T) {
 	fake.KubeInformer = kubeinformers.NewSharedInformerFactory(fake.KubeClient, 0)
 
 	client := newTestScrapeClient(testStats, []error{nil, nil, errors.New("okay"), nil})
-	scraper, err := serviceScraperForTest(t, client, true)
+	scraper, err := serviceScraperForTest(t, client, nil /* mesh not used */, true)
 	if err != nil {
 		t.Fatal("serviceScraperForTest:", err)
 	}
@@ -305,7 +301,7 @@ func TestPodDirectScrapePodsExhausted(t *testing.T) {
 
 func TestScrapeReportStatWhenAllCallsSucceed(t *testing.T) {
 	client := newTestScrapeClient(testStats, []error{nil})
-	scraper, err := serviceScraperForTest(t, client, false)
+	scraper, err := serviceScraperForTest(t, client, client, false)
 	if err != nil {
 		t.Fatal("serviceScraperForTest:", err)
 	}
@@ -336,8 +332,9 @@ func TestScrapeAllPodsYoungPods(t *testing.T) {
 	// acceptable.
 	testStats := testStatsWithTime(numP, 0. /*youngest*/)
 
-	client := newTestScrapeClient(testStats, []error{nil})
-	scraper, err := serviceScraperForTest(t, client, false)
+	direct := newTestScrapeClient(testStats, []error{errNoPodsScraped}) // fall back to service scrape
+	mesh := newTestScrapeClient(testStats, []error{nil})
+	scraper, err := serviceScraperForTest(t, direct, mesh, false)
 	if err != nil {
 		t.Fatalf("serviceScraperForTest=%v, want no error", err)
 	}
@@ -368,8 +365,9 @@ func TestScrapeAllPodsOldPods(t *testing.T) {
 	// All pods are at least cutoff time old, so first 5 stats will be picked.
 	testStats := testStatsWithTime(numP, youngPodCutOffDuration.Seconds() /*youngest*/)
 
-	client := newTestScrapeClient(testStats, []error{nil})
-	scraper, err := serviceScraperForTest(t, client, false)
+	direct := newTestScrapeClient(testStats, []error{errNoPodsScraped}) // fall back to service scrape
+	mesh := newTestScrapeClient(testStats, []error{nil})
+	scraper, err := serviceScraperForTest(t, direct, mesh, false)
 	if err != nil {
 		t.Fatalf("serviceScraperForTest=%v, want no error", err)
 	}
@@ -401,8 +399,9 @@ func TestScrapeSomePodsOldPods(t *testing.T) {
 	// So pods 3-10 qualify (for 11 total sample is 7).
 	testStats := testStatsWithTime(numP, youngPodCutOffDuration.Seconds()/2 /*youngest*/)
 
-	client := newTestScrapeClient(testStats, []error{nil})
-	scraper, err := serviceScraperForTest(t, client, false)
+	direct := newTestScrapeClient(testStats, []error{errNoPodsScraped}) // fall back to service scrape
+	mesh := newTestScrapeClient(testStats, []error{nil})
+	scraper, err := serviceScraperForTest(t, direct, mesh, false)
 	if err != nil {
 		t.Fatalf("serviceScraperForTest=%v, want no error", err)
 	}
@@ -430,7 +429,7 @@ func TestScrapeSomePodsOldPods(t *testing.T) {
 
 func TestScrapeReportErrorCannotFindEnoughPods(t *testing.T) {
 	client := newTestScrapeClient(testStats[2:], []error{nil})
-	scraper, err := serviceScraperForTest(t, client, false)
+	scraper, err := serviceScraperForTest(t, client, client, false)
 	if err != nil {
 		t.Fatalf("serviceScraperForTest=%v, want no error", err)
 	}
@@ -448,9 +447,9 @@ func TestScrapeReportErrorIfAnyFails(t *testing.T) {
 	errTest := errors.New("test")
 
 	// 1 success and 10 failures so one scrape fails permanently through retries.
-	client := newTestScrapeClient(testStats, []error{nil,
-		errTest, errTest, errTest, errTest, errTest, errTest, errTest, errTest, errTest, errTest})
-	scraper, err := serviceScraperForTest(t, client, false)
+	client := newTestScrapeClient(testStats, []error{nil, errTest, errTest,
+		errTest, errTest, errTest, errTest, errTest, errTest, errTest, errTest})
+	scraper, err := serviceScraperForTest(t, client, client, false)
 	if err != nil {
 		t.Fatalf("serviceScraperForTest=%v, want no error", err)
 	}
@@ -466,7 +465,7 @@ func TestScrapeReportErrorIfAnyFails(t *testing.T) {
 
 func TestScrapeDoNotScrapeIfNoPodsFound(t *testing.T) {
 	client := newTestScrapeClient(testStats, nil)
-	scraper, err := serviceScraperForTest(t, client, false)
+	scraper, err := serviceScraperForTest(t, client, client, false)
 	if err != nil {
 		t.Fatalf("serviceScraperForTest=%v, want no error", err)
 	}
@@ -483,7 +482,7 @@ func TestScrapeDoNotScrapeIfNoPodsFound(t *testing.T) {
 	}
 }
 
-func serviceScraperForTest(t *testing.T, sClient scrapeClient, podsAddressable bool) (*serviceScraper, error) {
+func serviceScraperForTest(t *testing.T, directClient, meshClient scrapeClient, podsAddressable bool) (*serviceScraper, error) {
 	metric := testMetric()
 	counter := resources.NewScopedEndpointsCounter(
 		fake.KubeInformer.Core().V1().Endpoints().Lister(),
@@ -492,7 +491,7 @@ func serviceScraperForTest(t *testing.T, sClient scrapeClient, podsAddressable b
 		fake.KubeInformer.Core().V1().Pods().Lister(),
 		fake.TestNamespace, fake.TestRevision)
 	logger := logtesting.TestLogger(t)
-	ss, err := newServiceScraperWithClient(metric, counter, accessor, sClient, logger)
+	ss, err := newServiceScraperWithClient(metric, counter, accessor, directClient, meshClient, logger)
 	if ss != nil {
 		ss.podsAddressable = podsAddressable
 	}
diff --git a/pkg/metrics/tags_test.go b/pkg/metrics/tags_test.go
index 1f01ac133..2fd1ce59b 100644
--- a/pkg/metrics/tags_test.go
+++ b/pkg/metrics/tags_test.go
@@ -36,7 +36,7 @@ var testM = stats.Int64(
 	stats.UnitDimensionless)
 
 func register(t *testing.T) func() {
-	if err := view.Register(
+	if err := pkgmetrics.RegisterResourceView(
 		&view.View{
 			Description: "Number of pods autoscaler wants to allocate",
 			Measure:     testM,
diff --git a/pkg/reconciler/autoscaling/kpa/kpa_test.go b/pkg/reconciler/autoscaling/kpa/kpa_test.go
index ec32928c2..dbeda6a4d 100644
--- a/pkg/reconciler/autoscaling/kpa/kpa_test.go
+++ b/pkg/reconciler/autoscaling/kpa/kpa_test.go
@@ -196,6 +196,7 @@ func markInactive(pa *asv1a1.PodAutoscaler) {
 func kpa(ns, n string, opts ...PodAutoscalerOption) *asv1a1.PodAutoscaler {
 	rev := newTestRevision(ns, n)
 	kpa := revisionresources.MakePA(rev)
+	kpa.Generation = 1
 	kpa.Annotations["autoscaling.knative.dev/class"] = "kpa.autoscaling.knative.dev"
 	kpa.Annotations["autoscaling.knative.dev/metric"] = "concurrency"
 	for _, opt := range opts {
@@ -244,18 +245,21 @@ func TestReconcile(t *testing.T) {
 		return kpa(
 			testNamespace, testRevision, markInactive, withScales(g, unknownScale), WithReachabilityReachable,
 			withMinScale(defaultScale), WithPAStatusService(testRevision), WithPAMetricsService(privateSvc),
+			WithObservedGeneration(1),
 		)
 	}
 	activatingKPAMinScale := func(g int32) *asv1a1.PodAutoscaler {
 		return kpa(
 			testNamespace, testRevision, markActivating, withScales(g, defaultScale), WithReachabilityReachable,
 			withMinScale(defaultScale), WithPAStatusService(testRevision), WithPAMetricsService(privateSvc),
+			WithObservedGeneration(1),
 		)
 	}
 	activeKPAMinScale := func(g, w int32) *asv1a1.PodAutoscaler {
 		return kpa(
 			testNamespace, testRevision, markActive, withScales(g, w), WithReachabilityReachable,
 			withMinScale(defaultScale), WithPAStatusService(testRevision), WithPAMetricsService(privateSvc),
+			WithObservedGeneration(1),
 		)
 	}
 
@@ -290,7 +294,7 @@ func TestReconcile(t *testing.T) {
 		Key:  key,
 		Objects: []runtime.Object{
 			kpa(testNamespace, testRevision, markActive, WithPAMetricsService(privateSvc),
-				withScales(1, defaultScale), WithPAStatusService(testRevision)),
+				withScales(1, defaultScale), WithPAStatusService(testRevision), WithObservedGeneration(1)),
 			sks(testNamespace, testRevision, WithDeployRef(deployName), WithSKSReady),
 			metric(testNamespace, testRevision),
 			defaultDeployment, defaultEndpoints,
@@ -316,10 +320,10 @@ func TestReconcile(t *testing.T) {
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: kpa(testNamespace, testRevision, markUnknown, WithPAMetricsService(privateSvc), withScales(1, defaultScale),
-				WithPAStatusService(testRevision)),
+				WithPAStatusService(testRevision), WithObservedGeneration(1), WithObservedGenerationFailure()),
 		}, {
 			Object: kpa(testNamespace, testRevision, markUnknown, WithPAMetricsService(privateSvc), withScales(1, defaultScale),
-				WithPAStatusService(testRevision)),
+				WithPAStatusService(testRevision), WithObservedGeneration(1), WithObservedGenerationFailure()),
 		}},
 		WantErr: true,
 		WantEvents: []string{
@@ -331,7 +335,7 @@ func TestReconcile(t *testing.T) {
 		Key:  key,
 		Objects: []runtime.Object{
 			kpa(testNamespace, testRevision, markActive, WithPAMetricsService(privateSvc),
-				withScales(1, defaultScale), WithPAStatusService(testRevision)),
+				withScales(1, defaultScale), WithPAStatusService(testRevision), WithObservedGeneration(1)),
 			sks(testNamespace, testRevision, WithDeployRef(deployName), WithSKSReady),
 			defaultDeployment, defaultEndpoints,
 		},
@@ -351,7 +355,7 @@ func TestReconcile(t *testing.T) {
 		Key:  key,
 		Objects: []runtime.Object{
 			kpa(testNamespace, testRevision, markActive, WithPAMetricsService(privateSvc),
-				withScales(1, defaultScale), WithPAStatusService(testRevision)),
+				withScales(1, defaultScale), WithPAStatusService(testRevision), WithObservedGeneration(1)),
 			sks(testNamespace, testRevision, WithDeployRef(deployName), WithSKSReady),
 			defaultDeployment, defaultEndpoints,
 			metricWithDiffSvc(testNamespace, testRevision),
@@ -379,7 +383,7 @@ func TestReconcile(t *testing.T) {
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: kpa(testNamespace, testRevision, markActive, withScales(1, defaultScale),
-				WithPAStatusService(testRevision), WithPAMetricsService(privateSvc)),
+				WithPAStatusService(testRevision), WithPAMetricsService(privateSvc), WithObservedGeneration(1)),
 		}},
 		WantCreates: []runtime.Object{
 			metric(testNamespace, testRevision),
@@ -389,7 +393,7 @@ func TestReconcile(t *testing.T) {
 		Key:  key,
 		Objects: []runtime.Object{
 			kpa(testNamespace, testRevision, markActive, WithPAMetricsService(privateSvc),
-				withScales(1, defaultScale), WithPAStatusService(testRevision)),
+				withScales(1, defaultScale), WithPAStatusService(testRevision), WithObservedGeneration(1)),
 			defaultSKS,
 			metric(testNamespace, testRevision),
 			deploy(testNamespace, testRevision),
@@ -411,7 +415,7 @@ func TestReconcile(t *testing.T) {
 		WantErr: true,
 		Objects: []runtime.Object{
 			kpa(testNamespace, testRevision, markActive, WithPAMetricsService(privateSvc),
-				withScales(1, defaultScale), WithPAStatusService(testRevision)),
+				withScales(1, defaultScale), WithPAStatusService(testRevision), WithObservedGeneration(1)),
 			defaultSKS,
 			metric(testNamespace, testRevision),
 			deploy(testNamespace, testRevision),
@@ -433,7 +437,7 @@ func TestReconcile(t *testing.T) {
 		Key:  key,
 		Objects: []runtime.Object{
 			kpa(testNamespace, testRevision, markActive, WithPAMetricsService(privateSvc),
-				withScales(1, defaultScale), WithPAStatusService(testRevision)),
+				withScales(1, defaultScale), WithPAStatusService(testRevision), WithObservedGeneration(1)),
 			defaultSKS,
 			metric(testNamespace, testRevision),
 			defaultDeployment,
@@ -462,8 +466,7 @@ func TestReconcile(t *testing.T) {
 		}},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: kpa(testNamespace, testRevision, markActivating, withScales(0, defaultScale),
-				WithPAMetricsService(privateSvc),
-				WithPAStatusService(testRevision)),
+				WithPAMetricsService(privateSvc), WithPAStatusService(testRevision), WithObservedGeneration(1)),
 		}},
 	}, {
 		Name: "sks is still not ready",
@@ -478,7 +481,7 @@ func TestReconcile(t *testing.T) {
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: kpa(testNamespace, testRevision, markActivating, withScales(0, defaultScale),
-				WithPAMetricsService(privateSvc), WithPAStatusService(testRevision)),
+				WithPAMetricsService(privateSvc), WithPAStatusService(testRevision), WithObservedGeneration(1)),
 		}},
 	}, {
 		Name: "sks becomes ready",
@@ -491,7 +494,7 @@ func TestReconcile(t *testing.T) {
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: kpa(testNamespace, testRevision, markActive, WithPAStatusService(testRevision),
-				WithPAMetricsService(privateSvc), withScales(1, defaultScale)),
+				WithPAMetricsService(privateSvc), withScales(1, defaultScale), WithObservedGeneration(1)),
 		}},
 	}, {
 		Name: "kpa does not become ready without minScale endpoints when reachable",
@@ -505,7 +508,8 @@ func TestReconcile(t *testing.T) {
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: kpa(testNamespace, testRevision, markActivating, withMinScale(2), WithPAMetricsService(privateSvc),
-				withScales(1, defaultScale), WithPAStatusService(testRevision), WithReachabilityReachable),
+				withScales(1, defaultScale), WithPAStatusService(testRevision), WithReachabilityReachable,
+				WithObservedGeneration(1)),
 		}},
 	}, {
 		Name: "kpa does not become ready without minScale endpoints when reachability is unknown",
@@ -519,7 +523,8 @@ func TestReconcile(t *testing.T) {
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: kpa(testNamespace, testRevision, markActivating, withMinScale(2), WithPAMetricsService(privateSvc),
-				withScales(1, defaultScale), WithPAStatusService(testRevision), WithReachabilityUnknown),
+				withScales(1, defaultScale), WithPAStatusService(testRevision), WithReachabilityUnknown,
+				WithObservedGeneration(1)),
 		}},
 	}, {
 		Name: "kpa becomes ready without minScale endpoints when unreachable",
@@ -533,7 +538,8 @@ func TestReconcile(t *testing.T) {
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: kpa(testNamespace, testRevision, markActive, withMinScale(2), WithPAMetricsService(privateSvc),
-				withScales(1, defaultScale), WithPAStatusService(testRevision), WithReachabilityUnreachable),
+				withScales(1, defaultScale), WithPAStatusService(testRevision), WithReachabilityUnreachable,
+				WithObservedGeneration(1)),
 		}},
 	}, {
 		Name: "kpa becomes ready with minScale endpoints when reachable",
@@ -548,7 +554,8 @@ func TestReconcile(t *testing.T) {
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: kpa(testNamespace, testRevision, markActive, withMinScale(2), WithPAMetricsService(privateSvc),
-				withScales(2, defaultScale), WithPAStatusService(testRevision), WithReachabilityReachable),
+				withScales(2, defaultScale), WithPAStatusService(testRevision), WithReachabilityReachable,
+				WithObservedGeneration(1)),
 		}},
 	}, {
 		Name: "kpa becomes ready with minScale endpoints when reachability is unknown",
@@ -563,7 +570,8 @@ func TestReconcile(t *testing.T) {
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: kpa(testNamespace, testRevision, markActive, withMinScale(2), WithPAMetricsService(privateSvc),
-				withScales(2, defaultScale), WithPAStatusService(testRevision), WithReachabilityUnknown),
+				withScales(2, defaultScale), WithPAStatusService(testRevision), WithReachabilityUnknown,
+				WithObservedGeneration(1)),
 		}},
 	}, {
 		Name: "sks does not exist",
@@ -574,7 +582,8 @@ func TestReconcile(t *testing.T) {
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			// SKS does not exist, so we're just creating and have no status.
-			Object: kpa(testNamespace, testRevision, markActivating, WithPAMetricsService(privateSvc), withScales(0, unknownScale)),
+			Object: kpa(testNamespace, testRevision, markActivating, WithPAMetricsService(privateSvc), withScales(0, unknownScale),
+				WithObservedGeneration(1)),
 		}},
 		WantCreates: []runtime.Object{
 			sks(testNamespace, testRevision, WithDeployRef(deployName), WithNumActivators(0)),
@@ -591,7 +600,8 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			// SKS just got updated and we don't have up to date status.
 			Object: kpa(testNamespace, testRevision, markActivating,
-				withScales(0, defaultScale), WithPAStatusService(testRevision), WithPAMetricsService(privateSvc)),
+				withScales(0, defaultScale), WithPAStatusService(testRevision), WithPAMetricsService(privateSvc),
+				WithObservedGeneration(1)),
 		}},
 		WantUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: sks(testNamespace, testRevision, WithPubService, WithPrivateService,
@@ -601,7 +611,7 @@ func TestReconcile(t *testing.T) {
 		Name: "sks cannot be created",
 		Key:  key,
 		Objects: []runtime.Object{
-			kpa(testNamespace, testRevision, markActive, WithPAMetricsService(privateSvc), withScales(1, defaultScale)),
+			kpa(testNamespace, testRevision, markActive, WithPAMetricsService(privateSvc), withScales(1, defaultScale), WithObservedGeneration(1)),
 			metric(testNamespace, testRevision),
 			defaultDeployment,
 		},
@@ -620,7 +630,7 @@ func TestReconcile(t *testing.T) {
 		Name: "sks cannot be updated",
 		Key:  key,
 		Objects: []runtime.Object{
-			kpa(testNamespace, testRevision, withScales(1, defaultScale), WithPAMetricsService(privateSvc), markActive),
+			kpa(testNamespace, testRevision, withScales(1, defaultScale), WithPAMetricsService(privateSvc), markActive, WithObservedGeneration(1)),
 			sks(testNamespace, testRevision, WithDeployRef("bar")),
 			metric(testNamespace, testRevision),
 			defaultDeployment,
@@ -648,7 +658,7 @@ func TestReconcile(t *testing.T) {
 		WantErr: true,
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: kpa(testNamespace, testRevision, withScales(1, defaultScale),
-				WithPAMetricsService(privateSvc), markResourceNotOwned("ServerlessService", testRevision)),
+				WithPAMetricsService(privateSvc), markResourceNotOwned("ServerlessService", testRevision), WithObservedGeneration(1)),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeWarning, "InternalError", "error reconciling SKS: PA: test-revision does not own SKS: test-revision"),
@@ -665,7 +675,7 @@ func TestReconcile(t *testing.T) {
 		WantErr: true,
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: kpa(testNamespace, testRevision, withScales(1, defaultScale),
-				WithPAMetricsService(privateSvc), markResourceNotOwned("Metric", testRevision)),
+				WithPAMetricsService(privateSvc), markResourceNotOwned("Metric", testRevision), WithObservedGeneration(1)),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeWarning, "InternalError", `error reconciling Metric: PA: test-revision does not own Metric: test-revision`),
@@ -678,7 +688,7 @@ func TestReconcile(t *testing.T) {
 		Objects: []runtime.Object{
 			kpa(testNamespace, testRevision, withScales(0, 0),
 				WithNoTraffic("NoTraffic", "The target is not receiving traffic."),
-				markOld, WithPAStatusService(testRevision), WithPAMetricsService(privateSvc)),
+				markOld, WithPAStatusService(testRevision), WithPAMetricsService(privateSvc), WithObservedGeneration(1)),
 			sks(testNamespace, testRevision, WithDeployRef(deployName), WithProxyMode, WithSKSReady),
 			metric(testNamespace, testRevision),
 			deploy(testNamespace, testRevision, func(d *appsv1.Deployment) {
@@ -695,7 +705,7 @@ func TestReconcile(t *testing.T) {
 		Objects: []runtime.Object{
 			kpa(testNamespace, testRevision, withScales(0, 0),
 				WithNoTraffic("NoTraffic", "The target is not receiving traffic."),
-				markOld, WithPAStatusService(testRevision), WithPAMetricsService(privateSvc)),
+				markOld, WithPAStatusService(testRevision), WithPAMetricsService(privateSvc), WithObservedGeneration(1)),
 			sks(testNamespace, testRevision, WithDeployRef(deployName), WithProxyMode, WithSKSReady),
 			metric(testNamespace, testRevision),
 			deploy(testNamespace, testRevision),
@@ -725,7 +735,8 @@ func TestReconcile(t *testing.T) {
 			Object: kpa(testNamespace, testRevision, withScales(1, 0),
 				WithPAMetricsService(privateSvc),
 				WithNoTraffic("NoTraffic", "The target is not receiving traffic."),
-				WithPAStatusService(testRevision), WithPAMetricsService(privateSvc)),
+				WithPAStatusService(testRevision), WithPAMetricsService(privateSvc),
+				WithObservedGeneration(1)),
 		}},
 		WantUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: sks(testNamespace, testRevision, WithSKSReady,
@@ -738,7 +749,7 @@ func TestReconcile(t *testing.T) {
 			decider(testNamespace, testRevision, 0 /* desiredScale */, 0 /* ebc */, scaling.MinActivators)),
 		Objects: []runtime.Object{
 			kpa(testNamespace, testRevision, markActive, withScales(1, 1),
-				WithPAStatusService(testRevision), WithPAMetricsService(privateSvc)),
+				WithPAStatusService(testRevision), WithPAMetricsService(privateSvc), WithObservedGeneration(1)),
 			defaultSKS,
 			metric(testNamespace, testRevision),
 			deploy(testNamespace, testRevision), defaultEndpoints,
@@ -759,7 +770,8 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: kpa(testNamespace, testRevision, WithPAMetricsService(privateSvc),
 				WithNoTraffic("TimedOut", "The target could not be activated."), withScales(1, 0),
-				WithPAStatusService(testRevision), WithPAMetricsService(privateSvc)),
+				WithPAStatusService(testRevision), WithPAMetricsService(privateSvc),
+				WithObservedGeneration(1)),
 		}},
 		WantUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: sks(testNamespace, testRevision, WithSKSReady,
@@ -911,7 +923,7 @@ func TestReconcile(t *testing.T) {
 				0 /* ebc */, scaling.MinActivators)),
 		Objects: []runtime.Object{
 			kpa(testNamespace, testRevision, markInactive, WithPAMetricsService(privateSvc),
-				withScales(0, -1), WithPAStatusService(testRevision)),
+				withScales(0, -1), WithPAStatusService(testRevision), WithObservedGeneration(1)),
 			sks(testNamespace, testRevision, WithDeployRef(deployName), WithProxyMode, WithPubService, WithPrivateService),
 			metric(testNamespace, testRevision),
 			defaultDeployment, defaultEndpoints,
@@ -924,7 +936,7 @@ func TestReconcile(t *testing.T) {
 				-42 /* ebc */, scaling.MinActivators)),
 		Objects: []runtime.Object{
 			kpa(testNamespace, testRevision, markActive, WithPAMetricsService(privateSvc),
-				withScales(1, defaultScale), WithPAStatusService(testRevision)),
+				withScales(1, defaultScale), WithPAStatusService(testRevision), WithObservedGeneration(1)),
 			sks(testNamespace, testRevision, WithDeployRef(deployName), WithProxyMode, WithSKSReady),
 			metric(testNamespace, testRevision),
 			defaultDeployment, defaultEndpoints,
@@ -937,7 +949,7 @@ func TestReconcile(t *testing.T) {
 				-42 /* ebc */, 1982 /*numActivators*/)),
 		Objects: []runtime.Object{
 			kpa(testNamespace, testRevision, markActive, WithPAMetricsService(privateSvc),
-				withScales(1, defaultScale), WithPAStatusService(testRevision)),
+				withScales(1, defaultScale), WithPAStatusService(testRevision), WithObservedGeneration(1)),
 			sks(testNamespace, testRevision, WithDeployRef(deployName),
 				WithProxyMode, WithSKSReady, WithNumActivators(4)),
 			metric(testNamespace, testRevision),
@@ -955,7 +967,7 @@ func TestReconcile(t *testing.T) {
 				-18 /* ebc */, scaling.MinActivators+1)),
 		Objects: []runtime.Object{
 			kpa(testNamespace, testRevision, markActive, WithPAMetricsService(privateSvc),
-				withScales(1, defaultScale), WithPAStatusService(testRevision)),
+				withScales(1, defaultScale), WithPAStatusService(testRevision), WithObservedGeneration(1)),
 			sks(testNamespace, testRevision, WithDeployRef(deployName), WithSKSReady,
 				WithNumActivators(2)),
 			metric(testNamespace, testRevision),
@@ -973,7 +985,7 @@ func TestReconcile(t *testing.T) {
 				1 /* ebc */, scaling.MinActivators)),
 		Objects: []runtime.Object{
 			kpa(testNamespace, testRevision, markActive, WithPAMetricsService(privateSvc),
-				withScales(1, defaultScale), WithPAStatusService(testRevision)),
+				withScales(1, defaultScale), WithPAStatusService(testRevision), WithObservedGeneration(1)),
 			sks(testNamespace, testRevision, WithDeployRef(deployName), WithSKSReady, WithProxyMode,
 				WithNumActivators(3)),
 			metric(testNamespace, testRevision),
diff --git a/pkg/reconciler/autoscaling/kpa/resources/decider_test.go b/pkg/reconciler/autoscaling/kpa/resources/decider_test.go
index 3fb330608..c442cf5eb 100644
--- a/pkg/reconciler/autoscaling/kpa/resources/decider_test.go
+++ b/pkg/reconciler/autoscaling/kpa/resources/decider_test.go
@@ -130,6 +130,16 @@ func TestMakeDecider(t *testing.T) {
 		name: "with metric annotation",
 		pa:   pa(WithMetricAnnotation("rps")),
 		want: decider(withTarget(100.0), withPanicThreshold(2.0), withTotal(100), withMetric("rps"), withMetricAnnotation("rps")),
+	}, {
+		name: "with initial scale",
+		pa: pa(func(pa *v1alpha1.PodAutoscaler) {
+			pa.ObjectMeta.Annotations[autoscaling.InitialScaleAnnotationKey] = "2"
+		}),
+		want: decider(withTarget(100.0), withPanicThreshold(2.0), withTotal(100),
+			func(d *scaling.Decider) {
+				d.Spec.InitialScale = 2
+				d.ObjectMeta.Annotations[autoscaling.InitialScaleAnnotationKey] = "2"
+			}),
 	}}
 
 	for _, tc := range cases {
@@ -146,6 +156,58 @@ func TestMakeDecider(t *testing.T) {
 	}
 }
 
+func TestGetInitialScale(t *testing.T) {
+	tests := []struct {
+		name          string
+		paMutation    func(*v1alpha1.PodAutoscaler)
+		configMutator func(*autoscalerconfig.Config)
+		want          int
+	}{{
+		name: "revision initial scale not set",
+		want: 1,
+	}, {
+		name: "revision initial scale overrides cluster initial scale",
+		paMutation: func(pa *v1alpha1.PodAutoscaler) {
+			pa.ObjectMeta.Annotations[autoscaling.InitialScaleAnnotationKey] = "2"
+		},
+		want: 2,
+	}, {
+		name: "cluster allows initial scale zero",
+		paMutation: func(pa *v1alpha1.PodAutoscaler) {
+			pa.ObjectMeta.Annotations[autoscaling.InitialScaleAnnotationKey] = "0"
+		},
+		configMutator: func(c *autoscalerconfig.Config) {
+			c.AllowZeroInitialScale = true
+		},
+		want: 0,
+	}, {
+		name: "cluster does not allows initial scale zero",
+		paMutation: func(pa *v1alpha1.PodAutoscaler) {
+			pa.ObjectMeta.Annotations[autoscaling.InitialScaleAnnotationKey] = "0"
+		},
+		configMutator: func(c *autoscalerconfig.Config) {
+			c.AllowZeroInitialScale = false
+		},
+		want: 1,
+	}}
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			autoscalerConfig, _ := autoscalerconfig.NewConfigFromMap(map[string]string{})
+			if test.configMutator != nil {
+				test.configMutator(autoscalerConfig)
+			}
+			pa := pa()
+			if test.paMutation != nil {
+				test.paMutation(pa)
+			}
+			got := int(GetInitialScale(autoscalerConfig, pa))
+			if want := test.want; got != want {
+				t.Errorf("got = %v, want: %v", got, want)
+			}
+		})
+	}
+}
+
 func pa(options ...PodAutoscalerOption) *v1alpha1.PodAutoscaler {
 	p := &v1alpha1.PodAutoscaler{
 		ObjectMeta: metav1.ObjectMeta{
@@ -200,6 +262,7 @@ func decider(options ...deciderOption) *scaling.Decider {
 			PanicThreshold:      200,
 			ActivatorCapacity:   811,
 			StableWindow:        config.StableWindow,
+			InitialScale:        1,
 		},
 	}
 	for _, fn := range options {
@@ -288,4 +351,6 @@ var config = &autoscalerconfig.Config{
 	PanicThresholdPercentage:           200,
 	PanicWindowPercentage:              10,
 	ScaleToZeroGracePeriod:             30 * time.Second,
+	InitialScale:                       1,
+	AllowZeroInitialScale:              false,
 }
diff --git a/pkg/reconciler/configuration/configuration_test.go b/pkg/reconciler/configuration/configuration_test.go
index 7bd8575a0..11130fefb 100644
--- a/pkg/reconciler/configuration/configuration_test.go
+++ b/pkg/reconciler/configuration/configuration_test.go
@@ -94,12 +94,12 @@ func TestReconcile(t *testing.T) {
 			Object: cfg("no-revisions-yet", "foo", 1234,
 				// The following properties are set when we first reconcile a
 				// Configuration and a Revision is created.
-				WithLatestCreated("no-revisions-yet-00001"), WithObservedGen),
+				WithLatestCreated("no-revisions-yet-00001"), WithConfigObservedGen),
 		}, {
 			Object: cfg("no-revisions-yet", "foo", 1234,
 				// The following properties are set when we first reconcile a
 				// Configuration and a Revision is created.
-				WithLatestCreated("no-revisions-yet-00001"), WithObservedGen),
+				WithLatestCreated("no-revisions-yet-00001"), WithConfigObservedGen),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeNormal, "Created", "Created Revision %q", "no-revisions-yet-00001"),
@@ -124,7 +124,7 @@ func TestReconcile(t *testing.T) {
 			},
 				// The following properties are set when we first reconcile a
 				// Configuration and a Revision is created.
-				WithLatestCreated("byo-name-create-foo"), WithObservedGen),
+				WithLatestCreated("byo-name-create-foo"), WithConfigObservedGen),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeNormal, "Created", "Created Revision %q", "byo-name-create-foo"),
@@ -138,7 +138,7 @@ func TestReconcile(t *testing.T) {
 			},
 				// The following properties are set when we first reconcile a
 				// Configuration and a Revision is created.
-				WithLatestCreated("byo-name-exists-foo"), WithObservedGen),
+				WithLatestCreated("byo-name-exists-foo"), WithConfigObservedGen),
 			rev("byo-name-exists", "foo", 1234, WithCreationTimestamp(now), func(rev *v1.Revision) {
 				rev.Name = "byo-name-exists-foo"
 				rev.GenerateName = ""
@@ -160,7 +160,7 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: cfg("byo-name-git-revert", "foo", 1234, func(cfg *v1.Configuration) {
 				cfg.Spec.GetTemplate().Name = "byo-name-git-revert-foo"
-			}, WithLatestCreated("byo-name-git-revert-foo"), WithObservedGen),
+			}, WithLatestCreated("byo-name-git-revert-foo"), WithConfigObservedGen),
 		}},
 		Key: "foo/byo-name-git-revert",
 	}, {
@@ -181,7 +181,7 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: cfg("byo-name-wrong-gen-wrong-spec", "foo", 1234, func(cfg *v1.Configuration) {
 				cfg.Spec.GetTemplate().Name = "byo-name-wrong-gen-wrong-spec-foo"
-			}, MarkRevisionCreationFailed(`revisions.serving.knative.dev "byo-name-wrong-gen-wrong-spec-foo" already exists`), WithObservedGen),
+			}, MarkRevisionCreationFailed(`revisions.serving.knative.dev "byo-name-wrong-gen-wrong-spec-foo" already exists`), WithConfigObservedGen),
 		}},
 		Key: "foo/byo-name-wrong-gen-wrong-spec",
 	}, {
@@ -199,7 +199,7 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: cfg("byo-rev-not-owned", "foo", 1234, func(cfg *v1.Configuration) {
 				cfg.Spec.GetTemplate().Name = "byo-rev-not-owned-foo"
-			}, MarkRevisionCreationFailed(`revisions.serving.knative.dev "byo-rev-not-owned-foo" already exists`), WithObservedGen),
+			}, MarkRevisionCreationFailed(`revisions.serving.knative.dev "byo-rev-not-owned-foo" already exists`), WithConfigObservedGen),
 		}},
 		Key: "foo/byo-rev-not-owned",
 	}, {
@@ -215,7 +215,7 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: cfg("validation-failure", "foo", 1234, WithConfigContainerConcurrency(-1),
 				// Expect Revision creation to fail with the following error.
-				MarkRevisionCreationFailed("expected 0 <= -1 <= 1000: spec.containerConcurrency"), WithObservedGen),
+				MarkRevisionCreationFailed("expected 0 <= -1 <= 1000: spec.containerConcurrency"), WithConfigObservedGen),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeWarning, "CreationFailed", "Failed to create Revision: expected 0 <= -1 <= 1000: spec.containerConcurrency"),
@@ -235,18 +235,18 @@ func TestReconcile(t *testing.T) {
 			Object: cfg("matching-revision-not-done", "foo", 5432,
 				// If the Revision already exists, we still update these fields.
 				// This could happen if the prior status update failed for some reason.
-				WithLatestCreated("matching-revision-not-done-00001"), WithObservedGen),
+				WithLatestCreated("matching-revision-not-done-00001"), WithConfigObservedGen),
 		}},
 		Key: "foo/matching-revision-not-done",
 	}, {
 		Name: "reconcile revision matching generation (ready: true)",
 		Objects: []runtime.Object{
-			cfg("matching-revision-done", "foo", 5555, WithLatestCreated("matching-revision-done-00001"), WithObservedGen),
+			cfg("matching-revision-done", "foo", 5555, WithLatestCreated("matching-revision-done-00001"), WithConfigObservedGen),
 			rev("matching-revision-done", "foo", 5555,
 				WithCreationTimestamp(now), MarkRevisionReady, WithRevName("matching-revision-done-00001")),
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
-			Object: cfg("matching-revision-done", "foo", 5555, WithObservedGen,
+			Object: cfg("matching-revision-done", "foo", 5555, WithConfigObservedGen,
 				// When we see the LatestCreatedRevision become Ready, then we
 				// update the latest ready revision.
 				WithLatestCreated("matching-revision-done-00001"),
@@ -262,7 +262,7 @@ func TestReconcile(t *testing.T) {
 		Name: "reconcile revision matching generation (ready: true, idempotent)",
 		Objects: []runtime.Object{
 			cfg("matching-revision-done-idempotent", "foo", 5566,
-				WithObservedGen, WithLatestCreated("matching-revision"), WithLatestReady("matching-revision")),
+				WithConfigObservedGen, WithLatestCreated("matching-revision"), WithLatestReady("matching-revision")),
 			rev("matching-revision-done-idempotent", "foo", 5566,
 				WithCreationTimestamp(now), MarkRevisionReady, WithRevName("matching-revision")),
 		},
@@ -270,13 +270,13 @@ func TestReconcile(t *testing.T) {
 	}, {
 		Name: "reconcile revision matching generation (ready: false)",
 		Objects: []runtime.Object{
-			cfg("matching-revision-failed", "foo", 5555, WithLatestCreated("matching-revision"), WithObservedGen),
+			cfg("matching-revision-failed", "foo", 5555, WithLatestCreated("matching-revision"), WithConfigObservedGen),
 			rev("matching-revision-failed", "foo", 5555,
 				WithCreationTimestamp(now), MarkContainerMissing, WithRevName("matching-revision")),
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: cfg("matching-revision-failed", "foo", 5555,
-				WithLatestCreated("matching-revision"), WithObservedGen,
+				WithLatestCreated("matching-revision"), WithConfigObservedGen,
 				// When the LatestCreatedRevision reports back a failure,
 				// then we surface that failure.
 				MarkLatestCreatedFailed("It's the end of the world as we know it")),
@@ -289,7 +289,7 @@ func TestReconcile(t *testing.T) {
 	}, {
 		Name: "reconcile revision matching generation (ready: bad)",
 		Objects: []runtime.Object{
-			cfg("bad-condition", "foo", 5555, WithLatestCreated("bad-condition"), WithObservedGen),
+			cfg("bad-condition", "foo", 5555, WithLatestCreated("bad-condition"), WithConfigObservedGen),
 			rev("bad-condition", "foo", 5555,
 				WithRevName("bad-condition"),
 				WithRevStatus(v1.RevisionStatus{
@@ -324,7 +324,7 @@ func TestReconcile(t *testing.T) {
 			Object: cfg("create-revision-failure", "foo", 99998,
 				// When we fail to create a Revision is should be surfaced in
 				// the Configuration status.
-				MarkRevisionCreationFailed("inducing failure for create revisions"), WithObservedGen),
+				MarkRevisionCreationFailed("inducing failure for create revisions"), WithConfigObservedGen),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeWarning, "CreationFailed", "Failed to create Revision: inducing failure for create revisions"),
@@ -349,7 +349,7 @@ func TestReconcile(t *testing.T) {
 				// These would be the status updates after a first
 				// reconcile, which we use to trigger the update
 				// where we've induced a failure.
-				WithLatestCreated("update-config-failure-00001"), WithObservedGen),
+				WithLatestCreated("update-config-failure-00001"), WithConfigObservedGen),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeNormal, "Created", "Created Revision %q", "update-config-failure-00001"),
@@ -362,7 +362,7 @@ func TestReconcile(t *testing.T) {
 			cfg("revision-recovers", "foo", 1337,
 				WithLatestCreated("revision-recovers-00001"),
 				WithLatestReady("revision-recovers-00001"),
-				WithObservedGen,
+				WithConfigObservedGen,
 				MarkLatestCreatedFailed("Weebles wobble, but they don't fall down")),
 			rev("revision-recovers", "foo", 1337,
 				WithCreationTimestamp(now),
@@ -374,7 +374,7 @@ func TestReconcile(t *testing.T) {
 			Object: cfg("revision-recovers", "foo", 1337,
 				WithLatestCreated("revision-recovers-00001"),
 				WithLatestReady("revision-recovers-00001"),
-				WithObservedGen,
+				WithConfigObservedGen,
 				// When a LatestReadyRevision recovers from failure,
 				// then we should go back to Ready.
 			),
@@ -390,10 +390,10 @@ func TestReconcile(t *testing.T) {
 			// when no fix is present
 			cfg("double-trouble", "foo", 1,
 				WithLatestCreated("double-trouble-00001"),
-				WithLatestReady("double-trouble-00001"), WithObservedGen),
+				WithLatestReady("double-trouble-00001"), WithConfigObservedGen),
 			cfg("first-trouble", "foo", 1,
 				WithLatestCreated("first-trouble-00001"),
-				WithLatestReady("first-trouble-00001"), WithObservedGen),
+				WithLatestReady("first-trouble-00001"), WithConfigObservedGen),
 
 			rev("first-trouble", "foo", 1,
 				WithRevName("first-trouble-00001"),
@@ -408,7 +408,7 @@ func TestReconcile(t *testing.T) {
 		Objects: []runtime.Object{
 			cfg("threerevs", "foo", 3,
 				WithLatestCreated("threerevs-00002"),
-				WithLatestReady("threerevs-00001"), WithObservedGen, func(cfg *v1.Configuration) {
+				WithLatestReady("threerevs-00001"), WithConfigObservedGen, func(cfg *v1.Configuration) {
 					cfg.Spec.GetTemplate().Name = "threerevs-00003"
 				},
 			),
@@ -426,7 +426,7 @@ func TestReconcile(t *testing.T) {
 			Object: cfg("threerevs", "foo", 3,
 				WithLatestCreated("threerevs-00003"),
 				WithLatestReady("threerevs-00002"),
-				WithObservedGen, func(cfg *v1.Configuration) {
+				WithConfigObservedGen, func(cfg *v1.Configuration) {
 					cfg.Spec.GetTemplate().Name = "threerevs-00003"
 				},
 			),
@@ -440,7 +440,7 @@ func TestReconcile(t *testing.T) {
 		Objects: []runtime.Object{
 			cfg("revnotready", "foo", 3,
 				WithLatestCreated("revnotready-00002"),
-				WithLatestReady("revnotready-00001"), WithObservedGen, func(cfg *v1.Configuration) {
+				WithLatestReady("revnotready-00001"), WithConfigObservedGen, func(cfg *v1.Configuration) {
 					cfg.Spec.GetTemplate().Name = "revnotready-00003"
 				},
 			),
@@ -459,7 +459,7 @@ func TestReconcile(t *testing.T) {
 				// The config should NOT be ready, because LCR != LRR
 				WithLatestCreated("revnotready-00003"),
 				WithLatestReady("revnotready-00002"),
-				WithObservedGen, func(cfg *v1.Configuration) {
+				WithConfigObservedGen, func(cfg *v1.Configuration) {
 					cfg.Spec.GetTemplate().Name = "revnotready-00003"
 				},
 			),
@@ -473,7 +473,7 @@ func TestReconcile(t *testing.T) {
 		Objects: []runtime.Object{
 			cfg("lrrnotexist", "foo", 2,
 				WithLatestCreated("lrrnotexist-00002"),
-				WithLatestReady("lrrnotexist-00001"), WithObservedGen, func(cfg *v1.Configuration) {
+				WithLatestReady("lrrnotexist-00001"), WithConfigObservedGen, func(cfg *v1.Configuration) {
 					cfg.Spec.GetTemplate().Name = "lrrnotexist-00002"
 				},
 			),
@@ -488,7 +488,7 @@ func TestReconcile(t *testing.T) {
 			Object: cfg("lrrnotexist", "foo", 2,
 				WithLatestCreated("lrrnotexist-00002"),
 				WithLatestReady("lrrnotexist-00002"),
-				WithObservedGen, func(cfg *v1.Configuration) {
+				WithConfigObservedGen, func(cfg *v1.Configuration) {
 					cfg.Spec.GetTemplate().Name = "lrrnotexist-00002"
 				},
 			),
diff --git a/pkg/reconciler/gc/gc_test.go b/pkg/reconciler/gc/gc_test.go
index 709333f02..4a39b94fb 100644
--- a/pkg/reconciler/gc/gc_test.go
+++ b/pkg/reconciler/gc/gc_test.go
@@ -71,7 +71,7 @@ func TestGCReconcile(t *testing.T) {
 			cfg("keep-two", "foo", 5556,
 				WithLatestCreated("5556"),
 				WithLatestReady("5556"),
-				WithObservedGen),
+				WithConfigObservedGen),
 			rev("keep-two", "foo", 5554, MarkRevisionReady,
 				WithRevName("5554"),
 				WithCreationTimestamp(oldest),
@@ -104,7 +104,7 @@ func TestGCReconcile(t *testing.T) {
 			cfg("keep-no-last-pinned", "foo", 5556,
 				WithLatestCreated("5556"),
 				WithLatestReady("5556"),
-				WithObservedGen),
+				WithConfigObservedGen),
 			// No lastPinned so we will keep this.
 			rev("keep-no-last-pinned", "foo", 5554, MarkRevisionReady,
 				WithRevName("5554"),
@@ -125,7 +125,7 @@ func TestGCReconcile(t *testing.T) {
 			cfg("keep-recent-last-pinned", "foo", 5556,
 				WithLatestCreated("5556"),
 				WithLatestReady("5556"),
-				WithObservedGen),
+				WithConfigObservedGen),
 			rev("keep-recent-last-pinned", "foo", 5554, MarkRevisionReady,
 				WithRevName("5554"),
 				WithCreationTimestamp(oldest),
@@ -151,7 +151,7 @@ func TestGCReconcile(t *testing.T) {
 				// This comes after 'WithLatestReady' so the
 				// Configuration's 'Ready' Status is 'Unknown'
 				WithLatestCreated("5556"),
-				WithObservedGen),
+				WithConfigObservedGen),
 			rev("keep-two", "foo", 5554, MarkRevisionReady,
 				WithRevName("5554"),
 				WithCreationTimestamp(oldest),
@@ -173,7 +173,7 @@ func TestGCReconcile(t *testing.T) {
 				// Don't set the latest ready revision here
 				// since those by default are always retained
 				WithLatestCreated("keep-all"),
-				WithObservedGen),
+				WithConfigObservedGen),
 			rev("keep-all", "foo", 5554,
 				WithRevName("keep-all"),
 				WithCreationTimestamp(oldest),
diff --git a/pkg/reconciler/labeler/labeler_test.go b/pkg/reconciler/labeler/labeler_test.go
index d22b3b965..2166e78d6 100644
--- a/pkg/reconciler/labeler/labeler_test.go
+++ b/pkg/reconciler/labeler/labeler_test.go
@@ -333,7 +333,7 @@ func revTraffic(name string, latest bool) v1.TrafficTarget {
 
 func routeWithTraffic(namespace, name string, spec, status v1.TrafficTarget, opts ...RouteOption) *v1.Route {
 	return Route(namespace, name,
-		append([]RouteOption{WithSpecTraffic(spec), WithStatusTraffic(status)}, opts...)...)
+		append([]RouteOption{WithSpecTraffic(spec), WithStatusTraffic(status), WithInitRouteConditions}, opts...)...)
 }
 
 func simpleRunLatest(namespace, name, config string, opts ...RouteOption) *v1.Route {
diff --git a/pkg/reconciler/revision/table_test.go b/pkg/reconciler/revision/table_test.go
index e67402d7c..cdcd20cef 100644
--- a/pkg/reconciler/revision/table_test.go
+++ b/pkg/reconciler/revision/table_test.go
@@ -85,7 +85,7 @@ func TestReconcile(t *testing.T) {
 			Object: rev("foo", "first-reconcile",
 				// The first reconciliation Populates the following status properties.
 				WithLogURL, AllUnknownConditions, MarkDeploying("Deploying"),
-				withDefaultContainerStatuses()),
+				withDefaultContainerStatuses(), withObservedGeneration(1)),
 		}},
 		Key: "foo/first-reconcile",
 	}, {
@@ -109,7 +109,7 @@ func TestReconcile(t *testing.T) {
 			Object: rev("foo", "update-status-failure",
 				// Despite failure, the following status properties are set.
 				WithLogURL, AllUnknownConditions, MarkDeploying("Deploying"),
-				withDefaultContainerStatuses()),
+				withDefaultContainerStatuses(), withObservedGeneration(1)),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeWarning, "UpdateFailed", "Failed to update status for %q: %v",
@@ -137,7 +137,7 @@ func TestReconcile(t *testing.T) {
 			Object: rev("foo", "create-pa-failure",
 				// Despite failure, the following status properties are set.
 				WithLogURL, WithInitRevConditions,
-				MarkDeploying("Deploying"), withDefaultContainerStatuses()),
+				MarkDeploying("Deploying"), withDefaultContainerStatuses(), withObservedGeneration(1)),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeWarning, "InternalError", `failed to create PA "create-pa-failure": inducing failure for create podautoscalers`),
@@ -163,7 +163,7 @@ func TestReconcile(t *testing.T) {
 			Object: rev("foo", "create-user-deploy-failure",
 				// Despite failure, the following status properties are set.
 				WithLogURL, WithInitRevConditions,
-				MarkDeploying("Deploying"), withDefaultContainerStatuses()),
+				MarkDeploying("Deploying"), withDefaultContainerStatuses(), withObservedGeneration(1)),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeWarning, "InternalError",
@@ -178,7 +178,7 @@ func TestReconcile(t *testing.T) {
 		// are necessary.
 		Objects: []runtime.Object{
 			rev("foo", "stable-reconcile", WithLogURL, AllUnknownConditions,
-				withDefaultContainerStatuses()),
+				withDefaultContainerStatuses(), withObservedGeneration(1)),
 			pa("foo", "stable-reconcile", WithReachability(asv1a1.ReachabilityUnknown)),
 
 			deploy(t, "foo", "stable-reconcile"),
@@ -192,7 +192,7 @@ func TestReconcile(t *testing.T) {
 		// with our desired spec.
 		Objects: []runtime.Object{
 			rev("foo", "fix-containers",
-				WithLogURL, AllUnknownConditions, withDefaultContainerStatuses()),
+				WithLogURL, AllUnknownConditions, withDefaultContainerStatuses(), withObservedGeneration(1)),
 			pa("foo", "fix-containers", WithReachability(asv1a1.ReachabilityUnknown)),
 			changeContainers(deploy(t, "foo", "fix-containers")),
 			image("foo", "fix-containers"),
@@ -211,7 +211,7 @@ func TestReconcile(t *testing.T) {
 		Objects: []runtime.Object{
 			rev("foo", "failure-update-deploy",
 				withK8sServiceName("whateves"), WithLogURL, AllUnknownConditions,
-				withDefaultContainerStatuses()),
+				withDefaultContainerStatuses(), withObservedGeneration(1)),
 			pa("foo", "failure-update-deploy"),
 			changeContainers(deploy(t, "foo", "failure-update-deploy")),
 			image("foo", "failure-update-deploy"),
@@ -233,7 +233,7 @@ func TestReconcile(t *testing.T) {
 			rev("foo", "stable-deactivation",
 				WithLogURL, MarkRevisionReady,
 				MarkInactive("NoTraffic", "This thing is inactive."),
-				withDefaultContainerStatuses()),
+				withDefaultContainerStatuses(), withObservedGeneration(1)),
 			pa("foo", "stable-deactivation",
 				WithNoTraffic("NoTraffic", "This thing is inactive.")),
 			deploy(t, "foo", "stable-deactivation"),
@@ -254,7 +254,7 @@ func TestReconcile(t *testing.T) {
 				WithLogURL,
 				// When the endpoint and pa are ready, then we will see the
 				// Revision become ready.
-				MarkRevisionReady, withDefaultContainerStatuses()),
+				MarkRevisionReady, withDefaultContainerStatuses(), withObservedGeneration(1)),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeNormal, "RevisionReady", "Revision becomes ready upon all resources being ready"),
@@ -266,7 +266,7 @@ func TestReconcile(t *testing.T) {
 		Objects: []runtime.Object{
 			rev("foo", "pa-not-ready",
 				withK8sServiceName("somebody-told-me"), WithLogURL,
-				MarkRevisionReady),
+				MarkRevisionReady, withObservedGeneration(1)),
 			pa("foo", "pa-not-ready",
 				WithPAStatusService("its-not-confidential"),
 				WithBufferedTraffic("Something", "This is something longer")),
@@ -280,6 +280,7 @@ func TestReconcile(t *testing.T) {
 				// When we reconcile a ready state and our pa is in an activating
 				// state, we should see the following mutation.
 				MarkActivating("Something", "This is something longer"),
+				withObservedGeneration(1),
 			),
 		}},
 		Key: "foo/pa-not-ready",
@@ -288,7 +289,8 @@ func TestReconcile(t *testing.T) {
 		// Test propagating the inactivity signal from the pa to the Revision.
 		Objects: []runtime.Object{
 			rev("foo", "pa-inactive",
-				withK8sServiceName("something-in-the-way"), WithLogURL, MarkRevisionReady),
+				withK8sServiceName("something-in-the-way"), WithLogURL,
+				MarkRevisionReady, withObservedGeneration(1)),
 			pa("foo", "pa-inactive",
 				WithNoTraffic("NoTraffic", "This thing is inactive.")),
 			readyDeploy(deploy(t, "foo", "pa-inactive")),
@@ -299,7 +301,7 @@ func TestReconcile(t *testing.T) {
 				WithLogURL, MarkRevisionReady, withDefaultContainerStatuses(),
 				// When we reconcile an "all ready" revision when the PA
 				// is inactive, we should see the following change.
-				MarkInactive("NoTraffic", "This thing is inactive.")),
+				MarkInactive("NoTraffic", "This thing is inactive."), withObservedGeneration(1)),
 		}},
 		Key: "foo/pa-inactive",
 	}, {
@@ -308,7 +310,8 @@ func TestReconcile(t *testing.T) {
 		// But propagate the service name.
 		Objects: []runtime.Object{
 			rev("foo", "pa-inactive",
-				withK8sServiceName("here-comes-the-sun"), WithLogURL, MarkRevisionReady),
+				withK8sServiceName("here-comes-the-sun"), WithLogURL,
+				MarkRevisionReady, withObservedGeneration(1)),
 			pa("foo", "pa-inactive",
 				WithNoTraffic("NoTraffic", "This thing is inactive."),
 				WithPAStatusService("pa-inactive-svc")),
@@ -322,7 +325,7 @@ func TestReconcile(t *testing.T) {
 				// When we reconcile an "all ready" revision when the PA
 				// is inactive, we should see the following change.
 				MarkInactive("NoTraffic", "This thing is inactive."),
-				withDefaultContainerStatuses()),
+				withDefaultContainerStatuses(), withObservedGeneration(1)),
 		}},
 		Key: "foo/pa-inactive",
 	}, {
@@ -344,12 +347,15 @@ func TestReconcile(t *testing.T) {
 				// When our reconciliation has to change the service
 				// we should see the following mutations to status.
 				withK8sServiceName("fix-mutated-pa"), WithLogURL, MarkRevisionReady,
-				withDefaultContainerStatuses()),
+				withDefaultContainerStatuses(), withObservedGeneration(1)),
 		}},
 		WantUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: pa("foo", "fix-mutated-pa", WithTraffic,
 				WithPAStatusService("fix-mutated-pa")),
 		}},
+		WantEvents: []string{
+			Eventf(corev1.EventTypeNormal, "RevisionReady", "Revision becomes ready upon all resources being ready"),
+		},
 		Key: "foo/fix-mutated-pa",
 	}, {
 		Name: "mutated pa gets error during the fix",
@@ -357,7 +363,7 @@ func TestReconcile(t *testing.T) {
 		Objects: []runtime.Object{
 			rev("foo", "fix-mutated-pa-fail",
 				withK8sServiceName("some-old-stuff"),
-				WithLogURL, AllUnknownConditions, withDefaultContainerStatuses()),
+				WithLogURL, AllUnknownConditions, withDefaultContainerStatuses(), withObservedGeneration(1)),
 			pa("foo", "fix-mutated-pa-fail", WithProtocolType(networking.ProtocolH2C), WithReachability(asv1a1.ReachabilityUnknown)),
 			deploy(t, "foo", "fix-mutated-pa-fail"),
 			image("foo", "fix-mutated-pa-fail"),
@@ -392,7 +398,8 @@ func TestReconcile(t *testing.T) {
 				WithLogURL, AllUnknownConditions,
 				// When the revision is reconciled after a Deployment has
 				// timed out, we should see it marked with the PDE state.
-				MarkProgressDeadlineExceeded("I timed out!"), withDefaultContainerStatuses()),
+				MarkProgressDeadlineExceeded("I timed out!"), withDefaultContainerStatuses(),
+				withObservedGeneration(1)),
 		}},
 		Key: "foo/deploy-timeout",
 	}, {
@@ -414,7 +421,7 @@ func TestReconcile(t *testing.T) {
 				// When the revision is reconciled after a Deployment has
 				// timed out, we should see it marked with the FailedCreate state.
 				MarkResourcesUnavailable("FailedCreate", "I replica failed!"),
-				withDefaultContainerStatuses()),
+				withDefaultContainerStatuses(), withObservedGeneration(1)),
 		}},
 		Key: "foo/deploy-replica-failure",
 	}, {
@@ -431,7 +438,7 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: rev("foo", "pull-backoff",
 				WithLogURL, AllUnknownConditions,
-				MarkResourcesUnavailable("ImagePullBackoff", "can't pull it"), withDefaultContainerStatuses()),
+				MarkResourcesUnavailable("ImagePullBackoff", "can't pull it"), withDefaultContainerStatuses(), withObservedGeneration(1)),
 		}},
 		WantUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: pa("foo", "pull-backoff", WithReachability(asv1a1.ReachabilityUnreachable)),
@@ -454,7 +461,7 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: rev("foo", "pod-error",
 				WithLogURL, AllUnknownConditions, MarkContainerExiting(5,
-					v1.RevisionContainerExitingMessage("I failed man!")), withDefaultContainerStatuses()),
+					v1.RevisionContainerExitingMessage("I failed man!")), withDefaultContainerStatuses(), withObservedGeneration(1)),
 		}},
 		Key: "foo/pod-error",
 	}, {
@@ -473,7 +480,7 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: rev("foo", "pod-schedule-error",
 				WithLogURL, AllUnknownConditions, MarkResourcesUnavailable("Insufficient energy",
-					"Unschedulable"), withDefaultContainerStatuses()),
+					"Unschedulable"), withDefaultContainerStatuses(), withObservedGeneration(1)),
 		}},
 		Key: "foo/pod-schedule-error",
 	}, {
@@ -493,7 +500,7 @@ func TestReconcile(t *testing.T) {
 			Object: rev("foo", "steady-ready", withK8sServiceName("steadier-even"), WithLogURL,
 				// All resources are ready to go, we should see the revision being
 				// marked ready
-				MarkRevisionReady, withDefaultContainerStatuses()),
+				MarkRevisionReady, withDefaultContainerStatuses(), withObservedGeneration(1)),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeNormal, "RevisionReady", "Revision becomes ready upon all resources being ready"),
@@ -513,7 +520,7 @@ func TestReconcile(t *testing.T) {
 			Object: rev("foo", "missing-owners", withK8sServiceName("lesser-revision"), WithLogURL,
 				MarkRevisionReady,
 				// When we're missing the OwnerRef for PodAutoscaler we see this update.
-				MarkResourceNotOwned("PodAutoscaler", "missing-owners"), withDefaultContainerStatuses()),
+				MarkResourceNotOwned("PodAutoscaler", "missing-owners"), withDefaultContainerStatuses(), withObservedGeneration(1)),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeWarning, "InternalError", `revision: "missing-owners" does not own PodAutoscaler: "missing-owners"`),
@@ -533,7 +540,7 @@ func TestReconcile(t *testing.T) {
 			Object: rev("foo", "missing-owners", withK8sServiceName("youre-gonna-lose"), WithLogURL,
 				MarkRevisionReady,
 				// When we're missing the OwnerRef for Deployment we see this update.
-				MarkResourceNotOwned("Deployment", "missing-owners-deployment"), withDefaultContainerStatuses()),
+				MarkResourceNotOwned("Deployment", "missing-owners-deployment"), withDefaultContainerStatuses(), withObservedGeneration(1)),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeWarning, "InternalError", `revision: "missing-owners" does not own Deployment: "missing-owners-deployment"`),
@@ -553,7 +560,7 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: rev("foo", "image-pull-secrets",
 				WithImagePullSecrets("foo-secret"),
-				WithLogURL, AllUnknownConditions, MarkDeploying("Deploying"), withDefaultContainerStatuses()),
+				WithLogURL, AllUnknownConditions, MarkDeploying("Deploying"), withDefaultContainerStatuses(), withObservedGeneration(1)),
 		}},
 		Key: "foo/image-pull-secrets",
 	}}
@@ -635,9 +642,10 @@ func changeContainers(deploy *appsv1.Deployment) *appsv1.Deployment {
 func rev(namespace, name string, ro ...RevisionOption) *v1.Revision {
 	r := &v1.Revision{
 		ObjectMeta: metav1.ObjectMeta{
-			Name:      name,
-			Namespace: namespace,
-			UID:       "test-uid",
+			Name:       name,
+			Namespace:  namespace,
+			UID:        "test-uid",
+			Generation: 1,
 		},
 		Spec: v1.RevisionSpec{
 			PodSpec: corev1.PodSpec{
@@ -662,6 +670,12 @@ func withK8sServiceName(sn string) RevisionOption {
 	}
 }
 
+func withObservedGeneration(gen int64) RevisionOption {
+	return func(r *v1.Revision) {
+		r.Status.ObservedGeneration = gen
+	}
+}
+
 func withDefaultContainerStatuses() RevisionOption {
 	return func(r *v1.Revision) {
 		r.Status.ContainerStatuses = []v1.ContainerStatuses{{
diff --git a/pkg/reconciler/route/table_test.go b/pkg/reconciler/route/table_test.go
index ec9b9a9b7..1c98a11d3 100644
--- a/pkg/reconciler/route/table_test.go
+++ b/pkg/reconciler/route/table_test.go
@@ -76,8 +76,8 @@ func TestReconcile(t *testing.T) {
 	}, {
 		Name: "configuration not yet ready",
 		Objects: []runtime.Object{
-			Route("default", "first-reconcile", WithConfigTarget("not-ready")),
-			cfg("default", "not-ready", WithGeneration(1), WithLatestCreated("not-ready-00001")),
+			Route("default", "first-reconcile", WithConfigTarget("not-ready"), WithRouteGeneration(1)),
+			cfg("default", "not-ready", WithConfigGeneration(1), WithLatestCreated("not-ready-00001")),
 			rev("default", "not-ready", 1, WithInitRevConditions, WithRevName("not-ready-00001")),
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
@@ -90,9 +90,9 @@ func TestReconcile(t *testing.T) {
 	}, {
 		Name: "configuration permanently failed",
 		Objects: []runtime.Object{
-			Route("default", "first-reconcile", WithConfigTarget("permanently-failed")),
+			Route("default", "first-reconcile", WithConfigTarget("permanently-failed"), WithRouteGeneration(1)),
 			cfg("default", "permanently-failed",
-				WithGeneration(1), WithLatestCreated("permanently-failed-00001"), MarkLatestCreatedFailed("blah")),
+				WithConfigGeneration(1), WithLatestCreated("permanently-failed-00001"), MarkLatestCreatedFailed("blah")),
 			rev("default", "permanently-failed", 1,
 				WithRevName("permanently-failed-00001"),
 				WithInitRevConditions, MarkContainerMissing),
@@ -109,8 +109,8 @@ func TestReconcile(t *testing.T) {
 			InduceFailure("update", "routes"),
 		},
 		Objects: []runtime.Object{
-			Route("default", "first-reconcile", WithConfigTarget("not-ready")),
-			cfg("default", "not-ready", WithGeneration(1), WithLatestCreated("not-ready-00001")),
+			Route("default", "first-reconcile", WithConfigTarget("not-ready"), WithRouteGeneration(1)),
+			cfg("default", "not-ready", WithConfigGeneration(1), WithLatestCreated("not-ready-00001")),
 			rev("default", "not-ready", 1, WithInitRevConditions, WithRevName("not-ready-00001")),
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
@@ -125,15 +125,15 @@ func TestReconcile(t *testing.T) {
 	}, {
 		Name: "simple route becomes ready, ingress unknown",
 		Objects: []runtime.Object{
-			Route("default", "becomes-ready", WithConfigTarget("config"), WithRouteUID("12-34")),
+			Route("default", "becomes-ready", WithConfigTarget("config"), WithRouteUID("12-34"), WithRouteGeneration(1)),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithServiceName("mcd")),
 		},
 		WantCreates: []runtime.Object{
 			simpleIngress(
 				Route("default", "becomes-ready", WithConfigTarget("config"), WithURL,
-					WithRouteUID("12-34")),
+					WithRouteUID("12-34"), WithRouteGeneration(1)),
 				&traffic.Config{
 					Targets: map[string]traffic.RevisionTargets{
 						traffic.DefaultTarget: {{
@@ -156,7 +156,7 @@ func TestReconcile(t *testing.T) {
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "becomes-ready", WithConfigTarget("config"),
-				WithRouteUID("12-34"),
+				WithRouteUID("12-34"), WithRouteGeneration(1), WithRouteObservedGeneration,
 				// Populated by reconciliation when all traffic has been assigned.
 				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
 				MarkTrafficAssigned, MarkIngressNotConfigured, WithStatusTraffic(
@@ -174,9 +174,9 @@ func TestReconcile(t *testing.T) {
 	}, {
 		Name: "simple route, ingress failed",
 		Objects: []runtime.Object{
-			Route("default", "ingress-failed", WithConfigTarget("config"), WithRouteUID("12-34")),
+			Route("default", "ingress-failed", WithConfigTarget("config"), WithRouteUID("12-34"), WithRouteGeneration(1)),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithServiceName("mcd")),
 			simpleIngress(
 				Route("default", "ingress-failed", WithConfigTarget("config"), WithURL,
@@ -203,7 +203,7 @@ func TestReconcile(t *testing.T) {
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "ingress-failed", WithConfigTarget("config"),
-				WithRouteUID("12-34"),
+				WithRouteUID("12-34"), WithRouteGeneration(1), WithRouteObservedGeneration,
 				// Populated by reconciliation when all traffic has been assigned.
 				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled, WithInitRouteConditions,
 				MarkTrafficAssigned,
@@ -222,9 +222,10 @@ func TestReconcile(t *testing.T) {
 		Name: "custom ingress route becomes ready, ingress unknown",
 		Objects: []runtime.Object{
 			Route("default", "becomes-ready",
-				WithConfigTarget("config"), WithRouteUID("12-34"), WithIngressClass("custom-ingress-class")),
+				WithConfigTarget("config"), WithRouteUID("12-34"), WithIngressClass("custom-ingress-class"),
+				WithRouteGeneration(1)),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithServiceName("bk")),
 		},
 		WantCreates: []runtime.Object{
@@ -255,6 +256,7 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "becomes-ready", WithConfigTarget("config"),
 				WithRouteUID("12-34"), WithIngressClass("custom-ingress-class"),
+				WithRouteGeneration(1), WithRouteObservedGeneration,
 				// Populated by reconciliation when all traffic has been assigned.
 				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
 				MarkTrafficAssigned, MarkIngressNotConfigured, WithStatusTraffic(
@@ -274,9 +276,9 @@ func TestReconcile(t *testing.T) {
 		Objects: []runtime.Object{
 			Route("default", "becomes-ready", WithConfigTarget("config"), WithLocalDomain,
 				WithRouteLabel(map[string]string{"serving.knative.dev/visibility": "cluster-local"}),
-				WithRouteUID("65-23")),
+				WithRouteUID("65-23"), WithRouteGeneration(1)),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithServiceName("tb")),
 		},
 		WantCreates: []runtime.Object{
@@ -305,13 +307,13 @@ func TestReconcile(t *testing.T) {
 				getContext(),
 				Route("default", "becomes-ready", WithConfigTarget("config"), WithLocalDomain,
 					WithRouteLabel(map[string]string{"serving.knative.dev/visibility": "cluster-local"}),
-					WithRouteUID("65-23")),
+					WithRouteUID("65-23"), WithRouteGeneration(1)),
 				"",
 			),
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "becomes-ready", WithConfigTarget("config"),
-				WithRouteUID("65-23"),
+				WithRouteUID("65-23"), WithRouteGeneration(1), WithRouteObservedGeneration,
 				// Populated by reconciliation when all traffic has been assigned.
 				WithLocalDomain, WithAddress, WithRouteConditionsAutoTLSDisabled,
 				WithRouteLabel(map[string]string{"serving.knative.dev/visibility": "cluster-local"}),
@@ -330,9 +332,9 @@ func TestReconcile(t *testing.T) {
 	}, {
 		Name: "simple route becomes ready",
 		Objects: []runtime.Object{
-			Route("default", "becomes-ready", WithConfigTarget("config")),
+			Route("default", "becomes-ready", WithConfigTarget("config"), WithRouteGeneration(1)),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001")),
 			simpleReadyIngress(
 				Route("default", "becomes-ready", WithConfigTarget("config"), WithURL),
@@ -360,7 +362,7 @@ func TestReconcile(t *testing.T) {
 			Object: Route("default", "becomes-ready", WithConfigTarget("config"),
 				// Populated by reconciliation when the route becomes ready.
 				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady, WithStatusTraffic(
+				MarkTrafficAssigned, MarkIngressReady, WithRouteObservedGeneration, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
 						Percent:        ptr.Int64(100),
@@ -379,9 +381,9 @@ func TestReconcile(t *testing.T) {
 			InduceFailure("create", "services"),
 		},
 		Objects: []runtime.Object{
-			Route("default", "create-svc-failure", WithConfigTarget("config"), WithRouteFinalizer),
+			Route("default", "create-svc-failure", WithConfigTarget("config"), WithRouteFinalizer, WithRouteGeneration(1)),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001")),
 		},
 		WantCreates: []runtime.Object{
@@ -409,9 +411,9 @@ func TestReconcile(t *testing.T) {
 	}, {
 		Name: "failure creating ingress",
 		Objects: []runtime.Object{
-			Route("default", "ingress-create-failure", WithConfigTarget("config"), WithRouteFinalizer),
+			Route("default", "ingress-create-failure", WithConfigTarget("config"), WithRouteFinalizer, WithRouteGeneration(1)),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithServiceName("astrid")),
 		},
 		// We induce a failure creating the Ingress.
@@ -447,7 +449,7 @@ func TestReconcile(t *testing.T) {
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "ingress-create-failure", WithConfigTarget("config"),
-				WithRouteFinalizer,
+				WithRouteFinalizer, WithRouteGeneration(1), WithRouteObservedGeneration,
 				// Populated by reconciliation when we fail to create the ingress.
 				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
 				MarkTrafficAssigned, WithStatusTraffic(
@@ -468,7 +470,7 @@ func TestReconcile(t *testing.T) {
 		Objects: []runtime.Object{
 			Route("default", "steady-state", WithConfigTarget("config"),
 				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady,
+				MarkTrafficAssigned, MarkIngressReady, WithRouteGeneration(1), WithRouteObservedGeneration,
 				WithRouteFinalizer, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
@@ -476,7 +478,7 @@ func TestReconcile(t *testing.T) {
 						LatestRevision: ptr.Bool(true),
 					})),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
 				// The Route controller attaches our label to this Configuration.
 				WithConfigLabel("serving.knative.dev/route", "steady-state"),
 			),
@@ -504,14 +506,14 @@ func TestReconcile(t *testing.T) {
 		WantErr: true,
 		Objects: []runtime.Object{
 			Route("default", "unhappy-owner", WithConfigTarget("config"),
-				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady, WithRouteFinalizer, WithStatusTraffic(
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled, WithRouteGeneration(1),
+				MarkTrafficAssigned, MarkIngressReady, WithRouteObservedGeneration, WithRouteFinalizer, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName: "config-00001",
 						Percent:      ptr.Int64(100),
 					})),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
 				// The Route controller attaches our label to this Configuration.
 				WithConfigLabel("serving.knative.dev/route", "unhappy-owner"),
 			),
@@ -521,8 +523,8 @@ func TestReconcile(t *testing.T) {
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "unhappy-owner", WithConfigTarget("config"),
-				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady, WithRouteFinalizer, WithStatusTraffic(
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled, WithRouteGeneration(1),
+				MarkTrafficAssigned, MarkIngressReady, WithRouteObservedGeneration, WithRouteFinalizer, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
 						Percent:        ptr.Int64(100),
@@ -542,7 +544,7 @@ func TestReconcile(t *testing.T) {
 		Name: "different labels, different domain - steady state",
 		Objects: []runtime.Object{
 			Route("default", "different-domain", WithConfigTarget("config"),
-				WithAnotherDomain, WithAddress,
+				WithAnotherDomain, WithAddress, WithRouteGeneration(1), WithRouteObservedGeneration,
 				WithRouteConditionsAutoTLSDisabled, MarkTrafficAssigned, MarkIngressReady,
 				WithRouteFinalizer, WithStatusTraffic(
 					v1.TrafficTarget{
@@ -551,7 +553,7 @@ func TestReconcile(t *testing.T) {
 						LatestRevision: ptr.Bool(true),
 					}), WithRouteLabel(map[string]string{"app": "prod"})),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
 				// The Route controller attaches our label to this Configuration.
 				WithConfigLabel("serving.knative.dev/route", "different-domain"),
 			),
@@ -579,7 +581,7 @@ func TestReconcile(t *testing.T) {
 			{
 				Object: simpleReadyIngress(
 					Route("default", "different-domain", WithConfigTarget("config"),
-						WithAnotherDomain),
+						WithAnotherDomain, WithRouteGeneration(1)),
 					&traffic.Config{
 						Targets: map[string]traffic.RevisionTargets{
 							traffic.DefaultTarget: {{
@@ -605,15 +607,15 @@ func TestReconcile(t *testing.T) {
 		Name: "new latest created revision",
 		Objects: []runtime.Object{
 			Route("default", "new-latest-created", WithConfigTarget("config"),
-				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady, WithRouteFinalizer, WithStatusTraffic(
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled, WithRouteGeneration(1),
+				MarkTrafficAssigned, MarkIngressReady, WithRouteObservedGeneration, WithRouteFinalizer, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
 						Percent:        ptr.Int64(100),
 						LatestRevision: ptr.Bool(true),
 					})),
 			cfg("default", "config",
-				WithGeneration(2), WithLatestReady("config-00001"), WithLatestCreated("config-00002"),
+				WithConfigGeneration(2), WithLatestReady("config-00001"), WithLatestCreated("config-00002"),
 				WithConfigLabel("serving.knative.dev/route", "new-latest-created"),
 			),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithServiceName("daisy")),
@@ -643,14 +645,14 @@ func TestReconcile(t *testing.T) {
 		Name: "new latest ready revision",
 		Objects: []runtime.Object{
 			Route("default", "new-latest-ready", WithConfigTarget("config"),
-				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady, WithRouteFinalizer, WithStatusTraffic(
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled, WithRouteGeneration(1),
+				MarkTrafficAssigned, MarkIngressReady, WithRouteObservedGeneration, WithRouteFinalizer, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName: "config-00001",
 						Percent:      ptr.Int64(100),
 					})),
 			cfg("default", "config",
-				WithGeneration(2), WithLatestCreated("config-00002"), WithLatestReady("config-00002"),
+				WithConfigGeneration(2), WithLatestCreated("config-00002"), WithLatestReady("config-00002"),
 				// The Route controller attaches our label to this Configuration.
 				WithConfigLabel("serving.knative.dev/route", "new-latest-ready"),
 			),
@@ -678,7 +680,7 @@ func TestReconcile(t *testing.T) {
 		// A new LatestReadyRevisionName on the Configuration should result in the new Revision being rolled out.
 		WantUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: simpleReadyIngress(
-				Route("default", "new-latest-ready", WithConfigTarget("config"), WithURL),
+				Route("default", "new-latest-ready", WithConfigTarget("config"), WithURL, WithRouteGeneration(1)),
 				&traffic.Config{
 					Targets: map[string]traffic.RevisionTargets{
 						traffic.DefaultTarget: {{
@@ -696,8 +698,8 @@ func TestReconcile(t *testing.T) {
 		}},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "new-latest-ready", WithConfigTarget("config"),
-				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady, WithRouteFinalizer, WithStatusTraffic(
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled, WithRouteGeneration(1),
+				MarkTrafficAssigned, MarkIngressReady, WithRouteObservedGeneration, WithRouteFinalizer, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00002",
 						Percent:        ptr.Int64(100),
@@ -708,11 +710,11 @@ func TestReconcile(t *testing.T) {
 	}, {
 		Name: "public becomes cluster local",
 		Objects: []runtime.Object{
-			Route("default", "becomes-local", WithConfigTarget("config"),
+			Route("default", "becomes-local", WithConfigTarget("config"), WithRouteGeneration(1),
 				WithRouteLabel(map[string]string{"serving.knative.dev/visibility": "cluster-local"}),
 				WithRouteUID("65-23")),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithServiceName("tb")),
 			simpleIngress(
 				Route("default", "becomes-local", WithConfigTarget("config"), WithRouteUID("65-23"), WithRouteLabel(map[string]string{"serving.knative.dev/visibility": "cluster-local"})),
@@ -736,7 +738,7 @@ func TestReconcile(t *testing.T) {
 		},
 		WantUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: simpleIngress(
-				Route("default", "becomes-local", WithConfigTarget("config"),
+				Route("default", "becomes-local", WithConfigTarget("config"), WithRouteGeneration(1),
 					WithRouteUID("65-23"),
 					WithRouteLabel(map[string]string{"serving.knative.dev/visibility": "cluster-local"})),
 				&traffic.Config{
@@ -759,7 +761,7 @@ func TestReconcile(t *testing.T) {
 		}},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "becomes-local", WithConfigTarget("config"),
-				WithRouteUID("65-23"),
+				WithRouteUID("65-23"), WithRouteGeneration(1), WithRouteObservedGeneration,
 				MarkTrafficAssigned, MarkIngressNotConfigured,
 				WithLocalDomain, WithAddress, WithRouteConditionsAutoTLSDisabled,
 				WithRouteLabel(map[string]string{"serving.knative.dev/visibility": "cluster-local"}),
@@ -775,9 +777,9 @@ func TestReconcile(t *testing.T) {
 		Name: "cluster local becomes public",
 		Objects: []runtime.Object{
 			Route("default", "becomes-public", WithConfigTarget("config"),
-				WithRouteUID("65-23")),
+				WithRouteUID("65-23"), WithRouteGeneration(1)),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithServiceName("tb")),
 			simpleIngress(
 				Route("default", "becomes-public", WithConfigTarget("config"), WithRouteUID("65-23"),
@@ -805,7 +807,8 @@ func TestReconcile(t *testing.T) {
 		WantUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: simpleIngress(
 				Route("default", "becomes-public", WithConfigTarget("config"),
-					WithRouteUID("65-23"), WithRouteLabel(map[string]string{"serving.knative.dev/visibility": "cluster-local"})),
+					WithRouteUID("65-23"), WithRouteGeneration(1), WithRouteObservedGeneration,
+					WithRouteLabel(map[string]string{"serving.knative.dev/visibility": "cluster-local"})),
 				&traffic.Config{
 					Targets: map[string]traffic.RevisionTargets{
 						traffic.DefaultTarget: {{
@@ -823,7 +826,7 @@ func TestReconcile(t *testing.T) {
 		}},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "becomes-public", WithConfigTarget("config"),
-				WithRouteUID("65-23"),
+				WithRouteUID("65-23"), WithRouteGeneration(1), WithRouteObservedGeneration,
 				MarkTrafficAssigned, MarkIngressNotConfigured,
 				WithAddress, WithRouteConditionsAutoTLSDisabled, WithURL,
 				WithStatusTraffic(
@@ -843,14 +846,14 @@ func TestReconcile(t *testing.T) {
 		},
 		Objects: []runtime.Object{
 			Route("default", "update-ci-failure", WithConfigTarget("config"),
-				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady, WithRouteFinalizer, WithStatusTraffic(
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled, WithRouteGeneration(1),
+				MarkTrafficAssigned, MarkIngressReady, WithRouteObservedGeneration, WithRouteFinalizer, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName: "config-00001",
 						Percent:      ptr.Int64(100),
 					})),
 			cfg("default", "config",
-				WithGeneration(2), WithLatestCreated("config-00002"), WithLatestReady("config-00002"),
+				WithConfigGeneration(2), WithLatestCreated("config-00002"), WithLatestReady("config-00002"),
 				// The Route controller attaches our label to this Configuration.
 				WithConfigLabel("serving.knative.dev/route", "update-ci-failure"),
 			),
@@ -877,7 +880,7 @@ func TestReconcile(t *testing.T) {
 		},
 		WantUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: simpleReadyIngress(
-				Route("default", "update-ci-failure", WithConfigTarget("config"), WithURL),
+				Route("default", "update-ci-failure", WithConfigTarget("config"), WithURL, WithRouteGeneration(1)),
 				&traffic.Config{
 					Targets: map[string]traffic.RevisionTargets{
 						traffic.DefaultTarget: {{
@@ -895,8 +898,8 @@ func TestReconcile(t *testing.T) {
 		}},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "update-ci-failure", WithConfigTarget("config"),
-				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady, WithRouteFinalizer, WithStatusTraffic(
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled, WithRouteGeneration(1),
+				MarkTrafficAssigned, MarkIngressReady, WithRouteObservedGeneration, WithRouteFinalizer, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00002",
 						Percent:        ptr.Int64(100),
@@ -911,15 +914,15 @@ func TestReconcile(t *testing.T) {
 		Name: "reconcile service mutation",
 		Objects: []runtime.Object{
 			Route("default", "svc-mutation", WithConfigTarget("config"),
-				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady, WithRouteFinalizer, WithStatusTraffic(
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled, WithRouteGeneration(1),
+				MarkTrafficAssigned, MarkIngressReady, WithRouteObservedGeneration, WithRouteFinalizer, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
 						Percent:        ptr.Int64(100),
 						LatestRevision: ptr.Bool(true),
 					})),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
 				// The Route controller attaches our label to this Configuration.
 				WithConfigLabel("serving.knative.dev/route", "svc-mutation"),
 			),
@@ -955,15 +958,15 @@ func TestReconcile(t *testing.T) {
 		},
 		Objects: []runtime.Object{
 			Route("default", "svc-mutation", WithConfigTarget("config"), WithRouteFinalizer,
-				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady, WithStatusTraffic(
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled, WithRouteGeneration(1),
+				MarkTrafficAssigned, MarkIngressReady, WithRouteObservedGeneration, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
 						Percent:        ptr.Int64(100),
 						LatestRevision: ptr.Bool(true),
 					})),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
 				// The Route controller attaches our label to this Configuration.
 				WithConfigLabel("serving.knative.dev/route", "svc-mutation"),
 			),
@@ -1000,15 +1003,15 @@ func TestReconcile(t *testing.T) {
 		Name: "drop cluster ip",
 		Objects: []runtime.Object{
 			Route("default", "cluster-ip", WithConfigTarget("config"), WithRouteFinalizer,
-				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady, WithStatusTraffic(
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled, WithRouteGeneration(1),
+				MarkTrafficAssigned, MarkIngressReady, WithRouteObservedGeneration, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
 						Percent:        ptr.Int64(100),
 						LatestRevision: ptr.Bool(true),
 					})),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
 				// The Route controller attaches our label to this Configuration.
 				WithConfigLabel("serving.knative.dev/route", "cluster-ip"),
 			),
@@ -1040,15 +1043,15 @@ func TestReconcile(t *testing.T) {
 		Name: "fix external name",
 		Objects: []runtime.Object{
 			Route("default", "external-name", WithConfigTarget("config"), WithRouteFinalizer,
-				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady, WithStatusTraffic(
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled, WithRouteGeneration(1),
+				MarkTrafficAssigned, MarkIngressReady, WithRouteObservedGeneration, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
 						Percent:        ptr.Int64(100),
 						LatestRevision: ptr.Bool(true),
 					})),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
 				// The Route controller attaches our label to this Configuration.
 				WithConfigLabel("serving.knative.dev/route", "external-name"),
 			),
@@ -1079,15 +1082,15 @@ func TestReconcile(t *testing.T) {
 		Name: "reconcile ingress mutation",
 		Objects: []runtime.Object{
 			Route("default", "ingress-mutation", WithConfigTarget("config"), WithRouteFinalizer,
-				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady, WithStatusTraffic(
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled, WithRouteGeneration(1),
+				MarkTrafficAssigned, MarkIngressReady, WithRouteObservedGeneration, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
 						Percent:        ptr.Int64(100),
 						LatestRevision: ptr.Bool(true),
 					})),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
 				// The Route controller attaches our label to this Configuration.
 				WithConfigLabel("serving.knative.dev/route", "ingress-mutation"),
 			),
@@ -1112,7 +1115,7 @@ func TestReconcile(t *testing.T) {
 		},
 		WantUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: simpleReadyIngress(
-				Route("default", "ingress-mutation", WithConfigTarget("config"), WithURL),
+				Route("default", "ingress-mutation", WithConfigTarget("config"), WithURL, WithRouteGeneration(1)),
 				&traffic.Config{
 					Targets: map[string]traffic.RevisionTargets{
 						traffic.DefaultTarget: {{
@@ -1134,20 +1137,20 @@ func TestReconcile(t *testing.T) {
 		Objects: []runtime.Object{
 			// The status reflects "oldconfig", but the spec "newconfig".
 			Route("default", "change-configs", WithConfigTarget("newconfig"), WithRouteFinalizer,
-				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady, WithStatusTraffic(
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled, WithRouteGeneration(1),
+				MarkTrafficAssigned, MarkIngressReady, WithRouteObservedGeneration, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName: "oldconfig-00001",
 						Percent:      ptr.Int64(100),
 					})),
 			// Both configs exist, but only "oldconfig" is labelled.
 			cfg("default", "oldconfig",
-				WithGeneration(1), WithLatestCreated("oldconfig-00001"), WithLatestReady("oldconfig-00001"),
+				WithConfigGeneration(1), WithLatestCreated("oldconfig-00001"), WithLatestReady("oldconfig-00001"),
 				// The Route controller attaches our label to this Configuration.
 				WithConfigLabel("serving.knative.dev/route", "change-configs"),
 			),
 			cfg("default", "newconfig",
-				WithGeneration(1), WithLatestCreated("newconfig-00001"), WithLatestReady("newconfig-00001")),
+				WithConfigGeneration(1), WithLatestCreated("newconfig-00001"), WithLatestReady("newconfig-00001")),
 			rev("default", "oldconfig", 1, MarkRevisionReady, WithRevName("oldconfig-00001"), WithServiceName("greenwood")),
 			rev("default", "newconfig", 1, MarkRevisionReady, WithRevName("newconfig-00001"), WithServiceName("broadview")),
 			simpleReadyIngress(
@@ -1171,7 +1174,7 @@ func TestReconcile(t *testing.T) {
 		WantUpdates: []clientgotesting.UpdateActionImpl{{
 			// Updated to point to "newconfig" things.
 			Object: simpleReadyIngress(
-				Route("default", "change-configs", WithConfigTarget("newconfig"), WithURL),
+				Route("default", "change-configs", WithConfigTarget("newconfig"), WithURL, WithRouteGeneration(1)),
 				&traffic.Config{
 					Targets: map[string]traffic.RevisionTargets{
 						traffic.DefaultTarget: {{
@@ -1190,8 +1193,8 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			// Status updated to "newconfig"
 			Object: Route("default", "change-configs", WithConfigTarget("newconfig"),
-				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady, WithRouteFinalizer, WithStatusTraffic(
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled, WithRouteGeneration(1),
+				MarkTrafficAssigned, MarkIngressReady, WithRouteObservedGeneration, WithRouteFinalizer, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "newconfig-00001",
 						Percent:        ptr.Int64(100),
@@ -1202,10 +1205,11 @@ func TestReconcile(t *testing.T) {
 	}, {
 		Name: "configuration missing",
 		Objects: []runtime.Object{
-			Route("default", "config-missing", WithConfigTarget("not-found")),
+			Route("default", "config-missing", WithConfigTarget("not-found"), WithRouteGeneration(1)),
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "config-missing", WithConfigTarget("not-found"), WithURL,
+				WithRouteGeneration(1), WithRouteObservedGeneration,
 				WithInitRouteConditions, MarkMissingTrafficTarget("Configuration", "not-found")),
 		}},
 		PostConditions: []func(*testing.T, *TableRow){
@@ -1215,9 +1219,9 @@ func TestReconcile(t *testing.T) {
 	}, {
 		Name: "revision missing (direct)",
 		Objects: []runtime.Object{
-			Route("default", "missing-revision-direct", WithRevTarget("not-found")),
+			Route("default", "missing-revision-direct", WithRevTarget("not-found"), WithRouteGeneration(1)),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "missing-revision-direct", WithRevTarget("not-found"), WithURL,
@@ -1230,9 +1234,9 @@ func TestReconcile(t *testing.T) {
 	}, {
 		Name: "revision missing (indirect)",
 		Objects: []runtime.Object{
-			Route("default", "missing-revision-indirect", WithConfigTarget("config")),
+			Route("default", "missing-revision-indirect", WithConfigTarget("config"), WithRouteGeneration(1)),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "missing-revision-indirect", WithConfigTarget("config"), WithURL,
@@ -1245,9 +1249,10 @@ func TestReconcile(t *testing.T) {
 			Route("default", "pinned-becomes-ready",
 				// Use the Revision name from the config
 				WithRevTarget("config-00001"), WithRouteFinalizer,
+				WithRouteGeneration(1),
 			),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001")),
 			simpleK8sService(Route("default", "pinned-becomes-ready", WithConfigTarget("config"))),
 			simpleReadyIngress(
@@ -1270,9 +1275,9 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "pinned-becomes-ready",
 				// Use the Revision name from the config
-				WithRevTarget("config-00001"), WithRouteFinalizer,
+				WithRevTarget("config-00001"), WithRouteFinalizer, WithRouteGeneration(1),
 				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady, WithStatusTraffic(
+				MarkTrafficAssigned, MarkIngressReady, WithRouteObservedGeneration, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
 						Percent:        ptr.Int64(100),
@@ -1283,7 +1288,7 @@ func TestReconcile(t *testing.T) {
 	}, {
 		Name: "traffic split becomes ready",
 		Objects: []runtime.Object{
-			Route("default", "named-traffic-split", WithSpecTraffic(
+			Route("default", "named-traffic-split", WithRouteGeneration(1), WithSpecTraffic(
 				v1.TrafficTarget{
 					ConfigurationName: "blue",
 					Percent:           ptr.Int64(50),
@@ -1292,15 +1297,15 @@ func TestReconcile(t *testing.T) {
 					Percent:           ptr.Int64(50),
 				}), WithRouteUID("34-78"), WithRouteFinalizer),
 			cfg("default", "blue",
-				WithGeneration(1), WithLatestCreated("blue-00001"), WithLatestReady("blue-00001")),
+				WithConfigGeneration(1), WithLatestCreated("blue-00001"), WithLatestReady("blue-00001")),
 			cfg("default", "green",
-				WithGeneration(1), WithLatestCreated("green-00001"), WithLatestReady("green-00001")),
+				WithConfigGeneration(1), WithLatestCreated("green-00001"), WithLatestReady("green-00001")),
 			rev("default", "blue", 1, MarkRevisionReady, WithRevName("blue-00001"), WithServiceName("blue-ridge")),
 			rev("default", "green", 1, MarkRevisionReady, WithRevName("green-00001"), WithServiceName("green-lake")),
 		},
 		WantCreates: []runtime.Object{
 			simpleIngress(
-				Route("default", "named-traffic-split", WithURL, WithSpecTraffic(
+				Route("default", "named-traffic-split", WithURL, WithRouteGeneration(1), WithSpecTraffic(
 					v1.TrafficTarget{
 						ConfigurationName: "blue",
 						Percent:           ptr.Int64(50),
@@ -1332,20 +1337,22 @@ func TestReconcile(t *testing.T) {
 			),
 			simplePlaceholderK8sService(
 				getContext(),
-				Route("default", "named-traffic-split", WithSpecTraffic(
-					v1.TrafficTarget{
-						ConfigurationName: "blue",
-						Percent:           ptr.Int64(50),
-					},
-					v1.TrafficTarget{
-						ConfigurationName: "green",
-						Percent:           ptr.Int64(50),
-					}), WithRouteUID("34-78"), WithRouteFinalizer),
+				Route("default", "named-traffic-split", WithRouteGeneration(1),
+					WithSpecTraffic(
+						v1.TrafficTarget{
+							ConfigurationName: "blue",
+							Percent:           ptr.Int64(50),
+						},
+						v1.TrafficTarget{
+							ConfigurationName: "green",
+							Percent:           ptr.Int64(50),
+						}), WithRouteUID("34-78"), WithRouteFinalizer),
 				"",
 			),
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "named-traffic-split", WithRouteFinalizer,
+				WithRouteGeneration(1), WithRouteObservedGeneration,
 				WithSpecTraffic(v1.TrafficTarget{
 					ConfigurationName: "blue",
 					Percent:           ptr.Int64(50),
@@ -1373,23 +1380,8 @@ func TestReconcile(t *testing.T) {
 	}, {
 		Name: "same revision targets",
 		Objects: []runtime.Object{
-			Route("default", "same-revision-targets", WithSpecTraffic(
-				v1.TrafficTarget{
-					Tag:               "gray",
-					ConfigurationName: "gray",
-					Percent:           ptr.Int64(50),
-				}, v1.TrafficTarget{
-					Tag:          "also-gray",
-					RevisionName: "gray-00001",
-					Percent:      ptr.Int64(50),
-				}), WithRouteUID("1-2"), WithRouteFinalizer),
-			cfg("default", "gray",
-				WithGeneration(1), WithLatestCreated("gray-00001"), WithLatestReady("gray-00001")),
-			rev("default", "gray", 1, MarkRevisionReady, WithRevName("gray-00001"), WithServiceName("shades")),
-		},
-		WantCreates: []runtime.Object{
-			simpleIngress(
-				Route("default", "same-revision-targets", WithURL, WithSpecTraffic(
+			Route("default", "same-revision-targets", WithRouteGeneration(1),
+				WithSpecTraffic(
 					v1.TrafficTarget{
 						Tag:               "gray",
 						ConfigurationName: "gray",
@@ -1398,7 +1390,24 @@ func TestReconcile(t *testing.T) {
 						Tag:          "also-gray",
 						RevisionName: "gray-00001",
 						Percent:      ptr.Int64(50),
-					}), WithRouteUID("1-2")),
+					}), WithRouteUID("1-2"), WithRouteFinalizer),
+			cfg("default", "gray",
+				WithConfigGeneration(1), WithLatestCreated("gray-00001"), WithLatestReady("gray-00001")),
+			rev("default", "gray", 1, MarkRevisionReady, WithRevName("gray-00001"), WithServiceName("shades")),
+		},
+		WantCreates: []runtime.Object{
+			simpleIngress(
+				Route("default", "same-revision-targets", WithURL, WithRouteGeneration(1),
+					WithSpecTraffic(
+						v1.TrafficTarget{
+							Tag:               "gray",
+							ConfigurationName: "gray",
+							Percent:           ptr.Int64(50),
+						}, v1.TrafficTarget{
+							Tag:          "also-gray",
+							RevisionName: "gray-00001",
+							Percent:      ptr.Int64(50),
+						}), WithRouteUID("1-2")),
 				&traffic.Config{
 					Targets: map[string]traffic.RevisionTargets{
 						traffic.DefaultTarget: {{
@@ -1433,17 +1442,19 @@ func TestReconcile(t *testing.T) {
 			),
 			simplePlaceholderK8sService(
 				getContext(),
-				Route("default", "same-revision-targets", WithSpecTraffic(
-					v1.TrafficTarget{
-						Tag:               "gray",
-						ConfigurationName: "gray",
-						Percent:           ptr.Int64(50),
-					},
-					v1.TrafficTarget{
-						Tag:          "also-gray",
-						RevisionName: "gray-00001",
-						Percent:      ptr.Int64(50),
-					}), WithRouteUID("1-2"), WithRouteFinalizer),
+				Route("default", "same-revision-targets",
+					WithRouteGeneration(1), WithRouteObservedGeneration,
+					WithSpecTraffic(
+						v1.TrafficTarget{
+							Tag:               "gray",
+							ConfigurationName: "gray",
+							Percent:           ptr.Int64(50),
+						},
+						v1.TrafficTarget{
+							Tag:          "also-gray",
+							RevisionName: "gray-00001",
+							Percent:      ptr.Int64(50),
+						}), WithRouteUID("1-2"), WithRouteFinalizer),
 				"",
 			),
 			simplePlaceholderK8sService(
@@ -1463,22 +1474,23 @@ func TestReconcile(t *testing.T) {
 			),
 			simplePlaceholderK8sService(
 				getContext(),
-				Route("default", "same-revision-targets", WithSpecTraffic(
-					v1.TrafficTarget{
-						Tag:               "gray",
-						ConfigurationName: "gray",
-						Percent:           ptr.Int64(50),
-					},
-					v1.TrafficTarget{
-						Tag:          "also-gray",
-						RevisionName: "gray-00001",
-						Percent:      ptr.Int64(50),
-					}), WithRouteUID("1-2"), WithRouteFinalizer),
+				Route("default", "same-revision-targets", WithRouteGeneration(1),
+					WithSpecTraffic(
+						v1.TrafficTarget{
+							Tag:               "gray",
+							ConfigurationName: "gray",
+							Percent:           ptr.Int64(50),
+						},
+						v1.TrafficTarget{
+							Tag:          "also-gray",
+							RevisionName: "gray-00001",
+							Percent:      ptr.Int64(50),
+						}), WithRouteUID("1-2"), WithRouteFinalizer),
 				"gray",
 			),
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
-			Object: Route("default", "same-revision-targets",
+			Object: Route("default", "same-revision-targets", WithRouteGeneration(1), WithRouteGeneration(1), WithRouteObservedGeneration,
 				WithSpecTraffic(
 					v1.TrafficTarget{
 						Tag:               "gray",
@@ -1526,7 +1538,8 @@ func TestReconcile(t *testing.T) {
 		Objects: []runtime.Object{
 			Route("default", "switch-configs", WithConfigTarget("green"),
 				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady, WithStatusTraffic(
+				MarkTrafficAssigned, MarkIngressReady, WithRouteGeneration(1), WithRouteObservedGeneration,
+				WithStatusTraffic(
 					v1.TrafficTarget{
 						Tag:          "blue",
 						RevisionName: "blue-00001",
@@ -1534,12 +1547,12 @@ func TestReconcile(t *testing.T) {
 						URL:          url("http://blue.example.com"),
 					}), WithRouteFinalizer),
 			cfg("default", "blue",
-				WithGeneration(1), WithLatestCreated("blue-00001"), WithLatestReady("blue-00001"),
+				WithConfigGeneration(1), WithLatestCreated("blue-00001"), WithLatestReady("blue-00001"),
 				// The Route controller attaches our label to this Configuration.
 				WithConfigLabel("serving.knative.dev/route", "switch-configs"),
 			),
 			cfg("default", "green",
-				WithGeneration(1), WithLatestCreated("green-00001"), WithLatestReady("green-00001")),
+				WithConfigGeneration(1), WithLatestCreated("green-00001"), WithLatestReady("green-00001")),
 			rev("default", "blue", 1, MarkRevisionReady, WithRevName("blue-00001"), WithServiceName("alki-beach")),
 			rev("default", "green", 1, MarkRevisionReady, WithRevName("green-00001"), WithServiceName("rainier-beach")),
 			simpleReadyIngress(
@@ -1581,7 +1594,7 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "switch-configs", WithConfigTarget("green"),
 				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady, WithStatusTraffic(
+				MarkTrafficAssigned, MarkIngressReady, WithRouteObservedGeneration, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "green-00001",
 						Percent:        ptr.Int64(100),
@@ -1596,6 +1609,8 @@ func TestReconcile(t *testing.T) {
 		Objects: []runtime.Object{
 			Route("default", "split", WithURL, WithAddress,
 				WithInitRouteConditions, MarkTrafficAssigned, MarkIngressReady,
+				WithRouteObservedGeneration,
+				WithRouteGeneration(1),
 				WithSpecTraffic(
 					v1.TrafficTarget{
 						ConfigurationName: "blue",
@@ -1611,9 +1626,9 @@ func TestReconcile(t *testing.T) {
 						Percent:      ptr.Int64(100),
 					},
 				)),
-			cfg("default", "blue", WithGeneration(1),
+			cfg("default", "blue", WithConfigGeneration(1),
 				WithLatestCreated("blue-00001"), WithLatestReady("blue-00001")),
-			cfg("default", "green", WithGeneration(1),
+			cfg("default", "green", WithConfigGeneration(1),
 				WithLatestCreated("green-00001")),
 			rev("default", "blue", 1, MarkRevisionReady, WithRevName("blue-00001")),
 			rev("default", "green", 1, WithRevName("green-00001")),
@@ -1623,6 +1638,7 @@ func TestReconcile(t *testing.T) {
 			Object: Route("default", "split", WithURL, WithAddress,
 				WithInitRouteConditions, MarkTrafficAssigned, MarkIngressReady,
 				MarkConfigurationNotReady("green"),
+				WithRouteGeneration(1), WithRouteObservedGeneration,
 				WithSpecTraffic(
 					v1.TrafficTarget{
 						ConfigurationName: "blue",
@@ -1644,14 +1660,16 @@ func TestReconcile(t *testing.T) {
 		Objects: []runtime.Object{
 			Route("default", "stale-lastpinned", WithConfigTarget("config"),
 				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady, WithRouteFinalizer, WithStatusTraffic(
+				MarkTrafficAssigned, MarkIngressReady, WithRouteFinalizer,
+				WithRouteGeneration(1), WithRouteObservedGeneration,
+				WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
 						Percent:        ptr.Int64(100),
 						LatestRevision: ptr.Bool(true),
 					})),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
 				// The Route controller attaches our label to this Configuration.
 				WithConfigLabel("serving.knative.dev/route", "stale-lastpinned"),
 			),
@@ -1684,14 +1702,15 @@ func TestReconcile(t *testing.T) {
 		Objects: []runtime.Object{
 			Route("default", "old-naming", WithConfigTarget("config"), WithRouteFinalizer,
 				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady, WithStatusTraffic(
+				MarkTrafficAssigned, MarkIngressReady, WithRouteGeneration(1), WithRouteObservedGeneration,
+				WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
 						Percent:        ptr.Int64(100),
 						LatestRevision: ptr.Bool(true),
 					})),
 			cfg("default", "config",
-				WithGeneration(1),
+				WithConfigGeneration(1),
 				WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
 				// The Route controller attaches our label to this Configuration.
 				WithConfigLabel("serving.knative.dev/route", "old-naming"),
@@ -1721,14 +1740,16 @@ func TestReconcile(t *testing.T) {
 			Route("default", "my-route", WithConfigTarget("config"),
 				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
 				MarkTrafficAssigned, MarkIngressReady,
-				WithRouteFinalizer, WithStatusTraffic(
+				WithRouteGeneration(1), WithRouteObservedGeneration,
+				WithRouteFinalizer,
+				WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
 						Percent:        ptr.Int64(100),
 						LatestRevision: ptr.Bool(true),
 					})),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
 				// The Route controller attaches our label to this Configuration.
 				WithConfigLabel("serving.knative.dev/route", "steady-state"),
 			),
@@ -1773,15 +1794,16 @@ func TestReconcile(t *testing.T) {
 		Objects: []runtime.Object{
 			Route("default", "my-route", WithConfigTarget("config"),
 				WithURL, WithAddress, WithInitRouteConditions,
-				MarkTrafficAssigned, MarkIngressReady,
-				WithRouteFinalizer, WithStatusTraffic(
+				MarkTrafficAssigned, MarkIngressReady, WithRouteFinalizer,
+				WithRouteGeneration(1), WithRouteObservedGeneration,
+				WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
 						Percent:        ptr.Int64(100),
 						LatestRevision: ptr.Bool(true),
 					})),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
 				// The Route controller attaches our label to this Configuration.
 				WithConfigLabel("serving.knative.dev/route", "steady-state"),
 			),
@@ -1837,7 +1859,7 @@ func TestReconcile_EnableAutoTLS(t *testing.T) {
 			wildcardCert("default", "example.com"),
 			Route("default", "becomes-ready", WithConfigTarget("config"), WithRouteUID("12-34")),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithServiceName("mcd")),
 		},
 		WantCreates: []runtime.Object{
@@ -1871,7 +1893,7 @@ func TestReconcile_EnableAutoTLS(t *testing.T) {
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "becomes-ready", WithConfigTarget("config"),
-				WithRouteUID("12-34"),
+				WithRouteUID("12-34"), WithRouteGeneration(1), WithRouteObservedGeneration,
 				// Populated by reconciliation when all traffic has been assigned.
 				WithAddress, WithInitRouteConditions,
 				MarkTrafficAssigned, MarkIngressNotConfigured, WithStatusTraffic(
@@ -1891,7 +1913,7 @@ func TestReconcile_EnableAutoTLS(t *testing.T) {
 		Objects: []runtime.Object{
 			Route("default", "becomes-ready", WithConfigTarget("config"), WithRouteUID("12-34")),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithServiceName("mcd")),
 		},
 		WantCreates: []runtime.Object{
@@ -1944,7 +1966,7 @@ func TestReconcile_EnableAutoTLS(t *testing.T) {
 		Objects: []runtime.Object{
 			Route("default", "becomes-ready", WithConfigTarget("config"), WithRouteUID("12-34")),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithServiceName("mcd")),
 			certificateWithStatus(resources.MakeCertificates(Route("default", "becomes-ready", WithConfigTarget("config"), WithURL, WithRouteUID("12-34")),
 				map[string]string{"becomes-ready.default.example.com": ""}, network.CertManagerCertificateClassName)[0], readyCertStatus()),
@@ -2002,7 +2024,7 @@ func TestReconcile_EnableAutoTLS(t *testing.T) {
 		Objects: []runtime.Object{
 			Route("default", "becomes-ready", WithConfigTarget("config"), WithRouteUID("12-34")),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithServiceName("mcd")),
 			// MakeCertificates will create a certificate with DNS name "*.test-ns.example.com" which is not the host name
 			// needed by the input Route.
@@ -2083,7 +2105,7 @@ func TestReconcile_EnableAutoTLS(t *testing.T) {
 		Objects: []runtime.Object{
 			Route("default", "becomes-ready", WithConfigTarget("config"), WithRouteUID("12-34")),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithServiceName("mcd")),
 			&netv1alpha1.Certificate{
 				ObjectMeta: metav1.ObjectMeta{
@@ -2176,9 +2198,9 @@ func TestReconcile_EnableAutoTLS(t *testing.T) {
 		Name:    "check that Route updates status and produces event log when valid name but not owned certificate",
 		WantErr: true,
 		Objects: []runtime.Object{
-			Route("default", "becomes-ready", WithConfigTarget("config"), WithRouteUID("12-34")),
+			Route("default", "becomes-ready", WithConfigTarget("config"), WithRouteUID("12-34"), WithRouteGeneration(1)),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithServiceName("mcd")),
 			&netv1alpha1.Certificate{
 				ObjectMeta: metav1.ObjectMeta{
@@ -2201,11 +2223,12 @@ func TestReconcile_EnableAutoTLS(t *testing.T) {
 		},
 		WantDeleteCollections: []clientgotesting.DeleteCollectionActionImpl{},
 		WantCreates: []runtime.Object{
-			simplePlaceholderK8sService(getContext(), Route("default", "becomes-ready", WithConfigTarget("config"), WithRouteUID("12-34")), ""),
+			simplePlaceholderK8sService(getContext(), Route("default", "becomes-ready", WithConfigTarget("config"), WithRouteUID("12-34"), WithRouteGeneration(1)), ""),
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "becomes-ready", WithConfigTarget("config"),
 				WithRouteUID("12-34"),
+				WithRouteGeneration(1), WithRouteObservedGeneration,
 				WithAddress, WithInitRouteConditions, WithURL,
 				MarkTrafficAssigned, WithStatusTraffic(
 					v1.TrafficTarget{
@@ -2222,9 +2245,9 @@ func TestReconcile_EnableAutoTLS(t *testing.T) {
 	}, {
 		Name: "check that Route is correctly updated when Certificate is not ready",
 		Objects: []runtime.Object{
-			Route("default", "becomes-ready", WithConfigTarget("config"), WithRouteUID("12-34")),
+			Route("default", "becomes-ready", WithConfigTarget("config"), WithRouteUID("12-34"), WithRouteGeneration(1)),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithServiceName("mcd")),
 			// MakeCertificates will create a certificate with DNS name "*.test-ns.example.com" which is not the host name
 			// needed by the input Route.
@@ -2250,7 +2273,7 @@ func TestReconcile_EnableAutoTLS(t *testing.T) {
 		WantCreates: []runtime.Object{
 			ingressWithTLS(
 				Route("default", "becomes-ready", WithConfigTarget("config"), WithURL,
-					WithRouteUID("12-34")),
+					WithRouteUID("12-34"), WithRouteGeneration(1)),
 				&traffic.Config{
 					Targets: map[string]traffic.RevisionTargets{
 						traffic.DefaultTarget: {{
@@ -2278,7 +2301,7 @@ func TestReconcile_EnableAutoTLS(t *testing.T) {
 		}},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "becomes-ready", WithConfigTarget("config"),
-				WithRouteUID("12-34"),
+				WithRouteUID("12-34"), WithRouteGeneration(1), WithRouteObservedGeneration,
 				// Populated by reconciliation when all traffic has been assigned.
 				WithAddress, WithRouteConditionsHTTPDowngrade,
 				MarkTrafficAssigned, MarkIngressNotConfigured, WithStatusTraffic(
@@ -2300,11 +2323,11 @@ func TestReconcile_EnableAutoTLS(t *testing.T) {
 		// This test is a same with "public becomes cluster local" above, but confirm it does not create certs with autoTLS for cluster-local.
 		Name: "public becomes cluster local w/ autoTLS",
 		Objects: []runtime.Object{
-			Route("default", "becomes-local", WithConfigTarget("config"),
+			Route("default", "becomes-local", WithConfigTarget("config"), WithRouteGeneration(1),
 				WithRouteLabel(map[string]string{serving.VisibilityLabelKey: serving.VisibilityClusterLocal}),
 				WithRouteUID("65-23")),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithServiceName("tb")),
 			simpleIngress(
 				Route("default", "becomes-local", WithConfigTarget("config"), WithRouteUID("65-23"), WithRouteLabel(map[string]string{"serving.knative.dev/visibility": "cluster-local"})),
@@ -2329,7 +2352,7 @@ func TestReconcile_EnableAutoTLS(t *testing.T) {
 		WantUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: simpleIngress(
 				Route("default", "becomes-local", WithConfigTarget("config"),
-					WithRouteUID("65-23"),
+					WithRouteUID("65-23"), WithRouteGeneration(1), WithRouteObservedGeneration,
 					WithRouteLabel(map[string]string{serving.VisibilityLabelKey: serving.VisibilityClusterLocal})),
 				&traffic.Config{
 					Targets: map[string]traffic.RevisionTargets{
@@ -2352,6 +2375,7 @@ func TestReconcile_EnableAutoTLS(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "becomes-local", WithConfigTarget("config"),
 				WithRouteUID("65-23"),
+				WithRouteGeneration(1), WithRouteObservedGeneration,
 				MarkTrafficAssigned, MarkIngressNotConfigured, WithRouteConditionsTLSNotEnabledForClusterLocalMessage,
 				WithLocalDomain, WithAddress, WithInitRouteConditions,
 				WithRouteLabel(map[string]string{serving.VisibilityLabelKey: serving.VisibilityClusterLocal}),
@@ -2409,7 +2433,7 @@ func TestReconcile_EnableAutoTLS_HTTPDisabled(t *testing.T) {
 		Objects: []runtime.Object{
 			Route("default", "becomes-ready", WithConfigTarget("config"), WithRouteUID("12-34")),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithServiceName("mcd")),
 			// MakeCertificates will create a certificate with DNS name "*.test-ns.example.com" which is not the host name
 			// needed by the input Route.
diff --git a/pkg/reconciler/service/service_test.go b/pkg/reconciler/service/service_test.go
index cd8d3bab8..7490135ab 100644
--- a/pkg/reconciler/service/service_test.go
+++ b/pkg/reconciler/service/service_test.go
@@ -70,10 +70,10 @@ func TestReconcile(t *testing.T) {
 	}, {
 		Name: "inline - byo rev name used in traffic serialize, with retry",
 		Objects: []runtime.Object{
-			DefaultService("byo-rev", "foo", WithNamedRevision),
+			DefaultService("byo-rev", "foo", WithNamedRevision, WithServiceGeneration(1)),
 			config("byo-rev", "foo",
 				WithNamedRevision,
-				WithGeneration(2)),
+				WithConfigGeneration(2)),
 		},
 		// Route should not be created until config progresses
 		WantCreates: []runtime.Object{},
@@ -81,11 +81,13 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: DefaultService("byo-rev", "foo", WithNamedRevision,
 				// Route conditions should be at init state while Config should be OutOfDate
-				WithInitSvcConditions, WithOutOfDateConfig),
+				WithInitSvcConditions, WithOutOfDateConfig,
+				WithServiceGeneration(1), WithServiceObservedGeneration),
 		}, {
 			Object: DefaultService("byo-rev", "foo", WithNamedRevision,
 				// Route conditions should be at init state while Config should be OutOfDate
-				WithInitSvcConditions, WithOutOfDateConfig),
+				WithInitSvcConditions, WithOutOfDateConfig,
+				WithServiceGeneration(1), WithServiceObservedGeneration),
 		}},
 		WithReactors: []clientgotesting.ReactionFunc{
 			func(action clientgotesting.Action) (handled bool, ret runtime.Object, err error) {
@@ -99,10 +101,9 @@ func TestReconcile(t *testing.T) {
 	}, {
 		Name: "inline - byo rev name - existing revision - same spec",
 		Objects: []runtime.Object{
-			DefaultService("byo-rev", "foo", WithNamedRevision,
-				WithServiceGeneration(2), WithServiceObservedGeneration),
+			DefaultService("byo-rev", "foo", WithNamedRevision, WithServiceGeneration(2)),
 			config("byo-rev", "foo", WithNamedRevision,
-				WithGeneration(2), WithObservedGen),
+				WithConfigGeneration(2), WithConfigObservedGen),
 			route("byo-rev", "foo", WithNamedRevision,
 				WithRouteGeneration(2), WithRouteObservedGeneration),
 			&v1.Revision{
@@ -125,33 +126,33 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: DefaultService("byo-rev", "foo", WithNamedRevision,
 				// The first reconciliation will initialize the status conditions.
-				WithInitSvcConditions),
+				WithInitSvcConditions, WithServiceObservedGenFailure,
+				WithServiceGeneration(2), WithServiceObservedGeneration),
 		}},
 	}, {
 		Name: "inline - byo rev name - existing older revision with same spec",
 		Objects: []runtime.Object{
-			DefaultService("byo-rev", "foo", WithNamedRevision,
-				WithServiceGeneration(2), WithServiceObservedGeneration),
+			DefaultService("byo-rev", "foo", WithNamedRevision, WithServiceGeneration(2)),
 			config("byo-rev", "foo", WithNamedRevision,
-				WithGeneration(2), WithObservedGen),
+				WithConfigGeneration(2), WithConfigObservedGen),
 			route("byo-rev", "foo", WithNamedRevision,
 				WithRouteGeneration(2), WithRouteObservedGeneration),
 			rev("byo-rev", "foo", WithNamedRevision,
 				// Older Revision
-				WithGeneration(1), WithObservedGen),
+				WithConfigGeneration(1), WithConfigObservedGen),
 		},
 		Key: "foo/byo-rev",
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: DefaultService("byo-rev", "foo",
-				WithNamedRevision, WithInitSvcConditions),
+				WithNamedRevision, WithInitSvcConditions, WithServiceObservedGenFailure,
+				WithServiceGeneration(2), WithServiceObservedGeneration),
 		}},
 	}, {
 		Name: "inline - byo rev name - existing older revision with different spec",
 		Objects: []runtime.Object{
-			DefaultService("byo-rev", "foo", WithNamedRevision,
-				WithServiceGeneration(2), WithServiceObservedGeneration),
+			DefaultService("byo-rev", "foo", WithNamedRevision, WithServiceGeneration(2)),
 			config("byo-rev", "foo", WithNamedRevision,
-				WithGeneration(2), WithObservedGen),
+				WithConfigGeneration(2), WithConfigObservedGen),
 			route("byo-rev", "foo", WithNamedRevision,
 				WithRouteGeneration(2), WithRouteObservedGeneration),
 
@@ -174,12 +175,14 @@ func TestReconcile(t *testing.T) {
 		Key: "foo/byo-rev",
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: DefaultService("byo-rev", "foo", WithNamedRevision,
-				WithInitSvcConditions, MarkRevisionNameTaken),
+				WithInitSvcConditions, MarkRevisionNameTaken,
+				WithServiceGeneration(2), WithServiceObservedGeneration,
+			),
 		}},
 	}, {
 		Name: "inline - byo rev name used in traffic",
 		Objects: []runtime.Object{
-			DefaultService("byo-rev", "foo", WithNamedRevision),
+			DefaultService("byo-rev", "foo", WithNamedRevision, WithServiceGeneration(1)),
 		},
 		Key: "foo/byo-rev",
 		WantCreates: []runtime.Object{
@@ -189,7 +192,8 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: DefaultService("byo-rev", "foo", WithNamedRevision,
 				// The first reconciliation will initialize the status conditions.
-				WithInitSvcConditions),
+				WithInitSvcConditions, WithServiceObservedGenFailure,
+				WithServiceGeneration(1), WithServiceObservedGeneration),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeNormal, "Created", "Created Configuration %q", "byo-rev"),
@@ -198,7 +202,7 @@ func TestReconcile(t *testing.T) {
 	}, {
 		Name: "create route and configuration",
 		Objects: []runtime.Object{
-			DefaultService("run-latest", "foo", WithRunLatestRollout),
+			DefaultService("run-latest", "foo", WithRunLatestRollout, WithServiceGeneration(1)),
 		},
 		Key: "foo/run-latest",
 		WantCreates: []runtime.Object{
@@ -208,7 +212,8 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: DefaultService("run-latest", "foo", WithRunLatestRollout,
 				// The first reconciliation will initialize the status conditions.
-				WithInitSvcConditions),
+				WithInitSvcConditions, WithServiceObservedGenFailure,
+				WithServiceGeneration(1), WithServiceObservedGeneration),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeNormal, "Created", "Created Configuration %q", "run-latest"),
@@ -217,7 +222,8 @@ func TestReconcile(t *testing.T) {
 	}, {
 		Name: "steady state",
 		Objects: []runtime.Object{
-			DefaultService("no-updates", "foo", WithRunLatestRollout, WithInitSvcConditions),
+			DefaultService("no-updates", "foo", WithRunLatestRollout, WithInitSvcConditions,
+				WithServiceGeneration(1), WithServiceObservedGeneration),
 			route("no-updates", "foo", WithRunLatestRollout),
 			config("no-updates", "foo", WithRunLatestRollout),
 		},
@@ -271,7 +277,8 @@ func TestReconcile(t *testing.T) {
 	}, {
 		Name: "update route and configuration",
 		Objects: []runtime.Object{
-			DefaultService("update-route-and-config", "foo", WithRunLatestRollout, WithInitSvcConditions),
+			DefaultService("update-route-and-config", "foo", WithRunLatestRollout,
+				WithInitSvcConditions),
 			// Mutate the Config/Route to have a different body than we want.
 			config("update-route-and-config", "foo", WithRunLatestRollout,
 				// This is just an unexpected mutation of the config spec vs. the service spec.
@@ -289,7 +296,7 @@ func TestReconcile(t *testing.T) {
 		Objects: []runtime.Object{
 			DefaultService("update-route-and-config", "foo", WithRunLatestRollout, func(svc *v1.Service) {
 				svc.Spec.GetTemplate().Name = "update-route-and-config-blah"
-			}, WithInitSvcConditions),
+			}, WithInitSvcConditions, WithServiceGeneration(1)),
 			// Mutate the Config/Route to have a different body than we want.
 			config("update-route-and-config", "foo", WithRunLatestRollout,
 				// Change the concurrency to ensure it is corrected.
@@ -313,15 +320,17 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: DefaultService("update-route-and-config", "foo", WithRunLatestRollout, func(svc *v1.Service) {
 				svc.Spec.GetTemplate().Name = "update-route-and-config-blah"
-			}, WithInitSvcConditions, func(svc *v1.Service) {
-				svc.Status.MarkRevisionNameTaken("update-route-and-config-blah")
-			}),
+			}, WithInitSvcConditions, WithServiceGeneration(1), WithServiceObservedGeneration,
+				func(svc *v1.Service) {
+					svc.Status.MarkRevisionNameTaken("update-route-and-config-blah")
+				}),
 		}},
 	}, {
 		Name: "update route and configuration labels",
 		Objects: []runtime.Object{
 			// Mutate the Service to add some more labels
-			DefaultService("update-route-and-config-labels", "foo", WithRunLatestRollout, WithInitSvcConditions, WithServiceLabel("new-label", "new-value")),
+			DefaultService("update-route-and-config-labels", "foo", WithRunLatestRollout,
+				WithInitSvcConditions, WithServiceLabel("new-label", "new-value")),
 			config("update-route-and-config-labels", "foo", WithRunLatestRollout),
 			route("update-route-and-config-labels", "foo", WithRunLatestRollout),
 		},
@@ -382,7 +391,7 @@ func TestReconcile(t *testing.T) {
 			InduceFailure("create", "routes"),
 		},
 		Objects: []runtime.Object{
-			DefaultService("create-route-failure", "foo", WithRunLatestRollout),
+			DefaultService("create-route-failure", "foo", WithRunLatestRollout, WithServiceGeneration(1)),
 		},
 		Key: "foo/create-route-failure",
 		WantCreates: []runtime.Object{
@@ -392,7 +401,8 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: DefaultService("create-route-failure", "foo", WithRunLatestRollout,
 				// First reconcile initializes conditions.
-				WithInitSvcConditions),
+				WithInitSvcConditions, WithServiceObservedGenFailure,
+				WithServiceGeneration(1), WithServiceObservedGeneration),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeNormal, "Created", "Created Configuration %q", "create-route-failure"),
@@ -408,7 +418,7 @@ func TestReconcile(t *testing.T) {
 			InduceFailure("create", "configurations"),
 		},
 		Objects: []runtime.Object{
-			DefaultService("create-config-failure", "foo", WithRunLatestRollout),
+			DefaultService("create-config-failure", "foo", WithRunLatestRollout, WithServiceGeneration(1)),
 		},
 		Key: "foo/create-config-failure",
 		WantCreates: []runtime.Object{
@@ -418,7 +428,8 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: DefaultService("create-config-failure", "foo", WithRunLatestRollout,
 				// First reconcile initializes conditions.
-				WithInitSvcConditions),
+				WithInitSvcConditions, WithServiceObservedGenFailure,
+				WithServiceGeneration(1), WithServiceObservedGeneration),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeWarning, "CreationFailed", "Failed to create Configuration %q: %v",
@@ -433,7 +444,8 @@ func TestReconcile(t *testing.T) {
 			InduceFailure("update", "routes"),
 		},
 		Objects: []runtime.Object{
-			DefaultService("update-route-failure", "foo", WithRunLatestRollout, WithInitSvcConditions),
+			DefaultService("update-route-failure", "foo", WithRunLatestRollout,
+				WithInitSvcConditions),
 			// Mutate the Route to have an unexpected body to trigger an update.
 			route("update-route-failure", "foo", WithRunLatestRollout, MutateRoute),
 			config("update-route-failure", "foo", WithRunLatestRollout),
@@ -453,7 +465,8 @@ func TestReconcile(t *testing.T) {
 			InduceFailure("update", "configurations"),
 		},
 		Objects: []runtime.Object{
-			DefaultService("update-config-failure", "foo", WithRunLatestRollout, WithInitSvcConditions),
+			DefaultService("update-config-failure", "foo", WithRunLatestRollout,
+				WithInitSvcConditions, WithServiceGeneration(1), WithServiceObservedGeneration),
 			route("update-config-failure", "foo", WithRunLatestRollout),
 			// Mutate the Config to have an unexpected body to trigger an update.
 			config("update-config-failure", "foo", WithRunLatestRollout,
@@ -475,7 +488,7 @@ func TestReconcile(t *testing.T) {
 			InduceFailure("update", "services"),
 		},
 		Objects: []runtime.Object{
-			DefaultService("run-latest", "foo", WithRunLatestRollout),
+			DefaultService("run-latest", "foo", WithRunLatestRollout, WithServiceGeneration(1)),
 		},
 		Key: "foo/run-latest",
 		WantCreates: []runtime.Object{
@@ -486,7 +499,8 @@ func TestReconcile(t *testing.T) {
 			Object: DefaultService("run-latest", "foo", WithRunLatestRollout,
 				// We attempt to update the Service to initialize its
 				// conditions, which is where we induce the failure.
-				WithInitSvcConditions),
+				WithInitSvcConditions, WithServiceObservedGenFailure,
+				WithServiceGeneration(1), WithServiceObservedGeneration),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeNormal, "Created", "Created Configuration %q", "run-latest"),
@@ -498,7 +512,7 @@ func TestReconcile(t *testing.T) {
 		Name: "route and config ready, propagate ready",
 		// When both route and config are ready, the service should become ready.
 		Objects: []runtime.Object{
-			DefaultService("all-ready", "foo", WithRunLatestRollout, WithInitSvcConditions),
+			DefaultService("all-ready", "foo", WithRunLatestRollout, WithInitSvcConditions, WithServiceGeneration(1)),
 			route("all-ready", "foo", WithRunLatestRollout, RouteReady,
 				WithURL, WithAddress, WithInitRouteConditions,
 				WithStatusTraffic(
@@ -507,7 +521,7 @@ func TestReconcile(t *testing.T) {
 						Percent:      ptr.Int64(100),
 					}), MarkTrafficAssigned, MarkIngressReady),
 			config("all-ready", "foo", WithRunLatestRollout,
-				WithGeneration(1), WithObservedGen,
+				WithConfigGeneration(1), WithConfigObservedGen,
 				// These turn a Configuration to Ready=true
 				WithLatestCreated("all-ready-00001"), WithLatestReady("all-ready-00001")),
 		},
@@ -527,7 +541,7 @@ func TestReconcile(t *testing.T) {
 		// When both route and config are ready, the service should become ready.
 		Objects: []runtime.Object{
 			DefaultService("all-ready", "foo", WithRunLatestRollout, WithInitSvcConditions,
-				WithReadyConfig("all-ready-00001")),
+				WithReadyConfig("all-ready-00001"), WithServiceGeneration(1)),
 			route("all-ready", "foo", WithRunLatestRollout, RouteReady,
 				WithURL, WithAddress, WithInitRouteConditions,
 				WithStatusTraffic(v1.TrafficTarget{
@@ -535,7 +549,7 @@ func TestReconcile(t *testing.T) {
 					Percent:      ptr.Int64(100),
 				}), MarkTrafficAssigned, MarkIngressReady),
 			config("all-ready", "foo", WithRunLatestRollout,
-				WithGeneration(1), WithObservedGen, WithGeneration(2),
+				WithConfigGeneration(1), WithConfigObservedGen, WithConfigGeneration(2),
 				// These turn a Configuration to Ready=true
 				WithLatestCreated("all-ready-00001"), WithLatestReady("all-ready-00001")),
 		},
@@ -556,7 +570,7 @@ func TestReconcile(t *testing.T) {
 		// When both route and config are ready, but the route points to the previous revision
 		// the service should not be ready.
 		Objects: []runtime.Object{
-			DefaultService("config-only-ready", "foo", WithRunLatestRollout, WithInitSvcConditions),
+			DefaultService("config-only-ready", "foo", WithRunLatestRollout, WithInitSvcConditions, WithServiceGeneration(1)),
 			route("config-only-ready", "foo", WithRunLatestRollout, RouteReady,
 				WithURL, WithAddress, WithInitRouteConditions,
 				WithStatusTraffic(v1.TrafficTarget{
@@ -564,7 +578,7 @@ func TestReconcile(t *testing.T) {
 					Percent:      ptr.Int64(100),
 				}), MarkTrafficAssigned, MarkIngressReady),
 			config("config-only-ready", "foo", WithRunLatestRollout,
-				WithGeneration(2 /*will generate revision -00002*/), WithObservedGen,
+				WithConfigGeneration(2 /*will generate revision -00002*/), WithConfigObservedGen,
 				// These turn a Configuration to Ready=true
 				WithLatestCreated("config-only-ready-00002"), WithLatestReady("config-only-ready-00002")),
 		},
@@ -584,16 +598,16 @@ func TestReconcile(t *testing.T) {
 		// Gen 2: config update fails;
 		//    => service is still OK serving Gen 1.
 		Objects: []runtime.Object{
-			DefaultService("config-fails", "foo", WithRunLatestRollout, WithInitSvcConditions),
+			DefaultService("config-fails", "foo", WithRunLatestRollout, WithInitSvcConditions, WithServiceGeneration(1)),
 			route("config-fails", "foo", WithRunLatestRollout, RouteReady,
 				WithURL, WithAddress, WithInitRouteConditions,
 				WithStatusTraffic(v1.TrafficTarget{
 					RevisionName: "config-fails-00001",
 					Percent:      ptr.Int64(100),
 				}), MarkTrafficAssigned, MarkIngressReady),
-			config("config-fails", "foo", WithRunLatestRollout, WithGeneration(2),
+			config("config-fails", "foo", WithRunLatestRollout, WithConfigGeneration(2),
 				WithLatestReady("config-fails-00001"), WithLatestCreated("config-fails-00002"),
-				MarkLatestCreatedFailed("blah"), WithObservedGen),
+				MarkLatestCreatedFailed("blah"), WithConfigObservedGen),
 		},
 		Key: "foo/config-fails",
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
@@ -610,9 +624,9 @@ func TestReconcile(t *testing.T) {
 		Name: "configuration failure is propagated",
 		// When config fails, the service should fail.
 		Objects: []runtime.Object{
-			DefaultService("config-fails", "foo", WithRunLatestRollout, WithInitSvcConditions),
+			DefaultService("config-fails", "foo", WithRunLatestRollout, WithInitSvcConditions, WithServiceGeneration(1)),
 			route("config-fails", "foo", WithRunLatestRollout, RouteReady),
-			config("config-fails", "foo", WithRunLatestRollout, WithGeneration(1), WithObservedGen,
+			config("config-fails", "foo", WithRunLatestRollout, WithConfigGeneration(1), WithConfigObservedGen,
 				WithLatestCreated("config-fails-00001"), MarkLatestCreatedFailed("blah")),
 		},
 		Key: "foo/config-fails",
@@ -625,10 +639,10 @@ func TestReconcile(t *testing.T) {
 		Name: "route failure is propagated",
 		// When route fails, the service should fail.
 		Objects: []runtime.Object{
-			DefaultService("route-fails", "foo", WithRunLatestRollout, WithInitSvcConditions),
+			DefaultService("route-fails", "foo", WithRunLatestRollout, WithInitSvcConditions, WithServiceGeneration(1)),
 			route("route-fails", "foo", WithRunLatestRollout,
 				RouteFailed("Propagate me, please", "")),
-			config("route-fails", "foo", WithRunLatestRollout, WithGeneration(1), WithObservedGen,
+			config("route-fails", "foo", WithRunLatestRollout, WithConfigGeneration(1), WithConfigObservedGen,
 				// These turn a Configuration to Ready=true
 				WithLatestCreated("route-fails-00001"), WithLatestReady("route-fails-00001")),
 		},
@@ -644,14 +658,15 @@ func TestReconcile(t *testing.T) {
 		Name:    "existing configuration without an owner causes a failure",
 		WantErr: true,
 		Objects: []runtime.Object{
-			DefaultService("run-latest", "foo", WithRunLatestRollout),
+			DefaultService("run-latest", "foo", WithRunLatestRollout, WithServiceGeneration(1)),
 			config("run-latest", "foo", WithRunLatestRollout, WithConfigOwnersRemoved),
 		},
 		Key: "foo/run-latest",
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: DefaultService("run-latest", "foo", WithRunLatestRollout,
 				// The first reconciliation will initialize the status conditions.
-				WithInitSvcConditions, MarkConfigurationNotOwned),
+				WithInitSvcConditions, MarkConfigurationNotOwned,
+				WithServiceGeneration(1), WithServiceObservedGeneration),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeWarning, "InternalError", `service: "run-latest" does not own configuration: "run-latest"`),
@@ -660,7 +675,7 @@ func TestReconcile(t *testing.T) {
 		Name:    "existing route without an owner causes a failure",
 		WantErr: true,
 		Objects: []runtime.Object{
-			DefaultService("run-latest", "foo", WithRunLatestRollout),
+			DefaultService("run-latest", "foo", WithRunLatestRollout, WithServiceGeneration(1)),
 			config("run-latest", "foo", WithRunLatestRollout),
 			route("run-latest", "foo", WithRunLatestRollout, WithRouteOwnersRemoved),
 		},
@@ -668,7 +683,7 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: DefaultService("run-latest", "foo", WithRunLatestRollout,
 				// The first reconciliation will initialize the status conditions.
-				WithInitSvcConditions, MarkRouteNotOwned),
+				WithInitSvcConditions, MarkRouteNotOwned, WithServiceGeneration(1), WithServiceObservedGeneration),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeWarning, "InternalError", `service: "run-latest" does not own route: "run-latest"`),
@@ -688,7 +703,7 @@ func TestReconcile(t *testing.T) {
 					RevisionName: "new-owner-00001",
 					Percent:      ptr.Int64(100),
 				}), MarkTrafficAssigned, MarkIngressReady),
-			config("new-owner", "foo", WithRunLatestRollout, WithGeneration(1), WithObservedGen,
+			config("new-owner", "foo", WithRunLatestRollout, WithConfigGeneration(1), WithConfigObservedGen,
 				// These turn a Configuration to Ready=true
 				WithLatestCreated("new-owner-00001"), WithLatestReady("new-owner-00001")),
 		},
diff --git a/pkg/testing/functional.go b/pkg/testing/functional.go
index 237ba90e5..4a9103f6a 100644
--- a/pkg/testing/functional.go
+++ b/pkg/testing/functional.go
@@ -187,6 +187,24 @@ func WithMetricAnnotation(metric string) PodAutoscalerOption {
 	return withAnnotationValue(autoscaling.MetricAnnotationKey, metric)
 }
 
+// WithObservedGeneration returns a PodAutoScalerOption which sets
+// the Status.ObservedGeneration field to the given generation.
+func WithObservedGeneration(gen int64) PodAutoscalerOption {
+	return func(pa *asv1a1.PodAutoscaler) {
+		pa.Status.ObservedGeneration = gen
+	}
+}
+
+// WithObservedGenerationFailure marks the top level condition as unknown when the reconciler
+// does not set any condition during reconciliation of a new generation.
+func WithObservedGenerationFailure() PodAutoscalerOption {
+	return func(pa *asv1a1.PodAutoscaler) {
+		condSet := pa.GetConditionSet()
+		condSet.Manage(&pa.Status).MarkUnknown(condSet.GetTopLevelConditionType(),
+			"NewObservedGenFailure", "unsuccessfully observed a new generation")
+	}
+}
+
 // WithMetricOwnersRemoved clears the owner references of this PodAutoscaler.
 func WithMetricOwnersRemoved(m *asv1a1.Metric) {
 	m.OwnerReferences = nil
diff --git a/pkg/testing/v1/configuration.go b/pkg/testing/v1/configuration.go
index ac74dbfa6..602deb0e0 100644
--- a/pkg/testing/v1/configuration.go
+++ b/pkg/testing/v1/configuration.go
@@ -56,15 +56,15 @@ func WithConfigContainerConcurrency(cc int64) ConfigOption {
 	}
 }
 
-// WithGeneration sets the generation of the Configuration.
-func WithGeneration(gen int64) ConfigOption {
+// WithConfigGeneration sets the generation of the Configuration.
+func WithConfigGeneration(gen int64) ConfigOption {
 	return func(cfg *v1.Configuration) {
 		cfg.Generation = gen
 	}
 }
 
-// WithObservedGen sets the observed generation of the Configuration.
-func WithObservedGen(cfg *v1.Configuration) {
+// WithConfigObservedGen sets the observed generation of the Configuration.
+func WithConfigObservedGen(cfg *v1.Configuration) {
 	cfg.Status.ObservedGeneration = cfg.Generation
 }
 
diff --git a/pkg/testing/v1/route.go b/pkg/testing/v1/route.go
index 5f3cf3b0f..26ecaf4e8 100644
--- a/pkg/testing/v1/route.go
+++ b/pkg/testing/v1/route.go
@@ -50,8 +50,8 @@ func WithRouteUID(uid types.UID) RouteOption {
 
 // WithRouteGeneration sets the route's generation
 func WithRouteGeneration(generation int64) RouteOption {
-	return func(svc *v1.Route) {
-		svc.Status.ObservedGeneration = generation
+	return func(r *v1.Route) {
+		r.Status.ObservedGeneration = generation
 	}
 }
 
diff --git a/pkg/testing/v1/service.go b/pkg/testing/v1/service.go
index d6d1b9e46..27ccdde70 100644
--- a/pkg/testing/v1/service.go
+++ b/pkg/testing/v1/service.go
@@ -72,6 +72,14 @@ func WithInitSvcConditions(s *v1.Service) {
 	s.Status.InitializeConditions()
 }
 
+// WithServiceObservedGenFailure marks the top level condition as unknown when the reconciler
+// does not set any condition during reconciliation of a new generation.
+func WithServiceObservedGenFailure(s *v1.Service) {
+	condSet := s.GetConditionSet()
+	condSet.Manage(&s.Status).MarkUnknown(condSet.GetTopLevelConditionType(),
+		"NewObservedGenFailure", "unsuccessfully observed a new generation")
+}
+
 // WithConfigSpec confgures the Service to use the given config spec
 func WithConfigSpec(config v1.ConfigurationSpec) ServiceOption {
 	return func(svc *v1.Service) {
diff --git a/test/performance/benchmarks/.ko.yaml b/test/performance/benchmarks/.ko.yaml
new file mode 100644
index 000000000..5e5fb741d
--- /dev/null
+++ b/test/performance/benchmarks/.ko.yaml
@@ -0,0 +1,4 @@
+# Use :nonroot base image for all containers
+defaultBaseImage: gcr.io/distroless/static:nonroot
+baseImageOverrides:
+  knative.dev/serving/vendor/github.com/tsenart/vegeta: ubuntu:latest
diff --git a/test/performance/benchmarks/dataplane-probe/continuous/.ko.yaml b/test/performance/benchmarks/dataplane-probe/continuous/.ko.yaml
deleted file mode 120000
index fc96611ba..000000000
--- a/test/performance/benchmarks/dataplane-probe/continuous/.ko.yaml
+++ /dev/null
@@ -1,1 +0,0 @@
-../../../../../.ko.yaml
\ No newline at end of file
diff --git a/test/performance/benchmarks/deployment-probe/continuous/.ko.yaml b/test/performance/benchmarks/deployment-probe/continuous/.ko.yaml
deleted file mode 120000
index fc96611ba..000000000
--- a/test/performance/benchmarks/deployment-probe/continuous/.ko.yaml
+++ /dev/null
@@ -1,1 +0,0 @@
-../../../../../.ko.yaml
\ No newline at end of file
diff --git a/test/performance/benchmarks/load-test/continuous/.ko.yaml b/test/performance/benchmarks/load-test/continuous/.ko.yaml
deleted file mode 120000
index fc96611ba..000000000
--- a/test/performance/benchmarks/load-test/continuous/.ko.yaml
+++ /dev/null
@@ -1,1 +0,0 @@
-../../../../../.ko.yaml
\ No newline at end of file
diff --git a/test/performance/benchmarks/rollout-probe/continuous/.ko.yaml b/test/performance/benchmarks/rollout-probe/continuous/.ko.yaml
deleted file mode 120000
index fc96611ba..000000000
--- a/test/performance/benchmarks/rollout-probe/continuous/.ko.yaml
+++ /dev/null
@@ -1,1 +0,0 @@
-../../../../../.ko.yaml
\ No newline at end of file
diff --git a/test/performance/benchmarks/rollout-probe/continuous/main.go b/test/performance/benchmarks/rollout-probe/continuous/main.go
index 84b839fa0..186275d2f 100644
--- a/test/performance/benchmarks/rollout-probe/continuous/main.go
+++ b/test/performance/benchmarks/rollout-probe/continuous/main.go
@@ -23,8 +23,12 @@ import (
 	"time"
 
 	vegeta "github.com/tsenart/vegeta/lib"
+
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/labels"
 
+	servingclient "knative.dev/serving/pkg/client/injection/client"
+
 	"knative.dev/pkg/signals"
 	"knative.dev/pkg/test/mako"
 	"knative.dev/serving/pkg/apis/serving"
@@ -37,9 +41,7 @@ var (
 	duration = flag.Duration("duration", 5*time.Minute, "The duration of the probe")
 )
 
-const (
-	namespace = "default"
-)
+const namespace = "default"
 
 func main() {
 	flag.Parse()
@@ -106,7 +108,9 @@ func main() {
 	log.Printf("Selector: %v", selector)
 	deploymentStatus := metrics.FetchDeploymentsStatus(ctx, namespace, selector, time.Second)
 	// Start the attack!
-	results := attacker.Attack(targeter, rate, *duration, "load-test")
+	results := attacker.Attack(targeter, rate, *duration, "rollout-test")
+	// After a minute, update the Ksvc.
+	updateSvc := time.After(time.Minute)
 LOOP:
 	for {
 		select {
@@ -115,6 +119,20 @@ LOOP:
 			// clean thing up.
 			break LOOP
 
+		case <-updateSvc:
+			log.Println("Updating the service: ", *target)
+			sc := servingclient.Get(ctx)
+			svc, err := sc.ServingV1().Services(namespace).Get(*target, metav1.GetOptions{})
+			if err != nil {
+				log.Fatalf("Error getting ksvc %s: %v", *target, err)
+			}
+			// Make sure we start with a single instance.
+			svc.Spec.Template.Annotations["autoscaling.knative.dev/minScale"] = "1"
+			_, err = sc.ServingV1().Services(namespace).Update(svc)
+			if err != nil {
+				log.Fatalf("Error updating ksvc %s: %v", *target, err)
+			}
+			log.Println("Successfully updated the service.")
 		case res, ok := <-results:
 			if !ok {
 				// Once we have read all of the request results, break out of
diff --git a/test/performance/benchmarks/rollout-probe/continuous/rollout-probe-setup.yaml b/test/performance/benchmarks/rollout-probe/continuous/rollout-probe-setup.yaml
index 19987e609..f081a2a28 100644
--- a/test/performance/benchmarks/rollout-probe/continuous/rollout-probe-setup.yaml
+++ b/test/performance/benchmarks/rollout-probe/continuous/rollout-probe-setup.yaml
@@ -37,7 +37,8 @@ spec:
   template:
     metadata:
       annotations:
-        autoscaling.knative.dev/minScale: "1"
+        autoscaling.knative.dev/minScale: "100"
+        autoscaling.knative.dev/maxScale: "150"
         # Always hook the activator in.
         autoscaling.knative.dev/targetBurstCapacity: "-1"
     spec:
@@ -77,7 +78,8 @@ spec:
   template:
     metadata:
       annotations:
-        autoscaling.knative.dev/minScale: "1"
+        autoscaling.knative.dev/minScale: "100"
+        autoscaling.knative.dev/maxScale: "150"
         # Only hook the activator in when scaled to zero.
         autoscaling.knative.dev/targetBurstCapacity: "0"
     spec:
diff --git a/test/performance/benchmarks/scale-from-zero/continuous/.ko.yaml b/test/performance/benchmarks/scale-from-zero/continuous/.ko.yaml
deleted file mode 120000
index fc96611ba..000000000
--- a/test/performance/benchmarks/scale-from-zero/continuous/.ko.yaml
+++ /dev/null
@@ -1,1 +0,0 @@
-../../../../../.ko.yaml
\ No newline at end of file
diff --git a/third_party/contour-latest/contour.yaml b/third_party/contour-latest/contour.yaml
index a0b2a9790..7b236f873 100644
--- a/third_party/contour-latest/contour.yaml
+++ b/third_party/contour-latest/contour.yaml
@@ -1432,7 +1432,7 @@ spec:
         # if we change this on each version, you can no longer upgrade
         # just by applying the deployment YAML.
         # See #2423, #2395, #2150, and #2030 for earlier questions about this.
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:7e8baabfdce34f22939ea89c666520dc95391cbf24efbd6850de040bb53a3e77
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:2078247f021fa1939f391d21c2625a192a5f3cd08465d2ceb9f4c6535f4fdabf
         imagePullPolicy: Always
         command:
         - contour
@@ -1672,7 +1672,7 @@ spec:
         - --contour-key-file=/certs/tls.key
         - --config-path=/config/contour.yaml
         command: ["contour"]
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:7e8baabfdce34f22939ea89c666520dc95391cbf24efbd6850de040bb53a3e77
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:2078247f021fa1939f391d21c2625a192a5f3cd08465d2ceb9f4c6535f4fdabf
         imagePullPolicy: Always
         name: contour
         ports:
@@ -1764,7 +1764,7 @@ spec:
         args:
         - envoy
         - shutdown-manager
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:7e8baabfdce34f22939ea89c666520dc95391cbf24efbd6850de040bb53a3e77
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:2078247f021fa1939f391d21c2625a192a5f3cd08465d2ceb9f4c6535f4fdabf
         imagePullPolicy: Always
         lifecycle:
           preStop:
@@ -1840,7 +1840,7 @@ spec:
         - --envoy-key-file=/certs/tls.key
         command:
         - contour
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:7e8baabfdce34f22939ea89c666520dc95391cbf24efbd6850de040bb53a3e77
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:2078247f021fa1939f391d21c2625a192a5f3cd08465d2ceb9f4c6535f4fdabf
         imagePullPolicy: Always
         name: envoy-initconfig
         volumeMounts:
@@ -3305,7 +3305,7 @@ spec:
         # if we change this on each version, you can no longer upgrade
         # just by applying the deployment YAML.
         # See #2423, #2395, #2150, and #2030 for earlier questions about this.
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:7e8baabfdce34f22939ea89c666520dc95391cbf24efbd6850de040bb53a3e77
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:2078247f021fa1939f391d21c2625a192a5f3cd08465d2ceb9f4c6535f4fdabf
         imagePullPolicy: Always
         command:
         - contour
@@ -3545,7 +3545,7 @@ spec:
         - --contour-key-file=/certs/tls.key
         - --config-path=/config/contour.yaml
         command: ["contour"]
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:7e8baabfdce34f22939ea89c666520dc95391cbf24efbd6850de040bb53a3e77
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:2078247f021fa1939f391d21c2625a192a5f3cd08465d2ceb9f4c6535f4fdabf
         imagePullPolicy: Always
         name: contour
         ports:
@@ -3637,7 +3637,7 @@ spec:
         args:
         - envoy
         - shutdown-manager
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:7e8baabfdce34f22939ea89c666520dc95391cbf24efbd6850de040bb53a3e77
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:2078247f021fa1939f391d21c2625a192a5f3cd08465d2ceb9f4c6535f4fdabf
         imagePullPolicy: Always
         lifecycle:
           preStop:
@@ -3713,7 +3713,7 @@ spec:
         - --envoy-key-file=/certs/tls.key
         command:
         - contour
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:7e8baabfdce34f22939ea89c666520dc95391cbf24efbd6850de040bb53a3e77
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:2078247f021fa1939f391d21c2625a192a5f3cd08465d2ceb9f4c6535f4fdabf
         imagePullPolicy: Always
         name: envoy-initconfig
         volumeMounts:
diff --git a/third_party/contour-latest/net-contour.yaml b/third_party/contour-latest/net-contour.yaml
index 66506770d..c70075650 100644
--- a/third_party/contour-latest/net-contour.yaml
+++ b/third_party/contour-latest/net-contour.yaml
@@ -35,7 +35,7 @@ metadata:
   namespace: knative-serving
   labels:
     networking.knative.dev/ingress-provider: contour
-    serving.knative.dev/release: "v20200616-e825070b"
+    serving.knative.dev/release: "v20200618-e5e8dbd1"
 data:
   _example: |
     ################################
@@ -94,7 +94,7 @@ spec:
       - name: controller
         # This is the Go import path for the binary that is containerized
         # and substituted here.
-        image: gcr.io/knative-nightly/knative.dev/net-contour/cmd/controller@sha256:c0bc3f3b6b8fe90f2feba120b37d1a4cba6e00c0fc91f8bd05a28db4376440b2
+        image: gcr.io/knative-nightly/knative.dev/net-contour/cmd/controller@sha256:117f50d9ef1867275c1ab66082c02ca930bd481fd6a4105fbad7f350f1275c53
         resources:
           requests:
             cpu: 40m
diff --git a/vendor/knative.dev/pkg/metrics/testing.go b/vendor/knative.dev/pkg/metrics/testing.go
index d33668ead..eb048f9cb 100644
--- a/vendor/knative.dev/pkg/metrics/testing.go
+++ b/vendor/knative.dev/pkg/metrics/testing.go
@@ -19,7 +19,7 @@ package metrics
 // InitForTesting initialize the necessary global variables for unit tests.
 func InitForTesting() {
 	setCurMetricsConfig(&metricsConfig{
-		backendDestination: Prometheus,
+		backendDestination: prometheus,
 		component:          "test",
 		domain:             "test",
 	})
diff --git a/vendor/knative.dev/test-infra/scripts/e2e-tests.sh b/vendor/knative.dev/test-infra/scripts/e2e-tests.sh
index 01b2d0e40..354819385 100644
--- a/vendor/knative.dev/test-infra/scripts/e2e-tests.sh
+++ b/vendor/knative.dev/test-infra/scripts/e2e-tests.sh
@@ -108,6 +108,8 @@ function dump_cluster_state() {
       echo ">>> Details" >> ${output}
       if [[ "${crd}" == "secrets" ]]; then
         echo "Secrets are ignored for security reasons" >> ${output}
+      elif [[ "${crd}" == "events" ]]; then
+        echo "events are ignored as making a lot of noise" >> ${output}
       else
         kubectl get ${crd} --all-namespaces -o yaml >> ${output}
       fi
diff --git a/vendor/knative.dev/test-infra/scripts/library.sh b/vendor/knative.dev/test-infra/scripts/library.sh
index de059f536..1ed038116 100644
--- a/vendor/knative.dev/test-infra/scripts/library.sh
+++ b/vendor/knative.dev/test-infra/scripts/library.sh
@@ -389,25 +389,8 @@ function mktemp_with_extension() {
 #             $2 - check name as an identifier (e.g., GoBuild)
 #             $3 - failure message (can contain newlines), optional (means success)
 function create_junit_xml() {
-  local xml="$(mktemp_with_extension ${ARTIFACTS}/junit_XXXXXXXX xml)"
-  local failure=""
-  if [[ "$3" != "" ]]; then
-    # Transform newlines into HTML code.
-    # Also escape `<` and `>` as here: https://github.com/golang/go/blob/50bd1c4d4eb4fac8ddeb5f063c099daccfb71b26/src/encoding/json/encode.go#L48,
-    # this is temporary solution for fixing https://github.com/knative/test-infra/issues/1204,
-    # which should be obsolete once Test-infra 2.0 is in place
-    local msg="$(echo -n "$3" | sed 's/$/\&#xA;/g' | sed 's/</\\u003c/' | sed 's/>/\\u003e/' | sed 's/&/\\u0026/' | tr -d '\n')"
-    failure="<failure message=\"Failed\" type=\"\">${msg}</failure>"
-  fi
-  cat << EOF > "${xml}"
-<testsuites>
-	<testsuite tests="1" failures="1" time="0.000" name="$1">
-		<testcase classname="" name="$2" time="0.0">
-			${failure}
-		</testcase>
-	</testsuite>
-</testsuites>
-EOF
+  local xml="$(mktemp_with_extension "${ARTIFACTS}"/junit_XXXXXXXX xml)"
+  run_kntest junit --suite="$1" --name="$2" --err-msg="$3" --dest="${xml}" || return 1
 }
 
 # Runs a go test and generate a junit summary.
@@ -542,6 +525,15 @@ function run_go_tool() {
   ${tool} "$@"
 }
 
+# Run kntest tool, error out and ask users to install it if it's not currently installed.
+# Parameters: $1..$n - parameters passed to the tool.
+function run_kntest() {
+  if [[ -z "$(which kntest)" ]]; then
+    echo "--- FAIL: kntest not installed, please clone test-infra repo and run \`go install ./kntest/cmd/kntest\` to install it"; return 1;
+  fi
+  kntest "$@"
+}
+
 # Run go-licenses to update licenses.
 # Parameters: $1 - output file, relative to repo root dir.
 #             $2 - directory to inspect.