ADR-0044: Performance hardening of the gather kernel¶
Status: Accepted Date: 2026-05-20 Tags: performance · memory · concurrency · kernel · migration · fences Related: ADR-0043, ADR-0011, ADR-0001, ADR-0007
Context¶
A performance, memory, and concurrency review of the codegenie gather
pipeline and the vuln-index ingest path surfaced a set of issues in
already-shipped Phase 0/1/2/3 code:
- Redundant filesystem walks. The cache-key derivation, the per-probe
input snapshot, and probe bodies each walk the repo tree independently;
Path.rglob/globcannot prune descent, so every walk descends intonode_modules. tree-sitterParser/Queryrebuilt per file in the Layer-B AST probes — the query S-expression recompiles once per source file.CacheStore.putran a fullchmodwalk of the whole cache tree on every call — O(cache size) per put.CacheStorecarried hidden state (_key_meta) that grew unbounded over a process lifetime and coupledputto a priorkey_for.get_index_recordre-parsed the entire append-onlyindex.jsonlon every cache lookup — O(history) per probe.- The probe cache key was content-blind — keyed on
(path, st_size), so a same-size content edit returned a stale cached result. vuln-indexingest committed one transaction per row plus a separateSELECT changes()per row, on an autocommit sqlite connection.- The async coordinator serialized in-process work — the per-probe
prelude is synchronous blocking I/O on the event loop thread, and
CPU-bound probe loops never yield, so
asyncio.wait_fortimeouts cannot fire.
Fixing these requires editing the existing components. A faster CacheStore
is still CacheStore; you cannot optimize an existing component by adding a
parallel one without forking behaviour. This is precisely the "genuinely
horizontal change" case ADR-0043
names: a migration — a loud, reviewed, all-at-once sweep across existing
code — is the sanctioned path, distinct from a silent edit.
Decision¶
Authorize a bounded performance migration of the gather kernel. Every edit in scope is either behaviour-neutral (byte-identical observable output) or a strict correctness improvement; none changes a probe's facts. The work ships as a sequence of independently-reviewable commits, each gated by the full conformance suite (unit + integration + adversarial + fence + golden).
The migration touches these Phase 0/1/2 kernel files; each is added to
_KERNEL_ALLOWLIST in tests/fence/test_kernel_frozen.py with an # adr:
reference to this ADR:
src/codegenie/cache/store.py— scope the mode re-walk to__init__; replace the_key_metaside-channel with explicitputarguments; serve the index from an in-memory map guarded byindex.jsonlsize.src/codegenie/cache/keys.py— derive the cache key from the input snapshot's content hashes (content-addressed); add an explicit_CACHE_KEY_VERSIONconstant.src/codegenie/coordinator/coordinator.py— reorder snapshot/key derivation; offload the blocking per-probe prelude viaasyncio.to_thread.src/codegenie/coordinator/input_snapshot.py— stream file hashes; match declared globs against the shared filesystem index.src/codegenie/coordinator/file_index.py(new) — one pruningos.walkper gather, shared by key derivation and the input snapshot.src/codegenie/probes/layer_b/tree_sitter_import_graph.py,src/codegenie/probes/layer_b/node_reflection.py— buildParser/Queryonce per language; add cooperative yields so the declared timeout fires.
src/codegenie/hashing.py (already allowlisted) gains a content-addressed
key helper. The vuln-index ingest fix is outside the kernel scope
(vuln_index/ is a Phase-3 package) and needs no allowlist entry.
Per ADR-0043, this allowlist amendment is interim: when ADR-0043's
category-based test_no_silent_edits.py lands, these entries fold into the
migration category and the per-file rows are retired.
Consequences¶
- One-time cache invalidation. Making the cache key content-addressed
(and adding
_CACHE_KEY_VERSION) changes every probe's key once. Warm caches recompute on the first run after the change; correctness improves — same-size content edits are no longer silent stale hits. - The conformance suite is the safety net. Behaviour-neutral edits are pinned by existing unit/golden tests; correctness-improving edits add regression tests (e.g. a same-size edit must change the key).
- Timeout enforcement becomes real. Cooperative yields let
asyncio.wait_forcancel a CPU-bound probe at its budget; a probe that previously ran over budget to completion now produces the coordinator's low-confidence synthetic output instead. This is the intended ADR-0007 failure-isolation behaviour. _phase2_baseline.txtis unchanged. The baseline stays a pre-Phase-3 commit; the migration is recorded in the allowlist, not by moving the goalposts.