Feature Interactions
This chapter explains the feature interactions of different features working together.
configurationFileMode - Feature Switch for Users
The mode looks like a statement about file count, and it is also the switch that decides which configuration features exist at all:
| Mode | Several files | Entities merge | Priority $fileType |
Fragments $path |
Mixins $mixins |
|---|---|---|---|---|---|
|
no |
no |
no |
no |
no |
|
yes |
no |
no |
no |
no |
|
yes |
yes |
no |
yes |
yes |
|
yes |
yes |
yes |
yes |
yes |
Consequences:
-
Choosing
SingleFiledenies your users every reuse mechanism Confµgami has on the configuration side.-
It is the right call for a small, single-owner model and the wrong one for anything a platform team and a project team fill in together.
-
-
Choosing a mergeable mode hands your users mixins and fragments
-
Expect entities of the same type to be configured through a class-wide default rather than one by one.
-
-
Only the mergeable modes allow a top level entity with multiplicity
1..1, because presence is then checked on the merged result. In plainMultipleFilesmode every file would have to satisfy it alone, which is why that combination is rejected. -
The
.default.def.json5and.preconfig.def.json5files you ship depend on the same switch, see below.
Defaults You Ship Are Mixins
A .default.def.json5 or .preconfig.def.json5 file is written in configuration syntax and usually holds a $mixins entry without a $mixin name,to each every type:
{
$definition: "SensorLoop@1.0",
$fileType: "DefaultValues",
$mixins: [
{
$type: "SensorLoop::Sensor",
sampleRate: 100
}
]
}
-
The content activates per CfgSpace only once one real
.cfg.json(5)file declares the definition. A workspace where nobody configures your definition sees none of your defaults. -
Its
$fileTypedecides the layer,DefaultValues*below the user andPreConfiguration*above, and the file suffix has to match the type. -
A parameter
defaultin the definition and a default file are different mechanisms. Prefer the definitiondefaultfor a per-parameter fallback and a default file for a coherent block of values a project is expected to override.
Reuse, Visibility and Generated Names
typeRef, inherits and scope solve different problems and constrain each other:
-
typeRefembeds a shape,inheritsextends an entity type. Structs cannot inherit, so shared struct fields move into a named struct type embedded bytypeRef. -
Both need the target to be visible: declared in the same definition, or
scope: "public". Making a type public is an API decision, not a convenience: another definition may then inherit from it, reference it, or use it as a union member, and renaming it breaks those definitions. -
A list property must declare an explicit
typeNamewith the singular name, and that name is what every type path, union member andrefTargetTypesentry spells. Renaming it is a breaking change for the configurations that write$typeand for the generated code alike. -
Simple type names have to stay unique across what is generated, so a definition
Indicatorwith a root relationindicatorproduces the clashing pairIndicatorandIndicator::Indicatorand aborts code generation. Name the definition and its root relation differently, or settypeName.
Unions Reach Into Deserialization
The union options look like validation settings and are really code generation settings:
-
A discriminated union (
allowTypeAmbiguity: false, the default) requires$typein the configuration, generates it as a constant per member, and deserializes unambiguously in every consumer. It accepts entity and struct members only, since a scalar or arefcannot carry the discriminator. -
allowTypeAmbiguity: trueis what allows scalar andrefmembers, and the price untagged deserialization. -
Use an
enuminstead when the variants carry no payload of their own. It configures more simply and generates a plain enum type.
Constraints Are Enforced, Hints Are Not
A definition mixes keys that change validation with keys that are documentation only:
| Key | Effect |
|---|---|
|
Enforced by the validator and, for the numeric ones. |
|
Completion only, other values stay valid. |
|
Hover, inlay hints and doc comments.
|
|
Documentation only, and rejected on an |
pattern deserves its own warning because it is unanchored unless you write ^…$.
See the caution in the type reference before using anything beyond character classes and quantifiers.
Annotations Cross Every Boundary
An annotation type is data about the model, and its declaration decides how far that data travels:
-
targetsrestricts the element kind and the side, theDefsuffix for definition elements andCfgfor configuration elements. An annotation meant for your users needs a…Cfgtarget, one meant for your own definition a…Deftarget, andAllcovers both. -
retention: "Source"keeps the annotation out of every merged JSON output, so downstream generators never see it.Output, the default, is what a tool consumingcfu cfg export-mergedrelies on. -
typeSymbolRefonly exists inside an annotation type declaration, and it makes an annotation field a checked type path instead of a free string, which is how_deprecatedvalidates itsreplacedBy. -
_deprecatedannotation has semantics: it produces a diagnostic of the declared level at every configuration element using the deprecated definition element, and it reaches generated code as#[deprecated]or@deprecatedand the schema as"deprecated": true. AdeprecationLevel: "error"therefore breaks every existing configuration on the spot, which is the point. -
Custom annotations deliberately cannot change the validation of the element they sit on.
Generation Follows the Configuration File Mode
cfu def generate emits a different set of schemas depending on the file mode: for a mergeable definition the strict <Name>.merged.schema.json validates the fully merged
configuration, while <Name>.schema.json treats every field as optional so that a single partial file still validates.
An editor pointed at the wrong one of the two reports missing required properties on every contributing file.
See Code Generation for the rest, including the cases where generated code is deliberately less strict than the validator.
-
How the Features Work Together in the user manual describes the same interactions from the configuration author’s side.