How do I add a new codegen entity model?

Table of Contents

Entity models drive C++ + SQL codegen. The current form is a literate ores.<component>.<entity>.org file under the component's modeling/ directory; codegen discovers it from the modeling_dir manifest entry by matching #+type: ores.codegen.entity in the frontmatter. The shape is documented in the Codegen org-entity meta-model.

1. Question

How do I add a new codegen entity model?

2. Answer

2.1. Scaffold the file

projects/ores.compass/compass.sh add entity_org \
    --slug <entity> --component <component> \
    --description "<one-line description of the modelled entity>" \
    --shape <shape>

--shape is the important part: pick the domain_entity shape that matches the entity being commissioned — simple-text-key (country-like), fk-scoped (book-like), hierarchical-composite (party/counterparty-like), richest (currency-like, every optional knob exercised), or timeseries (market_observation-like, no presentation drawer). Each pre-populates the scaffold's Flags/SQL/Presentation sections with that shape's knobs already set correctly, sampled from a real, proven-correct reference entity — omit --shape only to get the old bare, unknobbed skeleton. Individual --entity-<knob> flags (e.g. --entity-has-tenant-id false) override any preset value. Anything a shape can't derive (actual columns, foreign-key targets, display fields, …) is left as a visible, one-line TODO in the output — never silently omitted. See Codegen org-entity meta-model for the full knob catalogue behind each shape.

The scaffold writes projects/ores.<component>/modeling/ores.<component>.<entity>.org with the required frontmatter (#+type: ores.codegen.entity and friends) and the shape's knobbed sections (Flags, Primary key, Natural keys, Columns, SQL, optionally Foreign keys=/=Insert trigger, C++, optionally Presentation).

2.2. Fill in the model

Open the scaffolded file and resolve every TODO — the shape sets knobs, not entity-specific facts like actual columns, foreign-key targets, or display fields. The meta-model documents every required property and the well-known block kinds (e.g. additional repository declarations / implementations / includes) that custom methods attach to via :implements <kind-UUID>. Once the model compiles and generates cleanly, work through the post-generation checklist — the fixed manual integration steps (registrar wiring, NATS event-relay wiring, …) codegen does not and will not wire up for you.

2.3. Wire the component (first entity in a component only)

If projects/ores.<component>/modeling/ is the first org model in this component, add a modeling_dir entry to the component's COMPONENTS record in projects/ores.codegen/src/codegen/manifest.py:

"<component>-cpp": Component(
    name="<component>-cpp",
    modeling_dir="projects/ores.<component>/modeling",
),

Existing components that already have modeling_dir set need no change to add another entity.

2.4. Regenerate

./compass.sh codegen regenerate \
    --component <component>-cpp --address ores.cpp

Codegen discovers models through modeling_dir alone: org files whose #+type: says ores.codegen.entity.

3. Script

projects/ores.compass/compass.sh add entity_org — the scaffold itself. The codegen template is projects/ores.codegen/library/templates/doc_entity_org.org.mustache.

4. Tested by

The pilot entity ores.refdata.party under projects/ores.refdata/modeling/. Regenerating refdata picks it up via the modeling_dir entry and produces byte-identical output to the legacy party_entity.org location.

5. Two things a new entity needs that the build does not enforce

Adding an entity to an existing component passes compass build, the model drift check and every test, and can still miss the database. The schema parser reports both of the problems below, and CI runs it with --strict, so either one fails the pull request. Run it yourself first to see them before CI does:

python3 projects/ores.codegen/src/plantuml_er_parse_sql.py \
  --create-dir projects/ores.sql/create --drop-dir projects/ores.sql/drop \
  --output /tmp/er_model.json \
  --ignore-file projects/ores.sql/utility/validation_ignore.txt --warn

On a clean tree it prints Warnings: 0. A deliberate exception goes in projects/ores.sql/utility/validation_ignore.txt with its reason.

1. List the table in the component's create and drop aggregates (WIRE_001).

projects/ores.sql/create/<component>/<component>_create.sql is hand-written in every component, not generated. It lists \ir ./... lines in dependency order, with prose for each block. A new table is absent from a recreate until you add it, and the recreate still reports success. Do the same in projects/ores.sql/drop/<component>/drop_<component>.sql, children first, so nothing is dropped while a reference holds.

2. Set :rls_tenant_isolation: true on a tenant-scoped entity (RLS_001).

Without the flag the table has a tenant_id and no row-level security policy, so every tenant can read every other tenant's rows. Set it under * SQL / ** Flags, next to :tablename:.

6. See also

Emacs 29.3 (Org mode 9.6.15)