/**
 * Public WorkGraph contracts for PM-Core V2.
 *
 * The WorkGraph is the product boundary for task containment and non-containment
 * relations. These contracts intentionally describe graph-shaped projections
 * only; persistence, SQL tables, and CLI rendering stay behind core adapters.
 *
 * @task T10575
 * @saga T10538
 */
import { z } from 'zod';
import { TASK_RELATION_TYPES } from './enums.js';
import { TASK_STATUSES } from './status-registry.js';
/** Stable error code for WorkGraph parent/type matrix violations. */
export const E_WORKGRAPH_PARENT_TYPE_MATRIX = 'E_WORKGRAPH_PARENT_TYPE_MATRIX';
const taskTypeSchema = z.enum(['saga', 'epic', 'task', 'subtask']);
const taskPrioritySchema = z.enum(['critical', 'high', 'medium', 'low']);
const taskStatusSchema = z.enum(TASK_STATUSES);
const verificationGateSchema = z.enum([
    'implemented',
    'testsPassed',
    'qaPassed',
    'cleanupDone',
    'securityPassed',
    'documented',
    'nexusImpact',
]);
const workGraphRelationKindSchema = z.enum([
    'contains',
    'depends_on',
    'blocks',
    'relates_to',
    'groups',
    'satisfies',
]);
const workGraphTraversalDirectionSchema = z.enum([
    'ancestors',
    'descendants',
    'upstream',
    'downstream',
]);
const workGraphEdgeDirectionSchema = z.enum(['out', 'in', 'both']);
const paginationParamsSchema = z.object({
    cursor: z.string().min(1).optional(),
    limit: z.number().int().positive().max(500).optional(),
});
const workGraphNodeSchema = z.object({
    id: z.string().min(1),
    type: taskTypeSchema,
    title: z.string(),
    status: taskStatusSchema,
    priority: taskPrioritySchema,
    parentId: z.string().min(1).optional(),
});
const workGraphEdgeSchema = z.object({
    fromId: z.string().min(1),
    toId: z.string().min(1),
    kind: workGraphRelationKindSchema,
});
const workGraphHierarchyEdgeSchema = z
    .object({
    fromId: z.string().min(1),
    toId: z.string().min(1),
    kind: z.literal('contains'),
})
    .strict();
const workGraphPageInfoSchema = z.object({
    nextCursor: z.string().min(1).optional(),
    hasMore: z.boolean(),
});
const workGraphRollupCountsSchema = z.object({
    total: z.number().int().nonnegative(),
    byStatus: z.partialRecord(taskStatusSchema, z.number().int().nonnegative()),
    byType: z.partialRecord(taskTypeSchema, z.number().int().nonnegative()),
});
const workGraphSubtreePercentagesSchema = z.object({
    done: z.number().nonnegative(),
    active: z.number().nonnegative(),
    blocked: z.number().nonnegative(),
    pending: z.number().nonnegative(),
    cancelled: z.number().nonnegative(),
});
const workGraphProjectionMismatchSchema = z.object({
    field: z.string().min(1),
    expected: z.number().int().nonnegative(),
    actual: z.number().int().nonnegative(),
});
const workGraphReadyFrontierTaskSchema = workGraphNodeSchema.extend({
    role: z.string().min(1).optional(),
    dependencyBlockers: z.array(z.object({ taskId: z.string().min(1), status: taskStatusSchema })),
    gateBlockers: z.array(z.object({ gate: verificationGateSchema })),
});
const workGraphRelationEdgeSchema = workGraphEdgeSchema.extend({
    source: z.literal('relation'),
    relationType: z.enum(TASK_RELATION_TYPES),
    reason: z.string().min(1).optional(),
});
const workGraphDependencyEdgeSchema = workGraphEdgeSchema.extend({
    source: z.literal('dependency'),
    kind: z.literal('depends_on'),
});
const workGraphOmissionReasonSchema = z.enum([
    'budget_exceeded',
    'not_requested',
    'not_available',
    'redacted',
    'truncated',
]);
const workGraphContextBudgetSchema = z.object({
    tokenBudget: z.number().int().nonnegative(),
    estimatedTokens: z.number().int().nonnegative(),
    remainingTokens: z.number().int().nonnegative(),
    truncated: z.boolean(),
});
const workGraphOmissionSchema = z.object({
    path: z.string().min(1),
    reason: workGraphOmissionReasonSchema,
    message: z.string().min(1),
    estimatedTokens: z.number().int().nonnegative().optional(),
});
const workGraphDirectEdgeSchema = z.discriminatedUnion('source', [
    workGraphRelationEdgeSchema,
    workGraphDependencyEdgeSchema,
]);
/** Zod params for a bounded WorkGraph context pack. @task T10609 */
export const workGraphContextPackParamsSchema = paginationParamsSchema.extend({
    rootId: z.string().min(1),
    tokenBudget: z.number().int().positive().optional(),
    includeRelations: z.boolean().optional(),
    includeReadiness: z.boolean().optional(),
    includeRollup: z.boolean().optional(),
});
/** Zod params for a bounded WorkGraph graph slice. @task T10609 */
export const workGraphSliceParamsSchema = paginationParamsSchema.extend({
    rootId: z.string().min(1),
    direction: workGraphTraversalDirectionSchema.optional(),
    maxDepth: z.number().int().nonnegative().optional(),
    includeRelations: z.boolean().optional(),
});
/** Zod result contract for a bounded WorkGraph graph slice. @task T10609 */
export const workGraphSliceSchema = z.object({
    rootId: z.string().min(1),
    direction: workGraphTraversalDirectionSchema,
    nodes: z.array(workGraphNodeSchema),
    edges: z.array(workGraphEdgeSchema),
    pageInfo: workGraphPageInfoSchema,
    omissions: z.array(workGraphOmissionSchema).optional(),
});
/** Zod params for direct WorkGraph readiness checks. @task T10609 */
export const workGraphReadinessParamsSchema = z.object({
    rootId: z.string().min(1),
    role: z.string().min(1).optional(),
    includeGateBlockers: z.boolean().optional(),
});
/** Zod result contract for direct WorkGraph readiness checks. @task T10609 */
export const workGraphReadinessResultSchema = z.object({
    rootId: z.string().min(1),
    role: z.string().min(1).optional(),
    ready: z.boolean(),
    warnings: z.array(z.string()),
    groups: z.object({
        ready: z.array(workGraphReadyFrontierTaskSchema),
        blocked: z.array(workGraphReadyFrontierTaskSchema),
        blockedBy: z.array(z.discriminatedUnion('kind', [
            z.object({
                kind: z.literal('dependency'),
                blockerId: z.string().min(1),
                blocks: z.array(z.string().min(1)),
            }),
            z.object({
                kind: z.literal('gate'),
                gate: verificationGateSchema,
                blocks: z.array(z.string().min(1)),
            }),
        ])),
    }),
});
/** Zod result contract for a bounded WorkGraph context pack. @task T10609 */
export const workGraphContextPackSchema = z.object({
    rootId: z.string().min(1),
    generatedAt: z.string().min(1),
    budget: workGraphContextBudgetSchema,
    slice: workGraphSliceSchema,
    relationEdges: z
        .object({
        rootId: z.string().min(1),
        direction: workGraphEdgeDirectionSchema,
        edges: z.array(workGraphDirectEdgeSchema),
    })
        .optional(),
    readiness: workGraphReadinessResultSchema.optional(),
    rollup: z.lazy(() => tasksRollupResultSchema).optional(),
    omissions: z.array(workGraphOmissionSchema),
});
/** Zod params for WorkGraph scaffold validation. @task T10609 */
export const workGraphScaffoldValidateParamsSchema = z.object({
    rootId: z.string().min(1),
    nodes: z.array(z.object({
        id: z.string().min(1),
        type: taskTypeSchema,
        parentId: z.string().min(1).nullable().optional(),
    })),
    edges: z.array(workGraphDirectEdgeSchema).optional(),
    dryRun: z.boolean().optional(),
});
const workGraphScaffoldValidationIssueSchema = z.object({
    code: z.string().min(1),
    message: z.string().min(1),
    taskId: z.string().min(1).optional(),
    severity: z.enum(['error', 'warning']),
});
/** Zod result contract for WorkGraph scaffold validation. @task T10609 */
export const workGraphScaffoldValidateResultSchema = z.object({
    rootId: z.string().min(1),
    valid: z.boolean(),
    dryRun: z.boolean(),
    issues: z.array(workGraphScaffoldValidationIssueSchema),
    hierarchy: z.object({
        valid: z.boolean(),
        violations: z.array(z.object({
            code: z.literal(E_WORKGRAPH_PARENT_TYPE_MATRIX),
            taskId: z.string().min(1),
            taskType: taskTypeSchema,
            parentId: z.string().min(1).nullable(),
            parentType: taskTypeSchema.optional(),
            message: z.string().min(1),
        })),
    }),
});
/** Zod params for WorkGraph scaffold apply. @task T10609 */
export const workGraphScaffoldApplyParamsSchema = workGraphScaffoldValidateParamsSchema.extend({
    apply: z.boolean().optional(),
});
/** Zod result contract for WorkGraph scaffold apply. @task T10609 */
export const workGraphScaffoldApplyResultSchema = workGraphScaffoldValidateResultSchema.extend({
    applied: z.boolean(),
    nodesChanged: z.number().int().nonnegative(),
    edgesChanged: z.number().int().nonnegative(),
});
/** Zod params for WorkGraph planning doc generation. @task T10634 */
export const workGraphPlanningDocParamsSchema = z.object({
    rootId: z.string().min(1),
    audience: z.enum(['agent', 'maintainer']),
    tokenBudget: z.number().int().positive().optional(),
    includeRelations: z.boolean().optional(),
    includeReadiness: z.boolean().optional(),
    includeRollup: z.boolean().optional(),
});
/** Zod result contract for WorkGraph planning doc. @task T10634 */
export const workGraphPlanningDocSchema = z.object({
    rootId: z.string().min(1),
    generatedAt: z.string().min(1),
    audience: z.enum(['agent', 'maintainer']),
    title: z.string().min(1),
    content: z.string(),
    sections: z.array(z.string().min(1)),
    estimatedTokens: z.number().int().nonnegative(),
    budget: z
        .object({
        tokenBudget: z.number().int().positive(),
        truncated: z.boolean(),
    })
        .optional(),
});
/** Zod params for `tasks.traverse`. */
export const tasksTraverseParamsSchema = paginationParamsSchema.extend({
    rootId: z.string().min(1),
    direction: workGraphTraversalDirectionSchema,
    maxDepth: z.number().int().nonnegative().optional(),
    includeRelations: z.boolean().optional(),
});
/** Zod result contract for `tasks.traverse`. */
export const tasksTraverseResultSchema = z.object({
    rootId: z.string().min(1),
    direction: workGraphTraversalDirectionSchema,
    nodes: z.array(workGraphNodeSchema),
    edges: z.array(workGraphEdgeSchema),
    pageInfo: workGraphPageInfoSchema,
});
/** Zod params for `tasks.tree`. */
export const tasksTreeParamsSchema = paginationParamsSchema.extend({
    rootId: z.string().min(1),
    maxDepth: z.number().int().nonnegative().optional(),
});
/** Zod result contract for `tasks.tree`. */
export const tasksTreeResultSchema = z.object({
    rootId: z.string().min(1),
    nodes: z.array(workGraphNodeSchema.extend({ depth: z.number().int().positive() })),
    edges: z.array(workGraphHierarchyEdgeSchema),
    pageInfo: workGraphPageInfoSchema,
});
/** Zod params for `tasks.rollup`. */
export const tasksRollupParamsSchema = z.object({
    rootId: z.string().min(1),
    expectedDirectRollup: workGraphRollupCountsSchema.optional(),
});
/** Zod result contract for `tasks.rollup`. */
export const tasksRollupResultSchema = z.object({
    rootId: z.string().min(1),
    direct: workGraphRollupCountsSchema,
    subtree: workGraphRollupCountsSchema,
    percentDenominator: z.object({
        basis: z.literal('subtree-total'),
        total: z.number().int().nonnegative(),
        description: z.string().min(1),
    }),
    percentages: workGraphSubtreePercentagesSchema,
    staleProjection: z.boolean(),
    projectionMismatches: z.array(workGraphProjectionMismatchSchema),
});
/** Zod params for `tasks.frontier`. */
export const tasksFrontierParamsSchema = z.object({
    rootId: z.string().min(1),
    role: z.string().min(1).optional(),
});
/** Zod result contract for `tasks.frontier`. */
export const tasksFrontierResultSchema = z.object({
    rootId: z.string().min(1),
    role: z.string().min(1).optional(),
    groups: z.object({
        ready: z.array(workGraphReadyFrontierTaskSchema),
        blocked: z.array(workGraphReadyFrontierTaskSchema),
        blockedBy: z.array(z.discriminatedUnion('kind', [
            z.object({
                kind: z.literal('dependency'),
                blockerId: z.string().min(1),
                blocks: z.array(z.string().min(1)),
            }),
            z.object({
                kind: z.literal('gate'),
                gate: verificationGateSchema,
                blocks: z.array(z.string().min(1)),
            }),
        ])),
    }),
});
/** Zod params for `tasks.workgraph.audit`. */
export const tasksWorkGraphAuditParamsSchema = paginationParamsSchema.extend({
    rootId: z.string().min(1),
    maxDepth: z.number().int().nonnegative().optional(),
    includeRelations: z.boolean().optional(),
});
/** Zod result contract for `tasks.workgraph.audit`. */
export const tasksWorkGraphAuditResultSchema = z.object({
    rootId: z.string().min(1),
    hierarchy: z.object({
        valid: z.boolean(),
        violations: z.array(z.object({
            code: z.literal(E_WORKGRAPH_PARENT_TYPE_MATRIX),
            taskId: z.string().min(1),
            taskType: taskTypeSchema,
            parentId: z.string().min(1).nullable(),
            parentType: taskTypeSchema.optional(),
            message: z.string().min(1),
        })),
    }),
    traversal: tasksTraverseResultSchema,
    frontier: tasksFrontierResultSchema,
    rollup: tasksRollupResultSchema,
    relationEdges: z
        .object({
        rootId: z.string().min(1),
        direction: workGraphEdgeDirectionSchema,
        edges: z.array(z.discriminatedUnion('source', [
            workGraphRelationEdgeSchema,
            workGraphDependencyEdgeSchema,
        ])),
    })
        .optional(),
});
const ROOT_TASK_TYPES = new Set(['saga', 'epic']);
const PARENT_TYPE_MATRIX = {
    saga: [],
    epic: ['saga'],
    task: ['epic'],
    subtask: ['task'],
};
/**
 * Return true when the task type can exist without a parent in the canonical
 * PM-Core V2 containment tree.
 *
 * @param type - Task type to test.
 */
export function canWorkGraphTaskTypeBeRoot(type) {
    return ROOT_TASK_TYPES.has(type);
}
/**
 * Return true when a parent type can contain a child type in the canonical
 * PM-Core V2 containment tree.
 *
 * @param childType - Child task type.
 * @param parentType - Candidate parent task type.
 */
export function isAllowedWorkGraphParentType(childType, parentType) {
    return PARENT_TYPE_MATRIX[childType].includes(parentType);
}
function describeAllowedParents(type) {
    const allowed = PARENT_TYPE_MATRIX[type];
    return allowed.length === 0 ? 'root only' : allowed.join('|');
}
function formatHierarchyViolation(violation) {
    return `${violation.code}: task ${violation.taskId} type=${violation.taskType} parent=${violation.parentId ?? '<root>'} parentType=${violation.parentType ?? '<missing>'}; ${violation.message}`;
}
/** Error thrown by fail-fast WorkGraph hierarchy validation. */
export class WorkGraphHierarchyInvariantError extends Error {
    /** Stable machine-readable code for the invariant breach. */
    code = E_WORKGRAPH_PARENT_TYPE_MATRIX;
    /** Structured diagnostic for the first hierarchy invariant violation. */
    violation;
    /**
     * Build a WorkGraph hierarchy invariant error from a structured violation.
     * @param violation - The first violation encountered by the validator.
     */
    constructor(violation) {
        super(formatHierarchyViolation(violation));
        this.name = 'WorkGraphHierarchyInvariantError';
        this.violation = violation;
    }
}
function makeViolation(node, parent) {
    const parentId = node.parentId ?? null;
    const parentType = parent?.type;
    const expected = describeAllowedParents(node.type);
    const actual = parentId === null ? 'root' : (parentType ?? 'missing parent');
    return {
        code: E_WORKGRAPH_PARENT_TYPE_MATRIX,
        taskId: node.id,
        taskType: node.type,
        parentId,
        parentType,
        message: `tasks.parent_id must follow saga->epic, epic->task, and task->subtask; sagas and standalone epics may be roots; expected ${node.type} parent ${expected}, got ${actual}`,
    };
}
/**
 * Validate WorkGraph hierarchy rows against CLEO's canonical type/parent matrix.
 *
 * The validator mirrors the SQLite trigger invariant in a storage-agnostic form
 * for core adapters, projections, and API callers: sagas and standalone epics
 * may be roots, sagas may contain epics, epics may contain tasks, tasks may
 * contain subtasks, and subtasks are leaves. Saga membership uses `parentId`
 * containment, never `task_relations.groups`.
 *
 * @param nodes - Task-like hierarchy rows to validate.
 * @param options - Optional fail-fast behavior for enforcement call sites.
 * @returns Structured validation result with all violations in input order.
 * @task T10576
 * @saga T10538
 */
export function validateWorkGraphHierarchy(nodes, options = {}) {
    const byId = new Map();
    for (const node of nodes)
        byId.set(node.id, node);
    const violations = [];
    for (const node of nodes) {
        const parentId = node.parentId ?? null;
        const parent = parentId === null ? undefined : byId.get(parentId);
        const valid = parentId === null
            ? canWorkGraphTaskTypeBeRoot(node.type)
            : parent !== undefined && isAllowedWorkGraphParentType(node.type, parent.type);
        if (!valid) {
            const violation = makeViolation(node, parent);
            if (options.throwOnViolation)
                throw new WorkGraphHierarchyInvariantError(violation);
            violations.push(violation);
        }
    }
    return { valid: violations.length === 0, violations };
}
//# sourceMappingURL=workgraph.js.map