Everything extraction produces for one file. Content-addressed: the hash
is the incrementality key — same bytes, same facts. The persisted set of
FileFacts is the source of truth every derived table rebuilds from.
A language is data: a grammar, a capture query, and comment node kinds.
The engine consumes only this struct — adding a language adds a row here
and a .scm file, never engine code.