Build a Memory You Can Query
mksglu/context-mode does more than move raw work into a subprocess. Its ContentStore gives an agent a second place to keep evidence: a local, searchable knowledge base that can return an exact section when a later question needs it. This chapter focuses on that store and on the path from source material to ranked snippet. Understanding that path lets a reader decide when context-mode’s lower-volume summary path is appropriate, when exact retrieval matters more than maximum compression, and when a cache or fuzzy match should yield to freshness and verification.
Why one giant document is the wrong memory shape
A source can be preserved and still be difficult to use. If an entire API guide, issue export, or repository report is stored as one undifferentiated block, a later query has to search a large body and return a large body. The model then faces the same problem in a smaller disguise: too much material arrives before it knows which part matters.
Content-mode’s ContentStore addresses this by storing content in SQLite’s FTS5 full-text search system. The store treats a source as a set of labeled sections rather than as one opaque object. The label identifies where the content came from, while the chunk title provides a navigation handle. A search can therefore return a section such as a heading and its body instead of replaying the entire source.
The default Markdown path follows the document’s visible structure. Headings create section boundaries, and fenced code blocks are kept together so an example is not split into unrelated fragments. If a section is too large, the store divides it at paragraph boundaries and then at Unicode-safe byte boundaries when necessary. This is a practical compromise: the chunk must be small enough for ranking and response handling, but large enough to preserve the relationship between a heading, its explanation, and the code or qualification that gives it meaning.
The store uses different shapes for different kinds of input. Plain text can be divided at blank-line sections or overlapping line groups, which works for logs and command output that do not have Markdown headings. JSON is traversed by key path; nested objects become searchable paths, and arrays are grouped in size-bounded batches, with recognizable identity fields such as an id, name, or title helping create useful titles. A directory request is not an invitation to ingest an entire filesystem. The walk is bounded by extensions, depth, file count, ignore rules, and symlink policy, and each file goes through its own indexing path.
That distinction between source shape and storage shape prevents a generic summarizer from erasing useful structure. A summary might say that a document contains five examples. A chunked store can later return the example whose exact parameter name matters. A log summary might report twenty failures. Overlapping line groups preserve enough neighboring output to investigate one failure without putting every log line into the conversation.
The store preserves evidence, but the response remains selective
Context-mode’s documentation describes indexing as a way to retain the full raw content while returning only metadata and a search shape. The implementation stores chunks, source labels, content type, timestamps, and, for file-backed sources, the original path and a SHA-256 content hash. Re-indexing under the same label replaces the earlier chunks in one transaction, which prevents an iterative workflow from accumulating stale copies under one name.
The distinction is important: “full content is preserved” describes the stored representation, not every response shown to the model. ctx_index returns chunk counts and a source label. ctx_fetch_and_index returns a bounded preview. ctx_search returns result windows around matches. Those response limits are part of the context boundary. If a reader needs the complete source, the system can retrieve more sections through additional searches; it does not pretend that one small preview is the source itself.
File-backed indexing adds a feedback loop. On a later search, context-mode can compare the file’s modification time and content hash with the indexed metadata. If the file changed, it can refresh the source, while rechecking the current Read deny policy before opening it again. If the file disappeared or cannot be read, the cached result can remain available, but the store does not guarantee that it is a live copy. It degrades by retaining what it has rather than claiming live synchronization.
This is also why source labels matter. A label such as “React documentation” is easy to search, but a label that distinguishes a specific version, issue export, or URL gives later queries a clearer scope. The label is not just a display name; it is part of the boundary between one body of evidence and another. Two URLs can share a user-facing source name while the storage layer composes a distinct cache/index key so one response does not overwrite the other.
The system view is more useful than a list of internal tables. A source enters with identity and freshness metadata. A chunker adapts to the source’s structure. Two indexes provide different kinds of recall, and a vocabulary supports correction. Retrieval then combines those signals and returns a window around the evidence. No single component decides what the reader should believe; each component narrows the path from a large source to a usable passage.
Ranking is a layered navigation problem
A search engine for an agent does not need to prove the truth of a passage. It needs to find relevant evidence reliably enough that the agent can inspect it and use it with the source context intact. Context-mode’s ranking pipeline is built around that narrower goal.
The first layer uses FTS5 with the Porter tokenizer. Stemming reduces related word forms to a common search shape, so a query for “caching” can find text containing forms such as “cached” or “caches.” The query is sanitized and common stopwords are filtered when meaningful terms remain. BM25 then ranks the matching chunks using term frequency, inverse document frequency, and document-length normalization. Titles and headings receive extra weight in the project’s search statements because a match in a section title is often a strong navigation signal.
The second layer uses an FTS5 trigram index. A trigram index looks for short character sequences, so it can recover partial identifiers and substrings that a word-oriented tokenizer may miss. A partial query such as useEff can surface useEffect, and a fragment of a long technical identifier can still locate a relevant section. This layer is especially useful for code and for queries typed from memory, where the reader may know only part of a name.
The two ranked lists are combined with Reciprocal Rank Fusion, or RRF. Rather than pretending that one matcher has a universally correct score scale, RRF rewards a result that appears near the top in both lists. A passage that ranks well under stemming and substring matching is a stronger navigation candidate than one that happens to win under only one interpretation. The result is still a candidate: the model must read the returned evidence and judge whether it answers the question.
For multi-term queries, context-mode applies another reranking pass. It checks whether the terms appear close together, whether they occur in the title, and how often adjacent pairs appear. A section where “session” and “continuity” occur near one another is more useful for that query than a long section that mentions each word in unrelated paragraphs. This is a relevance heuristic, not semantic understanding. It can improve navigation while still returning a passage whose conclusion does not follow from the reader’s intended question.
When the initial query produces no result, the store can consult a vocabulary table and use Levenshtein distance to correct a likely typo before searching again. That helps with a misspelled technical term, but fuzzy correction has a predictable risk: a near spelling can refer to a different identifier. The corrected query should therefore be treated as a recovery path, not as permission to skip source verification. A successful fuzzy match means “look here,” not “this is definitely the term you meant.”
Smart snippets preserve the neighborhood of a match
Returning the first part of a matching chunk would be easy, but it would often miss the actual match. Context-mode uses highlighted FTS5 results to locate match positions and extracts windows around them. The result is a bounded selection from the original content, not a newly generated paraphrase. If several terms appear close together, their windows can be merged; if no match position is available, the implementation falls back to searching for the raw terms.
This design protects a subtle kind of evidence: the sentence before a parameter definition, the qualification after a recommendation, or the code block that follows an explanation. A snippet is still bounded, so it cannot guarantee that every dependency appears in one response. But it is more honest than a blind prefix because the omitted material is omitted around a known match, not because the system assumed the beginning was important.
The response also carries source and section identity. That makes the passage actionable: the agent can ask a narrower follow-up using the same source label, or query for another term in the same body of knowledge. Search is therefore not a single retrieval event but a controlled loop. The first call locates a candidate section; subsequent calls resolve details that were not needed initially.
That loop is deliberately paced. ctx_search accepts multiple queries in one call, so related questions can be answered together. It also exposes a rolling throttle and reduces or blocks repeated individual searches after a flood of calls. The purpose is not to make information inaccessible. It is to stop the retrieval loop from becoming another source of uncontrolled context growth. A good query set is specific enough to narrow the evidence and broad enough to avoid paying for many redundant round trips.
Exact retrieval and compressed summaries solve different problems
The project’s benchmark document makes this distinction explicit. ctx_execute_file can produce a one- or two-line aggregate such as a count of error lines, which yields very high reduction when the summary is all the task needs. ctx_index plus ctx_search returns complete, exact chunks and therefore achieves lower apparent reduction in some scenarios. That is intentional. A summary can answer “how many?” but cannot replace a precise code example when the next action depends on syntax.
This difference gives the reader a useful test. If the answer can be expressed as a stable statistic, a sandbox computation is often the better memory path. If the answer must preserve wording, code, parameter relationships, or qualifications, exact indexing and retrieval are safer. The two paths can be combined: compute over a large source to identify the relevant area, then search the indexed representation for the exact section that supports the final decision.
The content type filter reinforces this distinction. A query can request code-oriented results when the goal is an implementation example, or prose-oriented results when the goal is an explanation. That does not make the result semantically correct, but it reduces a common category error in which a matching identifier is returned when the reader actually needs the surrounding rationale—or a general explanation is returned when the reader needs executable shape.
Caching trades a round trip for a freshness decision
ctx_fetch_and_index adds a disk-backed time-to-live cache to the knowledge-base path. By default, a fetched source can be reused for 24 hours. A cache hit returns a small freshness hint and the source’s section count rather than downloading and indexing the page again. Stored content older than 14 days is cleaned up on startup. These choices reduce repeated network and indexing work, but they do not turn cached content into a current source by definition.
A cache is useful when the source is stable and the agent is asking several related questions: a specification, a framework guide, or a documentation page can be fetched once and searched repeatedly. It is less appropriate when the source changes quickly, when the task concerns the latest changelog, or when a policy decision depends on the current server response. The caller can extend the time-to-live for a stable source or bypass it with ttl: 0 or force: true. The right setting follows from the cost of stale evidence, not from the desire to maximize the cache-hit rate.
Caching also changes what the model sees. A hit contains no page content; it tells the agent that a stored representation exists and points back to search. This is a healthy failure mode for context preservation: the model is not accidentally given a large duplicate response, but it must make the explicit retrieval call if it needs details. A system that silently returned stale content would be more convenient and less trustworthy.
The same principle applies to automatic refresh of local files. A changed file can be re-indexed, but the refresh path rechecks access policy and hashes the bytes it reads. If a search result is stale, context-mode can report that it refreshed the source rather than making freshness invisible. The index is a memory aid with a maintenance policy, not a claim that external reality has stopped changing.
What this memory can and cannot promise
Context-mode’s knowledge base is strongest when the agent needs selective access to a body of exact evidence over time. It preserves more than a summary, returns less than a raw dump, and gives the model a route to ask for the next detail. Its ranking layers improve navigation, but they do not judge the truth of a result. Its cache saves work, but it introduces a freshness boundary. Its chunking protects structure, but a bounded snippet can still omit necessary context.
The practical discipline is to keep three questions separate. Was the source stored? Check the source label, section inventory, and indexing result. Was the right section retrieved? Check the returned title, match window, and source scope. Does the section support the decision? Read its qualifications and, when necessary, retrieve adjacent or corroborating sections. FTS5 can answer the first two questions; only the agent’s evidence-based reasoning can answer the third.
That discipline completes the knowledge-base model. Context-mode does not exchange memory for forgetting. It moves exact source material into a local structure that can be searched at the moment of need. The next challenge is broader than documents: an agent must also remember what it was doing, which files changed, and what the user decided when the conversation itself is compacted. That requires a different memory with a different owner—the session event and resume pipeline.