The plane model
The two plane axes — read them together
Atelier has two orthogonal plane vocabularies. They are not the same axis and must never be conflated; a row has a position on both.
Axis 1 — Provisioning planes (where a config row lives; distinguished only by ontology tenant_id)
| Plane | Tenant code | Holds | Forked? |
|---|---|---|---|
| Vocabulary | system | entity TYPES/schema, platform meta-types, KC vocab, notification_event vocabulary, action_surface/page_template/public_entity_surface registries, the org tree | No — shared by all tenants |
| Template | template_municipality | the forkable worked example: application rows, application_entity_membership, action_type + children, action_placement, notification rules/templates, admin_entity_config | It IS the fork source |
| Tenant | per fork (e.g. lisbon) | the same row classes, copied + FK-rewired by the one-shot full-catalog fork | created by fork |
The distinction is data, never a code branch — a row's plane is just its tenant_id. After fork, a tenant admin edits their copy with the same editors and zero special-case code, because every config endpoint keys reads/writes by the caller's resolved tenant. The only cross-plane bridge is one header, X-Author-Tenant, honored solely for callers holding the right UMS tier grant (authoring_update_allowed, auth.py:275). Fork copies exactly the rows whose type opts in via metadata.forkable (fork_plan.py:91-101) — not the control_plane axis, which marks shared infra/vocab a fork must never copy.
Axis 2 — Functional planes (what a thing IS; the M10 mental model)
| Plane | Owns | Primitive |
|---|---|---|
| Data | entities, properties, relationships, states — "what exists" | entity type + ResolvedSchema |
| Execution | actions, preconditions, edits, side-effects — "what can happen" | action_type + 6 child classes → ActionDefinition |
| Surface | widgets, layouts, data-bindings, interaction patterns — "what is experienced" | Block descriptor + WidgetKind + page_template |
How they compose
A single action_type row sits at (template plane, execution plane) before fork and (tenant plane, execution plane) after. A portal_page is (tenant plane, surface plane); its shared page_template shape is (vocabulary plane, surface plane). An entity type is (vocabulary plane, data plane) — never forked, because schema is shared; only its config (placements, dashboards, ACL) forks.
The clean rule: Axis 1 says whether a row is copied on fork; Axis 2 says which subsystem owns it. The ontology engine is blind to Axis 1 entirely — it sees only tenant_id on ordinary rows; the BFF supplies all plane semantics.