package cli

import (
	"context"
	"errors"
	"flag"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/Clyra-AI/wrkr/core/aggregate/agentdeploy"
	"github.com/Clyra-AI/wrkr/core/aggregate/agentresolver"
	"github.com/Clyra-AI/wrkr/core/aggregate/controlbacklog"
	aggexposure "github.com/Clyra-AI/wrkr/core/aggregate/exposure"
	agginventory "github.com/Clyra-AI/wrkr/core/aggregate/inventory"
	"github.com/Clyra-AI/wrkr/core/aggregate/privilegebudget"
	"github.com/Clyra-AI/wrkr/core/aggregate/scanquality"
	"github.com/Clyra-AI/wrkr/core/attribution"
	"github.com/Clyra-AI/wrkr/core/compliance"
	"github.com/Clyra-AI/wrkr/core/config"
	"github.com/Clyra-AI/wrkr/core/detect"
	"github.com/Clyra-AI/wrkr/core/detect/agnt"
	detectdefaults "github.com/Clyra-AI/wrkr/core/detect/defaults"
	"github.com/Clyra-AI/wrkr/core/diff"
	exportsarif "github.com/Clyra-AI/wrkr/core/export/sarif"
	"github.com/Clyra-AI/wrkr/core/lifecycle"
	"github.com/Clyra-AI/wrkr/core/manifest"
	"github.com/Clyra-AI/wrkr/core/policy/approvedtools"
	"github.com/Clyra-AI/wrkr/core/policy/productiontargets"
	profilemodel "github.com/Clyra-AI/wrkr/core/policy/profile"
	profileeval "github.com/Clyra-AI/wrkr/core/policy/profileeval"
	"github.com/Clyra-AI/wrkr/core/proofemit"
	reportcore "github.com/Clyra-AI/wrkr/core/report"
	"github.com/Clyra-AI/wrkr/core/risk"
	"github.com/Clyra-AI/wrkr/core/score"
	"github.com/Clyra-AI/wrkr/core/source"
	sourcegithub "github.com/Clyra-AI/wrkr/core/source/github"
	"github.com/Clyra-AI/wrkr/core/source/localsetup"
	sourceorg "github.com/Clyra-AI/wrkr/core/source/org"
	"github.com/Clyra-AI/wrkr/core/sourceprivacy"
	"github.com/Clyra-AI/wrkr/core/state"
)

func runScanWithContext(parentCtx context.Context, args []string, stdout io.Writer, stderr io.Writer) int {
	if len(args) > 0 && args[0] == "status" {
		return runScanStatus(args[1:], stdout, stderr)
	}
	if parentCtx == nil {
		parentCtx = context.Background()
	}

	jsonRequested := wantsJSONOutput(args)

	fs := flag.NewFlagSet("scan", flag.ContinueOnError)
	if jsonRequested {
		fs.SetOutput(io.Discard)
	} else {
		fs.SetOutput(stderr)
	}

	jsonOut := fs.Bool("json", false, "emit machine-readable output")
	jsonPath := fs.String("json-path", "", "write final machine-readable output to a file path")
	resume := fs.Bool("resume", false, "resume a prior interrupted org scan from checkpoint state")
	explain := fs.Bool("explain", false, "emit rationale details")
	quiet := fs.Bool("quiet", false, "suppress non-error output")
	repo := fs.String("repo", "", "scan one repo owner/repo")
	orgTarget := fs.String("org", "", "scan an organization")
	githubOrgTarget := fs.String("github-org", "", "scan an organization (alias for --org)")
	mySetup := fs.Bool("my-setup", false, "scan the local machine setup for AI tool posture")
	pathTarget := fs.String("path", "", "scan local pre-cloned repositories")
	var explicitTargets repeatedStringFlag
	fs.Var(&explicitTargets, "target", "repeatable scan target <mode>:<value>")
	timeout := fs.Duration("timeout", 0, "optional scan timeout (0 disables)")
	scanModeRaw := fs.String("mode", "governance", "scan mode [quick|governance|deep]")
	progressModeRaw := fs.String("progress", string(scanProgressModeAuto), "progress output [auto|bar|plain|events|none]")
	sourceRetentionRaw := fs.String("source-retention", sourceprivacy.RetentionEphemeral, "hosted source retention [ephemeral|retain_for_resume|retain]")
	deploymentModeRaw := fs.String("deployment-mode", sourceprivacy.DeploymentModeLocalOnly, "deployment/data mode [local_only|customer_controlled_storage|connected_saas_metadata|managed_platform]")
	allowSourceMaterialization := fs.Bool("allow-source-materialization", false, "allow hosted scans to fetch generic source-code files")
	diffMode := fs.Bool("diff", false, "show only changes since previous scan")
	enrich := fs.Bool("enrich", false, "enable non-deterministic enrichment lookups (network required)")
	baselinePath := fs.String("baseline", "", "optional fallback baseline when local state is absent")
	configPathFlag := fs.String("config", "", "config file path override")
	statePathFlag := fs.String("state", "", "state file path override")
	policyPath := fs.String("policy", "", "optional custom policy rule file")
	approvedToolsPath := fs.String("approved-tools", "", "optional approved tools policy file")
	productionTargetsPath := fs.String("production-targets", "", "optional production target rules file")
	productionTargetsStrict := fs.Bool("production-targets-strict", false, "fail scan when production target rules cannot be loaded")
	profileName := fs.String("profile", "standard", "posture profile [baseline|standard|strict|assessment]")
	githubBaseURL := fs.String("github-api", "", "github api base url")
	githubToken := fs.String("github-token", "", "github token override")
	reportMD := fs.Bool("report-md", false, "emit deterministic markdown summary artifact after scan")
	reportMDPath := fs.String("report-md-path", "wrkr-scan-summary.md", "scan summary markdown output path")
	reportTemplate := fs.String("report-template", string(reportcore.TemplateOperator), "scan summary template [exec|operator|audit|public|ciso|appsec|platform|customer-draft|agent-action-bom|design-partner-summary]")
	reportShareProfile := fs.String("report-share-profile", string(reportcore.ShareProfileInternal), "scan summary share profile [internal|public|customer-redacted|design-partner|external-redacted|investor-safe]")
	reportTop := fs.Int("report-top", 5, "number of top findings included in scan summary artifact")
	sarifOut := fs.Bool("sarif", false, "emit SARIF artifact")
	sarifPath := fs.String("sarif-path", "wrkr.sarif", "SARIF output path")
	fs.Usage = func() {
		writeScanUsage(fs.Output(), fs)
	}

	if code, handled := parseFlags(fs, args, stderr, jsonRequested || *jsonOut); handled {
		return code
	}
	if *timeout < 0 {
		return emitError(stderr, jsonRequested || *jsonOut, "invalid_input", "--timeout must be >= 0", exitInvalidInput)
	}
	scanMode, scanModeErr := parseScanMode(*scanModeRaw)
	if scanModeErr != nil {
		return emitError(stderr, jsonRequested || *jsonOut, "invalid_input", scanModeErr.Error(), exitInvalidInput)
	}
	progressMode, progressModeErr := parseScanProgressMode(*progressModeRaw)
	if progressModeErr != nil {
		return emitError(stderr, jsonRequested || *jsonOut, "invalid_input", progressModeErr.Error(), exitInvalidInput)
	}
	sourceRetentionMode, sourceRetentionErr := sourceprivacy.ParseRetentionMode(*sourceRetentionRaw)
	if sourceRetentionErr != nil {
		return emitError(stderr, jsonRequested || *jsonOut, "invalid_input", sourceRetentionErr.Error(), exitInvalidInput)
	}
	deploymentMode, deploymentModeErr := sourceprivacy.ParseDeploymentMode(*deploymentModeRaw)
	if deploymentModeErr != nil {
		return emitError(stderr, jsonRequested || *jsonOut, "invalid_input", deploymentModeErr.Error(), exitInvalidInput)
	}
	allowHostedSourceMaterialization := *allowSourceMaterialization || scanMode == "deep"
	productionTargetsFile := strings.TrimSpace(*productionTargetsPath)
	if *productionTargetsStrict && productionTargetsFile == "" {
		return emitError(
			stderr,
			jsonRequested || *jsonOut,
			"invalid_input",
			"--production-targets-strict requires --production-targets <path>",
			exitInvalidInput,
		)
	}

	hasExplicitTarget := strings.TrimSpace(*repo) != "" || strings.TrimSpace(*orgTarget) != "" || strings.TrimSpace(*githubOrgTarget) != "" || strings.TrimSpace(*pathTarget) != "" || *mySetup || len(explicitTargets) > 0
	loadedCfg, hasLoadedCfg, cfgLoadErr := loadOptionalScanConfig(*configPathFlag, hasExplicitTarget)
	if cfgLoadErr != nil {
		return emitError(stderr, jsonRequested || *jsonOut, "runtime_failure", cfgLoadErr.Error(), exitRuntime)
	}
	targets, cfg, err := resolveScanTargets(*repo, *orgTarget, *githubOrgTarget, *pathTarget, *mySetup, explicitTargets, *configPathFlag)
	if err != nil {
		if isNoTargetProvidedError(err) {
			return emitErrorWithDetails(
				stderr,
				jsonRequested || *jsonOut,
				"invalid_input",
				err.Error(),
				exitInvalidInput,
				map[string]any{"next_steps": missingTargetNextSteps()},
			)
		}
		return emitError(stderr, jsonRequested || *jsonOut, "invalid_input", err.Error(), exitInvalidInput)
	}
	if *resume && !allTargetsAreOrg(targets) {
		return emitError(stderr, jsonRequested || *jsonOut, "invalid_input", "--resume is only supported when every requested target is an org target", exitInvalidInput)
	}
	if hasLoadedCfg {
		cfg.Auth = loadedCfg.Auth
		cfg.GitHubAPIBase = loadedCfg.GitHubAPIBase
	}
	*githubBaseURL = resolveScanGitHubAPIBase(*githubBaseURL, cfg)
	*githubToken = resolveScanGitHubToken(*githubToken, cfg)
	if *enrich && strings.TrimSpace(*githubBaseURL) == "" {
		return emitError(
			stderr,
			jsonRequested || *jsonOut,
			"dependency_missing",
			"--enrich requires a reachable network source; set --github-api, configure github_api_base in wrkr init config, or set WRKR_GITHUB_API_BASE",
			exitDependencyMissing,
		)
	}
	if anyTargetNeedsGitHub(targets) && strings.TrimSpace(*githubBaseURL) == "" {
		return emitError(
			stderr,
			jsonRequested || *jsonOut,
			"dependency_missing",
			"--repo and --org scans require --github-api, config github_api_base, or WRKR_GITHUB_API_BASE",
			exitDependencyMissing,
		)
	}
	artifactPreflight, preflightErr := preflightScanArtifacts(
		state.ResolvePath(*statePathFlag),
		*jsonPath,
		*reportMD,
		*reportMDPath,
		*reportTemplate,
		*reportShareProfile,
		*sarifOut,
		*sarifPath,
	)
	if preflightErr != nil {
		if isUnsafeManagedArtifactPathError(preflightErr) {
			return emitError(stderr, jsonRequested || *jsonOut, "unsafe_operation_blocked", preflightErr.Error(), exitUnsafeBlocked)
		}
		return emitError(stderr, jsonRequested || *jsonOut, "invalid_input", preflightErr.Error(), exitInvalidInput)
	}
	jsonSink, jsonSinkErr := newJSONOutputSink(*jsonOut, artifactPreflight.JSONPath, stdout)
	if jsonSinkErr != nil {
		return emitError(stderr, jsonRequested || *jsonOut, "invalid_input", jsonSinkErr.Error(), exitInvalidInput)
	}
	statePath := artifactPreflight.StatePath
	if recoveryErr := recoverManagedArtifactTransaction(statePath); recoveryErr != nil {
		return emitScanRuntimeError(stderr, jsonRequested || *jsonOut, recoveryErr)
	}
	materializedRoot := ""
	if targetsNeedMaterializedRoot(targets) {
		var rootErr error
		if *resume {
			materializedRoot, rootErr = prepareMaterializedRootForResume(statePath)
		} else {
			materializedRoot, rootErr = prepareMaterializedRoot(statePath)
		}
		if rootErr != nil {
			return emitScanRuntimeError(stderr, jsonRequested || *jsonOut, rootErr)
		}
	}
	sourcePrivacy := sourceprivacy.InitialContract(sourceRetentionMode, materializedRoot != "", allowHostedSourceMaterialization, deploymentMode)
	sourceCleanupFinalized := false

	ctx := parentCtx
	cancel := func() {}
	if *timeout > 0 {
		ctx, cancel = context.WithTimeout(parentCtx, *timeout)
	}
	defer cancel()
	scanStartedAt := time.Now().UTC().Truncate(time.Second)
	progressTargetMode, progressTargetValue := scanProgressTargetLabel(targets)
	artifactPaths := map[string]string{
		"state":     statePath,
		"manifest":  artifactPreflight.ManifestPath,
		"lifecycle": artifactPreflight.LifecyclePath,
		"proof":     artifactPreflight.ProofChainPath,
	}
	statusTracker := newScanStatusTracker(
		statePath,
		source.Target{Mode: progressTargetMode, Value: progressTargetValue},
		configTargetsToSourceTargets(targets),
		scanStartedAt,
		artifactPaths,
		sourcePrivacy,
	)
	if err := statusTracker.Start(); err != nil {
		return emitError(stderr, jsonRequested || *jsonOut, "runtime_failure", err.Error(), exitRuntime)
	}
	if *quiet {
		progressMode = scanProgressModeNone
	}
	if !anyTargetUsesProgress(targets) {
		progressMode = scanProgressModeNone
	}
	progress := newScanProgressReporter(scanProgressReporterOptions{
		RequestedMode:     progressMode,
		JSONOutput:        *jsonOut,
		Stderr:            stderr,
		StartedAt:         scanStartedAt,
		TargetMode:        progressTargetMode,
		TargetValue:       progressTargetValue,
		StatusSink:        statusTracker.Progress,
		HeartbeatInterval: scanProgressHeartbeatInterval(stderr),
	})
	progress.Start(ctx)
	defer progress.Stop()
	finalizeSourceCleanup := func(success bool) error {
		if sourceCleanupFinalized {
			return nil
		}
		sourceCleanupFinalized = true
		if strings.TrimSpace(materializedRoot) == "" {
			sourcePrivacy = sourceprivacy.Normalize(sourcePrivacy)
			statusTracker.SetSourcePrivacy(sourcePrivacy)
			return nil
		}
		if sourceprivacy.ShouldRetainMaterializedSource(sourceRetentionMode, success) {
			sourcePrivacy = sourceprivacy.MarkRetained(sourcePrivacy)
			statusTracker.SetSourcePrivacy(sourcePrivacy)
			return nil
		}
		if err := cleanupManagedMaterializedRoot(statePath, materializedRoot); err != nil {
			sourcePrivacy = sourceprivacy.MarkFailed(sourcePrivacy, err.Error())
			statusTracker.SetSourcePrivacy(sourcePrivacy)
			return err
		}
		sourcePrivacy = sourceprivacy.MarkRemoved(sourcePrivacy)
		statusTracker.SetSourcePrivacy(sourcePrivacy)
		return nil
	}
	cleanupFailure := func(err error) error {
		cleanupErr := finalizeSourceCleanup(false)
		if cleanupErr == nil {
			return err
		}
		if err == nil {
			return cleanupErr
		}
		return fmt.Errorf("%v (source cleanup failed: %w)", err, cleanupErr)
	}
	checkScanContext := func() error {
		if ctxErr := ctx.Err(); ctxErr != nil {
			return ctxErr
		}
		return nil
	}
	emitScanFailure := func(err error) int {
		err = cleanupFailure(err)
		statusTracker.Fail(err, artifactPaths)
		progress.Finish(statusTracker.FooterData())
		return emitScanRuntimeError(stderr, jsonRequested || *jsonOut, err)
	}
	emitScanError := func(code, message string, exitCode int) int {
		if cleanupErr := finalizeSourceCleanup(false); cleanupErr != nil {
			statusTracker.Fail(cleanupErr, artifactPaths)
			progress.Finish(statusTracker.FooterData())
			return emitScanRuntimeError(stderr, jsonRequested || *jsonOut, cleanupErr)
		}
		statusTracker.Fail(errors.New(message), artifactPaths)
		progress.Finish(statusTracker.FooterData())
		return emitError(stderr, jsonRequested || *jsonOut, code, message, exitCode)
	}
	progress.ScanPhase(progressTargetMode, progressTargetValue, "source_acquire_start")
	if err := statusTracker.Phase("source_acquire_start"); err != nil {
		return emitScanFailure(err)
	}
	manifestOut, findings, err := acquireSources(ctx, targets, *githubBaseURL, *githubToken, acquireOptions{
		StatePath:                  statePath,
		Progress:                   progress,
		Resume:                     *resume,
		MaterializedRoot:           materializedRoot,
		AllowSourceMaterialization: allowHostedSourceMaterialization,
	})
	if err != nil {
		if source.IsPublicSurfaceInputError(err) {
			return emitScanError("invalid_input", err.Error(), exitInvalidInput)
		}
		if source.IsPublicSurfaceSafetyError(err) {
			return emitScanError("unsafe_operation_blocked", err.Error(), exitUnsafeBlocked)
		}
		return emitScanFailure(err)
	}
	if err := checkScanContext(); err != nil {
		return emitScanFailure(err)
	}
	progress.ScanPhase(progressTargetMode, progressTargetValue, "source_acquire_complete")
	if err := statusTracker.Phase("source_acquire_complete"); err != nil {
		return emitScanFailure(err)
	}
	if repoTotal := len(manifestOut.Repos) + len(manifestOut.Failures); repoTotal > 0 {
		if err := statusTracker.Repos(repoTotal, len(manifestOut.Repos), len(manifestOut.Failures)); err != nil {
			return emitScanFailure(err)
		}
	}

	scopes := detectorScopes(manifestOut)
	detectorErrors := []detect.DetectorError{}
	if len(scopes) > 0 {
		registry, regErr := detectdefaults.RegistryForMode(scanMode)
		if regErr != nil {
			return emitScanFailure(regErr)
		}
		progress.ScanPhase(progressTargetMode, progressTargetValue, "detectors_start")
		if err := statusTracker.Phase("detectors_start"); err != nil {
			return emitScanFailure(err)
		}
		var detectorProgress detect.DetectorProgressReporter
		if progressMode != scanProgressModeNone {
			detectorProgress = progress
		}
		detected, runErr := registry.Run(ctx, scopes, detect.Options{
			Enrich:   *enrich,
			ScanMode: scanMode,
			Progress: detectorProgress,
		})
		if runErr != nil {
			return emitScanFailure(runErr)
		}
		if err := checkScanContext(); err != nil {
			return emitScanFailure(err)
		}
		findings = append(findings, detected.Findings...)
		findings = append(findings, agnt.SynthesizeDrift(findings)...)
		detectorErrors = append(detectorErrors, detected.DetectorErrors...)

		policyFindings, policyErr := evaluatePolicies(scopes, findings, strings.TrimSpace(*policyPath))
		if policyErr != nil {
			return emitScanError("policy_schema_violation", policyErr.Error(), exitPolicyViolation)
		}
		if err := checkScanContext(); err != nil {
			return emitScanFailure(err)
		}
		findings = append(findings, policyFindings...)
		progress.ScanPhase(progressTargetMode, progressTargetValue, "detectors_complete")
		if err := statusTracker.Phase("detectors_complete"); err != nil {
			return emitScanFailure(err)
		}
	}
	source.SortFindings(findings)
	progress.ScanPhase(progressTargetMode, progressTargetValue, "analysis_start")
	if err := statusTracker.Phase("analysis_start"); err != nil {
		return emitScanFailure(err)
	}

	previousSnapshot, loadPreviousErr := loadPreviousSnapshot(statePath, strings.TrimSpace(*baselinePath))
	if loadPreviousErr != nil {
		return emitScanFailure(loadPreviousErr)
	}
	if *diffMode && previousSnapshot != nil && !scanModesCompatible(previousSnapshot.ScanMode, scanMode) {
		return emitScanError("invalid_input", fmt.Sprintf("--diff requires matching scan modes: previous=%s current=%s", previousSnapshot.ScanMode, scanMode), exitInvalidInput)
	}

	now := time.Now().UTC().Truncate(time.Second)
	if err := validateRepoControlDeclarations(manifestOut); err != nil {
		return emitScanError("policy_schema_violation", err.Error(), exitPolicyViolation)
	}
	scanMethodology := buildScanMethodology(manifestOut, findings, scanStartedAt, now)
	riskReport := risk.Score(findings, 5, now)
	if err := checkScanContext(); err != nil {
		return emitScanFailure(err)
	}
	repoRisk := map[string]float64{}
	for _, repo := range riskReport.Repos {
		repoRisk[repo.Org+"::"+repo.Repo] = repo.Score
	}

	manifestPath := artifactPreflight.ManifestPath
	previousManifest, manifestErr := loadLifecycleManifest(manifestPath, statePath, previousSnapshot)
	if manifestErr != nil {
		return emitScanFailure(manifestErr)
	}

	baseContexts := buildFindingContexts(riskReport)
	observed := observedTools(findings, baseContexts)
	nextManifest, transitions := lifecycle.Reconcile(previousManifest, observed, now)

	identityByAgent := map[string]manifest.IdentityRecord{}
	for _, record := range nextManifest.Identities {
		identityByAgent[record.AgentID] = record
	}
	contexts := enrichFindingContexts(findings, baseContexts, identityByAgent)

	repoExposure := aggexposure.Build(findings, repoRisk)
	agentBindings := agentresolver.Resolve(findings)
	agentDeployments := agentdeploy.Resolve(findings)
	inventoryOut := agginventory.Build(agginventory.BuildInput{
		Manifest:              manifestOut,
		Findings:              findings,
		Contexts:              contexts,
		AgentBindings:         agentBindings,
		AgentDeployments:      agentDeployments,
		Methodology:           scanMethodology,
		RepoExposureSummaries: repoExposure,
		GeneratedAt:           now,
	})
	if err := checkScanContext(); err != nil {
		return emitScanFailure(err)
	}
	approvedConfigured := false
	if approvedToolsPolicyPath := strings.TrimSpace(*approvedToolsPath); approvedToolsPolicyPath != "" {
		approvedCfg, approvedErr := approvedtools.Load(approvedToolsPolicyPath)
		if approvedErr != nil {
			return emitScanError("invalid_input", approvedErr.Error(), exitInvalidInput)
		}
		if approvedCfg.HasRules() {
			approvedConfigured = true
			agginventory.ReclassifyApprovalWithMatcher(&inventoryOut, func(tool agginventory.Tool) bool {
				return approvedCfg.Match(approvedtools.ToolCandidate{
					ToolID:   tool.ToolID,
					AgentID:  tool.AgentID,
					ToolType: tool.ToolType,
					Org:      tool.Org,
					Repos:    tool.Repos,
				})
			})
		}
	}
	if anyTargetIsMySetup(targets) {
		localInventory := inventoryLocalMachineSlice(inventoryOut)
		findings = append(findings, approvedtools.CompareLocalInventory(&localInventory, approvedConfigured, strings.TrimSpace(*approvedToolsPath))...)
		inventoryOut.LocalGovernance = localInventory.LocalGovernance
		source.SortFindings(findings)
		riskReport = risk.Score(findings, 5, now)
		repoRisk = map[string]float64{}
		for _, repo := range riskReport.Repos {
			repoRisk[repo.Org+"::"+repo.Repo] = repo.Score
		}
		repoExposure = aggexposure.Build(findings, repoRisk)
		inventoryOut.RepoExposureSummaries = append([]aggexposure.RepoExposureSummary(nil), repoExposure...)
	}
	agginventory.ApplySecurityVisibility(&inventoryOut, buildSecurityVisibilityReference(previousSnapshot, statePath, strings.TrimSpace(*baselinePath)))
	defaultProductionTargets := productiontargets.DefaultConfig()
	productionTargets := &defaultProductionTargets
	productionTargetWarnings := []string{}
	productionWriteStatus := agginventory.ProductionTargetsStatusConfigured
	if productionTargetsFile != "" {
		productionTargets = nil
		cfg, cfgErr := productiontargets.Load(productionTargetsFile)
		if cfgErr != nil {
			if *productionTargetsStrict {
				return emitScanError("invalid_input", cfgErr.Error(), exitInvalidInput)
			}
			productionWriteStatus = agginventory.ProductionTargetsStatusInvalid
			productionTargetWarnings = append(productionTargetWarnings, fmt.Sprintf("production targets not applied: %v", cfgErr))
		} else if cfg.HasTargets() {
			productionTargets = &cfg
			productionWriteStatus = agginventory.ProductionTargetsStatusConfigured
		} else {
			productionWriteStatus = agginventory.ProductionTargetsStatusNotConfigured
			productionTargetWarnings = append(productionTargetWarnings, fmt.Sprintf("production targets file %s has no configured targets; production_write budget is not configured", productionTargetsFile))
		}
	}
	inventoryOut.PrivilegeBudget, inventoryOut.AgentPrivilegeMap = privilegebudget.Build(inventoryOut.Tools, inventoryOut.Agents, findings, productionTargets)
	inventoryOut.PrivilegeBudget.ProductionWrite.Status = productionWriteStatus
	inventoryOut.PrivilegeBudget.ProductionWrite.Configured = productionWriteStatus == agginventory.ProductionTargetsStatusConfigured
	if !inventoryOut.PrivilegeBudget.ProductionWrite.Configured {
		inventoryOut.PrivilegeBudget.ProductionWrite.Count = nil
	}
	for idx := range inventoryOut.AgentPrivilegeMap {
		inventoryOut.AgentPrivilegeMap[idx].ProductionTargetStatus = productionWriteStatus
	}
	agginventory.ApplySecurityVisibilityToPrivilegeMap(&inventoryOut)
	agginventory.ApplyCanonicalStores(&inventoryOut)
	riskReport.ActionPaths, riskReport.ActionPathToControlFirst = risk.BuildActionPaths(riskReport.AttackPaths, &inventoryOut)
	riskReport.ActionPaths = risk.DecoratePolicyCoverage(riskReport.ActionPaths, findings)
	riskReport.ActionPaths = risk.DecorateIntroducedBy(riskReport.ActionPaths, repoAttributionContexts(manifestOut, now))
	riskReport.ActionPaths = risk.DecorateControlMetadata(riskReport.ActionPaths, repoAttributionContexts(manifestOut, now))
	riskReport.ActionPaths = risk.ProjectActionPaths(riskReport.ActionPaths)
	riskReport.ActionPathToControlFirst = risk.BuildActionPathChoice(riskReport.ActionPaths)

	profileDef, profileErr := profilemodel.Builtin(*profileName)
	if profileErr != nil {
		return emitScanError("unsafe_operation_blocked", profileErr.Error(), exitUnsafeBlocked)
	}
	profileDef, profileErr = profilemodel.WithOverrides(profileDef, strings.TrimSpace(*policyPath), repoRootFromScopes(scopes))
	if profileErr != nil {
		return emitScanError("policy_schema_violation", profileErr.Error(), exitPolicyViolation)
	}
	var previousProfile *profileeval.Result
	if previousSnapshot != nil && previousSnapshot.Profile != nil {
		copyResult := *previousSnapshot.Profile
		previousProfile = &copyResult
	}
	profileResult := profileeval.Evaluate(profileDef, findings, previousProfile)
	riskReport.ActionPaths, riskReport.ActionPathToControlFirst = risk.ApplyGovernFirstProfile(profileResult.ProfileName, riskReport.ActionPaths)
	if err := checkScanContext(); err != nil {
		return emitScanFailure(err)
	}
	scanQuality := scanquality.Build(scanquality.Input{
		Mode:           scanMode,
		Scopes:         scopes,
		Findings:       findings,
		DetectorErrors: detectorErrors,
	})
	artifactFindings := summarizeArtifactFindings(findings, scanMode)
	riskReport.ActionPaths = risk.DecorateEvidenceContext(riskReport.ActionPaths, &scanQuality)
	riskReport.ControlPathGraph = risk.BuildControlPathGraph(riskReport.ActionPaths)
	riskReport.WorkflowChains = risk.BuildWorkflowChains(riskReport.ActionPaths, riskReport.ControlPathGraph)
	riskReport.ActionPaths = risk.DecorateWorkflowChainRefs(riskReport.ActionPaths, riskReport.WorkflowChains)
	riskReport.ActionPaths = risk.DecorateActionLineage(riskReport.ActionPaths, riskReport.ControlPathGraph)
	riskReport.ActionPathToControlFirst = risk.BuildActionPathChoice(riskReport.ActionPaths)
	lifecycleGaps := lifecycle.DetectGaps(lifecycle.GapInput{
		Identities:  nextManifest.Identities,
		Inventory:   &inventoryOut,
		Transitions: transitions,
	})
	inventoryOut.LifecycleQueue = lifecycle.BuildQueue(lifecycleGaps)
	controlBacklog := controlbacklog.Build(controlbacklog.Input{
		Mode:             scanMode,
		GeneratedAt:      now,
		Findings:         findings,
		Inventory:        &inventoryOut,
		Identities:       nextManifest.Identities,
		LifecycleGaps:    lifecycleGaps,
		ActionPaths:      riskReport.ActionPaths,
		ControlPathGraph: riskReport.ControlPathGraph,
	})

	weights, weightErr := score.LoadWeights(strings.TrimSpace(*policyPath), repoRootFromScopes(scopes))
	if weightErr != nil {
		return emitScanError("policy_schema_violation", weightErr.Error(), exitPolicyViolation)
	}
	var previousScore *score.Result
	if previousSnapshot != nil && previousSnapshot.PostureScore != nil {
		copyResult := *previousSnapshot.PostureScore
		previousScore = &copyResult
	}
	postureScore := score.Compute(score.Input{
		Findings:        findings,
		Identities:      nextManifest.Identities,
		ProfileResult:   profileResult,
		TransitionCount: driftTransitionCount(transitions),
		Weights:         weights,
		Previous:        previousScore,
	})
	if err := checkScanContext(); err != nil {
		return emitScanFailure(err)
	}

	snapshot := state.Snapshot{
		Version:                    state.SnapshotVersion,
		Target:                     manifestOut.Target,
		Targets:                    manifestOut.Targets,
		Findings:                   artifactFindings,
		Inventory:                  &inventoryOut,
		ControlBacklog:             &controlBacklog,
		LifecycleGaps:              lifecycleGaps,
		ScanQuality:                &scanQuality,
		ScanMode:                   scanMode,
		RiskReport:                 &riskReport,
		Profile:                    &profileResult,
		PostureScore:               &postureScore,
		Identities:                 nextManifest.Identities,
		Transitions:                transitions,
		SourcePrivacy:              &sourcePrivacy,
		PublicEvidenceManifestName: manifestOut.PublicEvidenceManifestName,
		PublicEvidence:             manifestOut.PublicEvidence,
	}
	chainPath := artifactPreflight.LifecyclePath
	proofChainPath := artifactPreflight.ProofChainPath
	transaction, snapshotErr := beginManagedArtifactTransaction(statePath, "scan", []managedArtifactFile{
		{label: "state", path: statePath},
		{label: "manifest", path: manifestPath},
		{label: "lifecycle chain", path: chainPath},
		{label: "proof chain", path: proofChainPath},
		{label: "proof attestation", path: artifactPreflight.ProofAttestationPath},
		{label: "proof signing key", path: artifactPreflight.SigningKeyPath},
		{label: "report markdown", path: artifactPreflight.ReportPath},
		{label: "sarif", path: artifactPreflight.SARIFPath},
		{label: "json output", path: jsonSink.outputPath},
	})
	if snapshotErr != nil {
		return emitScanFailure(snapshotErr)
	}
	if err := checkScanContext(); err != nil {
		return emitScanFailure(err)
	}
	progress.ScanPhase(progressTargetMode, progressTargetValue, "artifact_commit_start")
	if err := statusTracker.Phase("artifact_commit_start"); err != nil {
		return emitScanFailure(err)
	}
	emitRolledBackScanFailure := func(err error) int {
		if err == nil {
			return exitSuccess
		}
		err = cleanupFailure(err)
		statusTracker.Fail(err, artifactPaths)
		progress.Finish(statusTracker.FooterData())
		return emitScanRuntimeError(stderr, jsonRequested || *jsonOut, transaction.Rollback(err))
	}
	emitRolledBackScanError := func(code, message string, exitCode int) int {
		if cleanupErr := finalizeSourceCleanup(false); cleanupErr != nil {
			statusTracker.Fail(cleanupErr, artifactPaths)
			progress.Finish(statusTracker.FooterData())
			return emitScanRuntimeError(stderr, jsonRequested || *jsonOut, transaction.Rollback(cleanupErr))
		}
		statusTracker.Fail(errors.New(message), artifactPaths)
		progress.Finish(statusTracker.FooterData())
		rollbackErr := transaction.Rollback(errors.New(message))
		if rollbackErr != nil && rollbackErr.Error() != message {
			return emitError(stderr, jsonRequested || *jsonOut, "runtime_failure", rollbackErr.Error(), exitRuntime)
		}
		return emitError(stderr, jsonRequested || *jsonOut, code, message, exitCode)
	}
	completeManagedScanTransaction := func() int {
		if err := verifyManagedArtifactConsistency(statePath, managedArtifactVerificationFull); err != nil {
			return emitRolledBackScanFailure(err)
		}
		if err := transaction.Complete(); err != nil {
			return emitRolledBackScanFailure(err)
		}
		return exitSuccess
	}

	if err := state.Save(statePath, snapshot); err != nil {
		return emitRolledBackScanFailure(err)
	}
	if err := checkScanContext(); err != nil {
		return emitRolledBackScanFailure(err)
	}
	chain, chainErr := lifecycle.LoadChain(chainPath)
	if chainErr != nil {
		return emitRolledBackScanFailure(chainErr)
	}
	for _, transition := range transitions {
		if err := lifecycle.AppendTransitionRecord(chain, transition, "lifecycle_transition"); err != nil {
			return emitRolledBackScanFailure(err)
		}
	}
	if err := lifecycle.SaveChain(chainPath, chain); err != nil {
		return emitRolledBackScanFailure(err)
	}
	if err := checkScanContext(); err != nil {
		return emitRolledBackScanFailure(err)
	}
	if _, err := proofemit.EmitScanWithContext(ctx, statePath, now, findings, &inventoryOut, riskReport, profileResult, postureScore, transitions); err != nil {
		return emitRolledBackScanFailure(err)
	}
	if err := checkScanContext(); err != nil {
		return emitRolledBackScanFailure(err)
	}
	proofChain, err := proofemit.LoadChain(proofChainPath)
	if err != nil {
		return emitRolledBackScanFailure(err)
	}
	complianceSummary, err := compliance.BuildRollupSummary(findings, proofChain)
	if err != nil {
		return emitRolledBackScanError("policy_schema_violation", err.Error(), exitPolicyViolation)
	}
	if err := manifest.Save(manifestPath, nextManifest); err != nil {
		return emitRolledBackScanFailure(err)
	}
	if err := checkScanContext(); err != nil {
		return emitRolledBackScanFailure(err)
	}
	if err := finalizeSourceCleanup(true); err != nil {
		return emitRolledBackScanFailure(err)
	}
	snapshot.SourcePrivacy = &sourcePrivacy
	if err := state.Save(statePath, snapshot); err != nil {
		return emitRolledBackScanFailure(err)
	}

	payload := buildScanJSONSummary(scanJSONSummaryInput{
		DeploymentMode:          deploymentMode,
		Manifest:                manifestOut,
		SourcePrivacy:           sourcePrivacy,
		ScanMode:                scanMode,
		ScanQuality:             scanQuality,
		StatePath:               statePath,
		Inventory:               inventoryOut,
		RiskReport:              riskReport,
		ControlBacklog:          controlBacklog,
		Profile:                 profileResult,
		PostureScore:            postureScore,
		ComplianceSummary:       complianceSummary,
		Findings:                artifactFindings,
		Warnings:                reportcore.MCPVisibilityWarnings(findings),
		ProductionTargetWarning: productionTargetWarnings,
		DetectorErrors:          detectorErrors,
		PartialResult:           len(manifestOut.Failures) > 0,
		SourceErrors:            manifestOut.Failures,
	})
	if len(manifestOut.Targets) > 0 {
		payload["targets"] = manifestOut.Targets
	}
	if len(manifestOut.Failures) > 0 {
		payload["partial_result"] = true
		payload["source_errors"] = manifestOut.Failures
		payload["source_degraded"] = hasDegradedFailures(manifestOut.Failures)
	}
	if len(detectorErrors) > 0 {
		payload["detector_errors"] = detectorErrors
	}
	scanReportPath := ""
	scanSARIFPath := ""

	if *diffMode {
		payload = map[string]any{
			"status":          "ok",
			"deployment_mode": deploymentMode,
			"target":          manifestOut.Target,
			"source_manifest": manifestOut,
			"source_privacy":  sourcePrivacy,
			"scan_mode":       scanMode,
			"scan_quality":    scanQuality,
		}
		previousFindings := []source.Finding{}
		if previousSnapshot != nil {
			previousFindings = previousSnapshot.Findings
		}
		result := diff.Compute(previousFindings, findings)
		payload["diff"] = result
		payload["diff_empty"] = diff.Empty(result)
	}
	if *reportMD {
		if err := checkScanContext(); err != nil {
			return emitRolledBackScanFailure(err)
		}
		manifestCopy := nextManifest
		artifacts, reportErr := generateReportArtifacts(reportArtifactOptions{
			StatePath:        statePath,
			Snapshot:         snapshot,
			PreviousSnapshot: previousSnapshot,
			Manifest:         &manifestCopy,
			Top:              *reportTop,
			Template:         artifactPreflight.ReportTemplate,
			ShareProfile:     artifactPreflight.ReportShareProfile,
			WriteMarkdown:    true,
			MarkdownPath:     artifactPreflight.ReportPath,
		})
		if reportErr != nil {
			if isArtifactPathError(reportErr) {
				return emitRolledBackScanError("invalid_input", reportErr.Error(), exitInvalidInput)
			}
			return emitRolledBackScanFailure(reportErr)
		}
		scanReportPath = artifacts.MarkdownPath
		payload["report"] = map[string]any{
			"md_path":       artifacts.MarkdownPath,
			"template":      string(artifactPreflight.ReportTemplate),
			"share_profile": string(artifactPreflight.ReportShareProfile),
		}
	}
	if *sarifOut {
		if err := checkScanContext(); err != nil {
			return emitRolledBackScanFailure(err)
		}
		report := exportsarif.BuildWithSourcePrivacy(findings, wrkrVersion(), &sourcePrivacy)
		if writeErr := exportsarif.Write(artifactPreflight.SARIFPath, report); writeErr != nil {
			return emitRolledBackScanFailure(writeErr)
		}
		scanSARIFPath = artifactPreflight.SARIFPath
		payload["sarif"] = map[string]any{
			"path": artifactPreflight.SARIFPath,
		}
	}
	statusCompleted := false
	completeScanStatus := func() int {
		if statusCompleted {
			return exitSuccess
		}
		artifactPaths = finalScanArtifactPaths(statePath, artifactPreflight, jsonSink.outputPath, scanReportPath, scanSARIFPath)
		if err := statusTracker.Phase("artifact_commit_complete"); err != nil {
			return emitRolledBackScanFailure(err)
		}
		if err := statusTracker.Complete(artifactPaths, len(manifestOut.Failures) > 0); err != nil {
			return emitRolledBackScanFailure(err)
		}
		statusCompleted = true
		return exitSuccess
	}

	if jsonSink.enabled() {
		if err := checkScanContext(); err != nil {
			return emitRolledBackScanFailure(err)
		}
		if err := jsonSink.writePayload(payload); err != nil {
			return emitRolledBackScanError("runtime_failure", err.Error(), exitRuntime)
		}
		progress.ScanPhase(progressTargetMode, progressTargetValue, "artifact_commit_complete")
		if code := completeScanStatus(); code != exitSuccess {
			return code
		}
		if code := completeManagedScanTransaction(); code != exitSuccess {
			return code
		}
		progress.Finish(statusTracker.FooterData())
		if *jsonOut {
			return exitSuccess
		}
	}
	if !*quiet {
		for _, sourceFailure := range manifestOut.Failures {
			_, _ = fmt.Fprintf(stderr, "warning: source repo=%s reason=%s\n", sourceFailure.Repo, sourceFailure.Reason)
		}
		for _, detectorErr := range detectorErrors {
			_, _ = fmt.Fprintf(stderr, "warning: detector=%s repo=%s org=%s code=%s class=%s message=%s\n", detectorErr.Detector, detectorErr.Repo, detectorErr.Org, detectorErr.Code, detectorErr.Class, detectorErr.Message)
		}
		for _, warning := range productionTargetWarnings {
			_, _ = fmt.Fprintf(stderr, "warning: %s\n", warning)
		}
		for _, warning := range sourcePrivacy.Warnings {
			_, _ = fmt.Fprintf(stderr, "warning: source privacy: %s\n", warning)
		}
	}
	if *quiet {
		if code := completeScanStatus(); code != exitSuccess {
			return code
		}
		if code := completeManagedScanTransaction(); code != exitSuccess {
			return code
		}
		progress.Finish(statusTracker.FooterData())
		return exitSuccess
	}
	if *explain {
		if code := completeScanStatus(); code != exitSuccess {
			return code
		}
		if code := completeManagedScanTransaction(); code != exitSuccess {
			return code
		}
		progress.Finish(statusTracker.FooterData())
		_, _ = fmt.Fprintf(stdout, "wrkr scan completed for %s (profile=%s score=%.2f grade=%s)\n", renderScanTargetSet(targets), profileResult.ProfileName, postureScore.Score, postureScore.Grade)
		for _, line := range explainControlBacklog(controlBacklog, 10) {
			_, _ = fmt.Fprintf(stdout, "control backlog: %s\n", line)
		}
		if hasIncompleteFilesystemVisibility(detectorErrors, manifestOut.Failures) {
			_, _ = fmt.Fprintln(stdout, "scan completeness: some files or directories could not be read; review detector_errors/source_errors for permission or stat failures.")
		}
		for _, line := range compliance.ExplainRollupSummary(complianceSummary, 3) {
			_, _ = fmt.Fprintf(stdout, "compliance: %s\n", line)
		}
		if scanReportPath != "" {
			_, _ = fmt.Fprintf(stdout, "scan report: %s\n", scanReportPath)
		}
		if scanSARIFPath != "" {
			_, _ = fmt.Fprintf(stdout, "scan sarif: %s\n", scanSARIFPath)
		}
		return exitSuccess
	}
	if code := completeScanStatus(); code != exitSuccess {
		return code
	}
	if code := completeManagedScanTransaction(); code != exitSuccess {
		return code
	}
	progress.Finish(statusTracker.FooterData())
	_, _ = fmt.Fprintln(stdout, "wrkr scan complete")
	return exitSuccess
}

func writeScanUsage(out io.Writer, fs *flag.FlagSet) {
	_, _ = fmt.Fprintln(out, "Usage of scan:")
	_, _ = fmt.Fprintln(out, "  wrkr scan [flags]")
	_, _ = fmt.Fprintln(out, "")
	_, _ = fmt.Fprintln(out, "Start here:")
	_, _ = fmt.Fprintln(out, "  Hosted org posture when prerequisites are ready:")
	_, _ = fmt.Fprintln(out, "    wrkr init --non-interactive --org acme --github-api https://api.github.com --json")
	_, _ = fmt.Fprintln(out, "    wrkr scan --config ~/.wrkr/config.json --json")
	_, _ = fmt.Fprintln(out, "  Evaluator-safe fallback when hosted prerequisites are not ready yet:")
	_, _ = fmt.Fprintln(out, "    wrkr scan --path ./scenarios/wrkr/scan-mixed-org/repos --json")
	_, _ = fmt.Fprintln(out, "  Developer-machine hygiene:")
	_, _ = fmt.Fprintln(out, "    wrkr scan --my-setup --json")
	_, _ = fmt.Fprintln(out, "")
	_, _ = fmt.Fprintln(out, "Flags:")
	fs.PrintDefaults()
}

func isNoTargetProvidedError(err error) bool {
	return err != nil && strings.HasPrefix(err.Error(), "no target provided and no usable config default target")
}

func explainControlBacklog(backlog controlbacklog.Backlog, limit int) []string {
	if limit <= 0 {
		limit = 10
	}
	lines := []string{
		fmt.Sprintf("%d item(s), %d unique Wrkr signal(s), %d supporting security signal(s)",
			backlog.Summary.TotalItems,
			backlog.Summary.UniqueWrkrSignalItems,
			backlog.Summary.SupportingSecurityItems,
		),
	}
	if len(backlog.Items) == 0 {
		return append(lines, "no active governance review items")
	}
	for idx, item := range backlog.Items {
		if idx >= limit {
			break
		}
		lines = append(lines, fmt.Sprintf(
			"review #%d %s %s action=%s visibility=%s confidence=%s write_path=%s rationale=%s",
			idx+1,
			fallbackForExplain(item.Repo, "local"),
			fallbackForExplain(item.Path, "<unknown>"),
			item.RecommendedAction,
			item.SecurityVisibility,
			item.Confidence,
			fallbackForExplain(strings.Join(item.WritePathClasses, "+"), item.Capability),
			fallbackForExplain(strings.Join(item.EvidenceBasis, "+"), item.EvidenceSource),
		))
	}
	return lines
}

func fallbackForExplain(value, fallbackValue string) string {
	if strings.TrimSpace(value) != "" {
		return strings.TrimSpace(value)
	}
	return strings.TrimSpace(fallbackValue)
}

func inventoryLocalMachineSlice(inv agginventory.Inventory) agginventory.Inventory {
	local := inv
	local.Tools = make([]agginventory.Tool, 0, len(inv.Tools))
	for _, tool := range inv.Tools {
		if toolTouchesLocalMachine(tool) {
			local.Tools = append(local.Tools, tool)
		}
	}
	return local
}

func toolTouchesLocalMachine(tool agginventory.Tool) bool {
	for _, repo := range tool.Repos {
		if strings.TrimSpace(repo) == localsetup.RepoName {
			return true
		}
	}
	for _, location := range tool.Locations {
		if strings.TrimSpace(location.Repo) == localsetup.RepoName {
			return true
		}
	}
	return false
}

type scanArtifactPreflight struct {
	StatePath            string
	ManifestPath         string
	LifecyclePath        string
	ProofChainPath       string
	ProofAttestationPath string
	SigningKeyPath       string
	JSONPath             string
	ReportTemplate       reportcore.Template
	ReportShareProfile   reportcore.ShareProfile
	ReportPath           string
	SARIFPath            string
}

func finalScanArtifactPaths(statePath string, preflight scanArtifactPreflight, jsonPath string, reportPath string, sarifPath string) map[string]string {
	paths := map[string]string{
		"state":             statePath,
		"manifest":          preflight.ManifestPath,
		"lifecycle":         preflight.LifecyclePath,
		"proof_chain":       preflight.ProofChainPath,
		"proof_attestation": preflight.ProofAttestationPath,
	}
	if strings.TrimSpace(jsonPath) != "" {
		paths["json"] = jsonPath
	}
	if strings.TrimSpace(reportPath) != "" {
		paths["report_md"] = reportPath
	}
	if strings.TrimSpace(sarifPath) != "" {
		paths["sarif"] = sarifPath
	}
	return paths
}

func preflightScanArtifacts(statePathRaw, jsonPath string, reportEnabled bool, reportPath, reportTemplateRaw, reportShareProfileRaw string, sarifEnabled bool, sarifPath string) (scanArtifactPreflight, error) {
	preflight := scanArtifactPreflight{}
	statePath, err := preflightTrustedStatePath(statePathRaw)
	if err != nil {
		return scanArtifactPreflight{}, err
	}
	manifestPath, err := normalizeManagedArtifactPath(manifest.ResolvePath(statePath))
	if err != nil {
		return scanArtifactPreflight{}, err
	}
	lifecyclePath, err := normalizeManagedArtifactPath(lifecycle.ChainPath(statePath))
	if err != nil {
		return scanArtifactPreflight{}, err
	}
	proofChainPath, err := normalizeManagedArtifactPath(proofemit.ChainPath(statePath))
	if err != nil {
		return scanArtifactPreflight{}, err
	}
	proofAttestationPath, err := normalizeManagedArtifactPath(proofemit.ChainAttestationPath(proofChainPath))
	if err != nil {
		return scanArtifactPreflight{}, err
	}
	signingKeyPath, err := normalizeManagedArtifactPath(proofemit.SigningKeyPath(statePath))
	if err != nil {
		return scanArtifactPreflight{}, err
	}

	preflight.StatePath = statePath
	preflight.ManifestPath = manifestPath
	preflight.LifecyclePath = lifecyclePath
	preflight.ProofChainPath = proofChainPath
	preflight.ProofAttestationPath = proofAttestationPath
	preflight.SigningKeyPath = signingKeyPath

	entries := make([]scanArtifactPathEntry, 0, 9)
	for _, item := range []struct {
		label string
		path  string
	}{
		{label: "--state", path: preflight.StatePath},
		{label: "manifest", path: preflight.ManifestPath},
		{label: "lifecycle chain", path: preflight.LifecyclePath},
		{label: "proof chain", path: preflight.ProofChainPath},
		{label: "proof attestation", path: preflight.ProofAttestationPath},
		{label: "proof signing key", path: preflight.SigningKeyPath},
	} {
		entry, entryErr := newScanArtifactPathEntry(item.label, item.path)
		if entryErr != nil {
			return scanArtifactPreflight{}, entryErr
		}
		entries = append(entries, entry)
	}

	if strings.TrimSpace(jsonPath) != "" {
		path, err := resolveArtifactOutputPath(jsonPath)
		if err != nil {
			return scanArtifactPreflight{}, err
		}
		preflight.JSONPath = path
		entry, entryErr := newScanArtifactPathEntry("--json-path", path)
		if entryErr != nil {
			return scanArtifactPreflight{}, entryErr
		}
		entries = append(entries, entry)
	}
	if reportEnabled {
		template, shareProfile, err := parseReportTemplateShare(reportTemplateRaw, reportShareProfileRaw)
		if err != nil {
			return scanArtifactPreflight{}, err
		}
		path, err := resolveArtifactOutputPath(reportPath)
		if err != nil {
			return scanArtifactPreflight{}, err
		}
		preflight.ReportTemplate = template
		preflight.ReportShareProfile = shareProfile
		preflight.ReportPath = path
		entry, entryErr := newScanArtifactPathEntry("--report-md-path", path)
		if entryErr != nil {
			return scanArtifactPreflight{}, entryErr
		}
		entries = append(entries, entry)
	}
	if sarifEnabled {
		path, err := resolveArtifactOutputPath(sarifPath)
		if err != nil {
			return scanArtifactPreflight{}, err
		}
		preflight.SARIFPath = path
		entry, entryErr := newScanArtifactPathEntry("--sarif-path", path)
		if entryErr != nil {
			return scanArtifactPreflight{}, entryErr
		}
		entries = append(entries, entry)
	}
	if err := detectScanArtifactPathCollisions(entries); err != nil {
		return scanArtifactPreflight{}, err
	}
	return preflight, nil
}

func emitScanRuntimeError(stderr io.Writer, jsonOut bool, err error) int {
	switch {
	case errors.Is(err, context.DeadlineExceeded):
		return emitError(stderr, jsonOut, "scan_timeout", "scan exceeded configured timeout", exitRuntime)
	case errors.Is(err, context.Canceled):
		return emitError(stderr, jsonOut, "scan_canceled", "scan canceled by signal or parent context", exitRuntime)
	case isMaterializedRootSafetyError(err):
		return emitError(stderr, jsonOut, "unsafe_operation_blocked", err.Error(), exitUnsafeBlocked)
	case sourceorg.IsCheckpointSafetyError(err):
		return emitError(stderr, jsonOut, "unsafe_operation_blocked", err.Error(), exitUnsafeBlocked)
	case sourceorg.IsCheckpointInputError(err):
		return emitError(stderr, jsonOut, "invalid_input", err.Error(), exitInvalidInput)
	case sourcegithub.IsRateLimitedError(err):
		return emitError(stderr, jsonOut, "rate_limited", scanRateLimitedMessage(err), exitRuntime)
	default:
		return emitError(stderr, jsonOut, "runtime_failure", scanRuntimeErrorMessage(err), exitRuntime)
	}
}

func resolveScanGitHubToken(explicit string, cfg config.Config) string {
	for _, candidate := range []string{
		strings.TrimSpace(explicit),
		strings.TrimSpace(cfg.Auth.Scan.Token),
		strings.TrimSpace(os.Getenv("WRKR_GITHUB_TOKEN")),
		strings.TrimSpace(os.Getenv("GITHUB_TOKEN")),
	} {
		if candidate != "" {
			return candidate
		}
	}
	return ""
}

func resolveScanGitHubAPIBase(explicit string, cfg config.Config) string {
	for _, candidate := range []string{
		strings.TrimSpace(explicit),
		strings.TrimSpace(cfg.GitHubAPIBase),
		strings.TrimSpace(os.Getenv("WRKR_GITHUB_API_BASE")),
	} {
		if candidate != "" {
			return candidate
		}
	}
	return ""
}

func loadOptionalScanConfig(configPath string, hasExplicitTarget bool) (config.Config, bool, error) {
	resolvedPath, err := config.ResolvePath(configPath)
	if err != nil {
		return config.Config{}, false, err
	}
	cfg, err := config.Load(resolvedPath)
	if err == nil {
		return cfg, true, nil
	}
	if hasExplicitTarget && strings.TrimSpace(configPath) == "" {
		return config.Config{}, false, nil
	}
	if errors.Is(err, os.ErrNotExist) && strings.TrimSpace(configPath) == "" && strings.TrimSpace(os.Getenv("WRKR_CONFIG_PATH")) == "" {
		return config.Config{}, false, nil
	}
	return config.Config{}, false, err
}

func scanRuntimeErrorMessage(err error) string {
	if err == nil {
		return ""
	}
	return err.Error()
}

func scanRateLimitedMessage(err error) string {
	if err == nil {
		return ""
	}
	return err.Error() + "; authenticate hosted scans with --github-token, config auth.scan.token, WRKR_GITHUB_TOKEN, or GITHUB_TOKEN; wait for the reported GitHub reset window before retrying"
}

func parseScanMode(raw string) (string, error) {
	switch strings.TrimSpace(raw) {
	case "", "governance":
		return "governance", nil
	case "quick":
		return "quick", nil
	case "deep":
		return "deep", nil
	default:
		return "", fmt.Errorf("--mode must be one of quick, governance, or deep")
	}
}

func scanModesCompatible(previous, current string) bool {
	previous = strings.TrimSpace(previous)
	current = strings.TrimSpace(current)
	return previous == "" || previous == current
}

func repoAttributionContexts(manifest source.Manifest, generatedAt time.Time) map[string]attribution.Context {
	if len(manifest.Repos) == 0 {
		return nil
	}
	out := make(map[string]attribution.Context, len(manifest.Repos))
	for _, repo := range manifest.Repos {
		key := strings.TrimSpace(repo.Repo)
		if strings.TrimSpace(key) == "" {
			continue
		}
		repoRoot := strings.TrimSpace(repo.ScanRoot)
		if repoRoot == "" {
			repoRoot = strings.TrimSpace(repo.Location)
		}
		repoRoot = filepath.Clean(repoRoot)
		ctx := attribution.LoadContextAt(repoRoot, generatedAt)
		for _, composite := range []string{"local::" + key, "::" + key} {
			if existing := out[composite]; strings.TrimSpace(existing.RepoRoot) == "" {
				out[composite] = ctx
			}
		}
	}
	return out
}

func validateRepoControlDeclarations(manifest source.Manifest) error {
	for _, repo := range manifest.Repos {
		repoRoot := strings.TrimSpace(repo.ScanRoot)
		if repoRoot == "" {
			repoRoot = strings.TrimSpace(repo.Location)
		}
		repoRoot = filepath.Clean(repoRoot)
		if repoRoot == "" {
			continue
		}
		if _, _, err := config.LoadControlDeclarations(repoRoot); err != nil {
			return err
		}
	}
	return nil
}

func hasIncompleteFilesystemVisibility(detectorErrors []detect.DetectorError, sourceFailures []source.RepoFailure) bool {
	for _, detectorErr := range detectorErrors {
		if detectorErr.Code == "permission_denied" || detectorErr.Code == "path_not_found" {
			return true
		}
	}
	for _, failure := range sourceFailures {
		lower := strings.ToLower(strings.TrimSpace(failure.Reason))
		if strings.Contains(lower, "permission denied") || strings.Contains(lower, "no such file") || strings.Contains(lower, "not found") {
			return true
		}
	}
	return false
}
