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.