Skip to content

Family plugins

Each family has a schema under program/ and a definition under families/. defineFamily() preserves the relationship between its program type and its finite relation-style keys.

flowchart LR
  Schema["strict schema<br/>program/architecture.ts"] --> Define["defineFamily()<br/>families/architecture.ts"]
  Define --> Normalize["normalize()<br/>GraphIR"]
  Define --> Placement["placement()<br/>PlacementSpec"]
  Define --> Notation["notation()<br/>caps · dash · legend"]
  Normalize --> Prepared["PreparedFamily"]
  Placement --> Prepared
  Notation --> Prepared
  Prepared --> Shared["shared engine pipeline"]

A family may:

  • define and validate semantic input;
  • normalize that input into shared nodes, relations, and groups;
  • choose a configured placement strategy or supply a bespoke placer;
  • map its finite relation styles to shared notation.

A family may not import routing, label, icon, colour, harness, or renderer code. Those restrictions are executable ast-grep and contract-test rules.

FamilyRegistry is an instance class because it owns a configured set of families for a meaningful lifetime. with(family) returns a new registry, so a custom extension cannot mutate global state or affect another engine consumer.

flowchart LR
  Builtins["builtInFamilyRegistry"] --> LayoutA["layout call A"]
  Builtins --> With["registry.with(custom)"] --> Custom["isolated registry"] --> LayoutB["layout call B"]
  Custom -. "does not mutate" .-> Builtins

See Add a family for the implementation sequence.