Skip to content

The mental model

The core mental model — declare, don't code; generate, don't build

Atelier has exactly four authored primitives and three generated experiences.

The four primitives an operator declares (as ontology rows):

  • Entity — a type with a schema: fields, relationships, FK-less declarative links, states. "What exists." (data plane)
  • Action — an action_type row plus six child classes (parameters, criteria, edits, creates, side-effects, placements): a gated, audited mutation with fan-out. "What can happen." (execution plane)
  • Surface — a page_template/portal_page/public_entity_surface/action_placement/dashboard_config that projects entities and actions into widgets, layouts, and bindings. "What is experienced." (surface plane)
  • Identity & policyacl_rule rows, application_entity_membership, organization tree, and the static role-action matrix that the BFF compiles into row-level filters. "Who may do what." (governs all three planes)

Around those, three more declared classes ride along: Notifications (notification_event/rule/template), Durable execution (submission_contract + execution_mode), and Reports/Analytics (report_template, source_kind:view).

The three generated experiences the platform produces from the declaration, with zero per-vertical code:

  • the staff admin console (SvelteKit, port 13002) — list/detail/dashboards driven by admin_entity_config and the shared renderer;
  • the citizen portal (SvelteKit) — generated from portal_page rows and the strict public reader;
  • the APIs — entity proxy, action submit, public reader, all keyed by the caller's resolved tenant.

The two declarations that matter most:

  1. Declare-not-code. A new entity, action, surface, or notification is a row, not a Python branch. The rules reference entity fields and templates ({{caller.organization_memberships}}); a compiler turns them into SQL. When tempted to write if/else in the BFF, declare a rule instead.
  2. Generated, not bespoke. The renderer is presentation-only (@aiaiai-pt/design-system, v0.38.0): it dispatches a list of declarative Block descriptors to widgets via a tester/priority registry, and both admin and portal hosts drive it through their own security-scoped DataProvider. Authoring field-schema lives in the host, never in the DS.

If you remember one sentence: the rows ARE the runtime contract — the admin_entity_config.actions JSONB cache is dead (#292 S2); the action engine compiles action_type rows per request; nothing is generated from a derived cache.

Atelier — Platform Specification. Internal canonical reference.