# core/dsl/model — directory guide

The model half of the projection machinery, symmetric with
`core/dsl/operation/oas/`. Files:

- `toModelProjectionBase.ts` — factory. Takes a
  `base: LangSnippetConstructor` (a language package's snippet base,
  pre-bound by the lang veneer — e.g. `toModelProjectionBase` from
  `@skmtc/lang-typescript`) and builds a class on it carrying the
  projection machinery (`settings`, `refName`, `generatorKey`
  injection, `insertModel` / `insertNormalizedModel` with optional
  `variant`) plus the statics `toIdentifier` / `toExportPath` /
  `toEnrichments` / `isSupported` / `id` / `type` / the inherited
  `lang`. Defines NO register — that's the veneer's. The constructor
  reads `args.settings.variant` and threads it into the
  `toModelGeneratorKey` call so the resulting Definition's
  `generatorKey` carries the variant. Enrichment lookup walks
  `enrichments.${id}.${refName}.${variant}`.
- `toModelEntry.ts` — pipeline-side factory; pure pipeline config (no
  `lang` — the import graph declares the language). `transform`
  (`({ context, refName, variant }) => void` — no accumulator),
  `toPreviewModule`, and `toMappingModule` all receive `variant`.
  Optional `isSupported` (symmetric with the operation entries):
  wrapped to pre-parse the `{ subject, generator, stack }` enrichment
  umbrella, then exposed on the built config (defaulted to
  `() => true`). The predicate gets `{ context, refName, enrichments,
  variant }` — no schema; resolve it via `context.resolveSchemaRefOnce`
  when the gate needs it. The engine evaluates it per (refName, variant)
  before `include`/`skip` and records `notSupported` on a `false`.
- `ModelDriver.ts` — the insertion lifecycle (compose
  `ContentSettings`, look up via `findDefinition`, integrity-check
  via `affirmDefinition`, register the Definition, register the
  import). Variant-related entry points:
  - Constructor takes a `variant: string` arg and stores it on the
    instance.
  - `assertPeerVariantExists` runs before `toModelContentSettings`
    — throws if the caller asked for a non-`'main'` variant the peer
    doesn't declare.
  - `assertPeerSupported` runs next — probes the peer projection's
    static `isSupported({ refName, context })` and throws if it
    returns `false` (the model counterpart of the OAS-operation
    peer-capability guard; a peer with no static is treated as
    supporting everything).
  - `affirmDefinition` builds the call's `generatorKey` with variant
    in the 3rd segment; the cache-key uniqueness invariant is
    enforced here.
- `types.ts` — arg shapes. Every `To*Args` and `Transform*Args`
  carries `variant: string`.

**Variants-aware vs unaware (mirrors the OAS-operation patterns):**

- *Unaware*: destructure `variant` in `toExportPath` (since the body
  calls `this.toIdentifier({…, variant})`), ignore it in
  `toIdentifier`. Engine still dispatches with `variant: 'main'` for
  every refName.
- *Aware*: `toIdentifier` folds variant into the name via
  `withVariant(base, variant)` from `@/helpers/withVariant.ts`.
  `toExportPath` inherits the suffix through the
  `this.toIdentifier({…, variant})` call.

**Cross-generator insertion**: callers compose with the peer's
`'main'` Definition by default — `context.insertModel(Peer, refName)`
defaults `variant` to `'main'`. Pass `{ variant }` explicitly only
when the peer is known to be variants-aware AND declares that
variant in its enrichment block.

Tests pinning model-variant invariants:
- `core/context/GenerateContext.model-variants.test.ts` — engine
  fan-out, missing-`main` throw, per-variant skip/include matching,
  and the `isSupported` capability gate (per-refName gating;
  `isSupported` runs before `include`).
- `ModelDriver.variants.test.ts` — Driver-level peer-variant guard
  and the `generatorKey` collision check for variants-aware model
  Projections.
- `ModelDriver.test.ts` → "Peer support validation" — Driver probes
  the peer's `isSupported` on `insertModel`.

Concept doc: `docs/concepts/variants.md`. Skill:
`docs/skills/skmtc-generator/SKILL.md`.

---

<claude-mem-context>
# Recent Activity

<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->

### May 11, 2026

| ID | Time | T | Title | Read |
|----|------|---|-------|------|
| #20455 | 7:03 PM | 🔄 | ModelDriver simplified using Identifier.toImport instead of manual import construction | ~577 |
| #20421 | 6:59 PM | 🔵 | EntityType already exists on Identifier and drives isType boolean conversion | ~569 |
| #20419 | " | 🔵 | isType flag implementation across core DSL import system | ~592 |
| #20418 | " | 🔵 | Current isType boolean implementation in Import DSL | ~522 |

### May 12, 2026

| ID | Time | T | Title | Read |
|----|------|---|-------|------|
| #20928 | 2:20 PM | 🔵 | Critical enrichment routing path discrepancy discovered in documentation | ~940 |

### May 13, 2026

| ID | Time | T | Title | Read |
|----|------|---|-------|------|
| #21201 | 11:04 AM | ✅ | Created comprehensive API reference documentation for entry factory functions | ~544 |
| #21156 | 8:10 AM | 🔵 | toPreviewModule confirmed active in entry factory APIs | ~488 |
</claude-mem-context>