Pillar 02 · The wedge
Greppability
One name, one place. Every symbol has exactly one syntactic form at the
spot it's declared, so a plain grep finds the definition — always, with no
index, no language server, and no luck required.
Why "where is this defined?" is hard
In a mature TypeScript codebase, the simplest question — where does this name come
from? — is often the hardest. The language offers half a dozen ways to spread one
symbol across many places: overloads split a function into several signatures, decorators
wrap it in something else, namespace and interface merging scatter
a type across files, declare module augments code you don't own, and barrel
index.ts files re-export everything so the path you import from isn't where
anything actually lives.
A human copes with an LSP and muscle memory. An agent greps — it reads the repo as text — and every one of those constructs is a place the text lies about where the code is. Glyph removes them. The result is a language where search is a reliable primitive, not a heuristic.
01 One declaration, one form
A function, type, or component is declared exactly once, with one syntax. There are no
overloads to split a signature and no way to redeclare a name — two top-level things
with the same name is a hard error (E0100). So the search for a definition has
exactly one answer.
users.ts:4: export function parseUser( users.ts:9: export function parseUser( index.ts:2: export { parseUser } from api.ts:7: import { parseUser } from
users.glyph:12:fn parseUser(
raw: unknown,
) -> Result<User, string> {
The declaration keyword is part of the name you search for. grep "fn parseUser"
cannot match a call, an import, or a re-export — only the one place it's
defined.
02 No barrel files — the import path is the location
Glyph imports never re-export, and a module whose only contents are imports is rejected
(E0102). There is no index.ts laundering layer. So an import names
the module the symbol actually lives in — the path is the address.
import myapp/users { parseUser }
// ^^^^^^^^^^^ the file is src/myapp/users.glyph.
// not a re-export of a re-export of a barrel.
03 No hidden ways to attach behavior
There are no decorators wrapping a function in an unseen layer, no namespace
merging pooling members from across the tree, no declare module augmenting a
third-party type from a file you'd never think to open, and no implicit this
rebinding what a method refers to. A name resolves to one declaration, and everything that
shapes it is visible at that declaration.
04 Even mutation is greppable
Greppability isn't only about finding definitions — it's about finding
effects. Shared state lives in a std/store, and every change to it is a
literal .set( or .update( call. So one search enumerates the entire
write-surface of a piece of state, instead of hunting for assignments scattered through the
file.
src/tasks.glyph:16: tasks.update(fn(cur) { ... }) src/tasks.glyph:24: tasks.set([]) # every place the task list can change, enumerated. # nothing hides in an ordinary `x = ...` elsewhere.
A codebase is greppable when its text tells the truth about its structure. Every restriction here — one form per name, no re-exports, no merging — is there so that a search returns the answer, not a lead.
The benchmark — coming
The measurable version of this claim is precision: for a given symbol, what fraction of a plain grep's hits are the thing you were actually looking for, and how many steps does it take an agent to reach a definition? Glyph isn't in real-world use yet, so the chart below is a placeholder — it fills in with real numbers once there's a real codebase to measure.
TypeScript vs Glyph
The payoff compounds with scale and with autonomy. A human learns a codebase's hiding places; an agent starting cold does not. Greppability is what lets a tool — or a new teammate — navigate by reading, the way the repo actually is.