Annotations

Annotations attach custom, typed metadata to definition and configuration elements: requirement traces, safety classifications, tool specific hints. The definition declares the annotation types; both definition and configuration authors can then use them, and the values are validated like any other data.

Declaring an Annotation Type

Annotation types live in the top level annotationTypes array. The PumpControl example declares a requirement trace:

annotationTypes: [
    {
        typeName: "Requirement",
        title: "Requirement trace",
        description: "Links an element to the safety requirements it implements.",
        targets: [
            "All"
        ],
        retention: "Output",
        annotation: {
            ids: {
                typeName: "Id",
                multiplicity: "1..*",
                description: "Requirement identifiers.",
                string: {
                    pattern: "^REQ-[0-9]+$"
                }
            }
        }
    }
]
  • typeName names the type. The usable key is derived from it by lowercasing the first letter and prefixing _: type Requirement becomes key _requirement.

  • targets restricts where the annotation may appear. All allows every element; specific targets like EntityDef, ParameterCfg, or DocumentCfg narrow it to definition or configuration elements of a kind (the Def/Cfg suffix selects the side).

  • annotation declares the value structure with the normal element syntax. Only parameters and structs are allowed inside; entities, unions, and references are not.

  • retention declares how far the annotation travels, Output (the default) or Source, see Retention.

Retention

retention states whether an annotation is meant to leave the source files:

Value Meaning

Output

The annotation is part of the sources and outputs generated from them.

Source

The annotation only exists in the definition and configuration files. Use it for authoring metadata that downstream consumers will not see them.

cfu cfg export-merged and every other merged JSON output drop the Source annotations, so only the Output ones reach a downstream consumer. Retention changes nothing about validation: an annotation is checked the same way under either value.

Using an Annotation

Any element within the declared targets can carry the annotation as a _-prefixed key. In the definition:

maxCurrent: {
    description: "Overcurrent shutoff threshold of the pump driver.",
    multiplicity: "1..1",
    u16: {
        min: 100,
        max: 20000,
        unit: "mA"
    },
    _requirement: {
        ids: [
            "REQ-4711",
            "REQ-4712"
        ]
    }
},

And equally in a configuration file:

{
    $definition: "PumpControl@1.0",
    pump: {
        name: "MainPump",
        maxCurrent: 8500,
        _requirement: {
            ids: [
                "REQ-4711"
            ]
        }
    }
}

Annotation values are validated against the annotation type: the ids list above must contain at least one entry matching the REQ-…​ pattern.

If the annotation type has exactly one required scalar child, the value can be written in shorthand, directly as that scalar (_requirement: "REQ-4711" style) instead of the object form.

Confµgami Built-in Annotations

Four annotation types come with Confµgami itself and exist in every workspace, without any definition declaring them. Their names are reserved.

Key Targets Purpose

_origin

all definition and configuration elements

Records the file and the position an element was read from.

_deprecated

all definition elements

Marks the element as deprecated, so every configuration using it reports a diagnostic.

_codeGen

entities and structs

Per-language options for the code generators.

_acceptConstraintViolation

all configuration elements

Accepts the violations of constraints on the element, see Accepting a Violation.

Unlike a declared annotation type, these change behavior: they are read by Confµgami itself instead of only traveling with the data.

Deprecating Elements

Put _deprecated on a definition element to mark it as deprecated:

overcurrentLimit: {
    description: "Overcurrent shutoff threshold in mA.",
    u16: {
        min: 100,
        max: 20000,
        unit: "mA"
    },
    _deprecated: {
        reason: "The threshold moved to 'maxCurrent', which validates the range.",
        deprecationLevel: "warning",
        forRemoval: true,
        replacedBy: "PumpControl::Pump::MaxCurrent",
        migrationDescription: "Set `maxCurrent` to the same value and remove `overcurrentLimit`."
    }
}
Key Required Meaning

reason

yes

Short text why the element is deprecated. It is shown in the diagnostic, so keep it to one sentence.

deprecationLevel

yes

warning or error, the severity of that diagnostic.

forRemoval

no

true when the element is planned to disappear in a future version.

replacedBy

no

Type path of the element that supersedes this one.

migrationDescription

no

Longer migration instructions for the configuration author. Markdown, indent-normalized like a description.

Each configuration element written against a deprecated definition element reports a diagnostic of the declared level at its own position, with a related location pointing at the annotation in the definition. So the configuration author sees the deprecation exactly where it has to be fixed, and deprecationLevel: "error" turns the deprecated element into a hard validation failure. An entity inheriting a deprecated entity, directly or further up its inherits chain, counts as deprecated too.

Hovering a deprecated element in a definition or a configuration file shows reason, the migration description, the removal hint, and the replacement above the element’s own description. Generated bindings carry the language’s own marker built from reason: #[deprecated(note = "…​")] in Rust, @deprecated("…​") in Python. The generated JSON Schemas set "deprecated": true on the element, the standard annotation keyword since draft 2019-09. The reason and the migration text are part of the schema description, since JSON Schema has no field for them.

replacedBy is a typeSymbolRef, a type path checked against the definition, so a renamed or removed replacement is reported instead of going stale. Completion offers the types that are visible from the annotated file.

Code Generation Options

_codeGen sits on definition elements steers the generators of cfu def generate. The only option today is the Python module the type is imported from:

_codeGen: { python: { import: "confugami_common.interfaces" } },

Generation then emits a guarded import wherever another definition uses that type, instead of leaving the import to a computed block, see Code Generation.

Recording Origins

_origin records where an element came from. cfu cfg export-merged --with-origin writes it for every element of the merged output, and an author or a foreign tool can write it by hand to record a source outside Confµgami.

Its retention is Output, so it survives into the merged JSON, and its entry format is specified in Origin Annotations.

What Annotations Are Good For

  • Traceability: requirements, test case links, review status.

  • Process metadata: ownership, ASIL classification, change control.

  • Tool integration: downstream generators can read annotations from the merged model (cfu cfg export-merged preserves the Output ones, see Retention

Custom annotations deliberately cannot change validation semantics of the annotated element, they are data about the model, not part of the model. The built-in annotations are the exception.