feat(context): carry the questions a context answers in its bundle - #79
Merged
Merged
Conversation
A routing description answers "what is this?", which is not how anyone searches. People search with the words of their problem, so a context described as "billing-postgres default_pool_size regression" is invisible to someone asking why checkout was throwing 5xx. Two optional lists close that gap: the questions a context should catch, in the phrasings a user would type, and the names inside it — services, ticket ids, error strings, the words that appear in one context and nowhere else. Both are matched against and neither is ever shown, so they cost nothing in the prompt and can be kept in bulk. They live in the bundle rather than in this machine's routing cache, so a context handed to a teammate is findable by the same words on their machine as on yours. Import carries them; export already did, since it copies the manifest. Both are optional. A bundle written before this existed, or by a host that does not generate them, matches on less and works exactly as before. A save that says nothing about them leaves what is there alone, the same rule extensions already follow, so one host cannot silently strip what another wrote. Nothing generates them yet — that is the next change. This is the storage, the indexing, and the proof that it works.
tanglearncode
added a commit
that referenced
this pull request
Aug 8, 2026
…d by (#80) #79 added the storage; nothing filled it. This is the half that does the work, and it does it at save time on purpose: no plugin process has a model, but the session doing the saving does, and it has just read the material. Doing the language work once, where it is free, beats doing it on every question, where it is impossible. Every host's save document now asks for two lists alongside the routing description: the questions this context should answer, in the words a user would type rather than the words the profile uses, and the names that appear in this work and rarely anywhere else. Both are matched against and never shown, which the documents say outright — a list written for a reader is a worse list than one written to be matched. Omitting them on an update leaves the stored lists alone, so a save from a host or a turn that generated none cannot wipe what an earlier one wrote. pi takes a save as tool arguments rather than a capture file, so its tool schema and runtime carry the fields directly. A test sweeps every host's save document for both fields and both rules. The storage was shared core and arrived everywhere at once; this instruction is per host, and a host that forgot to ask would store contexts that are quietly harder to find than the same work saved elsewhere.
tanglearncode
added a commit
that referenced
this pull request
Aug 8, 2026
The lists added in #79 travel inside the bundle, and the guidance in #80 told the model to fill them with "commands, hosts, people". Hostnames and people mean nothing on anyone else's machine, and asking for people contradicts the save command's own rule against writing unnecessary personal information. A matching list is the easiest place for a context to quietly stop being shareable: an absolute path looks like a usefully rare term while it is written, and is worthless or revealing the moment the bundle is handed over. The hosts had also drifted apart — three asked for hosts and people, two asked for neither — so the same work saved from different hosts produced different bundles. One wording everywhere now, asking for names that belong to the subject: services, components, repositories, ticket ids and prefixes, error strings, product and system names. Each document states that the lists travel and must stay free of absolute paths, home directories, usernames, personal names and email addresses. If the work genuinely is about a particular environment, the profile is where that belongs and is untouched. What changes is that the matching index is not. And a filter, so none of this rests on prose: entries carrying a drive letter path, a UNC path, ~/, /home/, /Users/, /root/, or an email address are dropped from both lists at save time. Ordinary domain terms that merely look technical are kept, which the tests pin.
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The largest recall win in the routing work, and the change that makes shared contexts route the same way for everyone on a team.
The problem
A routing description answers "what is this?". Nobody searches that way — people search with the words of their problem. A context described as
billing-postgres default_pool_size regressionis invisible to someone asking "why was checkout throwing 5xx last week". Same incident, no shared words.What this adds
Two optional lists on a context:
routingQuestions— the questions it should catch, in the phrasings a user would actually type.routingEntities— the names inside it: services, ticket ids, error strings. The words that appear in one context and nowhere else, which is what makes them decisive.Both are matched against and neither is ever shown to a session, so they cost nothing in the prompt and can be kept in bulk.
Why they live in the bundle
So a context handed to a teammate is findable by the same words on their machine as on yours. If this material stayed in the local routing cache, every teammate would rediscover it separately and the same context would drift into private variants. Import carries them; export already did, since it copies the manifest.
Compatibility
Both are optional, and absent rather than empty when there is nothing — a manifest should not carry a field that says only that a host did not fill it in.
Scope
Nothing generates them yet; that is the next change. This is the storage, the indexing, and the proof that it works.
Tests
11 new. Storage and normalisation (trimming, case-insensitive de-duplication, non-string entries, a field that is not a list, the caps), update semantics in both directions, and that a change to this material counts as a routing change.
The three that matter are under what it buys: with the material, a question sharing no words with the description finds the context, and so does a bare
dep-9001. Without it, the same corpus and the same question do not — which is the recall failure this exists to fix, asserted rather than asserted-about.One earlier draft of that test was worthless and I fixed it: the context was called "Checkout incident", so the query word
checkoutmatched the name and the test passed for the wrong reason. It is now named so that the name and description share nothing with the question.Diff coverage: 78 changed lines, 0 uncovered. Full suite 391 passing.