package main

import (
	"bytes"
	"crypto/ed25519"
	"encoding/json"
	"maps"
	"os"
	"os/exec"
	"path/filepath"
	"reflect"
	"runtime/debug"
	"slices"
	"strings"
	"testing"

	"github.com/pingcap-inc/nexuscrm-contract/pkg/appauthority"
	catalogsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/catalog/consumerv1"
	navigationsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/applicationnavigationv1"
	approvalnotificationsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/approvalnotificationflowv1"
	approvalsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/approvalv1"
	auditsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/auditlogv1"
	authorizationsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/authorizationv1"
	bulkingestsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/bulkingestv1"
	commandsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/commandv1"
	permissionsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/effectivepermissionsv1"
	filessemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/filesv1"
	flowcommandsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/flowcommandv1"
	flowcommandsemanticv2 "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/flowcommandv2"
	flowcommandsemanticv3 "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/flowcommandv3"
	flowsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/flowv1"
	layoutauthoringsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/layoutauthoringv1"
	layoutauthoringv2semantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/layoutauthoringv2"
	listviewsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/listviewv1"
	outboundintentsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/outboundintentv1"
	pagelayoutsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/pagelayoutresolutionv1"
	permissionauthoringsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/permissionauthoringv1"
	querysemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/queryv1"
	accesssemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/recordaccessv1"
	attachmentsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/recordattachmentsv1"
	defaultsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/recorddefaultsv1"
	mutationsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/recordmutationv1"
	viewsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/recordviewv1"
	recyclemutationsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/recyclebinmutationv1"
	recyclesemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/recyclebinv1"
	relatedsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/relatedcountsv1"
	reportsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/reportv1"
	searchsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/searchv1"
	sessionsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/sessionv1"
	"github.com/pingcap-inc/nexuscrm-controlplane/releaseartifact"
)

func TestCRMReleaseBuildIsDeterministicCompleteAndMinimal(t *testing.T) {
	packRoot := filepath.Join("..", "..", "salesforce-crm-standard")
	first, firstRaw, firstSourceDigest, err := buildCRMRelease(packRoot)
	if err != nil {
		t.Fatal(err)
	}
	second, secondRaw, secondSourceDigest, err := buildCRMRelease(packRoot)
	if err != nil {
		t.Fatal(err)
	}
	if first.AppReleaseDigest != second.AppReleaseDigest || firstSourceDigest != secondSourceDigest || !bytes.Equal(firstRaw, secondRaw) {
		t.Fatal("same CRM source inputs produced different release bytes or identity")
	}
	manifestRaw, err := os.ReadFile(filepath.Join(packRoot, "pack.json"))
	if err != nil {
		t.Fatal(err)
	}
	var legacy legacyPackManifest
	if err := json.Unmarshal(manifestRaw, &legacy); err != nil {
		t.Fatal(err)
	}
	wantCommandSources := runtimeFlowCommandSources(legacy.Version)
	wantComponentCount := len(legacy.Components) + len(wantCommandSources)
	if len(first.Bundle.Components) != wantComponentCount ||
		len(first.ComponentBlobs) != wantComponentCount {
		t.Fatalf(
			"AppPackage components=%d blobs=%d, want %d source-owned components",
			len(first.Bundle.Components),
			len(first.ComponentBlobs),
			wantComponentCount,
		)
	}
	campaignMemberLayoutPublished := false
	for index, component := range first.Bundle.Components {
		if component.ComponentKind != crmLegacyComponentKinds["page_layout"] ||
			component.BusinessKey != "CampaignMember/default" {
			continue
		}
		var layout struct {
			ObjectAPIName string `json:"objectApiName"`
			IsDefault     bool   `json:"isDefault"`
		}
		if err := json.Unmarshal(first.ComponentBlobs[index].CanonicalBehavior, &layout); err != nil {
			t.Fatal(err)
		}
		if layout.ObjectAPIName != "CampaignMember" || !layout.IsDefault {
			t.Fatalf("CampaignMember launch layout drifted: %#v", layout)
		}
		campaignMemberLayoutPublished = true
	}
	if !campaignMemberLayoutPublished {
		t.Fatal("CampaignMember/default layout is absent from the immutable AppRelease")
	}
	wantCommandVersions := map[uint32]string{
		flowcommandsemantic.PayloadFormatVersion:   flowcommandsemantic.CommandKind,
		flowcommandsemanticv2.PayloadFormatVersion: flowcommandsemantic.CommandKind + "/v2",
		flowcommandsemanticv3.PayloadFormatVersion: flowcommandsemantic.CommandKind + "/v3",
	}
	gotCommandVersions := make(map[uint32]string, len(wantCommandVersions))
	for index, component := range first.Bundle.Components {
		if component.ComponentKind != crmRuntimeCommandComponent {
			continue
		}
		var command struct {
			CommandKind          string `json:"commandKind"`
			PayloadFormatVersion uint32 `json:"payloadFormatVersion"`
		}
		if err := json.Unmarshal(
			first.ComponentBlobs[index].CanonicalBehavior,
			&command,
		); err != nil {
			t.Fatal(err)
		}
		if command.CommandKind != flowcommandsemantic.CommandKind {
			t.Fatalf("Runtime command kind = %q", command.CommandKind)
		}
		gotCommandVersions[command.PayloadFormatVersion] = component.BusinessKey
	}
	if !maps.Equal(gotCommandVersions, wantCommandVersions) {
		t.Fatalf("Runtime Flow command registrations = %#v, want %#v", gotCommandVersions, wantCommandVersions)
	}
	previewSupport := map[string]bool{}
	for index, component := range first.Bundle.Components {
		if component.ComponentKind != crmLegacyComponentKinds["invocable_action"] {
			continue
		}
		var actions []struct {
			Name                     string `json:"name"`
			FlowDeveloperName        string `json:"flowDeveloperName"`
			PreviewFlowDeveloperName string `json:"previewFlowDeveloperName"`
			CreatePreviewSupported   bool   `json:"createPreviewSupported"`
		}
		if err := json.Unmarshal(first.ComponentBlobs[index].CanonicalBehavior, &actions); err != nil {
			t.Fatal(err)
		}
		for _, action := range actions {
			previewSupport[action.Name] = action.CreatePreviewSupported
			for _, flowName := range []string{
				action.FlowDeveloperName,
				action.PreviewFlowDeveloperName,
			} {
				if flowName == "" {
					continue
				}
				flowComponentID := appauthority.ComponentID(
					crmAppID,
					crmLegacyComponentKinds["standard_flow"],
					flowName,
				)
				if !slices.Contains(component.LocalDependencies, flowComponentID) {
					t.Fatalf(
						"Standard Action %s Flow %s is absent from the immutable dependency graph",
						action.Name,
						flowName,
					)
				}
			}
		}
	}
	if previewSupport["convertLead"] || !previewSupport["createQuote"] ||
		!previewSupport["createOrder"] || previewSupport["sendRecordEmail"] {
		t.Fatalf("Standard Action create-preview declarations drifted: %#v", previewSupport)
	}
	usedKinds := make(map[string]struct{}, len(crmLegacyComponentKinds))
	for _, component := range legacy.Components {
		usedKinds[component.Kind] = struct{}{}
	}
	if len(usedKinds) != len(crmLegacyComponentKinds) {
		t.Fatalf("reviewed CRM component-kind mappings=%d, source kinds=%d; stale or missing mapping", len(crmLegacyComponentKinds), len(usedKinds))
	}
	resolverComponents := 0
	for i, component := range first.Bundle.Components {
		if component.ComponentKind != crmRecordViewComponent {
			continue
		}
		resolverComponents++
		var document struct {
			Resolvers []struct {
				ResolverID          string `json:"resolverId"`
				Kind                string `json:"kind"`
				SourceRecordMatcher *struct {
					Kind             string  `json:"kind"`
					StartOffsetBytes *uint16 `json:"startOffsetBytes"`
					LengthBytes      uint16  `json:"lengthBytes"`
				} `json:"sourceRecordMatcher"`
			} `json:"resolvers"`
			Fields []struct {
				ParentAPIName string `json:"parentApiName"`
				FieldAPIName  string `json:"fieldApiName"`
				ResolverID    string `json:"resolverId"`
			} `json:"fields"`
		}
		if err := json.Unmarshal(first.ComponentBlobs[i].CanonicalBehavior, &document); err != nil {
			t.Fatal(err)
		}
		if len(document.Resolvers) != 1 || document.Resolvers[0].ResolverID != "content_distribution" ||
			document.Resolvers[0].Kind != "record-attachment-by-source-record/v2" ||
			document.Resolvers[0].SourceRecordMatcher == nil ||
			document.Resolvers[0].SourceRecordMatcher.Kind != "record-identifier-segment/v2" ||
			document.Resolvers[0].SourceRecordMatcher.StartOffsetBytes == nil || *document.Resolvers[0].SourceRecordMatcher.StartOffsetBytes != 3 ||
			document.Resolvers[0].SourceRecordMatcher.LengthBytes != 12 ||
			len(document.Fields) != 1 || document.Fields[0].ParentAPIName != "Contract" ||
			document.Fields[0].FieldAPIName != "LegacyLink__c" || document.Fields[0].ResolverID != "content_distribution" {
			t.Fatalf("record-view resolver component drifted: %#v", document)
		}
	}
	if resolverComponents != 1 {
		t.Fatalf("record-view resolver component owners = %d, want exactly one", resolverComponents)
	}
	if bytes.Contains(firstRaw, []byte("metadata.pack.href-rewrite-fields.v1")) {
		t.Fatal("legacy href-rewrite component identity leaked into the immutable record-view AppRelease")
	}
	if got, err := appComponentKind("href_rewrite_fields", "0.7.0"); err != nil || got != crmRecordViewComponent {
		t.Fatalf("record-view AppRelease component kind = %q, %v; want neutral %s", got, err, crmRecordViewComponent)
	}
	expectedCapabilities := []struct {
		id      string
		handler string
	}{
		{crmAnalyticsReadCapability, crmAnalyticsReadHandlerRef},
		{crmApplicationNavigationCapability, crmApplicationNavigationHandlerRef},
		{crmApprovalLifecycleCapability, crmApprovalLifecycleHandlerRef},
		{crmApprovalFlowReadCapability, crmApprovalFlowReadHandlerRef},
		{crmApprovalReadCapability, crmApprovalReadHandlerRef},
		{crmAuditLogReadCapability, crmAuditLogReadHandlerRef},
		{crmBulkIngestCapability, crmBulkIngestHandlerRef},
		{crmEffectivePermissionsCapability, crmEffectivePermissionsHandlerRef},
		{crmFlowActionsCapability, crmFlowActionsHandlerRef},
		{crmLayoutAuthoringCapability, crmLayoutAuthoringHandlerRef},
		{crmMetadataAdminReadCapability, crmMetadataAdminReadHandlerRef},
		{crmPermissionAuthoringCapability, crmPermissionAuthoringHandlerRef},
		{crmPrivateListViewsCapability, crmPrivateListViewsHandlerRef},
		{crmQueryCapability, crmQueryHandlerRef},
		{crmFilesCapability, crmFilesHandlerRef},
		{crmMutationCapability, crmMutationHandlerRef},
		{crmRecycleBinMutationCapability, crmRecycleBinMutationHandlerRef},
		{crmRecycleBinCapability, crmRecycleBinHandlerRef},
		{crmRelatedCountCapability, crmRelatedCountHandlerRef},
		{crmSearchCapability, crmSearchHandlerRef},
		{crmUIAppsCapability, crmUIAppsHandlerRef},
		{crmRecordAccessCapability, crmRecordAccessHandlerRef},
		{crmRecordDefaultsCapability, crmRecordDefaultsHandlerRef},
		{
			crmRecordViewAttachmentSourceCapability,
			crmRecordViewAttachmentSourceHandlerRef,
		},
		{crmRecordViewCapability, crmRecordViewHandlerRef},
	}
	if first.Manifest.AppID != crmAppID || first.Manifest.PublisherID != crmPublisherID ||
		len(first.Manifest.Dependencies) != 0 ||
		len(first.Manifest.ProvidedCapabilities) != len(expectedCapabilities) {
		t.Fatalf("unexpected minimum CRM AppManifest: %#v", first.Manifest)
	}
	for index, expected := range expectedCapabilities {
		actual := first.Manifest.ProvidedCapabilities[index]
		if actual.CapabilityID != expected.id ||
			actual.HandlerRef != expected.handler {
			t.Fatalf(
				"CRM capability declarations drifted: %#v",
				first.Manifest.ProvidedCapabilities,
			)
		}
	}
	for capabilityID, wantRequirements := range map[string][]string{
		crmApplicationNavigationCapability: {navigationsemantic.CapabilityName},
		crmSearchCapability:                {catalogsemantic.AppMetadataV3CapabilityName, permissionsemantic.CapabilityName, querysemantic.CapabilityName, searchsemantic.CapabilityName},
		crmUIAppsCapability:                {catalogsemantic.AppMetadataV3CapabilityName, navigationsemantic.CapabilityName, permissionsemantic.CapabilityName},
		crmPermissionAuthoringCapability: {
			catalogsemantic.AppMetadataV3CapabilityName,
			permissionauthoringsemantic.RecordTypeObjectGroupsCapabilityName,
			permissionauthoringsemantic.CapabilityName,
		},
		crmApprovalFlowReadCapability: {
			approvalnotificationsemantic.CapabilityName,
			permissionsemantic.CapabilityName,
		},
		crmAuditLogReadCapability: {
			auditsemantic.CapabilityName,
			permissionsemantic.CapabilityName,
		},
		crmEffectivePermissionsCapability: {
			catalogsemantic.AppMetadataV3CapabilityName,
			pagelayoutsemantic.CapabilityName,
			permissionsemantic.CapabilityName,
		},
		crmRecordViewCapability: {
			catalogsemantic.AppMetadataV3CapabilityName,
			pagelayoutsemantic.CapabilityName,
			permissionsemantic.CapabilityName,
			accesssemantic.CapabilityName,
			viewsemantic.CapabilityName,
		},
		crmRecordDefaultsCapability: {
			catalogsemantic.AppMetadataV3CapabilityName,
			pagelayoutsemantic.CapabilityName,
			permissionsemantic.CapabilityName,
			defaultsemantic.CapabilityName,
			viewsemantic.CapabilityName,
		},
		crmMetadataAdminReadCapability: {
			crmMetadataAdminReadRuntimeCapability,
		},
		crmLayoutAuthoringCapability: {
			layoutauthoringv2semantic.CapabilityName,
		},
	} {
		index := slices.IndexFunc(
			first.Manifest.ProvidedCapabilities,
			func(capability appauthority.ProvidedCapability) bool {
				return capability.CapabilityID == capabilityID
			},
		)
		if index < 0 || !slices.Equal(
			first.Manifest.ProvidedCapabilities[index].RequiredRuntimeCapabilities,
			wantRequirements,
		) {
			t.Fatalf(
				"CRM current coarse handler %s requirements = %#v, want %#v",
				capabilityID,
				first.Manifest.ProvidedCapabilities[index].RequiredRuntimeCapabilities,
				wantRequirements,
			)
		}
	}
	if slices.ContainsFunc(
		first.Manifest.ProvidedCapabilities,
		func(capability appauthority.ProvidedCapability) bool {
			return capability.CapabilityID == crmSCIMCapability
		},
	) {
		t.Fatal("unfinished SCIM capability leaked into the current AppRelease")
	}
	if !slices.Equal(first.Manifest.RequiredEngineCapabilities, []string{
		catalogsemantic.AppMetadataV3CapabilityName,
		catalogsemantic.IntegrationBindingCapabilityName,
		reportsemantic.CapabilityName,
		navigationsemantic.CapabilityName,
		approvalsemantic.ExecuteCapabilityName,
		approvalnotificationsemantic.CapabilityName,
		approvalsemantic.ReadCapabilityName,
		auditsemantic.CapabilityName,
		flowsemantic.CapabilityName,
		commandsemantic.CapabilityName,
		crmMailboxAuthorizationRuntimeCapability,
		filessemantic.CapabilityName,
		sessionsemantic.CapabilityName,
		layoutauthoringv2semantic.CapabilityName,
		listviewsemantic.CapabilityName,
		crmMetadataAdminReadRuntimeCapability,
		pagelayoutsemantic.CapabilityName,
		outboundintentsemantic.CapabilityName,
		outboundintentsemantic.MailAttachmentDeliveryCapabilityName,
		permissionauthoringsemantic.RecordTypeObjectGroupsCapabilityName,
		permissionauthoringsemantic.CapabilityName,
		permissionsemantic.FieldCreateCapabilityName,
		permissionsemantic.CapabilityName,
		bulkingestsemantic.CapabilityName,
		accesssemantic.CapabilityName,
		recyclesemantic.CapabilityName,
		recyclemutationsemantic.CapabilityName,
		mutationsemantic.CapabilityName,
		defaultsemantic.CapabilityName,
		querysemantic.CapabilityName,
		viewsemantic.CapabilityName,
		relatedsemantic.CapabilityName,
		attachmentsemantic.CapabilityName,
		searchsemantic.CapabilityName,
	}) {
		t.Fatalf("CRM engine capability requirements drifted: %#v", first.Manifest.RequiredEngineCapabilities)
	}
	// Authorization-code operations deliberately extend the existing neutral
	// session lifecycle. Prove that the release declaration already covers the
	// OAuth handlers without inventing a duplicate deploy-time capability.
	if authorizationsemantic.CapabilityName != sessionsemantic.CapabilityName ||
		!slices.Contains(first.Manifest.RequiredEngineCapabilities, authorizationsemantic.CapabilityName) {
		t.Fatalf(
			"CRM authorization lifecycle capability %q is not covered by declared session capability %q",
			authorizationsemantic.CapabilityName, sessionsemantic.CapabilityName,
		)
	}
	if bytes.Contains(firstRaw, []byte("service_release")) || bytes.Contains(firstRaw, []byte("frontend_release")) || bytes.Contains(firstRaw, []byte("source_revision")) {
		t.Fatal("independent release-plane or provenance fact leaked into AppRelease bytes")
	}
	if _, verified, err := releaseartifact.ParsePackage(firstRaw); err != nil || verified.AppReleaseDigest != first.AppReleaseDigest {
		t.Fatalf("published carrier verification = %#v, %v", verified, err)
	}
	if first.Manifest.SemanticVersion == "" {
		t.Fatal("current CRM source produced an empty semantic version")
	}
}

func TestCurrentCRMReleaseMatchesCommittedArtifactAndSourceTree(t *testing.T) {
	packRoot := filepath.Join("..", "..", "salesforce-crm-standard")
	_, currentPackage, currentSourceTreeDigest, err := buildCRMRelease(packRoot)
	if err != nil {
		t.Fatal(err)
	}
	manifestRaw, err := os.ReadFile(filepath.Join(packRoot, "pack.json"))
	if err != nil {
		t.Fatal(err)
	}
	var manifest legacyPackManifest
	if err := json.Unmarshal(manifestRaw, &manifest); err != nil {
		t.Fatal(err)
	}
	releaseRoot := filepath.Join("..", "..", "releases", crmAppID, manifest.Version)
	committedPackage, err := os.ReadFile(filepath.Join(releaseRoot, "app-package.json"))
	if err != nil {
		t.Fatal(err)
	}
	if !bytes.Equal(currentPackage, committedPackage) {
		t.Fatal("current CRM pack does not rebuild the committed current-version AppRelease byte-for-byte")
	}
	attestationRaw, err := os.ReadFile(filepath.Join(releaseRoot, "publisher-attestation.json"))
	if err != nil {
		t.Fatal(err)
	}
	var attestation struct {
		Provenance struct {
			SourceTreeDigest string `json:"source_tree_digest"`
		} `json:"provenance"`
	}
	if err := json.Unmarshal(attestationRaw, &attestation); err != nil {
		t.Fatal(err)
	}
	if currentSourceTreeDigest != attestation.Provenance.SourceTreeDigest {
		t.Fatalf("current CRM source-tree digest = %s, attested = %s", currentSourceTreeDigest, attestation.Provenance.SourceTreeDigest)
	}
}

func TestCurrentCRMReleaseAttestedRevisionContainsCurrentFrontendCorrection(t *testing.T) {
	packRoot := filepath.Join("..", "..", "salesforce-crm-standard")
	manifestRaw, err := os.ReadFile(filepath.Join(packRoot, "pack.json"))
	if err != nil {
		t.Fatal(err)
	}
	var manifest legacyPackManifest
	if err := json.Unmarshal(manifestRaw, &manifest); err != nil {
		t.Fatal(err)
	}
	attestationRaw, err := os.ReadFile(filepath.Join(
		"..", "..", "releases", crmAppID, manifest.Version, "publisher-attestation.json",
	))
	if err != nil {
		t.Fatal(err)
	}
	attestation, err := releaseartifact.ParseAttestation(attestationRaw)
	if err != nil {
		t.Fatal(err)
	}
	for _, relativePath := range []string{
		"frontend/src/platform/components/RichTextEditor.tsx",
		"frontend/src/send-record-email-modal.tsx",
	} {
		current, err := os.ReadFile(filepath.Join(packRoot, filepath.FromSlash(relativePath)))
		if err != nil {
			t.Fatal(err)
		}
		attested := gitBlobAtRevision(
			t,
			filepath.Join("..", ".."),
			attestation.Provenance.SourceRevision,
			filepath.ToSlash(filepath.Join(attestation.Provenance.SourcePath, relativePath)),
		)
		if !bytes.Equal(current, attested) {
			t.Fatalf("current frontend source %s is absent from attested revision %s", relativePath, attestation.Provenance.SourceRevision)
		}
	}
}

func TestCurrentCRMReleasePublishesTicketPublicMutationClosure(t *testing.T) {
	packRoot := filepath.Join("..", "..", "salesforce-crm-standard")
	release, _, _, err := buildCRMRelease(packRoot)
	if err != nil {
		t.Fatal(err)
	}
	index := slices.IndexFunc(release.Bundle.Components, func(component appauthority.Component) bool {
		return component.ComponentKind == crmLegacyComponentKinds["object_record_mutation_policy"] &&
			component.BusinessKey == "IdentitySyncDeactivationTicket"
	})
	if index < 0 {
		t.Fatal("IdentitySyncDeactivationTicket public-mutation closure is absent from the immutable AppRelease")
	}
	var policy struct {
		ObjectAPIName  string `json:"objectApiName"`
		RecordMutation struct {
			Deny []struct {
				Surfaces []string `json:"surfaces"`
			} `json:"deny"`
		} `json:"recordMutation"`
	}
	if err := json.Unmarshal(release.ComponentBlobs[index].CanonicalBehavior, &policy); err != nil {
		t.Fatal(err)
	}
	wantSurfaces := []string{
		"singleCreate", "singleUpdate", "singleDelete", "externalIDUpsert",
		"collectionCreate", "collectionUpdate", "collectionDelete", "collectionUpsert", "treeCreate",
	}
	if policy.ObjectAPIName != "IdentitySyncDeactivationTicket" || len(policy.RecordMutation.Deny) != 1 ||
		!slices.Equal(policy.RecordMutation.Deny[0].Surfaces, wantSurfaces) {
		t.Fatalf("ticket public-mutation policy = %#v, want all nine surfaces", policy)
	}
}

func TestCurrentCRMReleaseVersionHasCompleteReviewedPlan(t *testing.T) {
	packRoot := filepath.Join("..", "..", "salesforce-crm-standard")
	manifestRaw, err := os.ReadFile(filepath.Join(packRoot, "pack.json"))
	if err != nil {
		t.Fatal(err)
	}
	var manifest legacyPackManifest
	if err := json.Unmarshal(manifestRaw, &manifest); err != nil {
		t.Fatal(err)
	}

	declarations, ok := crmReleaseCapabilities[manifest.Version]
	if !ok {
		t.Fatalf("current CRM version %q has no reviewed capability mapping", manifest.Version)
	}
	if _, ok := crmCatalogSelectionClosedVersions[manifest.Version]; !ok {
		t.Fatalf("current CRM version %q is missing the Catalog selection gate", manifest.Version)
	}
	if _, ok := crmCatalogDeployableVersions[manifest.Version]; !ok {
		t.Fatalf("current CRM version %q is missing the deployable Catalog gate", manifest.Version)
	}
	if got := runtimeFlowCommandSources(manifest.Version); len(got) != 3 {
		t.Fatalf("current CRM version %q Runtime Flow command sources = %#v, want V1, V2, and V3", manifest.Version, got)
	}
	consumerRequirements := crmConsumerRuntimeRequirements(manifest.Version)
	for _, capabilityID := range []string{
		sessionsemantic.CapabilityName,
		crmMailboxAuthorizationRuntimeCapability,
		outboundintentsemantic.CapabilityName,
	} {
		if !slices.Contains(consumerRequirements, capabilityID) {
			t.Fatalf(
				"current CRM version %q is missing consumer capability %q",
				manifest.Version, capabilityID,
			)
		}
	}
	if err := validateCatalogDeployableClosure(declarations, consumerRequirements); err != nil {
		t.Fatalf("current CRM version %q deployable Catalog closure: %v", manifest.Version, err)
	}
}

func TestCRMReleaseCapabilityEvolutionPreservesImmutablePriorReleasesAndBuildsNextCandidate(t *testing.T) {
	root := copyPackFixture(t)
	manifestPath := filepath.Join(root, "pack.json")
	manifestRaw, err := os.ReadFile(manifestPath)
	if err != nil {
		t.Fatal(err)
	}
	var manifest legacyPackManifest
	if err := json.Unmarshal(manifestRaw, &manifest); err != nil {
		t.Fatal(err)
	}
	manifest.Version = "0.2.0"
	writeJSON(t, manifestPath, manifest)

	release, _, _, err := buildCRMRelease(root)
	if err != nil {
		t.Fatal(err)
	}
	if len(release.Manifest.ProvidedCapabilities) != 1 ||
		release.Manifest.ProvidedCapabilities[0].CapabilityID != crmRecordAccessCapability ||
		release.Manifest.ProvidedCapabilities[0].HandlerRef != crmRecordAccessHandlerRef ||
		!slices.Equal(release.Manifest.RequiredEngineCapabilities, []string{accesssemantic.CapabilityName}) {
		t.Fatalf("immutable CRM 0.2.0 capability declaration drifted: %#v", release.Manifest)
	}

	manifest.Version = "0.3.0"
	writeJSON(t, manifestPath, manifest)
	release, _, _, err = buildCRMRelease(root)
	if err != nil {
		t.Fatal(err)
	}
	if len(release.Manifest.ProvidedCapabilities) != 2 ||
		release.Manifest.ProvidedCapabilities[0].CapabilityID != crmQueryCapability ||
		release.Manifest.ProvidedCapabilities[1].CapabilityID != crmRecordAccessCapability ||
		!slices.Equal(release.Manifest.RequiredEngineCapabilities, []string{accesssemantic.CapabilityName, querysemantic.CapabilityName}) {
		t.Fatalf("immutable CRM 0.3.0 capability declaration drifted: %#v", release.Manifest)
	}

	manifest.Version = "0.4.0"
	writeJSON(t, manifestPath, manifest)
	release, _, _, err = buildCRMRelease(root)
	if err != nil {
		t.Fatal(err)
	}
	if len(release.Manifest.ProvidedCapabilities) != 3 ||
		release.Manifest.ProvidedCapabilities[0].CapabilityID != crmQueryCapability ||
		release.Manifest.ProvidedCapabilities[1].CapabilityID != crmRelatedCountCapability ||
		release.Manifest.ProvidedCapabilities[2].CapabilityID != crmRecordAccessCapability ||
		!slices.Equal(release.Manifest.RequiredEngineCapabilities, []string{accesssemantic.CapabilityName, querysemantic.CapabilityName, relatedsemantic.CapabilityName}) {
		t.Fatalf("reviewed CRM 0.4.0 capability declaration drifted: %#v", release.Manifest)
	}

	manifest.Version = "0.5.0"
	writeJSON(t, manifestPath, manifest)
	release, _, _, err = buildCRMRelease(root)
	if err != nil {
		t.Fatal(err)
	}
	if len(release.Manifest.ProvidedCapabilities) != 4 ||
		release.Manifest.ProvidedCapabilities[0].CapabilityID != crmQueryCapability ||
		release.Manifest.ProvidedCapabilities[1].CapabilityID != crmRecycleBinCapability ||
		release.Manifest.ProvidedCapabilities[2].CapabilityID != crmRelatedCountCapability ||
		release.Manifest.ProvidedCapabilities[3].CapabilityID != crmRecordAccessCapability ||
		!slices.Equal(release.Manifest.RequiredEngineCapabilities, []string{accesssemantic.CapabilityName, recyclesemantic.CapabilityName, querysemantic.CapabilityName, relatedsemantic.CapabilityName}) {
		t.Fatalf("reviewed CRM 0.5.0 capability declaration drifted: %#v", release.Manifest)
	}

	manifest.Version = "0.6.0"
	writeJSON(t, manifestPath, manifest)
	release, _, _, err = buildCRMRelease(root)
	if err != nil {
		t.Fatal(err)
	}
	if len(release.Manifest.ProvidedCapabilities) != 5 ||
		release.Manifest.ProvidedCapabilities[0].CapabilityID != crmQueryCapability ||
		release.Manifest.ProvidedCapabilities[1].CapabilityID != crmRecycleBinCapability ||
		release.Manifest.ProvidedCapabilities[2].CapabilityID != crmRelatedCountCapability ||
		release.Manifest.ProvidedCapabilities[3].CapabilityID != crmRecordAccessCapability ||
		release.Manifest.ProvidedCapabilities[4].CapabilityID != crmRecordViewCapability ||
		!slices.Equal(release.Manifest.RequiredEngineCapabilities, []string{accesssemantic.CapabilityName, recyclesemantic.CapabilityName, querysemantic.CapabilityName, viewsemantic.CapabilityName, relatedsemantic.CapabilityName}) {
		t.Fatalf("reviewed CRM 0.6.0 capability declaration drifted: %#v", release.Manifest)
	}

	manifest.Version = "0.7.0"
	writeJSON(t, manifestPath, manifest)
	release, _, _, err = buildCRMRelease(root)
	if err != nil {
		t.Fatal(err)
	}
	if len(release.Manifest.ProvidedCapabilities) != 5 ||
		release.Manifest.ProvidedCapabilities[0].CapabilityID != crmQueryCapability ||
		release.Manifest.ProvidedCapabilities[1].CapabilityID != crmRecycleBinCapability ||
		release.Manifest.ProvidedCapabilities[2].CapabilityID != crmRelatedCountCapability ||
		release.Manifest.ProvidedCapabilities[3].CapabilityID != crmRecordAccessCapability ||
		release.Manifest.ProvidedCapabilities[4].CapabilityID != crmRecordViewCapability ||
		!slices.Equal(release.Manifest.RequiredEngineCapabilities, []string{accesssemantic.CapabilityName, recyclesemantic.CapabilityName, querysemantic.CapabilityName, viewsemantic.CapabilityName, relatedsemantic.CapabilityName}) {
		t.Fatalf("reviewed CRM 0.7.0 capability declaration drifted: %#v", release.Manifest)
	}

	manifest.Version = "0.8.0"
	writeJSON(t, manifestPath, manifest)
	release, _, _, err = buildCRMRelease(root)
	if err != nil {
		t.Fatal(err)
	}
	if len(release.Manifest.ProvidedCapabilities) != 6 ||
		release.Manifest.ProvidedCapabilities[0].CapabilityID != crmQueryCapability ||
		release.Manifest.ProvidedCapabilities[1].CapabilityID != crmMutationCapability ||
		release.Manifest.ProvidedCapabilities[2].CapabilityID != crmRecycleBinCapability ||
		release.Manifest.ProvidedCapabilities[3].CapabilityID != crmRelatedCountCapability ||
		release.Manifest.ProvidedCapabilities[4].CapabilityID != crmRecordAccessCapability ||
		release.Manifest.ProvidedCapabilities[5].CapabilityID != crmRecordViewCapability ||
		!slices.Equal(release.Manifest.RequiredEngineCapabilities, []string{accesssemantic.CapabilityName, recyclesemantic.CapabilityName, mutationsemantic.CapabilityName, querysemantic.CapabilityName, viewsemantic.CapabilityName, relatedsemantic.CapabilityName}) {
		t.Fatalf("reviewed CRM 0.8.0 mutation capability declaration drifted: %#v", release.Manifest)
	}

	manifest.Version = "0.9.0"
	writeJSON(t, manifestPath, manifest)
	release, _, _, err = buildCRMRelease(root)
	if err != nil {
		t.Fatal(err)
	}
	if len(release.Manifest.ProvidedCapabilities) != 7 ||
		release.Manifest.ProvidedCapabilities[0].CapabilityID != crmQueryCapability ||
		release.Manifest.ProvidedCapabilities[1].CapabilityID != crmMutationCapability ||
		release.Manifest.ProvidedCapabilities[2].CapabilityID != crmRecycleBinCapability ||
		release.Manifest.ProvidedCapabilities[3].CapabilityID != crmRelatedCountCapability ||
		release.Manifest.ProvidedCapabilities[4].CapabilityID != crmRecordAccessCapability ||
		release.Manifest.ProvidedCapabilities[5].CapabilityID != crmRecordViewAttachmentSourceCapability ||
		release.Manifest.ProvidedCapabilities[6].CapabilityID != crmRecordViewCapability ||
		!slices.Equal(release.Manifest.RequiredEngineCapabilities, []string{accesssemantic.CapabilityName, recyclesemantic.CapabilityName, mutationsemantic.CapabilityName, querysemantic.CapabilityName, viewsemantic.CapabilityName, relatedsemantic.CapabilityName, attachmentsemantic.CapabilityName}) {
		t.Fatalf("CRM 0.9.0 attachment-proof capability declaration drifted: %#v", release.Manifest)
	}

	manifest.Version = "0.10.0"
	writeJSON(t, manifestPath, manifest)
	release, _, _, err = buildCRMRelease(root)
	if err != nil {
		t.Fatal(err)
	}
	if len(release.Manifest.ProvidedCapabilities) != 7 ||
		release.Manifest.ProvidedCapabilities[5].CapabilityID != crmRecordViewAttachmentSourceCapability ||
		!slices.Contains(release.Manifest.RequiredEngineCapabilities, attachmentsemantic.CapabilityName) {
		t.Fatalf("CRM 0.10.0 source-closed attachment capability declaration drifted: %#v", release.Manifest)
	}

	manifest.Version = "0.11.0"
	writeJSON(t, manifestPath, manifest)
	release, _, _, err = buildCRMRelease(root)
	if err != nil {
		t.Fatal(err)
	}
	if len(release.Manifest.ProvidedCapabilities) != 8 ||
		release.Manifest.ProvidedCapabilities[1].CapabilityID != crmFilesCapability ||
		!slices.Contains(release.Manifest.RequiredEngineCapabilities, filessemantic.CapabilityName) {
		t.Fatalf("CRM 0.11.0 record-files capability declaration drifted: %#v", release.Manifest)
	}

	manifest.Version = "0.12.0"
	writeJSON(t, manifestPath, manifest)
	release, _, _, err = buildCRMRelease(root)
	if err != nil {
		t.Fatal(err)
	}
	if len(release.Manifest.ProvidedCapabilities) != 10 ||
		!slices.Contains(
			release.Manifest.RequiredEngineCapabilities,
			permissionsemantic.CapabilityName,
		) ||
		!slices.Contains(
			release.Manifest.RequiredEngineCapabilities,
			catalogsemantic.AppMetadataCapabilityName,
		) {
		t.Fatalf(
			"CRM 0.12.0 permission metadata capability declaration drifted: %#v",
			release.Manifest,
		)
	}
	searchScopeIndex := slices.IndexFunc(
		release.Manifest.ProvidedCapabilities,
		func(capability appauthority.ProvidedCapability) bool {
			return capability.CapabilityID == crmSearchScopeOrderCapability
		},
	)
	if searchScopeIndex < 0 {
		t.Fatalf(
			"CRM 0.12.0 search-scope capability is absent: %#v",
			release.Manifest,
		)
	}
	searchScope := release.Manifest.ProvidedCapabilities[searchScopeIndex]
	if !slices.Equal(searchScope.RequiredRuntimeCapabilities, []string{
		catalogsemantic.AppMetadataCapabilityName,
		permissionsemantic.CapabilityName,
	}) {
		t.Fatalf(
			"CRM 0.12.0 search-scope dependencies = %#v",
			searchScope.RequiredRuntimeCapabilities,
		)
	}

	manifest.Version = "0.13.0"
	writeJSON(t, manifestPath, manifest)
	release, _, _, err = buildCRMRelease(root)
	if err != nil {
		t.Fatal(err)
	}
	searchScopeIndex = slices.IndexFunc(
		release.Manifest.ProvidedCapabilities,
		func(capability appauthority.ProvidedCapability) bool {
			return capability.CapabilityID == crmSearchScopeOrderCapability
		},
	)
	if searchScopeIndex < 0 {
		t.Fatalf(
			"CRM 0.13.0 search-scope capability is absent: %#v",
			release.Manifest,
		)
	}
	searchScope = release.Manifest.ProvidedCapabilities[searchScopeIndex]
	if !slices.Equal(searchScope.RequiredRuntimeCapabilities, []string{
		catalogsemantic.AppMetadataCapabilityName,
		searchsemantic.CapabilityName,
	}) {
		t.Fatalf(
			"CRM 0.13.0 search-scope dependencies = %#v",
			searchScope.RequiredRuntimeCapabilities,
		)
	}

	manifest.Version = "0.14.0"
	writeJSON(t, manifestPath, manifest)
	release, _, _, err = buildCRMRelease(root)
	if err != nil {
		t.Fatal(err)
	}
	searchIndex := slices.IndexFunc(
		release.Manifest.ProvidedCapabilities,
		func(capability appauthority.ProvidedCapability) bool {
			return capability.CapabilityID == crmSearchCapability
		},
	)
	if searchIndex < 0 {
		t.Fatalf(
			"CRM 0.14.0 search capability is absent: %#v",
			release.Manifest,
		)
	}
	search := release.Manifest.ProvidedCapabilities[searchIndex]
	if search.HandlerRef != crmSearchHandlerRef ||
		!slices.Equal(search.RequiredRuntimeCapabilities, []string{
			catalogsemantic.AppMetadataV2CapabilityName,
			querysemantic.CapabilityName,
			searchsemantic.CapabilityName,
		}) {
		t.Fatalf(
			"CRM 0.14.0 search declaration = %#v",
			search,
		)
	}

	manifest.Version = "0.15.0"
	writeJSON(t, manifestPath, manifest)
	if err := os.MkdirAll(
		filepath.Join(root, "app-release"),
		0o755,
	); err != nil {
		t.Fatal(err)
	}
	if err := os.WriteFile(
		filepath.Join(root, crmRuntimeFlowCommandV1Source),
		[]byte(`{"commandKind":"runtime.flow.execute","payloadFormatVersion":1}`),
		0o644,
	); err != nil {
		t.Fatal(err)
	}
	release, _, _, err = buildCRMRelease(root)
	if err != nil {
		t.Fatal(err)
	}
	if slices.IndexFunc(
		release.Bundle.Components,
		func(component appauthority.Component) bool {
			return component.ComponentKind == crmRuntimeCommandComponent &&
				component.BusinessKey == flowcommandsemantic.CommandKind
		},
	) < 0 {
		t.Fatal("CRM 0.15.0 Runtime Flow command component is absent")
	}
	if slices.Contains(
		release.Manifest.RequiredEngineCapabilities,
		commandsemantic.CapabilityName,
	) {
		t.Fatal("immutable CRM 0.15.0 unexpectedly changed its Runtime requirements")
	}

	manifest.Version = "0.16.0"
	writeJSON(t, manifestPath, manifest)
	release, _, _, err = buildCRMRelease(root)
	if err != nil {
		t.Fatal(err)
	}
	if !slices.Contains(
		release.Manifest.RequiredEngineCapabilities,
		commandsemantic.CapabilityName,
	) || slices.IndexFunc(
		release.Manifest.ProvidedCapabilities,
		func(capability appauthority.ProvidedCapability) bool {
			return slices.Contains(
				capability.RequiredRuntimeCapabilities,
				commandsemantic.CapabilityName,
			)
		},
	) >= 0 {
		t.Fatalf(
			"CRM 0.16.0 command consumer requirement leaked into an App handler: %#v",
			release.Manifest,
		)
	}

	manifest.Version = "0.17.0"
	writeJSON(t, manifestPath, manifest)
	release, _, _, err = buildCRMRelease(root)
	if err != nil {
		t.Fatal(err)
	}
	for _, capabilityID := range []string{
		catalogsemantic.IntegrationBindingCapabilityName,
		commandsemantic.CapabilityName,
	} {
		if !slices.Contains(
			release.Manifest.RequiredEngineCapabilities,
			capabilityID,
		) || slices.IndexFunc(
			release.Manifest.ProvidedCapabilities,
			func(capability appauthority.ProvidedCapability) bool {
				return slices.Contains(
					capability.RequiredRuntimeCapabilities,
					capabilityID,
				)
			},
		) >= 0 {
			t.Fatalf(
				"CRM 0.17.0 consumer requirement %q leaked into an App handler: %#v",
				capabilityID,
				release.Manifest,
			)
		}
	}
	v1ComponentIndex := slices.IndexFunc(
		release.Bundle.Components,
		func(component appauthority.Component) bool {
			return component.ComponentKind == crmRuntimeCommandComponent &&
				component.BusinessKey == flowcommandsemantic.CommandKind
		},
	)
	if v1ComponentIndex < 0 {
		t.Fatal("CRM 0.17.0 V1 Runtime Flow command component is absent")
	}
	v1ComponentID := release.Bundle.Components[v1ComponentIndex].ComponentID
	if err := os.WriteFile(
		filepath.Join(root, crmRuntimeFlowCommandV2Source),
		[]byte(`{"commandKind":"runtime.flow.execute","payloadFormatVersion":2}`),
		0o644,
	); err != nil {
		t.Fatal(err)
	}
	manifest.Version = "0.18.0"
	writeJSON(t, manifestPath, manifest)
	release, _, _, err = buildCRMRelease(root)
	if err != nil {
		t.Fatal(err)
	}
	registrations := map[uint32]appauthority.Component{}
	for index, component := range release.Bundle.Components {
		if component.ComponentKind != crmRuntimeCommandComponent {
			continue
		}
		var command struct {
			CommandKind          string `json:"commandKind"`
			PayloadFormatVersion uint32 `json:"payloadFormatVersion"`
		}
		if err := json.Unmarshal(release.ComponentBlobs[index].CanonicalBehavior, &command); err != nil {
			t.Fatal(err)
		}
		if command.CommandKind != flowcommandsemantic.CommandKind {
			t.Fatalf("CRM 0.18.0 command kind = %q", command.CommandKind)
		}
		registrations[command.PayloadFormatVersion] = component
	}
	if len(registrations) != 2 ||
		registrations[flowcommandsemantic.PayloadFormatVersion].ComponentID != v1ComponentID ||
		registrations[flowcommandsemanticv2.PayloadFormatVersion].BusinessKey != flowcommandsemantic.CommandKind+"/v2" {
		t.Fatalf("CRM 0.18.0 Runtime command registrations = %#v", registrations)
	}

	manifest.Version = "0.19.0"
	writeJSON(t, manifestPath, manifest)
	release, _, _, err = buildCRMRelease(root)
	if err != nil {
		t.Fatal(err)
	}
	flowActionsIndex := slices.IndexFunc(
		release.Manifest.ProvidedCapabilities,
		func(capability appauthority.ProvidedCapability) bool {
			return capability.CapabilityID == crmFlowActionsCapability
		},
	)
	if flowActionsIndex < 0 {
		t.Fatalf("CRM 0.19.0 Flow Actions capability is absent: %#v", release.Manifest)
	}
	flowActions := release.Manifest.ProvidedCapabilities[flowActionsIndex]
	if flowActions.HandlerRef != crmFlowActionsHandlerRef ||
		!slices.Equal(flowActions.RequiredRuntimeCapabilities, []string{
			catalogsemantic.AppMetadataCapabilityName,
			flowsemantic.CapabilityName,
			permissionsemantic.CapabilityName,
		}) {
		t.Fatalf("CRM 0.19.0 Flow Actions declaration = %#v", flowActions)
	}

	manifest.Version = "0.20.0"
	writeJSON(t, manifestPath, manifest)
	release, _, _, err = buildCRMRelease(root)
	if err != nil {
		t.Fatal(err)
	}
	analyticsReadIndex := slices.IndexFunc(
		release.Manifest.ProvidedCapabilities,
		func(capability appauthority.ProvidedCapability) bool {
			return capability.CapabilityID == crmAnalyticsReadCapability
		},
	)
	if analyticsReadIndex < 0 {
		t.Fatalf("CRM 0.20.0 analytics read capability is absent: %#v", release.Manifest)
	}
	analyticsRead := release.Manifest.ProvidedCapabilities[analyticsReadIndex]
	if analyticsRead.HandlerRef != crmAnalyticsReadHandlerRef ||
		!slices.Equal(analyticsRead.RequiredRuntimeCapabilities, []string{
			catalogsemantic.AppMetadataV2CapabilityName,
			reportsemantic.CapabilityName,
			permissionsemantic.CapabilityName,
			querysemantic.CapabilityName,
		}) {
		t.Fatalf("CRM 0.20.0 analytics read declaration = %#v", analyticsRead)
	}

	manifest.Version = "0.21.0"
	writeJSON(t, manifestPath, manifest)
	release, _, _, err = buildCRMRelease(root)
	if err != nil {
		t.Fatal(err)
	}
	approvalLifecycleIndex := slices.IndexFunc(
		release.Manifest.ProvidedCapabilities,
		func(capability appauthority.ProvidedCapability) bool {
			return capability.CapabilityID == crmApprovalLifecycleCapability
		},
	)
	if approvalLifecycleIndex < 0 {
		t.Fatalf("CRM 0.21.0 Approval lifecycle capability is absent: %#v", release.Manifest)
	}
	approvalLifecycle := release.Manifest.ProvidedCapabilities[approvalLifecycleIndex]
	if approvalLifecycle.HandlerRef != crmApprovalLifecycleHandlerRef ||
		!slices.Equal(approvalLifecycle.RequiredRuntimeCapabilities, []string{
			approvalsemantic.ExecuteCapabilityName,
			approvalsemantic.ReadCapabilityName,
		}) {
		t.Fatalf("CRM 0.21.0 Approval lifecycle declaration = %#v", approvalLifecycle)
	}

	manifest.Version = "0.22.0"
	writeJSON(t, manifestPath, manifest)
	release, _, _, err = buildCRMRelease(root)
	if err != nil {
		t.Fatal(err)
	}
	uiAppsIndex := slices.IndexFunc(
		release.Manifest.ProvidedCapabilities,
		func(capability appauthority.ProvidedCapability) bool {
			return capability.CapabilityID == crmUIAppsCapability
		},
	)
	if uiAppsIndex < 0 {
		t.Fatalf("CRM 0.22.0 UI Apps capability is absent: %#v", release.Manifest)
	}
	uiApps := release.Manifest.ProvidedCapabilities[uiAppsIndex]
	if uiApps.HandlerRef != crmUIAppsHandlerRef ||
		!slices.Equal(uiApps.RequiredRuntimeCapabilities, []string{
			catalogsemantic.AppMetadataV2CapabilityName,
			permissionsemantic.CapabilityName,
		}) {
		t.Fatalf("CRM 0.22.0 UI Apps declaration = %#v", uiApps)
	}

	manifest.Version = "0.23.0"
	writeJSON(t, manifestPath, manifest)
	release, _, _, err = buildCRMRelease(root)
	if err != nil {
		t.Fatal(err)
	}
	standardActionsIndex := slices.IndexFunc(
		release.Manifest.ProvidedCapabilities,
		func(capability appauthority.ProvidedCapability) bool {
			return capability.CapabilityID == crmStandardActionsCapability
		},
	)
	if standardActionsIndex < 0 {
		t.Fatalf("CRM 0.23.0 Standard Actions capability is absent: %#v", release.Manifest)
	}
	standardActions := release.Manifest.ProvidedCapabilities[standardActionsIndex]
	if standardActions.HandlerRef != crmStandardActionsHandlerRef ||
		!slices.Equal(standardActions.RequiredRuntimeCapabilities, []string{
			catalogsemantic.AppMetadataCapabilityName,
			flowsemantic.CapabilityName,
			permissionsemantic.CapabilityName,
		}) {
		t.Fatalf("CRM 0.23.0 Standard Actions declaration = %#v", standardActions)
	}

	manifest.Version = "0.24.0"
	writeJSON(t, manifestPath, manifest)
	release, _, _, err = buildCRMRelease(root)
	if err != nil {
		t.Fatal(err)
	}
	if slices.ContainsFunc(
		release.Manifest.ProvidedCapabilities,
		func(capability appauthority.ProvidedCapability) bool {
			return capability.CapabilityID == crmStandardActionsCapability
		},
	) {
		t.Fatalf("CRM 0.24.0 retained duplicate Standard Actions handler: %#v", release.Manifest)
	}
	flowActionsIndex = slices.IndexFunc(
		release.Manifest.ProvidedCapabilities,
		func(capability appauthority.ProvidedCapability) bool {
			return capability.CapabilityID == crmFlowActionsCapability
		},
	)
	if flowActionsIndex < 0 || len(release.Manifest.ProvidedCapabilities) != 15 {
		t.Fatalf("CRM 0.24.0 coarse action handler closure = %#v", release.Manifest)
	}
	if err := validateCatalogSelectionClosure(
		crmReleaseCapabilities[manifest.Version],
	); err != nil {
		t.Fatalf("CRM 0.24.0 Catalog selection closure: %v", err)
	}

	manifest.Version = "0.25.0"
	writeJSON(t, manifestPath, manifest)
	release, _, _, err = buildCRMRelease(root)
	if err != nil {
		t.Fatal(err)
	}
	if len(release.Manifest.ProvidedCapabilities) != 16 {
		t.Fatalf("CRM 0.25.0 deployable handler closure = %#v", release.Manifest)
	}
	for capabilityID, runtimeCapability := range map[string]string{
		crmApprovalLifecycleCapability: approvalsemantic.ExecuteCapabilityName,
		crmApprovalReadCapability:      approvalsemantic.ReadCapabilityName,
	} {
		index := slices.IndexFunc(
			release.Manifest.ProvidedCapabilities,
			func(capability appauthority.ProvidedCapability) bool {
				return capability.CapabilityID == capabilityID
			},
		)
		if index < 0 || !slices.Equal(
			release.Manifest.ProvidedCapabilities[index].RequiredRuntimeCapabilities,
			[]string{runtimeCapability},
		) {
			t.Fatalf(
				"CRM 0.25.0 Approval capability %s = %#v",
				capabilityID, release.Manifest.ProvidedCapabilities,
			)
		}
	}
	if err := validateCatalogDeployableClosure(
		crmReleaseCapabilities[manifest.Version],
		crmConsumerRuntimeRequirements(manifest.Version),
	); err != nil {
		t.Fatalf("CRM 0.25.0 deployable Catalog closure: %v", err)
	}
}

func TestCRMReleaseCatalogSelectionClosureRejectsUnmatchableHandler(t *testing.T) {
	declarations := slices.Clone(crmReleaseCapabilities["0.24.0"])
	declarations = append(declarations, releaseCapability{
		capabilityID:      "io.nexuscrm.sfcrm.duplicate-actions.v1",
		handlerRef:        "io.nexuscrm.sfcrm.handler.duplicate-actions.v1",
		runtimeCapability: flowsemantic.CapabilityName,
		additionalRuntimeCapabilities: []string{
			catalogsemantic.AppMetadataCapabilityName,
			permissionsemantic.CapabilityName,
		},
	})
	if err := validateCatalogSelectionClosure(declarations); err == nil ||
		!strings.Contains(err.Error(), "has no unique Catalog serving selection") {
		t.Fatalf("unmatchable Catalog selection error = %v", err)
	}
}

func TestCRMReleaseDeployableClosureRejectsUnownedHandlerDependency(t *testing.T) {
	err := validateCatalogDeployableClosure(
		crmReleaseCapabilities["0.24.0"],
		crmConsumerRuntimeRequirements("0.24.0"),
	)
	if err == nil || !strings.Contains(
		err.Error(),
		"runtime.approvals.read.v1\" without a serving selection or supported consumer-only owner",
	) {
		t.Fatalf("0.24.0 hidden Approval read owner error = %v", err)
	}
}

func TestCRMReleaseBuildRejectsMissingRecordViewSourceSchema(t *testing.T) {
	root := copyPackFixture(t)
	specPath := filepath.Join(root, "objects", "standard_objects_spec.json")
	raw, err := os.ReadFile(specPath)
	if err != nil {
		t.Fatal(err)
	}
	var specs []map[string]any
	if err := json.Unmarshal(raw, &specs); err != nil {
		t.Fatal(err)
	}
	for i, spec := range specs {
		if spec["apiName"] == "ContentDistribution" {
			specs = append(specs[:i], specs[i+1:]...)
			break
		}
	}
	writeJSON(t, specPath, specs)
	if _, _, _, err := buildCRMRelease(root); err == nil || !strings.Contains(err.Error(), `source object "ContentDistribution" is absent`) {
		t.Fatalf("missing source object error = %v", err)
	}

	copyPackFixtureTo(t, root)
	raw, err = os.ReadFile(specPath)
	if err != nil {
		t.Fatal(err)
	}
	specs = nil
	if err := json.Unmarshal(raw, &specs); err != nil {
		t.Fatal(err)
	}
	for _, spec := range specs {
		if spec["apiName"] != "ContentDistribution" {
			continue
		}
		fields := spec["fields"].([]any)
		fields[0].(map[string]any)["apiName"] = "WrongContentVersionId"
	}
	writeJSON(t, specPath, specs)
	if _, _, _, err := buildCRMRelease(root); err == nil || !strings.Contains(err.Error(), "ContentDistribution.ContentVersionId is absent") {
		t.Fatalf("missing source field error = %v", err)
	}
}

func TestMetadataAdminReadReleaseDeclaresWorkloadOnlyCapability(t *testing.T) {
	required, provided, err := appCapabilities("0.46.0")
	if err != nil {
		t.Fatal(err)
	}
	const (
		appCapability     = "io.nexuscrm.sfcrm.metadata-admin-read.v1"
		runtimeCapability = "runtime.metadata.read-admin.v1"
	)
	if !slices.Contains(required, runtimeCapability) {
		t.Fatalf("0.46.0 Runtime requirements = %v, want %s", required, runtimeCapability)
	}
	index := slices.IndexFunc(provided, func(capability appauthority.ProvidedCapability) bool {
		return capability.CapabilityID == appCapability
	})
	if index < 0 {
		t.Fatalf("0.46.0 App capabilities omit %s", appCapability)
	}
	if provided[index].HandlerRef != "io.nexuscrm.sfcrm.handler.metadata-admin-read.v1" ||
		!slices.Equal(provided[index].RequiredRuntimeCapabilities, []string{runtimeCapability}) {
		t.Fatalf("metadata administration read declaration = %#v", provided[index])
	}
	wantCommands := []runtimeFlowCommandSource{
		{path: crmRuntimeFlowCommandV1Source, businessKey: flowcommandsemantic.CommandKind},
		{path: crmRuntimeFlowCommandV2Source, businessKey: flowcommandsemantic.CommandKind + "/v2"},
		{path: crmRuntimeFlowCommandV3Source, businessKey: flowcommandsemantic.CommandKind + "/v3"},
	}
	if got := runtimeFlowCommandSources("0.46.0"); !slices.Equal(got, wantCommands) {
		t.Fatalf("0.46.0 Runtime Flow command sources = %#v, want %#v", got, wantCommands)
	}
}

func TestEffectiveListFieldsReleasePreservesCapabilityClosure(t *testing.T) {
	wantRequired, wantProvided, err := appCapabilities("0.46.0")
	if err != nil {
		t.Fatal(err)
	}
	gotRequired, gotProvided, err := appCapabilities("0.47.0")
	if err != nil {
		t.Fatal(err)
	}
	if !slices.Equal(gotRequired, wantRequired) || !slices.EqualFunc(
		gotProvided,
		wantProvided,
		func(left, right appauthority.ProvidedCapability) bool {
			return left.CapabilityID == right.CapabilityID &&
				left.Kind == right.Kind &&
				left.HandlerRef == right.HandlerRef &&
				slices.Equal(left.RequiredRuntimeCapabilities, right.RequiredRuntimeCapabilities)
		},
	) {
		t.Fatalf(
			"0.47.0 capability closure = (%v, %#v), want (%v, %#v)",
			gotRequired,
			gotProvided,
			wantRequired,
			wantProvided,
		)
	}
	if !slices.Equal(runtimeFlowCommandSources("0.47.0"), runtimeFlowCommandSources("0.46.0")) {
		t.Fatal("0.47.0 Runtime Flow command registrations drifted from 0.46.0")
	}
}

func TestApprovalNotificationFlowsReleaseDeclaresWorkloadOnlyReadAndPermission(t *testing.T) {
	wantRequired, wantProvided, err := appCapabilities("0.47.0")
	if err != nil {
		t.Fatal(err)
	}
	gotRequired, gotProvided, err := appCapabilities("0.48.0")
	if err != nil {
		t.Fatal(err)
	}
	if len(gotProvided) != len(wantProvided)+1 {
		t.Fatalf("0.48.0 provided capabilities = %#v, want one addition to %#v", gotProvided, wantProvided)
	}
	index := slices.IndexFunc(gotProvided, func(capability appauthority.ProvidedCapability) bool {
		return capability.CapabilityID == crmApprovalFlowReadCapability
	})
	if index < 0 {
		t.Fatalf("0.48.0 App capabilities omit %s", crmApprovalFlowReadCapability)
	}
	if gotProvided[index].HandlerRef != crmApprovalFlowReadHandlerRef ||
		!slices.Equal(gotProvided[index].RequiredRuntimeCapabilities, []string{
			approvalnotificationsemantic.CapabilityName,
			permissionsemantic.CapabilityName,
		}) {
		t.Fatalf("approval notification Flow declaration = %#v", gotProvided[index])
	}
	if !slices.Contains(gotRequired, approvalnotificationsemantic.CapabilityName) ||
		len(gotRequired) != len(wantRequired)+1 {
		t.Fatalf("0.48.0 Runtime requirements = %v, want %s added to %v", gotRequired, approvalnotificationsemantic.CapabilityName, wantRequired)
	}
	if !slices.Equal(runtimeFlowCommandSources("0.48.0"), runtimeFlowCommandSources("0.47.0")) {
		t.Fatal("0.48.0 Runtime Flow command registrations drifted from 0.47.0")
	}
	if !slices.Equal(crmConsumerRuntimeRequirements("0.48.0"), crmConsumerRuntimeRequirements("0.47.0")) {
		t.Fatal("0.48.0 consumer-only Runtime requirements drifted from 0.47.0")
	}
}

func TestConvertPreviewCapabilityCorrectionReleasePreservesCapabilityClosure(t *testing.T) {
	wantRequired, wantProvided, err := appCapabilities("0.48.0")
	if err != nil {
		t.Fatal(err)
	}
	gotRequired, gotProvided, err := appCapabilities("0.49.0")
	if err != nil {
		t.Fatal(err)
	}
	if !slices.Equal(gotRequired, wantRequired) ||
		!slices.EqualFunc(gotProvided, wantProvided, func(left, right appauthority.ProvidedCapability) bool {
			return left.CapabilityID == right.CapabilityID &&
				left.Kind == right.Kind &&
				left.HandlerRef == right.HandlerRef &&
				slices.Equal(left.RequiredRuntimeCapabilities, right.RequiredRuntimeCapabilities)
		}) {
		t.Fatalf(
			"0.49.0 capability closure = (%v, %#v), want unchanged (%v, %#v)",
			gotRequired, gotProvided, wantRequired, wantProvided,
		)
	}
	if !slices.Equal(runtimeFlowCommandSources("0.49.0"), runtimeFlowCommandSources("0.48.0")) {
		t.Fatal("0.49.0 Runtime Flow command registrations drifted from 0.48.0")
	}
	if !slices.Equal(crmConsumerRuntimeRequirements("0.49.0"), crmConsumerRuntimeRequirements("0.48.0")) {
		t.Fatal("0.49.0 consumer-only Runtime requirements drifted from 0.48.0")
	}
}

func TestBulkIngestReleaseDeclaresOneCompleteRuntimeLifecycle(t *testing.T) {
	required, provided, err := appCapabilities("0.50.0")
	if err != nil {
		t.Fatal(err)
	}
	for _, capability := range []string{
		bulkingestsemantic.CapabilityName,
		mutationsemantic.CapabilityName,
	} {
		if !slices.Contains(required, capability) {
			t.Fatalf("0.50.0 Runtime requirements = %v, want %s", required, capability)
		}
	}
	index := slices.IndexFunc(provided, func(capability appauthority.ProvidedCapability) bool {
		return capability.CapabilityID == crmBulkIngestCapability
	})
	if index < 0 {
		t.Fatalf("0.50.0 App capabilities omit %s", crmBulkIngestCapability)
	}
	if provided[index].HandlerRef != crmBulkIngestHandlerRef ||
		!slices.Equal(provided[index].RequiredRuntimeCapabilities, []string{
			bulkingestsemantic.CapabilityName,
			mutationsemantic.CapabilityName,
		}) {
		t.Fatalf("Bulk Ingest declaration = %#v", provided[index])
	}
	if !slices.Equal(runtimeFlowCommandSources("0.50.0"), runtimeFlowCommandSources("0.49.0")) {
		t.Fatal("0.50.0 Runtime Flow command registrations drifted from 0.49.0")
	}
	if !slices.Equal(crmConsumerRuntimeRequirements("0.50.0"), crmConsumerRuntimeRequirements("0.49.0")) {
		t.Fatal("0.50.0 consumer-only Runtime requirements drifted from 0.49.0")
	}
}

func TestPageLayoutResolutionReleaseDeclaresSharedHandlerDependencyAndConsumerGrant(t *testing.T) {
	wantRequired, wantProvided, err := appCapabilities("0.50.0")
	if err != nil {
		t.Fatal(err)
	}
	gotRequired, gotProvided, err := appCapabilities("0.51.0")
	if err != nil {
		t.Fatal(err)
	}
	wantRequired = append(wantRequired, pagelayoutsemantic.CapabilityName)
	slices.Sort(wantRequired)
	if !slices.Equal(gotRequired, wantRequired) || len(gotProvided) != len(wantProvided) {
		t.Fatalf("0.51.0 handler capability closure = (%v, %#v), want required %v and %d handlers", gotRequired, gotProvided, wantRequired, len(wantProvided))
	}
	for index := range wantProvided {
		want := wantProvided[index]
		if want.CapabilityID == crmEffectivePermissionsCapability ||
			want.CapabilityID == crmRecordViewCapability {
			want.RequiredRuntimeCapabilities = append(
				slices.Clone(want.RequiredRuntimeCapabilities),
				pagelayoutsemantic.CapabilityName,
			)
			slices.Sort(want.RequiredRuntimeCapabilities)
		}
		if got := gotProvided[index]; got.CapabilityID != want.CapabilityID ||
			got.Kind != want.Kind || got.HandlerRef != want.HandlerRef ||
			!slices.Equal(got.RequiredRuntimeCapabilities, want.RequiredRuntimeCapabilities) {
			t.Fatalf("0.51.0 handler[%d] = %#v, want %#v", index, got, want)
		}
	}
	wantConsumers := append(
		slices.Clone(crmConsumerRuntimeRequirements("0.50.0")),
		pagelayoutsemantic.CapabilityName,
	)
	slices.Sort(wantConsumers)
	if gotConsumers := crmConsumerRuntimeRequirements("0.51.0"); !slices.Equal(gotConsumers, wantConsumers) {
		t.Fatalf("0.51.0 consumer Runtime requirements = %v, want %v", gotConsumers, wantConsumers)
	}
	if !slices.Equal(runtimeFlowCommandSources("0.51.0"), runtimeFlowCommandSources("0.50.0")) {
		t.Fatal("0.51.0 Runtime Flow command registrations drifted from 0.50.0")
	}
}

func TestRecordDefaultsReleaseDeclaresOneCompleteRuntimeReadPath(t *testing.T) {
	for _, version := range []string{"0.52.0", "0.53.0"} {
		required, provided, err := appCapabilities(version)
		if err != nil {
			t.Fatal(err)
		}
		for _, capability := range []string{
			defaultsemantic.CapabilityName,
			catalogsemantic.AppMetadataV3CapabilityName,
			pagelayoutsemantic.CapabilityName,
			permissionsemantic.CapabilityName,
		} {
			if !slices.Contains(required, capability) {
				t.Fatalf("%s Runtime requirements = %v, want %s", version, required, capability)
			}
		}
		index := slices.IndexFunc(provided, func(capability appauthority.ProvidedCapability) bool {
			return capability.CapabilityID == crmRecordDefaultsCapability
		})
		if index < 0 {
			t.Fatalf("%s App capabilities omit %s", version, crmRecordDefaultsCapability)
		}
		if provided[index].HandlerRef != crmRecordDefaultsHandlerRef ||
			!slices.Equal(provided[index].RequiredRuntimeCapabilities, []string{
				catalogsemantic.AppMetadataV3CapabilityName,
				pagelayoutsemantic.CapabilityName,
				permissionsemantic.CapabilityName,
				defaultsemantic.CapabilityName,
			}) {
			t.Fatalf("%s Record Defaults declaration = %#v", version, provided[index])
		}
	}
	if !slices.Equal(runtimeFlowCommandSources("0.52.0"), runtimeFlowCommandSources("0.51.0")) {
		t.Fatal("0.52.0 Runtime Flow command registrations drifted from 0.51.0")
	}
	if !slices.Equal(crmConsumerRuntimeRequirements("0.52.0"), crmConsumerRuntimeRequirements("0.51.0")) {
		t.Fatal("0.52.0 consumer-only Runtime requirements drifted from 0.51.0")
	}
	if !slices.Equal(runtimeFlowCommandSources("0.53.0"), runtimeFlowCommandSources("0.52.0")) {
		t.Fatal("0.53.0 Runtime Flow command registrations drifted from 0.52.0")
	}
	if !slices.Equal(crmConsumerRuntimeRequirements("0.53.0"), crmConsumerRuntimeRequirements("0.52.0")) {
		t.Fatal("0.53.0 consumer-only Runtime requirements drifted from 0.52.0")
	}
	required, provided, err := appCapabilities("0.54.0")
	if err != nil {
		t.Fatal(err)
	}
	for _, capability := range []string{
		defaultsemantic.CapabilityName,
		catalogsemantic.AppMetadataV3CapabilityName,
		pagelayoutsemantic.CapabilityName,
		permissionsemantic.CapabilityName,
		viewsemantic.CapabilityName,
	} {
		if !slices.Contains(required, capability) {
			t.Fatalf("0.54.0 Runtime requirements = %v, want %s", required, capability)
		}
	}
	index := slices.IndexFunc(provided, func(capability appauthority.ProvidedCapability) bool {
		return capability.CapabilityID == crmRecordDefaultsCapability
	})
	if index < 0 || provided[index].HandlerRef != crmRecordDefaultsHandlerRef ||
		!slices.Equal(provided[index].RequiredRuntimeCapabilities, []string{
			catalogsemantic.AppMetadataV3CapabilityName,
			pagelayoutsemantic.CapabilityName,
			permissionsemantic.CapabilityName,
			defaultsemantic.CapabilityName,
			viewsemantic.CapabilityName,
		}) {
		t.Fatalf("0.54.0 Record Defaults declaration = %#v", provided[index])
	}
	if !slices.Equal(runtimeFlowCommandSources("0.54.0"), runtimeFlowCommandSources("0.53.0")) {
		t.Fatal("0.54.0 Runtime Flow command registrations drifted from 0.53.0")
	}
	if !slices.Equal(crmConsumerRuntimeRequirements("0.54.0"), crmConsumerRuntimeRequirements("0.53.0")) {
		t.Fatal("0.54.0 consumer-only Runtime requirements drifted from 0.53.0")
	}
}

func TestRecycleBinMutationReleaseAddsOneLeastPrivilegeWriteOwner(t *testing.T) {
	required, provided, err := appCapabilities("0.55.0")
	if err != nil {
		t.Fatal(err)
	}
	if !slices.Contains(required, recyclemutationsemantic.CapabilityName) {
		t.Fatalf("0.55.0 Runtime requirements = %v, want %s", required, recyclemutationsemantic.CapabilityName)
	}
	index := slices.IndexFunc(provided, func(capability appauthority.ProvidedCapability) bool {
		return capability.CapabilityID == crmRecycleBinMutationCapability
	})
	if index < 0 || provided[index].HandlerRef != crmRecycleBinMutationHandlerRef ||
		!slices.Equal(provided[index].RequiredRuntimeCapabilities, []string{recyclemutationsemantic.CapabilityName}) {
		t.Fatalf("0.55.0 recycle-bin mutation declaration = %#v", provided[index])
	}
	_, previous, err := appCapabilities("0.54.0")
	if err != nil {
		t.Fatal(err)
	}
	if len(provided) != len(previous)+1 {
		t.Fatalf("0.55.0 provided capabilities = %d, want 0.54.0 + 1 (%d)", len(provided), len(previous)+1)
	}
	if !slices.Equal(runtimeFlowCommandSources("0.55.0"), runtimeFlowCommandSources("0.54.0")) {
		t.Fatal("0.55.0 Runtime Flow command registrations drifted from 0.54.0")
	}
	if !slices.Equal(crmConsumerRuntimeRequirements("0.55.0"), crmConsumerRuntimeRequirements("0.54.0")) {
		t.Fatal("0.55.0 consumer-only Runtime requirements drifted from 0.54.0")
	}
}

func TestEmailTimelineReleaseReusesExistingRuntimeCapabilities(t *testing.T) {
	required, provided, err := appCapabilities("0.56.0")
	if err != nil {
		t.Fatal(err)
	}
	previousRequired, previousProvided, err := appCapabilities("0.55.0")
	if err != nil {
		t.Fatal(err)
	}
	if !slices.Equal(required, previousRequired) || !reflect.DeepEqual(provided, previousProvided) {
		t.Fatal("0.56.0 changed Runtime or handler ownership for a frontend-only projection")
	}
	if !slices.Equal(runtimeFlowCommandSources("0.56.0"), runtimeFlowCommandSources("0.55.0")) {
		t.Fatal("0.56.0 Runtime Flow command registrations drifted from 0.55.0")
	}
	if !slices.Equal(crmConsumerRuntimeRequirements("0.56.0"), crmConsumerRuntimeRequirements("0.55.0")) {
		t.Fatal("0.56.0 consumer-only Runtime requirements drifted from 0.55.0")
	}
}

func TestStableActionReferenceReleaseReusesExistingRuntimeCapabilities(t *testing.T) {
	required, provided, err := appCapabilities("0.57.0")
	if err != nil {
		t.Fatal(err)
	}
	previousRequired, previousProvided, err := appCapabilities("0.56.0")
	if err != nil {
		t.Fatal(err)
	}
	if !slices.Equal(required, previousRequired) || !reflect.DeepEqual(provided, previousProvided) {
		t.Fatal("0.57.0 changed Runtime or handler ownership for an L1 action-reference projection")
	}
	if !slices.Equal(runtimeFlowCommandSources("0.57.0"), runtimeFlowCommandSources("0.56.0")) {
		t.Fatal("0.57.0 Runtime Flow command registrations drifted from 0.56.0")
	}
	if !slices.Equal(crmConsumerRuntimeRequirements("0.57.0"), crmConsumerRuntimeRequirements("0.56.0")) {
		t.Fatal("0.57.0 consumer-only Runtime requirements drifted from 0.56.0")
	}
}

func TestMailV2ReleaseReusesExistingRuntimeCapabilities(t *testing.T) {
	required, provided, err := appCapabilities("0.58.0")
	if err != nil {
		t.Fatal(err)
	}
	previousRequired, previousProvided, err := appCapabilities("0.57.0")
	if err != nil {
		t.Fatal(err)
	}
	if !slices.Equal(required, previousRequired) || !reflect.DeepEqual(provided, previousProvided) {
		t.Fatal("0.58.0 changed Runtime or handler ownership for the versioned SEND_MAIL payload")
	}
	if !slices.Equal(runtimeFlowCommandSources("0.58.0"), runtimeFlowCommandSources("0.57.0")) {
		t.Fatal("0.58.0 Runtime Flow command registrations drifted from 0.57.0")
	}
	if !slices.Equal(crmConsumerRuntimeRequirements("0.58.0"), crmConsumerRuntimeRequirements("0.57.0")) {
		t.Fatal("0.58.0 consumer-only Runtime requirements drifted from 0.57.0")
	}
}

func TestCampaignMemberWriteProtectionReleaseReusesExistingRuntimeCapabilities(t *testing.T) {
	required, provided, err := appCapabilities("0.59.0")
	if err != nil {
		t.Fatal(err)
	}
	previousRequired, previousProvided, err := appCapabilities("0.58.0")
	if err != nil {
		t.Fatal(err)
	}
	if !slices.Equal(required, previousRequired) || !reflect.DeepEqual(provided, previousProvided) {
		t.Fatal("0.59.0 changed Runtime or handler ownership for L1 CampaignMember write protection")
	}
	if !slices.Equal(runtimeFlowCommandSources("0.59.0"), runtimeFlowCommandSources("0.58.0")) {
		t.Fatal("0.59.0 Runtime Flow command registrations drifted from 0.58.0")
	}
	if !slices.Equal(crmConsumerRuntimeRequirements("0.59.0"), crmConsumerRuntimeRequirements("0.58.0")) {
		t.Fatal("0.59.0 consumer-only Runtime requirements drifted from 0.58.0")
	}
}

func TestQuoteMasterFallbackReleaseReusesExistingRuntimeCapabilities(t *testing.T) {
	required, provided, err := appCapabilities("0.60.0")
	if err != nil {
		t.Fatal(err)
	}
	previousRequired, previousProvided, err := appCapabilities("0.59.0")
	if err != nil {
		t.Fatal(err)
	}
	if !slices.Equal(required, previousRequired) || !reflect.DeepEqual(provided, previousProvided) {
		t.Fatal("0.60.0 changed Runtime or handler ownership for a metadata-only Quote record type correction")
	}
	if !slices.Equal(runtimeFlowCommandSources("0.60.0"), runtimeFlowCommandSources("0.59.0")) {
		t.Fatal("0.60.0 Runtime Flow command registrations drifted from 0.59.0")
	}
	if !slices.Equal(crmConsumerRuntimeRequirements("0.60.0"), crmConsumerRuntimeRequirements("0.59.0")) {
		t.Fatal("0.60.0 consumer-only Runtime requirements drifted from 0.59.0")
	}
}

func TestStableSendComposerReleaseReusesExistingRuntimeCapabilities(t *testing.T) {
	required, provided, err := appCapabilities("0.61.0")
	if err != nil {
		t.Fatal(err)
	}
	previousRequired, previousProvided, err := appCapabilities("0.60.0")
	if err != nil {
		t.Fatal(err)
	}
	if !slices.Equal(required, previousRequired) || !reflect.DeepEqual(provided, previousProvided) {
		t.Fatal("0.61.0 changed Runtime or handler ownership for a frontend-only send composer correction")
	}
	if !slices.Equal(runtimeFlowCommandSources("0.61.0"), runtimeFlowCommandSources("0.60.0")) {
		t.Fatal("0.61.0 Runtime Flow command registrations drifted from 0.60.0")
	}
	if !slices.Equal(crmConsumerRuntimeRequirements("0.61.0"), crmConsumerRuntimeRequirements("0.60.0")) {
		t.Fatal("0.61.0 consumer-only Runtime requirements drifted from 0.60.0")
	}
}

func TestDurableEmailRetryReleaseReusesExistingRuntimeCapabilities(t *testing.T) {
	required, provided, err := appCapabilities("0.62.0")
	if err != nil {
		t.Fatal(err)
	}
	previousRequired, previousProvided, err := appCapabilities("0.61.0")
	if err != nil {
		t.Fatal(err)
	}
	if !slices.Equal(required, previousRequired) || !reflect.DeepEqual(provided, previousProvided) {
		t.Fatal("0.62.0 changed Runtime or handler ownership for a frontend-only retry identity correction")
	}
	if !slices.Equal(runtimeFlowCommandSources("0.62.0"), runtimeFlowCommandSources("0.61.0")) {
		t.Fatal("0.62.0 Runtime Flow command registrations drifted from 0.61.0")
	}
	if !slices.Equal(crmConsumerRuntimeRequirements("0.62.0"), crmConsumerRuntimeRequirements("0.61.0")) {
		t.Fatal("0.62.0 consumer-only Runtime requirements drifted from 0.61.0")
	}
}

func TestCampaignMemberWriteProtectionRollbackReleaseReusesExistingRuntimeCapabilities(t *testing.T) {
	required, provided, err := appCapabilities("0.63.0")
	if err != nil {
		t.Fatal(err)
	}
	previousRequired, previousProvided, err := appCapabilities("0.62.0")
	if err != nil {
		t.Fatal(err)
	}
	if !slices.Equal(required, previousRequired) || !reflect.DeepEqual(provided, previousProvided) {
		t.Fatal("0.63.0 changed Runtime or handler ownership for an L1 metadata rollback")
	}
	if !slices.Equal(runtimeFlowCommandSources("0.63.0"), runtimeFlowCommandSources("0.62.0")) {
		t.Fatal("0.63.0 Runtime Flow command registrations drifted from 0.62.0")
	}
	if !slices.Equal(crmConsumerRuntimeRequirements("0.63.0"), crmConsumerRuntimeRequirements("0.62.0")) {
		t.Fatal("0.63.0 consumer-only Runtime requirements drifted from 0.62.0")
	}
}

func TestLeadConvertPrefillReleaseReusesExistingRuntimeCapabilities(t *testing.T) {
	required, provided, err := appCapabilities("0.64.0")
	if err != nil {
		t.Fatal(err)
	}
	previousRequired, previousProvided, err := appCapabilities("0.63.0")
	if err != nil {
		t.Fatal(err)
	}
	if !slices.Equal(required, previousRequired) || !reflect.DeepEqual(provided, previousProvided) {
		t.Fatal("0.64.0 changed Runtime or handler ownership for an L1 action metadata correction")
	}
	if !slices.Equal(runtimeFlowCommandSources("0.64.0"), runtimeFlowCommandSources("0.63.0")) {
		t.Fatal("0.64.0 Runtime Flow command registrations drifted from 0.63.0")
	}
	if !slices.Equal(crmConsumerRuntimeRequirements("0.64.0"), crmConsumerRuntimeRequirements("0.63.0")) {
		t.Fatal("0.64.0 consumer-only Runtime requirements drifted from 0.63.0")
	}
}

func TestRetainedObjectAndFieldProjectionReleaseReusesExistingRuntimeCapabilities(t *testing.T) {
	required, provided, err := appCapabilities("0.65.0")
	if err != nil {
		t.Fatal(err)
	}
	previousRequired, previousProvided, err := appCapabilities("0.64.0")
	if err != nil {
		t.Fatal(err)
	}
	if !slices.Equal(required, previousRequired) || !reflect.DeepEqual(provided, previousProvided) {
		t.Fatal("0.65.0 changed Runtime or handler ownership for an L1 projection correction")
	}
	if !slices.Equal(runtimeFlowCommandSources("0.65.0"), runtimeFlowCommandSources("0.64.0")) {
		t.Fatal("0.65.0 Runtime Flow command registrations drifted from 0.64.0")
	}
	if !slices.Equal(crmConsumerRuntimeRequirements("0.65.0"), crmConsumerRuntimeRequirements("0.64.0")) {
		t.Fatal("0.65.0 consumer-only Runtime requirements drifted from 0.64.0")
	}
}

func TestEmailAttachmentReleaseRequiresCompleteRuntimeDeliveryFeature(t *testing.T) {
	required, provided, err := appCapabilities("0.66.0")
	if err != nil {
		t.Fatal(err)
	}
	previousRequired, previousProvided, err := appCapabilities("0.65.0")
	if err != nil {
		t.Fatal(err)
	}
	if !slices.Equal(required, previousRequired) || !reflect.DeepEqual(provided, previousProvided) {
		t.Fatal("0.66.0 changed Runtime capability ownership for attachment-aware mail metadata")
	}
	if !slices.Equal(runtimeFlowCommandSources("0.66.0"), runtimeFlowCommandSources("0.65.0")) {
		t.Fatal("0.66.0 Runtime Flow command registrations drifted from 0.65.0")
	}
	wantConsumers := append(
		slices.Clone(crmConsumerRuntimeRequirements("0.65.0")),
		outboundintentsemantic.MailAttachmentDeliveryCapabilityName,
	)
	slices.Sort(wantConsumers)
	if gotConsumers := crmConsumerRuntimeRequirements("0.66.0"); !slices.Equal(gotConsumers, wantConsumers) {
		t.Fatalf("0.66.0 consumer-only Runtime requirements = %v, want %v", gotConsumers, wantConsumers)
	}
}

func TestResolvedFieldCreationReleaseRequiresSupportingHost(t *testing.T) {
	pkg, _, _, err := buildCRMRelease(filepath.Join("..", "..", "salesforce-crm-standard"))
	if err != nil {
		t.Fatal(err)
	}
	host := releaseartifact.HostCompatibility{EngineCapabilities: slices.Clone(pkg.Manifest.RequiredEngineCapabilities),
		Protocols: []releaseartifact.SupportedProtocol{}, ComponentKinds: []releaseartifact.SupportedComponentKind{}}
	for _, protocol := range pkg.Manifest.RequiredProtocols {
		host.Protocols = append(host.Protocols, releaseartifact.SupportedProtocol{ProtocolID: protocol.ProtocolID, Major: protocol.Major, Minor: protocol.MinimumMinor})
	}
	for _, component := range pkg.Bundle.Components {
		kind := releaseartifact.SupportedComponentKind{ComponentKind: component.ComponentKind, ComponentFormatVersion: component.ComponentFormatVersion}
		if !slices.Contains(host.ComponentKinds, kind) {
			host.ComponentKinds = append(host.ComponentKinds, kind)
		}
	}
	if err := releaseartifact.VerifyCompatibility(pkg, host); err != nil {
		t.Fatalf("supporting host rejected: %v", err)
	}
	host.EngineCapabilities = slices.DeleteFunc(host.EngineCapabilities, func(capability string) bool { return capability == permissionsemantic.FieldCreateCapabilityName })
	if err := releaseartifact.VerifyCompatibility(pkg, host); err == nil || !strings.Contains(err.Error(), permissionsemantic.FieldCreateCapabilityName) {
		t.Fatalf("old host or Runtime rollback must reject the new release: %v", err)
	}
	_, provided, err := appCapabilities("0.75.0")
	if err != nil {
		t.Fatal(err)
	}
	previous, _, err := appCapabilities("0.74.0")
	if err != nil {
		t.Fatal(err)
	}
	if slices.Contains(previous, permissionsemantic.FieldCreateCapabilityName) {
		t.Fatal("historical release requirements changed")
	}
	if !slices.Contains(pkg.Manifest.RequiredEngineCapabilities, permissionsemantic.FieldCreateCapabilityName) ||
		!slices.Contains(crmConsumerRuntimeRequirements("0.75.0"), permissionsemantic.FieldCreateCapabilityName) {
		t.Fatal("new release can install on a host that cannot resolve field creation permissions")
	}
	for _, capability := range provided {
		if slices.Contains(capability.RequiredRuntimeCapabilities, permissionsemantic.FieldCreateCapabilityName) {
			t.Fatalf("host-only field creation feature became a callable dependency of %s", capability.CapabilityID)
		}
	}
	if !slices.Equal(runtimeFlowCommandSources("0.75.0"), runtimeFlowCommandSources("0.74.0")) {
		t.Fatal("field permission release changed unrelated Flow sources")
	}
}

func TestPermissionAuthoringReleaseSelectsAggregateRuntime(t *testing.T) {
	required, provided, err := appCapabilities("0.67.0")
	if err != nil {
		t.Fatal(err)
	}
	previousRequired, previousProvided, err := appCapabilities("0.66.0")
	if err != nil {
		t.Fatal(err)
	}
	if len(provided) != len(previousProvided)+1 {
		t.Fatalf("0.67.0 provided capabilities = %d, want %d", len(provided), len(previousProvided)+1)
	}
	index := slices.IndexFunc(provided, func(value appauthority.ProvidedCapability) bool {
		return value.CapabilityID == crmPermissionAuthoringCapability
	})
	wantPermissionAuthoringRuntime := []string{
		permissionauthoringsemantic.RecordTypeObjectGroupsCapabilityName,
		permissionauthoringsemantic.CapabilityName,
	}
	if index < 0 || provided[index].HandlerRef != crmPermissionAuthoringHandlerRef ||
		!slices.Equal(provided[index].RequiredRuntimeCapabilities, wantPermissionAuthoringRuntime) {
		t.Fatalf("Permission Authoring capability = %#v", provided)
	}
	wantRequired := append(slices.Clone(previousRequired), wantPermissionAuthoringRuntime...)
	slices.Sort(wantRequired)
	if !slices.Equal(required, wantRequired) {
		t.Fatalf("0.67.0 Runtime requirements = %v, want %v", required, wantRequired)
	}
	if !slices.Equal(runtimeFlowCommandSources("0.67.0"), runtimeFlowCommandSources("0.66.0")) {
		t.Fatal("0.67.0 drifted unrelated Flow-command requirements")
	}
	wantConsumers := append(
		slices.Clone(crmConsumerRuntimeRequirements("0.66.0")),
		permissionauthoringsemantic.RecordTypeObjectGroupsCapabilityName,
	)
	slices.Sort(wantConsumers)
	if !slices.Equal(crmConsumerRuntimeRequirements("0.67.0"), wantConsumers) {
		t.Fatal("0.67.0 did not classify the RecordType object-group marker as consumer-only")
	}
}

func TestPermissionAuthoringCatalogReleaseDeclaresAppMetadataV3(t *testing.T) {
	required, provided, err := appCapabilities("0.68.0")
	if err != nil {
		t.Fatal(err)
	}
	previousRequired, previousProvided, err := appCapabilities("0.67.0")
	if err != nil {
		t.Fatal(err)
	}
	if !slices.Equal(required, previousRequired) || len(provided) != len(previousProvided) {
		t.Fatal("0.68.0 drifted the aggregate Runtime requirement set")
	}
	index := slices.IndexFunc(provided, func(value appauthority.ProvidedCapability) bool {
		return value.CapabilityID == crmPermissionAuthoringCapability
	})
	want := []string{
		catalogsemantic.AppMetadataV3CapabilityName,
		permissionauthoringsemantic.RecordTypeObjectGroupsCapabilityName,
		permissionauthoringsemantic.CapabilityName,
	}
	if index < 0 || !slices.Equal(provided[index].RequiredRuntimeCapabilities, want) {
		t.Fatalf("Permission Authoring capability = %#v", provided)
	}
	if !slices.Equal(runtimeFlowCommandSources("0.68.0"), runtimeFlowCommandSources("0.67.0")) ||
		!slices.Equal(crmConsumerRuntimeRequirements("0.68.0"), crmConsumerRuntimeRequirements("0.67.0")) {
		t.Fatal("0.68.0 drifted unrelated Runtime dependencies")
	}
}

func TestPublishedCRMReleasesRebuildFromAttestedSource(t *testing.T) {
	publishedRoots, err := filepath.Glob(filepath.Join("..", "..", "releases", crmAppID, "*"))
	if err != nil {
		t.Fatal(err)
	}
	if len(publishedRoots) == 0 {
		t.Fatal("no published CRM AppRelease exists")
	}
	for _, publishedRoot := range publishedRoots {
		t.Run(filepath.Base(publishedRoot), func(t *testing.T) {
			verifyPublishedCRMRelease(t, publishedRoot)
		})
	}
}

func verifyPublishedCRMRelease(t *testing.T, publishedRoot string) {
	t.Helper()
	publishedPackage, err := os.ReadFile(filepath.Join(publishedRoot, "app-package.json"))
	if err != nil {
		t.Fatal(err)
	}
	attestationRaw, err := os.ReadFile(filepath.Join(publishedRoot, "publisher-attestation.json"))
	if err != nil {
		t.Fatal(err)
	}
	keyRaw, err := os.ReadFile(filepath.Join(publishedRoot, "publisher-public-key.json"))
	if err != nil {
		t.Fatal(err)
	}
	var key publisherPublicKey
	if err := json.Unmarshal(keyRaw, &key); err != nil {
		t.Fatal(err)
	}
	canonicalKey, err := json.Marshal(key)
	if err != nil {
		t.Fatal(err)
	}
	if !bytes.Equal(keyRaw, canonicalKey) || key.PublisherID != crmPublisherID || key.Algorithm != releaseartifact.SignatureAlgorithm {
		t.Fatal("committed publisher verification key is non-canonical or belongs to another owner")
	}
	attestation, err := releaseartifact.ParseAttestation(attestationRaw)
	if err != nil {
		t.Fatal(err)
	}
	if attestation.Provenance.SourceRepository != "pingcap-inc/nexuscrm-app-packs" || attestation.Provenance.SourcePath != crmPackName {
		t.Fatalf("release provenance points outside the CRM AppPack owner: %#v", attestation.Provenance)
	}
	packRoot := materializePackAtRevision(t, attestation.Provenance.SourceRevision, attestation.Provenance.SourcePath)
	rebuilt, rebuiltRaw, sourceDigest, err := buildCRMRelease(packRoot)
	if err != nil {
		t.Fatal(err)
	}
	if !bytes.Equal(rebuiltRaw, publishedPackage) {
		t.Fatal("committed AppPackage is not byte-identical to its attested source revision")
	}
	if sourceDigest != attestation.Provenance.SourceTreeDigest {
		t.Fatalf("attested source tree digest=%s, rebuilt=%s", attestation.Provenance.SourceTreeDigest, sourceDigest)
	}
	published, err := releaseartifact.Verify(publishedPackage, attestationRaw, ed25519.PublicKey(key.PublicKey))
	if err != nil {
		t.Fatal(err)
	}
	if published.Package.AppReleaseDigest != rebuilt.AppReleaseDigest || published.Attestation.KeyID != key.KeyID {
		t.Fatalf("committed publisher proof does not bind rebuilt AppRelease: %#v", published.Attestation)
	}
}

func materializePackAtRevision(t *testing.T, revision, sourcePath string) string {
	t.Helper()
	repositoryRoot := filepath.Join("..", "..")
	manifestRaw := gitBlobAtRevision(t, repositoryRoot, revision, filepath.ToSlash(filepath.Join(sourcePath, "pack.json")))
	var manifest legacyPackManifest
	if err := json.Unmarshal(manifestRaw, &manifest); err != nil {
		t.Fatal(err)
	}
	root := t.TempDir()
	if err := os.WriteFile(filepath.Join(root, "pack.json"), manifestRaw, 0o644); err != nil {
		t.Fatal(err)
	}
	commandSources := runtimeFlowCommandSources(manifest.Version)
	sourcePaths := make([]string, 0, len(manifest.Components)+len(commandSources))
	for _, component := range manifest.Components {
		sourcePaths = append(sourcePaths, component.Path)
	}
	for _, source := range commandSources {
		sourcePaths = append(sourcePaths, source.path)
	}
	for _, source := range sourcePaths {
		cleanPath := filepath.ToSlash(filepath.Clean(source))
		if cleanPath == "." || cleanPath != source || filepath.IsAbs(source) || strings.HasPrefix(cleanPath, "../") {
			t.Fatalf("attested source path %q is not canonical and local", source)
		}
		raw := gitBlobAtRevision(t, repositoryRoot, revision, filepath.ToSlash(filepath.Join(sourcePath, cleanPath)))
		target := filepath.Join(root, filepath.FromSlash(cleanPath))
		if err := os.MkdirAll(filepath.Dir(target), 0o755); err != nil {
			t.Fatal(err)
		}
		if err := os.WriteFile(target, raw, 0o644); err != nil {
			t.Fatal(err)
		}
	}
	return root
}

func gitBlobAtRevision(t *testing.T, repositoryRoot, revision, path string) []byte {
	t.Helper()
	command := exec.Command("git", "-C", repositoryRoot, "show", revision+":"+path)
	raw, err := command.Output()
	if err != nil {
		t.Fatalf("read %s:%s: %v", revision, path, err)
	}
	return raw
}

func TestCRMReleaseBuildRejectsMissingDuplicateAndUnownedFacts(t *testing.T) {
	root := copyPackFixture(t)
	manifestPath := filepath.Join(root, "pack.json")
	if err := os.WriteFile(manifestPath, []byte(`{"manifestFormatVersion":"1","name":"salesforce-crm-standard","name":"other","category":"app-pack","version":"0.2.0","dependencies":[],"components":[]}`), 0o644); err != nil {
		t.Fatal(err)
	}
	if _, _, _, err := buildCRMRelease(root); err == nil || !strings.Contains(err.Error(), "duplicate") {
		t.Fatalf("duplicate pack ownership fact error = %v", err)
	}

	root = copyPackFixture(t)
	manifestPath = filepath.Join(root, "pack.json")
	var manifest legacyPackManifest
	raw, err := os.ReadFile(manifestPath)
	if err != nil {
		t.Fatal(err)
	}
	if err := json.Unmarshal(raw, &manifest); err != nil {
		t.Fatal(err)
	}

	manifest.Components = append(manifest.Components, manifest.Components[0])
	writeJSON(t, manifestPath, manifest)
	if _, _, _, err := buildCRMRelease(root); err == nil || !strings.Contains(err.Error(), "duplicate") {
		t.Fatalf("duplicate ownership fact error = %v", err)
	}

	root = copyPackFixture(t)
	raw, _ = os.ReadFile(filepath.Join(root, "pack.json"))
	_ = json.Unmarshal(raw, &manifest)
	manifest.Components[0].Path = "missing.json"
	writeJSON(t, filepath.Join(root, "pack.json"), manifest)
	if _, _, _, err := buildCRMRelease(root); err == nil {
		t.Fatal("missing component bytes were accepted")
	}

	root = copyPackFixture(t)
	raw, _ = os.ReadFile(filepath.Join(root, "pack.json"))
	_ = json.Unmarshal(raw, &manifest)
	manifest.Name = "other"
	writeJSON(t, filepath.Join(root, "pack.json"), manifest)
	if _, _, _, err := buildCRMRelease(root); err == nil || !strings.Contains(err.Error(), "exact") {
		t.Fatalf("wrong AppPack owner error = %v", err)
	}

	root = copyPackFixture(t)
	raw, _ = os.ReadFile(filepath.Join(root, "pack.json"))
	_ = json.Unmarshal(raw, &manifest)
	manifest.Components[0].Kind = "unknown_kind"
	writeJSON(t, filepath.Join(root, "pack.json"), manifest)
	if _, _, _, err := buildCRMRelease(root); err == nil || !strings.Contains(err.Error(), "no reviewed CRM AppRelease v1 mapping") {
		t.Fatalf("unknown component kind error = %v", err)
	}
}

func TestCRMReleaseUsesRegisteredCanonicalComponentBehavior(t *testing.T) {
	root := copyPackFixture(t)
	first, firstRaw, firstSourceDigest, err := buildCRMRelease(root)
	if err != nil {
		t.Fatal(err)
	}
	var manifest legacyPackManifest
	manifestRaw, err := os.ReadFile(filepath.Join(root, "pack.json"))
	if err != nil {
		t.Fatal(err)
	}
	if err := json.Unmarshal(manifestRaw, &manifest); err != nil {
		t.Fatal(err)
	}
	componentPath := filepath.Join(root, filepath.FromSlash(manifest.Components[0].Path))
	componentRaw, err := os.ReadFile(componentPath)
	if err != nil {
		t.Fatal(err)
	}
	var compact bytes.Buffer
	if err := json.Compact(&compact, componentRaw); err != nil {
		t.Fatal(err)
	}
	if bytes.Equal(compact.Bytes(), componentRaw) {
		compact.WriteByte('\n')
	}
	if err := os.WriteFile(componentPath, compact.Bytes(), 0o644); err != nil {
		t.Fatal(err)
	}
	second, secondRaw, secondSourceDigest, err := buildCRMRelease(root)
	if err != nil {
		t.Fatal(err)
	}
	if first.AppReleaseDigest != second.AppReleaseDigest || !bytes.Equal(firstRaw, secondRaw) {
		t.Fatal("JSON formatting changed canonical AppRelease behavior")
	}
	if firstSourceDigest == secondSourceDigest {
		t.Fatal("raw source provenance did not record the source-byte change")
	}

	if err := os.WriteFile(componentPath, []byte(`{"duplicate":1,"duplicate":2}`), 0o644); err != nil {
		t.Fatal(err)
	}
	if _, _, _, err := buildCRMRelease(root); err == nil || !strings.Contains(err.Error(), "duplicate") {
		t.Fatalf("duplicate JSON key error = %v", err)
	}
}

func TestAttestationSourceRevisionMustBeExactCommit(t *testing.T) {
	err := run([]string{
		"attest-one-shot",
		"--pack-root", "unused",
		"--package", "unused",
		"--attestation", "unused",
		"--public-key", "unused",
		"--key-id", "release-key",
		"--source-revision", "main",
		"--issued-at", "2026-07-13T17:30:00Z",
	})
	if err == nil || !strings.Contains(err.Error(), "full lowercase Git commit SHA") {
		t.Fatalf("mutable source revision error = %v", err)
	}
}

func TestAttestationIssuedAtMustNotBeFutureDated(t *testing.T) {
	err := run([]string{
		"attest-one-shot",
		"--pack-root", "unused",
		"--package", "unused",
		"--attestation", "unused",
		"--public-key", "unused",
		"--key-id", "release-key",
		"--source-revision", strings.Repeat("a", 40),
		"--issued-at", "2999-01-01T00:00:00Z",
	})
	if err == nil || !strings.Contains(err.Error(), "must not be in the future") {
		t.Fatalf("future issuance error = %v", err)
	}
}

func TestDependencyModuleVersionUsesTheResolvedBuildDependency(t *testing.T) {
	buildInfo := &debug.BuildInfo{Deps: []*debug.Module{
		{Path: "example.invalid/unrelated", Version: "v1.0.0"},
		{Path: contractModulePath, Version: "v0.1.18"},
	}}
	version, err := dependencyModuleVersionFromBuildInfo(buildInfo, contractModulePath)
	if err != nil {
		t.Fatal(err)
	}
	if version != "v0.1.18" {
		t.Fatalf("contract module version = %q", version)
	}
}

func TestDependencyModuleVersionFollowsReplacementAndRejectsLocalSource(t *testing.T) {
	resolved := &debug.BuildInfo{Deps: []*debug.Module{{
		Path: contractModulePath, Version: "v0.1.16",
		Replace: &debug.Module{Path: contractModulePath, Version: "v0.1.18"},
	}}}
	version, err := dependencyModuleVersionFromBuildInfo(resolved, contractModulePath)
	if err != nil {
		t.Fatal(err)
	}
	if version != "v0.1.18" {
		t.Fatalf("replacement contract module version = %q", version)
	}

	local := &debug.BuildInfo{Deps: []*debug.Module{{
		Path: contractModulePath, Version: "v0.1.18",
		Replace: &debug.Module{Path: "/tmp/nexuscrm-contract"},
	}}}
	if _, err := dependencyModuleVersionFromBuildInfo(local, contractModulePath); err == nil || !strings.Contains(err.Error(), "non-authoritative module") {
		t.Fatalf("local replacement error = %v", err)
	}

	fork := &debug.BuildInfo{Deps: []*debug.Module{{
		Path: contractModulePath, Version: "v0.1.18",
		Replace: &debug.Module{Path: "example.invalid/forked-contract", Version: "v0.1.18"},
	}}}
	if _, err := dependencyModuleVersionFromBuildInfo(fork, contractModulePath); err == nil || !strings.Contains(err.Error(), "non-authoritative module") {
		t.Fatalf("forked replacement error = %v", err)
	}
}

func TestDependencyModuleVersionRejectsMissingDependency(t *testing.T) {
	if _, err := dependencyModuleVersionFromBuildInfo(&debug.BuildInfo{}, contractModulePath); err == nil || !strings.Contains(err.Error(), "absent") {
		t.Fatalf("missing dependency error = %v", err)
	}
}

func TestAttestationSourceCheckoutMustMatchRevisionExactly(t *testing.T) {
	packRoot := filepath.Join("..", "..", crmPackName)
	if err := verifySourceCheckout(packRoot, strings.Repeat("0", 40)); err == nil || !strings.Contains(err.Error(), "does not match") {
		t.Fatalf("wrong source revision error = %v", err)
	}

	dirtyRoot := copyPackFixture(t)
	runGit(t, dirtyRoot, "init", "-q")
	runGit(t, dirtyRoot, "add", ".")
	runGit(t, dirtyRoot, "-c", "user.name=CRM Release Test", "-c", "user.email=release-test@example.invalid", "commit", "-qm", "fixture")
	revision := strings.TrimSpace(string(runGit(t, dirtyRoot, "rev-parse", "HEAD")))
	manifestPath := filepath.Join(dirtyRoot, "pack.json")
	manifestRaw, err := os.ReadFile(manifestPath)
	if err != nil {
		t.Fatal(err)
	}
	if err := os.WriteFile(manifestPath, append(manifestRaw, '\n'), 0o644); err != nil {
		t.Fatal(err)
	}
	if err := verifySourceCheckout(dirtyRoot, revision); err == nil || !strings.Contains(err.Error(), "must be the repository") {
		// A temporary fixture is intentionally not named salesforce-crm-standard;
		// prove that even a Git-backed lookalike cannot claim the publisher path.
		t.Fatalf("wrong source path error = %v", err)
	}

	repositoryRoot := t.TempDir()
	canonicalRoot := filepath.Join(repositoryRoot, crmPackName)
	copyPackFixtureTo(t, canonicalRoot)
	runGit(t, repositoryRoot, "init", "-q")
	runGit(t, repositoryRoot, "add", ".")
	runGit(t, repositoryRoot, "-c", "user.name=CRM Release Test", "-c", "user.email=release-test@example.invalid", "commit", "-qm", "fixture")
	revision = strings.TrimSpace(string(runGit(t, repositoryRoot, "rev-parse", "HEAD")))
	exactRoot, cleanup, err := materializeExactSourceCheckout(
		canonicalRoot,
		revision,
	)
	if err != nil {
		t.Fatalf("exact clean source checkout rejected: %v", err)
	}
	defer cleanup()
	if _, err := os.Stat(
		filepath.Join(exactRoot, crmRuntimeFlowCommandV1Source),
	); err != nil {
		t.Fatalf("exact source omitted AppRelease-owned V1 command input: %v", err)
	}
	if _, err := os.Stat(
		filepath.Join(exactRoot, crmRuntimeFlowCommandV2Source),
	); err != nil {
		t.Fatalf("exact source omitted AppRelease-owned V2 command input: %v", err)
	}
	manifestPath = filepath.Join(canonicalRoot, "pack.json")
	manifestRaw, err = os.ReadFile(manifestPath)
	if err != nil {
		t.Fatal(err)
	}
	if err := os.WriteFile(manifestPath, append(manifestRaw, '\n'), 0o644); err != nil {
		t.Fatal(err)
	}
	if err := verifySourceCheckout(canonicalRoot, revision); err == nil || !strings.Contains(err.Error(), "differs") {
		t.Fatalf("dirty source checkout error = %v", err)
	}
}

func copyPackFixture(t *testing.T) string {
	t.Helper()
	target := t.TempDir()
	copyPackFixtureTo(t, target)
	return target
}

func copyPackFixtureTo(t *testing.T, target string) {
	t.Helper()
	source := filepath.Join("..", "..", "salesforce-crm-standard")
	if err := os.MkdirAll(target, 0o755); err != nil {
		t.Fatal(err)
	}
	raw, err := os.ReadFile(filepath.Join(source, "pack.json"))
	if err != nil {
		t.Fatal(err)
	}
	if err := os.WriteFile(filepath.Join(target, "pack.json"), raw, 0o644); err != nil {
		t.Fatal(err)
	}
	var manifest legacyPackManifest
	if err := json.Unmarshal(raw, &manifest); err != nil {
		t.Fatal(err)
	}
	for _, component := range manifest.Components {
		sourcePath := filepath.Join(source, filepath.FromSlash(component.Path))
		targetPath := filepath.Join(target, filepath.FromSlash(component.Path))
		content, err := os.ReadFile(sourcePath)
		if err != nil {
			t.Fatal(err)
		}
		if err := os.MkdirAll(filepath.Dir(targetPath), 0o755); err != nil {
			t.Fatal(err)
		}
		if err := os.WriteFile(targetPath, content, 0o644); err != nil {
			t.Fatal(err)
		}
	}
	for _, commandSource := range runtimeFlowCommandSources(manifest.Version) {
		appReleaseSource := filepath.Join(source, commandSource.path)
		content, err := os.ReadFile(appReleaseSource)
		if err != nil {
			t.Fatal(err)
		}
		targetPath := filepath.Join(target, commandSource.path)
		if err := os.MkdirAll(filepath.Dir(targetPath), 0o755); err != nil {
			t.Fatal(err)
		}
		if err := os.WriteFile(targetPath, content, 0o644); err != nil {
			t.Fatal(err)
		}
	}
}

func runGit(t *testing.T, directory string, args ...string) []byte {
	t.Helper()
	command := exec.Command("git", append([]string{"-C", directory}, args...)...)
	output, err := command.CombinedOutput()
	if err != nil {
		t.Fatalf("git %s: %v: %s", strings.Join(args, " "), err, output)
	}
	return output
}

func writeJSON(t *testing.T, path string, value any) {
	t.Helper()
	raw, err := json.Marshal(value)
	if err != nil {
		t.Fatal(err)
	}
	if err := os.WriteFile(path, raw, 0o644); err != nil {
		t.Fatal(err)
	}
}

func TestLayoutAuthoringReleaseAddsOneDeclaredRuntimeOwner(t *testing.T) {
	required, provided, err := appCapabilities("0.69.0")
	if err != nil {
		t.Fatal(err)
	}
	previousRequired, previousProvided, err := appCapabilities("0.68.0")
	if err != nil {
		t.Fatal(err)
	}
	if len(provided) != len(previousProvided)+1 {
		t.Fatalf("provided capabilities: %d", len(provided))
	}
	index := slices.IndexFunc(provided, func(c appauthority.ProvidedCapability) bool { return c.CapabilityID == crmLayoutAuthoringCapability })
	if index < 0 || provided[index].HandlerRef != crmLayoutAuthoringHandlerRef || !slices.Equal(provided[index].RequiredRuntimeCapabilities, []string{"runtime.layout.authoring.v1"}) {
		t.Fatal("Layout authoring is not declared with its Runtime owner")
	}
	want := append(slices.Clone(previousRequired), "runtime.layout.authoring.v1")
	slices.Sort(want)
	if !slices.Equal(required, want) {
		t.Fatalf("requirements %v want %v", required, want)
	}
	if !slices.Equal(crmConsumerRuntimeRequirements("0.69.0"), crmConsumerRuntimeRequirements("0.68.0")) || !slices.Equal(runtimeFlowCommandSources("0.69.0"), runtimeFlowCommandSources("0.68.0")) {
		t.Fatal("unrelated consumer requirements drifted")
	}
}

func TestLayoutAuthoringReleaseIsRetiredWithoutChangingPageResolution(t *testing.T) {
	required, provided, err := appCapabilities("0.70.0")
	if err != nil {
		t.Fatal(err)
	}
	previousRequired, previousProvided, err := appCapabilities("0.69.0")
	if err != nil {
		t.Fatal(err)
	}
	if slices.ContainsFunc(provided, func(c appauthority.ProvidedCapability) bool {
		return c.CapabilityID == crmLayoutAuthoringCapability
	}) {
		t.Fatal("0.70.0 still exposes Layout authoring")
	}
	if len(provided) != len(previousProvided)-1 {
		t.Fatalf("provided capabilities: %d, want %d", len(provided), len(previousProvided)-1)
	}
	wantRequired := slices.DeleteFunc(slices.Clone(previousRequired), func(capability string) bool {
		return capability == layoutauthoringsemantic.CapabilityName
	})
	if !slices.Equal(required, wantRequired) {
		t.Fatalf("requirements %v want %v", required, wantRequired)
	}
	if !slices.Contains(required, pagelayoutsemantic.CapabilityName) {
		t.Fatal("PageLayout resolution was removed with Layout authoring")
	}
	if !slices.Equal(crmConsumerRuntimeRequirements("0.70.0"), crmConsumerRuntimeRequirements("0.69.0")) || !slices.Equal(runtimeFlowCommandSources("0.70.0"), runtimeFlowCommandSources("0.69.0")) {
		t.Fatal("unrelated consumer requirements drifted")
	}
}

func TestLayoutAuthoringReleaseUsesSynchronousRuntimeWorkspace(t *testing.T) {
	required, provided, err := appCapabilities("0.72.0")
	if err != nil {
		t.Fatal(err)
	}
	previousRequired, previousProvided, err := appCapabilities("0.71.0")
	if err != nil {
		t.Fatal(err)
	}
	if len(provided) != len(previousProvided)+1 {
		t.Fatalf("provided capabilities: %d, want %d", len(provided), len(previousProvided)+1)
	}
	index := slices.IndexFunc(provided, func(capability appauthority.ProvidedCapability) bool {
		return capability.CapabilityID == crmLayoutAuthoringCapability
	})
	if index < 0 || provided[index].HandlerRef != crmLayoutAuthoringHandlerRef ||
		!slices.Equal(provided[index].RequiredRuntimeCapabilities, []string{layoutauthoringv2semantic.CapabilityName}) {
		t.Fatal("Layout authoring is not declared with its synchronous Runtime owner")
	}
	wantRequired := append(slices.Clone(previousRequired), layoutauthoringv2semantic.CapabilityName)
	slices.Sort(wantRequired)
	if !slices.Equal(required, wantRequired) {
		t.Fatalf("requirements %v want %v", required, wantRequired)
	}
	if !slices.Contains(required, pagelayoutsemantic.CapabilityName) {
		t.Fatal("PageLayout resolution was removed while restoring Layout authoring")
	}
	if !slices.Equal(crmConsumerRuntimeRequirements("0.72.0"), crmConsumerRuntimeRequirements("0.71.0")) ||
		!slices.Equal(runtimeFlowCommandSources("0.72.0"), runtimeFlowCommandSources("0.71.0")) {
		t.Fatal("unrelated consumer requirements drifted")
	}
}

func TestNavigationReleaseDeclaresLiveRuntimeDependency(t *testing.T) {
	required, provided, err := appCapabilities("0.73.0")
	if err != nil {
		t.Fatal(err)
	}
	previousRequired, previousProvided, err := appCapabilities("0.72.0")
	if err != nil {
		t.Fatal(err)
	}
	const navigation = "runtime.application-navigation.v1"
	want := append(slices.Clone(previousRequired), navigation)
	slices.Sort(want)
	if !slices.Equal(required, want) || len(provided) != len(previousProvided)+1 {
		t.Fatalf("navigation release changed unrelated capabilities: %v", required)
	}
	for _, id := range []string{crmUIAppsCapability, crmApplicationNavigationCapability} {
		index := slices.IndexFunc(provided, func(c appauthority.ProvidedCapability) bool { return c.CapabilityID == id })
		if index < 0 || !slices.Contains(provided[index].RequiredRuntimeCapabilities, navigation) {
			t.Fatalf("%s lacks current navigation dependency", id)
		}
	}
	if !slices.Equal(crmConsumerRuntimeRequirements("0.73.0"), crmConsumerRuntimeRequirements("0.72.0")) || !slices.Equal(runtimeFlowCommandSources("0.73.0"), runtimeFlowCommandSources("0.72.0")) {
		t.Fatal("unrelated consumer requirements drifted")
	}
}

func TestInstalledSearchScopeReleaseRequiresEffectiveMetadata(t *testing.T) {
	_, provided, err := appCapabilities("0.74.0")
	if err != nil {
		t.Fatal(err)
	}
	_, old, err := appCapabilities("0.73.0")
	if err != nil {
		t.Fatal(err)
	}
	find := func(capabilities []appauthority.ProvidedCapability, id string) int {
		return slices.IndexFunc(capabilities, func(c appauthority.ProvidedCapability) bool { return c.CapabilityID == id })
	}
	if find(provided, crmSearchScopeOrderCapability) >= 0 || len(provided) != len(old)-1 {
		t.Fatal("scope was not consolidated under search")
	}
	index := find(provided, crmSearchCapability)
	if index < 0 {
		t.Fatal("missing search handler")
	}
	for _, dependency := range []string{catalogsemantic.AppMetadataV3CapabilityName, permissionsemantic.CapabilityName, searchsemantic.CapabilityName} {
		if !slices.Contains(provided[index].RequiredRuntimeCapabilities, dependency) {
			t.Fatalf("search missing %s", dependency)
		}
	}
	index = find(old, crmSearchScopeOrderCapability)
	if index < 0 || !slices.Contains(old[index].RequiredRuntimeCapabilities, catalogsemantic.AppMetadataCapabilityName) {
		t.Fatal("historical scope dependency changed")
	}
}
