memory_index_incremental — Incremental Index Updates¶
Source:
agentteams/memory_index_incremental.py
Patches references/memory-index.json in place when exactly one indexed document
changed, instead of rebuilding the whole index — an optimisation of the full
memory_index rebuild. Opt-in via
AGENTTEAMS_MEMORY_INDEX_INCREMENTAL_SED=1; it runs from the memory-index refresh
path (see the CLI reference for --refresh-index /
--query-index).
Legacy layout only. The patch addresses the indent=2 single-file layout line by line.
Since the compact and sharded layouts (memory_index_store), the
refresh path calls it only when the on-disk file is still in that layout, i.e. at most once
before the first full rebuild rewrites it.
Deliberately conservative: it declines far more often than it applies. Every decline falls back to a full rebuild, which is always correct, so the failure direction is slow rather than wrong.
API¶
try_incremental_sed_update(*, index_path, index, sources, project_name, framework, validate_index, root=None)¶
Attempt the patch; return a non-applied result on any risk.
Args (all keyword-only):
index_path(Path) — The on-disk index to patch.index(dict[str, Any]) — Its parsed contents.sources(Iterable[Path | str]) — Current source files.project_name(str),framework(str) — Metadata refreshed in the patched index.validate_index(callable) — Schema validator applied to the patched result before it is accepted.root(Path | None) — Project root. Must match whatbuild_memory_indexwas given. The matcher compares stored paths against source paths, so keying by absolute path while the index stores relative ones makes every set comparison differ — the incremental path would then silently never apply again. Correct output, optimisation quietly dead.
Returns: IncrementalUpdateResult — .applied and .reason.
Decline reasons¶
reason |
Meaning |
|---|---|
missing_index_file |
Nothing to patch |
invalid_index_shape |
documents/postings are not the expected types |
source_set_changed |
The set of indexed paths differs from the current sources |
missing_doc_entry |
A source has no matching document |
eligible_only_single_changed_doc |
Zero or more than one document changed |
invalid_doc_id |
The document's doc_id is not an int |
changed_doc_unreadable_or_empty |
The changed source file could not be read, or tokenized to zero terms |
term_set_changed |
The changed document's term vocabulary differs from the indexed version (reliability gate against structural postings adds/deletes) |
doc_anchor_not_found |
The document's line range could not be located in the on-disk index file |
term_anchor_missing:<term> |
A term's postings block could not be located in the on-disk index file |
meta_anchor_missing:<prefix> |
A top-level metadata line (e.g. built_at, avgdl) could not be located in the on-disk index file |
sed_failed:<exc> |
The sed subprocess failed; file restored from backup |
mutated_json_invalid |
The patched file could not be read back or parsed as JSON after the sed patch; restored from backup |
mutated_schema_invalid |
The patched index failed schema validation (validate_index); restored from backup |
post_patch_mismatch |
The patched file's parsed contents don't exactly match the expected in-memory result; restored from backup |
Migrating an index that stores absolute paths¶
An index built before root= existed stores absolute paths. With root now supplied,
the stored set and the offered set differ, so the matcher returns
source_set_changed and the run falls back to a full rebuild — which rewrites the
whole file in the new relative form. That is the intended migration: applying
incrementally would produce an index mixing absolute and relative paths.
Pinned by tests/test_memory_index.py::test_legacy_absolute_index_migrates_via_full_rebuild.
Why sed¶
The patch is applied by a generated sed script over deterministic line ranges, not by
re-serialising JSON — re-serialising a 16 MB index is the cost the incremental path
exists to avoid. _restore_backup reverts the file if the patched result fails
validation, so a malformed patch never lands.