export type NodeKind = 'dir' | 'file';
export type StalenessStatus = 'fresh' | 'stale' | 'never-authored' | 'path-gone' | 'dirty';
export interface CartographerNodeProvenance {
    framework?: string;
    /** Size tier of the model that authored the summary — never a vendor name. */
    modelTier?: 'light' | 'standard';
    /**
     * Which tier authored this summary (spec #2 — doc-freshness). 'inline-agent' =
     * a Tier-1 opportunistic refresh by the agent that edited the code; 'sweep' =
     * the background CartographerSweep poller. Inline summaries are NOT protected
     * ground truth — the sweep may re-author them on a later pass.
     */
    source?: 'inline-agent' | 'sweep';
}
/** Coarse trust signal for a summary (spec #2). `fresh` ≠ correct; this hints how much. */
export type CartographerConfidence = 'low' | 'medium' | 'high';
export interface CartographerNode {
    /** Repo-relative POSIX path this node covers ('' = repo root). */
    path: string;
    kind: NodeKind;
    /** Plain-language description of what the code here does. '' until authored. */
    summary: string;
    /** ISO timestamp the summary was last authored; null = never. */
    summaryUpdatedAt: string | null;
    /** The git object id of the covered code AT authoring time (dir→tree, file→blob). null = never. */
    codeHash: string | null;
    /** Short HEAD sha when the summary was authored (provenance only). */
    codeRev: string | null;
    /** Child node paths (dirs only; sorted, repo-relative). */
    children: string[];
    /** Provenance marker — this file is cartographer state, not a user file. */
    builtinKind: 'cartographer-node';
    provenance?: CartographerNodeProvenance;
    /** True if codeHash was a working-tree hash rather than a committed oid. */
    dirtyAtAuthor?: boolean;
    /** ISO timestamp this node path was first scaffolded — the grace-clock anchor. */
    firstSeenAt?: string;
    /** Who last authored: 'inline-agent' | `sweep:<framework>`. Consumers must not read fresh as trusted. */
    lastAuthoredBy?: string;
    /** Coarse trust signal — `fresh` means fingerprint-current, NOT verified-correct. */
    confidence?: CartographerConfidence;
    /**
     * Hash of the concatenated DIRECT-child summaries at this dir's last author.
     * The dir re-author amplification guard: a dir whose tree-oid flipped but whose
     * childDigestHash is unchanged (e.g. a comment-only deep edit) gets its
     * fingerprint refreshed WITHOUT an LLM call. Leaf nodes leave this null.
     */
    childDigestHash?: string | null;
    /** Anti-starvation: passes this node has been deferred (dir authored after its children). */
    staleSincePass?: number;
    /** Consecutive failed author attempts — drives per-node quarantine. */
    consecutiveAuthorFailures?: number;
    /** True once consecutiveAuthorFailures crossed the quarantine threshold. Surfaced in health(). */
    authorFailed?: boolean;
}
export interface CartographerIndexEntry {
    kind: NodeKind;
    summaryUpdatedAt: string | null;
    codeHash: string | null;
    hasChildren: boolean;
    /** Anti-starvation defer counter (mirrors CartographerNode.staleSincePass). */
    staleSincePass?: number;
    /** Grace-clock anchor for the never-authored-past-grace freshness split. */
    firstSeenAt?: string;
    /** Quarantine flag (mirrors CartographerNode.authorFailed) for the freshness aggregate. */
    authorFailed?: boolean;
}
export interface CartographerIndex {
    schemaVersion: number;
    root: string;
    generatedAt: string;
    nodes: Record<string, CartographerIndexEntry>;
}
export interface CartographerStaleEntry {
    path: string;
    status: StalenessStatus;
    reason: string;
}
export interface CartographerConfig {
    /** Project root directory (the repo to map). */
    projectDir: string;
    /** Instar state directory (cartographer state nests under here). */
    stateDir: string;
    /** Hard cap on descent depth (default 12). */
    maxDepth?: number;
    /** File extensions that become leaf nodes (default ts/js/mjs/cjs). */
    leafExtensions?: string[];
    /** Substrings that exclude a file from being a leaf (default test/spec/.d.ts). */
    excludeSubstrings?: string[];
}
export declare class CartographerTree {
    private readonly projectDir;
    private readonly cartoDir;
    private readonly nodesDir;
    private readonly indexPath;
    private readonly maxDepth;
    private readonly leafExtensions;
    private readonly excludeSubstrings;
    constructor(config: CartographerConfig);
    /** Collision-free, opaque node-file slug. The index is the authoritative reverse lookup. */
    private slug;
    private nodeFilePath;
    /** Atomic write — temp file + rename (atomic on POSIX) so a reader never sees a torn file. */
    private atomicWrite;
    private ensureDirs;
    private git;
    /**
     * ONE batched call: every tracked tree (dir) and blob (file) oid in HEAD, keyed
     * by repo-relative path. Plus the root tree oid (key ''). The make-or-break perf
     * primitive — no per-node `git rev-parse`.
     */
    private currentOids;
    /** Single-path current oid (for setSummary capture) — `git rev-parse HEAD:<path>`. */
    private currentOid;
    private headShort;
    private isLeafFile;
    /**
     * (Re)build the structural skeleton from a directory walk. Preserves an existing
     * node's authored summary/codeHash/codeRev (so staleness still reflects time of
     * authoring); only structure (kind/children) is refreshed. New nodes start
     * never-authored (codeHash null). Honors skip-set, maxDepth, and a symlink-loop
     * guard (visited real paths).
     */
    scaffold(): CartographerIndex;
    /**
     * fix instar#1069 — the BOOT-PATH chunked scaffold. Same result as scaffold()
     * but the directory walk AND the per-node writes YIELD the event loop every
     * `chunkNodes` operations (via the injected `onYield`, e.g. setImmediate), so a
     * huge tree's structural build never freezes /health (the ~2-minute freeze the
     * incident observed). The ONLY sanctioned caller is AgentServer/boot — never a
     * request handler. P19: `shouldAbort` lets the caller bound total time; on abort
     * the partial state is discarded by NOT writing the index (atomic; a half-built
     * index is never read — routes serve indexState:'not-built' until a full run).
     */
    scaffoldChunked(opts: {
        chunkNodes: number;
        onYield: () => Promise<void>;
        shouldAbort?: () => boolean;
    }): Promise<{
        nodeCount: number;
    }>;
    /** Merge structure into a node, preserving any existing authored fields. */
    private upsertNode;
    private rewriteAll;
    private buildIndex;
    private nowIso;
    private readNodeFile;
    /**
     * Load + parse the whole index.
     *
     * ⚠ O(67MB) SYNCHRONOUS JSON.parse — FORBIDDEN on the server hot path (fix
     * instar#1069). On a real tree this blocks the event loop for seconds. The
     * off-event-loop detect parses the index inside a worker; routes serve the
     * snapshot. The Slice-5 lint forbids calling it from routes.ts and
     * CartographerSweepEngine.ts. NO version-gate here: a v1 index (without the
     * staleSincePass/firstSeenAt/authorFailed entry fields) parses unchanged.
     */
    loadIndex(): CartographerIndex | null;
    getNode(nodePath: string): CartographerNode | null;
    /**
     * fix instar#1069 — the LINT-CLEAN, BYTE-BOUNDED index loader for the
     * `/cartographer/tree` request path. It stats the file FIRST and refuses
     * ('too-large') above `maxBytes` BEFORE attempting the parse, so a request can
     * never trigger the 67MB sync parse/serialize that froze the event loop. Small
     * trees parse + serve as before. (Distinct method name so the Slice-5 callsite
     * lint — which bans the unbounded `.loadIndex(` — does not flag this.)
     */
    loadIndexBounded(maxBytes: number): {
        state: 'ok' | 'not-built' | 'too-large';
        index?: CartographerIndex;
        nodeCount?: number;
    };
    getChildren(nodePath: string): CartographerNode[];
    /**
     * Author/refresh a node's summary, capturing the current code oid so future
     * staleness reflects time-of-authoring. Writes the node file first, then patches
     * the index (so a reader never sees an index entry ahead of its node file).
     */
    setSummary(nodePath: string, summary: string, opts?: {
        codeHash?: string;
        codeRev?: string;
        provenance?: CartographerNodeProvenance;
        /** Spec #2 authoring metadata, written atomically with the summary. */
        meta?: {
            lastAuthoredBy?: string;
            confidence?: CartographerConfidence;
            childDigestHash?: string | null;
        };
        /**
         * fix instar#1069 — index-write discipline. When true, write ONLY the small
         * node file and skip the 67MB index parse+serialize; the caller (the sweep
         * engine) batches all of a pass's index updates into ONE off-thread write.
         * The returned node carries the delta the caller persists. Default false
         * (the inline-refresh route + tests keep today's single-call behavior).
         */
        deferIndexWrite?: boolean;
    }): CartographerNode;
    /**
     * Spec #2: patch a node's freshness METADATA without re-authoring its summary.
     * Used by the sweep for failure counters / quarantine / defer-pass bookkeeping,
     * and for a fingerprint-only refresh (the dir re-author amplification guard:
     * bump `codeHash` to the current oid so a comment-only deep edit goes `fresh`
     * with NO LLM call). Never touches `summary` or `summaryUpdatedAt` unless
     * `codeHash` is supplied (a fingerprint refresh) — then the index entry is
     * patched too so staleness derives correctly. No-op (returns null) if absent.
     */
    patchNodeMeta(nodePath: string, partial: {
        codeHash?: string | null;
        codeRev?: string | null;
        childDigestHash?: string | null;
        lastAuthoredBy?: string;
        confidence?: CartographerConfidence;
        staleSincePass?: number;
        consecutiveAuthorFailures?: number;
        authorFailed?: boolean;
        provenance?: CartographerNodeProvenance;
    }, 
    /** fix instar#1069 — when true, write ONLY the node file; the caller batches the index delta off-thread. */
    opts?: {
        deferIndexWrite?: boolean;
    }): CartographerNode | null;
    /**
     * Spec #2: read the COMMITTED content of a leaf node's covered file, bounded to
     * `maxBytes` (head-truncated). Reads `git show HEAD:<path>` — never the dirty
     * working tree — so an uncommitted edit (possibly containing just-typed secrets)
     * is never read, and a long-lived uncommitted edit never causes re-author churn.
     * Returns null when the path is absent from HEAD or git is unavailable.
     */
    committedContent(nodePath: string, maxBytes: number): {
        content: string;
        truncated: boolean;
    } | null;
    /** Public: the current short HEAD sha (compare-and-skip / provenance). */
    currentHeadShort(): string | null;
    /** Public: the current root tree oid (the CI ratchet short-circuit key). */
    rootTreeOid(): string | null;
    /** Public: the current git oid for a single node path (compare-and-skip on HEAD). */
    currentNodeOid(nodePath: string): string | null;
    /**
     * Public: ONE batched current-oid map (path → oid) for the whole HEAD tree. The
     * spec #5 navigator derives per-node `fresh` from a SINGLE read of this map
     * (compared to each node's stored `codeHash`) instead of a per-node staleness
     * call — keeping the "cheap on a wide frontier" guarantee.
     */
    currentOidMap(): Map<string, string>;
    /** Absolute path to index.json (the worker reads/writes it off-thread). */
    indexFilePath(): string;
    /** The repo root the detect git plumbing runs against. */
    projectDirPath(): string;
    /** Absolute path to the per-host snapshot every /cartographer/* read route serves from. */
    snapshotPath(): string;
    /** Read the per-host snapshot (null when absent/corrupt). Cheap — no index parse. */
    readSnapshot(): import('./cartographerDetect.js').CartographerSnapshot | null;
    /**
     * Derive staleness for the whole tree from ONE batched git read.
     *
     * ⚠ O(nodeCount)/67MB SYNCHRONOUS — FORBIDDEN on the server hot path (fix
     * instar#1069). It loads the full 67MB index and a full git ls-tree on the
     * calling thread; on a real tree that starves the event loop for tens of
     * seconds. Use the off-event-loop detect (cartographerDetect.runDetect) on the
     * sweep path and serve routes from the snapshot. Retained only for small trees,
     * the pure module, and tests; the Slice-5 lint forbids calling it from
     * routes.ts and CartographerSweepEngine.ts.
     */
    staleNodes(): CartographerStaleEntry[];
    /** Derive staleness for a single node. */
    computeStaleness(nodePath: string): StalenessStatus;
    private deriveStatus;
    private statusReason;
    health(): {
        nodeCount: number;
        authoredCount: number;
        neverAuthoredCount: number;
        staleCount: number;
        generatedAt: string | null;
    };
    /**
     * Spec #2 (doc-freshness) richer health: the freshness ratio over AUTHORABLE
     * nodes (excludes `path-gone`), plus the two ABSOLUTE backlog counts the CI
     * ratchet also gates on so a green ratio over a small authored set cannot hide
     * a growing un-authored backlog (Goodhart guard). `freshRatio` is `fresh /
     * authorable` with 1 when there are no authorable nodes. A node is
     * never-authored "past grace" when it has no summary AND was first seen longer
     * ago than `graceMs`.
     */
    freshnessHealth(opts?: {
        graceMs: number;
        now?: number;
    }): {
        nodeCount: number;
        authorableCount: number;
        freshCount: number;
        staleCount: number;
        neverAuthoredCount: number;
        neverAuthoredWithinGrace: number;
        neverAuthoredPastGrace: number;
        authorFailedCount: number;
        freshRatio: number;
        generatedAt: string | null;
    };
}
//# sourceMappingURL=CartographerTree.d.ts.map