"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.discoverCanonicalWork = discoverCanonicalWork;
exports.projectCanonicalWorkCandidate = projectCanonicalWorkCandidate;
const node_crypto_1 = require("node:crypto");
const node_fs_1 = __importDefault(require("node:fs"));
const node_path_1 = __importDefault(require("node:path"));
const errors_1 = require("../errors");
const paths_1 = require("../paths");
const workspace_discovery_1 = require("../workspace-discovery");
const artifact_policy_projection_1 = require("./artifact-policy-projection");
const basic_workflow_1 = require("./basic-workflow");
const next_action_resolver_1 = require("./next-action-resolver");
const ordered_root_identity_1 = require("./ordered-root-identity");
const planning_artifact_document_1 = require("./planning-artifact-document");
const policy_resolution_1 = require("./policy-resolution");
const task_attempts_1 = require("./task-attempts");
const task_cycle_runtime_attribution_1 = require("./task-cycle-runtime-attribution");
const task_lifecycle_1 = require("./task-lifecycle");
const task_validation_1 = require("./task-validation");
const DEFAULT_SCAN_BUDGETS = Object.freeze({
    files: 25_000,
    directories: 10_000,
    metadataBytes: 32_000_000,
    contentBytes: 1,
    elapsedMs: 5_000,
    evidenceEntries: 50_000,
});
function discoverCanonicalWork(options) {
    const planningRootPath = (0, paths_1.parseRepositoryRelativePath)(options.planningRootPath ?? '.day-shift/planning').normalizedPath;
    const absolutePlanningRoot = (0, paths_1.resolveRepositoryRelativePath)(options.repositoryRoot, (0, paths_1.parseRepositoryRelativePath)(planningRootPath));
    if (!node_fs_1.default.existsSync(absolutePlanningRoot) || !node_fs_1.default.statSync(absolutePlanningRoot).isDirectory()) {
        throw (0, errors_1.createUsageError)(`Planning root '${planningRootPath}' does not exist or is not a directory.`);
    }
    const scan = (0, workspace_discovery_1.discoverWorkspacePaths)({
        workspaceRoot: absolutePlanningRoot,
        budgets: { ...DEFAULT_SCAN_BUDGETS, ...options.budgets },
        generatedAt: options.generatedAt ?? 'not-recorded',
        include: (entry) => entry.kind === 'file' && (entry.path === 'task-definition.md' || entry.path.endsWith('/task-definition.md')),
    });
    const discovery = {
        ...scan.evidence,
        usage: { ...scan.evidence.usage, elapsedMs: 0 },
    };
    const taskPaths = scan.paths
        .map((relativePath) => toRepositoryPath(node_path_1.default.join(planningRootPath, relativePath)))
        .sort((left, right) => left.localeCompare(right));
    const drafts = taskPaths.map((taskPath) => readCandidate(options.repositoryRoot, taskPath));
    const findings = [];
    applyDuplicateIdentityBlockers(drafts, findings);
    applyOrderedDirectoryDuplicateBlockers(drafts, findings);
    const projectAware = (options.projectContext?.projects.length ?? 0) > 0;
    if (projectAware) {
        applyProjectEvidence(drafts, new Set(options.projectContext?.projects.map((project) => project.id) ?? []));
    }
    const candidates = drafts
        .map((draft) => finalizeCandidate(draft.candidate))
        .sort((left, right) => left.artifactPath.localeCompare(right.artifactPath));
    if (!scan.evidence.complete) {
        findings.push({
            code: 'INCOMPLETE_DISCOVERY',
            paths: scan.evidence.omission?.nextPath === undefined ? [] : [scan.evidence.omission.nextPath],
            message: `Canonical work discovery exhausted its ${scan.evidence.exhaustedBudget ?? 'configured'} budget; results are incomplete.`,
        });
    }
    const repositoryView = createWorkDiscoveryView('repository', null, candidates, findings);
    const selectedProjectId = projectAware ? (options.projectContext?.projectId ?? null) : null;
    const projectCandidates = selectedProjectId === null
        ? []
        : candidates.filter((candidate) => candidate.project.ownerProjectId === selectedProjectId ||
            candidate.project.sharedProjectIds.includes(selectedProjectId));
    const projectView = selectedProjectId === null ? null : createWorkDiscoveryView('project', selectedProjectId, projectCandidates, []);
    const activeView = projectView ?? repositoryView;
    return {
        planningRootPath,
        candidates: activeView.candidates,
        findings: activeView.findings,
        implicitSelection: activeView.implicitSelection,
        discovery,
        indexEvidence: { used: false, required: false, status: 'not-consulted' },
        readOnly: true,
        ...(projectAware
            ? {
                context: options.projectContext,
                repositoryView,
                projectView,
            }
            : {}),
    };
}
function projectCanonicalWorkCandidate(options) {
    const artifactPath = (0, paths_1.parseRepositoryRelativePath)(options.artifactPath).normalizedPath;
    const absolutePath = (0, paths_1.resolveRepositoryRelativePath)(options.repositoryRoot, (0, paths_1.parseRepositoryRelativePath)(artifactPath));
    if (!node_fs_1.default.existsSync(absolutePath) || !node_fs_1.default.statSync(absolutePath).isFile())
        return null;
    const text = node_fs_1.default.readFileSync(absolutePath, 'utf8');
    if (!/^artifact_type:\s*["']?task_definition["']?\s*$/m.test(text))
        return null;
    return finalizeCandidate(readCandidate(options.repositoryRoot, artifactPath).candidate);
}
function readCandidate(repositoryRoot, artifactPath) {
    const taskText = readRepositoryFile(repositoryRoot, artifactPath);
    const taskDocument = (0, planning_artifact_document_1.parsePlanningArtifactDocument)(taskText);
    const policy = (0, artifact_policy_projection_1.projectArtifactPolicy)(taskText);
    const summaryPath = toRepositoryPath(node_path_1.default.posix.join(node_path_1.default.posix.dirname(artifactPath), 'implementation-summary.md'));
    const summaryText = readRepositoryFileIfExists(repositoryRoot, summaryPath);
    const summaryPolicy = summaryText === null ? null : (0, artifact_policy_projection_1.projectArtifactPolicy)(summaryText);
    const lifecyclePair = (0, task_lifecycle_1.projectTaskLifecyclePair)({
        taskDefinition: taskText,
        implementationSummary: summaryText,
    });
    const summaryDocument = summaryText === null ? null : (0, planning_artifact_document_1.parsePlanningArtifactDocument)(summaryText);
    const authorizationState = deriveAuthorizationState(lifecyclePair);
    const attemptState = deriveCurrentAttemptState(repositoryRoot, artifactPath, taskText, summaryPath, summaryText, lifecyclePair);
    const collaborationPolicy = (0, policy_resolution_1.resolveCollaborationPolicy)({
        task: policy.agentModeSource === 'built-in' ? null : policy.effectiveAgentMode,
    });
    const taskId = policy.taskId ?? fallbackTaskId(artifactPath);
    const workflowLevel = policy.workflowLevel ?? 'basic';
    const evidenceProfile = policy.evidenceProfile ?? 'runtime';
    const blockers = policy.blockingFindings.map((finding) => ({
        code: finding.code,
        field: finding.field,
        path: artifactPath,
        message: finding.message,
    }));
    if (attemptState.inspectionIssue !== null) {
        blockers.push({
            code: 'attempt-target-inspection-unavailable',
            field: 'attempts.runtime_attribution_baseline',
            path: summaryPath,
            message: attemptState.inspectionIssue,
        });
    }
    if (summaryPolicy !== null) {
        blockers.push(...summaryPolicy.blockingFindings.map((finding) => ({
            code: `summary-${finding.code}`,
            field: finding.field,
            path: summaryPath,
            message: finding.message,
        })));
        if (summaryPolicy.taskId !== null && summaryPolicy.taskId !== taskId) {
            blockers.push({
                code: 'paired-task-identity-mismatch',
                field: 'id',
                path: summaryPath,
                message: `Paired implementation summary identity '${summaryPolicy.taskId}' does not match task '${taskId}'.`,
            });
        }
    }
    blockers.push(...lifecyclePair.blockingFindings
        .filter((finding) => finding.code !== 'stale-readiness-authorization' && finding.code !== 'stale-attempt-readiness-reference')
        .map((finding) => ({
        code: `lifecycle-${finding.code}`,
        field: finding.field,
        path: finding.artifact === 'implementation_summary' ? summaryPath : artifactPath,
        message: finding.message,
    })));
    const validation = deriveValidationProjection(repositoryRoot, summaryPath, summaryText, lifecyclePair);
    blockers.push(...validation.invalidFindings.map((finding) => ({
        code: finding.code,
        field: 'attempts.validation_results',
        path: summaryPath,
        message: finding.message,
    })));
    const evidenceState = deriveEvidenceState(summaryText, lifecyclePair);
    const ownerIdentity = deriveOwnerIdentity(taskDocument.frontMatterFields, workflowLevel, policy.parents);
    const orderedDirectoryProjection = deriveOrderedDirectoryEvidence({
        repositoryRoot,
        artifactPath,
        workflowLevel,
        taskId,
        workId: ownerIdentity.kind === 'work' ? ownerIdentity.value : null,
        taskFields: taskDocument.frontMatterFields,
        summaryFields: summaryDocument?.frontMatterFields ?? null,
        parents: policy.parents,
    });
    blockers.push(...orderedDirectoryProjection.blockers);
    const identityKey = [workflowLevel, ownerIdentity.value ?? '-', taskId].join(':');
    if (workflowLevel === 'governed') {
        blockers.push(...validateCanonicalGovernedOwner({
            artifactPath,
            sliceId: ownerIdentity.value,
            parents: policy.parents,
        }));
    }
    const ownerProjectId = taskDocument.frontMatterFields.project_id?.trim() || null;
    const sharedProjectIds = parseInlineStringList(taskDocument.frontMatterFields.shared_project_ids);
    if (sharedProjectIds.invalid) {
        blockers.push({
            code: 'invalid-shared-project-ids',
            field: 'shared_project_ids',
            path: artifactPath,
            message: 'shared_project_ids must be an inline array of unique machine-safe project ids.',
        });
    }
    return {
        identityKey,
        dependencyPaths: readDependencyPaths(taskDocument),
        candidate: {
            artifactPath,
            implementationSummaryPath: summaryPath,
            workflowLevel,
            workflowLevelSource: policy.workflowLevelSource ?? 'built-in',
            ...(workflowLevel === 'governed' ? { sliceId: ownerIdentity.value } : { workId: ownerIdentity.value }),
            taskId,
            taskIdentitySource: policy.taskIdentitySource ?? 'task',
            orderedDirectory: orderedDirectoryProjection.evidence,
            declaredAgentMode: policy.declaredAgentMode,
            effectiveAgentMode: collaborationPolicy.mode,
            agentModeSource: collaborationPolicy.source,
            collaborationPolicy,
            evidenceProfile,
            evidenceProfileSource: policy.evidenceProfileSource ?? 'built-in',
            lifecycle: {
                taskRevision: (0, node_crypto_1.createHash)('sha256').update(Buffer.from(taskText)).digest('hex'),
                readiness: lifecyclePair.task.readiness,
                status: lifecyclePair.task.status,
                authorizationState,
                currentAttemptState: attemptState.state,
                currentAttemptId: attemptState.attemptId,
                baselineId: attemptState.baselineId,
                implementationStatus: effectiveImplementationStatus(lifecyclePair),
                reviewDisposition: effectiveReviewDisposition(lifecyclePair),
                validationState: validation.state,
            },
            objective: sectionContent(taskDocument, 'Objective'),
            scope: sectionContent(taskDocument, 'Scope'),
            validationExpectations: sectionContent(taskDocument, 'Validation Commands') || sectionContent(taskDocument, 'Validation Focus'),
            parentRelationships: policy.parents,
            compatibilityEvidence: [
                ...policy.compatibilityEvidence,
                ...(summaryPolicy?.compatibilityEvidence ?? []),
                ...lifecyclePair.compatibilityEvidence.map((entry) => ({
                    code: entry.code,
                    field: entry.field,
                    sourceValue: entry.sourceValue,
                    projectedValue: entry.projectedValue,
                })),
            ],
            evidenceState,
            status: 'blocked',
            blockers,
            nextAction: (0, next_action_resolver_1.resolveNextAction)({
                invalidStateFindings: [
                    {
                        code: 'candidate-not-finalized',
                        message: 'Candidate finalization is pending.',
                        path: artifactPath,
                    },
                ],
            }),
            commandRouting: {
                id: 'repair-policy',
                reason: 'Candidate finalization is pending.',
            },
            basicWorkflow: workflowLevel === 'basic'
                ? (0, basic_workflow_1.projectBasicWorkflow)({
                    taskId,
                    taskStatus: lifecyclePair.task.status,
                    implementationStatus: effectiveImplementationStatus(lifecyclePair),
                })
                : null,
            project: {
                ownerProjectId,
                sharedProjectIds: sharedProjectIds.values,
                visibility: ownerProjectId === null ? 'repository' : 'project',
                dependencies: [],
            },
        },
    };
}
function applyDuplicateIdentityBlockers(drafts, findings) {
    const byIdentity = new Map();
    for (const draft of drafts) {
        const group = byIdentity.get(draft.identityKey) ?? [];
        group.push(draft);
        byIdentity.set(draft.identityKey, group);
    }
    for (const [identity, group] of byIdentity) {
        if (group.length < 2)
            continue;
        const paths = group.map((entry) => entry.candidate.artifactPath).sort((a, b) => a.localeCompare(b));
        findings.push({
            code: 'DUPLICATE_ACTIVE_IDENTITY',
            paths,
            message: `Stable work identity '${identity}' is present at multiple active locations.`,
        });
        for (const entry of group) {
            entry.candidate.blockers = [
                ...entry.candidate.blockers,
                {
                    code: 'duplicate-active-identity',
                    field: 'identity',
                    path: entry.candidate.artifactPath,
                    message: `Stable work identity '${identity}' is duplicated at: ${paths.join(', ')}.`,
                },
            ];
        }
    }
}
function deriveOrderedDirectoryEvidence(input) {
    if (input.workflowLevel === 'governed')
        return { evidence: null, blockers: [] };
    const blockers = [];
    const observedTaskRootPath = node_path_1.default.posix.dirname(input.artifactPath);
    if (input.workflowLevel === 'basic') {
        const rootIdentity = projectObservedIdentity('basic-root', node_path_1.default.posix.basename(observedTaskRootPath), input.taskId, input.artifactPath, blockers);
        const taskOrder = readOrderedRootMetadata(input.taskFields.implementation_order, 'implementation_order', input.artifactPath, blockers);
        const summaryOrder = input.summaryFields === null
            ? null
            : readOrderedRootMetadata(input.summaryFields.implementation_order, 'implementation_order', toRepositoryPath(node_path_1.default.posix.join(observedTaskRootPath, 'implementation-summary.md')), blockers);
        if (rootIdentity.shape === 'canonical' && taskOrder === null) {
            blockers.push(orderedBlocker('missing-root-implementation-order', 'implementation_order', input.artifactPath, `Numbered Basic root '${observedTaskRootPath}' requires implementation_order metadata.`));
        }
        if (rootIdentity.orderPrefix !== null && taskOrder !== null && rootIdentity.orderPrefix !== taskOrder) {
            blockers.push(orderedBlocker('root-order-mismatch', 'implementation_order', input.artifactPath, `Basic root prefix '${rootIdentity.orderPrefix}' does not match implementation_order '${taskOrder}'.`));
        }
        if (input.summaryFields !== null && taskOrder !== summaryOrder) {
            blockers.push(orderedBlocker('paired-root-order-mismatch', 'implementation_order', toRepositoryPath(node_path_1.default.posix.join(observedTaskRootPath, 'implementation-summary.md')), 'Basic task definition and implementation summary must agree on implementation_order.'));
        }
        return {
            evidence: {
                rootContext: 'basic-root',
                rootStableId: input.taskId,
                rootImplementationOrder: taskOrder,
                rootShape: rootIdentity.shape,
                observedRootPath: observedTaskRootPath,
                taskSequence: null,
                taskSequencePrefix: null,
                taskShape: null,
                observedTaskRootPath,
                migrationCandidate: rootIdentity.shape === 'legacy',
            },
            blockers,
        };
    }
    const workOverviewPath = input.parents.find((entry) => entry.kind === 'work_overview')?.path ??
        deriveStructuredOverviewPathFromTask(input.artifactPath);
    const observedRootPath = node_path_1.default.posix.dirname(workOverviewPath);
    const rootStableId = input.workId ?? node_path_1.default.posix.basename(observedRootPath);
    const rootIdentity = projectObservedIdentity('structured-root', node_path_1.default.posix.basename(observedRootPath), rootStableId, input.artifactPath, blockers);
    const overviewText = readRepositoryFileIfExists(input.repositoryRoot, workOverviewPath);
    const overviewFields = overviewText === null ? null : (0, planning_artifact_document_1.parsePlanningArtifactDocument)(overviewText).frontMatterFields;
    const rootOrder = readOrderedRootMetadata(overviewFields?.implementation_order, 'implementation_order', workOverviewPath, blockers);
    if (rootIdentity.shape === 'canonical' && rootOrder === null) {
        blockers.push(orderedBlocker('missing-root-implementation-order', 'implementation_order', workOverviewPath, `Numbered Structured root '${observedRootPath}' requires overview implementation_order metadata.`));
    }
    if (rootIdentity.orderPrefix !== null && rootOrder !== null && rootIdentity.orderPrefix !== rootOrder) {
        blockers.push(orderedBlocker('root-order-mismatch', 'implementation_order', workOverviewPath, `Structured root prefix '${rootIdentity.orderPrefix}' does not match implementation_order '${rootOrder}'.`));
    }
    const taskIdentity = projectObservedIdentity('structured-task', node_path_1.default.posix.basename(observedTaskRootPath), input.taskId, input.artifactPath, blockers);
    const taskSequence = readStructuredTaskSequence(input.taskFields.sequence, input.artifactPath, blockers);
    const sequencePrefix = taskSequence === null ? null : String(taskSequence).padStart(2, '0');
    if (taskIdentity.shape === 'canonical' && taskSequence === null) {
        blockers.push(orderedBlocker('missing-task-sequence', 'sequence', input.artifactPath, `Numbered Structured task '${observedTaskRootPath}' requires a sequence from 1 through 99.`));
    }
    if (taskIdentity.orderPrefix !== null && sequencePrefix !== null && taskIdentity.orderPrefix !== sequencePrefix) {
        blockers.push(orderedBlocker('task-sequence-mismatch', 'sequence', input.artifactPath, `Structured task prefix '${taskIdentity.orderPrefix}' does not match sequence '${taskSequence}'.`));
    }
    return {
        evidence: {
            rootContext: 'structured-root',
            rootStableId,
            rootImplementationOrder: rootOrder,
            rootShape: rootIdentity.shape,
            observedRootPath,
            taskSequence,
            taskSequencePrefix: sequencePrefix,
            taskShape: taskIdentity.shape,
            observedTaskRootPath,
            migrationCandidate: rootIdentity.shape === 'legacy' || taskIdentity.shape === 'legacy',
        },
        blockers,
    };
}
function projectObservedIdentity(context, basename, stableId, artifactPath, blockers) {
    try {
        const parsed = (0, ordered_root_identity_1.parseOrderedDirectoryIdentity)(context, basename);
        if (basename === stableId) {
            return {
                context,
                basename,
                stableId,
                order: null,
                orderPrefix: null,
                shape: 'legacy',
            };
        }
        if (parsed.stableId !== stableId) {
            blockers.push(orderedBlocker('ordered-directory-identity-mismatch', 'path', artifactPath, `${context} directory '${basename}' does not match stable identity '${stableId}'.`));
        }
        return parsed;
    }
    catch (error) {
        blockers.push(orderedBlocker('invalid-ordered-directory', 'path', artifactPath, error instanceof Error ? error.message : `Invalid ${context} directory '${basename}'.`));
        return {
            context,
            basename,
            stableId,
            order: null,
            orderPrefix: null,
            shape: 'legacy',
        };
    }
}
function readOrderedRootMetadata(value, field, artifactPath, blockers) {
    if (value === undefined || value.trim() === '')
        return null;
    try {
        return (0, ordered_root_identity_1.normalizeImplementationOrder)(value, field);
    }
    catch (error) {
        blockers.push(orderedBlocker('invalid-root-implementation-order', field, artifactPath, error instanceof Error ? error.message : `${field} is invalid.`));
        return null;
    }
}
function readStructuredTaskSequence(value, artifactPath, blockers) {
    const normalized = value?.trim() ?? '';
    if (normalized === '')
        return null;
    if (!/^(?:[1-9]|[1-9]\d)$/u.test(normalized)) {
        blockers.push(orderedBlocker('invalid-task-sequence', 'sequence', artifactPath, 'Structured task sequence must be an integer from 1 through 99.'));
        return null;
    }
    return Number.parseInt(normalized, 10);
}
function deriveStructuredOverviewPathFromTask(artifactPath) {
    const marker = '/tasks/';
    const markerIndex = artifactPath.lastIndexOf(marker);
    return markerIndex < 0
        ? node_path_1.default.posix.join(node_path_1.default.posix.dirname(artifactPath), 'overview.md')
        : `${artifactPath.slice(0, markerIndex)}/overview.md`;
}
function orderedBlocker(code, field, pathValue, message) {
    return { code, field, path: pathValue, message };
}
function applyOrderedDirectoryDuplicateBlockers(drafts, findings) {
    const ordered = drafts.filter((draft) => draft.candidate.orderedDirectory !== null);
    const uniqueRoots = new Map();
    for (const draft of ordered) {
        const evidence = draft.candidate.orderedDirectory;
        const group = uniqueRoots.get(evidence.observedRootPath) ?? [];
        group.push(draft);
        uniqueRoots.set(evidence.observedRootPath, group);
    }
    const roots = [...uniqueRoots.entries()].map(([rootPath, group]) => ({
        rootPath,
        evidence: group[0].candidate.orderedDirectory,
        drafts: group,
    }));
    applyRootDuplicateGroups(roots, (entry) => `${entry.evidence.rootContext}:${entry.evidence.rootStableId}`, 'DUPLICATE_ACTIVE_IDENTITY', 'duplicate-active-root-identity', 'stable root identity', findings);
    applyRootDuplicateGroups(roots.filter((entry) => entry.evidence.rootImplementationOrder !== null), (entry) => `${entry.evidence.rootContext}:${entry.evidence.rootImplementationOrder}`, 'DUPLICATE_ROOT_ORDER', 'duplicate-root-order', 'level-local root order', findings);
    const bySequence = new Map();
    for (const draft of ordered) {
        const evidence = draft.candidate.orderedDirectory;
        if (evidence.taskSequence === null || evidence.taskShape === null)
            continue;
        const key = `${evidence.observedRootPath}:${evidence.taskSequence}`;
        const group = bySequence.get(key) ?? [];
        group.push(draft);
        bySequence.set(key, group);
    }
    for (const [identity, group] of bySequence) {
        if (group.length < 2)
            continue;
        const paths = group.map((entry) => entry.candidate.artifactPath).sort();
        findings.push({
            code: 'DUPLICATE_TASK_SEQUENCE',
            paths,
            message: `Parent-local Structured task sequence '${identity}' is present at multiple active locations.`,
        });
        for (const draft of group) {
            draft.candidate.blockers = [
                ...draft.candidate.blockers,
                orderedBlocker('duplicate-task-sequence', 'sequence', draft.candidate.artifactPath, `Parent-local task sequence '${identity}' is duplicated at: ${paths.join(', ')}.`),
            ];
        }
    }
}
function applyRootDuplicateGroups(roots, keyOf, findingCode, blockerCode, label, findings) {
    const groups = new Map();
    for (const root of roots) {
        const key = keyOf(root);
        groups.set(key, [...(groups.get(key) ?? []), root]);
    }
    for (const [identity, group] of groups) {
        if (group.length < 2)
            continue;
        const paths = group.flatMap((entry) => entry.drafts.map((draft) => draft.candidate.artifactPath)).sort();
        findings.push({
            code: findingCode,
            paths,
            message: `Ordered ${label} '${identity}' is present at multiple active roots.`,
        });
        for (const root of group) {
            for (const draft of root.drafts) {
                draft.candidate.blockers = [
                    ...draft.candidate.blockers,
                    orderedBlocker(blockerCode, 'identity', draft.candidate.artifactPath, `Ordered ${label} '${identity}' is ambiguous across: ${paths.join(', ')}.`),
                ];
            }
        }
    }
}
function applyProjectEvidence(drafts, configuredProjectIds) {
    const byPath = new Map(drafts.map((draft) => [draft.candidate.artifactPath, draft]));
    for (const draft of drafts) {
        const project = draft.candidate.project;
        const projectBlockers = [];
        if (project.ownerProjectId !== null && !configuredProjectIds.has(project.ownerProjectId)) {
            projectBlockers.push({
                code: 'unknown-project-owner',
                field: 'project_id',
                path: draft.candidate.artifactPath,
                message: `Project owner '${project.ownerProjectId}' is not declared in monorepo_projects.`,
            });
        }
        for (const sharedProjectId of project.sharedProjectIds) {
            if (!configuredProjectIds.has(sharedProjectId)) {
                projectBlockers.push({
                    code: 'unknown-shared-project',
                    field: 'shared_project_ids',
                    path: draft.candidate.artifactPath,
                    message: `Shared project '${sharedProjectId}' is not declared in monorepo_projects.`,
                });
            }
        }
        if (draft.candidate.workflowLevel === 'basic' && project.sharedProjectIds.length > 0) {
            projectBlockers.push({
                code: 'basic-work-cannot-be-shared',
                field: 'shared_project_ids',
                path: draft.candidate.artifactPath,
                message: 'Basic work is project-local; only Structured or Governed work may be explicitly shared.',
            });
        }
        const dependencies = draft.dependencyPaths
            .map((dependencyPath) => {
            let normalizedPath;
            try {
                normalizedPath = (0, paths_1.parseRepositoryRelativePath)(dependencyPath).normalizedPath;
            }
            catch {
                return {
                    path: dependencyPath,
                    targetProjectId: null,
                    relationship: 'invalid',
                };
            }
            const target = byPath.get(normalizedPath)?.candidate ?? null;
            if (target === null) {
                return {
                    path: normalizedPath,
                    targetProjectId: null,
                    relationship: 'missing',
                };
            }
            const sourceProjectId = project.ownerProjectId;
            const targetProjectId = target.project.ownerProjectId;
            const relationship = sourceProjectId !== null && target.project.sharedProjectIds.includes(sourceProjectId)
                ? 'shared'
                : sourceProjectId !== null && targetProjectId === sourceProjectId
                    ? 'local'
                    : sourceProjectId !== null && targetProjectId !== null
                        ? 'cross-project'
                        : 'repository';
            return { path: normalizedPath, targetProjectId, relationship };
        })
            .sort((left, right) => left.path.localeCompare(right.path));
        draft.candidate.blockers = [...draft.candidate.blockers, ...projectBlockers];
        draft.candidate.project = {
            ...project,
            visibility: project.sharedProjectIds.length > 0 ? 'shared' : project.ownerProjectId === null ? 'repository' : 'project',
            dependencies,
        };
    }
}
function createWorkDiscoveryView(kind, projectId, candidates, baseFindings) {
    const findings = [...baseFindings];
    const actionable = candidates.filter((candidate) => candidate.status === 'actionable');
    if (actionable.length > 1) {
        findings.push({
            code: 'AMBIGUOUS_ACTIONABLE_WORK',
            paths: actionable.map((candidate) => candidate.artifactPath),
            message: kind === 'project'
                ? `Project '${projectId}' has multiple actionable tasks; explicit selection is required.`
                : 'Multiple actionable tasks exist; implicit selection is ambiguous and no candidate was selected.',
        });
    }
    return {
        kind,
        projectId,
        candidates,
        findings: findings.sort((left, right) => left.code.localeCompare(right.code) || left.paths.join('\u0000').localeCompare(right.paths.join('\u0000'))),
        implicitSelection: actionable.length === 1
            ? {
                status: 'resolved',
                artifactPath: actionable[0].artifactPath,
                reason: kind === 'project'
                    ? `Exactly one actionable task is visible in project '${projectId}'.`
                    : 'Exactly one actionable task is available from canonical evidence.',
            }
            : actionable.length === 0
                ? {
                    status: 'none',
                    artifactPath: null,
                    reason: kind === 'project'
                        ? `Project '${projectId}' contains no actionable task.`
                        : 'Canonical evidence contains no actionable task.',
                }
                : {
                    status: 'ambiguous',
                    artifactPath: null,
                    reason: kind === 'project'
                        ? `Project '${projectId}' contains multiple actionable tasks; explicit selection is required.`
                        : 'Canonical evidence contains multiple actionable tasks; explicit selection is required.',
                },
    };
}
function finalizeCandidate(candidate) {
    const invalidStateFindings = candidate.blockers.map((blocker) => ({
        code: blocker.code,
        message: blocker.message,
        path: blocker.path,
    }));
    const activeBlockers = [];
    if (candidate.lifecycle.implementationStatus === 'blocked') {
        activeBlockers.push({
            code: 'implementation-blocked',
            message: 'The current implementation attempt is blocked.',
            path: candidate.implementationSummaryPath,
        });
    }
    if (candidate.lifecycle.reviewDisposition === 'blocked') {
        activeBlockers.push({
            code: 'review-blocked',
            message: 'The current review disposition is blocked.',
            path: candidate.implementationSummaryPath,
        });
    }
    const validationFindings = validationFindingsForCandidate(candidate);
    const nextAction = (0, next_action_resolver_1.resolveNextAction)({
        invalidStateFindings,
        taskStatus: candidate.lifecycle.status === 'draft' ||
            candidate.lifecycle.status === 'completed' ||
            candidate.lifecycle.status === 'cancelled'
            ? candidate.lifecycle.status
            : null,
        sourceSpecificationState: 'current',
        activeBlockers,
        readinessState: candidate.lifecycle.readiness === 'ready_to_implement' && candidate.lifecycle.authorizationState === 'current'
            ? 'authorized'
            : candidate.lifecycle.authorizationState === 'waived-current'
                ? 'waived'
                : 'incomplete',
        authorizationState: candidate.lifecycle.authorizationState,
        currentAttemptState: candidate.lifecycle.currentAttemptState,
        reviewDisposition: canonicalReviewDisposition(candidate.lifecycle.reviewDisposition),
        implementationStatus: canonicalImplementationStatus(candidate.lifecycle.implementationStatus),
        validationState: candidate.lifecycle.validationState,
        validationFindings,
        closeoutRecorded: candidate.lifecycle.status === 'completed' || candidate.lifecycle.status === 'cancelled',
        reconciliationState: 'not_applicable',
    });
    const status = candidate.lifecycle.status === 'cancelled'
        ? 'cancelled'
        : candidate.lifecycle.status === 'completed'
            ? 'completed'
            : isBlockingAction(nextAction.nextAction)
                ? 'blocked'
                : 'actionable';
    return {
        ...candidate,
        status,
        nextAction,
        commandRouting: routeDerivedWorkCommand(nextAction),
    };
}
function deriveAuthorizationState(projection) {
    const authorization = projection.task.readinessAuthorization;
    const present = authorization.id !== null ||
        authorization.waiverId !== null ||
        authorization.taskRevision !== null ||
        authorization.protocolVersion !== null ||
        authorization.reviewer !== null ||
        authorization.reviewedAt !== null;
    if (!present)
        return 'absent';
    if (!authorization.complete || !authorization.valid)
        return 'invalid';
    if (!authorization.applicable)
        return 'stale';
    return authorization.kind === 'waiver' ? 'waived-current' : 'current';
}
function deriveCurrentAttemptState(repositoryRoot, taskDefinitionPath, taskText, summaryPath, summaryText, projection) {
    const current = projection.summary?.currentAttempt ?? null;
    if (current === null) {
        return {
            state: (projection.summary?.attempts.length ?? 0) > 0 ? 'closed' : 'absent',
            attemptId: null,
            baselineId: null,
            inspectionIssue: null,
        };
    }
    if (current.closedAt !== null || current.attemptId === null || summaryText === null) {
        return { state: 'closed', attemptId: current.attemptId, baselineId: null, inspectionIssue: null };
    }
    if ((0, task_attempts_1.inspectTaskAttemptSupersessionEvidence)(summaryText, current.attemptId).length > 0) {
        return {
            state: 'open-evidence-bearing',
            attemptId: current.attemptId,
            baselineId: null,
            inspectionIssue: null,
        };
    }
    try {
        const baseline = (0, task_cycle_runtime_attribution_1.readAttemptRuntimeBaseline)(summaryPath, summaryText, current.attemptId);
        if (baseline !== null) {
            try {
                if ((0, task_attempts_1.inspectSupersessionTargetDelta)(repositoryRoot, taskDefinitionPath, taskText, baseline).length > 0) {
                    return {
                        state: 'open-evidence-bearing',
                        attemptId: current.attemptId,
                        baselineId: baseline.baselineId,
                        inspectionIssue: null,
                    };
                }
            }
            catch (error) {
                if (error instanceof task_attempts_1.TaskAttemptSupersedeError && error.code === 'git-inspection-unavailable') {
                    return {
                        state: 'open-baseline-only',
                        attemptId: current.attemptId,
                        baselineId: baseline.baselineId,
                        inspectionIssue: error.message,
                    };
                }
                throw error;
            }
            return {
                state: 'open-baseline-only',
                attemptId: current.attemptId,
                baselineId: baseline.baselineId,
                inspectionIssue: null,
            };
        }
    }
    catch {
        return {
            state: 'open-evidence-bearing',
            attemptId: current.attemptId,
            baselineId: null,
            inspectionIssue: null,
        };
    }
    return { state: 'open-no-evidence', attemptId: current.attemptId, baselineId: null, inspectionIssue: null };
}
function isBlockingAction(action) {
    return (action === 'repair_state' ||
        action === 'recertify_plan' ||
        action === 'resolve_blocker' ||
        action === 'refine_task' ||
        action === 'address_validation');
}
function routeDerivedWorkCommand(result) {
    switch (result.nextAction) {
        case 'repair_state':
            return { id: 'repair-policy', reason: result.reasonCode };
        case 'recertify_plan':
        case 'refine_task':
            return { id: 'complete-readiness', reason: result.reasonCode };
        case 'supersede_attempt':
            return { id: 'supersede-attempt', reason: result.reasonCode };
        case 'recover_contract_revision':
            return { id: 'recover-contract-revision', reason: result.reasonCode };
        case 'implement':
        case 'revise':
            return { id: 'implement', reason: result.reasonCode };
        case 'address_validation':
            return { id: 'complete-summary', reason: result.reasonCode };
        case 'resolve_blocker':
        case 'review':
        case 'close':
        case 'reconcile':
        case 'none':
            return { id: 'review-closeout', reason: result.reasonCode };
    }
}
function validationFindingsForCandidate(candidate) {
    const state = candidate.lifecycle.validationState;
    if (state === 'current' || state === 'waived' || state === 'not_required')
        return [];
    return [
        {
            code: `validation-${state ?? 'unknown'}`,
            message: `Current-attempt validation evidence is ${state ?? 'unknown'}.`,
            path: candidate.implementationSummaryPath,
        },
    ];
}
function canonicalImplementationStatus(value) {
    return value === 'pending' || value === 'in_progress' || value === 'completed' || value === 'blocked' ? value : null;
}
function canonicalReviewDisposition(value) {
    return value === 'pending' ||
        value === 'changes_requested' ||
        value === 'accepted' ||
        value === 'accepted_with_follow_up' ||
        value === 'waived' ||
        value === 'blocked'
        ? value
        : null;
}
function deriveValidationProjection(repositoryRoot, summaryPath, summaryText, pair) {
    if (effectiveImplementationStatus(pair) !== 'completed') {
        return { state: 'not_required', invalidFindings: [] };
    }
    if (summaryText === null || pair.summary?.currentAttemptId === null) {
        return { state: 'missing', invalidFindings: [] };
    }
    let results;
    try {
        results = (0, task_validation_1.readTaskValidationResults)(summaryPath, summaryText).filter((entry) => entry.attemptId === pair.summary?.currentAttemptId);
    }
    catch (error) {
        return {
            state: 'unknown',
            invalidFindings: [
                {
                    code: 'malformed-validation-evidence',
                    message: error instanceof Error ? error.message : String(error),
                    path: summaryPath,
                },
            ],
        };
    }
    if (results.length === 0)
        return { state: 'missing', invalidFindings: [] };
    const required = results.filter((entry) => entry.required && entry.status !== 'not_applicable');
    const applicable = required.length > 0 ? required : results;
    if (applicable.some((entry) => (0, task_validation_1.deriveTaskValidationFreshness)(repositoryRoot, entry).freshness === 'stale')) {
        return { state: 'stale', invalidFindings: [] };
    }
    if (applicable.some((entry) => entry.status === 'failed')) {
        return { state: 'failed', invalidFindings: [] };
    }
    if (applicable.some((entry) => entry.status === 'blocked' || entry.status === 'not_run')) {
        return { state: 'blocked', invalidFindings: [] };
    }
    if (applicable.every((entry) => entry.status === 'waived')) {
        return { state: 'waived', invalidFindings: [] };
    }
    return { state: 'current', invalidFindings: [] };
}
function deriveEvidenceState(summaryText, pair) {
    if (summaryText === null || pair.summary === null)
        return 'missing';
    const summaryFindings = pair.blockingFindings.filter((finding) => finding.artifact === 'implementation_summary' || finding.artifact === 'pair');
    if (summaryFindings.length > 0)
        return 'invalid';
    return effectiveImplementationStatus(pair) === 'completed' &&
        (effectiveReviewDisposition(pair) === 'accepted' || effectiveReviewDisposition(pair) === 'accepted_with_follow_up')
        ? 'completed'
        : 'pending';
}
function effectiveImplementationStatus(pair) {
    return pair.implementationStatus ?? pair.summary?.legacyImplementationStatus ?? null;
}
function effectiveReviewDisposition(pair) {
    return pair.reviewDisposition ?? pair.summary?.legacyReviewDisposition ?? null;
}
function deriveOwnerIdentity(fields, workflowLevel, parents) {
    if (workflowLevel === 'basic')
        return { kind: 'none', value: null };
    if (workflowLevel === 'governed') {
        const explicit = fields.slice?.trim();
        if (explicit)
            return { kind: 'slice', value: explicit };
        const parent = parents.find((entry) => entry.kind === 'slice_overview');
        return {
            kind: 'slice',
            value: parent === undefined ? null : node_path_1.default.posix.basename(node_path_1.default.posix.dirname(parent.path)),
        };
    }
    const explicit = fields.work_id?.trim() || fields.work?.trim();
    if (explicit)
        return { kind: 'work', value: explicit };
    const parent = parents.find((entry) => entry.kind === 'work_overview');
    const parentBasename = parent === undefined ? null : node_path_1.default.posix.basename(node_path_1.default.posix.dirname(parent.path));
    let projectedParentIdentity = parentBasename;
    if (parentBasename !== null) {
        try {
            projectedParentIdentity = (0, ordered_root_identity_1.parseOrderedDirectoryIdentity)('structured-root', parentBasename).stableId;
        }
        catch {
            projectedParentIdentity = parentBasename;
        }
    }
    return {
        kind: 'work',
        value: projectedParentIdentity,
    };
}
function validateCanonicalGovernedOwner(options) {
    const blockers = [];
    if (options.sliceId === null) {
        blockers.push({
            code: 'governed-slice-identity-missing',
            field: 'slice',
            path: options.artifactPath,
            message: 'Canonical Governed work requires a numbered slice identity.',
        });
        return blockers;
    }
    const sliceOverview = options.parents.find((entry) => entry.kind === 'slice_overview');
    const expectedRoot = `.day-shift/planning/governed/${options.sliceId}`;
    const expectedOverview = `${expectedRoot}/slice-overview.md`;
    if (sliceOverview?.path !== expectedOverview) {
        blockers.push({
            code: 'governed-slice-owner-mismatch',
            field: 'upstream.slice_overview',
            path: options.artifactPath,
            message: `Governed task must reference canonical slice owner '${expectedOverview}'.`,
        });
    }
    if (!options.artifactPath.startsWith(`${expectedRoot}/phases/`)) {
        blockers.push({
            code: 'governed-task-outside-slice-root',
            field: 'path',
            path: options.artifactPath,
            message: `Governed task is outside canonical numbered slice root '${expectedRoot}'.`,
        });
    }
    return blockers;
}
function sectionContent(document, heading) {
    return document.sections.find((section) => section.heading === heading)?.contentText.trim() ?? '';
}
function readDependencyPaths(document) {
    const content = sectionContent(document, 'Dependencies');
    const paths = content
        .split(/\r?\n/)
        .map((line) => line.trim())
        .filter((line) => line.startsWith('- '))
        .map((line) => {
        const markdownLink = /^-\s+\[[^\]]+\]\(([^)]+)\)/.exec(line);
        if (markdownLink !== null)
            return markdownLink[1].trim();
        const backtick = /^-\s+`([^`]+)`/.exec(line);
        if (backtick !== null)
            return backtick[1].trim();
        return line.slice(2).trim();
    })
        .filter((dependencyPath) => dependencyPath.endsWith('/task-definition.md'));
    return [...new Set(paths)].sort((left, right) => left.localeCompare(right));
}
function parseInlineStringList(value) {
    if (value === undefined || value.trim() === '') {
        return { values: [], invalid: false };
    }
    const trimmed = value.trim();
    if (!trimmed.startsWith('[') || !trimmed.endsWith(']')) {
        return { values: [], invalid: true };
    }
    const values = trimmed
        .slice(1, -1)
        .split(',')
        .map((entry) => entry.trim().replace(/^(["'])(.*)\1$/, '$2'))
        .filter((entry) => entry !== '');
    const unique = [...new Set(values)].sort((left, right) => left.localeCompare(right));
    const valid = values.length === unique.length && unique.every((entry) => /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/.test(entry));
    return { values: valid ? unique : [], invalid: !valid };
}
function fallbackTaskId(artifactPath) {
    return node_path_1.default.posix.basename(node_path_1.default.posix.dirname(artifactPath));
}
function readRepositoryFile(repositoryRoot, repositoryPath) {
    return node_fs_1.default.readFileSync((0, paths_1.resolveRepositoryRelativePath)(repositoryRoot, (0, paths_1.parseRepositoryRelativePath)(repositoryPath)), 'utf8');
}
function readRepositoryFileIfExists(repositoryRoot, repositoryPath) {
    const absolutePath = (0, paths_1.resolveRepositoryRelativePath)(repositoryRoot, (0, paths_1.parseRepositoryRelativePath)(repositoryPath));
    return node_fs_1.default.existsSync(absolutePath) ? node_fs_1.default.readFileSync(absolutePath, 'utf8') : null;
}
function toRepositoryPath(input) {
    return input.split(node_path_1.default.sep).join('/');
}
