Add a family
- Define semantic input. Add
program/<kind>.tswith strict Zod objects, unique identity checks, reference validation, and cycle checks where needed. - Define the plugin. Add
families/<kind>.tsusingdefineFamily(). - Normalize. Convert every node, relation, and group to
GraphIR; never parse facts from display labels. - Select placement. Return a configured shared strategy or a cohesive
bespoke placer that still emits the common
Placementshape. - Define notation. Map every finite style key to shared caps, dashes, and a legend label.
- Register built-ins. Add the definition to
BUILT_IN_FAMILIES, or use an isolated customFamilyRegistryoutside the built-in catalogue. - Create fixtures. Hand-author realistic easy, medium, and hard programs.
- Verify visually. Run the family harness, inspect all PNGs, then run the complete gate.
A family is intentionally small
Section titled “A family is intentionally small”export const exampleFamily = defineFamily({ kind: 'example', schema: exampleProgramSchema, orthogonalEdges: true, normalize(program) { return { title: program.title, nodes: [], relations: [], groups: [] }; }, placement() { return { strategy: 'layered', config: { direction: 'RIGHT' } }; }, notation(styleKey) { return NOTATION[styleKey]; },});If the definition needs to import routing, labels, icons, colours, or the harness, the responsibility is in the wrong layer.