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]+$"
}
}
}
}
]
-
typeNamenames the type. The usable key is derived from it by lowercasing the first letter and prefixing_: typeRequirementbecomes key_requirement. -
targetsrestricts where the annotation may appear.Allallows every element; specific targets likeEntityDef,ParameterCfg, orDocumentCfgnarrow it to definition or configuration elements of a kind (theDef/Cfgsuffix selects the side). -
annotationdeclares the value structure with the normal element syntax. Only parameters and structs are allowed inside; entities, unions, and references are not. -
retentiondeclares how far the annotation travels,Output(the default) orSource, see Retention.
Retention
retention states whether an annotation is meant to leave the source files:
| Value | Meaning |
|---|---|
|
The annotation is part of the sources and outputs generated from them. |
|
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 |
|---|---|---|
|
all definition and configuration elements |
Records the file and the position an element was read from. |
|
all definition elements |
Marks the element as deprecated, so every configuration using it reports a diagnostic. |
|
entities and structs |
Per-language options for the code generators. |
|
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 |
|---|---|---|
|
yes |
Short text why the element is deprecated. It is shown in the diagnostic, so keep it to one sentence. |
|
yes |
|
|
no |
|
|
no |
Type path of the element that supersedes this one. |
|
no |
Longer migration instructions for the configuration author.
Markdown, indent-normalized like a |
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-mergedpreserves theOutputones, 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.