Typology Reference — column types & roll-up
The Typology is how a Gold column declares what kind of value it is — a
measurement, an identifier, an ordered rank, a category. Every column carries its
type in the entity YAML’s column_types: block, sibling to the columns:
role registry.
A number without a declared type is undeclared — not a licence to guess. The
compiler (typologycheck.py) verifies each declaration; the KLS
carries data only. Types are declarations, not runtime data.
Conceptual background: Sense 54 — The Typology and Sense 53 — The Codex.
Where it lives
Section titled “Where it lives”# entities/<entity>.yamlcolumn_types: material_id: { kind: identifier, subkind: foreign_key, references: materials.material_id } quantity: { kind: quantity, standard: null, rollup: additive } unit_price: { kind: quantity, standard: iso_4217, unit: CHF, rollup: non_additive } level: { kind: ordinal } # a tree depth — ordered, not summable billing_status:{ kind: category } billing_time: { kind: temporal, subkind: instant, standard: iso_8601 }Each entry declares exactly one kind, plus optional refinements.
The eight kinds
Section titled “The eight kinds”Every column is exactly one:
| Kind | What it is | Examples |
|---|---|---|
quantity | A measured value with units and (usually) uncertainty | mass_g, temperature_c, unit_price |
identifier | A key that identifies an entity — primary, business, external, foreign | sensor_id, serial_number, iso_country_code |
category | A value from a bounded, unordered vocabulary | status, event_type, region_name |
ordinal | A value from an ordered scale — rank / grade / level. Orderable but not summable | level (hierarchy depth), severity_rank |
temporal | A time-shaped value — instant, interval, duration, period | phenomenon_time, deployment_window |
boolean | Two-valued truth (optional third state) | is_active, has_calibration |
narrative | Uncontrolled text for human reading, not grouping or filtering | notes, interpretation |
geometry | Structured spatial data with a coordinate reference system | boundary_geojson |
The line between category and ordinal is order: a category is a plain
set (statuses, event types); an ordinal is a ranked scale where min/max/
median are meaningful but a sum is not (rank 1 + rank 2 ≠ rank 3).
Declaration fields
Section titled “Declaration fields”| Field | Applies to | Meaning |
|---|---|---|
kind | all | One of the eight above (required) |
subkind | all | Refines the kind — identifier.foreign_key, temporal.instant, geometry.polygon |
standard | quantity, temporal, geometry | A Codex standard id (ucum, iso_4217, iso_8601, rfc_7946_geojson). The checker enforces it legally binds the kind |
unit | quantity | Static unit — UCUM for physical quantities, an ISO 4217 code (CHF) for currency |
unit_column | quantity | Polymorphic unit — carried per-row in a sibling column (log-shaped data) |
references | identifier.foreign_key | FK target as entity.column |
derived_from | quantity | {entity, column, standard} — a typed edge: this column is produced from another by a cited conversion |
rollup | quantity | Roll-up policy up a hierarchy — see below |
Roll-up — how a quantity aggregates up a hierarchy
Section titled “Roll-up — how a quantity aggregates up a hierarchy”rollup answers: when this column is summarised over the children of a
hierarchy node (the Level Ladder — see Sense 42 — The Landscape),
what is the honest aggregate? It is a property of the type (intensive vs
extensive), not display configuration — it never cascades and never overrides.
| Value | Aggregate | Use for |
|---|---|---|
additive | SUM | Extensive quantities — counts, loads, money totals. Summing is honest |
averageable | AVG | Intensive quantities that average — a rate already normalised per unit |
non_additive | does not roll | Prices, extrema (min/max), ratios, percentages, pre-computed averages. Neither sum nor average is honest |
distinct_count | COUNT(DISTINCT …) recomputed over the subtree | Group values that need recomputation, not aggregation of parts (distinct-of-a-union ≠ sum-of-distincts) |
A cross-node total and share % are only meaningful for additive — the
“total” of a set of averages or distinct-counts is not their sum.
An undeclared rollup does not roll
Section titled “An undeclared rollup does not roll”Leaving rollup off a quantity is not an instruction to sum. An undeclared
rollup means the measure does not roll up at all — because guessing is the wrong
act. A declaration means a person decided, on the record; a silent default means
the engine decided, invisibly, where a wrong number looks exactly like a right
one — the principle of no silent behavioural defaults (see Sense 54 — The
Typology). The
typology-coverage gauge counts a quantity with no rollup as a gap, so a
non-rolling measure is visible, not just silently inert.
ordinal, category, identifier, etc. carry no rollup — they are not
summed up a hierarchy.
Coverage
Section titled “Coverage”jin make bakes an inspector-only typology_coverage table per entity: typed /
untyped / phantom columns and a coverage %. Complete coverage — every Gold column
typed, every quantity’s rollup declared — is a data-engineering requirement,
not a metric to optimise. You inspect a single column’s full type at the point of
use via the Column Passport.
Commands
Section titled “Commands”# Validate every column_types declaration (kind + Codex binding + rollup vocab)python3 scripts/typologycheck.pypython3 scripts/typologycheck.py entities/cases.yaml # one or more explicit files
# Bake the inspector tables (runs inside `jin make` as a post-dbt step)python3 scripts/typologycompile.py --kls <kls> --tenant <t> --entities-root <afs>/entitiesSee also
Section titled “See also”- Entity YAML Reference — the full entity file, of which
column_types:is one block - Sense 54 — The Typology — the design rationale
- Sense 53 — The Codex — the standards registry
- Inspecting — Portraits & Passports — reading a column’s type at the point of use