//go:build functional

package spectests_test

import (
	"bytes"
	"context"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/fs"
	"os"
	"os/exec"
	"path/filepath"
	"reflect"
	"regexp"
	"slices"
	"strconv"
	"strings"
	"syscall"
	"testing"
	"time"

	"gopkg.in/yaml.v3"
)

const (
	ownerArtifactResolutionEnabled = "CP9_OWNER_ARTIFACT_RESOLUTION_ENABLED"
	resolverConfigTag              = "044bbf126f08579c07f98060ac09b6c44e9c15bd"
	resolverDataflowImage          = "example.invalid/cp9-spec/dataflow:full-shape"
	resolverDataplaneImage         = "TPML_IMAGE_REGISTRY/tidbcloud/dataplane-manager:token-proof"
)

var (
	resolverFormatToken = regexp.MustCompile(`\$\{([1-9]\d*)\}`)
	resolverPlaceholder = regexp.MustCompile(`\b(?:TPML|TMPL|IMAGE)_[A-Z0-9_]+\b`)
)

type resolverRun struct {
	t            *testing.T
	repoRoot     string
	root         string
	ownerWork    string
	ownerBare    string
	awsBare      string
	gitConfig    string
	commandEnv   map[string]string
	commandID    int
	prerequisite string
	harnessFail  bool
	cleanup      string
	stages       map[string]*resolverStage
	registry     resolverRegistry
	registryRaw  []byte
	files        resolverFixtureFiles
	original     map[string][]byte
	valid        map[string][]byte
	fixtureHead  string
	awsHeads     map[string]string
	failure      *string
}

type resolverStage struct {
	Product string
	Harness string
	Skipped bool
}

type resolverFixtureFiles struct {
	Config    string
	CRD       string
	Dataflow  string
	Dataplane string
	CRDGroup  string
}

type resolverRegistry struct {
	Environments map[string]resolverEnvironment `yaml:"environments"`
	Artifacts    []resolverRegistryArtifact     `yaml:"artifacts"`
	Blocked      []any                          `yaml:"blocked"`
}

type resolverEnvironment struct {
	Substitutions map[string]string `yaml:"substitutions"`
}

type resolverRegistryArtifact struct {
	Name    string          `yaml:"name"`
	Kind    string          `yaml:"kind"`
	Dev     resolverLocator `yaml:"dev"`
	Staging resolverLocator `yaml:"staging"`
}

type resolverLocator struct {
	Repo      string   `yaml:"repo"`
	Ref       string   `yaml:"ref"`
	File      string   `yaml:"file,omitempty"`
	Regex     string   `yaml:"regex,omitempty"`
	Format    string   `yaml:"format,omitempty"`
	Directory string   `yaml:"directory,omitempty"`
	Files     []string `yaml:"files,omitempty"`
}

type resolverSnapshot struct {
	APIVersion     string                     `json:"apiVersion"`
	Environment    string                     `json:"environment"`
	RegistryDigest string                     `json:"registry_digest"`
	Artifacts      []resolverSnapshotArtifact `json:"artifacts"`
	Digest         string                     `json:"digest"`
}

type resolverSnapshotArtifact struct {
	Name          string   `json:"name"`
	Kind          string   `json:"kind"`
	Repo          string   `json:"repo"`
	Ref           string   `json:"ref"`
	Commit        string   `json:"commit"`
	Image         string   `json:"image,omitempty"`
	ImageOverride bool     `json:"image_override,omitempty"`
	Files         []string `json:"files,omitempty"`
	CRDNames      []string `json:"crd_names,omitempty"`
	Digest        string   `json:"digest"`
	LocalPath     string   `json:"local_path,omitempty"`
}

type resolverExpectedArtifact struct {
	Name          string
	Kind          string
	Repo          string
	Ref           string
	Commit        string
	Image         string
	ImageOverride bool
	Files         []string
	CRDNames      []string
	Digest        string
}

type resolverCommandReceipt struct {
	Command    []string `json:"command"`
	StartedAt  string   `json:"started_at"`
	FinishedAt string   `json:"finished_at"`
	DurationMS int64    `json:"duration_ms"`
	ExitCode   int      `json:"exit_code"`
	Stdout     string   `json:"stdout"`
	Stderr     string   `json:"stderr"`
	Error      string   `json:"error,omitempty"`
}

type resolverCommandResult struct {
	ExitCode int
	Stdout   string
	Stderr   string
	Started  bool
	Recorded bool
}

// # Owner artifact resolution is deterministic and fail-closed
//
// This case proves the read-only resolver behavior required by issue
// [#1002](https://github.com/tidbcloud/controlplane9/issues/1002). At fixed owner
// commits, Dev and Staging produce stable snapshots with the expected images and
// CRDs. Invalid owner input publishes no snapshot.
//
// ## Agent execution
//
// **Mode: single agent.** Native Go functional entrypoint:
//
// ```bash
// CP9_OWNER_ARTIFACT_RESOLUTION_ENABLED=1 \
//   GOTOOLCHAIN=go1.26.1 GO_FUNCTIONAL_TEST_PACKAGES=./spec-tests \
//   GO_FUNCTIONAL_TEST_RUN='^TestFunctional_OwnerArtifactResolution$$' \
//   make test-functional
// ```
//
// `TestFunctional_OwnerArtifactResolution` owns preparation, execution,
// assertions, evidence, verdicts, and fixture cleanup. The product behavior and
// verdict below remain authoritative.
//
// ## Defaults and authority
//
// - Test the exact checked-out CP9 commit.
// - Freeze both `tidbcloud/app-delivery` and `tidbcloud/aws-shared-cd`, including
//   every Dev and Staging ref used by the registry.
// - When `CP9_SPEC_APP_DELIVERY_COMMIT` is set, freeze that exact pending owner
//   commit instead of current `main`; the default remains current `main`.
// - This case is read-only outside its run-owned local mirrors and evidence.
// - Required tools: `gh`, `git`, and `make`.
//
// Stop before the first resolution when a tool is missing or either owner
// repository cannot be read. No AWS session, Kubernetes resource, or TiDB cluster
// belongs to this case.
//
// ## Execute
//
// 1. Record the CP9 commit, both upstream owner commits, and the exact private
//    fixture commit consumed by CP9.
// 2. Resolve Dev twice without moving either owner ref. Require byte-identical
//    normalized snapshots. Repeat for Staging.
// 3. Independently project the checked-in registry and require exact one-to-one
//    snapshot coverage for every declared service and CRD group, including each
//    entry's name, kind, repository, ref, resolved image or CRD digest, canonical
//    CRD file list, and CRD names. Require every materialized CRD file to be
//    byte-identical to its frozen owner source and every artifact to record the
//    frozen commit for its repository and ref. Then check these real text shapes:
//    - a complete image string;
//    - split image name and tag;
//    - a `TPML_IMAGE_REGISTRY` token with the environment prefix and unchanged
//      owner suffix;
//    - one CRD directory selected from canonical files only.
// 4. In explicit Go subtests and separate private fixture commits, require each
//    of these inputs to return nonzero with its target error and publish no
//    `snapshot.json`:
//    - a service locator with multiple matches;
//    - a service locator with zero matches;
//    - an unknown `TPML_...` token;
//    - a selected YAML document that is not a CRD.
// 5. Discard the run-owned mirrors and record their cleanup result.
//
// Each step completes only when its direct result is observed. A deterministic
// but wrong image is not a pass.
//
// ## Verdicts
//
// ### Product
//
// `PASS` requires both environments to be byte-identical across repeated reads
// of the same exact commits, every independent expected value to match, and all
// four invalid inputs to fail without publishing a snapshot. A wrong value,
// moving commit, accepted invalid input, or partial snapshot is `FAIL`.
//
// An inaccessible repository prevents this customer path and is `NOT RUN`.
//
// ### Harness
//
// Harness `PASS` requires every chosen resolution and comparison probe to
// complete correctly. A script, comparison, parser, or evidence-capture defect is
// harness `FAIL`; it cannot supply missing product evidence or erase product
// evidence already observed. Harness is `NOT RUN` only when a prerequisite stops
// the case before any probe begins.
//
// ### Cleanup
//
// Cleanup `PASS` requires every run-owned private mirror removed and its result
// recorded. Known residue or a missing receipt is cleanup `FAIL` and does not
// change the product verdict. Cleanup is `NOT RUN` only when the case created no
// run-owned state.

func TestFunctional_OwnerArtifactResolution(t *testing.T) {
	if os.Getenv(ownerArtifactResolutionEnabled) != "1" {
		t.Skipf("set %s=1 to run the read-only owner artifact resolver journey", ownerArtifactResolutionEnabled)
	}

	run := newResolverRun(t)
	run.prepare(t)

	ran := false
	passed := t.Run("dev-repeatable", func(t *testing.T) {
		ran = true
		run.resolveTwice(t, "dev")
	})
	run.finishStage("dev-repeatable", ran, passed)

	ran = false
	passed = t.Run("staging-repeatable", func(t *testing.T) {
		ran = true
		run.resolveTwice(t, "staging")
	})
	run.finishStage("staging-repeatable", ran, passed)

	ran = false
	passed = t.Run("expected-artifacts", func(t *testing.T) {
		ran = true
		if run.stages["dev-repeatable"].Product != "pass" || run.stages["staging-repeatable"].Product != "pass" {
			run.stages["expected-artifacts"].Skipped = true
			t.Skip("requires fresh passing dev and staging snapshots from this invocation")
		}
		run.assertExpectedArtifacts(t)
	})
	run.finishStage("expected-artifacts", ran, passed)

	ran = false
	passed = t.Run("reject-multiple-match", func(t *testing.T) {
		ran = true
		run.restoreValidFixture(t, "before-multiple-match")
		data := append(append([]byte(nil), run.valid[run.files.Dataflow]...), run.original[run.files.Dataflow]...)
		run.writeFixture(t, run.files.Dataflow, data)
		run.publishFixture(t, "service-multiple-match", run.files.Dataflow)
		run.expectResolutionRejection(t, run.stages["reject-multiple-match"], "service-multiple-match", "regex matched 2 times; expected exactly 1")
	})
	run.finishStage("reject-multiple-match", ran, passed)

	ran = false
	passed = t.Run("reject-zero-match", func(t *testing.T) {
		ran = true
		run.restoreValidFixture(t, "before-zero-match")
		data := replaceOne(t, run, run.original[run.files.Dataflow], `(?m)^(\s*)image:`, `${1}not_image:`)
		run.writeFixture(t, run.files.Dataflow, data)
		run.publishFixture(t, "service-zero-match", run.files.Dataflow)
		run.expectResolutionRejection(t, run.stages["reject-zero-match"], "service-zero-match", "regex matched 0 times; expected exactly 1")
	})
	run.finishStage("reject-zero-match", ran, passed)

	ran = false
	passed = t.Run("reject-unknown-token", func(t *testing.T) {
		ran = true
		run.restoreValidFixture(t, "before-unknown-token")
		data := replaceOne(t, run, run.valid[run.files.Dataplane], `(?m)^(\s*image:\s*)\S+(\s*)$`, `${1}TPML_UNKNOWN/tidbcloud/dataplane-manager:token-proof${2}`)
		run.writeFixture(t, run.files.Dataplane, data)
		run.publishFixture(t, "unknown-token", run.files.Dataplane)
		run.expectResolutionRejection(t, run.stages["reject-unknown-token"], "unknown-token", "unsupported placeholder TPML_UNKNOWN remains after substitution")
	})
	run.finishStage("reject-unknown-token", ran, passed)

	ran = false
	passed = t.Run("reject-non-crd", func(t *testing.T) {
		ran = true
		run.restoreValidFixture(t, "before-non-crd")
		run.writeFixture(t, run.files.CRD, []byte("apiVersion: v1\nkind: ConfigMap\nmetadata:\n  name: not-a-crd\n"))
		run.publishFixture(t, "non-crd-document", run.files.CRD)
		run.expectResolutionRejection(t, run.stages["reject-non-crd"], "non-crd-document", "is not an apiextensions.k8s.io/v1 CustomResourceDefinition")
	})
	run.finishStage("reject-non-crd", ran, passed)
}

func newResolverRun(t *testing.T) *resolverRun {
	t.Helper()
	wd, err := os.Getwd()
	if err != nil {
		t.Fatalf("resolve spec-tests working directory: %v", err)
	}
	repoRoot := filepath.Dir(wd)
	if _, err := os.Stat(filepath.Join(repoRoot, "go.mod")); err != nil {
		t.Fatalf("resolve repository root from %s: %v", wd, err)
	}
	runRoot := filepath.Join(repoRoot, "artifacts", "spec-tests", time.Now().UTC().Format("20060102T150405Z")+"-resolver-owner-artifacts")
	if err := os.MkdirAll(filepath.Dir(runRoot), 0o700); err != nil {
		t.Fatalf("create resolver evidence parent: %v", err)
	}
	if err := os.Mkdir(runRoot, 0o700); err != nil {
		t.Fatalf("create resolver evidence directory %s: %v", runRoot, err)
	}
	run := &resolverRun{
		t:            t,
		repoRoot:     repoRoot,
		root:         runRoot,
		ownerWork:    filepath.Join(runRoot, "app-delivery-work"),
		ownerBare:    filepath.Join(runRoot, "app-delivery.git"),
		awsBare:      filepath.Join(runRoot, "aws-shared-cd.git"),
		gitConfig:    filepath.Join(runRoot, "gitconfig"),
		commandEnv:   map[string]string{},
		prerequisite: "not_run",
		cleanup:      "not_run",
		stages:       map[string]*resolverStage{},
		original:     map[string][]byte{},
		valid:        map[string][]byte{},
		awsHeads:     map[string]string{},
	}
	for _, name := range []string{
		"dev-repeatable", "staging-repeatable", "expected-artifacts",
		"reject-multiple-match", "reject-zero-match", "reject-unknown-token", "reject-non-crd",
	} {
		run.stages[name] = &resolverStage{Product: "not_run", Harness: "not_run"}
	}
	t.Cleanup(func() { run.finalize(t) })
	return run
}

func (r *resolverRun) prepare(t *testing.T) {
	t.Helper()
	for _, tool := range []string{"gh", "git", "make"} {
		if _, err := exec.LookPath(tool); err != nil {
			r.prerequisite = "fail"
			t.Fatalf("required tool %s is unavailable: %v", tool, err)
		}
	}
	r.writeText(t, "cp9-head.txt", r.gitOutput(t, "cp9-head", r.repoRoot, "rev-parse", "HEAD"))
	r.mustCommand(t, "prerequisite", "clone-app-delivery", 10*time.Minute,
		"gh", "repo", "clone", "tidbcloud/app-delivery", r.ownerWork, "--", "--depth=1")
	if commit := strings.TrimSpace(os.Getenv("CP9_SPEC_APP_DELIVERY_COMMIT")); commit != "" {
		r.mustCommand(t, "prerequisite", "fetch-app-delivery-commit", 10*time.Minute,
			"git", "-C", r.ownerWork, "-c", "credential.helper=", "-c", "credential.helper=!gh auth git-credential",
			"fetch", "--depth=1", "origin", commit)
		r.mustCommand(t, "prerequisite", "checkout-app-delivery-commit", 2*time.Minute,
			"git", "-C", r.ownerWork, "checkout", "--detach", commit)
		if got := r.gitOutput(t, "selected-app-delivery-head", r.ownerWork, "rev-parse", "HEAD"); got != commit {
			r.prerequisite = "fail"
			t.Fatalf("selected app-delivery commit=%s, want %s", got, commit)
		}
	}
	r.writeText(t, "app-delivery-upstream.txt", r.gitOutput(t, "app-delivery-upstream", r.ownerWork, "rev-parse", "HEAD"))
	r.mustCommand(t, "prerequisite", "configure-app-delivery-user-name", time.Minute,
		"git", "-C", r.ownerWork, "config", "user.name", "cp9-spec-test")
	r.mustCommand(t, "prerequisite", "configure-app-delivery-user-email", time.Minute,
		"git", "-C", r.ownerWork, "config", "user.email", "cp9-spec-test@localhost")
	r.mustCommand(t, "prerequisite", "clone-aws-shared-cd", 10*time.Minute,
		"gh", "repo", "clone", "tidbcloud/aws-shared-cd", r.awsBare, "--", "--bare")
	for _, ref := range []string{"main", "release-0.62"} {
		head := r.gitOutput(t, "aws-shared-cd-"+sanitizeResolverLabel(ref), r.awsBare, "rev-parse", ref)
		r.awsHeads[ref] = head
		r.writeText(t, "aws-shared-cd-"+sanitizeResolverLabel(ref)+".txt", head)
	}
	r.loadRegistry(t)
	r.prepareFixture(t)
	r.mustCommand(t, "harness", "build-cp9", 30*time.Minute, "make", "-C", r.repoRoot, "build")
	r.prerequisite = "pass"
}

func (r *resolverRun) loadRegistry(t *testing.T) {
	t.Helper()
	path := filepath.Join(r.repoRoot, "personas", "dev-full", "runtime", "artifact-registry.yaml")
	raw, err := os.ReadFile(path)
	if err != nil {
		r.harnessFatalf(t, nil, "read artifact registry: %v", err)
	}
	if err := yaml.Unmarshal(raw, &r.registry); err != nil {
		r.harnessFatalf(t, nil, "parse artifact registry: %v", err)
	}
	if len(r.registry.Blocked) != 0 || len(r.registry.Artifacts) == 0 {
		r.harnessFatalf(t, nil, "artifact registry has %d blocked and %d active entries", len(r.registry.Blocked), len(r.registry.Artifacts))
	}
	r.registryRaw = raw
}

func (r *resolverRun) prepareFixture(t *testing.T) {
	t.Helper()
	config := r.registryArtifact(t, "config-center").Dev
	dataflow := r.registryArtifact(t, "dataflow").Dev
	dataplane := r.registryArtifact(t, "dataplane-manager").Dev
	crdArtifact := r.registryArtifact(t, "cluster-ng-service-crds")
	crdFiles, _, _, _, err := r.canonicalCRDs(crdArtifact.Dev)
	if err != nil {
		r.harnessFatalf(t, nil, "select canonical fixture CRD: %v", err)
	}
	r.files = resolverFixtureFiles{
		Config:    config.File,
		CRD:       filepath.ToSlash(filepath.Join(crdArtifact.Dev.Directory, filepath.FromSlash(crdFiles[0]))),
		Dataflow:  dataflow.File,
		Dataplane: dataplane.File,
		CRDGroup:  crdArtifact.Name,
	}

	for _, relative := range []string{r.files.Config, r.files.CRD, r.files.Dataflow, r.files.Dataplane} {
		data, err := os.ReadFile(filepath.Join(r.ownerWork, filepath.FromSlash(relative)))
		if err != nil {
			r.harnessFatalf(t, nil, "read owner fixture %s: %v", relative, err)
		}
		r.original[relative] = data
		r.valid[relative] = append([]byte(nil), data...)
	}
	r.valid[r.files.Config] = replaceOne(t, r, r.valid[r.files.Config], `(?m)^(\s*newTag:\s*)\S+(\s*)$`, `${1}`+resolverConfigTag+`${2}`)
	r.valid[r.files.Dataflow] = replaceOne(t, r, r.valid[r.files.Dataflow], `(?m)^(\s*image:\s*)\S+(\s*)$`, `${1}`+resolverDataflowImage+`${2}`)
	r.valid[r.files.Dataplane] = replaceOne(t, r, r.valid[r.files.Dataplane], `(?m)^(\s*image:\s*)\S+(\s*)$`, `${1}`+resolverDataplaneImage+`${2}`)
	needle := []byte("  annotations:\n")
	if bytes.Count(r.valid[r.files.CRD], needle) != 1 {
		r.harnessFatalf(t, nil, "fixture CRD %s does not have exactly one metadata annotations block", r.files.CRD)
	}
	r.valid[r.files.CRD] = bytes.Replace(r.valid[r.files.CRD], needle,
		[]byte("  annotations:\n    cp9.tidbcloud.com/spec-artifact: resolver\n"), 1)
	r.restoreValidFiles(t)
	r.commitFixture(t, "prepare resolver owner inputs", r.fixturePaths()...)
	r.fixtureHead = r.gitOutput(t, "app-delivery-fixture", r.ownerWork, "rev-parse", "HEAD")
	r.writeText(t, "app-delivery-fixture.txt", r.fixtureHead)
	r.writeText(t, "app-delivery-consumed.txt", r.fixtureHead)

	r.mustCommand(t, "harness", "clone-app-delivery-bare", 5*time.Minute,
		"git", "clone", "--bare", r.ownerWork, r.ownerBare)
	r.mustCommand(t, "harness", "redirect-app-delivery-origin", time.Minute,
		"git", "-C", r.ownerWork, "remote", "set-url", "origin", r.ownerBare)
	r.mustCommand(t, "harness", "publish-initial-owner-fixture", 2*time.Minute,
		"git", "-C", r.ownerWork, "push", "--force", "origin", "HEAD:main")
	r.mustCommand(t, "harness", "allow-local-owner-remotes", time.Minute,
		"git", "config", "--file", r.gitConfig, "protocol.file.allow", "always")
	r.mustCommand(t, "harness", "redirect-app-delivery-clones", time.Minute,
		"git", "config", "--file", r.gitConfig, "url.file://"+r.ownerBare+".insteadOf",
		"https://github.com/tidbcloud/app-delivery.git")
	r.mustCommand(t, "harness", "redirect-aws-shared-cd-clones", time.Minute,
		"git", "config", "--file", r.gitConfig, "url.file://"+r.awsBare+".insteadOf",
		"https://github.com/tidbcloud/aws-shared-cd.git")
	r.commandEnv["GIT_CONFIG_GLOBAL"] = r.gitConfig
}

func (r *resolverRun) resolveTwice(t *testing.T, environment string) {
	t.Helper()
	stage := r.stages[environment+"-repeatable"]
	first := filepath.Join(r.root, "read-only", environment+"-a")
	second := filepath.Join(r.root, "read-only", environment+"-b")
	r.resolve(t, stage, environment+"-a", environment, first, true)
	r.resolve(t, stage, environment+"-b", environment, second, true)
	a, err := os.ReadFile(filepath.Join(first, "snapshot.json"))
	if err != nil {
		r.harnessFatalf(t, stage, "read first %s snapshot: %v", environment, err)
	}
	b, err := os.ReadFile(filepath.Join(second, "snapshot.json"))
	if err != nil {
		r.harnessFatalf(t, stage, "read second %s snapshot: %v", environment, err)
	}
	if !bytes.Equal(a, b) {
		r.productFatalf(t, stage, "%s repeated snapshots are not byte-identical", environment)
	}
}

func (r *resolverRun) resolve(t *testing.T, stage *resolverStage, label, environment, output string, wantSuccess bool) resolverCommandResult {
	t.Helper()
	result, err := r.command(label, 10*time.Minute, filepath.Join(r.repoRoot, "bin", "cp9"),
		"artifacts.resolve", "github-app-client-id=", "environment="+environment, "output="+output)
	if err != nil {
		r.harnessFatalf(t, stage, "capture resolver command %s: %v", label, err)
	}
	if wantSuccess && result.ExitCode != 0 {
		r.productFatalf(t, stage, "resolver command %s exited %d; see %s and %s", label, result.ExitCode, result.Stdout, result.Stderr)
	}
	return result
}

func (r *resolverRun) assertExpectedArtifacts(t *testing.T) {
	t.Helper()
	stage := r.stages["expected-artifacts"]
	dev := r.loadSnapshot(t, stage, filepath.Join(r.root, "read-only", "dev-a", "snapshot.json"))
	staging := r.loadSnapshot(t, stage, filepath.Join(r.root, "read-only", "staging-a", "snapshot.json"))
	r.assertSnapshot(t, stage, "dev", dev, filepath.Join(r.root, "read-only", "dev-a"))
	r.assertSnapshot(t, stage, "staging", staging, filepath.Join(r.root, "read-only", "staging-a"))
	r.assertFixtureShapes(t, stage, dev, filepath.Join(r.root, "read-only", "dev-a"))
	r.writeInventory(t, "dev", dev)
	r.writeInventory(t, "staging", staging)
}

func (r *resolverRun) assertSnapshot(t *testing.T, stage *resolverStage, environment string, snapshot resolverSnapshot, snapshotRoot string) {
	t.Helper()
	if snapshot.APIVersion != "cp9.tidbcloud.com/artifact-snapshot/v1alpha1" {
		r.productFatalf(t, stage, "%s snapshot apiVersion=%q", environment, snapshot.APIVersion)
	}
	wantSnapshotDigest, err := resolverSnapshotDigest(snapshot)
	if err != nil {
		r.harnessFatalf(t, stage, "marshal %s snapshot digest input: %v", environment, err)
	}
	if snapshot.Digest != wantSnapshotDigest {
		r.productFatalf(t, stage, "%s snapshot digest=%q, want %q", environment, snapshot.Digest, wantSnapshotDigest)
	}
	if snapshot.Environment != environment {
		r.productFatalf(t, stage, "%s snapshot environment=%q", environment, snapshot.Environment)
	}
	wantRegistryDigest := digestResolverBytes(r.registryRaw)
	if snapshot.RegistryDigest != wantRegistryDigest {
		r.productFatalf(t, stage, "%s registry digest=%q, want %q", environment, snapshot.RegistryDigest, wantRegistryDigest)
	}
	want := make([]resolverExpectedArtifact, 0, len(r.registry.Artifacts))
	for _, artifact := range r.registry.Artifacts {
		locator := artifact.locator(environment)
		expected := resolverExpectedArtifact{
			Name: artifact.Name, Kind: artifact.Kind, Repo: locator.Repo, Ref: locator.Ref,
		}
		switch locator.Repo {
		case "tidbcloud/app-delivery":
			expected.Commit = r.fixtureHead
		case "tidbcloud/aws-shared-cd":
			expected.Commit = r.awsHeads[locator.Ref]
		default:
			r.harnessFatalf(t, stage, "registry artifact %s uses unsupported owner repository %s", artifact.Name, locator.Repo)
		}
		if expected.Commit == "" {
			r.harnessFatalf(t, stage, "registry artifact %s has no frozen commit for %s", artifact.Name, locator.Ref)
		}
		if artifact.Kind == "service" {
			data, err := r.ownerBytes(t, environment+"-"+artifact.Name, locator)
			if err != nil {
				r.harnessFatalf(t, stage, "read expected %s owner input: %v", artifact.Name, err)
			}
			image, err := expectedResolverImage(data, locator, r.registry.Environments[environment].Substitutions)
			if err != nil {
				r.harnessFatalf(t, stage, "derive expected %s image: %v", artifact.Name, err)
			}
			expected.Image = image
			expected.Digest = digestResolverBytes([]byte(image))
		} else if artifact.Kind == "crd" {
			files, names, digest, _, err := r.canonicalCRDs(locator)
			if err != nil {
				r.harnessFatalf(t, stage, "derive expected %s CRDs: %v", artifact.Name, err)
			}
			expected.Files, expected.CRDNames, expected.Digest = files, names, digest
		} else {
			r.harnessFatalf(t, stage, "registry artifact %s has unsupported kind %q", artifact.Name, artifact.Kind)
		}
		want = append(want, expected)
	}
	slices.SortFunc(want, func(a, b resolverExpectedArtifact) int { return strings.Compare(a.Name, b.Name) })

	got := make([]resolverExpectedArtifact, 0, len(snapshot.Artifacts))
	for _, artifact := range snapshot.Artifacts {
		actual := resolverExpectedArtifact{
			Name: artifact.Name, Kind: artifact.Kind, Repo: artifact.Repo, Ref: artifact.Ref,
			Commit: artifact.Commit, Image: artifact.Image, ImageOverride: artifact.ImageOverride,
			Files: append([]string(nil), artifact.Files...), CRDNames: append([]string(nil), artifact.CRDNames...),
			Digest: artifact.Digest,
		}
		slices.Sort(actual.Files)
		slices.Sort(actual.CRDNames)
		if artifact.Kind == "crd" {
			r.assertMaterializedCRDs(t, stage, environment, snapshotRoot, artifact)
		}
		got = append(got, actual)
	}
	slices.SortFunc(got, func(a, b resolverExpectedArtifact) int { return strings.Compare(a.Name, b.Name) })
	if !reflect.DeepEqual(got, want) {
		gotJSON, _ := json.MarshalIndent(got, "", "  ")
		wantJSON, _ := json.MarshalIndent(want, "", "  ")
		r.writeBytes(t, filepath.Join("read-only", environment+"-actual.json"), append(gotJSON, '\n'))
		r.writeBytes(t, filepath.Join("read-only", environment+"-expected.json"), append(wantJSON, '\n'))
		r.productFatalf(t, stage, "%s snapshot does not match the independent registry projection", environment)
	}
}

func (r *resolverRun) assertMaterializedCRDs(t *testing.T, stage *resolverStage, environment, snapshotRoot string, artifact resolverSnapshotArtifact) {
	t.Helper()
	root, err := secureResolverPath(snapshotRoot, artifact.LocalPath, true)
	if err != nil {
		r.productFatalf(t, stage, "artifact %s local path: %v", artifact.Name, err)
	}
	materialized := []string{}
	err = filepath.WalkDir(root, func(path string, entry fs.DirEntry, walkErr error) error {
		if walkErr != nil {
			return walkErr
		}
		if entry.Type()&os.ModeSymlink != 0 {
			return fmt.Errorf("symlink %s is not allowed", path)
		}
		if entry.IsDir() {
			return nil
		}
		relative, relErr := filepath.Rel(root, path)
		if relErr != nil {
			return relErr
		}
		materialized = append(materialized, filepath.ToSlash(relative))
		return nil
	})
	if err != nil {
		r.harnessFatalf(t, stage, "enumerate materialized %s CRDs: %v", artifact.Name, err)
	}
	slices.Sort(materialized)
	wantFiles := append([]string(nil), artifact.Files...)
	slices.Sort(wantFiles)
	if !slices.Equal(materialized, wantFiles) {
		r.productFatalf(t, stage, "artifact %s materialized files=%v, want %v", artifact.Name, materialized, wantFiles)
	}
	locator := r.registryArtifact(t, artifact.Name).locator(environment)
	_, _, _, contents, err := r.canonicalCRDs(locator)
	if err != nil {
		r.harnessFatalf(t, stage, "load source CRDs for %s: %v", artifact.Name, err)
	}
	for relative, source := range contents {
		data, err := os.ReadFile(filepath.Join(root, filepath.FromSlash(relative)))
		if err != nil {
			r.harnessFatalf(t, stage, "read materialized CRD %s/%s: %v", artifact.Name, relative, err)
		}
		if !bytes.Equal(data, source) {
			r.productFatalf(t, stage, "materialized CRD %s/%s differs from frozen owner source", artifact.Name, relative)
		}
	}
}

func (r *resolverRun) assertFixtureShapes(t *testing.T, stage *resolverStage, snapshot resolverSnapshot, snapshotRoot string) {
	t.Helper()
	configText := string(r.valid[r.files.Config])
	namePattern := regexp.MustCompile(`(?m)^\s*newName:\s*(\S+)`)
	match := namePattern.FindStringSubmatch(configText)
	if len(match) != 2 {
		r.harnessFatalf(t, stage, "fixture config-center image has no newName")
	}
	r.assertFixtureImage(t, stage, snapshot, "config-center", match[1]+":"+resolverConfigTag)
	r.assertFixtureImage(t, stage, snapshot, "dataflow", resolverDataflowImage)
	r.assertFixtureImage(t, stage, snapshot, "dataplane-manager",
		r.registry.Environments["dev"].Substitutions["TPML_IMAGE_REGISTRY"]+"/tidbcloud/dataplane-manager:token-proof")
	for _, artifact := range snapshot.Artifacts {
		if artifact.Kind == "crd" {
			for _, file := range artifact.Files {
				if strings.EqualFold(filepath.Base(file), "kustomization.yaml") || strings.EqualFold(filepath.Base(file), "kustomization.yml") {
					r.productFatalf(t, stage, "CRD artifact %s selected non-canonical file %s", artifact.Name, file)
				}
			}
		}
		if artifact.Name != r.files.CRDGroup {
			continue
		}
		root, err := secureResolverPath(snapshotRoot, artifact.LocalPath, true)
		if err != nil {
			r.productFatalf(t, stage, "fixture CRD materialization path: %v", err)
		}
		found := false
		for _, relative := range artifact.Files {
			data, err := os.ReadFile(filepath.Join(root, filepath.FromSlash(relative)))
			if err != nil {
				r.harnessFatalf(t, stage, "read fixture CRD marker: %v", err)
			}
			found = found || bytes.Contains(data, []byte("cp9.tidbcloud.com/spec-artifact: resolver"))
		}
		if !found {
			r.productFatalf(t, stage, "fixture CRD marker is absent from %s", artifact.Name)
		}
		return
	}
	r.productFatalf(t, stage, "dev snapshot is missing %s", r.files.CRDGroup)
}

func (r *resolverRun) assertFixtureImage(t *testing.T, stage *resolverStage, snapshot resolverSnapshot, name, image string) {
	t.Helper()
	matches := 0
	for _, artifact := range snapshot.Artifacts {
		if artifact.Name == name && artifact.Image == image {
			matches++
		}
	}
	if matches != 1 {
		r.productFatalf(t, stage, "dev snapshot has %d %s artifacts with image %s, want 1", matches, name, image)
	}
}

func (r *resolverRun) expectResolutionRejection(t *testing.T, stage *resolverStage, label, errorText string) {
	t.Helper()
	output := filepath.Join(r.root, "read-only", label)
	result := r.resolve(t, stage, label, "dev", output, false)
	if result.ExitCode == 0 {
		r.productFatalf(t, stage, "%s unexpectedly resolved", label)
	}
	stdout, outErr := os.ReadFile(result.Stdout)
	stderr, errErr := os.ReadFile(result.Stderr)
	if outErr != nil || errErr != nil {
		r.harnessFatalf(t, stage, "read %s rejection output: stdout=%v stderr=%v", label, outErr, errErr)
	}
	if !bytes.Contains(append(stdout, stderr...), []byte(errorText)) {
		r.productFatalf(t, stage, "%s rejected for the wrong reason; want %q", label, errorText)
	}
	if _, err := os.Stat(filepath.Join(output, "snapshot.json")); err == nil {
		r.productFatalf(t, stage, "%s published snapshot.json after rejection", label)
	} else if !errors.Is(err, os.ErrNotExist) {
		r.harnessFatalf(t, stage, "inspect %s rejected snapshot: %v", label, err)
	}
}

func (r *resolverRun) loadSnapshot(t *testing.T, stage *resolverStage, path string) resolverSnapshot {
	t.Helper()
	data, err := os.ReadFile(path)
	if err != nil {
		r.harnessFatalf(t, stage, "read snapshot %s: %v", path, err)
	}
	var snapshot resolverSnapshot
	if err := json.Unmarshal(data, &snapshot); err != nil {
		r.harnessFatalf(t, stage, "parse snapshot %s: %v", path, err)
	}
	return snapshot
}

func (r *resolverRun) registryArtifact(t *testing.T, name string) resolverRegistryArtifact {
	t.Helper()
	for _, artifact := range r.registry.Artifacts {
		if artifact.Name == name {
			return artifact
		}
	}
	r.harnessFatalf(t, nil, "artifact registry is missing %s", name)
	return resolverRegistryArtifact{}
}

func (a resolverRegistryArtifact) locator(environment string) resolverLocator {
	if environment == "staging" {
		return a.Staging
	}
	return a.Dev
}

func (r *resolverRun) ownerBytes(t *testing.T, label string, locator resolverLocator) ([]byte, error) {
	t.Helper()
	if locator.Repo == "tidbcloud/app-delivery" {
		path, err := secureResolverPath(r.ownerWork, locator.File, false)
		if err != nil {
			return nil, err
		}
		return os.ReadFile(path)
	}
	if locator.Repo != "tidbcloud/aws-shared-cd" {
		return nil, fmt.Errorf("unsupported owner repository %s", locator.Repo)
	}
	result, err := r.command("owner-source-"+sanitizeResolverLabel(label), 2*time.Minute,
		"git", "--git-dir="+r.awsBare, "show", locator.Ref+":"+locator.File)
	if err != nil {
		return nil, err
	}
	if result.ExitCode != 0 {
		return nil, fmt.Errorf("git show exited %d; see %s", result.ExitCode, result.Stderr)
	}
	return os.ReadFile(result.Stdout)
}

func expectedResolverImage(data []byte, locator resolverLocator, substitutions map[string]string) (string, error) {
	re, err := regexp.Compile(locator.Regex)
	if err != nil {
		return "", err
	}
	matches := re.FindAllSubmatch(data, -1)
	if len(matches) != 1 {
		return "", fmt.Errorf("regex matched %d times, want 1", len(matches))
	}
	var formatErr error
	image := resolverFormatToken.ReplaceAllStringFunc(locator.Format, func(token string) string {
		parts := resolverFormatToken.FindStringSubmatch(token)
		index, err := strconv.Atoi(parts[1])
		if err != nil || index >= len(matches[0]) || len(matches[0][index]) == 0 {
			formatErr = fmt.Errorf("missing capture group %s", token)
			return token
		}
		return string(matches[0][index])
	})
	if formatErr != nil {
		return "", formatErr
	}
	var unknown string
	image = resolverPlaceholder.ReplaceAllStringFunc(image, func(token string) string {
		value, ok := substitutions[token]
		if !ok {
			unknown = token
			return token
		}
		return value
	})
	if unknown != "" || resolverFormatToken.MatchString(image) || resolverPlaceholder.MatchString(image) {
		return "", fmt.Errorf("image contains unresolved token %s", unknown)
	}
	return strings.TrimSpace(image), nil
}

func (r *resolverRun) canonicalCRDs(locator resolverLocator) ([]string, []string, string, map[string][]byte, error) {
	if locator.Repo != "tidbcloud/app-delivery" || locator.Ref != "main" {
		return nil, nil, "", nil, fmt.Errorf("CRD locator uses unsupported owner %s@%s", locator.Repo, locator.Ref)
	}
	root, err := secureResolverPath(r.ownerWork, locator.Directory, true)
	if err != nil {
		return nil, nil, "", nil, err
	}
	files := append([]string(nil), locator.Files...)
	if locator.Files == nil {
		err := filepath.WalkDir(root, func(path string, entry fs.DirEntry, walkErr error) error {
			if walkErr != nil {
				return walkErr
			}
			if entry.Type()&os.ModeSymlink != 0 {
				return fmt.Errorf("symlink %s is not allowed", path)
			}
			if entry.IsDir() || strings.EqualFold(entry.Name(), "kustomization.yaml") || strings.EqualFold(entry.Name(), "kustomization.yml") {
				return nil
			}
			ext := strings.ToLower(filepath.Ext(entry.Name()))
			if ext != ".yaml" && ext != ".yml" {
				return nil
			}
			relative, err := filepath.Rel(root, path)
			if err != nil {
				return err
			}
			files = append(files, filepath.ToSlash(relative))
			return nil
		})
		if err != nil {
			return nil, nil, "", nil, err
		}
	}
	slices.Sort(files)
	if len(files) == 0 {
		return nil, nil, "", nil, errors.New("contains no canonical YAML files")
	}
	for i, relative := range files {
		if filepath.ToSlash(filepath.Clean(filepath.FromSlash(relative))) != relative {
			return nil, nil, "", nil, fmt.Errorf("non-canonical CRD path %q", relative)
		}
		if i > 0 && relative == files[i-1] {
			return nil, nil, "", nil, fmt.Errorf("duplicate CRD path %s", relative)
		}
	}
	names := []string{}
	contents := map[string][]byte{}
	hash := sha256.New()
	seen := map[string]bool{}
	for _, relative := range files {
		path, err := secureResolverPath(root, relative, false)
		if err != nil {
			return nil, nil, "", nil, err
		}
		data, err := os.ReadFile(path)
		if err != nil {
			return nil, nil, "", nil, err
		}
		contents[relative] = data
		decoder := yaml.NewDecoder(bytes.NewReader(data))
		documents := 0
		for {
			var document struct {
				APIVersion string `yaml:"apiVersion"`
				Kind       string `yaml:"kind"`
				Metadata   struct {
					Name string `yaml:"name"`
				} `yaml:"metadata"`
			}
			if err := decoder.Decode(&document); errors.Is(err, io.EOF) {
				break
			} else if err != nil {
				return nil, nil, "", nil, fmt.Errorf("parse %s: %w", relative, err)
			}
			documents++
			name := strings.TrimSpace(document.Metadata.Name)
			if document.APIVersion != "apiextensions.k8s.io/v1" || document.Kind != "CustomResourceDefinition" || name == "" {
				return nil, nil, "", nil, fmt.Errorf("%s document %d is not a CRD", relative, documents)
			}
			if seen[name] {
				return nil, nil, "", nil, fmt.Errorf("duplicate CRD name %s", name)
			}
			seen[name] = true
			names = append(names, name)
		}
		if documents == 0 {
			return nil, nil, "", nil, fmt.Errorf("%s contains no YAML document", relative)
		}
		_, _ = hash.Write([]byte(relative))
		_, _ = hash.Write([]byte{0})
		_, _ = hash.Write(data)
		_, _ = hash.Write([]byte{0})
	}
	slices.Sort(names)
	return files, names, "sha256:" + hex.EncodeToString(hash.Sum(nil)), contents, nil
}

func secureResolverPath(root, relative string, directory bool) (string, error) {
	if filepath.IsAbs(relative) || strings.TrimSpace(relative) == "" {
		return "", fmt.Errorf("path %q must be relative", relative)
	}
	root, err := filepath.EvalSymlinks(root)
	if err != nil {
		return "", err
	}
	path, err := filepath.EvalSymlinks(filepath.Join(root, filepath.FromSlash(relative)))
	if err != nil {
		return "", err
	}
	rel, err := filepath.Rel(root, path)
	if err != nil || rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
		return "", fmt.Errorf("path %q escapes %s", relative, root)
	}
	info, err := os.Stat(path)
	if err != nil {
		return "", err
	}
	if info.IsDir() != directory || (!directory && !info.Mode().IsRegular()) {
		return "", fmt.Errorf("path %q has the wrong file type", relative)
	}
	return path, nil
}

func replaceOne(t *testing.T, r *resolverRun, data []byte, pattern, replacement string) []byte {
	t.Helper()
	re, err := regexp.Compile(pattern)
	if err != nil {
		r.harnessFatalf(t, nil, "compile fixture pattern: %v", err)
	}
	if len(re.FindAllIndex(data, -1)) != 1 {
		r.harnessFatalf(t, nil, "fixture pattern %q does not match exactly once", pattern)
	}
	return re.ReplaceAll(data, []byte(replacement))
}

func (r *resolverRun) fixturePaths() []string {
	return []string{r.files.Config, r.files.CRD, r.files.Dataflow, r.files.Dataplane}
}

func (r *resolverRun) restoreValidFiles(t *testing.T) {
	t.Helper()
	for _, relative := range r.fixturePaths() {
		r.writeFixture(t, relative, r.valid[relative])
	}
}

func (r *resolverRun) restoreValidFixture(t *testing.T, label string) {
	t.Helper()
	r.restoreValidFiles(t)
	r.publishFixture(t, label, r.fixturePaths()...)
}

func (r *resolverRun) writeFixture(t *testing.T, relative string, data []byte) {
	t.Helper()
	path, err := secureResolverPath(r.ownerWork, relative, false)
	if err != nil {
		r.harnessFatalf(t, nil, "resolve fixture path %s: %v", relative, err)
	}
	if err := os.WriteFile(path, data, 0o600); err != nil {
		r.harnessFatalf(t, nil, "write fixture path %s: %v", relative, err)
	}
}

func (r *resolverRun) commitFixture(t *testing.T, label string, paths ...string) {
	t.Helper()
	argv := append([]string{"git", "-C", r.ownerWork, "add", "--"}, paths...)
	r.mustCommand(t, "harness", "stage-"+label, time.Minute, argv...)
	result, err := r.command("diff-"+label, time.Minute, "git", "-C", r.ownerWork, "diff", "--cached", "--quiet")
	if err != nil {
		r.harnessFatalf(t, nil, "inspect staged fixture %s: %v", label, err)
	}
	if result.ExitCode == 0 {
		return
	}
	if result.ExitCode != 1 {
		r.harnessFatalf(t, nil, "inspect staged fixture %s exited %d", label, result.ExitCode)
	}
	r.mustCommand(t, "harness", "commit-"+label, 2*time.Minute,
		"git", "-C", r.ownerWork, "commit", "-m", "spec: "+label)
}

func (r *resolverRun) publishFixture(t *testing.T, label string, paths ...string) {
	t.Helper()
	r.commitFixture(t, label, paths...)
	r.mustCommand(t, "harness", "publish-"+label, 2*time.Minute,
		"git", "-C", r.ownerWork, "push", "--force", "origin", "HEAD:main")
	r.writeText(t, filepath.Join("read-only", label+"-commit.txt"), r.gitOutput(t, label+"-head", r.ownerWork, "rev-parse", "HEAD"))
}

func (r *resolverRun) gitOutput(t *testing.T, label, directory string, args ...string) string {
	t.Helper()
	argv := append([]string{"git", "-C", directory}, args...)
	result, err := r.command(label, 2*time.Minute, argv...)
	if err != nil {
		r.harnessFatalf(t, nil, "capture git %s: %v", label, err)
	}
	if result.ExitCode != 0 {
		r.harnessFatalf(t, nil, "git %s exited %d; see %s", label, result.ExitCode, result.Stderr)
	}
	data, err := os.ReadFile(result.Stdout)
	if err != nil {
		r.harnessFatalf(t, nil, "read git %s output: %v", label, err)
	}
	return strings.TrimSpace(string(data))
}

func (r *resolverRun) mustCommand(t *testing.T, kind, label string, timeout time.Duration, argv ...string) resolverCommandResult {
	t.Helper()
	result, err := r.command(label, timeout, argv...)
	if err != nil {
		r.failCommand(t, kind, nil, "capture command %s: %v", label, err)
	}
	if result.ExitCode != 0 {
		r.failCommand(t, kind, nil, "command %s exited %d; see %s and %s", label, result.ExitCode, result.Stdout, result.Stderr)
	}
	return result
}

func (r *resolverRun) command(label string, timeout time.Duration, argv ...string) (resolverCommandResult, error) {
	return r.commandWithInput(label, timeout, nil, argv...)
}

func (r *resolverRun) commandWithInput(label string, timeout time.Duration, stdin io.Reader, argv ...string) (resolverCommandResult, error) {
	result := resolverCommandResult{ExitCode: -1}
	r.commandID++
	prefix := fmt.Sprintf("%03d-%s", r.commandID, sanitizeResolverLabel(label))
	directory := filepath.Join(r.root, "commands")
	if err := os.MkdirAll(directory, 0o700); err != nil {
		return result, err
	}
	stdoutPath := filepath.Join(directory, prefix+".stdout.log")
	stderrPath := filepath.Join(directory, prefix+".stderr.log")
	stdout, err := os.OpenFile(stdoutPath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0o600)
	if err != nil {
		return result, err
	}
	stderr, err := os.OpenFile(stderrPath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0o600)
	if err != nil {
		_ = stdout.Close()
		return result, err
	}
	result.Stdout, result.Stderr = stdoutPath, stderrPath
	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()
	started := time.Now()
	command := exec.CommandContext(ctx, argv[0], argv[1:]...)
	command.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
	command.Cancel = func() error {
		if command.Process == nil {
			return os.ErrProcessDone
		}
		err := syscall.Kill(-command.Process.Pid, syscall.SIGKILL)
		if errors.Is(err, syscall.ESRCH) {
			return os.ErrProcessDone
		}
		return err
	}
	command.WaitDelay = 5 * time.Second
	command.Dir = r.repoRoot
	command.Env = resolverCommandEnv(r.commandEnv)
	command.Stdin = stdin
	command.Stdout = stdout
	command.Stderr = stderr
	runErr := command.Start()
	if runErr == nil {
		result.Started = true
		runErr = command.Wait()
	}
	finished := time.Now()
	closeErr := errors.Join(stdout.Close(), stderr.Close())
	if result.Started {
		result.ExitCode = 0
	}
	if runErr != nil && result.Started {
		var exitErr *exec.ExitError
		if errors.As(runErr, &exitErr) {
			result.ExitCode = exitErr.ExitCode()
		}
	}
	receipt := resolverCommandReceipt{
		Command: argv, StartedAt: started.UTC().Format(time.RFC3339Nano), FinishedAt: finished.UTC().Format(time.RFC3339Nano),
		DurationMS: finished.Sub(started).Milliseconds(), ExitCode: result.ExitCode,
		Stdout: filepath.ToSlash(filepath.Join("commands", filepath.Base(stdoutPath))),
		Stderr: filepath.ToSlash(filepath.Join("commands", filepath.Base(stderrPath))),
	}
	if runErr != nil {
		receipt.Error = runErr.Error()
	}
	if ctx.Err() != nil {
		receipt.Error = ctx.Err().Error()
	}
	data, marshalErr := json.MarshalIndent(receipt, "", "  ")
	if marshalErr == nil {
		data = append(data, '\n')
		marshalErr = os.WriteFile(filepath.Join(directory, prefix+".json"), data, 0o600)
	}
	if closeErr != nil || marshalErr != nil {
		return result, errors.Join(closeErr, marshalErr)
	}
	result.Recorded = true
	if ctx.Err() != nil {
		return result, ctx.Err()
	}
	if runErr != nil {
		var exitErr *exec.ExitError
		if !errors.As(runErr, &exitErr) {
			return result, runErr
		}
	}
	return result, nil
}

func resolverCommandEnv(overrides map[string]string) []string {
	env := make([]string, 0, len(os.Environ())+len(overrides))
	for _, value := range os.Environ() {
		key, _, _ := strings.Cut(value, "=")
		if _, replaced := overrides[key]; !replaced {
			env = append(env, value)
		}
	}
	for key, value := range overrides {
		env = append(env, key+"="+value)
	}
	return env
}

func sanitizeResolverLabel(label string) string {
	label = strings.ToLower(label)
	var result strings.Builder
	for _, value := range label {
		if value >= 'a' && value <= 'z' || value >= '0' && value <= '9' || value == '-' {
			result.WriteRune(value)
		} else {
			result.WriteByte('-')
		}
	}
	return strings.Trim(result.String(), "-")
}

func (r *resolverRun) writeInventory(t *testing.T, environment string, snapshot resolverSnapshot) {
	t.Helper()
	services, crds := []string{}, []string{}
	for _, artifact := range snapshot.Artifacts {
		if artifact.Kind == "service" {
			services = append(services, artifact.Name)
		} else if artifact.Kind == "crd" {
			crds = append(crds, artifact.Name)
		}
	}
	slices.Sort(services)
	slices.Sort(crds)
	r.writeText(t, filepath.Join("read-only", environment+"-inventory", "services.txt"), strings.Join(services, "\n"))
	r.writeText(t, filepath.Join("read-only", environment+"-inventory", "crd-groups.txt"), strings.Join(crds, "\n"))
}

func (r *resolverRun) writeText(t *testing.T, relative, value string) {
	t.Helper()
	if !strings.HasSuffix(value, "\n") {
		value += "\n"
	}
	r.writeBytes(t, relative, []byte(value))
}

func (r *resolverRun) writeBytes(t *testing.T, relative string, data []byte) {
	t.Helper()
	path := filepath.Join(r.root, filepath.FromSlash(relative))
	rel, err := filepath.Rel(r.root, path)
	if err != nil || rel == "." || rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
		r.harnessFatalf(t, nil, "evidence path %q escapes run root", relative)
	}
	if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil {
		r.harnessFatalf(t, nil, "create evidence parent for %s: %v", relative, err)
	}
	if err := os.WriteFile(path, data, 0o600); err != nil {
		r.harnessFatalf(t, nil, "write evidence %s: %v", relative, err)
	}
}

func (r *resolverRun) finishStage(name string, ran, passed bool) {
	stage := r.stages[name]
	if !ran || stage.Skipped {
		return
	}
	if passed {
		stage.Product = "pass"
		stage.Harness = "pass"
		return
	}
	if stage.Product == "not_run" && stage.Harness == "not_run" {
		stage.Harness = "fail"
		r.harnessFail = true
	}
}

func (r *resolverRun) productFatalf(t *testing.T, stage *resolverStage, format string, args ...any) {
	t.Helper()
	if r.failure != nil {
		*r.failure = "product"
	}
	if stage != nil {
		stage.Product = "fail"
		stage.Harness = "pass"
	}
	t.Fatalf(format, args...)
}

func (r *resolverRun) harnessFatalf(t *testing.T, stage *resolverStage, format string, args ...any) {
	t.Helper()
	r.harnessFail = true
	if r.failure != nil {
		*r.failure = "harness"
	}
	if stage != nil {
		stage.Harness = "fail"
	}
	t.Fatalf(format, args...)
}

func (r *resolverRun) failCommand(t *testing.T, kind string, stage *resolverStage, format string, args ...any) {
	t.Helper()
	if kind == "prerequisite" {
		r.prerequisite = "fail"
		t.Fatalf(format, args...)
	}
	r.harnessFatalf(t, stage, format, args...)
}

func (r *resolverRun) finalize(t *testing.T) {
	t.Helper()
	fixtureLive := false
	for _, path := range []string{r.ownerWork, r.ownerBare, r.awsBare, r.gitConfig} {
		if _, err := os.Lstat(path); err == nil {
			fixtureLive = true
		} else if !errors.Is(err, os.ErrNotExist) {
			fixtureLive = true
		}
	}
	if fixtureLive && !t.Failed() {
		r.cleanup = "pass"
		for _, path := range []string{r.ownerWork, r.ownerBare, r.awsBare, r.gitConfig} {
			if err := removeResolverRunPath(r.root, path); err != nil {
				r.cleanup = "fail"
				t.Errorf("remove resolver fixture %s: %v", path, err)
			}
		}
	} else if fixtureLive {
		r.cleanup = "fail"
		_ = os.WriteFile(filepath.Join(r.root, "scene-preserved.txt"), []byte("Scene preserved after first red; cleanup is pending.\n"), 0o600)
	}
	product := "pass"
	for _, name := range []string{
		"dev-repeatable", "staging-repeatable", "expected-artifacts",
		"reject-multiple-match", "reject-zero-match", "reject-unknown-token", "reject-non-crd",
	} {
		switch r.stages[name].Product {
		case "fail":
			product = "fail"
		case "not_run":
			if product == "pass" {
				product = "not_run"
			}
		}
	}
	harness := "pass"
	if r.harnessFail {
		harness = "fail"
	} else if r.prerequisite != "pass" {
		harness = "not_run"
	}
	var summary strings.Builder
	fmt.Fprintf(&summary, "# Owner artifact resolution is deterministic and fail-closed\n\n")
	fmt.Fprintf(&summary, "- Product verdict: %s\n- Harness verdict: %s\n- Cleanup verdict: %s\n", product, harness, r.cleanup)
	fmt.Fprintf(&summary, "- Prerequisites: %s\n- CP9: %s\n", r.prerequisite, summaryResolverFile(r.root, "cp9-head.txt"))
	fmt.Fprintf(&summary, "- App Delivery upstream: %s\n- App Delivery fixture: %s\n", summaryResolverFile(r.root, "app-delivery-upstream.txt"), r.fixtureHead)
	fmt.Fprintf(&summary, "- AWS Shared CD main: %s\n- AWS Shared CD release-0.62: %s\n", r.awsHeads["main"], r.awsHeads["release-0.62"])
	summary.WriteString("\n| Go subtest | Product | Harness |\n| --- | --- | --- |\n")
	for _, name := range []string{
		"dev-repeatable", "staging-repeatable", "expected-artifacts",
		"reject-multiple-match", "reject-zero-match", "reject-unknown-token", "reject-non-crd",
	} {
		fmt.Fprintf(&summary, "| %s | %s | %s |\n", name, r.stages[name].Product, r.stages[name].Harness)
	}
	summary.WriteString("\n- Evidence: [run evidence](.)\n")
	if err := os.WriteFile(filepath.Join(r.root, "summary.md"), []byte(summary.String()), 0o600); err != nil {
		t.Errorf("write resolver summary: %v", err)
	}
}

func removeResolverRunPath(root, path string) error {
	relative, err := filepath.Rel(root, path)
	if err != nil || relative == "." || relative == ".." || strings.HasPrefix(relative, ".."+string(filepath.Separator)) {
		return fmt.Errorf("refuse to remove non-child path %s", path)
	}
	if err := os.RemoveAll(path); err != nil {
		return err
	}
	if _, err := os.Stat(path); !errors.Is(err, os.ErrNotExist) {
		return fmt.Errorf("path remains after removal")
	}
	return nil
}

func summaryResolverFile(root, name string) string {
	data, err := os.ReadFile(filepath.Join(root, name))
	if err != nil {
		return "not_recorded"
	}
	return strings.TrimSpace(string(data))
}

func digestResolverBytes(data []byte) string {
	sum := sha256.Sum256(data)
	return "sha256:" + hex.EncodeToString(sum[:])
}

func resolverSnapshotDigest(snapshot resolverSnapshot) (string, error) {
	digestInput := snapshot
	digestInput.Artifacts = append([]resolverSnapshotArtifact(nil), snapshot.Artifacts...)
	digestInput.Digest = ""
	for i := range digestInput.Artifacts {
		digestInput.Artifacts[i].LocalPath = ""
	}
	data, err := json.Marshal(digestInput)
	if err != nil {
		return "", err
	}
	return digestResolverBytes(data), nil
}
