Schema Versioning

Profile JSON schema versioning policy: additive changes, breaking changes, MIN_SCHEMA_VERSION / MAX_SCHEMA_VERSION, and the codegen compatibility matrix.

Profile Schema Versioning

Each profile file (mig.json, ahb.json, codelists.json) carries a top-level schema_version integer that describes the structure of that file. The codegen in xtask/src/codegen.rs enforces a range — it rejects profiles whose version is below MIN_SCHEMA_VERSION (format too old) or above MAX_SCHEMA_VERSION (format too new for this tool version).

MIN_SCHEMA_VERSION  ≤  schema_version  ≤  MAX_SCHEMA_VERSION

Both constants live in xtask/src/codegen.rs inside discover_profiles().


Versioning policy

Additive-only change (new optional field) — do NOT bump version

New optional fields are added to the Rust struct with #[serde(default)] so existing JSON files without the field continue to parse cleanly under the deny_unknown_fields policy.

Procedure:

  1. Add the new field to the struct with a #[serde(default)] annotation.
  2. Add the field to the JSON Schema with "default": … (see profiles/schemas/).
  3. Optionally populate the field in affected JSON files.
  4. No version bump required; --check remains green without touching every profile.

Example: The archived field (v1 addition).


Structural / breaking change — bump MAX_SCHEMA_VERSION

A breaking change removes or renames an existing field, changes its type, or alters its semantics in a way that requires all profiles to be updated.

Procedure:

  1. Bump MAX_SCHEMA_VERSION in xtask/src/codegen.rs.
  2. Add conditional deserialization in discover_profiles() to handle both the old and new version (or drop support for the old version by raising MIN_SCHEMA_VERSION).
  3. Update every profile JSON file to the new schema_version.
  4. Run cargo xtask codegen and commit the regenerated output.

Error messages

SituationMessage
schema_version < MIN_SCHEMA_VERSION"has schema_version N (minimum is M) — update the profile JSON file to at least schema_version M"
schema_version > MAX_SCHEMA_VERSION"has schema_version N (maximum supported is M) — this profile was authored for a newer codegen; update xtask/src/codegen.rs to support schema version N"

The archived field (profile lifecycle)

Profiles have an optional valid_until date (ISO 8601, e.g. "2026-09-30"). Once the BDEW transition grace period has elapsed, the profile should no longer be compiled by default.

A profile moves through three compilation states over its lifetime:

stateDiagram-v2
    [*] --> Active : profile registered
    Active --> Expired : valid_until date passes
    Expired --> Archived : cargo xtask codegen --prune-expired<br/>(valid_until + grace_days elapsed, default 90 days)
  • Active — compiled under the plain feature (e.g. mscons).
  • Expired — past valid_until but still compiled; archived is not yet set.
  • Archivedarchived: true in mig.json; now gated behind the {type}-archive / archive feature instead of the plain feature.

The archived boolean field in mig.json is the explicit, deterministic marker for the final transition. It is set by cargo xtask codegen --prune-expired:

cargo xtask codegen --prune-expired [--grace-days N]

Default grace period: 90 days after valid_until.

What happens when a profile is archived

  • mod.rs gates it behind #[cfg(any(feature = "{type}-archive", feature = "archive"))] instead of the plain #[cfg(feature = "{type}")].
  • Users who only enable mscons will no longer compile expired MSCONS profiles.
  • Users who need historical validation enable mscons-archive (or archive for all types).

Why use an explicit JSON flag (not a date comparison at codegen time)?

If mod.rs were generated by comparing valid_until against now(), the output would differ every time the clock passed another expiry. This would cause cargo xtask codegen --check (the CI drift guard) to fail on an unrelated PR just because calendar time advanced.

The explicit "archived": true JSON field makes the generated mod.rs fully deterministic — identical output on every run until someone explicitly calls --prune-expired and commits the updated JSON and regenerated mod.rs.

Annual workflow

# 1. Mark newly expired profiles as archived
cargo xtask codegen --prune-expired

# 2. Review which profiles were archived
git diff profiles/

# 3. Regenerate (already done by --prune-expired, but re-run for --check confirmation)
cargo xtask codegen

# 4. Verify everything still compiles
cargo check --all-features --all-targets

# 5. Commit both the mig.json updates and the regenerated mod.rs
git add crates/edi-energy/profiles/*/mig.json crates/edi-energy/src/generated/mod.rs
git commit -m "chore: archive expired profiles after BDEW format update"

Cargo features

FeatureEffect
utilmdCurrent UTILMD profiles only
utilmd-archiveCurrent + archived UTILMD profiles
archiveCurrent + archived profiles for all message types

See crates/edi-energy/Cargo.toml for the full feature list.


Schema version

Every generated profile JSON carries a schema_version so the codegen can reject a profile written for an incompatible shape. The current schema is version 1, which defines the base fields release, valid_from, valid_until, archived, segments, pruefidentifikatoren, conditional_rules, segment_rules, and group_rules.

The accepted range is bounded by MIN_SCHEMA_VERSION and MAX_SCHEMA_VERSION (both 1) in discover_profiles() (xtask/src/codegen.rs); a profile outside that range fails codegen rather than being silently misread.

Edit this page ↗