import type { EntityChangeFlags, EntityChangeType, EntityCommitRecord, EntityCounters, EntityDiagnostic, EntityRecord, LineageEvent, LineageEvidence, LineageHead, LineageMatchReason, LineagePool, LineageThresholds, EntityKind } from './types.js';
export interface LineageBirth {
    repoIdentity: string;
    birthCommit: string;
    birthPath: string;
    birthKind: EntityKind;
    birthQualifiedName: string;
    birthOrdinal: number;
}
/**
 * A lineage id is derived from where a lineage *started*, never from where it
 * currently is — that is what lets it survive renames and moves while staying
 * reproducible from history alone.
 *
 * The schema version is both an input to the digest and the id's literal
 * prefix, so a format bump changes every id and a consumer holding a persisted
 * id can detect staleness from the string without a lookup.
 */
export declare function makeLineageId(birth: LineageBirth): string;
export interface LineageState {
    byLineageId: Map<string, LineageHead>;
    /** `entityStateKey` → lineageId, for the exact-identity rung. */
    byStateKey: Map<string, string>;
}
export declare function createLineageState(): LineageState;
/**
 * A function that gains or loses a container is still the same function, so
 * those two kinds are interchangeable. A class becoming an interface is a
 * genuine redeclaration and is not.
 */
export declare function kindsCompatible(a: EntityKind, b: EntityKind): boolean;
export interface Candidate {
    record: EntityRecord;
    lineageId?: string;
    pool: LineagePool;
}
export interface MatchOutcome {
    previous: Candidate;
    current: EntityRecord;
    reason: LineageMatchReason;
    evidence: LineageEvidence;
}
export interface MatchFileEntitiesInput {
    previous: readonly Candidate[];
    current: readonly EntityRecord[];
    pool: LineagePool;
    thresholds: LineageThresholds;
    /** Source text by state key, for the similarity rung only. */
    sourceFor?: (record: EntityRecord, side: 'previous' | 'current') => string | undefined;
    /** Decline the entire similarity rung above this many candidates on either side. */
    maxSimilarityCandidates?: number;
    counters?: EntityCounters;
}
export interface MatchFileEntitiesResult {
    matches: MatchOutcome[];
    unmatchedPrevious: Candidate[];
    unmatchedCurrent: EntityRecord[];
    refusals: number;
    refusedPrevious: Set<EntityRecord>;
    refusedCurrent: Set<EntityRecord>;
    similaritySkipped: boolean;
    acceptDeclines: Array<{
        rung: LineageMatchReason;
        count: number;
    }>;
}
/**
 * The evidence ladder. Rungs run in order and each removes what it matched, so
 * a loose rung never competes for an entity a strong rung already claimed.
 */
export declare function matchFileEntities(input: MatchFileEntitiesInput): MatchFileEntitiesResult;
export interface ClassifyInput {
    previous: EntityRecord;
    current: EntityRecord;
    boundHit?: boolean;
    /**
     * Whether this entity moved relative to its siblings. Only a whole-file view
     * can decide that, so the caller supplies it; omitting it falls back to a
     * declaration-index comparison, which is right for a single isolated pair.
     */
    orderChanged?: boolean;
}
/**
 * Classification precedence. Evaluated top to bottom, first match wins, and
 * every fact a lower rule would have reported survives as a flag — a rename
 * that also changed logic is `renamed` with `logicChanged: true`, not one at
 * the expense of the other.
 */
export declare function classifyEntityChange(input: ClassifyInput): {
    changeType: EntityChangeType;
    flags: EntityChangeFlags;
};
/**
 * True when nothing about the entity changed, so no event should be emitted.
 *
 * Position is deliberately excluded, both line numbers and declaration index.
 * Both shift whenever anything above the entity in the same file is inserted or
 * removed, which is a property of the file rather than of the entity; the head
 * still advances to the new position, but silently. Comparing them here put a
 * spurious event on every entity below every edit — on this repository that
 * alone accounted for two thirds of all events generated. Whether an entity
 * genuinely moved relative to its siblings is answered by the caller and passed
 * to `classifyEntityChange` as `orderChanged`.
 */
export declare function isUnchanged(previous: EntityRecord, current: EntityRecord): boolean;
export interface BuildEventInput {
    lineageId: string;
    commit: EntityCommitRecord;
    changeType: EntityChangeType;
    matchReason: LineageMatchReason;
    evidence: LineageEvidence;
    flags: EntityChangeFlags;
    previous?: EntityRecord;
    current?: EntityRecord;
    confidence?: number;
}
export declare function buildLineageEvent(input: BuildEventInput): LineageEvent;
export interface MatchCommitInput {
    commit: EntityCommitRecord;
    repoIdentity: string;
    state: LineageState;
    /** Entities per changed path, after the commit. */
    currentByPath: ReadonlyMap<string, readonly EntityRecord[]>;
    /** Entities per changed path, before the commit. */
    previousByPath: ReadonlyMap<string, readonly EntityRecord[]>;
    /** currentPath → previousPath, from git rename detection. */
    renameMap: ReadonlyMap<string, string>;
    /** Paths git reported as deleted by this commit. */
    deletedPaths: ReadonlySet<string>;
    /** Changed paths whose post-image could not be observed within the evidence bounds. */
    evidenceUnavailablePaths?: ReadonlySet<string>;
    thresholds?: LineageThresholds;
    boundHit?: boolean;
    atWindowStart?: boolean;
    sourceFor?: (record: EntityRecord, side: 'previous' | 'current') => string | undefined;
    /** Per-file similarity is skipped, never sampled, above this side bound. */
    maxSimilarityCandidates?: number;
    /**
     * Cap on either side of the cross-file pool. Above it the pass is skipped
     * entirely rather than sampled: a partial cross-file search would make the
     * result depend on iteration order.
     */
    maxCrossFileCandidates?: number;
    counters?: EntityCounters;
}
export interface MatchCommitResult {
    events: LineageEvent[];
    diagnostics: EntityDiagnostic[];
}
/**
 * Match one commit's entities against the running lineage state and emit the
 * resulting events. `state` is mutated in place: under a first-parent walk the
 * running state *is* the parent snapshot, so no full parent tree is retained.
 */
export declare function matchCommitEntities(input: MatchCommitInput): MatchCommitResult;
