Skip to content

feat(context): carry the questions a context answers in its bundle - #79

Merged
tanglearncode merged 1 commit into
mainfrom
routing-expansion-storage
Aug 8, 2026
Merged

tanglearncode merged 1 commit into
mainfrom
routing-expansion-storage

Conversation

@tanglearncode

Copy link
Copy Markdown
Contributor

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 regression is 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.

  • A bundle written before this existed 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 a host that does not generate them cannot silently strip what another host wrote.

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 checkout matched 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.

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
tanglearncode merged commit cb518fc into main Aug 8, 2026
11 checks passed
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.
@tanglearncode tanglearncode mentioned this pull request Aug 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant