LinkML 1.10’s codegen pipeline deliberately separates annotation-bearing surfaces from data-validation surfaces. Treat this as a feature, not a bug.

Why: S3 spike 2026-05-02 verified that gen-typescript and gen-json-schema drop class-level + attribute-level annotations: (and extensions:) blocks under all flag combinations tried (--metadata, --no-metadata, --include-induced-slots, extensions:-instead-of-annotations:). Only description: (a first-class JSON Schema / JSDoc field) survives. By contrast, gen-pydantic preserves annotations via linkml_meta: ClassVar[LinkMLMeta] (class-level) and Field.json_schema_extra['linkml_meta']['annotations'] (attribute-level) when invoked with --meta full (or default --meta auto). This is not a defect — it’s LinkML’s deliberate design choice: TS interfaces + JSON Schema are runtime data-validation surfaces, the data shape; YAML + Pydantic are metadata + tooling surfaces, the schema metadata.

How to apply:

  • For runtime metadata access in Python: invoke ~/tools/inherit-spike-env/bin/gen-pydantic --meta full schema.yaml and read annotations via MyClass.linkml_meta.root['annotations'] (class-level) + MyClass.model_fields[<slot>].json_schema_extra['linkml_meta']['annotations'] (attribute-level).
  • For build-time scope-binding / metadata-driven emission (e.g. Catala scopes filtered by inherit:phase_activation_status): read the LinkML YAML directly via yaml.safe_load. The YAML is canonical; everything downstream is derived. Don’t rely on gen-json-schema output for metadata.
  • For browser-side / wire-format metadata access (e.g. TS clients needing OntoUML stereotype info at runtime): emit a sidecar *.metadata.json via a ~10-line Python build script (parse YAML; project annotations into a flat metadata map). Or wait for LinkML 1.11.x/2.0 if it adds custom-annotation propagation to non-Python codegens.
  • Don’t treat TS / JSON-Schema annotation-drop as a “broken pipeline” → don’t try to fix it via custom Jinja templates or LinkML-version chasing. The architectural answer is YAML-as-canonical + Pydantic-runtime + sidecar-JSON-for-browser.
  • annotations: block vs extensions: block: identical generator support (both drop in TS+JSON-Schema; both preserve in YAML+Pydantic). Use annotations: (the LinkML community’s de-facto pattern for domain-specific metadata). extensions: is for typed extensions to LinkML schema language itself; not what you want for OntoUML/inherit:phase metadata.

Tooling pin (verified 2026-05-02 at S3 spike):

  • LinkML 1.10.0 → --meta full flag (NOT --include-annotations=True — that flag does not exist; older docs/plans had wrong flag name)
  • Catala 1.1.0 → typecheck --no-stdlib for spike-level scope validation; production builds use clerk start for full stdlib
  • node 24.14.1 --experimental-strip-types --check for TS syntactic validation without tsc

Source: T-spike-eps-iota-S3-ontouml-linkml-2026-05-02.md §3 (working configuration); arch-state v3.19 §11 S3 row + Changelog v3.19 row; plan v1.4 §1.8 + §2 Task 3 Step 3 + Step 7. PensionAsset (richard-task #213 deferred Year-2+) used as canonical phase-stereotype pilot class.

Related memories:

  • feedback_yaml_as_canonical_metadata_carrier — derived architectural pattern for INHERIT v2
  • feedback_kill_condition_strict_vs_spirit_reading_via_outcome_MITIGATED — methodological framing of why TS+JSON-Schema annotation-drop is outcome-MITIGATED not outcome-KILL-CONDITION-MET
  • feedback_surface_alternatives_before_collapsing_synthesis_to_baseline — informed the 5-alternative search at S3 (—meta/—no-metadata/—include-induced-slots/extensions/—no-stdlib) before settling on architecturally-correct YAML-as-canonical