Story: Resolve codegen model unification blockers
Table of Contents
This page documents a story in Sprint 21. It captures the goal, current status, acceptance criteria, and the tasks that compose it.
Goal
The codegen model unification analysis identifies six concrete blockers that prevent
merging the dual domain_entity + table org files into a single entity file per entity.
This story resolves five of them (B6 is abandoned — subsumed by B5's generic mechanism),
leaving the generator in a state where the content migration (moving _table.org sections
into entity files and deleting the table files) can proceed safely and mechanically.
The blockers, in implementation order:
- (B2)
get_model_type()andload_model()are hard-coupled to filename suffixes — switch to#+type:frontmatter detection. - (B3) Entirely separate parsers — teach
load_org_model()to parse* Validation function,* Insert trigger,has_tenant_id,coding_scheme,image_id. - (B1) Two incompatible SQL templates — extend
sql_schema_domain_entity_create.mustacheto handle the full table-template data shape. - (B5) No element-level physical-space binding; profile concept leaked to CLI — introduce the
supported/target set model: the entity's
ores.*drawer declares its supported set (what it is capable of generating); the optional CLI--addressdeclares the target set (what to generate this run; defaults to the full supported set). What actually generates = target ∩ supported. Target outside supported → warning for that entity. Remove--profilefrom the CLI. Subsumes the originally planned#+sql_onlyand#+has_qtflags. - (B4)
refdatavsrefdata-cppcomponent split — collapse into a single component.
Status
| Field | Value |
|---|---|
| State | STARTED |
| Parent sprint | Sprint 21 |
| Now | Scaffolding tasks. |
| Waiting on | Nothing. |
| Next | Implement blockers in order B2→B3→B1→B5→B4. |
| Last touched | 2026-06-27 |
Acceptance
- Running
compass codegen entity --component refdata-cpp --profile sqlno longer overwrites table-pathway SQL output with the structurally incomplete domain-entity SQL. get_model_type()andload_model()resolve model type from#+type:frontmatter; the filename-suffix fallback remains for any model without a#+type:header.load_org_model()correctly parses* Validation function,* Insert trigger,#+has_tenant_id,#+coding_scheme, and#+image_idfrom entity org files that carry them.sql_schema_domain_entity_create.mustachegenerates byte-identical SQL tosql_schema_create.mustachefor any dual-file entity when given the merged entity model.- Each entity's
:PROPERTIES:drawer declares its supported set: the archetypes it can produce.ores.{ts}[.{facet}[.{archetype}]].enabledproperties, resolved with specificity-ordered override semantics (more-specific wins), produce the supported set. Absent properties leave the model-types filter as the sole gate (backward-compatible). - The CLI accepts an optional
--addressargument that declares the target set; absent, the target set equals the full supported set. What generates = target ∩ supported. Target ∩ supported = ∅ for an entity → warning "nothing to generate for<entity>"; the run still succeeds for other entities in the component. --profileis removed from the CLI. The entity's binding IS the profile.- A SQL-only entity is expressed as
:ores.cpp.enabled: false; an entity without Qt as:ores.cpp.qt.enabled: false. No ad-hoc boolean flags required. - The supported/target set resolution logic is isolated in a dedicated Python module with unit tests covering: empty properties (backward compat), TS-level disable, facet-level disable, combined TS-enable + facet-disable override, target ⊆ supported (normal filter), target ∩ supported = ∅ (warn), unknown address (error).
- The
refdataandrefdata-cppcomponent catalogue entries are merged; all profiles fire from a single component with one discovery glob. compass codegen entity archetypeslists the full physical space inores.ts.facet.archetypenotation;--entity <name>shows the entity's supported set with per-archetype ✅/❌ status.- All existing regression tests pass;
compass codegen entityproduces zero diff vs HEAD for all currently registered components.
Tasks
| Task | State | Start | End | Description |
|---|---|---|---|---|
| Scaffold story: Resolve codegen model unification blockers | DONE | 2026-06-27 | 2026-06-27 | Story scaffolding rides this task: documents, sprint wiring, and the scaffold PR. Close it before merging that PR. |
| B2: Switch model-type detection from filename suffix to #+type: frontmatter | DONE | 2026-06-27 | 2026-06-27 | Change get_model_type() and load_model() in core.py to resolve the model type from the #+type: frontmatter key rather than filename suffix; keep suffix fallback for files without #+type:. |
| B3: Teach load_org_model() to parse table-only sections | DONE | 2026-06-27 | 2026-06-27 | Extend load_org_model() in org_loader.py to parse * Validation function, * Insert trigger / ** Validations, and the #+has_tenant_id, #+coding_scheme, #+image_id frontmatter keys that currently only load_org_table_model() handles. |
| B1: Extend entity SQL template to cover full table-template data shape | STARTED | 2026-06-27 | Extend sql_schema_domain_entity_create.mustache to render coding_scheme columns, image_id, the validation_fn block, and insert_trigger.validations — the sections that previously only sql_schema_create.mustache (table pathway) produced — so a merged entity model generates byte-identical SQL to the table pathway. | |
| B5: Implement element-level physical space binding via ores.* property drawer | BACKLOG | Implement ores.{ts}[.{facet}].enabled property-drawer activation with hierarchical override semantics. The generator reads these from each entity's :PROPERTIES: drawer, resolves specificity (more-specific address wins), and skips matching facets. Replaces the ad-hoc #+sql_only and #+has_qt flags. Resolution logic isolated and unit-tested. | ||
| B6: Make C++/Qt section optional with #+has_qt: false guard | ABANDONED | Superseded by B5: the generic ores.* property-drawer mechanism covers this use case via :ores.cpp.qt.enabled: false. | ||
| B4: Collapse refdata and refdata-cpp into a single component | BACKLOG | Merge the refdata and refdata-cpp entries in the component catalogue into one component with a single discovery glob over ores.refdata.*.org. Remove the dead models_dir entries from both. Update all compass codegen entity commands that reference either component. | ||
| B6: Add compass codegen entity archetypes subcommand | BACKLOG | New 'archetypes' subcommand lists the full physical space catalogue in ores.ts.facet.archetype notation. With –entity shows the entity's supported set with per-archetype enabled/disabled status. Makes the address system self-documenting. Depends on B5. | ||
| B7: Remove split-model backward-compatibility once all models migrated | BACKLOG | Retire the dual-pathway scaffolding (table + entity templates, inert defensive defaults) added by B1-B3 for coexistence, once every model is migrated to the unified domain_entity format and verified byte-identical. |
Decisions
- Implement in order B2 → B3 → B1 → B5 → B4: frontmatter detection must come first (B3, B1 depend on it); parsers before templates (template needs the richer data shape); physical-space binding before component collapse (collapse relies on binding to handle SQL-only entities); component collapse last.
- The five active tasks share one feature branch (
feature/codegen-unification-blockers) and one PR, since each is a prerequisite for the next and none produces a shippable intermediate state. B6 (#+has_qt) is ABANDONED — superseded by B5. - Originally scoped B5 and B6 as separate boolean flags (
#+sql_only,#+has_qt). Replaced with a single MASD-aligned mechanism:ores.{ts}[.{facet}[.{archetype}]].enabledin the entity's:PROPERTIES:drawer, following Dogen's variability model (cf.:masd.cpp.enabled:,:masd.csharp.enabled:) and ORE Studio Variability Model. Resolution is specificity-ordered::ores.cpp.enabled: truefollowed by:ores.cpp.qt.enabled: falseenables all C++ except Qt. Any future per-element activation need is handled by the same mechanism — no new flags. - Physical space is a "set of sets" (TS → facet → archetype). The entity declares its
supported set; the CLI
--addressdeclares the target set; what generates is their intersection.--profileis removed: the entity's binding IS the profile, and the CLI address is only a filter, never an expansion. Updating all existing compass commands and skills to remove--profileinvocations is part of B5's scope.
Out of scope
- Content migration (Step 5 of the migration path): moving
_table.orgsections into entity files and deleting the table files. That is a follow-on story. - Retiring the
ores.codegen.tabletype fromget_model_type()(Step 7): deferred until all table files are gone.