// Copyright (c) 2026 Hellmai Ltd
// SPDX-License-Identifier: AGPL-3.0-only
import { getDocsOnlyPrefixes } from '@lumenflow/core';
import { DelegationRegistryStore } from '@lumenflow/core/delegation-registry-store';
import { getConfig } from '@lumenflow/core/config';
import { die, getErrorMessage } from '@lumenflow/core/error-handler';
import { isDocumentationType } from '@lumenflow/core/wu-type-helpers';
import { CLI_FLAGS, EMOJI, LOG_PREFIX, PKG_MANAGER, SCRIPTS, WU_EXPOSURE, WU_TYPES, } from '@lumenflow/core/wu-constants';
import { getLatestWuBriefEvidence, getWuBriefEvidenceAgeMinutes, hasDelegationIntentMarker, hasMatchingWuBriefEvidenceHash, isWuBriefEvidenceStale, WUStateStore, } from '@lumenflow/core/wu-state-store';
import { validateExposure, validateFeatureAccessibility } from '@lumenflow/core/wu-validation';
import { createSignal } from '@lumenflow/memory/signal';
import { resolveStateDir } from './state-path-resolvers.js';
export const WU_BRIEF_POLICY_MODES = ['off', 'manual', 'auto', 'required'];
const DEFAULT_WU_BRIEF_POLICY_MODE = 'auto';
const DEFAULT_WU_BRIEF_FRESHNESS_MINUTES = 1440;
const PREP_LOG_PREFIX = '[wu-prep]';
const PREP_FORCE_REASON_REQUIRED_MESSAGE = 'Missing required --reason for wu:brief policy bypass in wu:prep.';
const CLAIM_AUTO_EVIDENCE_MODE = 'claim-auto';
/**
 * WU-1999: Print exposure validation warnings.
 *
 * Validates exposure field and UI pairing for user-facing WUs.
 * Non-blocking - logs warnings but doesn't prevent completion.
 */
export function printExposureWarnings(wu, options = {}) {
    const result = validateExposure(wu, { skipExposureCheck: options.skipExposureCheck });
    if (result.warnings.length > 0) {
        console.log(`\n${LOG_PREFIX.DONE} ${EMOJI.WARNING} WU-1999: Exposure validation warnings:`);
        for (const warning of result.warnings) {
            console.log(`${LOG_PREFIX.DONE}   ${warning}`);
        }
        console.log(`${LOG_PREFIX.DONE} These are non-blocking warnings. ` +
            `To skip, use --skip-exposure-check flag.\n`);
    }
}
/**
 * WU-2022: Validate feature accessibility for UI-exposed WUs.
 *
 * BLOCKING validation - prevents wu:done if exposure=ui but feature is not accessible.
 */
export function validateAccessibilityOrDie(wu, options = {}) {
    const result = validateFeatureAccessibility(wu, {
        skipAccessibilityCheck: options.skipAccessibilityCheck,
    });
    if (!result.valid) {
        console.log(`\n${LOG_PREFIX.DONE} ${EMOJI.FAILURE} WU-2022: Feature accessibility validation failed`);
        die(`❌ FEATURE ACCESSIBILITY VALIDATION FAILED (WU-2022)\n\n` +
            `Cannot complete wu:done - UI feature accessibility not verified.\n\n` +
            `${result.errors.join('\n\n')}\n\n` +
            `This gate prevents "orphaned code" - features that exist but users cannot access.`);
    }
}
/**
 * WU-1012: Validate --docs-only flag usage.
 */
export function validateDocsOnlyFlag(wu, args) {
    if (!args.docsOnly) {
        return { valid: true, errors: [] };
    }
    const wuId = wu.id || 'unknown';
    const exposure = wu.exposure;
    const type = wu.type;
    const codePaths = wu.code_paths;
    if (exposure === WU_EXPOSURE.DOCUMENTATION) {
        return { valid: true, errors: [] };
    }
    if (isDocumentationType(type)) {
        return { valid: true, errors: [] };
    }
    const docsOnlyPrefixes = getDocsOnlyPrefixes().map((prefix) => prefix.toLowerCase());
    // WU-2538: docs paths = configured prefixes OR any markdown file.
    // Mirrors docs-path-validator.ts so preflight matches commit-time gate.
    const isDocsPath = (p) => {
        const normalizedPath = p.trim().toLowerCase();
        for (const prefix of docsOnlyPrefixes) {
            if (normalizedPath.startsWith(prefix)) {
                return true;
            }
        }
        return normalizedPath.endsWith('.md');
    };
    if (codePaths && Array.isArray(codePaths) && codePaths.length > 0) {
        const allDocsOnly = codePaths.every((p) => typeof p === 'string' && isDocsPath(p));
        if (allDocsOnly) {
            return { valid: true, errors: [] };
        }
    }
    const currentExposure = exposure || 'not set';
    const currentType = type || 'not set';
    return {
        valid: false,
        errors: [
            `--docs-only flag used on ${wuId} but WU is not documentation-focused.\n\n` +
                `Current exposure: ${currentExposure}\n` +
                `Current type: ${currentType}\n\n` +
                `--docs-only requires one of:\n` +
                `  1. exposure: documentation\n` +
                `  2. type: documentation\n` +
                `  3. All code_paths under configured docs prefixes (${docsOnlyPrefixes.join(', ')}), or *.md files\n\n` +
                `To fix, either:\n` +
                `  - Remove --docs-only flag and run full gates\n` +
                `  - Change WU exposure to 'documentation' if this is truly a docs-only change`,
        ],
    };
}
/**
 * WU-1012: Build gates command with --docs-only flag support.
 */
export function buildGatesCommand(options) {
    const { docsOnly = false, isDocsOnly = false } = options;
    if (docsOnly || isDocsOnly) {
        return `${PKG_MANAGER} ${SCRIPTS.GATES} -- ${CLI_FLAGS.DOCS_ONLY}`;
    }
    return `${PKG_MANAGER} ${SCRIPTS.GATES}`;
}
/**
 * Enforce wu:brief evidence for feature and bug WUs.
 */
export function shouldEnforceWuBriefEvidence(doc) {
    return doc.type === WU_TYPES.FEATURE || doc.type === WU_TYPES.BUG;
}
function isWuBriefPolicyMode(value) {
    return (typeof value === 'string' &&
        WU_BRIEF_POLICY_MODES.includes(value));
}
export function resolveWuBriefPolicyMode(config = getConfig()) {
    const configured = config.wu?.brief?.policyMode;
    if (isWuBriefPolicyMode(configured)) {
        return configured;
    }
    return DEFAULT_WU_BRIEF_POLICY_MODE;
}
/**
 * Resolve wu:brief freshness threshold in minutes.
 * 0 disables freshness checks.
 */
export function resolveWuBriefFreshnessMinutes(config = getConfig()) {
    const configured = config.wu?.brief?.freshnessMinutes;
    if (typeof configured === 'number' && Number.isFinite(configured)) {
        if (configured <= 0) {
            return null;
        }
        return Math.floor(configured);
    }
    return DEFAULT_WU_BRIEF_FRESHNESS_MINUTES;
}
/**
 * Build remediation guidance when wu:brief evidence is missing.
 */
export function buildMissingWuBriefEvidenceMessage(id) {
    return (`Missing wu:brief evidence for ${id}.\n\n` +
        `Completion policy requires an auditable wu:brief execution record for feature/bug WUs.\n\n` +
        `Fix options:\n` +
        `  1. Generate handoff prompt + evidence (also works for self-implementation):\n` +
        `     pnpm wu:brief --id ${id} --client <client>\n` +
        `  2. Retry completion:\n` +
        `     pnpm wu:done --id ${id}\n` +
        `  3. Legacy/manual override (audited):\n` +
        `     pnpm wu:done --id ${id} --force`);
}
export function buildMissingWuBriefEvidenceMessageForPrep(id, mode) {
    return (`Missing wu:brief evidence for ${id} (policy=${mode}).\n\n` +
        `wu:prep enforces wu:brief evidence when policy mode is required.\n\n` +
        `Fix options:\n` +
        `  1. Generate handoff prompt + evidence (also works for self-implementation):\n` +
        `     pnpm wu:brief --id ${id} --client <client>\n` +
        `  2. Retry prep:\n` +
        `     pnpm wu:prep --id ${id}\n` +
        `  3. Emergency audited bypass (requires explicit reason):\n` +
        `     pnpm wu:prep --id ${id} --force --reason "<why bypass is required>"`);
}
/**
 * WU-2379: Build remediation guidance when only claim-auto evidence exists.
 * claim-auto is auto-recorded by wu:claim but does not generate the handoff
 * prompt or run lane/sizing checks — agents must run wu:brief explicitly.
 */
export function buildClaimAutoEvidenceMessage(id) {
    return (`wu:brief evidence for ${id} is claim-auto (auto-recorded by wu:claim).\n\n` +
        `claim-auto evidence does not satisfy completion policy because wu:claim only\n` +
        `records a stub — it does not generate the handoff prompt, check lane occupation,\n` +
        `or validate sizing. Agents must run wu:brief explicitly after wu:claim.\n\n` +
        `Fix:\n` +
        `  pnpm wu:brief --id ${id} --client <client>\n\n` +
        `Then retry completion:\n` +
        `  pnpm wu:done --id ${id}`);
}
function describeWuBriefFreshness(evidenceTimestamp, freshnessMinutes, now) {
    const ageMinutes = getWuBriefEvidenceAgeMinutes(evidenceTimestamp, now);
    if (ageMinutes === null) {
        return `Evidence timestamp is invalid: ${evidenceTimestamp}`;
    }
    return `Evidence age is ${ageMinutes} minute(s); threshold is ${freshnessMinutes} minute(s).`;
}
function buildStaleWuBriefEvidenceMessageForPrep(options) {
    return (`Stale wu:brief evidence for ${options.id} (policy=${options.mode}).\n\n` +
        `${describeWuBriefFreshness(options.evidenceTimestamp, options.freshnessMinutes, options.now)}\n\n` +
        `Fix options:\n` +
        `  1. If you need a fresh handoff prompt, rerun:\n` +
        `     pnpm wu:brief --id ${options.id} --client <client>\n` +
        `  2. If self-implementing, run wu:brief for context + evidence:\n` +
        `     pnpm wu:brief --id ${options.id} --client <client>\n` +
        `  3. Retry prep:\n` +
        `     pnpm wu:prep --id ${options.id}\n` +
        `  4. Emergency audited bypass (requires explicit reason):\n` +
        `     pnpm wu:prep --id ${options.id} --force --reason "<why stale evidence bypass is required>"`);
}
function buildStaleWuBriefEvidenceMessageForDone(options) {
    return (`Stale wu:brief evidence for ${options.id} (policy=${options.mode}).\n\n` +
        `${describeWuBriefFreshness(options.evidenceTimestamp, options.freshnessMinutes, options.now)}\n\n` +
        `Fix options:\n` +
        `  1. If you need a fresh handoff prompt, rerun:\n` +
        `     pnpm wu:brief --id ${options.id} --client <client>\n` +
        `  2. If self-implementing, run wu:brief for context + evidence:\n` +
        `     pnpm wu:brief --id ${options.id} --client <client>\n` +
        `  3. Retry completion:\n` +
        `     pnpm wu:done --id ${options.id}\n` +
        `  4. Legacy/manual override (audited):\n` +
        `     pnpm wu:done --id ${options.id} --force`);
}
export async function recordWuBriefPrepBypassAudit(options) {
    const baseDir = options.baseDir ?? process.cwd();
    const stateDir = resolveStateDir(baseDir);
    const store = new WUStateStore(stateDir);
    await store.checkpoint(options.wuId, `[wu:brief] prep force bypass accepted (${options.policyMode}): ${options.reason}`, {
        progress: 'wu:brief policy bypass',
        nextSteps: `policy=${options.policyMode}`,
    });
}
export async function enforceWuBriefEvidenceForPrep(id, doc, options = {}) {
    if (!shouldEnforceWuBriefEvidence(doc)) {
        return;
    }
    const mode = options.mode ?? resolveWuBriefPolicyMode();
    if (mode === 'off' || mode === 'manual') {
        return;
    }
    const baseDir = options.baseDir ?? process.cwd();
    const stateDir = resolveStateDir(baseDir);
    const force = options.force === true;
    const getBriefEvidenceFn = options.getBriefEvidenceFn ?? getLatestWuBriefEvidence;
    const blocker = options.blocker ?? ((message) => die(message));
    const warn = options.warn ?? console.warn;
    const recordBypassAudit = options.recordBypassAudit ?? recordWuBriefPrepBypassAudit;
    const freshnessMinutes = options.freshnessMinutes === undefined
        ? resolveWuBriefFreshnessMinutes()
        : options.freshnessMinutes;
    const now = options.now ?? new Date();
    let evidence;
    try {
        evidence = await getBriefEvidenceFn(stateDir, id);
    }
    catch (error) {
        if (mode === 'required' && !force) {
            blocker(buildWuBriefEvidenceReadFailureMessage(id, stateDir, error));
            return;
        }
        warn(`${PREP_LOG_PREFIX} ${EMOJI.WARNING} Could not verify wu:brief evidence for ${id}: ${getErrorMessage(error)}`);
        return;
    }
    if (evidence &&
        freshnessMinutes !== null &&
        isWuBriefEvidenceStale({ timestamp: evidence.timestamp, freshnessMinutes, now })) {
        if (mode === 'auto') {
            warn(`${PREP_LOG_PREFIX} ${EMOJI.WARNING} wu:brief evidence stale for ${id} (policy=auto).`);
            warn(buildStaleWuBriefEvidenceMessageForPrep({
                id,
                mode,
                evidenceTimestamp: evidence.timestamp,
                freshnessMinutes,
                now,
            }));
            return;
        }
        if (!force) {
            blocker(buildStaleWuBriefEvidenceMessageForPrep({
                id,
                mode,
                evidenceTimestamp: evidence.timestamp,
                freshnessMinutes,
                now,
            }));
            return;
        }
        const reason = typeof options.reason === 'string' ? options.reason.trim() : '';
        if (!reason) {
            blocker(PREP_FORCE_REASON_REQUIRED_MESSAGE);
            return;
        }
        await recordBypassAudit({ wuId: id, baseDir, reason, policyMode: mode });
        warn(`${PREP_LOG_PREFIX} ${EMOJI.WARNING} wu:brief policy override accepted for ${id} (policy=${mode}, reason="${reason}").`);
        return;
    }
    // WU-2379: Reject claim-auto evidence — it only records a stub, not actual wu:brief output.
    if (evidence && evidence.mode === CLAIM_AUTO_EVIDENCE_MODE) {
        const claimAutoMessage = buildClaimAutoEvidenceMessage(id);
        if (mode === 'auto') {
            warn(`${PREP_LOG_PREFIX} ${EMOJI.WARNING} ${claimAutoMessage}`);
            return;
        }
        if (!force) {
            blocker(claimAutoMessage);
            return;
        }
    }
    if (evidence) {
        return;
    }
    if (mode === 'auto') {
        warn(`${PREP_LOG_PREFIX} ${EMOJI.WARNING} wu:brief evidence missing for ${id} (policy=auto).`);
        warn(buildMissingWuBriefEvidenceMessageForPrep(id, mode));
        return;
    }
    if (!force) {
        blocker(buildMissingWuBriefEvidenceMessageForPrep(id, mode));
        return;
    }
    const reason = typeof options.reason === 'string' ? options.reason.trim() : '';
    if (!reason) {
        blocker(PREP_FORCE_REASON_REQUIRED_MESSAGE);
        return;
    }
    await recordBypassAudit({ wuId: id, baseDir, reason, policyMode: mode });
    warn(`${PREP_LOG_PREFIX} ${EMOJI.WARNING} wu:brief policy override accepted for ${id} (policy=${mode}, reason="${reason}").`);
}
function buildWuBriefEvidenceReadFailureMessage(id, stateDir, error) {
    return (`Could not verify wu:brief evidence for ${id}.\n\n` +
        `State path: ${stateDir}\n` +
        `Error: ${getErrorMessage(error)}\n\n` +
        `Fix options:\n` +
        `  1. Repair/restore state store, then rerun wu:done\n` +
        `  2. Use --force for audited override when recovery is not possible`);
}
export async function enforceWuBriefEvidenceForDone(id, doc, options = {}) {
    if (!shouldEnforceWuBriefEvidence(doc)) {
        return;
    }
    const baseDir = options.baseDir ?? process.cwd();
    const mode = options.mode ?? resolveWuBriefPolicyMode();
    const force = options.force === true;
    const freshnessMinutes = options.freshnessMinutes === undefined
        ? resolveWuBriefFreshnessMinutes()
        : options.freshnessMinutes;
    const now = options.now ?? new Date();
    const stateDir = resolveStateDir(baseDir);
    const getBriefEvidenceFn = options.getBriefEvidenceFn ?? getLatestWuBriefEvidence;
    const blocker = options.blocker ?? ((message) => die(message));
    const warn = options.warn ?? console.warn;
    let evidence;
    try {
        evidence = await getBriefEvidenceFn(stateDir, id);
    }
    catch (error) {
        if (!force) {
            blocker(buildWuBriefEvidenceReadFailureMessage(id, stateDir, error));
            return;
        }
        warn(`${LOG_PREFIX.DONE} ${EMOJI.WARNING} WU-2132: brief evidence verification failed for ${id}, override accepted via --force`);
        return;
    }
    if (evidence &&
        freshnessMinutes !== null &&
        isWuBriefEvidenceStale({ timestamp: evidence.timestamp, freshnessMinutes, now })) {
        if (mode === 'off' || mode === 'manual') {
            return;
        }
        if (mode === 'auto') {
            warn(`${LOG_PREFIX.DONE} ${EMOJI.WARNING} WU-2289: stale wu:brief evidence for ${id}`);
            warn(buildStaleWuBriefEvidenceMessageForDone({
                id,
                mode,
                evidenceTimestamp: evidence.timestamp,
                freshnessMinutes,
                now,
            }));
            return;
        }
        if (!force) {
            blocker(buildStaleWuBriefEvidenceMessageForDone({
                id,
                mode,
                evidenceTimestamp: evidence.timestamp,
                freshnessMinutes,
                now,
            }));
            return;
        }
        warn(`${LOG_PREFIX.DONE} ${EMOJI.WARNING} WU-2289: stale brief evidence override accepted for ${id} via --force`);
        return;
    }
    // WU-2379: Reject claim-auto evidence — it only records a stub, not actual wu:brief output.
    if (evidence && evidence.mode === CLAIM_AUTO_EVIDENCE_MODE) {
        const claimAutoMessage = buildClaimAutoEvidenceMessage(id);
        if (!force) {
            blocker(claimAutoMessage);
            return;
        }
        warn(`${LOG_PREFIX.DONE} ${EMOJI.WARNING} WU-2379: claim-auto evidence override accepted for ${id} via --force`);
        return;
    }
    if (evidence) {
        return;
    }
    if (!force) {
        blocker(buildMissingWuBriefEvidenceMessage(id));
        return;
    }
    warn(`${LOG_PREFIX.DONE} ${EMOJI.WARNING} WU-2132: brief evidence override accepted for ${id} via --force`);
}
/**
 * Returns true when completion should enforce spawn provenance.
 */
export function shouldEnforceSpawnProvenance(doc) {
    return typeof doc?.initiative === 'string' && doc.initiative.trim().length > 0;
}
/**
 * Build actionable remediation guidance for missing spawn provenance.
 */
export function buildMissingSpawnProvenanceMessage(id, initiativeId) {
    return (`Missing spawn provenance for initiative-governed WU ${id} (${initiativeId}).\n\n` +
        `This completion path enforces auditable delegation lineage for initiative work.\n\n` +
        `Fix options:\n` +
        `  1. Re-run with --force for an audited override (legacy/manual workflow)\n` +
        `  2. Register spawn lineage before completion (preferred):\n` +
        `     pnpm wu:delegate --id ${id} --parent-wu WU-XXXX --client codex-cli\n\n` +
        `Then retry: pnpm wu:done --id ${id}`);
}
/**
 * Build actionable remediation guidance when delegation intent exists but the registry record is missing.
 */
export function buildMissingSpawnRegistryMessage(id, initiativeId) {
    return (`Missing delegation registry provenance for delegated WU ${id} (${initiativeId}).\n\n` +
        `A durable delegation-intent marker exists in wu-events.jsonl, but no delegation registry entry was found.\n` +
        `This indicates lost or incomplete delegation provenance, not ordinary manual work.\n\n` +
        `Fix options:\n` +
        `  1. Restore delegation-registry.jsonl from the delegating checkout or repair the state store\n` +
        `  2. Re-run with --force for an audited override when recovery is not possible\n\n` +
        `Then retry: pnpm wu:done --id ${id}`);
}
/**
 * Build actionable remediation guidance for intent-only spawn provenance.
 */
export function buildMissingSpawnPickupEvidenceMessage(id, initiativeId) {
    return (`Missing pickup evidence for initiative-governed WU ${id} (${initiativeId}).\n\n` +
        `Delegation intent exists, but this WU has no claim-time pickup handshake.\n` +
        `Completion policy requires both intent and pickup evidence.\n\n` +
        `Fix options:\n` +
        `  1. Re-run with --force for an audited override (legacy/manual claim)\n` +
        `  2. Ensure future delegated work is picked up via wu:claim (records handshake automatically)\n\n` +
        `Then retry: pnpm wu:done --id ${id}`);
}
/**
 * Build actionable remediation guidance for missing brief attestation.
 */
export function buildMissingSpawnBriefAttestationMessage(id, initiativeId) {
    return (`Missing brief attestation for delegated WU ${id} (${initiativeId}).\n\n` +
        `Delegation provenance exists, but no attested wu:brief payload hash was stored.\n` +
        `Completion policy now requires auditable prompt-hash attestation.\n\n` +
        `Fix options:\n` +
        `  1. Re-run delegation from parent WU using full wu:delegate output:\n` +
        `     pnpm wu:delegate --id ${id} --parent-wu WU-XXXX --client codex-cli\n` +
        `  2. Re-run with --force for audited legacy override\n\n` +
        `Then retry: pnpm wu:done --id ${id}`);
}
/**
 * Build actionable remediation guidance for attestation hash mismatch.
 */
export function buildSpawnBriefAttestationMismatchMessage(id, initiativeId) {
    return (`Spawn brief attestation mismatch for delegated WU ${id} (${initiativeId}).\n\n` +
        `Stored delegation hash could not be matched to wu:brief evidence for this WU.\n` +
        `This indicates the delegated prompt was modified, condensed, or not generated through wu:delegate.\n\n` +
        `Fix options:\n` +
        `  1. Re-run delegation using full wu:delegate output for ${id}\n` +
        `  2. Re-run with --force for audited legacy override\n\n` +
        `Then retry: pnpm wu:done --id ${id}`);
}
/**
 * Returns true when spawn provenance includes claim-time pickup evidence.
 */
export function hasSpawnPickupEvidence(spawnEntry) {
    const pickedUpAt = typeof spawnEntry?.pickedUpAt === 'string' && spawnEntry.pickedUpAt.trim().length > 0
        ? spawnEntry.pickedUpAt
        : '';
    const pickedUpBy = typeof spawnEntry?.pickedUpBy === 'string' && spawnEntry.pickedUpBy.trim().length > 0
        ? spawnEntry.pickedUpBy
        : '';
    return pickedUpAt.length > 0 && pickedUpBy.length > 0;
}
/**
 * Returns true when spawn provenance includes delegation intent.
 */
export function hasDelegationIntent(spawnEntry) {
    return typeof spawnEntry?.intent === 'string' && spawnEntry.intent.trim() === 'delegation';
}
/**
 * Returns true when spawn provenance includes brief prompt-hash attestation.
 */
export function hasSpawnBriefAttestation(spawnEntry) {
    const attestation = spawnEntry?.briefAttestation;
    if (!attestation) {
        return false;
    }
    return (attestation.algorithm === 'sha256' &&
        typeof attestation.promptHash === 'string' &&
        /^[a-f0-9]{64}$/.test(attestation.promptHash));
}
/**
 * Record forced spawn-provenance bypass in memory signals for auditability.
 */
async function recordSpawnProvenanceOverride(id, doc, baseDir = process.cwd()) {
    try {
        const initiativeId = typeof doc?.initiative === 'string' ? doc.initiative.trim() : 'unknown';
        const lane = typeof doc?.lane === 'string' ? doc.lane : undefined;
        const result = await createSignal(baseDir, {
            message: `spawn-provenance override used for ${id} in ${initiativeId} via --force`,
            wuId: id,
            lane,
        });
        if (result.success) {
            console.log(`${LOG_PREFIX.DONE} ${EMOJI.INFO} Spawn-provenance override recorded (${result.signal.id})`);
        }
    }
    catch (err) {
        console.warn(`${LOG_PREFIX.DONE} ${EMOJI.WARNING} Could not record spawn-provenance override: ${getErrorMessage(err)}`);
    }
}
/**
 * Enforce spawn provenance policy for initiative-governed and explicitly delegated WUs.
 *
 * WU-2359: Non-spawned initiative WUs (no delegation registry entry) get a warning
 * instead of a hard-block. Only explicitly delegated/spawned WUs are hard-blocked
 * when provenance is incomplete.
 */
export async function enforceSpawnProvenanceForDone(id, doc, options = {}) {
    const initiativeId = typeof doc.initiative === 'string' && doc.initiative.trim() ? doc.initiative.trim() : 'unknown';
    const enforceForInitiative = shouldEnforceSpawnProvenance(doc);
    const baseDir = options.baseDir ?? process.cwd();
    const force = options.force === true;
    const warn = options.warn ?? console.warn;
    const blocker = options.blocker ?? ((message) => die(message));
    const stateDir = resolveStateDir(baseDir);
    const hasDelegationMarker = options._hasDelegationMarkerOverride
        ? await options._hasDelegationMarkerOverride(stateDir, id)
        : await hasDelegationIntentMarker(stateDir, id);
    let spawnEntry;
    if (options._storeOverride) {
        spawnEntry = options._storeOverride.getByTarget(id);
    }
    else {
        const store = new DelegationRegistryStore(stateDir);
        await store.load();
        spawnEntry = store.getByTarget(id);
    }
    const enforceForDelegation = hasDelegationMarker || hasDelegationIntent(spawnEntry);
    if (!enforceForInitiative && !enforceForDelegation) {
        return;
    }
    // WU-2359: No spawn entry means this WU was NOT created through spawn/delegate.
    // It just has an initiative: field. Warn instead of blocking.
    if (!spawnEntry) {
        if (hasDelegationMarker) {
            if (!force) {
                blocker(buildMissingSpawnRegistryMessage(id, initiativeId));
                return;
            }
            warn(`${LOG_PREFIX.DONE} ${EMOJI.WARNING} WU-2362: missing delegation registry provenance override accepted for ${id} (${initiativeId}) via --force`);
            await recordSpawnProvenanceOverride(id, doc, baseDir);
            return;
        }
        if (force) {
            warn(`${LOG_PREFIX.DONE} ${EMOJI.WARNING} WU-1599: spawn provenance override accepted for ${id} (${initiativeId}) via --force`);
            await recordSpawnProvenanceOverride(id, doc, baseDir);
            return;
        }
        // Non-spawned initiative WU: warn only (WU-2359)
        warn(`${LOG_PREFIX.DONE} ${EMOJI.WARNING} WU-2359: No spawn provenance for initiative WU ${id} (${initiativeId}). ` +
            `This is expected for non-orchestrated work. ` +
            `Delegation lineage must be recorded at delegation time, not retroactively during wu:done.`);
        return;
    }
    // From here on, spawnEntry exists — this WU was explicitly spawned/delegated.
    // These paths remain hard-blocking because the delegation ceremony was started.
    if (hasSpawnPickupEvidence(spawnEntry)) {
        if (!hasSpawnBriefAttestation(spawnEntry)) {
            if (!force) {
                blocker(buildMissingSpawnBriefAttestationMessage(id, initiativeId));
                return;
            }
            warn(`${LOG_PREFIX.DONE} ${EMOJI.WARNING} WU-2301: brief attestation override accepted for ${id} (${initiativeId}) via --force`);
            await recordSpawnProvenanceOverride(id, doc, baseDir);
            return;
        }
        const attestedPromptHash = spawnEntry.briefAttestation?.promptHash;
        if (!attestedPromptHash) {
            if (!force) {
                blocker(buildMissingSpawnBriefAttestationMessage(id, initiativeId));
                return;
            }
            warn(`${LOG_PREFIX.DONE} ${EMOJI.WARNING} WU-2301: missing prompt hash override accepted for ${id} (${initiativeId}) via --force`);
            await recordSpawnProvenanceOverride(id, doc, baseDir);
            return;
        }
        let hashMatchesEvidence;
        try {
            hashMatchesEvidence = await hasMatchingWuBriefEvidenceHash(stateDir, id, attestedPromptHash);
        }
        catch (error) {
            if (!force) {
                blocker(`Could not verify spawn brief attestation for ${id}.\n\n` +
                    `State path: ${stateDir}\n` +
                    `Error: ${getErrorMessage(error)}\n\n` +
                    `Fix options:\n` +
                    `  1. Repair/restore state store, then rerun wu:done\n` +
                    `  2. Use --force for audited override when recovery is not possible`);
                return;
            }
            warn(`${LOG_PREFIX.DONE} ${EMOJI.WARNING} WU-2301: attestation verification failure override accepted for ${id} (${initiativeId}) via --force`);
            await recordSpawnProvenanceOverride(id, doc, baseDir);
            return;
        }
        if (hashMatchesEvidence) {
            return;
        }
        if (!force) {
            blocker(buildSpawnBriefAttestationMismatchMessage(id, initiativeId));
            return;
        }
        warn(`${LOG_PREFIX.DONE} ${EMOJI.WARNING} WU-2301: attestation mismatch override accepted for ${id} (${initiativeId}) via --force`);
        await recordSpawnProvenanceOverride(id, doc, baseDir);
        return;
    }
    if (!force) {
        blocker(buildMissingSpawnPickupEvidenceMessage(id, initiativeId));
        return;
    }
    warn(`${LOG_PREFIX.DONE} ${EMOJI.WARNING} WU-1605: pickup evidence override accepted for ${id} (${initiativeId}) via --force`);
    await recordSpawnProvenanceOverride(id, doc, baseDir);
}
//# sourceMappingURL=wu-done-policies.js.map