Semantic & Computation Registries
If the typed query algebra is the runtime’s grammar, the registries are its dictionary. They are the mechanism behind a core engineering principle in CLAUDE.md: keep LLM outputs constrained behind Zod schemas, registries, validators, and evals. A planner may only reference concepts, metrics, and formulas that a registry defines, because an LLM left to name its own metrics will invent plausible-sounding ones — anything outside the registry becomes a typed gap, not an improvised answer.
Semantic registry (packages/semantic-registry)
A small, schema-first package (src/schema.ts, src/registry.ts, src/effect.ts) defining the controlled vocabulary v0 for planner and query-engine work:
- Entities:
Fund,LP,Commitment,SideLetter,SideLetterRight,PortfolioCompany,Investment,ValuationMark - Metrics:
commitment_amount,current_markup,unfunded_commitment,latest_carrying_value - Rubrics:
side_letter_onerousness,reconciliation_severity,audit_priority - Operations:
resolveEntities,queryFacts,calculateMetric,rank,compare,retrieveEvidence,summarizeEvidence,explainResult - Gap types:
unknown_metric,missing_extraction_schema,missing_source_data,insufficient_evidence, plus adjacent gaps
Every definition kind has a schema (EntityDefinitionSchema, MetricDefinitionSchema, RubricDefinitionSchema, OperationDefinitionSchema, GapTypeDefinitionSchema, …) and decode helpers (decodeSemanticRegistry, safeDecodeSemanticRegistry). Inspect the live vocabulary with:
bun run demo:registryComputation registry (packages/computation-registry)
Where the semantic registry names things, the computation registry makes numbers derivable and reproducible. Its exports (src/index.ts) cover defined terms, formulas, an evaluator, reconciliation definitions and policies, typed assertions, rule sets, allocation logic, and an LPA compiler (lpa-compiler.ts — compiling limited-partnership-agreement economics into registry terms).
The centerpiece is a recursive, fully typed formula AST — no eval, no free-form expressions:
// packages/computation-registry/src/schema.ts (abridged)
export type FormulaAst =
| { readonly op: "literal"; ... }
| { readonly op: "ref"; ... }
| { readonly op: "add"; readonly args: readonly FormulaAst[] }
| { readonly op: "subtract"; readonly left: FormulaAst; readonly right: FormulaAst }
| { readonly op: "multiply"; readonly args: readonly FormulaAst[] }
| { readonly op: "divide"; readonly numerator: FormulaAst; readonly denominator: FormulaAst }
| { readonly op: "round" | "floor" | "ceil"; readonly value: FormulaAst }
| { readonly op: "min" | "max"; readonly args: readonly FormulaAst[] }
export const FormulaAstSchema = Schema.suspend(() => Schema.Union([ ... ]))evaluator.ts executes these ASTs against decoded inputs (safeDecodeComputationInput), and reconciliation.ts compares registered assertions from independent sources (reconcileRegisteredAssertions) — this is what a reconcile query step invokes. execution.ts in query-runtime imports exactly these functions; a compute step cannot run a formula the registry doesn’t hold.
Version pinning
QueryExecutionContext.registryVersions (see Typed Query Runtime) records the semantic-registry, computation-registry, and permission-policy-bundle versions into each AnswerRun. An answer is reproducible only relative to the registry snapshot that produced it, so the snapshot identity is part of the audit trace.
When a question needs a metric or formula that doesn’t exist, the correct behavior is to emit a gap (unknown_metric, missing_extraction_schema), not to sneak a computation into adapter code. Gaps are first-class runtime outputs — they drive the registry-growth backlog. Adding a formula means registering it (with its defined-term provenance, DefinitionSourceSchema) so it is hashed, versioned, and auditable.
Fund-accounting computations that reach the general ledger have additional ASC 946/820 constraints — see Fund Accounting.