Module 2 · Tools, Knowledge, Memory & Research Design · scripted

Jumping-Off Points: Naming for the Searching Agent

30 minoutcomes: knowledge-systems, context-engineering, tool-design

When the task doesn't map onto a tree

We like trees. A tree gives the agent a designed entry point — start at the root, read the table of contents, descend — and the structure itself encodes a process for looking through the information. But often the situation isn't so obliging. Tasks frequently arrive with no clear mapping onto any tree we could reasonably build: a large codebase where nobody can say in advance which files a change will touch; a web of research notes that interconnect rather than nest; an organization's accumulated documents, where the task "update our consent procedure" doesn't correspond to any single branch. The information is a graph with no front door, and the agent must manufacture its own way in. How do we handle these situations — where finding the starting point is itself part of the task? That is this lesson: a set of principles for designing information, and tasks, for the agent that has to search its way to a beginning.

Two ways in

The mechanic on the right is worth stating plainly, because everything in this lesson follows from it: task words → guessed names → text search → jumping-off points → explore outward. The searches are cheap and lexical — text matching over names and contents, not understanding. Which means the whole scheme lives or dies on one question: do the names in the information correlate with the words in the task?

Watch an off-the-shelf agent bootstrap

This is not a hypothetical design. It is exactly what the coding agents you can buy today — Claude Code, Codex and their kin — do in the first seconds of any task. Watch the opening moves:

Example 1 · The first three moves are searches
user

The invoice page times out when it loads. I think it's a database problem. Fix it.

assistant
⟨tool_use⟩
search_files(pattern = "database")
user
⟨tool_result⟩
47 matches across 12 files:
src/legacy/database_helper.py (19), src/db/session.py (6),
src/db/queries.py (8), docs/database_setup.md (5), …
assistant
⟨tool_use⟩
search_files(pattern = "invoice")
user
⟨tool_result⟩
src/billing/invoices.py (31), src/billing/invoice_views.py (14),
src/legacy/database_helper.py (2), templates/invoice.html (7)
assistant
⟨tool_use⟩
read_file(path = "src/billing/invoice_views.py")
NoteNot one file was read until two searches had run. The agent turned the task's own words — "invoice," "database" — into queries, and the overlap between the two result lists told it where to look first. The task vocabulary WAS the map.

And the queries get smarter as the corpus reveals its dialect. An agent told to "fix the layout of the settings screen" that can see React in the project doesn't search for "screen" — it searches the way React projects name things:

Example 2 · Inferring the dialect

task:"fix the layout of the settings screen"
observed:package.json lists react; src/components/ exists
queries:"Settings" in src/components/ SettingsPage, SettingsPanel, settings.tsx "useSettings"

The agent blends TWO vocabularies: the task's words and the corpus's visible conventions. Both are naming systems — and both are under YOUR control.

Names are addresses, not labels

A name is not just a label for the human who opens the file. It is the address by which task language finds the thing. Those are different jobs, and they can disagree: a module named persistence/ is accurately labeled — but if every task that touches it says "database," the address is wrong. The searching agent will fly right past it, or land somewhere worse.

The test of a name

Not:"does this name describe the content?"
But:"does the sentence that ASSIGNS WORK contain this name?"
"update the database schema"→ db/schema.py found
"update the database schema"→ persistence/model.py missed
"revise the consent procedure"→ consent-procedure.md found
"revise the consent procedure"→ ethics-docs-v2.md missed

Name things with the words tasks will use. When you cannot predict the task vocabulary, you are guessing — so listen for it: the way your team actually phrases requests is the vocabulary your names need to speak.

One concept, one name

Search returns what matches — and only what matches. If the same concept lives under db, database, store, and dao, then every query returns a shard of the truth, and the agent has no way to know the shards are siblings. It will explore the shard it found and believe it has seen the whole.

Synonyms fragment the view

search "database"→ finds db/… and database_helper.py MISSES store/ and dao/

The agent now "knows" how this system does persistence. It is wrong — and nothing it found will tell it so.

The rule: one concept, one name, everywhere. A dull consistent name beats a clever varied one, because consistency is what makes search results COMPLETE.

This is the conventions file again, with a sharper reason to keep it. A convention like "we always call it db" isn't just for readers — it is what guarantees that whoever searches the corpus, human or agent, gets the whole picture in one query.

A hit carries no authority — and becomes training data

Here is the trap in the middle of this lesson. Text search ranks by match, not by correctness. And corpora accumulate: the old way of doing things rarely gets deleted — it gets left. So the query "database" matches the deprecated helper from four years ago exactly as well as the current code. Often better — old code tends to be more literally named.

What happens next is the dangerous part, and you already know the mechanism from teaching by example: the agent doesn't just read its first hit. It imitates it. Whatever pattern search surfaces becomes the worked example in the agent's context — the in-context training set for everything it writes next.

Example 3 · The wrong example, faithfully learned
user

Add a query that fetches unpaid invoices from the database.

assistant
⟨tool_use⟩
search_files(pattern = "database")
user
⟨tool_result⟩
src/legacy/database_helper.py — 19 matches, e.g.
  "conn = raw_connection(DATABASE_URL)"
  "cursor.execute('SELECT * FROM invoices …')"
assistant

I found the database access pattern. Following the existing style, I'll add a function using raw_connection and a cursor…

NoteThe agent did nothing wrong. It searched, found a strong match, and followed the local pattern — exactly the behavior we normally want. The corpus taught it the old way, fluently. A wrong entry point doesn't just misdirect the reading; it miseducates the writing.

Two designs follow from this trap — one for the information, one below for the marks it must carry. The first is blunt: don't leave two ways of doing the same thing alive and unlabeled. Delete what you can. What you cannot delete, you must mark — which raises the question of where the mark has to live.

Marks must live where the search lands

A wiki page that says "the old database helper is deprecated" does nothing. The searching agent never routes through the wiki — it lands directly on the hit, reads outward from there, and starts work. For an authority mark to function, it must be lexically attached to the thing search finds: in its name, its path, or its opening lines.

Marks that travel with the hit

works:src/legacy/database_helper.py the path says it old_db_access.py the name says it # DEPRECATED — use db/session.py line 1 says it, and points to the replacement
does nothing:a wiki page listing deprecated modules a warning in the onboarding slides the team's shared memory of "oh, we don't use that anymore"

The searching agent gets the hit and ONLY the hit. Whatever it must know about the hit has to arrive WITH it.

Note the best version does two jobs: it warns, and it forwards — "use db/session.py instead" turns a dead end into a redirect. The wrong entry point becomes a signpost to the right one.

Plant signposts — small trees floating in the graph

You cannot impose a global tree on a living codebase or a knowledge graph. But you can scatter local roots: small, name-dense landmark files — an INDEX.md per region, a README per subsystem, a start-here map. These are search magnets. Precisely because they are dense with task vocabulary — a good index mentions everything it points to — they outcompete random middle-of-the-corpus files for the first hit. And once found, each one acts as a local table of contents: the agent falls back into the tree-reading behavior we wanted all along.

A signpost is a search magnet

src/billing/README.md

# Billing
Invoices, payments, refunds, and the invoice PDF pipeline.
invoices.pycreate/query invoices (see db/session.py)
invoice_views.pythe invoice page and its API
pdf/rendering; templates in templates/invoice.html
Legacy note: anything under src/legacy/ is dead — do not copy.
search "invoice"→ this file is FULL of the word "invoice"
found first or near-first
and the agent now holds a local map, a forward pointer, and a warning

The design goal in one sentence: make the first thing search finds be a signpost, not a random page.

Or hand the entry point over in the task

Everything so far designs the information. The dual is to design the task: if you already know the names, say them. Every name you provide converts a search gamble into simple navigation — the task prompt can carry a table of contents even when the corpus has none.

Two versions of the same task

gamble:"Fix the invoice page timeout — I think it's the database."
navigation:"Fix the invoice page timeout. The page is src/billing/invoice_views.py; our current DB access pattern is db/session.py. Ignore everything under src/legacy/."

Three filenames. They replace the entire bootstrapping phase — every search, every wrong hit, every token of exploring the legacy code — and they close the trap from Example 3 before it opens.

Cheap insurance, and it compounds: the guidance costs you one sentence, and it saves the priciest and most error-prone stretch of the whole trajectory — the part where the agent knows the least.

The grep test

All of these principles compress into one design check you can run in five minutes, on any corpus, before any agent touches it. It is the same move as stress-testing a skill's catalog line — pointed at your whole information space.

The grep test

Write down three REAL tasks, in the words someone would
actually use to assign them.
Extract the searchable words from each — the nouns a stranger
would grep for.
Run the searches yourself over the corpus.
For each top hit, ask:
current, or leftover?
signpost, or random middle page?
if it's wrong or old — does IT say so, and forward you on?
one query — or did synonyms hide half the picture?

Every failure has a fix from this lesson: rename toward task vocabulary · collapse synonyms · delete or mark the dead · plant a signpost · seed the task.

Your turn

Run the grep test on an information space you actually own — your codebase, your notes, your lab's shared drive. Three real tasks, their words, the searches. Bring the single worst hit you found: the leftover, the shadowed synonym, the missing signpost — and say which fix you'd apply and why.