Repository: tektoncd/pipeline (language: go)

Issue:
Pipeline failing with "invalid pipelineresults" when referencing results of skipped tasks
# Expected Behavior

According to the documentation pipeline results can contain references to task results. If a task did not run because of it's whenConditions, the pipeline result is not emitted but the pipeline succeeds. This worked with v0.37.5 of Tekton Pipelines available on OpenShift.

# Actual Behavior

With the update of the RedHat OpenShift Pipelines Operator, which brings Tekton Pipeline v0.41.0, pipelines with skipped tasks and composed results now fail with the message `invalid pipelineresults [result-goodbye], the referred results don't exist`.

# Steps to Reproduce the Problem

Run the following PipelineRun:

```yaml
apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
  name: pipelinerun-test
spec:
  pipelineSpec:
    params:
      - name: say-hello
        default: 'false'
    tasks:
      - name: hello
        taskSpec:
          results:
            - name: result-one
          steps:
          - image: alpine
            script: |
              #!/bin/sh
              echo "Hello world!"
              echo -n "RES1" > $(results.result-one.path)
  
      - name: goodbye
        runAfter:
          - hello
        taskSpec:
          results:
            - name: result-two
          steps:
          - image: alpine
            script: |
              #!/bin/sh
              echo "Goodbye world!"
              echo -n "RES2" > $(results.result-two.path)
        when:
          - input: $(params.say-hello)
            operator: in
            values: ["true"]

    results:
      - name: result-hello
        description: Result one
        value: '$(tasks.hello.results.result-one)'
      - name: result-goodbye
        description: Result two
        value: '$(tasks.goodbye.results.result-two)'
```

# Additional Info

- Kubernetes version:

```
Server Version: version.Info{Major:"1", Minor:"25", GitVersion:"v1.25.4+a34b9e9", GitCommit:"b6d1f054747e9886f61dd85316deac3415e2726f", GitTreeState:"clean", BuildDate:"2023-01-10T15:55:28Z", GoVersion:"go1.19.4", Compiler:"gc", Platform:"linux/amd64"}
```

- Tekton Pipeline version:

```
Pipeline version: v0.41.0
Triggers version: v0.22.0
Openshift Pipelines Version: 1.9.0
```



Discussion / hints:
Success on v0.37.5:
![pipeline-success-v0 37 5](https://user-images.githubusercontent.com/1577511/217885012-846347f5-a1b1-4c22-a58f-e7ab62551d71.png)

Failure on v0.41.0:
![pipeline-failure-v0 41 0](https://user-images.githubusercontent.com/1577511/217885006-197d9eec-4758-4418-9053-47665cd4c5b0.png)
Hi @thomascube, thanks!
I think the validation is added intentionally,  do you think it makes more sense to remove the check?
Since I may lack the knowledge of the use cases, I'd like to learn more about your opinons.
@Yongxuanzhang We would like to display these values in our UI tool (if they are available). Is there any way to default to empty, if the task was not run?
e.g.  something like `'$(tasks.hello.results.result-one || '')'` or `'$(tasks.hello && tasks.hello.results.result-one)'`?
@kyubisation we don't have default result yet but it's something that @vinamra28 has been looking into -- considering a syntax similar to the first one you suggested: `'$(tasks.hello.results.result-one || '')'`
> @Yongxuanzhang We would like to display these values in our UI tool (if they are available). Is there any way to default to empty, if the task was not run? e.g. something like `'$(tasks.hello.results.result-one || '')'` or `'$(tasks.hello && tasks.hello.results.result-one)'`?

I think default results would be a solution. But it may take much longer time?

I wonder before this update, if the results are not available, there will be nothing showing in UI?
The reason I add the check is to help debugging. So you would know which results are not emitted. 
I'm thinking if there is a better way to meet both needs? 
e.g. We don't fail the pipelinerun but only log the warning message?

Or actually we don't want to have any check/validation on this? If so I think we could just remove the validation.

@kyubisation @jerop any suggestions? 
@Yongxuanzhang I think the old behavior which is also [described in the documentation](https://tekton.dev/docs/pipelines/pipelines/#emitting-results-from-a-pipeline) under "A Pipeline Result is not emitted if any of the following are true" makes perfectly sense and we were running fine with it. With the new check and the failure of the pipeline it's impossible to use task results in conjunction with when conditions.
@Yongxuanzhang Having validation is reasonable, but directly breaking the pipeline without a workaround/migration possibility is not ideal. I'd prefer to have this either be opt-in or have to have a fallback/default available at the same time.
> @Yongxuanzhang I think the old behavior which is also [described in the documentation](https://tekton.dev/docs/pipelines/pipelines/#emitting-results-from-a-pipeline) under "A Pipeline Result is not emitted if any of the following are true" makes perfectly sense and we were running fine with it. With the new check and the failure of the pipeline it's impossible to use task results in conjunction with when conditions.

In the doc you linked it says:

>A PipelineTask referenced by the Pipeline Result didn’t emit the referenced Task Result. This should be considered a bug in the Task and [may fail a PipelineTask in future](https://github.com/tektoncd/pipeline/issues/3497).

It looks like we want to fail the run if the results are not emitted but referenced? 
You case is like the results may or may not emit and it depends on the task's when condition. 

I'm ok with @kyubisation's suggestion. 


@Yongxuanzhang apparently I read the following statement differently

> This should be considered a bug in the Task and [may fail a PipelineTask in the future](https://github.com/tektoncd/pipeline/issues/3497).

For me this sounds like a case where a task specifies a result but no write to that result file was made in any of the steps. I didn't make the connection to skipped tasks.

Anyway, you asked about my use-case: we have pipelines for building software projects such as a Java Maven application. The pipelines includes tasks for build the jar, testing it, running quality checks (sonar scan) and creating a Docker image from it. Some tasks are optional (e.g. sonar scan or docker build) and can be enabled/disabled by pipeline parameters consumed by when expressions guarding the task execution. These tasks also emit results (if executed) which are mapped into pipeline results. If for example a docker image was built, the image name and tag are visible in the pipeline results and vice versa: if the docker build was disabled by parameter, no such pipeline result exists.

Or a bit shorter: _we run pipelines with optional tasks that yield optional results._

I was just surprised to suddenly see pipeline fail although all tasks have executed successfully. For possible solutions I can just copy @kyubisation's suggestions.



Thanks for your input and suggestions! @thomascube @kyubisation  
I have a better understanding of the use cases. I will fix it soon!
Will keep you updated. :D
/assign
I believe https://github.com/tektoncd/community/pull/954 is a long term solution. But we should fix this to unblock the use cases
I have opened this [PR](https://github.com/tektoncd/pipeline/pull/6157) to fix it. @kyubisation @thomascube. Plz take a look 🙏  and let me know if this works for you?

> we run pipelines with optional tasks that yield optional results

@thomascube what do you mean by optional tasks? is it that they are guarded using when expressions?

don't think results were ever meant to be optional, we were planning to enforce it (https://github.com/tektoncd/pipeline/issues/3497) but looks like users have built around the current behavior 🤔  -- cc @pritidesai @vinamra28 
> > we run pipelines with optional tasks that yield optional results
> 
> @thomascube what do you mean by optional tasks? is it that they are guarded using when expressions?
> 
> don't think results were ever meant to be optional, we were planning to enforce it (#3497) but looks like users have built around the current behavior 🤔 -- cc @pritidesai @vinamra28

@jerop Do you think this worth discussing in a wg or some other way? I think this is about how we handle skipped tasks results.
Options:
1. fail the pipelinerun
2. skip the validation and log the messages

either way I think we will still need https://github.com/tektoncd/community/pull/954
@Yongxuanzhang just want to confirm, it seems like this regression was introduced in https://github.com/tektoncd/pipeline/pull/5088, i.e. it was not the result of an intentional change to add more validation to pipeline results?
Hi @kyubisation @thomascube, the fix PR is merged, so the release v0.46 should have this fix. And I will cherry pick this PR into previous releases. 
I think you're using v0.41.0 ? Maybe we could reopen this issue or I create a new one to track it?
@Yongxuanzhang Thanks for the work and the update. We are currently using 0.37.x and would like to upgrade to 0.41.x, so a backport would be very appreciated.
If the backport is planned/scheduled soonish, I don't need a separate issue to track it. If not, then reopening this issue should be enough.
> @Yongxuanzhang Thanks for the work and the update. We are currently using 0.37.x and would like to upgrade to 0.41.x, so a backport would be very appreciated. If the backport is planned/scheduled soonish, I don't need a separate issue to track it. If not, then reopening this issue should be enough.

Thanks! I will post the updates in this issue!
Hello. We just upgraded to Tekton v0.47.5 (via OpenShift Pipelines) and now this issue is back!

Our pipeline has defined a result which copies the value of a task result:

```yaml
  results:
    - description: The URL to the generated junit test report
      name: REPORT_URL
      value: $(finally.generate-report.results.REPORT_URL)
```

Now if the task generate-report is skipped due to a when condition, the pipeline fails with the following error:

```
invalid pipelineresults [REPORT_URL], the referred results don't exist
```

With Tekton v0.44.2 the same pipeline terminated without an error.
> Hello. We just upgraded to Tekton v0.47.5 (via OpenShift Pipelines) and now this issue is back!
> 
> Our pipeline has defined a result which copies the value of a task result:
> 
> ```yaml
>   results:
>     - description: The URL to the generated junit test report
>       name: REPORT_URL
>       value: $(finally.generate-report.results.REPORT_URL)
> ```
> 
> Now if the task generate-report is skipped due to a when condition, the pipeline fails with the following error:
> 
> ```
> invalid pipelineresults [REPORT_URL], the referred results don't exist
> ```
> 
> With Tekton v0.44.2 the same pipeline terminated without an error.

So there's are no issues with v0.47.4, just trying to know which release introduce the new change
@Yongxuanzhang we upgraded from v0.44.2 to v0.47.5 (this is what the Openshift Pipelines operator has bundled in its releases). Thus I don’t know in which version in between the old behavior came back.
> @Yongxuanzhang we upgraded from v0.44.2 to v0.47.5 (this is what the Openshift Pipelines operator has bundled in its releases). Thus I don’t know in which version in between the old behavior came back.

Ok I will take a look
To be more precise, the issue only occurs when results of a skipped task from the `finally` section are involved. Regular tasks which are skipped to not trigger the said validation error but the following test pipeline does:
```yaml
apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
  generateName: pipelinerun-test-
spec:
  pipelineSpec:
    params:
      - name: say-goobye
        default: 'false'
      - name: do-shutdown
        default: 'false'
    tasks:
      - name: hello
        taskSpec:
          results:
            - name: result-one
          steps:
          - image: alpine
            script: |
              #!/bin/sh
              echo "Hello world!"
              echo -n "RES1" > $(results.result-one.path)
  
      - name: goodbye
        runAfter:
          - hello
        taskSpec:
          results:
            - name: result-two
          steps:
          - image: alpine
            script: |
              #!/bin/sh
              echo "Goodbye world!"
              echo -n "RES2" > $(results.result-two.path)
        when:
          - input: $(params.say-goobye)
            operator: in
            values: ["true"]

    finally:
      - name: shutdown
        taskSpec:
          results:
            - name: result-three
          steps:
          - image: alpine
            script: |
              #!/bin/sh
              echo "Shutdown world!"
              echo -n "RES3" > $(results.result-three.path)
        when:
          - input: $(params.do-shutdown)
            operator: in
            values: ["true"]

    results:
      - name: result-hello
        description: Result one
        value: '$(tasks.hello.results.result-one)'
      - name: result-goodbye
        description: Result two
        value: '$(tasks.goodbye.results.result-two)'
      - name: result-shutdown
        description: Result shutdown
        value: '$(finally.shutdown.results.result-three)'
```

I'll now test through the versions between v0.44.2 and v0.47.5 to figure out the exaction version the behavior changed.
All right, I now tested through the releases and the given example pipeline still succeeds in v0.47.2 but starts failing with v0.47.3. I hope this helps you tracking down the change. It still fails with v0.50.2 though.
Thanks! Looking into it
I suspect the skipped finally tasks are not taken into consideration in the previous fix.
> I suspect the skipped finally tasks are not taken into consideration in the previous fix.

But it worked in versions until v0.47.2...
> > I suspect the skipped finally tasks are not taken into consideration in the previous fix.
> 
> But it worked in versions until v0.47.2...

There are 2 examples, 1 is your original example without final task, and the other with final task. The one without final task has no issues running on latest main. And I think the final task is not working since the validation was introduced
@thomascube  I will create a fix for this, which Tekton Pipeline Version do we need this fix? I wonder when we have the fix, which major versions we need to patch on to unblock you. 
We should support the LTS which are: 44, 47, 50, 53. Is that ok?



Fix patch:
diff --git a/examples/v1beta1/pipelineruns/6139-regression.yaml b/examples/v1beta1/pipelineruns/6139-regression.yaml
index f2a73f03299..409ad5eb699 100644
--- a/examples/v1beta1/pipelineruns/6139-regression.yaml
+++ b/examples/v1beta1/pipelineruns/6139-regression.yaml
@@ -7,6 +7,8 @@ spec:
     params:
       - name: say-hello
         default: 'false'
+      - name: do-shutdown
+        default: 'false'
     tasks:
       - name: hello
         taskSpec:
@@ -35,7 +37,21 @@ spec:
           - input: $(params.say-hello)
             operator: in
             values: ["true"]
-
+    finally:
+      - name: shutdown
+        taskSpec:
+          results:
+            - name: result-three
+          steps:
+          - image: alpine
+            script: |
+              #!/bin/sh
+              echo "Shutdown world!"
+              echo -n "RES3" > $(results.result-three.path)
+        when:
+          - input: $(params.do-shutdown)
+            operator: in
+            values: ["true"]
     results:
       - name: result-hello
         description: Result one
@@ -43,3 +59,6 @@ spec:
       - name: result-goodbye
         description: Result two
         value: '$(tasks.goodbye.results.result-two)'
+      - name: result-shutdown
+        description: Result shutdown
+        value: '$(finally.shutdown.results.result-three)'
diff --git a/pkg/apis/pipeline/v1beta1/customrun_types.go b/pkg/apis/pipeline/v1beta1/customrun_types.go
index 233270037db..166b4c12778 100644
--- a/pkg/apis/pipeline/v1beta1/customrun_types.go
+++ b/pkg/apis/pipeline/v1beta1/customrun_types.go
@@ -228,6 +228,11 @@ func (r *CustomRun) IsSuccessful() bool {
 	return r != nil && r.Status.GetCondition(apis.ConditionSucceeded).IsTrue()
 }
 
+// IsFailure returns true if the TaskRun's status indicates that it has failed.
+func (tr *CustomRun) IsFailure() bool {
+	return tr != nil && tr.Status.GetCondition(apis.ConditionSucceeded).IsFalse()
+}
+
 // GetCustomRunKey return the customrun's key for timeout handler map
 func (r *CustomRun) GetCustomRunKey() string {
 	// The address of the pointer is a threadsafe unique identifier for the customrun
diff --git a/pkg/apis/pipeline/v1beta1/run_interface.go b/pkg/apis/pipeline/v1beta1/run_interface.go
index 2a0ada19d68..0c59c8d3ae2 100644
--- a/pkg/apis/pipeline/v1beta1/run_interface.go
+++ b/pkg/apis/pipeline/v1beta1/run_interface.go
@@ -38,6 +38,7 @@ type RunObject interface {
 	IsCancelled() bool
 	HasStarted() bool
 	IsDone() bool
+	IsFailure() bool
 
 	GetRetryCount() int
 }
diff --git a/pkg/apis/pipeline/v1beta1/taskrun_types.go b/pkg/apis/pipeline/v1beta1/taskrun_types.go
index 27bb5143226..50a35fe0fc6 100644
--- a/pkg/apis/pipeline/v1beta1/taskrun_types.go
+++ b/pkg/apis/pipeline/v1beta1/taskrun_types.go
@@ -460,6 +460,11 @@ func (tr *TaskRun) IsCancelled() bool {
 	return tr.Spec.Status == TaskRunSpecStatusCancelled
 }
 
+// IsFailure returns true if the TaskRun's status indicates that it has failed.
+func (tr *TaskRun) IsFailure() bool {
+	return tr != nil && tr.Status.GetCondition(apis.ConditionSucceeded).IsFalse()
+}
+
 // IsTaskRunResultVerified returns true if the TaskRun's results have been validated by spire.
 func (tr *TaskRun) IsTaskRunResultVerified() bool {
 	return tr.Status.GetCondition(apis.ConditionType(TaskRunConditionResultsVerified.String())).IsTrue()
diff --git a/pkg/reconciler/pipelinerun/pipelinerun.go b/pkg/reconciler/pipelinerun/pipelinerun.go
index dbdbb9460dd..fadee6b7940 100644
--- a/pkg/reconciler/pipelinerun/pipelinerun.go
+++ b/pkg/reconciler/pipelinerun/pipelinerun.go
@@ -157,10 +157,8 @@ type Reconciler struct {
 	tracerProvider           trace.TracerProvider
 }
 
-var (
-	// Check that our Reconciler implements pipelinerunreconciler.Interface
-	_ pipelinerunreconciler.Interface = (*Reconciler)(nil)
-)
+// Check that our Reconciler implements pipelinerunreconciler.Interface
+var _ pipelinerunreconciler.Interface = (*Reconciler)(nil)
 
 // ReconcileKind compares the actual state with the desired, and attempts to
 // converge the two. It then updates the Status block of the Pipeline Run
@@ -314,7 +312,8 @@ func (c *Reconciler) resolvePipelineState(
 	ctx context.Context,
 	tasks []v1beta1.PipelineTask,
 	pipelineMeta *metav1.ObjectMeta,
-	pr *v1beta1.PipelineRun) (resources.PipelineRunState, error) {
+	pr *v1beta1.PipelineRun,
+) (resources.PipelineRunState, error) {
 	ctx, span := c.tracerProvider.Tracer(TracerName).Start(ctx, "resolvePipelineState")
 	defer span.End()
 	pst := resources.PipelineRunState{}
@@ -689,9 +688,14 @@ func (c *Reconciler) reconcile(ctx context.Context, pr *v1beta1.PipelineRun, get
 	pr.Status.ChildReferences = pipelineRunFacts.State.GetChildReferences()
 
 	pr.Status.SkippedTasks = pipelineRunFacts.GetSkippedTasks()
+
+	taskStatus := pipelineRunFacts.GetPipelineTaskStatus()
+	finalTaskStatus := pipelineRunFacts.GetPipelineFinalTaskStatus()
+	taskStatus = kmap.Union(taskStatus, finalTaskStatus)
+
 	if after.Status == corev1.ConditionTrue || after.Status == corev1.ConditionFalse {
 		pr.Status.PipelineResults, err = resources.ApplyTaskResultsToPipelineResults(ctx, pipelineSpec.Results,
-			pipelineRunFacts.State.GetTaskRunsResults(), pipelineRunFacts.State.GetRunsResults(), pipelineRunFacts.GetPipelineTaskStatus())
+			pipelineRunFacts.State.GetTaskRunsResults(), pipelineRunFacts.State.GetRunsResults(), taskStatus)
 		if err != nil {
 			pr.Status.MarkFailed(ReasonFailedValidation, err.Error())
 			return err
@@ -849,7 +853,8 @@ func (c *Reconciler) createTaskRun(ctx context.Context, taskRunName string, para
 			StepOverrides:      taskRunSpec.StepOverrides,
 			SidecarOverrides:   taskRunSpec.SidecarOverrides,
 			ComputeResources:   taskRunSpec.ComputeResources,
-		}}
+		},
+	}
 
 	// Add current spanContext as annotations to TaskRun
 	// so that tracing can be continued under the same traceId
diff --git a/pkg/reconciler/pipelinerun/resources/pipelinerunresolution.go b/pkg/reconciler/pipelinerun/resources/pipelinerunresolution.go
index 01724843f84..e5b08b6ab56 100644
--- a/pkg/reconciler/pipelinerun/resources/pipelinerunresolution.go
+++ b/pkg/reconciler/pipelinerun/resources/pipelinerunresolution.go
@@ -319,6 +319,38 @@ func (t ResolvedPipelineTask) isConditionStatusFalse() bool {
 	return false
 }
 
+// haveAnyRunsFailed returns true when any of the taskRuns/customRuns have succeeded condition with status set to false
+func (t ResolvedPipelineTask) haveAnyRunsFailed() bool {
+	if t.IsCustomTask() {
+		return t.haveAnyCustomRunsFailed()
+	}
+	return t.haveAnyTaskRunsFailed()
+}
+
+// haveAnyTaskRunsFailed returns true when any of the taskRuns have succeeded condition with status set to false
+func (t ResolvedPipelineTask) haveAnyTaskRunsFailed() bool {
+	if len(t.TaskRuns) > 0 || t.PipelineTask.IsMatrixed() {
+		for _, taskRun := range t.TaskRuns {
+			if taskRun.IsFailure() {
+				return true
+			}
+		}
+	}
+	return t.TaskRun.IsFailure()
+}
+
+// haveAnyCustomRunsFailed returns true when a CustomRun has succeeded condition with status set to false
+func (t ResolvedPipelineTask) haveAnyCustomRunsFailed() bool {
+	if len(t.RunObjects) > 0 || t.PipelineTask.IsMatrixed() {
+		for _, customRun := range t.RunObjects {
+			if customRun.IsFailure() {
+				return true
+			}
+		}
+	}
+	return t.RunObject != nil && t.RunObject.IsFailure()
+}
+
 func (t *ResolvedPipelineTask) checkParentsDone(facts *PipelineRunFacts) bool {
 	if facts.isFinalTask(t.PipelineTask.Name) {
 		return true
diff --git a/pkg/reconciler/pipelinerun/resources/pipelinerunstate.go b/pkg/reconciler/pipelinerun/resources/pipelinerunstate.go
index 208e473ab3a..8670b2f6265 100644
--- a/pkg/reconciler/pipelinerun/resources/pipelinerunstate.go
+++ b/pkg/reconciler/pipelinerun/resources/pipelinerunstate.go
@@ -518,6 +518,30 @@ func (facts *PipelineRunFacts) GetPipelineTaskStatus() map[string]string {
 	return tStatus
 }
 
+// GetPipelineTaskStatus returns the status of a PipelineFinalTask depending on its taskRun
+func (facts *PipelineRunFacts) GetPipelineFinalTaskStatus() map[string]string {
+	// construct a map of tasks.<pipelineTask>.status and its state
+	tStatus := make(map[string]string)
+	for _, t := range facts.State {
+		if facts.isFinalTask(t.PipelineTask.Name) {
+			var s string
+			switch {
+			// execution status is Succeeded when a task has succeeded condition with status set to true
+			case t.isSuccessful():
+				s = v1beta1.TaskRunReasonSuccessful.String()
+			// execution status is Failed when a task has succeeded condition with status set to false
+			case t.haveAnyRunsFailed():
+				s = v1beta1.TaskRunReasonFailed.String()
+			default:
+				// None includes skipped as well
+				s = PipelineTaskStateNone
+			}
+			tStatus[PipelineTaskStatusPrefix+t.PipelineTask.Name+PipelineTaskStatusSuffix] = s
+		}
+	}
+	return tStatus
+}
+
 // completedOrSkippedTasks returns a list of the names of all of the PipelineTasks in state
 // which have completed or skipped
 func (facts *PipelineRunFacts) completedOrSkippedDAGTasks() []string {


Test patch:
diff --git a/pkg/reconciler/pipelinerun/resources/pipelinerunstate_test.go b/pkg/reconciler/pipelinerun/resources/pipelinerunstate_test.go
index ebde2347874..2c32a9c5e46 100644
--- a/pkg/reconciler/pipelinerun/resources/pipelinerunstate_test.go
+++ b/pkg/reconciler/pipelinerun/resources/pipelinerunstate_test.go
@@ -44,7 +44,7 @@ var now = time.Date(2022, time.January, 1, 0, 0, 0, 0, time.UTC)
 var testClock = clock.NewFakePassiveClock(now)
 
 func TestPipelineRunFacts_CheckDAGTasksDoneDone(t *testing.T) {
-	var taskCancelledByStatusState = PipelineRunState{{
+	taskCancelledByStatusState := PipelineRunState{{
 		PipelineTask: &pts[4], // 2 retries needed
 		TaskRunName:  "pipelinerun-mytask1",
 		TaskRun:      withCancelled(makeRetried(trs[0])),
@@ -53,7 +53,7 @@ func TestPipelineRunFacts_CheckDAGTasksDoneDone(t *testing.T) {
 		},
 	}}
 
-	var taskCancelledBySpecState = PipelineRunState{{
+	taskCancelledBySpecState := PipelineRunState{{
 		PipelineTask: &pts[4],
 		TaskRunName:  "pipelinerun-mytask1",
 		TaskRun:      withCancelledBySpec(makeRetried(trs[0])),
@@ -62,7 +62,7 @@ func TestPipelineRunFacts_CheckDAGTasksDoneDone(t *testing.T) {
 		},
 	}}
 
-	var taskRunningState = PipelineRunState{{
+	taskRunningState := PipelineRunState{{
 		PipelineTask: &pts[4],
 		TaskRunName:  "pipelinerun-mytask1",
 		TaskRun:      makeStarted(trs[0]),
@@ -71,7 +71,7 @@ func TestPipelineRunFacts_CheckDAGTasksDoneDone(t *testing.T) {
 		},
 	}}
 
-	var taskSucceededState = PipelineRunState{{
+	taskSucceededState := PipelineRunState{{
 		PipelineTask: &pts[4],
 		TaskRunName:  "pipelinerun-mytask1",
 		TaskRun:      makeSucceeded(trs[0]),
@@ -80,7 +80,7 @@ func TestPipelineRunFacts_CheckDAGTasksDoneDone(t *testing.T) {
 		},
 	}}
 
-	var taskRetriedState = PipelineRunState{{
+	taskRetriedState := PipelineRunState{{
 		PipelineTask: &pts[3], // 1 retry needed
 		TaskRunName:  "pipelinerun-mytask1",
 		TaskRun:      withCancelled(makeRetried(trs[0])),
@@ -89,7 +89,7 @@ func TestPipelineRunFacts_CheckDAGTasksDoneDone(t *testing.T) {
 		},
 	}}
 
-	var taskExpectedState = PipelineRunState{{
+	taskExpectedState := PipelineRunState{{
 		PipelineTask: &pts[4], // 2 retries needed
 		TaskRunName:  "pipelinerun-mytask1",
 		TaskRun:      withRetries(makeToBeRetried(trs[0])),
@@ -98,7 +98,7 @@ func TestPipelineRunFacts_CheckDAGTasksDoneDone(t *testing.T) {
 		},
 	}}
 
-	var noTaskRunState = PipelineRunState{{
+	noTaskRunState := PipelineRunState{{
 		PipelineTask: &pts[4], // 2 retries needed
 		TaskRunName:  "pipelinerun-mytask1",
 		TaskRun:      nil,
@@ -107,28 +107,28 @@ func TestPipelineRunFacts_CheckDAGTasksDoneDone(t *testing.T) {
 		},
 	}}
 
-	var customRunRunningState = PipelineRunState{{
+	customRunRunningState := PipelineRunState{{
 		PipelineTask:  &pts[12],
 		CustomTask:    true,
 		RunObjectName: "pipelinerun-mytask13",
 		RunObject:     makeCustomRunStarted(customRuns[0]),
 	}}
 
-	var customRunSucceededState = PipelineRunState{{
+	customRunSucceededState := PipelineRunState{{
 		PipelineTask:  &pts[12],
 		CustomTask:    true,
 		RunObjectName: "pipelinerun-mytask13",
 		RunObject:     makeCustomRunSucceeded(customRuns[0]),
 	}}
 
-	var customRunFailedState = PipelineRunState{{
+	customRunFailedState := PipelineRunState{{
 		PipelineTask:  &pts[12],
 		CustomTask:    true,
 		RunObjectName: "pipelinerun-mytask13",
 		RunObject:     makeCustomRunFailed(customRuns[0]),
 	}}
 
-	var taskCancelledFailedWithRetries = PipelineRunState{{
+	taskCancelledFailedWithRetries := PipelineRunState{{
 		PipelineTask: &pts[4], // 2 retries needed
 		TaskRunName:  "pipelinerun-mytask1",
 		TaskRun:      withCancelled(makeFailed(trs[0])),
@@ -251,6 +251,7 @@ func TestIsBeforeFirstTaskRun_WithStartedRun(t *testing.T) {
 		t.Fatalf("Expected state to be after first taskrun (Run test)")
 	}
 }
+
 func TestIsBeforeFirstTaskRun_WithSucceededTask(t *testing.T) {
 	if finalScheduledState.IsBeforeFirstTaskRun() {
 		t.Fatalf("Expected state to be after first taskrun")
@@ -555,7 +556,7 @@ func TestGetNextTasks(t *testing.T) {
 }
 
 func TestGetNextTaskWithRetries(t *testing.T) {
-	var taskCancelledByStatusState = PipelineRunState{{
+	taskCancelledByStatusState := PipelineRunState{{
 		PipelineTask: &pts[4], // 2 retries needed
 		TaskRunName:  "pipelinerun-mytask1",
 		TaskRun:      withCancelled(makeRetried(trs[0])),
@@ -564,7 +565,7 @@ func TestGetNextTaskWithRetries(t *testing.T) {
 		},
 	}}
 
-	var taskCancelledBySpecState = PipelineRunState{{
+	taskCancelledBySpecState := PipelineRunState{{
 		PipelineTask: &pts[4],
 		TaskRunName:  "pipelinerun-mytask1",
 		TaskRun:      withCancelledBySpec(makeRetried(trs[0])),
@@ -573,7 +574,7 @@ func TestGetNextTaskWithRetries(t *testing.T) {
 		},
 	}}
 
-	var taskRunningState = PipelineRunState{{
+	taskRunningState := PipelineRunState{{
 		PipelineTask: &pts[4],
 		TaskRunName:  "pipelinerun-mytask1",
 		TaskRun:      makeStarted(trs[0]),
@@ -582,7 +583,7 @@ func TestGetNextTaskWithRetries(t *testing.T) {
 		},
 	}}
 
-	var taskSucceededState = PipelineRunState{{
+	taskSucceededState := PipelineRunState{{
 		PipelineTask: &pts[4],
 		TaskRunName:  "pipelinerun-mytask1",
 		TaskRun:      makeSucceeded(trs[0]),
@@ -591,7 +592,7 @@ func TestGetNextTaskWithRetries(t *testing.T) {
 		},
 	}}
 
-	var taskRetriedState = PipelineRunState{{
+	taskRetriedState := PipelineRunState{{
 		PipelineTask: &pts[3], // 1 retry needed
 		TaskRunName:  "pipelinerun-mytask1",
 		TaskRun:      withCancelled(makeRetried(trs[0])),
@@ -600,7 +601,7 @@ func TestGetNextTaskWithRetries(t *testing.T) {
 		},
 	}}
 
-	var customRunCancelledByStatusState = PipelineRunState{{
+	customRunCancelledByStatusState := PipelineRunState{{
 		PipelineTask:  &pts[4], // 2 retries needed
 		RunObjectName: "pipelinerun-mytask1",
 		RunObject:     withCustomRunCancelled(withCustomRunRetries(newCustomRun(customRuns[0]))),
@@ -610,7 +611,7 @@ func TestGetNextTaskWithRetries(t *testing.T) {
 		},
 	}}
 
-	var customRunCancelledBySpecState = PipelineRunState{{
+	customRunCancelledBySpecState := PipelineRunState{{
 		PipelineTask:  &pts[4],
 		RunObjectName: "pipelinerun-mytask1",
 		RunObject:     withCustomRunCancelledBySpec(withCustomRunRetries(newCustomRun(customRuns[0]))),
@@ -620,7 +621,7 @@ func TestGetNextTaskWithRetries(t *testing.T) {
 		},
 	}}
 
-	var customRunRunningState = PipelineRunState{{
+	customRunRunningState := PipelineRunState{{
 		PipelineTask:  &pts[4],
 		RunObjectName: "pipelinerun-mytask1",
 		RunObject:     makeCustomRunStarted(customRuns[0]),
@@ -630,7 +631,7 @@ func TestGetNextTaskWithRetries(t *testing.T) {
 		},
 	}}
 
-	var customRunSucceededState = PipelineRunState{{
+	customRunSucceededState := PipelineRunState{{
 		PipelineTask:  &pts[4],
 		RunObjectName: "pipelinerun-mytask1",
 		RunObject:     makeCustomRunSucceeded(customRuns[0]),
@@ -640,7 +641,7 @@ func TestGetNextTaskWithRetries(t *testing.T) {
 		},
 	}}
 
-	var taskCancelledByStatusStateMatrix = PipelineRunState{{
+	taskCancelledByStatusStateMatrix := PipelineRunState{{
 		PipelineTask: &pts[20], // 2 retries needed
 		TaskRunNames: []string{"pipelinerun-mytask1"},
 		TaskRuns:     []*v1beta1.TaskRun{withCancelled(makeRetried(trs[0]))},
@@ -649,7 +650,7 @@ func TestGetNextTaskWithRetries(t *testing.T) {
 		},
 	}}
 
-	var taskCancelledBySpecStateMatrix = PipelineRunState{{
+	taskCancelledBySpecStateMatrix := PipelineRunState{{
 		PipelineTask: &pts[20], // 2 retries needed
 		TaskRunNames: []string{"pipelinerun-mytask1"},
 		TaskRuns:     []*v1beta1.TaskRun{withCancelledBySpec(makeRetried(trs[0]))},
@@ -658,7 +659,7 @@ func TestGetNextTaskWithRetries(t *testing.T) {
 		},
 	}}
 
-	var taskRunningStateMatrix = PipelineRunState{{
+	taskRunningStateMatrix := PipelineRunState{{
 		PipelineTask: &pts[20], // 2 retries needed
 		TaskRunNames: []string{"pipelinerun-mytask1"},
 		TaskRuns:     []*v1beta1.TaskRun{makeStarted(trs[0])},
@@ -667,7 +668,7 @@ func TestGetNextTaskWithRetries(t *testing.T) {
 		},
 	}}
 
-	var taskSucceededStateMatrix = PipelineRunState{{
+	taskSucceededStateMatrix := PipelineRunState{{
 		PipelineTask: &pts[20], // 2 retries needed
 		TaskRunNames: []string{"pipelinerun-mytask1"},
 		TaskRuns:     []*v1beta1.TaskRun{makeSucceeded(trs[0])},
@@ -676,7 +677,7 @@ func TestGetNextTaskWithRetries(t *testing.T) {
 		},
 	}}
 
-	var taskRetriedStateMatrix = PipelineRunState{{
+	taskRetriedStateMatrix := PipelineRunState{{
 		PipelineTask: &pts[17], // 1 retry needed
 		TaskRunNames: []string{"pipelinerun-mytask1"},
 		TaskRuns:     []*v1beta1.TaskRun{withCancelled(makeRetried(trs[0]))},
@@ -685,7 +686,7 @@ func TestGetNextTaskWithRetries(t *testing.T) {
 		},
 	}}
 
-	var runCancelledByStatusStateMatrix = PipelineRunState{{
+	runCancelledByStatusStateMatrix := PipelineRunState{{
 		PipelineTask:   &pts[20], // 2 retries needed
 		RunObjectNames: []string{"pipelinerun-mytask1"},
 		RunObjects:     []v1beta1.RunObject{withCustomRunCancelled(withCustomRunRetries(newCustomRun(customRuns[0])))},
@@ -695,7 +696,7 @@ func TestGetNextTaskWithRetries(t *testing.T) {
 		},
 	}}
 
-	var runCancelledBySpecStateMatrix = PipelineRunState{{
+	runCancelledBySpecStateMatrix := PipelineRunState{{
 		PipelineTask:   &pts[20], // 2 retries needed
 		RunObjectNames: []string{"pipelinerun-mytask1"},
 		RunObjects:     []v1beta1.RunObject{withCustomRunCancelledBySpec(withCustomRunRetries(newCustomRun(customRuns[0])))},
@@ -705,7 +706,7 @@ func TestGetNextTaskWithRetries(t *testing.T) {
 		},
 	}}
 
-	var runRunningStateMatrix = PipelineRunState{{
+	runRunningStateMatrix := PipelineRunState{{
 		PipelineTask:   &pts[20], // 2 retries needed
 		RunObjectNames: []string{"pipelinerun-mytask1"},
 		RunObjects:     []v1beta1.RunObject{makeCustomRunStarted(customRuns[0])},
@@ -715,7 +716,7 @@ func TestGetNextTaskWithRetries(t *testing.T) {
 		},
 	}}
 
-	var customRunSucceededStateMatrix = PipelineRunState{{
+	customRunSucceededStateMatrix := PipelineRunState{{
 		PipelineTask:   &pts[20], // 2 retries needed
 		RunObjectNames: []string{"pipelinerun-mytask1"},
 		RunObjects:     []v1beta1.RunObject{makeCustomRunSucceeded(customRuns[0])},
@@ -725,7 +726,7 @@ func TestGetNextTaskWithRetries(t *testing.T) {
 		},
 	}}
 
-	var customRunRetriedStateMatrix = PipelineRunState{{
+	customRunRetriedStateMatrix := PipelineRunState{{
 		PipelineTask:   &pts[17], // 1 retry needed
 		RunObjectNames: []string{"pipelinerun-mytask1"},
 		RunObjects:     []v1beta1.RunObject{withCustomRunCancelled(withCustomRunRetries(newCustomRun(customRuns[0])))},
@@ -1283,7 +1284,7 @@ func TestPipelineRunState_CompletedOrSkippedDAGTasks(t *testing.T) {
 
 func buildPipelineStateWithLargeDependencyGraph(t *testing.T) PipelineRunState {
 	t.Helper()
-	var task = &v1beta1.Task{
+	task := &v1beta1.Task{
 		ObjectMeta: metav1.ObjectMeta{
 			Name: "task",
 		},
@@ -1342,7 +1343,7 @@ func buildPipelineStateWithLargeDependencyGraph(t *testing.T) PipelineRunState {
 
 func buildPipelineStateWithMultipleTaskResults(t *testing.T, includeWhen bool) PipelineRunState {
 	t.Helper()
-	var task = &v1beta1.Task{
+	task := &v1beta1.Task{
 		ObjectMeta: metav1.ObjectMeta{
 			Name: "task",
 		},
@@ -1523,7 +1524,7 @@ func TestPipelineRunState_GetFinalTasksAndNames(t *testing.T) {
 }
 
 func TestGetPipelineConditionStatus(t *testing.T) {
-	var taskRetriedState = PipelineRunState{{
+	taskRetriedState := PipelineRunState{{
 		PipelineTask: &pts[3], // 1 retry needed
 		TaskRunName:  "pipelinerun-mytask1",
 		TaskRun:      withCancelled(makeRetried(trs[0])),
@@ -1532,19 +1533,19 @@ func TestGetPipelineConditionStatus(t *testing.T) {
 		},
 	}}
 
-	var taskCancelledFailed = PipelineRunState{{
+	taskCancelledFailed := PipelineRunState{{
 		PipelineTask: &pts[4],
 		TaskRunName:  "pipelinerun-mytask1",
 		TaskRun:      withCancelled(makeFailed(trs[0])),
 	}}
 
-	var taskCancelledFailedTimedOut = PipelineRunState{{
+	taskCancelledFailedTimedOut := PipelineRunState{{
 		PipelineTask: &pts[4],
 		TaskRunName:  "pipelinerun-mytask1",
 		TaskRun:      withCancelledForTimeout(makeFailed(trs[0])),
 	}}
 
-	var cancelledTask = PipelineRunState{{
+	cancelledTask := PipelineRunState{{
 		PipelineTask: &pts[3], // 1 retry needed
 		TaskRunName:  "pipelinerun-mytask1",
 		TaskRun: &v1beta1.TaskRun{
@@ -1561,7 +1562,7 @@ func TestGetPipelineConditionStatus(t *testing.T) {
 		},
 	}}
 
-	var cancelledRun = PipelineRunState{{
+	cancelledRun := PipelineRunState{{
 		PipelineTask:  &pts[12],
 		CustomTask:    true,
 		RunObjectName: "pipelinerun-mytask13",
@@ -1576,7 +1577,7 @@ func TestGetPipelineConditionStatus(t *testing.T) {
 		},
 	}}
 
-	var timedOutRun = PipelineRunState{{
+	timedOutRun := PipelineRunState{{
 		PipelineTask:  &pts[12],
 		CustomTask:    true,
 		RunObjectName: "pipelinerun-mytask14",
@@ -1594,7 +1595,7 @@ func TestGetPipelineConditionStatus(t *testing.T) {
 		},
 	}}
 
-	var notRunningRun = PipelineRunState{{
+	notRunningRun := PipelineRunState{{
 		PipelineTask:  &pts[12],
 		CustomTask:    true,
 		RunObjectName: "pipelinerun-mytask14",
@@ -1604,7 +1605,7 @@ func TestGetPipelineConditionStatus(t *testing.T) {
 	// Of the 4, 1 passed, 1 cancelled, 2 failed
 	// 1 runAfter the passed one, currently running
 	// 1 runAfter the failed one, which is marked as incomplete
-	var taskMultipleFailuresSkipRunning = PipelineRunState{{
+	taskMultipleFailuresSkipRunning := PipelineRunState{{
 		TaskRunName:  "task0taskrun",
 		PipelineTask: &pts[5],
 		TaskRun:      makeSucceeded(trs[0]),
@@ -1618,10 +1619,10 @@ func TestGetPipelineConditionStatus(t *testing.T) {
 		TaskRun:      makeFailed(trs[0]),
 	}}
 
-	var taskMultipleFailuresOneCancel = taskMultipleFailuresSkipRunning
+	taskMultipleFailuresOneCancel := taskMultipleFailuresSkipRunning
 	taskMultipleFailuresOneCancel = append(taskMultipleFailuresOneCancel, cancelledTask[0])
 
-	var taskNotRunningWithSuccesfulParentsOneFailed = PipelineRunState{{
+	taskNotRunningWithSuccesfulParentsOneFailed := PipelineRunState{{
 		TaskRunName:  "task0taskrun",
 		PipelineTask: &pts[5],
 		TaskRun:      makeSucceeded(trs[0]),
@@ -2303,6 +2304,149 @@ func TestPipelineRunFacts_GetPipelineTaskStatus(t *testing.T) {
 	}
 }
 
+func TestPipelineRunFacts_GetPipelineFinalTaskStatus(t *testing.T) {
+	tcs := []struct {
+		name           string
+		state          PipelineRunState
+		finalTasks     []v1beta1.PipelineTask
+		expectedStatus map[string]string
+	}{{
+		name:       "no-tasks-started",
+		state:      noneStartedState,
+		finalTasks: []v1beta1.PipelineTask{pts[0], pts[1]},
+		expectedStatus: map[string]string{
+			PipelineTaskStatusPrefix + pts[0].Name + PipelineTaskStatusSuffix: PipelineTaskStateNone,
+			PipelineTaskStatusPrefix + pts[1].Name + PipelineTaskStatusSuffix: PipelineTaskStateNone,
+		},
+	}, {
+		name:       "one-task-started",
+		state:      oneStartedState,
+		finalTasks: []v1beta1.PipelineTask{pts[0], pts[1]},
+		expectedStatus: map[string]string{
+			PipelineTaskStatusPrefix + pts[0].Name + PipelineTaskStatusSuffix: PipelineTaskStateNone,
+			PipelineTaskStatusPrefix + pts[1].Name + PipelineTaskStatusSuffix: PipelineTaskStateNone,
+		},
+	}, {
+		name:       "one-task-finished",
+		state:      oneFinishedState,
+		finalTasks: []v1beta1.PipelineTask{pts[0], pts[1]},
+		expectedStatus: map[string]string{
+			PipelineTaskStatusPrefix + pts[0].Name + PipelineTaskStatusSuffix: v1beta1.TaskRunReasonSuccessful.String(),
+			PipelineTaskStatusPrefix + pts[1].Name + PipelineTaskStatusSuffix: PipelineTaskStateNone,
+		},
+	}, {
+		name:       "one-task-failed",
+		state:      oneFailedState,
+		finalTasks: []v1beta1.PipelineTask{pts[0], pts[1]},
+		expectedStatus: map[string]string{
+			PipelineTaskStatusPrefix + pts[0].Name + PipelineTaskStatusSuffix: v1beta1.TaskRunReasonFailed.String(),
+			PipelineTaskStatusPrefix + pts[1].Name + PipelineTaskStatusSuffix: PipelineTaskStateNone,
+		},
+	}, {
+		name:       "all-finished",
+		state:      allFinishedState,
+		finalTasks: []v1beta1.PipelineTask{pts[0], pts[1]},
+		expectedStatus: map[string]string{
+			PipelineTaskStatusPrefix + pts[0].Name + PipelineTaskStatusSuffix: v1beta1.TaskRunReasonSuccessful.String(),
+			PipelineTaskStatusPrefix + pts[1].Name + PipelineTaskStatusSuffix: v1beta1.TaskRunReasonSuccessful.String(),
+		},
+	}, {
+		name: "task-with-when-expressions-passed",
+		state: PipelineRunState{{
+			PipelineTask: &pts[9],
+			TaskRunNames: []string{"pr-guard-succeeded-task-not-started"},
+			TaskRuns:     nil,
+			ResolvedTask: &resources.ResolvedTask{
+				TaskSpec: &task.Spec,
+			},
+		}},
+		finalTasks: []v1beta1.PipelineTask{pts[9]},
+		expectedStatus: map[string]string{
+			PipelineTaskStatusPrefix + pts[9].Name + PipelineTaskStatusSuffix: PipelineTaskStateNone,
+		},
+	}, {
+		name: "tasks-when-expression-failed-and-task-skipped",
+		state: PipelineRunState{{
+			PipelineTask: &pts[10],
+			TaskRunNames: []string{"pr-guardedtask-skipped"},
+			ResolvedTask: &resources.ResolvedTask{
+				TaskSpec: &task.Spec,
+			},
+		}},
+		finalTasks: []v1beta1.PipelineTask{pts[10]},
+		expectedStatus: map[string]string{
+			PipelineTaskStatusPrefix + pts[10].Name + PipelineTaskStatusSuffix: PipelineTaskStateNone,
+		},
+	}, {
+		name: "when-expression-task-with-parent-started",
+		state: PipelineRunState{{
+			PipelineTask: &pts[0],
+			TaskRuns:     []*v1beta1.TaskRun{makeStarted(trs[0])},
+			ResolvedTask: &resources.ResolvedTask{
+				TaskSpec: &task.Spec,
+			},
+		}, {
+			PipelineTask: &pts[11],
+			TaskRuns:     nil,
+			ResolvedTask: &resources.ResolvedTask{
+				TaskSpec: &task.Spec,
+			},
+		}},
+		finalTasks: []v1beta1.PipelineTask{pts[0], pts[11]},
+		expectedStatus: map[string]string{
+			PipelineTaskStatusPrefix + pts[0].Name + PipelineTaskStatusSuffix:  PipelineTaskStateNone,
+			PipelineTaskStatusPrefix + pts[11].Name + PipelineTaskStatusSuffix: PipelineTaskStateNone,
+		},
+	}, {
+		name:       "task-cancelled",
+		state:      taskCancelled,
+		finalTasks: []v1beta1.PipelineTask{pts[4]},
+		expectedStatus: map[string]string{
+			PipelineTaskStatusPrefix + pts[4].Name + PipelineTaskStatusSuffix: PipelineTaskStateNone,
+		},
+	}, {
+		name: "one-skipped-one-failed-aggregate-status-must-be-failed",
+		state: PipelineRunState{{
+			PipelineTask: &pts[10],
+			TaskRunNames: []string{"pr-guardedtask-skipped"},
+			ResolvedTask: &resources.ResolvedTask{
+				TaskSpec: &task.Spec,
+			},
+		}, {
+			PipelineTask: &pts[0],
+			TaskRunNames: []string{"pipelinerun-mytask1"},
+			TaskRuns:     []*v1beta1.TaskRun{makeFailed(trs[0])},
+			ResolvedTask: &resources.ResolvedTask{
+				TaskSpec: &task.Spec,
+			},
+		}},
+		finalTasks: []v1beta1.PipelineTask{pts[0], pts[10]},
+		expectedStatus: map[string]string{
+			PipelineTaskStatusPrefix + pts[0].Name + PipelineTaskStatusSuffix:  v1beta1.PipelineRunReasonFailed.String(),
+			PipelineTaskStatusPrefix + pts[10].Name + PipelineTaskStatusSuffix: PipelineTaskStateNone,
+		},
+	}}
+	for _, tc := range tcs {
+		t.Run(tc.name, func(t *testing.T) {
+			d, err := dag.Build(v1beta1.PipelineTaskList(tc.finalTasks), v1beta1.PipelineTaskList(tc.finalTasks).Deps())
+			if err != nil {
+				t.Fatalf("Unexpected error while building graph for DAG tasks %v: %v", tc.finalTasks, err)
+			}
+			facts := PipelineRunFacts{
+				State:           tc.state,
+				FinalTasksGraph: d,
+				TimeoutsState: PipelineRunTimeoutsState{
+					Clock: testClock,
+				},
+			}
+			s := facts.GetPipelineFinalTaskStatus()
+			if d := cmp.Diff(tc.expectedStatus, s); d != "" {
+				t.Fatalf("Test failed: %s Mismatch in pipelineFinalTask execution state %s", tc.name, diff.PrintWantGot(d))
+			}
+		})
+	}
+}
+
 func TestPipelineRunFacts_GetSkippedTasks(t *testing.T) {
 	for _, tc := range []struct {
 		name                 string
@@ -2616,7 +2760,8 @@ func TestPipelineRunState_GetResultsFuncs(t *testing.T) {
 				}, {
 					Name:  "quxbaz",
 					Value: v1beta1.ParamValue{Type: v1beta1.ParamTypeArray, ArrayVal: []string{"qux", "baz"}},
-				}}},
+				}},
+			},
 		},
 		TaskRuns: []*v1beta1.TaskRun{{
 			TypeMeta:   metav1.TypeMeta{APIVersion: "tekton.dev/v1beta1"},
@@ -2679,7 +2824,8 @@ func TestPipelineRunState_GetResultsFuncs(t *testing.T) {
 				}, {
 					Name:  "quxbaz",
 					Value: v1beta1.ParamValue{Type: v1beta1.ParamTypeArray, ArrayVal: []string{"qux", "baz"}},
-				}}},
+				}},
+			},
 		},
 		RunObjects: []v1beta1.RunObject{
 			&v1beta1.CustomRun{
@@ -2976,7 +3122,8 @@ func TestPipelineRunState_GetChildReferences(t *testing.T) {
 						}, {
 							Name:  "quxbaz",
 							Value: v1beta1.ParamValue{Type: v1beta1.ParamTypeArray, ArrayVal: []string{"qux", "baz"}},
-						}}},
+						}},
+					},
 				},
 				TaskRuns: []*v1beta1.TaskRun{nil, nil, nil, nil},
 			}},
@@ -3005,7 +3152,8 @@ func TestPipelineRunState_GetChildReferences(t *testing.T) {
 						}, {
 							Name:  "quxbaz",
 							Value: v1beta1.ParamValue{Type: v1beta1.ParamTypeArray, ArrayVal: []string{"qux", "baz"}},
-						}}},
+						}},
+					},
 				},
 				TaskRuns: []*v1beta1.TaskRun{{
 					TypeMeta:   metav1.TypeMeta{APIVersion: "tekton.dev/v1beta1"},
@@ -3092,7 +3240,8 @@ func TestPipelineRunState_GetChildReferences(t *testing.T) {
 						}, {
 							Name:  "quxbaz",
 							Value: v1beta1.ParamValue{Type: v1beta1.ParamTypeArray, ArrayVal: []string{"qux", "baz"}},
-						}}},
+						}},
+					},
 				},
 				CustomTask: true,
 			}},
@@ -3119,7 +3268,8 @@ func TestPipelineRunState_GetChildReferences(t *testing.T) {
 						}, {
 							Name:  "quxbaz",
 							Value: v1beta1.ParamValue{Type: v1beta1.ParamTypeArray, ArrayVal: []string{"qux", "baz"}},
-						}}},
+						}},
+					},
 				},
 				CustomTask: true,
 				RunObjects: []v1beta1.RunObject{