Plan reference (seedling.yaml)

seedling init writes a fully specified plan. Every key is optional when you write one by hand: the plan is the inferred draft overlaid with whatever you specify.

seed: 42                 # integer; --seed overrides
locale: en_US            # en_US | en_IN | de_DE; --locale overrides
defaults:
  date_from: '2023-01-01'
  date_to: '2025-12-31'  # default range for date/datetime generators
tables:
  <table>:
    rows: ...            # see below
    fixed: false         # true: --scale does not apply (reference tables)
    locale: en_IN        # per-table locale
    columns:
      <column>: ...      # generator spec
    computed:
      <column>: ...      # aggregate spec

rows

form meaning
rows: 5000 exactly 5000 rows, times --scale (rounded, at least 1) unless fixed: true
rows: {per: customers, min: 3, max: 12, dist: poisson, lambda: 5} for every row of customers, draw a count and create that many rows whose FK points at it

Per-parent keys: per (parent table), via (FK column when there are several FKs to that parent), min, max, dist (poisson with lambda, uniform, normal with mean/sigma, fixed with n). Draws outside [min, max] are re-drawn (up to 30 times, then clamped), so the shape stays close to the requested distribution. --scale reaches per-parent tables through their parents.

columns

A column is either a generator name (email: email) or a mapping with gen plus parameters. Without gen the mapping is merged into the inferred spec: status: {null_ratio: 0} keeps the inferred generator.

Common parameters:

key meaning
gen generator name, see generators.md
null_ratio probability of NULL (inferred as 0.1 for nullable, non-key columns)
locale override the table/plan locale for this column

Columns are generated in dependency order (an email built from first_name/last_name runs after them). A circular dependency is reported.

computed

Post-pass aggregates over child rows:

orders:
  computed:
    total:        {agg: sum, of: order_items.line_total, by: order_id, decimals: 2}
    item_count:   {agg: count, of: order_items.id, by: order_id}

agg is sum | count | avg | min | max; of is child_table.column; by is the child's FK column pointing at this table; decimals rounds the result (default 2). While the parent is written the column holds a placeholder (0 for NOT NULL columns, NULL otherwise); parents without children keep it. The final values are written with UPDATEs (SQLite/PostgreSQL), trailing UPDATE statements (SQL dump) or by rewriting the file (CSV/JSONL).

Constraints and the plan