//go:build functional

package spectests_test

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"reflect"
	"sort"
	"strconv"
	"strings"
	"testing"
	"time"
)

const (
	ownerSessionCommandTimeout = 10 * time.Minute
	ownerSessionUpTimeout      = 3 * time.Hour
	ownerSessionJournalFile    = "owner-artifact-session-state.json"
	ownerSessionJournalV1      = 1
)

type ownerSessionRun struct {
	t             *testing.T
	repoRoot      string
	root          string
	caseName      string
	environment   string
	currentHead   string
	runDirEnv     string
	retryStageEnv string
	destroyEnv    string
	retryStage    string
	stageNames    []string
	ownerWork     string
	ownerBare     string
	awsBare       string
	gitConfig     string
	sessionDir    string
	cp9Root       string
	cp9Config     string
	sessionName   string
	stateDir      string
	contextPath   string
	snapshot      string
	commander     *resolverRun
	fixtureHead   string
	awsHeads      map[string]string
	journal       ownerSessionJournal
	activeFailure string
	cleaned       bool
}

type ownerSessionMetadata struct {
	Account  string `json:"account"`
	Region   string `json:"region"`
	Owner    string `json:"owner"`
	CP9Root  string `json:"cp9_root"`
	Config   string `json:"config"`
	Session  string `json:"session"`
	WorkRoot string `json:"work_root"`
	Head     string `json:"head"`
}

type ownerSessionJournal struct {
	Version     int                                   `json:"version"`
	Case        string                                `json:"case"`
	Environment string                                `json:"environment"`
	CP9Head     string                                `json:"cp9_head"`
	Session     string                                `json:"session,omitempty"`
	Cleanup     string                                `json:"cleanup"`
	Stages      map[string][]ownerSessionStageReceipt `json:"stages"`
	Actions     map[string]ownerSessionActionReceipt  `json:"actions"`
	CreatedAt   string                                `json:"created_at"`
	UpdatedAt   string                                `json:"updated_at"`
}

type ownerSessionStageReceipt struct {
	Head       string `json:"head"`
	StartedAt  string `json:"started_at"`
	FinishedAt string `json:"finished_at,omitempty"`
	Status     string `json:"status"`
	Product    string `json:"product"`
	Harness    string `json:"harness"`
	Security   string `json:"security,omitempty"`
	O11Y       string `json:"o11y,omitempty"`
	Message    string `json:"message,omitempty"`
}

type ownerSessionActionReceipt struct {
	Head       string   `json:"head"`
	Directory  string   `json:"directory"`
	Command    []string `json:"command"`
	StartedAt  string   `json:"started_at"`
	FinishedAt string   `json:"finished_at,omitempty"`
	Status     string   `json:"status"`
	ExitCode   int      `json:"exit_code"`
	Stdout     string   `json:"stdout,omitempty"`
	Stderr     string   `json:"stderr,omitempty"`
}

type ownerSessionContext struct {
	Artifacts struct {
		SnapshotPath string `json:"snapshot_path"`
	} `json:"artifacts"`
	Host struct {
		DedicatedAPI struct {
			RemoteAdminKubeconfigPath string `json:"remote_admin_kubeconfig_path"`
		} `json:"dedicated_api"`
	} `json:"host"`
}

type ownerRolloutTarget struct {
	Name      string
	Backend   string
	Kind      string
	Namespace string
	Workload  string
	Container string
}

type ownerContainer struct {
	Name  string `json:"name"`
	Image string `json:"image"`
}

type ownerContainerStatus struct {
	Name  string `json:"name"`
	Ready bool   `json:"ready"`
}

type ownerContractsFile struct {
	Contracts []struct {
		Name         string `json:"name"`
		StagingImage string `json:"stagingImage"`
		Backend      struct {
			Kind string `json:"kind"`
		} `json:"backend"`
		RolloutTarget struct {
			Kind      string `json:"kind"`
			Namespace string `json:"namespace"`
			Name      string `json:"name"`
			Container string `json:"container"`
		} `json:"rolloutTarget"`
	} `json:"contracts"`
}

type ownerObject struct {
	Metadata struct {
		Name              string            `json:"name"`
		UID               string            `json:"uid"`
		Generation        int64             `json:"generation"`
		Annotations       map[string]string `json:"annotations"`
		Labels            map[string]string `json:"labels"`
		DeletionTimestamp any               `json:"deletionTimestamp"`
		OwnerReferences   []struct {
			APIVersion string `json:"apiVersion"`
			Kind       string `json:"kind"`
			Name       string `json:"name"`
			UID        string `json:"uid"`
			Controller bool   `json:"controller"`
		} `json:"ownerReferences"`
	} `json:"metadata"`
	Spec struct {
		Package      string           `json:"package"`
		DesiredState string           `json:"desiredState"`
		Replicas     *int64           `json:"replicas"`
		Image        string           `json:"image"`
		Containers   []ownerContainer `json:"containers"`
		Selector     struct {
			MatchLabels      map[string]string `json:"matchLabels"`
			MatchExpressions []any             `json:"matchExpressions"`
		} `json:"selector"`
		Template struct {
			Spec struct {
				Containers []ownerContainer `json:"containers"`
			} `json:"spec"`
		} `json:"template"`
	} `json:"spec"`
	Status struct {
		Phase                  string                 `json:"phase"`
		ObservedGeneration     int64                  `json:"observedGeneration"`
		UpdatedReplicas        int64                  `json:"updatedReplicas"`
		ReadyReplicas          int64                  `json:"readyReplicas"`
		AvailableReplicas      int64                  `json:"availableReplicas"`
		CurrentRevision        string                 `json:"currentRevision"`
		UpdateRevision         string                 `json:"updateRevision"`
		DesiredNumberScheduled int64                  `json:"desiredNumberScheduled"`
		UpdatedNumberScheduled int64                  `json:"updatedNumberScheduled"`
		NumberReady            int64                  `json:"numberReady"`
		CurrentIdentifier      string                 `json:"currentIdentifier"`
		ContainerStatuses      []ownerContainerStatus `json:"containerStatuses"`
		Conditions             []struct {
			Type   string `json:"type"`
			Status string `json:"status"`
		} `json:"conditions"`
	} `json:"status"`
}

type ownerObjectList struct {
	Items []ownerObject `json:"items"`
}

func newOwnerSessionRun(t *testing.T, slug, environment, runDirEnv, retryStageEnv, destroyEnv string, stageNames ...string) *ownerSessionRun {
	t.Helper()
	wd, err := os.Getwd()
	if err != nil {
		t.Fatalf("resolve spec-tests directory: %v", err)
	}
	repoRoot := filepath.Dir(wd)
	evidenceRoot := filepath.Join(repoRoot, "artifacts", "spec-tests")
	if err := os.MkdirAll(evidenceRoot, 0o700); err != nil {
		t.Fatalf("create spec evidence root: %v", err)
	}
	root := strings.TrimSpace(os.Getenv(runDirEnv))
	if root == "" {
		root = filepath.Join(evidenceRoot, time.Now().UTC().Format("20060102T150405Z")+"-"+slug+"-owner-artifacts")
		if err := os.Mkdir(root, 0o700); err != nil {
			t.Fatalf("create evidence directory: %v", err)
		}
	} else {
		root, err = filepath.Abs(root)
		if err != nil {
			t.Fatalf("resolve %s: %v", runDirEnv, err)
		}
		canonicalEvidence, evidenceErr := filepath.EvalSymlinks(evidenceRoot)
		canonicalRoot, rootErr := filepath.EvalSymlinks(root)
		if evidenceErr != nil || rootErr != nil || !ownerSessionChildPath(canonicalEvidence, canonicalRoot) {
			t.Fatalf("%s must name an existing child of %s", runDirEnv, evidenceRoot)
		}
	}
	r := &ownerSessionRun{
		t:             t,
		repoRoot:      repoRoot,
		root:          root,
		caseName:      slug,
		environment:   environment,
		currentHead:   currentCommit(t, repoRoot),
		runDirEnv:     runDirEnv,
		retryStageEnv: retryStageEnv,
		destroyEnv:    destroyEnv,
		retryStage:    strings.TrimSpace(os.Getenv(retryStageEnv)),
		stageNames:    append([]string(nil), stageNames...),
		ownerWork:     filepath.Join(root, "app-delivery-work"),
		ownerBare:     filepath.Join(root, "app-delivery.git"),
		awsBare:       filepath.Join(root, "aws-shared-cd.git"),
		gitConfig:     filepath.Join(root, "gitconfig"),
		sessionDir:    filepath.Join(root, "session"),
		awsHeads:      map[string]string{},
		activeFailure: "harness",
	}
	r.commander = &resolverRun{
		t:          t,
		repoRoot:   repoRoot,
		root:       root,
		ownerWork:  r.ownerWork,
		ownerBare:  r.ownerBare,
		awsBare:    r.awsBare,
		gitConfig:  r.gitConfig,
		commandEnv: map[string]string{"GIT_CONFIG_GLOBAL": r.gitConfig},
		awsHeads:   r.awsHeads,
		commandID:  ownerSessionPriorCommandID(root),
		failure:    &r.activeFailure,
	}
	journalPath := filepath.Join(root, ownerSessionJournalFile)
	data, err := os.ReadFile(journalPath)
	if errors.Is(err, os.ErrNotExist) {
		now := time.Now().UTC().Format(time.RFC3339Nano)
		r.journal = ownerSessionJournal{
			Version: ownerSessionJournalV1, Case: slug, Environment: environment, CP9Head: r.currentHead,
			Cleanup: "not_run", Stages: map[string][]ownerSessionStageReceipt{}, Actions: map[string]ownerSessionActionReceipt{},
			CreatedAt: now, UpdatedAt: now,
		}
		if err := r.persistJournal(); err != nil {
			t.Fatalf("create owner-session journal: %v", err)
		}
	} else if err != nil {
		t.Fatalf("read owner-session journal: %v", err)
	} else if err := json.Unmarshal(data, &r.journal); err != nil {
		t.Fatalf("parse owner-session journal: %v", err)
	} else if r.journal.Version != ownerSessionJournalV1 || r.journal.Case != slug || r.journal.Environment != environment ||
		r.journal.Stages == nil || r.journal.Actions == nil {
		t.Fatal("owner-session journal has an incompatible identity or shape")
	}
	if _, err := os.Stat(filepath.Join(r.sessionDir, "default-804-session.json")); err == nil {
		r.loadSessionMetadata(t)
	} else if !errors.Is(err, os.ErrNotExist) {
		t.Fatalf("inspect owner-session metadata: %v", err)
	}
	return r
}

func (r *ownerSessionRun) persistJournal() error {
	r.journal.UpdatedAt = time.Now().UTC().Format(time.RFC3339Nano)
	data, err := json.MarshalIndent(r.journal, "", "  ")
	if err != nil {
		return err
	}
	temporary, err := os.CreateTemp(r.root, ".owner-session-state-")
	if err != nil {
		return err
	}
	temporaryPath := temporary.Name()
	defer os.Remove(temporaryPath)
	if err := temporary.Chmod(0o600); err != nil {
		_ = temporary.Close()
		return err
	}
	if _, err := temporary.Write(append(data, '\n')); err != nil {
		_ = temporary.Close()
		return err
	}
	if err := temporary.Sync(); err != nil {
		_ = temporary.Close()
		return err
	}
	if err := temporary.Close(); err != nil {
		return err
	}
	return os.Rename(temporaryPath, filepath.Join(r.root, ownerSessionJournalFile))
}

func (r *ownerSessionRun) loadSessionMetadata(t *testing.T) {
	t.Helper()
	var metadata ownerSessionMetadata
	r.readJSON(t, filepath.Join(r.sessionDir, "default-804-session.json"), &metadata)
	if metadata.Account != "804837308162" || metadata.Region != "us-west-2" || metadata.Owner == "" ||
		metadata.CP9Root == "" || metadata.Config == "" || metadata.Session == "" || metadata.Head != r.journal.CP9Head {
		t.Fatal("session metadata does not match the frozen head, account, region, owner, or session")
	}
	r.cp9Root, r.cp9Config, r.sessionName = metadata.CP9Root, metadata.Config, metadata.Session
	if r.journal.Session == "" {
		r.journal.Session = metadata.Session
		if err := r.persistJournal(); err != nil {
			t.Fatalf("record owner-session identity: %v", err)
		}
	} else if r.journal.Session != metadata.Session {
		t.Fatalf("session metadata names %s, want preserved %s", metadata.Session, r.journal.Session)
	}
	if r.journal.Cleanup == "pass" || r.cleanupRequested() {
		return
	}
	if head := r.gitOutput(t, "resume-cp9-head", r.cp9Root, "rev-parse", "HEAD"); head != r.journal.CP9Head {
		t.Fatalf("prepared CP9 head=%s, want %s", head, r.journal.CP9Head)
	}
	r.commander.commandEnv["CP9_REPO_ROOT"] = r.cp9Root
	for name, path := range map[string]string{
		"fixture":     filepath.Join(r.root, "app-delivery-current.txt"),
		"aws-main":    filepath.Join(r.root, "aws-shared-cd-main.txt"),
		"aws-staging": filepath.Join(r.root, "aws-shared-cd-release-0.62.txt"),
	} {
		data, err := os.ReadFile(path)
		if err != nil {
			t.Fatalf("read frozen %s identity: %v", name, err)
		}
		value := strings.TrimSpace(string(data))
		switch name {
		case "fixture":
			r.fixtureHead = value
		case "aws-main":
			r.awsHeads["main"] = value
		case "aws-staging":
			r.awsHeads["release-0.62"] = value
		}
	}
	r.commander.fixtureHead = r.fixtureHead
	statusPath := filepath.Join(r.sessionDir, "status-latest.json")
	if _, err := os.Stat(statusPath); err == nil {
		if len(r.stageNames) > 0 && r.retryStage == r.stageNames[0] && r.actionSucceeded("up") {
			stages := r.journal.Stages[r.stageNames[0]]
			if len(stages) > 0 && stages[len(stages)-1].Status != "passed" {
				// Validate completed action evidence; this path cannot execute another up.
				r.runInitialUp(t)
				r.activeFailure = "harness"
				r.copyFile(t, statusPath, filepath.Join(r.sessionDir,
					fmt.Sprintf("status-before-recovery-%d.json", time.Now().UnixNano())))
				return // The failed initial stage will collect a fresh derived observation.
			}
		}
		var status struct {
			SessionName string `json:"session_name"`
			StateDir    string `json:"state_dir"`
		}
		r.readJSON(t, statusPath, &status)
		if status.SessionName != r.sessionName || status.StateDir == "" {
			t.Fatal("saved session status does not match the preserved session")
		}
		r.stateDir = status.StateDir
		if !filepath.IsAbs(r.stateDir) {
			r.stateDir = filepath.Join(r.cp9Root, r.stateDir)
		}
		r.commander.commandEnv["CP9_STATE_DIR"] = r.stateDir
		r.contextPath = filepath.Join(r.stateDir, "context", "context.json")
		if _, err := os.Stat(r.contextPath); errors.Is(err, os.ErrNotExist) {
			r.contextPath = filepath.Join(r.sessionDir, "context.json")
		}
		var sessionContext ownerSessionContext
		r.readJSON(t, r.contextPath, &sessionContext)
		r.snapshot = sessionContext.Artifacts.SnapshotPath
	}
}

func ownerSessionPriorCommandID(root string) int {
	entries, err := os.ReadDir(filepath.Join(root, "commands"))
	if err != nil {
		return 0
	}
	maximum := 0
	for _, entry := range entries {
		prefix, _, ok := strings.Cut(entry.Name(), "-")
		if !ok {
			continue
		}
		value, err := strconv.Atoi(prefix)
		if err == nil && value > maximum {
			maximum = value
		}
	}
	return maximum
}

func ownerSessionChildPath(root, path string) bool {
	relative, err := filepath.Rel(root, path)
	return err == nil && relative != "." && relative != ".." && !strings.HasPrefix(relative, ".."+string(filepath.Separator))
}

func (r *ownerSessionRun) prepareRepositories(t *testing.T) {
	t.Helper()
	r.activeFailure = "prerequisite"
	if _, err := os.Stat(filepath.Join(r.repoRoot, "go.mod")); err != nil {
		t.Fatalf("repository root: %v", err)
	}
	for _, tool := range []string{"aws", "gh", "git", "jq", "kubectl", "make", "python3", "ssh"} {
		if result := r.run(t, "which-"+tool, time.Minute, "sh", "-c", "command -v \"$1\"", "sh", tool); result.ExitCode != 0 {
			t.Fatalf("required tool %s is unavailable", tool)
		}
	}
	r.must(t, "clone-app-delivery", 15*time.Minute, "gh", "repo", "clone", "tidbcloud/app-delivery", r.ownerWork, "--", "--depth=1")
	if pinned := os.Getenv("CP9_SPEC_APP_DELIVERY_COMMIT"); pinned != "" {
		r.must(t, "fetch-pinned-app-delivery", 10*time.Minute, "git", "-C", r.ownerWork, "-c", "credential.helper=", "-c", "credential.helper=!gh auth git-credential", "fetch", "--depth=1", "origin", pinned)
		r.must(t, "checkout-pinned-app-delivery", time.Minute, "git", "-C", r.ownerWork, "checkout", "--detach", pinned)
	}
	r.must(t, "configure-fixture-name", time.Minute, "git", "-C", r.ownerWork, "config", "user.name", "cp9-spec-test")
	r.must(t, "configure-fixture-email", time.Minute, "git", "-C", r.ownerWork, "config", "user.email", "cp9-spec-test@localhost")
	r.must(t, "clone-aws-shared-cd", 20*time.Minute, "gh", "repo", "clone", "tidbcloud/aws-shared-cd", r.awsBare, "--", "--bare")
	r.writeText(t, "cp9-head.txt", r.gitOutput(t, "cp9-head", r.repoRoot, "rev-parse", "HEAD"))
	r.writeText(t, "app-delivery-upstream.txt", r.gitOutput(t, "app-delivery-head", r.ownerWork, "rev-parse", "HEAD"))
	r.awsHeads["main"] = r.gitOutput(t, "aws-main", r.awsBare, "rev-parse", "main")
	r.awsHeads["release-0.62"] = r.gitOutput(t, "aws-staging", r.awsBare, "rev-parse", "release-0.62")
	r.writeText(t, "aws-shared-cd-main.txt", r.awsHeads["main"])
	r.writeText(t, "aws-shared-cd-release-0.62.txt", r.awsHeads["release-0.62"])
}

func (r *ownerSessionRun) freezeRepositories(t *testing.T) {
	t.Helper()
	r.fixtureHead = r.gitOutput(t, "fixture-head", r.ownerWork, "rev-parse", "HEAD")
	r.writeText(t, "app-delivery-fixture.txt", r.fixtureHead)
	r.must(t, "clone-app-delivery-mirror", 5*time.Minute, "git", "clone", "--bare", r.ownerWork, r.ownerBare)
	r.must(t, "set-app-delivery-origin", time.Minute, "git", "-C", r.ownerWork, "remote", "set-url", "origin", r.ownerBare)
	r.must(t, "push-fixture-main", 5*time.Minute, "git", "-C", r.ownerWork, "push", "--force", "origin", "HEAD:main")
	r.must(t, "allow-file-protocol", time.Minute, "git", "config", "--file", r.gitConfig, "protocol.file.allow", "always")
	r.must(t, "map-app-delivery", time.Minute, "git", "config", "--file", r.gitConfig, "url.file://"+r.ownerBare+".insteadOf", "https://github.com/tidbcloud/app-delivery.git")
	r.must(t, "map-aws-shared-cd", time.Minute, "git", "config", "--file", r.gitConfig, "url.file://"+r.awsBare+".insteadOf", "https://github.com/tidbcloud/aws-shared-cd.git")
	r.writeText(t, "app-delivery-current.txt", r.fixtureHead)
}

func (r *ownerSessionRun) commitFixture(t *testing.T, label string, paths ...string) string {
	t.Helper()
	args := append([]string{"git", "-C", r.ownerWork, "add"}, paths...)
	r.must(t, label+"-add", time.Minute, args...)
	result := r.run(t, label+"-diff", time.Minute, "git", "-C", r.ownerWork, "diff", "--cached", "--quiet")
	if result.ExitCode != 0 {
		r.must(t, label+"-commit", time.Minute, "git", "-C", r.ownerWork, "commit", "-m", "spec: "+label)
	}
	return r.gitOutput(t, label+"-head", r.ownerWork, "rev-parse", "HEAD")
}

func (r *ownerSessionRun) publishFixture(t *testing.T, label string, paths ...string) string {
	t.Helper()
	head := r.commitFixture(t, label, paths...)
	r.must(t, label+"-push", 5*time.Minute, "git", "-C", r.ownerWork, "push", "--force", "origin", "HEAD:main")
	r.writeText(t, filepath.Join("read-only", label+"-commit.txt"), head)
	r.writeText(t, "app-delivery-current.txt", head)
	r.fixtureHead = head
	r.commander.fixtureHead = head
	return head
}

func (r *ownerSessionRun) actionCompleted(name string) bool {
	return r.journal.Actions[name].Status == "completed"
}

func (r *ownerSessionRun) actionSucceeded(name string) bool {
	if name == "up" {
		name, _ = r.latestUpAction()
	}
	action := r.journal.Actions[name]
	return action.Status == "completed" && action.ExitCode == 0
}

func (r *ownerSessionRun) actionsSucceeded(names ...string) bool {
	for _, name := range names {
		if !r.actionSucceeded(name) {
			return false
		}
	}
	return true
}

func (r *ownerSessionRun) actionsReplaySafe(names ...string) bool {
	for _, name := range names {
		if name == "up" {
			name, _ = r.latestUpAction()
		}
		if r.journal.Actions[name].Status == "started" {
			return false
		}
	}
	return true
}

func (r *ownerSessionRun) beginInternalAction(t *testing.T, name, directory string, command ...string) bool {
	t.Helper()
	if saved, ok := r.journal.Actions[name]; ok {
		if saved.Head != r.journal.CP9Head || saved.Directory != directory || !reflect.DeepEqual(saved.Command, command) {
			t.Fatalf("action %s does not match the preserved receipt", name)
		}
		if saved.Status == "started" {
			t.Fatalf("action %s has unknown outcome; refusing to replay", name)
		}
		if saved.Status != "completed" {
			t.Fatalf("action %s has invalid status %q", name, saved.Status)
		}
		return false
	}
	r.journal.Actions[name] = ownerSessionActionReceipt{
		Head: r.journal.CP9Head, Directory: directory, Command: append([]string(nil), command...),
		StartedAt: time.Now().UTC().Format(time.RFC3339Nano), Status: "started", ExitCode: -1,
	}
	r.journal.Cleanup = "fail"
	if err := r.persistJournal(); err != nil {
		t.Fatalf("record start of action %s: %v", name, err)
	}
	return true
}

func (r *ownerSessionRun) completeInternalAction(t *testing.T, name string) {
	t.Helper()
	action := r.journal.Actions[name]
	if action.Status != "started" {
		t.Fatalf("action %s status=%q, want started", name, action.Status)
	}
	action.Status = "completed"
	action.ExitCode = 0
	action.FinishedAt = time.Now().UTC().Format(time.RFC3339Nano)
	r.journal.Actions[name] = action
	if err := r.persistJournal(); err != nil {
		t.Fatalf("record completion of action %s: %v", name, err)
	}
}

func (r *ownerSessionRun) startSession(t *testing.T, environment string) {
	t.Helper()
	if environment != r.environment {
		t.Fatalf("session environment=%q, want %q", environment, r.environment)
	}
	r.activeFailure = "prerequisite"
	r.mustActionDir(t, "prepare-session", 30*time.Minute, r.repoRoot, filepath.Join(r.repoRoot, "spec-tests", "scripts", "default-804-session.sh"), "prepare", "artifacts", r.sessionDir)
	r.activeFailure = "harness"
	r.loadSessionMetadata(t)
	config := fmt.Sprintf("\nartifacts:\n  environment: %s\nstarter:\n  enabled: false\n", environment)
	if r.beginInternalAction(t, "configure-session", r.cp9Root, "append-owner-artifact-config", environment) {
		f, err := os.OpenFile(r.cp9Config, os.O_APPEND|os.O_WRONLY, 0)
		if err != nil {
			t.Fatalf("open session config: %v", err)
		}
		if _, err := f.WriteString(config); err != nil {
			_ = f.Close()
			t.Fatalf("append owner-artifact config: %v", err)
		}
		if err := f.Close(); err != nil {
			t.Fatalf("close session config: %v", err)
		}
		r.completeInternalAction(t, "configure-session")
	}
	configData, err := os.ReadFile(r.cp9Config)
	if err != nil || bytes.Count(configData, []byte(config)) != 1 {
		t.Fatalf("session config does not contain exactly one frozen %s owner-artifact block: %v", environment, err)
	}
	r.mustDir(t, "build-session", 30*time.Minute, r.cp9Root, "make", "build")
	r.activeFailure = "product"
	dryRun := r.mustDir(t, "up-dry-run", 15*time.Minute, r.cp9Root, filepath.Join(r.cp9Root, "bin", "cp9"), "{sys.output.format=json}", "up", "dry-run=true", "config="+r.cp9Config)
	var dry map[string]any
	r.activeFailure = "harness"
	if err := json.Unmarshal([]byte(dryRun.Stdout), &dry); err != nil {
		t.Fatalf("parse dry-run result: %v", err)
	}
	if dry["no_mutation"] != true {
		r.activeFailure = "product"
		t.Fatal("dry-run did not prove no_mutation")
	}
	r.activeFailure = "product"
	r.mustActionDir(t, "setup", ownerSessionUpTimeout, r.cp9Root, filepath.Join(r.cp9Root, "bin", "cp9"), "setup", "checkout=false", "config="+r.cp9Config)
	r.runInitialUp(t)
	status := r.mustDir(t, "status", 15*time.Minute, r.cp9Root, filepath.Join(r.cp9Root, "bin", "cp9"), "{sys.output.format=json}", "status", "config="+r.cp9Config)
	var state struct {
		SessionName string `json:"session_name"`
		Status      string `json:"status"`
		StateDir    string `json:"state_dir"`
	}
	r.activeFailure = "harness"
	if err := json.Unmarshal([]byte(status.Stdout), &state); err != nil {
		t.Fatalf("parse session status: %v", err)
	}
	if state.SessionName != r.sessionName || state.Status != "ready" {
		r.activeFailure = "product"
		t.Fatalf("session is not Ready: session=%q status=%q", state.SessionName, state.Status)
	}
	if state.StateDir == "" {
		t.Fatal("session status has no state directory")
	}
	r.stateDir = state.StateDir
	if !filepath.IsAbs(r.stateDir) {
		r.stateDir = filepath.Join(r.cp9Root, r.stateDir)
	}
	r.commander.commandEnv["CP9_REPO_ROOT"] = r.cp9Root
	r.commander.commandEnv["CP9_STATE_DIR"] = r.stateDir
	r.contextPath = filepath.Join(r.stateDir, "context", "context.json")
	var sessionContext ownerSessionContext
	r.readJSON(t, r.contextPath, &sessionContext)
	r.snapshot = sessionContext.Artifacts.SnapshotPath
	if r.snapshot == "" {
		t.Fatal("session context has no artifact snapshot")
	}
	r.copyFile(t, r.contextPath, filepath.Join(r.sessionDir, "context.json"))
	if _, err := os.Stat(filepath.Join(r.sessionDir, "snapshot-initial.json")); errors.Is(err, os.ErrNotExist) {
		r.copyFile(t, r.snapshot, filepath.Join(r.sessionDir, "snapshot-initial.json"))
	} else if err != nil {
		t.Fatalf("inspect initial snapshot evidence: %v", err)
	}
	// Only a complete, validated observation can become the next entrypoint's checkpoint.
	checkpoint, err := os.CreateTemp(r.sessionDir, ".status-latest-")
	if err != nil {
		t.Fatalf("create session status checkpoint: %v", err)
	}
	defer os.Remove(checkpoint.Name())
	_, writeErr := checkpoint.Write([]byte(status.Stdout))
	if err := errors.Join(writeErr, checkpoint.Close()); err != nil {
		t.Fatalf("write session status checkpoint: %v", err)
	}
	if err := os.Rename(checkpoint.Name(), filepath.Join(r.sessionDir, "status-latest.json")); err != nil {
		t.Fatalf("publish session status checkpoint: %v", err)
	}
}

func (r *ownerSessionRun) latestUpAction() (string, int) {
	name := "up"
	for attempt := 1; ; attempt++ {
		next := fmt.Sprintf("up-resume-%d", attempt)
		if _, exists := r.journal.Actions[next]; !exists {
			return name, attempt
		}
		name = next
	}
}

func (r *ownerSessionRun) runInitialUp(t *testing.T) {
	t.Helper()
	name, attempt := r.latestUpAction()
	command := []string{filepath.Join(r.cp9Root, "bin", "cp9"), "up", "config=" + r.cp9Config}
	if name != "up" {
		original, ok := r.journal.Actions["up"]
		if !ok || original.Status != "completed" || original.ExitCode == 0 {
			t.Fatal("up recovery has no original completed failure")
		}
		r.actionDir(t, "up", ownerSessionUpTimeout, r.cp9Root, command...)
	}
	saved, exists := r.journal.Actions[name]
	if exists && saved.Status == "completed" && saved.ExitCode != 0 &&
		len(r.stageNames) > 0 && r.retryStage == r.stageNames[0] {
		// Validate the immutable prior receipt before creating a separate recovery action.
		r.actionDir(t, name, ownerSessionUpTimeout, r.cp9Root, command...)
		r.activeFailure = "harness"
		result := r.mustDir(t, "pre-resume-up-status", 15*time.Minute, r.cp9Root,
			command[0], "{sys.output.format=json}", "status", "config="+r.cp9Config)
		var state struct {
			SessionName    string `json:"session_name"`
			Status         string `json:"status"`
			LifecycleState string `json:"lifecycle_state"`
			ResumeAt       string `json:"resume_at"`
			Steps          []struct {
				Name   string `json:"name"`
				Status string `json:"status"`
			} `json:"steps"`
		}
		if err := json.Unmarshal([]byte(result.Stdout), &state); err != nil {
			t.Fatalf("parse failed-up recovery status: %v", err)
		}
		if state.SessionName != r.sessionName || state.Status != "failed" ||
			state.LifecycleState != "session_ready" || state.ResumeAt != "session_ready" {
			t.Fatalf("refuse up recovery outside the same failed session_ready step: %+v", state)
		}
		failedStep := false
		for _, step := range state.Steps {
			if step.Name == "session_ready" && step.Status == "failed" {
				failedStep = true
			} else if step.Status != "done" {
				t.Fatalf("refuse up recovery with incomplete lifecycle step %s: %s", step.Name, step.Status)
			}
		}
		if !failedStep {
			t.Fatal("up recovery has no failed session_ready step receipt")
		}
		name = fmt.Sprintf("up-resume-%d", attempt)
	}
	r.activeFailure = "product"
	r.mustActionDir(t, name, ownerSessionUpTimeout, r.cp9Root, command...)
}

func (r *ownerSessionRun) assertSnapshot(t *testing.T, environment string) resolverSnapshot {
	t.Helper()
	r.activeFailure = "harness"
	stage := &resolverStage{}
	defer r.propagateResolverFailure(stage)
	r.commander.fixtureHead = r.fixtureHead
	r.commander.awsHeads = r.awsHeads
	r.commander.loadRegistry(t)
	snapshot := r.commander.loadSnapshot(t, stage, r.snapshot)
	r.activeFailure = "product"
	r.commander.assertSnapshot(t, stage, environment, snapshot, filepath.Dir(r.snapshot))
	return snapshot
}

func (r *ownerSessionRun) assertCurrentOwnerSnapshot(t *testing.T, environment, overrideName, overrideImage string) resolverSnapshot {
	t.Helper()
	r.activeFailure = "harness"
	stage := &resolverStage{}
	defer r.propagateResolverFailure(stage)
	r.commander.fixtureHead = r.fixtureHead
	r.commander.awsHeads = r.awsHeads
	r.commander.loadRegistry(t)
	snapshot := r.commander.loadSnapshot(t, stage, r.snapshot)
	wantDigest, err := resolverSnapshotDigest(snapshot)
	if err != nil {
		t.Fatalf("calculate current snapshot digest: %v", err)
	}
	if snapshot.Digest != wantDigest {
		r.activeFailure = "product"
		t.Fatalf("current snapshot digest=%q, want %q", snapshot.Digest, wantDigest)
	}
	normalized := snapshot
	normalized.Artifacts = append([]resolverSnapshotArtifact(nil), snapshot.Artifacts...)
	for i := range normalized.Artifacts {
		artifact := &normalized.Artifacts[i]
		if artifact.Repo == "tidbcloud/app-delivery" {
			artifact.Commit = r.fixtureHead
		}
	}
	if overrideName != "" {
		actual := findSnapshotArtifact(t, snapshot, overrideName)
		if actual.Image != overrideImage || !actual.ImageOverride {
			r.activeFailure = "product"
			t.Fatalf("%s override = %q/%t; want %q/true", overrideName, actual.Image, actual.ImageOverride, overrideImage)
		}
		locator := r.commander.registryArtifact(t, overrideName).locator(environment)
		r.activeFailure = "harness"
		data, err := r.commander.ownerBytes(t, environment+"-"+overrideName+"-owner", locator)
		if err != nil {
			t.Fatalf("read %s owner image: %v", overrideName, err)
		}
		ownerImage, err := expectedResolverImage(data, locator, r.commander.registry.Environments[environment].Substitutions)
		if err != nil {
			t.Fatalf("derive %s owner image: %v", overrideName, err)
		}
		for i := range normalized.Artifacts {
			if normalized.Artifacts[i].Name == overrideName {
				normalized.Artifacts[i].Image = ownerImage
				normalized.Artifacts[i].ImageOverride = false
				normalized.Artifacts[i].Digest = digestResolverBytes([]byte(ownerImage))
			}
		}
	}
	normalized.Digest, err = resolverSnapshotDigest(normalized)
	if err != nil {
		t.Fatalf("calculate normalized owner snapshot digest: %v", err)
	}
	r.activeFailure = "product"
	r.commander.assertSnapshot(t, stage, environment, normalized, filepath.Dir(r.snapshot))
	return snapshot
}

func (r *ownerSessionRun) propagateResolverFailure(stage *resolverStage) {
	if stage.Harness == "fail" {
		r.activeFailure = "harness"
	} else if stage.Product == "fail" {
		r.activeFailure = "product"
	}
}

func (r *ownerSessionRun) assertRunningWorkloads(t *testing.T, evidenceDir string, snapshot resolverSnapshot) {
	t.Helper()
	r.activeFailure = "harness"
	contractsData, err := os.ReadFile(filepath.Join(r.cp9Root, "internal", "sourcebacked", "contracts.json"))
	if err != nil {
		t.Fatalf("read source-backed contracts: %v", err)
	}
	var contracts ownerContractsFile
	if err := json.Unmarshal(contractsData, &contracts); err != nil {
		t.Fatalf("parse source-backed contracts: %v", err)
	}
	services := map[string]string{}
	for _, artifact := range snapshot.Artifacts {
		if artifact.Kind == "service" {
			services[artifact.Name] = artifact.Image
		}
	}
	targets := make([]ownerRolloutTarget, 0, len(contracts.Contracts)+13)
	for _, contract := range contracts.Contracts {
		if contract.Backend.Kind == "real-eks-data-plane" {
			continue
		}
		if _, ok := services[contract.Name]; !ok {
			continue
		}
		targets = append(targets, ownerRolloutTarget{
			Name: contract.Name, Backend: contract.Backend.Kind,
			Kind:      strings.TrimPrefix(contract.RolloutTarget.Kind, "kubernetes-"),
			Namespace: contract.RolloutTarget.Namespace, Workload: contract.RolloutTarget.Name,
			Container: contract.RolloutTarget.Container,
		})
	}
	targets = append(targets, ownerRuntimeExtras()...)
	if err := os.MkdirAll(evidenceDir, 0o700); err != nil {
		t.Fatalf("create workload evidence directory: %v", err)
	}
	r.activeFailure = "product"
	targets, providerRevisions := r.resolveProviderTargets(t, evidenceDir, targets, services)
	controllers := r.readRuntimeControllers(t, evidenceDir, targets, services, providerRevisions)
	r.assertRuntimePods(t, evidenceDir, targets, services, controllers)
	observed := map[string]bool{}
	for _, target := range targets {
		observed[target.Name] = true
	}
	var names []string
	for name := range observed {
		names = append(names, name)
	}
	sort.Strings(names)
	r.writeBytesAt(t, filepath.Join(evidenceDir, "observed-services.txt"), []byte(strings.Join(names, "\n")+"\n"))
}

func ownerRuntimeExtras() []ownerRolloutTarget {
	return []ownerRolloutTarget{
		{Name: "dedicated-o11y-agent-control-server", Backend: "k3d-control-plane", Kind: "deployment", Namespace: "cp9-dedicated", Workload: "o11y-agent-control-server", Container: "agent-control-server"},
		{Name: "dedicated-upbound-eks", Backend: "k3d-control-plane", Kind: "deployment", Namespace: "cp9-dedicated", Workload: "dedicated-upbound-eks", Container: "provider-aws-eks"},
		{Name: "dedicated-upbound-family", Backend: "k3d-control-plane", Kind: "deployment", Namespace: "cp9-dedicated", Workload: "dedicated-upbound-family", Container: "provider-family-aws"},
		{Name: "nextgen-crossplane", Backend: "k3d-control-plane", Kind: "deployment", Namespace: "crossplane-system", Workload: "nextgen-crossplane", Container: "crossplane"},
		{Name: "nextgen-crossplane", Backend: "k3d-control-plane", Kind: "deployment", Namespace: "crossplane-system", Workload: "nextgen-crossplane-rbac-manager", Container: "crossplane"},
		{Name: "nextgen-metering-server", Backend: "k3d-control-plane", Kind: "statefulset", Namespace: "metering", Workload: "nextgen-metering-server", Container: "nextgen-metering-server"},
		{Name: "premium-o11y-agent-control-server", Backend: "k3d-control-plane", Kind: "deployment", Namespace: "cp9-premium", Workload: "o11y-agent-control-server", Container: "agent-control-server"},
		{Name: "premium-upbound-provider-family-aws", Backend: "crossplane-provider", Kind: "provider", Namespace: "crossplane-system", Workload: "provider-family-aws", Container: "-"},
		{Name: "premium-upbound-provider-aws-iam", Backend: "crossplane-provider", Kind: "provider", Namespace: "crossplane-system", Workload: "provider-aws-iam", Container: "-"},
		{Name: "premium-upbound-provider-aws-route53", Backend: "crossplane-provider", Kind: "provider", Namespace: "crossplane-system", Workload: "provider-aws-route53", Container: "-"},
		{Name: "premium-upbound-provider-aws-ec2", Backend: "crossplane-provider", Kind: "provider", Namespace: "crossplane-system", Workload: "provider-aws-ec2", Container: "-"},
		{Name: "premium-upbound-provider-aws-eks", Backend: "crossplane-provider", Kind: "provider", Namespace: "crossplane-system", Workload: "provider-aws-eks", Container: "-"},
		{Name: "premium-upbound-provider-aws-s3", Backend: "crossplane-provider", Kind: "provider", Namespace: "crossplane-system", Workload: "provider-aws-s3", Container: "-"},
		{Name: "premium-upbound-provider-aws-kms", Backend: "crossplane-provider", Kind: "provider", Namespace: "crossplane-system", Workload: "provider-aws-kms", Container: "-"},
	}
}

func (r *ownerSessionRun) resolveProviderTargets(t *testing.T, evidenceDir string, targets []ownerRolloutTarget, services map[string]string) ([]ownerRolloutTarget, map[string]ownerObject) {
	t.Helper()
	kubectl := r.hostKubectl()
	providersByName := map[string]ownerRolloutTarget{}
	var providerNames []string
	var ordinary []ownerRolloutTarget
	for _, target := range targets {
		if target.Kind == "provider" {
			providersByName[target.Workload] = target
			providerNames = append(providerNames, target.Workload)
		} else {
			ordinary = append(ordinary, target)
		}
	}
	if len(providerNames) == 0 {
		return ordinary, map[string]ownerObject{}
	}
	sort.Strings(providerNames)
	args := append(append([]string{}, kubectl...), "get", "provider")
	args = append(args, providerNames...)
	args = append(args, "-o", "json")
	providers := r.mustObjectList(t, evidenceDir, "providers", args...)
	if len(providers.Items) != len(providerNames) {
		t.Fatalf("observed %d providers; want %d", len(providers.Items), len(providerNames))
	}
	providerByRevision := map[string]ownerRolloutTarget{}
	var revisionNames []string
	for _, provider := range providers.Items {
		target, ok := providersByName[provider.Metadata.Name]
		if !ok {
			t.Fatalf("unexpected provider %s", provider.Metadata.Name)
		}
		expected := services[target.Name]
		if provider.Spec.Package != expected || provider.Status.CurrentIdentifier != expected || !hasOwnerCondition(provider, "Installed") || !hasOwnerCondition(provider, "Healthy") || provider.Status.CurrentRevision == "" {
			t.Fatalf("provider %s is not installed and healthy at %s", target.Name, expected)
		}
		providerByRevision[provider.Status.CurrentRevision] = target
		revisionNames = append(revisionNames, provider.Status.CurrentRevision)
	}
	sort.Strings(revisionNames)
	args = append(append([]string{}, kubectl...), "get", "providerrevision")
	args = append(args, revisionNames...)
	args = append(args, "-o", "json")
	revisions := r.mustObjectList(t, evidenceDir, "provider-revisions", args...)
	if len(revisions.Items) != len(revisionNames) {
		t.Fatalf("observed %d provider revisions; want %d", len(revisions.Items), len(revisionNames))
	}
	byRuntime := map[string]ownerObject{}
	for _, revision := range revisions.Items {
		target, ok := providerByRevision[revision.Metadata.Name]
		if !ok {
			t.Fatalf("unexpected provider revision %s", revision.Metadata.Name)
		}
		expected := services[target.Name]
		if revision.Spec.Image != expected || revision.Spec.DesiredState != "Active" || !hasOwnerCondition(revision, "Healthy") {
			t.Fatalf("provider revision %s is not active and healthy at %s", revision.Metadata.Name, expected)
		}
		byRuntime[revision.Metadata.Name] = revision
		ordinary = append(ordinary, ownerRolloutTarget{Name: target.Name, Backend: target.Backend, Kind: "deployment", Namespace: target.Namespace, Workload: revision.Metadata.Name, Container: "package-runtime"})
	}
	return ordinary, byRuntime
}

func (r *ownerSessionRun) readRuntimeControllers(t *testing.T, evidenceDir string, targets []ownerRolloutTarget, services map[string]string, providerRevisions map[string]ownerObject) map[string]ownerObject {
	t.Helper()
	groups := map[string][]ownerRolloutTarget{}
	for _, target := range targets {
		key := target.Namespace + "\x00" + target.Kind
		groups[key] = append(groups[key], target)
	}
	var groupNames []string
	for group := range groups {
		groupNames = append(groupNames, group)
	}
	sort.Strings(groupNames)
	controllers := map[string]ownerObject{}
	for _, group := range groupNames {
		selected := groups[group]
		sort.Slice(selected, func(i, j int) bool { return selected[i].Workload < selected[j].Workload })
		args := append(append([]string{}, r.hostKubectl()...), "-n", selected[0].Namespace, "get")
		for _, target := range selected {
			args = append(args, target.Kind+"/"+target.Workload)
		}
		args = append(args, "-o", "json")
		objects := r.mustObjectList(t, evidenceDir, "workloads-"+sanitizeResolverLabel(group), args...)
		byName := map[string]ownerObject{}
		for _, object := range objects.Items {
			byName[object.Metadata.Name] = object
		}
		for _, target := range selected {
			controller, ok := byName[target.Workload]
			if !ok {
				t.Fatalf("missing live %s/%s", target.Kind, target.Workload)
			}
			if revision, ok := providerRevisions[target.Workload]; ok && !ownerRevisionOwnsRuntime(revision, controller) {
				t.Fatalf("provider revision %s does not own runtime deployment %s", revision.Metadata.Name, controller.Metadata.Name)
			}
			expected := services[target.Name]
			if target.Container != "-" && !ownerContainerImage(controller.Spec.Template.Spec.Containers, target.Container, expected) {
				t.Fatalf("%s/%s does not select %s at %s", target.Kind, target.Workload, target.Container, expected)
			}
			if !ownerRolloutReady(controller, target.Kind) {
				t.Fatalf("%s/%s is not fully Ready", target.Kind, target.Workload)
			}
			if len(controller.Spec.Selector.MatchExpressions) != 0 || len(controller.Spec.Selector.MatchLabels) == 0 {
				r.activeFailure = "harness"
				t.Fatalf("%s/%s has unsupported selector", target.Kind, target.Workload)
			}
			controllers[ownerTargetKey(target)] = controller
		}
	}
	return controllers
}

func (r *ownerSessionRun) assertRuntimePods(t *testing.T, evidenceDir string, targets []ownerRolloutTarget, services map[string]string, controllers map[string]ownerObject) {
	t.Helper()
	namespaces := map[string]bool{}
	for _, target := range targets {
		namespaces[target.Namespace] = true
	}
	var ordered []string
	for namespace := range namespaces {
		ordered = append(ordered, namespace)
	}
	sort.Strings(ordered)
	for _, namespace := range ordered {
		args := append(append([]string{}, r.hostKubectl()...), "-n", namespace, "get", "pods", "-o", "json")
		pods := r.mustObjectList(t, evidenceDir, "pods-"+sanitizeResolverLabel(namespace), args...)
		for _, target := range targets {
			if target.Namespace != namespace {
				continue
			}
			controller := controllers[ownerTargetKey(target)]
			active := 0
			for _, pod := range pods.Items {
				if pod.Metadata.DeletionTimestamp != nil || !ownerLabelsMatch(pod.Metadata.Labels, controller.Spec.Selector.MatchLabels) {
					continue
				}
				active++
				if pod.Status.Phase != "Running" || !ownerPodContainerReady(pod.Status.ContainerStatuses, target.Container) {
					t.Fatalf("pod %s for %s is not Ready", pod.Metadata.Name, target.Name)
				}
				if target.Container != "-" && !ownerContainerImage(pod.Spec.Containers, target.Container, services[target.Name]) {
					t.Fatalf("pod %s for %s is not using %s", pod.Metadata.Name, target.Name, services[target.Name])
				}
			}
			if active == 0 {
				t.Fatalf("%s has no active Pods", target.Name)
			}
		}
	}
}

func (r *ownerSessionRun) mustObjectList(t *testing.T, evidenceDir, label string, argv ...string) ownerObjectList {
	t.Helper()
	r.activeFailure = "harness"
	result := r.must(t, label, ownerSessionCommandTimeout, argv...)
	r.writeBytesAt(t, filepath.Join(evidenceDir, label+".json"), []byte(result.Stdout))
	var list ownerObjectList
	r.activeFailure = "harness"
	if err := json.Unmarshal([]byte(result.Stdout), &list); err != nil {
		t.Fatalf("parse %s: %v", label, err)
	}
	if list.Items == nil {
		var object ownerObject
		if err := json.Unmarshal([]byte(result.Stdout), &object); err != nil || object.Metadata.Name == "" {
			t.Fatalf("parse %s as object: %v", label, err)
		}
		list.Items = []ownerObject{object}
	}
	r.activeFailure = "product"
	return list
}

func (r *ownerSessionRun) assertLiveCRDs(t *testing.T, evidenceDir string, snapshot resolverSnapshot) {
	t.Helper()
	r.activeFailure = "harness"
	var sessionContext ownerSessionContext
	r.readJSON(t, r.contextPath, &sessionContext)
	for _, artifact := range snapshot.Artifacts {
		if artifact.Kind != "crd" || artifact.Name == "dedicated-crossplane-gcp-crds" {
			continue
		}
		kubectl := r.hostKubectl()
		if strings.HasPrefix(artifact.Name, "dedicated-") {
			kubectl = r.dedicatedKubectl(sessionContext.Host.DedicatedAPI.RemoteAdminKubeconfigPath)
		}
		args := append([]string{}, kubectl...)
		args = append(args, "get", "customresourcedefinition")
		args = append(args, artifact.CRDNames...)
		args = append(args, "-o", "json")
		list := r.mustObjectList(t, evidenceDir, "crds-"+artifact.Name, args...)
		got := make([]string, 0, len(list.Items))
		for _, item := range list.Items {
			got = append(got, item.Metadata.Name)
		}
		sort.Strings(got)
		want := append([]string(nil), artifact.CRDNames...)
		sort.Strings(want)
		if !reflect.DeepEqual(got, want) {
			r.activeFailure = "product"
			t.Fatalf("live CRDs for %s = %v; want %v", artifact.Name, got, want)
		}
		var manifest bytes.Buffer
		r.activeFailure = "harness"
		root := filepath.Join(filepath.Dir(r.snapshot), artifact.LocalPath)
		for _, name := range artifact.Files {
			data, err := os.ReadFile(filepath.Join(root, filepath.FromSlash(name)))
			if err != nil {
				t.Fatalf("read %s CRD file %s: %v", artifact.Name, name, err)
			}
			manifest.Write(data)
			manifest.WriteString("\n---\n")
		}
		r.activeFailure = "harness"
		diff := r.runInput(t, "crd-diff-"+artifact.Name, ownerSessionCommandTimeout, manifest.Bytes(), append(kubectl, "diff", "--server-side", "--field-manager=kubectl", "-f", "-")...)
		r.writeBytesAt(t, filepath.Join(evidenceDir, "crd-diff-"+artifact.Name+".txt"), []byte(diff.Stdout))
		if diff.ExitCode != 0 && diff.ExitCode != 1 {
			t.Fatalf("server-side CRD diff probe for %s failed: exit=%d stderr=%q", artifact.Name, diff.ExitCode, diff.Stderr)
		}
		if diff.ExitCode == 1 || strings.TrimSpace(diff.Stdout) != "" {
			r.activeFailure = "product"
			t.Fatalf("server-side CRD diff for %s: exit=%d output=%q stderr=%q", artifact.Name, diff.ExitCode, diff.Stdout, diff.Stderr)
		}
	}
}

func (r *ownerSessionRun) cleanup(t *testing.T) {
	t.Helper()
	if r.cleaned || r.journal.Cleanup == "pass" {
		return
	}
	metadataPath := filepath.Join(r.sessionDir, "default-804-session.json")
	if _, err := os.Stat(metadataPath); err == nil {
		if previous := r.latestCleanupActionLabel(); previous != "" && !r.cleanupAttemptArchived(previous) {
			r.captureCleanupAttempt(t, previous)
			if !r.cleanupAttemptArchived(previous) {
				t.Fatalf("cleanup evidence from %s is incomplete", previous)
			}
		}
		actionLabel := r.cleanupActionLabel()
		result := r.actionDir(t, actionLabel, ownerSessionUpTimeout, r.repoRoot,
			filepath.Join(r.repoRoot, "spec-tests", "scripts", "default-804-session.sh"), "cleanup", r.sessionDir)
		r.captureCleanupAttempt(t, actionLabel)
		if result.ExitCode != 0 {
			r.journal.Cleanup = "fail"
			_ = r.persistJournal()
			t.Fatalf("exact-session cleanup failed: %s", result.Stderr)
		}
		destroyed, worktree, handoff, err := r.aggregateCleanupAttempts()
		if err != nil {
			r.journal.Cleanup = "fail"
			_ = r.persistJournal()
			t.Fatalf("read cleanup attempts: %v", err)
		}
		if !destroyed || !worktree || !handoff {
			r.journal.Cleanup = "fail"
			_ = r.persistJournal()
			t.Fatalf("cleanup attempts are incomplete: destroy=%t worktree=%t handoff=%t", destroyed, worktree, handoff)
		}
	} else if !errors.Is(err, os.ErrNotExist) {
		t.Fatalf("inspect session metadata before cleanup: %v", err)
	} else if _, started := r.journal.Actions["prepare-session"]; started {
		t.Fatal("prepare-session has no metadata; exact cleanup target is unknown")
	}
	for _, path := range []string{r.ownerWork, r.ownerBare, r.awsBare, r.gitConfig} {
		if _, err := os.Lstat(path); errors.Is(err, os.ErrNotExist) {
			continue
		} else if err != nil {
			t.Fatalf("inspect run-owned fixture %s: %v", path, err)
		}
		if err := removeResolverRunPath(r.root, path); err != nil {
			t.Fatalf("remove run-owned fixture %s: %v", path, err)
		}
	}
	r.cleaned = true
	r.journal.Cleanup = "pass"
	if err := r.persistJournal(); err != nil {
		t.Fatalf("record passing cleanup: %v", err)
	}
}

func (r *ownerSessionRun) cleanupRequested() bool {
	if os.Getenv(r.destroyEnv) == "1" {
		return true
	}
	for name := range r.journal.Actions {
		if name == "cleanup-session" || strings.HasPrefix(name, "cleanup-session-") {
			return true
		}
	}
	return false
}

func (r *ownerSessionRun) latestCleanupActionLabel() string {
	const base = "cleanup-session"
	latest := ""
	for attempt := 1; ; attempt++ {
		name := base
		if attempt > 1 {
			name = fmt.Sprintf("%s-%d", base, attempt)
		}
		action, ok := r.journal.Actions[name]
		if !ok {
			return latest
		}
		if action.Status != "completed" {
			return ""
		}
		latest = name
		if action.ExitCode == 0 {
			return latest
		}
	}
}

func (r *ownerSessionRun) cleanupAttemptArchived(action string) bool {
	directory := filepath.Join(r.sessionDir, "cleanup-attempts", action)
	data, err := os.ReadFile(filepath.Join(directory, "cleanup-receipt.json"))
	if err != nil {
		return false
	}
	var receipt struct {
		Destroy string `json:"destroy"`
	}
	if json.Unmarshal(data, &receipt) != nil {
		return false
	}
	if receipt.Destroy != "pass" {
		return true
	}
	log, err := os.ReadFile(filepath.Join(directory, "destroy.log"))
	return err == nil && bytes.Contains(log, []byte("cleanup_workflow_run_id="))
}

func (r *ownerSessionRun) cleanupActionLabel() string {
	const base = "cleanup-session"
	for attempt := 1; ; attempt++ {
		name := base
		if attempt > 1 {
			name = fmt.Sprintf("%s-%d", base, attempt)
		}
		action, ok := r.journal.Actions[name]
		if !ok || action.Status != "completed" || action.ExitCode == 0 {
			return name
		}
	}
}

func (r *ownerSessionRun) captureCleanupAttempt(t *testing.T, actionLabel string) {
	t.Helper()
	for _, name := range []string{"cleanup-receipt.json", "destroy.log", "remove-worktree.log", "remote-tilt-up.log", "remote-tilt-up.stderr"} {
		data, err := os.ReadFile(filepath.Join(r.sessionDir, name))
		if errors.Is(err, os.ErrNotExist) {
			continue
		}
		if err != nil {
			t.Fatalf("preserve %s from %s: %v", name, actionLabel, err)
		}
		r.writeBytesAt(t, filepath.Join(r.sessionDir, "cleanup-attempts", actionLabel, name), data)
	}
}

func (r *ownerSessionRun) aggregateCleanupAttempts() (bool, bool, bool, error) {
	entries, err := os.ReadDir(filepath.Join(r.sessionDir, "cleanup-attempts"))
	if err != nil {
		return false, false, false, err
	}
	_, upStarted := r.journal.Actions["up"]
	destroyed, worktree, handoff := false, false, !upStarted
	for _, entry := range entries {
		if !entry.IsDir() {
			continue
		}
		directory := filepath.Join(r.sessionDir, "cleanup-attempts", entry.Name())
		data, err := os.ReadFile(filepath.Join(directory, "cleanup-receipt.json"))
		if errors.Is(err, os.ErrNotExist) {
			continue
		}
		if err != nil {
			return false, false, false, err
		}
		var receipt struct {
			Destroy  string `json:"destroy"`
			Worktree string `json:"worktree"`
		}
		if err := json.Unmarshal(data, &receipt); err != nil {
			return false, false, false, err
		}
		if receipt.Destroy == "pass" || receipt.Destroy == "not_needed" && !upStarted {
			destroyed = true
		}
		if receipt.Worktree == "pass" || receipt.Worktree == "not_needed" {
			worktree = true
		}
		if receipt.Destroy == "pass" {
			log, err := os.ReadFile(filepath.Join(directory, "destroy.log"))
			if err == nil && bytes.Contains(log, []byte("cleanup_workflow_run_id=")) {
				handoff = true
			}
		}
	}
	return destroyed, worktree, handoff, nil
}

func (r *ownerSessionRun) hasOwnedState() bool {
	if len(r.journal.Actions) > 0 {
		return true
	}
	for _, path := range []string{r.sessionDir, r.ownerWork, r.ownerBare, r.awsBare, r.gitConfig} {
		if _, err := os.Lstat(path); err == nil {
			return true
		}
	}
	return false
}

func (r *ownerSessionRun) writeSummary(t *testing.T) {
	t.Helper()
	product, harness := "pass", "pass"
	for _, name := range r.stageNames {
		productStage := ownerSessionAttemptVerdict(r.journal.Stages[name], true)
		harnessStage := ownerSessionAttemptVerdict(r.journal.Stages[name], false)
		if productStage == "fail" {
			product = "fail"
		} else if productStage != "pass" && product == "pass" {
			product = "not_run"
		}
		if harnessStage == "fail" {
			harness = "fail"
		} else if harnessStage != "pass" && harness == "pass" {
			harness = "not_run"
		}
	}
	var body strings.Builder
	fmt.Fprintf(&body, "# Owner artifact session\n\n- Case: %s\n- Product CP9 head: %s\n- Current harness head: %s\n", r.caseName, r.journal.CP9Head, r.currentHead)
	fmt.Fprintf(&body, "- Product verdict: %s\n- Harness verdict: %s\n- Cleanup verdict: %s\n- Session: %s\n", product, harness, r.journal.Cleanup, r.journal.Session)
	body.WriteString("\n| Go subtest | Product | Harness | Status |\n| --- | --- | --- | --- |\n")
	for _, name := range r.stageNames {
		receipts := r.journal.Stages[name]
		if len(receipts) == 0 {
			fmt.Fprintf(&body, "| %s | not_run | not_run | not_run |\n", name)
			continue
		}
		for index := range receipts {
			receipt := &receipts[index]
			label := name
			if len(receipts) > 1 {
				label = fmt.Sprintf("%s attempt %d", name, index+1)
			}
			fmt.Fprintf(&body, "| %s | %s | %s | %s |\n", label, receipt.Product, receipt.Harness, receipt.Status)
		}
	}
	r.writeText(t, "summary.md", body.String())
}

func ownerSessionAttemptVerdict(receipts []ownerSessionStageReceipt, product bool) string {
	verdict := "not_run"
	for index := range receipts {
		receipt := &receipts[index]
		value := receipt.Harness
		if product {
			value = receipt.Product
		}
		if value == "fail" {
			return "fail"
		}
		if value == "pass" {
			verdict = "pass"
		}
	}
	return verdict
}

func (r *ownerSessionRun) finalize(t *testing.T) {
	t.Helper()
	if r.journal.Cleanup != "pass" {
		for _, path := range []string{r.sessionDir, r.ownerWork, r.ownerBare, r.awsBare, r.gitConfig} {
			if _, err := os.Lstat(path); err == nil {
				r.journal.Cleanup = "fail"
				_ = r.persistJournal()
				r.writeText(t, "scene-preserved.txt", "Scene preserved after first red; cleanup is pending.")
				break
			}
		}
	}
	r.writeSummary(t)
}

func (r *ownerSessionRun) runStage(t *testing.T, name string, replaySafe bool, body func(*testing.T)) {
	t.Helper()
	receipts := r.journal.Stages[name]
	if len(receipts) > 0 {
		latest := receipts[len(receipts)-1]
		if latest.Status == "passed" {
			t.Skipf("reuse passing %s receipt from head %s", name, latest.Head)
		}
		if latest.Status != "blocked" && latest.Status != "not_run" && r.retryStage != name {
			t.Fatalf("stage %s is %s; set %s=%s only after same-scene diagnosis establishes a safe continuation", name, latest.Status, r.retryStageEnv, name)
		}
		if !replaySafe {
			t.Fatalf("stage %s cannot continue because a product action has unknown outcome", name)
		}
	}
	r.activeFailure = "harness"
	r.journal.Stages[name] = append(receipts, ownerSessionStageReceipt{
		Head: r.currentHead, StartedAt: time.Now().UTC().Format(time.RFC3339Nano),
		Status: "running", Product: "not_run", Harness: "not_run",
	})
	stage := &r.journal.Stages[name][len(r.journal.Stages[name])-1]
	t.Cleanup(func() {
		stage.FinishedAt = time.Now().UTC().Format(time.RFC3339Nano)
		if t.Failed() {
			stage.Status = "failed"
			switch r.activeFailure {
			case "product":
				stage.Product, stage.Harness = "fail", "pass"
			case "blocked":
				stage.Status, stage.Product, stage.Harness = "blocked", "not_run", "pass"
			case "prerequisite":
				stage.Product, stage.Harness = "not_run", "not_run"
			default:
				stage.Product, stage.Harness = "not_run", "fail"
			}
		} else if t.Skipped() {
			stage.Status, stage.Product = "blocked", "not_run"
			if r.activeFailure == "harness" {
				stage.Harness = "fail"
			} else {
				stage.Harness = "pass"
			}
		} else {
			stage.Status, stage.Product, stage.Harness = "passed", "pass", "pass"
		}
		if err := r.persistJournal(); err != nil {
			stage.Status, stage.Harness = "failed", "fail"
			retryErr := r.persistJournal()
			t.Errorf("finish owner-session stage %s: %v", name, errors.Join(err, retryErr))
		}
	})
	if err := r.persistJournal(); err != nil {
		t.Fatalf("begin owner-session stage %s: %v", name, err)
	}
	body(t)
}

func (r *ownerSessionRun) stagesPassed(names ...string) bool {
	for _, name := range names {
		receipts := r.journal.Stages[name]
		if len(receipts) == 0 || receipts[len(receipts)-1].Status != "passed" {
			return false
		}
	}
	return true
}

func (r *ownerSessionRun) hostKubectl() []string {
	return []string{filepath.Join(r.cp9Root, "skills", "get-started", "scripts", "session-kubectl.sh"), r.sessionName}
}

func (r *ownerSessionRun) dedicatedKubectl(kubeconfig string) []string {
	return []string{filepath.Join(r.cp9Root, "skills", "get-started", "scripts", "session-ssh.sh"), r.sessionName, "kubectl", "--kubeconfig", kubeconfig, "--context", "cp9-dedicated"}
}

func (r *ownerSessionRun) kubectlObject(t *testing.T, evidenceDir, label string, prefix []string, args ...string) ownerObject {
	t.Helper()
	r.activeFailure = "harness"
	result := r.must(t, label, ownerSessionCommandTimeout, append(prefix, args...)...)
	r.writeBytesAt(t, filepath.Join(evidenceDir, label+".json"), []byte(result.Stdout))
	var object ownerObject
	if err := json.Unmarshal([]byte(result.Stdout), &object); err != nil {
		t.Fatalf("parse %s: %v", label, err)
	}
	r.activeFailure = "product"
	return object
}

func (r *ownerSessionRun) preflight(t *testing.T, environment string, live bool) bool {
	return r.preflightState(t, environment, "", "", live)
}

func (r *ownerSessionRun) preflightState(t *testing.T, environment, overrideName, overrideImage string, live bool) bool {
	t.Helper()
	defer func() {
		if r.activeFailure == "product" {
			r.activeFailure = "blocked"
		}
	}()
	if r.cp9Root == "" || r.snapshot == "" {
		r.activeFailure = "harness"
		t.Log("phase blocked: session metadata is incomplete")
		return false
	}
	result := r.runDir(t, "preflight-status", 15*time.Minute, r.cp9Root, filepath.Join(r.cp9Root, "bin", "cp9"), "{sys.output.format=json}", "status", "config="+r.cp9Config)
	if result.ExitCode != 0 {
		r.activeFailure = "harness"
		t.Logf("phase blocked: session status is unobservable: %s", result.Stderr)
		return false
	}
	var state struct {
		SessionName string `json:"session_name"`
		Status      string `json:"status"`
	}
	if err := json.Unmarshal([]byte(result.Stdout), &state); err != nil {
		r.activeFailure = "harness"
		t.Logf("phase blocked: parse session status: %v", err)
		return false
	}
	if state.SessionName != r.sessionName || state.Status != "ready" {
		r.activeFailure = "blocked"
		t.Logf("phase blocked: session is not Ready")
		return false
	}
	snapshot := r.assertCurrentOwnerSnapshot(t, environment, overrideName, overrideImage)
	if live {
		evidence := filepath.Join(r.root, "preflight-"+strconv.FormatInt(time.Now().UnixNano(), 10))
		r.assertRunningWorkloads(t, evidence, snapshot)
		r.assertLiveCRDs(t, evidence, snapshot)
	}
	r.activeFailure = "harness"
	return true
}

func (r *ownerSessionRun) refresh(t *testing.T, label string, args ...string) resolverCommandResult {
	t.Helper()
	command := []string{filepath.Join(r.cp9Root, "bin", "cp9"), "artifacts.refresh"}
	command = append(command, args...)
	command = append(command, "config="+r.cp9Config)
	return r.actionDir(t, label, ownerSessionUpTimeout, r.cp9Root, command...)
}

func (r *ownerSessionRun) must(t *testing.T, label string, timeout time.Duration, argv ...string) resolverCommandResult {
	t.Helper()
	result := r.run(t, label, timeout, argv...)
	if result.ExitCode != 0 {
		t.Fatalf("%s failed with exit %d: %s", label, result.ExitCode, result.Stderr)
	}
	return result
}

func (r *ownerSessionRun) mustDir(t *testing.T, label string, timeout time.Duration, directory string, argv ...string) resolverCommandResult {
	t.Helper()
	result := r.runDir(t, label, timeout, directory, argv...)
	if result.ExitCode != 0 {
		t.Fatalf("%s failed with exit %d: %s", label, result.ExitCode, result.Stderr)
	}
	return result
}

func (r *ownerSessionRun) mustActionDir(t *testing.T, label string, timeout time.Duration, directory string, argv ...string) resolverCommandResult {
	t.Helper()
	result := r.actionDir(t, label, timeout, directory, argv...)
	if result.ExitCode != 0 {
		t.Fatalf("%s failed with exit %d: %s", label, result.ExitCode, result.Stderr)
	}
	return result
}

func (r *ownerSessionRun) actionDir(t *testing.T, label string, timeout time.Duration, directory string, argv ...string) resolverCommandResult {
	t.Helper()
	failure := r.activeFailure
	r.activeFailure = "harness"
	if saved, ok := r.journal.Actions[label]; ok {
		if saved.Head != r.journal.CP9Head || saved.Directory != directory || !reflect.DeepEqual(saved.Command, argv) {
			t.Fatalf("action %s does not match the preserved receipt", label)
		}
		if saved.Status == "started" {
			t.Fatalf("action %s has unknown outcome; refusing to replay", label)
		}
		if saved.Status != "completed" || saved.Stdout == "" || saved.Stderr == "" {
			t.Fatalf("action %s has an incomplete receipt", label)
		}
		stdout := filepath.Join(r.root, filepath.FromSlash(saved.Stdout))
		stderr := filepath.Join(r.root, filepath.FromSlash(saved.Stderr))
		if !ownerSessionChildPath(r.root, stdout) || !ownerSessionChildPath(r.root, stderr) {
			t.Fatalf("action %s output escapes the evidence directory", label)
		}
		if _, err := os.Stat(stdout); err != nil {
			t.Fatalf("action %s stdout is unavailable: %v", label, err)
		}
		if _, err := os.Stat(stderr); err != nil {
			t.Fatalf("action %s stderr is unavailable: %v", label, err)
		}
		result := r.readCommandOutput(t, label, resolverCommandResult{ExitCode: saved.ExitCode, Stdout: stdout, Stderr: stderr, Started: true, Recorded: true})
		r.activeFailure = failure
		return result
	}
	r.journal.Actions[label] = ownerSessionActionReceipt{
		Head: r.journal.CP9Head, Directory: directory, Command: append([]string(nil), argv...),
		StartedAt: time.Now().UTC().Format(time.RFC3339Nano), Status: "started", ExitCode: -1,
	}
	r.journal.Cleanup = "fail"
	if err := r.persistJournal(); err != nil {
		t.Fatalf("record start of action %s: %v", label, err)
	}
	previous := r.commander.repoRoot
	r.commander.repoRoot = directory
	result, commandErr := r.commander.command(label, timeout, argv...)
	r.commander.repoRoot = previous
	if !result.Started {
		delete(r.journal.Actions, label)
		if err := r.persistJournal(); err != nil {
			t.Fatalf("record that action %s did not start: %v", label, errors.Join(commandErr, err))
		}
		t.Fatalf("start action %s: %v", label, commandErr)
	}
	if !result.Recorded {
		t.Fatalf("record action %s command result: %v", label, commandErr)
	}
	stdout, stdoutErr := filepath.Rel(r.root, result.Stdout)
	stderr, stderrErr := filepath.Rel(r.root, result.Stderr)
	if stdoutErr != nil || stderrErr != nil || !ownerSessionChildPath(r.root, result.Stdout) || !ownerSessionChildPath(r.root, result.Stderr) {
		t.Fatalf("record action %s output paths: %v", label, errors.Join(stdoutErr, stderrErr))
	}
	action := r.journal.Actions[label]
	action.Status = "completed"
	action.ExitCode = result.ExitCode
	action.FinishedAt = time.Now().UTC().Format(time.RFC3339Nano)
	action.Stdout = filepath.ToSlash(stdout)
	action.Stderr = filepath.ToSlash(stderr)
	r.journal.Actions[label] = action
	if err := r.persistJournal(); err != nil {
		t.Fatalf("record completion of action %s: %v", label, err)
	}
	result = r.readCommandOutput(t, label, result)
	r.activeFailure = failure
	return result
}

func (r *ownerSessionRun) run(t *testing.T, label string, timeout time.Duration, argv ...string) resolverCommandResult {
	t.Helper()
	result, err := r.commander.command(label, timeout, argv...)
	if err != nil || !result.Started || !result.Recorded {
		r.activeFailure = "harness"
		t.Fatalf("capture %s command: started=%t recorded=%t error=%v", label, result.Started, result.Recorded, err)
	}
	return r.readCommandOutput(t, label, result)
}

func (r *ownerSessionRun) runDir(t *testing.T, label string, timeout time.Duration, directory string, argv ...string) resolverCommandResult {
	t.Helper()
	previous := r.commander.repoRoot
	r.commander.repoRoot = directory
	defer func() { r.commander.repoRoot = previous }()
	return r.run(t, label, timeout, argv...)
}

func (r *ownerSessionRun) runInput(t *testing.T, label string, timeout time.Duration, input []byte, argv ...string) resolverCommandResult {
	t.Helper()
	result, err := r.commander.commandWithInput(label, timeout, bytes.NewReader(input), argv...)
	if err != nil || !result.Started || !result.Recorded {
		r.activeFailure = "harness"
		t.Fatalf("capture %s command: started=%t recorded=%t error=%v", label, result.Started, result.Recorded, err)
	}
	return r.readCommandOutput(t, label, result)
}

func (r *ownerSessionRun) readCommandOutput(t *testing.T, label string, result resolverCommandResult) resolverCommandResult {
	t.Helper()
	failure := r.activeFailure
	r.activeFailure = "harness"
	stdout, stdoutErr := os.ReadFile(result.Stdout)
	stderr, stderrErr := os.ReadFile(result.Stderr)
	if stdoutErr != nil || stderrErr != nil {
		t.Fatalf("read %s command output: %v", label, errors.Join(stdoutErr, stderrErr))
	}
	r.activeFailure = failure
	result.Stdout, result.Stderr = string(stdout), string(stderr)
	return result
}

func (r *ownerSessionRun) gitOutput(t *testing.T, label, directory string, args ...string) string {
	t.Helper()
	command := append([]string{"git", "-C", directory}, args...)
	return strings.TrimSpace(r.must(t, label, 5*time.Minute, command...).Stdout)
}

func (r *ownerSessionRun) readJSON(t *testing.T, path string, out any) {
	t.Helper()
	data, err := os.ReadFile(path)
	if err != nil {
		r.activeFailure = "harness"
		t.Fatalf("read %s: %v", path, err)
	}
	if err := json.Unmarshal(data, out); err != nil {
		r.activeFailure = "harness"
		t.Fatalf("parse %s: %v", path, err)
	}
}

func (r *ownerSessionRun) copyFile(t *testing.T, source, target string) {
	t.Helper()
	data, err := os.ReadFile(source)
	if err != nil {
		r.activeFailure = "harness"
		t.Fatalf("read %s: %v", source, err)
	}
	r.writeBytesAt(t, target, data)
}

func (r *ownerSessionRun) writeText(t *testing.T, relative, value string) {
	t.Helper()
	r.writeBytesAt(t, filepath.Join(r.root, relative), []byte(strings.TrimSpace(value)+"\n"))
}

func (r *ownerSessionRun) writeBytesAt(t *testing.T, path string, data []byte) {
	t.Helper()
	if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil {
		r.activeFailure = "harness"
		t.Fatalf("create parent for %s: %v", path, err)
	}
	if err := os.WriteFile(path, data, 0o600); err != nil {
		r.activeFailure = "harness"
		t.Fatalf("write %s: %v", path, err)
	}
}

func findSnapshotArtifact(t *testing.T, snapshot resolverSnapshot, name string) resolverSnapshotArtifact {
	t.Helper()
	for _, artifact := range snapshot.Artifacts {
		if artifact.Name == name {
			return artifact
		}
	}
	t.Fatalf("snapshot has no %s artifact", name)
	return resolverSnapshotArtifact{}
}

func hasOwnerCondition(object ownerObject, condition string) bool {
	for _, item := range object.Status.Conditions {
		if item.Type == condition && item.Status == "True" {
			return true
		}
	}
	return false
}

func ownerRevisionOwnsRuntime(revision, deployment ownerObject) bool {
	for _, owner := range deployment.Metadata.OwnerReferences {
		if owner.APIVersion == "pkg.crossplane.io/v1" && owner.Kind == "ProviderRevision" &&
			owner.Name == revision.Metadata.Name && owner.UID == revision.Metadata.UID && owner.Controller {
			return true
		}
	}
	return false
}

func ownerTargetKey(target ownerRolloutTarget) string {
	return target.Namespace + "\x00" + target.Kind + "\x00" + target.Workload
}

func ownerLabelsMatch(actual, selector map[string]string) bool {
	for key, value := range selector {
		if actual[key] != value {
			return false
		}
	}
	return true
}

func ownerContainerImage(containers []ownerContainer, name, image string) bool {
	for _, container := range containers {
		if container.Name == name && container.Image == image {
			return true
		}
	}
	return false
}

func ownerPodContainerReady(statuses []ownerContainerStatus, name string) bool {
	if len(statuses) == 0 {
		return false
	}
	if name == "-" {
		for _, status := range statuses {
			if !status.Ready {
				return false
			}
		}
		return true
	}
	for _, status := range statuses {
		if status.Name == name && status.Ready {
			return true
		}
	}
	return false
}

func ownerRolloutReady(object ownerObject, kind string) bool {
	want := int64(1)
	if object.Spec.Replicas != nil {
		want = *object.Spec.Replicas
	}
	if object.Status.ObservedGeneration < object.Metadata.Generation {
		return false
	}
	switch kind {
	case "deployment":
		return object.Status.UpdatedReplicas == want && object.Status.ReadyReplicas == want && object.Status.AvailableReplicas == want
	case "statefulset":
		return object.Status.CurrentRevision == object.Status.UpdateRevision && object.Status.UpdatedReplicas == want && object.Status.ReadyReplicas == want
	case "daemonset":
		return object.Status.DesiredNumberScheduled > 0 && object.Status.UpdatedNumberScheduled == object.Status.DesiredNumberScheduled && object.Status.NumberReady == object.Status.DesiredNumberScheduled
	default:
		return false
	}
}
