Task: Scaffold model .org files per entity type (regular/enum/junction)

Table of Contents

This page documents a task in the Commission ores.qt.dq — full-stack codegen for the DQ component story. It captures the goal, current status, acceptance, and any notes or results.

Goal

Generate correct, fully-knobbed entity .org model skeletons per entity metatype (domain_entity/junction/lookup_entity — the MASD-established classification, see Plan), with domain_entity further split into named shapes by knob combination, by sampling correctly-modeled reference entities, so new DQ (and future) entity models start from a known-good baseline instead of copy-paste. Also document the fixed set of manual post-generation integration steps (registrar wiring, NATS relay wiring, CMake/plugin wiring, …) so the scaffold — or the LLM using it — knows what still needs doing by hand, with a pointer to the capture/task ID tracking each step not yet automated.

Status

Field Value
State DONE
Parent story Commission ores.qt.dq — full-stack codegen for the DQ component
Now Nothing.
Waiting on Nothing.
Next Nothing.
Last touched 2026-07-17

Acceptance

  • [X] org_entity_meta_model.org documents every variability knob actually in use (has_change_reason_cache, history_response_data_field, delete_request_id_field, has_uuid_primary_key, badge/coding_scheme wiring, etc.) – today's doc omits all of them.
  • [X] A codegen command/template exists per entity metatype/shape that scaffolds a new model .org with the correct knobs already present – either set from the sampled exemplar or left as an explicit TODO with a one-line reason – so a knob can never be silently omitted.
  • [X] A "Post-generation checklist" is authored (in the scaffolded file or a companion doc, decide during implementation) enumerating every fixed manual integration step (registrar sub-registration, application.cpp NATS-relay wiring, CMakeLists entry, Qt plugin/menu wiring, manual chapter), each linking to a capture/task ID if the step is separately tracked for automation.
  • [ ] Used to author the DQ reference-entity models in the sibling commissioning task (819B050A) – that task's own job, not blocking this one's completion.

Plan

Not "stereotype" (UML mechanism) and not "profile" – metatype: the MASD term for an entity's classification, per Applied MASD § Metatypes, facets, archetypes, and transforms (domain_entity, junction, enumeration, … – "do not use 'stereotype' to mean 'metatype'"). Codegen's #+type: frontmatter already dispatches on exactly this: ores.codegen.entity (metatype domain_entity), ores.codegen.junction (metatype junction), ores.codegen.lookup_entity (maps to metatype enumeration? naming mismatch to confirm during implementation – the established metatype table in Applied MASD says enumeration, codegen's doc-type says lookup_entity; reconcile or document why they differ).

market_observation is #+type: ores.codegen.entity today (metatype domain_entity) despite being structurally quite different (TimescaleDB hypertable, immutable observation semantics, no Qt) – so per the current, established metatype list it is not a distinct metatype, it is a domain_entity with :ores.cpp.qt.enabled: false and a different natural-key/temporal shape. The four "regular" rows below are the same situation: same metatype (domain_entity), different knob combinations. Calling all five "metatypes" would be exactly the kind of stretching Applied MASD warns against, so this task uses shape for that finer, sub-metatype grouping – a shape is "which optional domain_entity knobs this kind of entity needs", not a new classification codegen dispatches on. Whether timeseries entities are common/different enough to eventually warrant becoming their own metatype (a real #+type: ores.codegen.timeseries, dispatching to a different archetype set) is a legitimate future question, out of scope for this task – flag it rather than deciding it here.

Candidate metatypes/shapes and their sampled exemplars (to confirm/revise once we start, these are reconnaissance picks, not final):

Metatype Shape Exemplar(s) Why
domain_entity simple text-key ores.refdata.country.org text natural key, has_uuid_primary_key: false, no hierarchy
domain_entity FK-scoped ores.refdata.book.org party_id soft-FK scoping, moderate size
domain_entity hierarchical composite ores.refdata.party.org, ores.refdata.counterparty.org has_parent_id, has_uuid_primary_key: true, composite child-entity tables
domain_entity richest/all-knobs ores.refdata.currency.org every optional knob exercised at least once, custom generator/service code
domain_entity timeseries/hypertable ores.marketdata.market_observation.org TimescaleDB hypertable, (series, observation_datetime, point_id) triple, ores.cpp.qt.enabled: false today (see Notes on future UI/chart work), transaction-time vs financial valid-time distinction
lookup_entity (none needed yet) ores.iam.tenant_type_lookup_entity.org (candidate) small closed code set, minimal knobs
junction (none needed yet) ores.refdata.party_counterparty_junction.org (candidate) two-sided FK association

Existing doc-type split (ores.codegen.entity=/=lookup_entity=/ =junction) already gives us the metatype boundary; extend within ores.codegen.entity for the domain_entity shapes rather than adding new doc types.

Notes

Promoted from a backlog capture (same :ID:, preserved for traceability).

Today every entity's model .org file (e.g. projects/ores.refdata/modeling/ores.refdata.<entity>.org) is hand-authored from scratch or copy-pasted from a similar entity, so variability knobs (has_change_reason_cache, history_response_data_field, delete_request_id_field, has_uuid_primary_key, badge wiring for enum-like lookups, junction wiring for many-to-many association entities, etc.) get set inconsistently or omitted entirely depending on which entity someone copied from. In practice there are really only a handful of entity kinds driving these knobs: a plain domain entity, an enum/lookup entity (small closed set of codes, usually badge-rendered in list views), and a junction/association entity (links two entities, often read-only or list-only). Add a mustache template per kind under projects/ores.codegen/library/templates/ that scaffolds a new model .org with the correct knobs for that kind already present and set to sensible defaults – derived by sampling known-good reference entities (e.g. ores.refdata.book.org, ores.refdata.currency.org) that are already proven to fill in every knob correctly, rather than generic per-kind defaults alone.

This traces back to task AA97E310 (PRs #1571/#1590), which found 8 *_convention entities missing has_change_reason_cache: true despite their schema requiring change_reason_code (saves silently failed with an empty change-reason dropdown), and the same 8 missing history_response_data_field=/=delete_request_id_field overrides because their protocol field names don't follow the codegen defaults. All three gaps trace back to the model .org file being authored without a checklist for "what does an entity of this kind need". A kind-aware scaffold sampled from correct entities would make these knobs impossible to silently omit.

See also: Story: Migrate all entities onto the generic HistoryDialog.

Test Scenarios

Manual QA scenarios (scaffolded via compass add test_scenario, run through the QA Validation Runner panel) that verify this task. Link new ones here as they're created; the scenario doc itself links back via its "Verifies task" field.

Scenario State Notes
     

PRs

PR Title
   

Review

# Comment summary File Decision Notes
1 claude-review: 4 of 5 –shape presets' knob bundles didn't match their stated exemplar .org files (richest missing has_tenant_id; simple-text-key had a false has_export_macro and was missing has_insert_trigger_validations/qt_has_change_reason_cache/qt_has_pagination; hierarchical-composite had a false has_foreign_keys/qt_has_export_macro and was missing has_insert_trigger_validations; timeseries had a false has_foreign_keys) projects/ores.codegen/src/doc_generate.py Accepted Verified each finding directly against country.org/currency.org/party.org/counterparty.org/market_observation.org; fixed all four presets to match.

Result

The documentation half of this task (org_entity_meta_model.org and its segment pages documenting every variability knob in use) shipped in PR #1620. This PR ships the remaining two acceptance criteria:

  • compass add entity_org --shape <name> (simple-text-key, fk-scoped, hierarchical-composite, richest, timeseries) scaffolds a knobbed entity model instead of the previous bare, unknobbed skeleton. The template itself (doc_entity_org.org.mustache, tangled from ores.doc.modeling.entity_org.org) carries the non-structural variability as mustache section-conditionals gated by the shape's knob bundle (ENTITY_ORG_SHAPE_PRESETS in doc_generate.py) – one template covers every shape, no per-shape file copying. Individual --entity-<knob> flags override any preset value. Anything a shape can't derive (actual columns, FK targets, display fields, …) is left as a visible, one-line TODO in the scaffolded output, never silently omitted.
  • Codegen entity — post-generation checklist enumerates the fixed manual integration steps (sub-registrar wiring, event-registrar wiring, CMake wiring, Qt plugin wiring), each explaining why it isn't generated yet and linking the capture/task tracking automation where one exists. Linked from the entity hub and from C++ NATS, replacing a dangling "see the hub's frontmatter" reference that pointed nowhere.

The five shape presets are reconnaissance picks sampled from one exemplar each (per the Plan table above), not a final catalogue – expect to refine them as 819B050A actually uses the scaffold to author the DQ reference entities.

Emacs 29.3 (Org mode 9.6.15)