/**
 * Mesh refine validation gates & gitlink fast-forward evaluation
 *
 * Extracted from commands/router.ts (behavior-preserving move). Contains:
 *   - the MeshCoordinator config-format type
 *   - refine validation / patch-equivalence / effective-diff / submodule
 *     reachability gates and their summary types + job handles
 *   - gitlink trivial-fast-forward evaluation and submodule alignment helpers
 *
 * router.ts re-exports every public symbol from here so existing import paths
 * keep working. `CommandRouterResult` is imported type-only from router.ts
 * (erased at compile time — no runtime import cycle).
 */
import type { ChangedPackageClassification } from '../git/git-status.js';
import type { MeshRefineValidationScope } from '../mesh/refine-config.js';
import type { WorktreeBootstrapState } from '../mesh/worktree-bootstrap-config.js';
import type { CommandRouterResult } from '../commands/router.js';
export type MeshCoordinatorConfigFormat = 'claude_mcp_json' | 'hermes_config_yaml';
type MeshRefineValidationStatus = 'passed' | 'failed' | 'skipped';
type MeshRefineValidationSummary = {
    status: MeshRefineValidationStatus;
    required: true;
    commandsRun: Array<Record<string, unknown>>;
    bootstrapCommandsRun: Array<Record<string, unknown>>;
    rejectedCommands: Array<Record<string, unknown>>;
    skippedReason?: string;
    failureKind?: string;
    failureCode?: string;
    /** Human-readable cause when failureKind === 'spawn_resolution_failed' (win32 .cmd shim, etc). */
    spawnResolutionError?: string;
    timeoutMs: number;
    outputLimitBytes: number;
    configSource?: string;
    configSourceType?: string;
    suggestions?: unknown[];
    suggestedConfig?: unknown;
    /**
     * M2-3: the bootstrap stage recorded separately from validation so review
     * surfaces can distinguish environment failures from validation failures.
     *   cached — worktree_bootstrap was 'ready' (staleInputs unchanged), skipped
     *   ran    — worktree_bootstrap was stale/never-ran and re-ran successfully
     *   failed — bootstrap run failed (refine stops before validation)
     *   skipped — refine config validation.bootstrap === 'skip'
     *   legacy — deprecated validation.bootstrapCommands path was used
     *   not_configured — no bootstrap definition anywhere
     */
    bootstrap?: {
        stage: 'cached' | 'ran' | 'failed' | 'skipped' | 'legacy' | 'not_configured';
        status?: string;
        skipped?: boolean;
        configSource?: string;
        staleReason?: string;
        error?: string;
        commandsRun?: Array<Record<string, unknown>>;
    };
    /** M2-2: deprecation notices from the refine config (e.g. bootstrapCommands). */
    deprecationWarnings?: string[];
    /**
     * Coarse daemon-vs-web change-impact used to scope the validation command set.
     * When `isDaemonAffecting === false`, daemon-scoped commands are recorded in
     * `commandsRun` with `skipped: true, skipReason: 'unaffected_daemon_scope'`
     * rather than executed; web + typecheck commands always run. Absent when no
     * change-impact was threaded in (legacy: full command set runs).
     */
    changeImpact?: {
        isDaemonAffecting: boolean;
        affectedPackages: string[];
        /** DOCS-ROOT: three-way change area ('none' | 'web' | 'daemon') when known. */
        changeArea?: MeshRefineValidationScope;
        /** displayCommands skipped because the daemon scope is unaffected. */
        skippedDaemonCommands?: string[];
        /** DOCS-ROOT: displayCommands skipped because the change-area scope excluded them. */
        skippedScopeCommands?: string[];
    };
};
type MeshRefineStageStatus = 'passed' | 'failed' | 'skipped';
type MeshRefinePatchEquivalenceSummary = {
    status: MeshRefineStageStatus;
    equivalent: boolean;
    baseHead: string;
    branchHead: string;
    mergeBase?: string;
    mergedTree?: string;
    expectedPatchId?: string;
    actualPatchId?: string;
    durationMs: number;
    error?: string;
    stdout?: string;
    stderr?: string;
    actionableHint?: MeshRefineSubmoduleConflictHint;
    /**
     * Set when a `merge-tree` submodule conflict was reclassified as a trivial
     * gitlink fast-forward and the gate passed via a synthesized merge tree.
     */
    gitlinkTrivialFastForward?: {
        resolved: boolean;
        gitlinks: Array<{
            path: string;
            baseCommit?: string;
            branchCommit?: string;
            fastForward: boolean;
        }>;
        reason?: string;
    };
};
type MeshRefineEffectiveDiffSummary = {
    status: MeshRefineStageStatus;
    /** True when there is at least one root-tree change between base and branch (incl. gitlink bumps). */
    hasEffectiveDiff: boolean;
    baseHead: string;
    branchHead: string;
    /** Root-level paths that differ between base and branch (capped). */
    changedPaths?: string[];
    /** Submodule paths with uncommitted/divergent commits but NO committed gitlink bump in the root tree. */
    submoduleHints?: Array<{
        path: string;
        reason: string;
    }>;
    durationMs: number;
    error?: string;
    stdout?: string;
    stderr?: string;
};
type MeshRefineSubmoduleConflictHint = {
    kind: 'submodule_conflict';
    message: string;
    conflicts: Array<{
        path: string;
        baseCommit?: string;
        branchCommit?: string;
    }>;
    nextSteps: string[];
};
type MeshRefineSubmoduleAlignmentSummary = {
    status: 'passed' | 'failed' | 'skipped';
    changedGitlinkPaths: string[];
    outOfSyncPaths: string[];
    updatedPaths: string[];
    verifiedPaths: string[];
    durationMs: number;
    reason?: string;
    command?: string;
    error?: string;
    stdout?: string;
    stderr?: string;
};
type MeshRefineSubmoduleReachabilityEntry = {
    path: string;
    commit: string;
    reachable: boolean;
    publishRequired?: boolean;
    autoPublishAllowed?: boolean;
    autoPublishAttempted?: boolean;
    autoPublishSucceeded?: boolean;
    autoPublishVerified?: boolean;
    autoPublishRefspec?: string;
    autoPublishSkippedReason?: string;
    importedFromWorktree?: boolean;
    checkedLocal?: boolean;
    localReachable?: boolean;
    remote?: string;
    remoteUrl?: string;
    remoteReachable?: boolean;
    remoteMainBranch?: string;
    remoteMainReachable?: boolean;
    fetchedFromOrigin?: boolean;
    error?: string;
    publishStdout?: string;
    publishStderr?: string;
};
type MeshRefineSubmoduleReachabilitySummary = {
    status: MeshRefineStageStatus;
    checked: number;
    unreachable: MeshRefineSubmoduleReachabilityEntry[];
    entries: MeshRefineSubmoduleReachabilityEntry[];
    durationMs: number;
    autoPublishAllowed?: boolean;
    autoPublishPolicySource?: string;
    error?: string;
};
export type MeshRefineAsyncJobStatus = 'accepted' | 'completed' | 'failed';
export type MeshRefineJobHandle = {
    success: true;
    async: true;
    status: MeshRefineAsyncJobStatus;
    jobId: string;
    interactionId: string;
    meshId: string;
    nodeId: string;
    targetNodeId: string;
    targetDaemonId?: string;
    workspace?: string;
    startedAt: string;
    completedAt?: string;
    duplicate?: boolean;
    retryOfJobId?: string;
    /**
     * The coordinator daemon ID that initiated this refine job.
     * When set, events for this job are scoped to that coordinator's
     * pending-events queue instead of the shared broadcast queue.
     */
    targetCoordinatorDaemonId?: string;
    eventDelivery: {
        pendingEvents: true;
        ledger: true;
    };
    evidence: {
        pendingEventsCommand: 'get_pending_mesh_events';
        ledgerCommand: 'get_mesh_ledger_slice';
        taskHistoryKind: 'task_dispatched' | 'task_completed' | 'task_failed';
    };
};
export type MeshRefineTerminalJob = MeshRefineJobHandle & {
    result?: Record<string, unknown>;
};
export type MeshRefineBatchJobStatus = 'accepted' | 'completed' | 'failed';
/**
 * Async handle returned by the batch Refinery the instant a convergence run is
 * accepted. Mirrors {@link MeshRefineJobHandle} (async:true / status:'accepted' +
 * terminal pending-event + ledger delivery) but scopes a whole batch of sibling
 * nodes rather than a single node. The synthetic `batchLabel` is used as the
 * `nodeLabel` for the shared refine event/message renderer.
 */
export type MeshRefineBatchJobHandle = {
    success: true;
    async: true;
    batch: true;
    status: MeshRefineBatchJobStatus;
    jobId: string;
    interactionId: string;
    meshId: string;
    batchLabel: string;
    nodeIds: string[];
    nodeCount: number;
    order: string[];
    startedAt: string;
    completedAt?: string;
    duplicate?: boolean;
    targetCoordinatorDaemonId?: string;
    eventDelivery: {
        pendingEvents: true;
        ledger: true;
    };
    evidence: {
        pendingEventsCommand: 'get_pending_mesh_events';
        ledgerCommand: 'get_mesh_ledger_slice';
        taskHistoryKind: 'task_dispatched' | 'task_completed' | 'task_failed';
    };
};
export type MeshRefineBatchTerminalJob = MeshRefineBatchJobHandle & {
    result?: Record<string, unknown>;
};
export declare function truncateValidationOutput(value: unknown): string;
/**
 * A spawn-resolution failure is when the executable itself could not be found by
 * the OS spawn boundary — `spawn <cmd> ENOENT` — as opposed to the command
 * running and exiting non-zero. On win32 this is the .cmd-shim case: libuv's
 * spawn search appends only .com/.exe, so a bare `npm`/`npx`/`tsc` (which are
 * .cmd shims) ENOENTs even though it is installed. It carries no stderr, so it
 * must be detected by error.code/syscall, not by string-matching output.
 */
export declare function isSpawnResolutionError(error: any): boolean;
export declare function describeSpawnError(error: any, command: string, spawnResolutionFailed: boolean): string;
export declare function recordMeshRefineStage(stages: Array<Record<string, unknown>>, stage: string, status: MeshRefineStageStatus, startedAt: number, details?: Record<string, unknown>): void;
export declare function buildSubmodulePublishRequiredNextStep(entries: MeshRefineSubmoduleReachabilityEntry[]): string;
/**
 * Async git exec helper used across the synchronous-refine stage pipeline. Bound
 * once in the orchestrator and threaded through RefineContext so every stage runs
 * git the same way (execFile + promisify, utf8). Returns the child's stdout/stderr.
 */
export type RefineExecFileAsync = (file: string, args: string[], options: {
    cwd: string;
    encoding: 'utf8';
}) => Promise<{
    stdout: string;
    stderr: string;
}>;
/**
 * Accumulated state shared by the synchronous-refine stages. The orchestrator
 * (executeMeshRefineNodeSynchronously) seeds this in the resolve_refs stage and
 * each later stage reads / extends it. `branchHead` and `patchEquivalence` are the
 * only fields a stage mutates after creation (auto-rebase updates both), so they
 * are carried on the mutable context rather than re-threaded through return types.
 */
export interface RefineContext {
    meshId: string;
    nodeId: string;
    args: any;
    refineStages: Array<Record<string, unknown>>;
    execFileAsync: RefineExecFileAsync;
    mesh: any;
    node: any;
    sourceNode: any;
    repoRoot: string;
    branch: string;
    baseBranch: string;
    baseHead: string;
    branchHead: string;
    /**
     * Coarse daemon-vs-web change-impact for baseHead..branchHead, resolved in the
     * resolve_refs stage and threaded into the validation gate to scope its command
     * set. `undefined` means "could not classify" → the gate fails open and runs ALL
     * commands (never skip on uncertainty).
     */
    changeImpact?: ChangedPackageClassification;
    validationSummary: Awaited<ReturnType<typeof runMeshRefineValidationGate>>;
    patchEquivalence: Awaited<ReturnType<typeof runMeshRefinePatchEquivalenceGate>>;
    submoduleReachability: Awaited<ReturnType<typeof runMeshRefineSubmoduleReachabilityGate>>;
}
/**
 * Stage outcome for the synchronous-refine pipeline. A stage either produces a
 * terminal CommandRouterResult (an early-exit gate failure, or a successful
 * already-merged short-circuit), in which case the orchestrator returns it
 * immediately, or it returns `continue` with the (possibly extended) context for
 * the next stage. This makes the orchestrator a flat sequence of stage calls
 * while preserving the original body's exact early-return control flow.
 */
export type RefineStageOutcome = {
    kind: 'terminal';
    result: CommandRouterResult;
} | {
    kind: 'continue';
    ctx: RefineContext;
};
export declare function resolveRefineryAutoPublishSubmoduleMainCommits(mesh: any, workspace: string): {
    enabled: boolean;
    source?: string;
};
export declare function runMeshRefinePatchEquivalenceGate(repoRoot: string, baseHead: string, branchHead: string): Promise<MeshRefinePatchEquivalenceSummary>;
/**
 * Machine-readable sub-classification of a `patch_equivalence_failed` (and the
 * related submodule-gitlink preflight blocks). The opaque top-level
 * `patch_equivalence_failed` code is preserved for backward compatibility; this
 * detailed reason is added ALONGSIDE it so coordinators no longer have to guess
 * WHY the preflight blocked (the 2026-07-17 hidden-spinner convergence incident:
 * the real cause was a diverged base + an unreachable submodule gitlink artifact,
 * not a real patch conflict, but Refinery only returned the opaque code and the
 * coordinator mis-attributed it to a stale daemon version).
 */
export type MeshRefinePatchEquivalenceDetailedReasonCode = 
/** Worktree base diverged from target base (HEAD is not a descendant of origin/main). */
'base_divergence'
/** Submodule gitlink commit is not reachable from the submodule's remote main branch (publish needed). */
 | 'submodule_unreachable'
/** Genuine non-equivalent content: expected tree vs actual merge diff differ. */
 | 'actual_patch_diff'
/** Submodule gitlink trivial fast-forward mis-judged as non-equivalent (HEAD descends origin/main, patch-id equal, blocked only by the gitlink). */
 | 'trivial_ff_misjudgment'
/** Already identical to origin/main (ahead 0 / behind 0, no diff) — should be treated as success/no-op. */
 | 'already_converged'
/** Fallback when the classifier itself could not run (git error); keep the opaque code, note the reason. */
 | 'unclassified';
export type MeshRefinePatchEquivalenceFailureClassification = {
    detailedReason: MeshRefinePatchEquivalenceDetailedReasonCode;
    /** Human-readable one-line description of the sub-cause. */
    detailedReasonDescription: string;
    /** Suggested next action for the coordinator/owner (free-form, actionable). */
    recommendedAction: string;
    /** Structured supporting evidence: SHAs, ahead/behind, submodule reachability, patch-id comparison, diff stat. */
    evidence: {
        baseHead?: string;
        branchHead?: string;
        mergeBase?: string;
        /** How many commits base (origin/main) is ahead of the branch's merge-base (branch is behind). */
        behind?: number;
        /** How many commits the branch is ahead of the merge-base. */
        ahead?: number;
        /** True when HEAD is NOT a descendant of the target base (diverged). */
        baseDiverged?: boolean;
        expectedPatchId?: string;
        actualPatchId?: string;
        patchIdEqual?: boolean;
        /** Compact one-line diff stat summary of the residual/actual merge diff (best-effort). */
        diffStat?: string;
        /** Per-submodule gitlink reachability against submodule origin/main (best-effort). */
        submoduleGitlinks?: Array<{
            path: string;
            baseCommit?: string;
            branchCommit?: string;
            /** True when branchCommit descends baseCommit (a strict fast-forward advance). */
            fastForward?: boolean;
            /** True when branchCommit is reachable from the submodule's local origin/main. */
            reachableFromOriginMain?: boolean;
        }>;
        /** Effective auto-publish-submodule-main-commits policy value at classification time. */
        autoPublishSubmoduleMainCommits?: boolean;
        /** Set when the classifier itself errored (detailedReason === 'unclassified'). */
        classifierError?: string;
    };
};
/**
 * Classify WHY a patch-equivalence preflight blocked, turning the opaque
 * `patch_equivalence_failed` code into a machine-readable {@link
 * MeshRefinePatchEquivalenceDetailedReasonCode} plus a recommended action and
 * structured evidence. Read-only: runs only `git` inspection commands (rev-list,
 * merge-base, diff --stat, submodule reachability probes) against the already-set
 * worktree — it never mutates the repo.
 *
 * Priority of classification (first match wins):
 *   1. already_converged     — ahead 0 & behind 0 & no residual diff
 *   2. submodule_unreachable  — a changed gitlink commit is not reachable from the
 *                               submodule's origin/main (publish needed)
 *   3. trivial_ff_misjudgment — HEAD descends origin/main AND (excl. gitlinks) the
 *                               patch-ids match — blocked only by a ff gitlink
 *   4. base_divergence        — HEAD is not a descendant of the target base
 *   5. actual_patch_diff      — genuine content divergence (the residual case)
 *
 * `targetBaseRef` is the ref the branch is meant to land on (e.g. 'origin/main'
 * or the pinned baseHead SHA). `autoPublishSubmoduleMainCommits` is threaded in so
 * the submodule_unreachable recommendation can name the current policy value.
 */
export declare function classifyPatchEquivalenceFailure(repoRoot: string, baseHead: string, branchHead: string, summary: MeshRefinePatchEquivalenceSummary, options?: {
    targetBaseRef?: string;
    autoPublishSubmoduleMainCommits?: boolean;
}): Promise<MeshRefinePatchEquivalenceFailureClassification>;
export type MeshWorktreePatchContainmentSummary = {
    /** True only when merging worktreeHead into ref introduces no new patch. */
    contained: boolean;
    ref: string;
    worktreeHead: string;
    mergeBase?: string;
    mergedTree?: string;
    /** patch-id of (ref -> synthesized merge tree); empty string when nothing new is added. */
    residualPatchId?: string;
    durationMs: number;
    /** Set when the check could not run (treated conservatively as NOT contained). */
    error?: string;
};
/**
 * Patch-equivalence containment check for the worktree force-cleanup convergence
 * guard. Answers a narrower question than {@link runMeshRefinePatchEquivalenceGate}:
 * "are the worktree branch's changes ALREADY present in `ref` (e.g. origin/main),
 * even though the worktree HEAD's commit SHA is not an ancestor of ref?"
 *
 * This is the cherry-pick / squash / rebase case: the same content landed on the
 * default ref under a different commit SHA, so `merge-base --is-ancestor` (the
 * primary cleanup guard) reports the worktree as un-converged and refuses to
 * remove it. Refinery already accepts patch-equivalent landings via merge-tree +
 * patch-id; this brings the same notion of "convergence" to the cleanup guard.
 *
 * Mechanism: synthesize the merge of `worktreeHead` into `ref` (reusing the same
 * trivial-gitlink-fast-forward handling as the refine gate) and compute the
 * patch-id of (ref -> mergedTree). If that residual diff is EMPTY, merging the
 * worktree adds nothing new on top of ref — its changes are already present there
 * and the worktree is safe to remove. A non-empty residual means the worktree
 * still carries content not in ref, so it is NOT contained and must stay blocked.
 *
 * Conservative by construction: any merge-tree / patch-id failure, a genuine
 * (non-trivial) submodule conflict, or any thrown error yields `contained: false`
 * so an exception can never widen the cleanup allow-list.
 */
export declare function checkWorktreeChangesPatchEquivalentInRef(repoRoot: string, ref: string, worktreeHead: string): Promise<MeshWorktreePatchContainmentSummary>;
/**
 * No-op guard: detect a "silent no-op" merge before the Refinery merge runs.
 *
 * A silent no-op occurs when the refine target branch's ROOT tree is byte-identical
 * to the merge base (origin/main). This is the trap where a submodule (e.g. oss) has
 * real commits but the root branch never committed the gitlink (oss-pointer) bump, so
 * the root diff Refinery would merge is empty. Merging that produces a merge commit with
 * no content change — reported as "success" while the actual work never reaches main.
 *
 * A committed gitlink bump (the legitimate oss-pointer bump) DOES show up in the root
 * tree diff (as a 160000-mode entry), so this guard does NOT block legitimate refines —
 * it only fires when the root tree diff vs base is COMPLETELY empty.
 *
 * Runs after the patch-equivalence gate; the "already merged via other path" case
 * (branch has real changes already present in base) is handled upstream and never
 * reaches here, so an empty root diff at this point is genuinely a no-op.
 */
export declare function runMeshRefineEffectiveDiffGate(repoRoot: string, baseHead: string, branchHead: string): Promise<MeshRefineEffectiveDiffSummary>;
/**
 * Result of evaluating whether a `git merge-tree --write-tree` submodule
 * conflict is in fact a trivial gitlink fast-forward that should pass the
 * patch-equivalence gate.
 *
 * `git merge-tree` (and `git merge` with the default recursive strategy)
 * refuses to 3-way merge gitlinks unless the case is "trivial" — and it
 * treats *any* gitlink that differs across merge-base/base/branch as
 * non-trivial, even when the branch-side commit is a strict descendant of the
 * base-side commit (i.e. a real fast-forward). Refinery only ever wants to
 * accept the branch's recorded gitlink, so a fast-forwardable bump is safe to
 * resolve to the branch side without any conflict.
 */
type GitlinkTrivialFastForwardEvaluation = {
    /** True only when the merge-tree conflict is *fully* explained by trivial-ff gitlinks. */
    trivial: boolean;
    /** Why the evaluation declined to treat the conflict as trivial (set when trivial=false). */
    reason?: string;
    /** Per-path detail for the changed gitlinks that were inspected. */
    gitlinks: Array<{
        path: string;
        baseCommit?: string;
        branchCommit?: string;
        fastForward: boolean;
    }>;
};
/**
 * Return the changed gitlink paths between base and branch whose advance is a
 * strict fast-forward (the base-side commit is an ancestor of the branch-side
 * commit inside that submodule's repo). These are the paths whose patch-id hunk
 * may legitimately differ when base has advanced the same submodule, so they
 * are safe to exclude from the patch-equivalence comparison. A non-ff (genuinely
 * diverged) gitlink is deliberately excluded from this set so it still fails the
 * gate.
 */
export declare function collectFastForwardGitlinkPaths(repoRoot: string, baseHead: string, branchHead: string): string[];
/**
 * Decide whether a merge-tree submodule conflict between base and branch is a
 * trivial gitlink fast-forward (and nothing else).
 *
 * The conflict is treated as trivial ONLY when:
 *   1. at least one changed gitlink exists,
 *   2. every changed gitlink fast-forwards (base-commit is an ancestor of the
 *      branch-commit inside that submodule's repo), and
 *   3. the *only* paths that changed on both sides of the merge (i.e. the paths
 *      that could possibly produce a 3-way conflict — the intersection of
 *      mergeBase→base and mergeBase→branch changes) are gitlinks. Any
 *      overlapping non-gitlink path means a genuine content conflict could be
 *      hiding behind the submodule failure, so we keep the block.
 *
 * If any of these fail, the conflict is left as a genuine block. This never
 * passes a regular-file conflict or a diverged (non-ff) gitlink.
 */
export declare function evaluateGitlinkTrivialFastForward(repoRoot: string, baseHead: string, branchHead: string): GitlinkTrivialFastForwardEvaluation;
export declare function alignRefinerySubmodulesAfterMerge(repoRoot: string, previousBaseHead: string, currentHead: string, options?: {
    submoduleIgnorePaths?: string[];
}): Promise<MeshRefineSubmoduleAlignmentSummary>;
export declare function runMeshRefineSubmoduleReachabilityGate(repoRoot: string, mergedTree: string, options?: {
    allowAutoPublishSubmoduleMainCommits?: boolean;
    autoPublishPolicySource?: string;
    worktreeRoot?: string;
}): Promise<MeshRefineSubmoduleReachabilitySummary>;
export declare function buildMeshRefineValidationPlan(mesh: any, workspace: string): Record<string, unknown>;
export declare function runMeshRefineValidationGate(mesh: any, workspace: string, opts?: {
    /** M2-2: persisted node bootstrap state for staleness evaluation. */
    persistedBootstrapState?: WorktreeBootstrapState | null;
    /** M2-2: called after an inherit-mode bootstrap run so the caller can persist the new state. */
    onBootstrapStateChange?: (state: WorktreeBootstrapState) => void;
    /**
     * Coarse daemon-vs-web change-impact for the branch (resolve_refs computes it
     * over baseHead..branchHead). When provided and `isDaemonAffecting === false`,
     * daemon-scoped validation commands are skipped (web + typecheck still run).
     * When omitted or `isDaemonAffecting === true`, the full command set runs —
     * fail-open to full validation on any uncertainty.
     */
    changeImpact?: ChangedPackageClassification;
}): Promise<MeshRefineValidationSummary>;
export {};
