genesis_taxonomy
Schema-first node vocabulary (ADR-0002): one catalog classifies the node species of a domain; codegen emits two projections from it —
- the typed Dart factory registry (
.g.dart) — aComponentRegistrybinding wire type names toSeedconstructors, validating at construction time (unknown type, missing/mistyped/unknown prop, invalid enum value, children on a leaf), every failure a structuredTaxonomyExceptionan agent loop can feed back to an LLM verbatim; - the LLM-facing tool schema (
.g.json) — a JSON Schema (draft 2020-12) for authoring an A2UI v0.9-shapedupdateComponentsmessage against the catalog, with prop types / descriptions / defaults / required flags flowing through,childrenon containers only, and action affordances surfaced asx-actions+ prose.
Both projections are byte-deterministic (types sorted by name, props in
catalog order, no timestamps; Dart output formatted with dart_style), so
the registry the runtime constructs from and the schema the LLM authors
against cannot drift — the generator-in-sync byte-equality test is the
standing guard on committed artifacts.
Catalog format
A catalog is a JSON document (*.catalog.json):
{
"$comment": "free-form, ignored",
"catalog": {
"name": "fixture", // parameterizes every generated header
"version": "0.1.0",
"description": "optional"
},
"types": {
"gauge": {
"description": "Flows to the tool schema verbatim.",
"container": false, // containers carry children; leaves reject them
"props": {
"label": { "type": "string", "required": true, "description": "..." },
"value": { "type": "number", "required": true, "description": "..." },
"scale": { "type": "integer", "required": false, "default": 10, "description": "..." },
"enabled": { "type": "boolean", "required": false, "default": true, "description": "..." },
"align": { "type": "enum", "values": ["start", "center", "end"],
"required": false, "default": "start", "description": "..." }
},
"dart": {
"class": "Gauge", // the Seed subclass to construct
"import": "fixture_seeds.dart", // package: or relative to the .g.dart
"positionalProps": ["label"],
"namedProps": ["value", "scale", "enabled", "align"],
"childrenParam": "children" // containers only
},
"actions": { // extension vocabulary (see below)
"set": { "description": "Overwrite ... with context.value." }
}
}
}
}
Rules the parser enforces loudly (structured CatalogFormatExceptions with
the document path):
- prop
typeis one ofstring/integer/number/boolean/enum(JSON Schema vocabulary;numberbinds to Dartdouble, integral values widen;enumneeds a non-emptyvalueslist and binds as a validated DartString); - props are required or optional-with-default — optional without
defaultis rejected, as is a default whose type (or enum membership) doesn't match; dart.positionalProps+dart.namedPropsmust cover exactly the declared props;dart.childrenParamexactly whencontaineris true.
The extension seam (loud unknown keys)
The core format owns only description / container / props / dart at
the type level. Any other type-level key must be claimed by a registered
CatalogExtension, or parsing fails with UnhandledCatalogKeysException
listing every unhandled key — unknown vocabulary is never silently dropped
(the spike-5 failure mode this seam exists to kill).
The actions block itself rides this seam as the proof:
ActionsCatalogExtension (in defaultCatalogExtensions) parses it into
ActionDeclaration data on CatalogType.actions and projects it into the
tool schema (x-actions + description prose). Affordances are carried as
data here; genesis_consent consumes them for hit-test routing (ADR-0005).
Consuming the generator
Via build_runner (the production path, ADR-0002 Decision 5): depend on
genesis_taxonomy, drop a <name>.catalog.json anywhere build_runner looks
(lib/, test/, ...), and run:
dart run build_runner build
The builder (auto-applied to dependents) emits <name>.g.dart +
<name>.g.json next to the catalog, build_to: source, so artifacts are
committed and the in-sync test guards them.
Programmatically (no build_runner required): the builder is a thin shell over the pure core —
final outputs = generateFromCatalog(catalogJson); // parse + both emitters
final catalog = Catalog.parse(catalogJson); // or stepwise
final registryDart = emitRegistry(catalog);
final toolSchemaJson = emitToolSchema(catalog);
The shipped builder runs with defaultCatalogExtensions; a domain needing
custom extensions wraps generateFromCatalog(json, extensions: [...]) in its own
builder.
Building trees through the registry
buildSeedTree(registry, components, {rootId}) turns a flat keyed component
list into a Seed tree through a registry passed as a parameter — it
never imports a generated file (the one line spike 5 had to fork, now a
seam). Component ids become Seed keys, so whole-tree re-emission
reconciles to an identity-preserving patch (ADR-0003). Tree-shape violations
(duplicate id, unknown root, dangling child, cycle) throw structured
TreeShapeExceptions. Envelope parsing (updateComponents itself) is wire
vocabulary and lives with genesis_dialogue, which hands the flat list to
this builder.
Deferred
- A2UI standard-catalog alignment (
Text/Column/Button, v0.9createSurface.catalogId) — thegenesis_dialogueboundary owns the envelope and any standard-catalog mapping; this package stays catalog-generic. - Per-instance actions — real A2UI v0.9 wires actions per instance via a
component's
actionproperty; the catalog declares type-level affordances only ("what CAN this afford"). Instance wiring isgenesis_dialogue/genesis_consentterritory (spike-5 fidelity ledger). - Extra extension projections — spike 5 emitted a third artifact
(
actions.g.dart, the Perception-class -> wire-type map for the action router's hit-test). The extension seam currently projects into the tool schema only; an emit-side artifact hook lands withgenesis_consent, which owns that file's consumer. - Dart-enum bindings — catalog
enumprops bind as validatedStrings; a value-mapping to real Dart enums is a future binding extension.
Libraries
- builder
- The build_runner shell over the programmatic generator core (ADR-0002 Decision 5).
- genesis_taxonomy
- Schema-first node vocabulary (ADR-0002): one catalog classifies the node species; codegen emits the Dart factory registry and the LLM tool schema.