honest framework

honest-parse

One place source becomes a tree. honest-check and honest-test read the same parse — never two grammars that drift.

You've watched ESLint and Prettier choke on syntax the other accepts — each ships its own parser. honest-parse is the single parse every tool reads.

Your linter uses one parser to find class declarations. Your test generator uses a different one — a regex, say, or a hand-rolled tokenizer — to find the same declarations so it can enumerate them. The two agree until the day they don't: a decorator, a multiline signature, a string that looks like code. Now the linter flags a file the generator skips, and nobody can say which one is right, because they never read the same tree.

honest-parse is the single place source becomes a tree. honest-check and honest-test both depend on it; neither carries its own grammar. There is exactly one parse of a given file, and every tool that reasons about that file reasons about the same nodes.

One boundary, one grammar per language

Parsing is I/O against a grammar, so it lives at a boundary, behind one function. honest-parse wraps tree-sitter (Python, and the grammars the framework's own languages need) and hands back a tree the rest of the framework walks as data. The walk is pure: given the same tree, every query returns the same nodes, every time.

The framework owns its grammars rather than renting them. When a surface needs a language tree-sitter doesn't ship — the framework's own template dialect, for instance — that grammar is built and validated here, in one place, against a fixture corpus, so a node's reported extent is exactly the source it covers.

Why a shared parse is a poka-yoke

Two parsers of the same language are two definitions of "what the code says," and two definitions drift. The drift is invisible: each tool passes its own tests, and the disagreement only surfaces on the input neither author thought to write. Collapsing them to one parse removes the category. honest-check and honest-test cannot disagree about what a file contains, because there is no second reading of the file for them to disagree about.

That is the whole contribution. Not a faster parser, not a cleverer one — the *only* one, so "the linter and the generator saw different code" stops being a bug that can exist.