/** * One loaded traversal structure per artifact generation * (change: optimize-reachability-precompute). * * The handlers used to rebuild `Map>` adjacency on every tool * call. They now ask this module for the shared {@link TraversalIndex} instead. * * INVALIDATION rides entirely on object identity, so it cannot drift: the memo is * a `WeakMap` keyed on the `SerializedCallGraph` object itself. `readCachedContext` * hands out one context object per `llm-context.json` generation and replaces it * wholesale when the file's mtime moves — so a new generation is a new `callGraph` * object, which is a memo miss, which reloads. An externally-run `openlore analyze` * that rewrites the artifact under a warm daemon therefore cannot be answered from * the old structure; there is no cached-generation bookkeeping to get wrong, and a * garbage-collected context takes its structure with it. * * The persisted artifact is an optimization on top of that, never a source of * truth. Anything it cannot prove about itself — absent, oversized, stale-digest, * truncated, altered in its own bytes, addressing outside its own arrays, written * by another schema version, byte-ordered for another host — falls back to building * the structure in memory. A slower correct answer always wins. * * TRUST BOUNDARY, stated plainly. Two different properties, deliberately not * conflated: * - CORRUPTION is caught. `contextDigest` proves the structure belongs to the * graph being served, `payloadDigest` proves its own bytes are intact, and the * bounds checks prove no index escapes the array it addresses. Bitrot, a partial * write, and a careless hand edit are all refused, not answered. * - FORGERY is not, and cannot be here. A writer that recomputes both digests * could serve a structure that lies about the graph. But that writer already has * write access to `.openlore/analysis/`, where `llm-context.json` — the graph * itself — sits beside it and is exactly as forgeable. This artifact adds no new * trust surface: it is trusted precisely as far as the graph artifact is, never * further, and verifying it against the graph would cost the O(N+E) rebuild it * exists to avoid while buying nothing an attacker could not get by editing the * graph directly. */ import { type TraversalIndex } from '../../analyzer/condensation.js'; import type { SerializedCallGraph } from '../../analyzer/call-graph.js'; /** * Cheap (two-stat) pre-check: could the persisted structure possibly belong to the * current `llm-context.json`? Only then is it worth digesting a multi-MB artifact * to find out for certain — see the call site in `readCachedContext`. * * The test is mtime ordering. `analyze` writes the structure strictly AFTER the * context (see `artifact-generator.ts`), so a structure older than the context can * only be left over from a previous generation. That is exactly the steady state in * watch mode: the watcher rewrites `llm-context.json` on every flush and * deliberately does not rebuild the structure, so without this check every cold read * would pay a full digest plus a full artifact read only to reject the result — * measured at ~40 ms per read on this repo, forever, until the next full `analyze`. * * A false NEGATIVE (clock skew, a filesystem with coarse timestamps) costs an * in-memory rebuild — the correct answer, slightly slower. A false POSITIVE costs * nothing but the digest, because `contextDigest` still has to agree before the * structure is used. Neither can produce a wrong answer. */ export declare function traversalIndexMayBeCurrent(analysisDir: string): Promise; /** * Record the `llm-context.json` content digest for a just-parsed graph. Called by * `readCachedContext` on the cold path; without it the persisted structure cannot * be validated and is simply not used. */ export declare function recordArtifactDigest(cg: SerializedCallGraph, digest: string): void; /** * The traversal structure for `cg`, built in memory and memoized for this * generation. Synchronous — for the pure, sync computations (`computeFootprint`, * called once per task in `plan_parallel_work` / `map_in_flight_conflicts`) that * cannot become async without rippling through their callers. */ export declare function traversalIndexFor(cg: SerializedCallGraph): TraversalIndex; /** * The traversal structure for `cg`, preferring the one persisted next to the * analysis artifacts. Memoized per generation, so the disk read and the digest * check happen at most once per `openlore analyze`, not once per tool call. */ export declare function loadTraversalIndex(absDir: string, cg: SerializedCallGraph): Promise; //# sourceMappingURL=traversal.d.ts.map