/** * Confidence-boundary disclosure (change: add-confidence-boundary-disclosure). * * Every conclusion answer carries a deterministic `confidenceBoundary` saying what * it does NOT know: how much of the traversal rested on directly-resolved edges vs * synthesized (heuristic) ones, which known-unknowable boundaries it crossed, and * whether the index it ran against still matches the working tree. Categorical * labels and counts only — never a blended confidence score, never an LLM call * (north star c6d1ad07). Additive metadata: a caller that ignores it sees today's * answer unchanged. * * The basis and crossings are derived from the `confidence`/`synthesizedBy` * provenance already on every edge (spec: add-synthesized-dynamic-dispatch-edges, * add-type-hierarchy-resolved-dispatch). The staleness marker reuses the project * fingerprint written at analyze time and the git diff machinery — nothing new is * computed at analyze time except the optional build commit. */ import type { IndexIntegrity } from '../../analyzer/index-attestation.js'; /** The only edge fields the boundary reads — a minimal, structural view. */ export interface BoundaryEdge { confidence?: string; synthesizedBy?: string; } /** An edge in the call graph, as the boundary needs to see it for pair indexing. */ interface PairableEdge extends BoundaryEdge { callerId?: string; calleeId?: string; } /** How an answer's traversal was grounded: direct vs heuristically-recovered edges. */ export interface EdgeBasis { /** Edges resting on direct name/type resolution. */ directEdges: number; /** Edges recovered heuristically (confidence === 'synthesized'). */ synthesizedEdges: number; /** Synthesized-edge count broken down by the rule that produced each. */ synthesizedByRule?: Record; } export type KnownUnknowableKind = 'synthesized-dispatch' | 'unindexed-repo'; /** A boundary the computation is known to be unable to see past. */ export interface KnownUnknowableCrossing { kind: KnownUnknowableKind; /** The synthesis rule, when kind === 'synthesized-dispatch'. */ rule?: string; count: number; /** Actionable, human-readable disclosure. */ detail: string; } /** The index lags the working tree: source has changed since the build commit. */ export interface StalenessMarker { /** Short SHA the index was built at. */ indexCommit: string; /** Source files changed since the build commit (graph-relevant extensions). */ filesChangedSince: number; detail: string; } /** * The persisted index this answer ran against did not reconcile against its build-time * attestation — it is materially smaller than the build committed (`degraded`) or built * at a different schema (`mismatched`). Negative conclusions over such an index may be * false. Absent when the index is `healthy` or unverifiable (change: * add-index-integrity-attestation). */ export interface IndexIntegrityDisclosure { verdict: 'degraded' | 'mismatched'; detail: string; } /** * A background index repair is in flight for the queried repo (change: * make-index-self-healing). The answer was served from the stale index without * waiting; a later call after the rebuild completes serves fresh results. Absent * when no repair is running. Distinguishes *repairing* from plain *stale* so an * agent can choose to proceed on the disclosed answer or retry. */ export interface RepairInProgressMarker { inProgress: true; /** Why the repair started (integrity-mismatched, stale-region, schema-reset, …). */ reason: string; detail: string; } export interface ConfidenceBoundary { /** How the traversal was grounded. Omitted for non-traversal answers (recall). */ basis?: EdgeBasis; /** Boundaries the computation cannot see past. Absent when none. */ knownUnknowable?: KnownUnknowableCrossing[]; /** Index-vs-working-tree staleness. Absent when the index is current. */ staleness?: StalenessMarker; /** Index integrity verdict when the underlying index did not reconcile. Absent when healthy. */ integrity?: IndexIntegrityDisclosure; /** A background repair is healing this index right now. Absent when none is running. */ repair?: RepairInProgressMarker; /** * True iff the computation crossed no boundary: no synthesized-edge reliance, no * known-unknowable crossing, a current index, AND a reconciled (non-degraded, * non-mismatched) index. The answer-level NoFalseCompleteness flag — an incomplete * answer is never dressed as complete. */ complete: boolean; } /** * Map an index integrity verdict to a confidence-boundary disclosure. Healthy and * unverifiable (undefined) indexes disclose nothing — only a verdict that actually * undermines the answer's completeness is surfaced. */ export declare function integrityDisclosure(integrity?: IndexIntegrity): IndexIntegrityDisclosure | undefined; /** * The repair-in-progress marker for a directory, or undefined when no background * repair is running. Handlers pass this into {@link assembleBoundary} so a stale * answer served during a self-heal is disclosed as *repairing*, not silently stale * (change: make-index-self-healing). */ export declare function repairDisclosure(directory: string): RepairInProgressMarker | undefined; /** Tally direct vs synthesized edges (by rule) from a set of traversed edges. */ export declare function edgeBasis(edges: Iterable): EdgeBasis; /** Count direct vs synthesized edges internal to a node-id set (both endpoints in). */ export declare function edgeBasisWithinSet(edges: Iterable, nodeIds: Set): EdgeBasis; /** * Index a call-edge list by `caller→callee` for chain lookups. When both a direct * and a synthesized edge exist for the same pair, the direct one wins: the path is * realizable without the heuristic, so it is not a boundary crossing. */ export declare function buildPairEdgeIndex(edges: Iterable): Map; /** Edge basis for a set of node-id chains, deduping repeated caller→callee pairs. */ export declare function edgeBasisForChains(chains: string[][], pairIndex: Map): EdgeBasis; /** * The known-unknowable crossings implied by a basis: each synthesized rule is a * recovered-heuristic dispatch boundary the agent should verify before asserting. */ export declare function crossingsFromBasis(basis: EdgeBasis): KnownUnknowableCrossing[]; export interface StalenessAssessment { indexCommit: string | null; changedSourceFiles: number | null; marker: StalenessMarker | undefined; } /** * Pure staleness decision: emit a marker only when we can both name the build * commit AND count graph-relevant source files changed since it. A null commit * (older index, or a non-git analyze) or a null count (not a git repo, git failed) * means we cannot assess staleness reliably — we stay silent rather than cry wolf * on every answer. Zero changed source files means the index is current. */ export declare function buildStalenessMarker(indexCommit: string | null, changedSourceFiles: number | null): StalenessMarker | undefined; /** * Staleness marker when source has changed since the index's build commit. Git-based * and deterministic: staleness fires iff graph-relevant source files changed since * the commit the index was built at. Returns undefined when the index is current, or * when staleness cannot be assessed (no build commit, or not a git repo). */ export declare function computeStaleness(absDir: string, now?: number): Promise; /** Viewer/custom-analysis variant of {@link computeStaleness}. */ export declare function computeStalenessForAnalysis(absDir: string, analysisDir: string, now?: number): Promise; /** Return the viewer-facing assessment without collapsing unknown and current. */ export declare function assessStalenessForAnalysis(absDir: string, analysisDir: string, now?: number, useMemo?: boolean): Promise; /** * Assemble a boundary from its parts and derive `complete`. The synthesized-edge * crossings are derived from the basis; callers may add extra crossings (e.g. an * unindexed federated repo). An answer is complete only when nothing was crossed * and the index is current. */ export declare function assembleBoundary(parts: { basis?: EdgeBasis; extraCrossings?: KnownUnknowableCrossing[]; staleness?: StalenessMarker; integrity?: IndexIntegrity; repair?: RepairInProgressMarker; }): ConfidenceBoundary; /** Reset the staleness memo — test-only hook so a stubbed fingerprint is re-read. */ export declare function __resetStalenessMemo(): void; export {}; //# sourceMappingURL=confidence-boundary.d.ts.map