/**
 * Central error utilities for CLEO.
 *
 * Provides consistent error handling patterns across the codebase.
 * DRY error formatting, normalization, and transformation utilities.
 *
 * @task T5702
 */
import { ExitCode } from './exit-codes.js';
/**
 * Thrown when a non-orchestrator role attempts to spawn another agent,
 * violating the thin-agent inversion-of-control rule (ORC-012).
 *
 * Only orchestrators may spawn subagents. Leads and workers must escalate
 * via the playbook approval gate defined in T889 Orchestration Coherence v3.
 *
 * @remarks
 * Emitted by the spawn-guard middleware when an agent's declared role does
 * not satisfy the orchestrator precondition. Carries `exitCode` aligned with
 * {@link ExitCode.THIN_AGENT_VIOLATION} so the CLI can surface exit 68 to
 * callers without additional mapping.
 *
 * @example
 * ```typescript
 * throw new ThinAgentViolationError('worker-42', 'lead', 'orchestrate spawn');
 * ```
 *
 * @task T889 Orchestration Coherence v3
 * @task T907 Thin-agent enforcement
 * @see INVARIANTS_REGISTRY['ADR-070.ORC-012'] — central-registry entry
 *   for this invariant. Source: `./invariants/adr-070-orchestration.ts`.
 */
export class ThinAgentViolationError extends Error {
    agentId;
    role;
    attemptedAction;
    /** Stable LAFS error code string for envelope emission. */
    code = 'E_THIN_AGENT_VIOLATION';
    /** Numeric exit code aligned with {@link ExitCode.THIN_AGENT_VIOLATION}. */
    exitCode = ExitCode.THIN_AGENT_VIOLATION;
    /**
     * @param agentId - The offending agent's unique identifier.
     * @param role - The offending agent's declared role (lead, worker, etc.).
     * @param attemptedAction - The spawn action that was blocked.
     */
    constructor(agentId, role, attemptedAction) {
        super(`E_THIN_AGENT_VIOLATION: agent '${agentId}' (role=${role}) attempted '${attemptedAction}'. ` +
            'Only orchestrators may spawn. Escalate via playbook approval gate.');
        this.agentId = agentId;
        this.role = role;
        this.attemptedAction = attemptedAction;
        this.name = 'ThinAgentViolationError';
    }
}
/**
 * Thrown when a subagent (or any non-owner session) attempts to advance a
 * parent epic's lifecycle stages while its session is scoped to a child task.
 *
 * Lifecycle stage mutations (progress / skip / reset) for an epic MUST come
 * from a session that is:
 *   (a) scoped to the epic itself (`epic:<epicId>`), OR
 *   (b) a global-scope session (owner-level), OR
 *   (c) accompanied by `CLEO_OWNER_OVERRIDE=1` (audited escape hatch).
 *
 * Root incident: during T1150 orchestration a subagent advanced all 9 lifecycle
 * stages within 75 seconds to bypass `E_LIFECYCLE_GATE_FAILED`. This error
 * class closes that vector (T1162).
 *
 * @remarks
 * Carries `exitCode` aligned with {@link ExitCode.TASK_NOT_IN_SCOPE} (34)
 * because the attempted lifecycle mutation is semantically outside the scope
 * granted by the current session.
 *
 * @example
 * ```typescript
 * throw new LifecycleScopeDeniedError('T1150', 'epic:T1162');
 * ```
 *
 * @task T1162
 * @adr ADR-054 (scope-guard addendum)
 */
export class LifecycleScopeDeniedError extends Error {
    epicId;
    sessionScope;
    /** Stable LAFS error code string for envelope emission. */
    code = 'E_LIFECYCLE_SCOPE_DENIED';
    /** Numeric exit code aligned with {@link ExitCode.TASK_NOT_IN_SCOPE} (34). */
    exitCode = ExitCode.TASK_NOT_IN_SCOPE;
    /**
     * @param epicId      - The epic whose lifecycle mutation was blocked.
     * @param sessionScope - Human-readable description of the current session scope.
     */
    constructor(epicId, sessionScope) {
        super(`E_LIFECYCLE_SCOPE_DENIED: lifecycle stage advancement for epic '${epicId}' requires ` +
            `a session scoped to that epic or an owner override. ` +
            `Current session scope: ${sessionScope}. ` +
            `Use CLEO_OWNER_OVERRIDE=1 if this is an authorized operation.`);
        this.epicId = epicId;
        this.sessionScope = sessionScope;
        this.name = 'LifecycleScopeDeniedError';
    }
}
/**
 * Thrown when the task classifier emits an agent ID that is not present in
 * the registry vocabulary it was configured with.
 *
 * The classifier's output space MUST be a strict subset of the registered
 * agent IDs (Council 2026-04-24 FP atomic truth #3). This error is raised
 * at classification time — before any spawn attempt — so that broken routing
 * is caught as early as possible and callers receive a fix-hint listing the
 * currently valid agent IDs.
 *
 * @remarks
 * Carry `exitCode` aligned with {@link ExitCode.SPAWN_VALIDATION_FAILED} (63)
 * because the failure occurs in the pre-spawn validation chain and callers
 * mapping exit codes to envelope errors already handle 63 for spawn failures.
 *
 * @example
 * ```typescript
 * throw new ClassifierUnregisteredAgentError(
 *   'project-dev-lead',
 *   ['project-orchestrator', 'project-code-worker', 'cleo-subagent'],
 * );
 * ```
 *
 * @task T1326
 * @epic T1323
 */
export class ClassifierUnregisteredAgentError extends Error {
    emittedAgentId;
    registeredIds;
    /** Stable LAFS error code string for envelope emission. */
    code = 'E_CLASSIFIER_UNREGISTERED_AGENT';
    /** Numeric exit code aligned with {@link ExitCode.SPAWN_VALIDATION_FAILED} (63). */
    exitCode = ExitCode.SPAWN_VALIDATION_FAILED;
    /**
     * @param emittedAgentId  - The agent ID the classifier tried to emit.
     * @param registeredIds   - The set of valid, registry-backed agent IDs.
     */
    constructor(emittedAgentId, registeredIds) {
        super(`E_CLASSIFIER_UNREGISTERED_AGENT: classifier emitted '${emittedAgentId}' which is not in ` +
            `the registered agent vocabulary. ` +
            `Valid agent IDs: [${registeredIds.join(', ')}]. ` +
            `Add this agent to the registry or remove it from the classifier rules.`);
        this.emittedAgentId = emittedAgentId;
        this.registeredIds = registeredIds;
        this.name = 'ClassifierUnregisteredAgentError';
    }
}
/**
 * Thrown when an ADR-typed decision write is rejected by the LLM
 * conflict-validator hook because the overall confidence score fell below the
 * configured threshold (`decisions.validatorConfidenceThreshold`, default 0.7).
 *
 * The hook runs before the `verifyCandidate` gate and checks for:
 *   - Collision: near-identical decisions already stored
 *   - Contradiction: decisions that contradict existing architectural choices
 *   - Supersession-graph violations: circular or inconsistent supersedes chains
 *
 * Exit code aligns with {@link ExitCode.DECISION_VALIDATOR_FAILED} (106).
 *
 * @example
 * ```typescript
 * throw new DecisionValidatorFailedError('D042', 0.45, ['collision:D017']);
 * ```
 *
 * @task T1828
 */
export class DecisionValidatorFailedError extends Error {
    decisionText;
    confidence;
    violations;
    /** Stable LAFS error code string for envelope emission. */
    code = 'E_DECISION_VALIDATOR_FAILED';
    /** Numeric exit code. */
    exitCode = 106;
    /**
     * @param decisionText  - The decision text that was rejected (truncated for safety).
     * @param confidence    - The computed confidence score from the LLM validator.
     * @param violations    - Short labels describing detected violations (e.g. 'collision:D017').
     */
    constructor(decisionText, confidence, violations) {
        super(`E_DECISION_VALIDATOR_FAILED: decision write rejected (confidence=${confidence.toFixed(3)}). ` +
            `Violations: [${violations.join(', ')}]. ` +
            `Revise the decision/rationale or explicitly supersede conflicting entries.`);
        this.decisionText = decisionText;
        this.confidence = confidence;
        this.violations = violations;
        this.name = 'DecisionValidatorFailedError';
    }
}
/**
 * Thrown when a Lead session attempts to end without delegating any work.
 *
 * Hard gate enforcement for T9230 (ADR-070): a Lead session with
 * `delegate_task_count=0` and `tasks_completed > 0` is a bypass attempt.
 * Leads MUST fan out work to Workers via `delegate_task`.
 *
 * @codeName E_LEAD_BYPASS_DETECTED
 * @task T9230
 * @adr ADR-070
 * @see INVARIANTS_REGISTRY['ADR-070.ORC-014'] — central-registry entry
 *   for this invariant. Source: `./invariants/adr-070-orchestration.ts`.
 */
export class LeadBypassDetectedError extends Error {
    tasksCompleted;
    sessionId;
    /** Stable LAFS error code string for envelope emission. */
    code = 'E_LEAD_BYPASS_DETECTED';
    /** Numeric exit code aligned with {@link ExitCode.LEAD_BYPASS_DETECTED} (107). */
    exitCode = 107;
    /**
     * @param tasksCompleted - Number of tasks completed in the session.
     * @param sessionId - The session ID being ended.
     */
    constructor(tasksCompleted, sessionId) {
        super(`E_LEAD_BYPASS_DETECTED: Lead session ${sessionId} completed ${tasksCompleted} task(s) ` +
            `without any delegate_task call. Leads MUST fan out work to Workers (T9230 / ADR-070). ` +
            `Set CLEO_OWNER_OVERRIDE=1 with reason to override (audited).`);
        this.tasksCompleted = tasksCompleted;
        this.sessionId = sessionId;
        this.name = 'LeadBypassDetectedError';
    }
}
/**
 * LAFS error code emitted when a saga-labeled epic carries forbidden parent or
 * `depends` edges — ADR-073 §1.2 invariant **I3** violation.
 *
 * Sagas MUST link to member Epics exclusively via
 * `task_relations.type='groups'`; a parent or depends edge re-introduces the
 * depth-budget consumption the saga tier was created to avoid.
 *
 * Runtime guard: `assertSagaInvariantI3` (in `@cleocode/core` saga module).
 *
 * @task T10115
 * @saga T10113
 * @see ADR-073-above-epic-naming.md §1.2
 */
export const E_SAGA_INVARIANT_VIOLATION_I3 = 'E_SAGA_INVARIANT_VIOLATION_I3';
/**
 * LAFS error code emitted when a saga-labeled row has a non-null `parentId` —
 * ADR-073 §1.2 invariant **I5** violation.
 *
 * Sagas are top-level groupings; they do not nest under any task. A non-null
 * `parentId` on a saga-labeled row indicates storage corruption or a faulty
 * import path.
 *
 * Runtime guard: `assertSagaInvariantI5` (in `@cleocode/core` saga module).
 *
 * @task T10115
 * @saga T10113
 * @see ADR-073-above-epic-naming.md §1.2
 */
export const E_SAGA_INVARIANT_VIOLATION_I5 = 'E_SAGA_INVARIANT_VIOLATION_I5';
/**
 * LAFS error code emitted when a saga-member candidate is itself a saga —
 * ADR-073 §1.2 invariant **I7** violation (no nested sagas).
 *
 * Nested sagas would re-introduce the multi-release grouping depth the tier
 * was created to flatten.
 *
 * Runtime guard: `assertSagaInvariantI7` (in `@cleocode/core` saga module).
 *
 * @task T10115
 * @saga T10113
 * @see ADR-073-above-epic-naming.md §1.2
 */
export const E_SAGA_INVARIANT_VIOLATION_I7 = 'E_SAGA_INVARIANT_VIOLATION_I7';
/**
 * Normalize any thrown value into a standardized error object.
 *
 * Handles:
 * - Error instances (preserves stack trace info)
 * - Strings (wraps in Error)
 * - Objects with message property
 * - null/undefined (provides fallback)
 *
 * @param error - The thrown value to normalize
 * @param fallbackMessage - Message to use if error provides none
 * @returns Normalized error with consistent shape
 *
 * @remarks
 * This function is safe to call on any value thrown by a `catch` clause.
 * It guarantees the returned object is always an `Error` instance with a
 * non-empty `message` property.
 *
 * @example
 * ```typescript
 * try {
 *   await riskyOperation();
 * } catch (err) {
 *   const error = normalizeError(err, 'Operation failed');
 *   console.error(error.message);
 * }
 * ```
 */
export function normalizeError(error, fallbackMessage = 'An unexpected error occurred') {
    if (error instanceof Error) {
        return error;
    }
    if (typeof error === 'string') {
        return new Error(error);
    }
    if (error !== null &&
        typeof error === 'object' &&
        'message' in error &&
        typeof error.message === 'string') {
        return new Error(error.message);
    }
    return new Error(fallbackMessage);
}
/**
 * Extract a human-readable message from any error value.
 *
 * Safe to use on unknown thrown values without type guards.
 *
 * @param error - The error value
 * @param fallback - Fallback message if extraction fails
 * @returns The error message string
 *
 * @remarks
 * Inspects the value for an `Error` instance, a plain string, or an object
 * with a `message` property before falling back to the provided default.
 *
 * @example
 * ```typescript
 * const message = getErrorMessage(err, 'Unknown error');
 * ```
 */
export function getErrorMessage(error, fallback = 'Unknown error') {
    if (error instanceof Error) {
        return error.message;
    }
    if (typeof error === 'string') {
        return error;
    }
    if (error !== null &&
        typeof error === 'object' &&
        'message' in error &&
        typeof error.message === 'string') {
        return error.message;
    }
    return fallback;
}
/**
 * Format error details for logging or display.
 *
 * Includes stack trace for Error instances when includeStack is true.
 *
 * @param error - The error to format
 * @param context - Optional context to prepend
 * @param includeStack - Whether to include stack traces (default: false)
 * @returns Formatted error string
 *
 * @remarks
 * When `context` is provided it is prefixed in square brackets (e.g.
 * `[Database] Connection refused`). Stack traces are appended on a new line
 * only when `includeStack` is `true` and the value is an `Error` with a stack.
 *
 * @example
 * ```typescript
 * console.error(formatError(err, 'Database connection'));
 * // Output: [Database connection] Connection refused
 * ```
 */
export function formatError(error, context, includeStack = false) {
    const message = getErrorMessage(error);
    const prefix = context ? `[${context}] ` : '';
    let result = `${prefix}${message}`;
    if (includeStack && error instanceof Error && error.stack) {
        result += `\n${error.stack}`;
    }
    return result;
}
/**
 * Check if an error represents a specific error type by code or name.
 *
 * Useful for conditional error handling based on error types.
 *
 * @param error - The error to check
 * @param codeOrName - The error code or name to match
 * @returns True if the error matches
 *
 * @remarks
 * Checks both `Error.name` and a custom `code` property, supporting both
 * standard and LAFS-style error codes (e.g. `"E_NOT_FOUND"`).
 *
 * @example
 * ```typescript
 * if (isErrorType(err, 'E_NOT_FOUND')) {
 *   // Handle not found specifically
 * }
 * ```
 */
export function isErrorType(error, codeOrName) {
    if (error instanceof Error) {
        if (error.name === codeOrName) {
            return true;
        }
        // Check for custom code property
        if ('code' in error && error.code === codeOrName) {
            return true;
        }
    }
    return false;
}
/**
 * Create a standardized error result object.
 *
 * Common pattern for operations that return { success: boolean, error?: string }
 *
 * @param error - The error value
 * @returns Error result object
 *
 * @remarks
 * Pairs with {@link createSuccessResult} and {@link isErrorResult} to provide
 * a consistent result-or-error pattern without exceptions.
 *
 * @example
 * ```typescript
 * return createErrorResult(err);
 * // Returns: { success: false, error: "Something went wrong" }
 * ```
 */
export function createErrorResult(error) {
    return {
        success: false,
        error: getErrorMessage(error),
    };
}
/**
 * Create a standardized success result object.
 *
 * @returns Success result object
 *
 * @remarks
 * Pairs with {@link createErrorResult} and {@link isErrorResult} to provide
 * a consistent result-or-error pattern without exceptions.
 *
 * @example
 * ```typescript
 * return createSuccessResult();
 * // Returns: { success: true }
 * ```
 */
export function createSuccessResult() {
    return { success: true };
}
/**
 * Type guard for error results.
 *
 * @param result - The result to check
 * @returns True if the result is an error result
 *
 * @remarks
 * Narrows the result type so that `result.error` is guaranteed to be a string
 * after the guard returns `true`.
 *
 * @example
 * ```typescript
 * const result = await someOperation();
 * if (isErrorResult(result)) {
 *   console.error(result.error);
 * }
 * ```
 */
export function isErrorResult(result) {
    return !result.success;
}
/**
 * Thrown when `cleo release plan` (or the lint script) encounters a `.changeset/*.md`
 * file whose YAML frontmatter cannot be parsed.
 *
 * Replaces the pre-T10105 silent-skip behaviour where a single malformed
 * file (e.g. an unquoted colon in `summary:`) caused the aggregator to
 * silently drop ALL changeset entries for that release. The v2026.5.100
 * ship lost the v5.100/v5.101/v5.103 CHANGELOG entries to exactly this
 * vector — see Saga T10099 SG-RELEASE-AUDIT-V2.
 *
 * @remarks
 * Carries `exitCode` aligned with {@link ExitCode.VALIDATION_ERROR} (6) so
 * the CLI surfaces a deterministic non-zero exit when invoked from CI.
 *
 * @example
 * ```typescript
 * throw new ChangesetYamlInvalidError({
 *   file: '.changeset/bad.md',
 *   line: 4,
 *   parserMessage: 'Nested mappings are not allowed in compact mappings',
 * });
 * ```
 *
 * @task T10105
 * @epic E-RELEASE-PLAN-CHANGELOG
 * @saga T10099
 */
export class ChangesetYamlInvalidError extends Error {
    /** Stable LAFS error code string for envelope emission. */
    code = 'E_CHANGESET_YAML_INVALID';
    /** Numeric exit code aligned with {@link ExitCode.VALIDATION_ERROR} (6). */
    exitCode = ExitCode.VALIDATION_ERROR;
    /** Structured payload describing the offending file + location. */
    details;
    /**
     * @param details - Structured failure context including file path and
     *   line number. The constructor builds a deterministic human-readable
     *   message of the form `<file>:<line> invalid YAML frontmatter: <msg>`.
     */
    constructor(details) {
        const linePart = details.line !== null ? `:${details.line}` : '';
        super(`E_CHANGESET_YAML_INVALID: ${details.file}${linePart} invalid YAML frontmatter: ${details.parserMessage}`);
        this.name = 'ChangesetYamlInvalidError';
        this.details = details;
    }
}
//# sourceMappingURL=errors.js.map