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:

  1. (B2) get_model_type() and load_model() are hard-coupled to filename suffixes — switch to #+type: frontmatter detection.
  2. (B3) Entirely separate parsers — teach load_org_model() to parse * Validation function, * Insert trigger, has_tenant_id, coding_scheme, image_id.
  3. (B1) Two incompatible SQL templates — extend sql_schema_domain_entity_create.mustache to handle the full table-template data shape.
  4. (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 --address declares 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 --profile from the CLI. Subsumes the originally planned #+sql_only and #+has_qt flags.
  5. (B4) refdata vs refdata-cpp component 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 sql no longer overwrites table-pathway SQL output with the structurally incomplete domain-entity SQL.
  • get_model_type() and load_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_id from entity org files that carry them.
  • sql_schema_domain_entity_create.mustache generates byte-identical SQL to sql_schema_create.mustache for 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}]].enabled properties, 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 --address argument 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.
  • --profile is 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 refdata and refdata-cpp component catalogue entries are merged; all profiles fire from a single component with one discovery glob.
  • compass codegen entity archetypes lists the full physical space in ores.ts.facet.archetype notation; --entity <name> shows the entity's supported set with per-archetype ✅/❌ status.
  • All existing regression tests pass; compass codegen entity produces 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}]].enabled in 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: true followed by :ores.cpp.qt.enabled: false enables 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 --address declares the target set; what generates is their intersection. --profile is 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 --profile invocations is part of B5's scope.

Out of scope

  • Content migration (Step 5 of the migration path): moving _table.org sections into entity files and deleting the table files. That is a follow-on story.
  • Retiring the ores.codegen.table type from get_model_type() (Step 7): deferred until all table files are gone.

Emacs 29.1 (Org mode 9.6.6)