Skip to content

Strategy Reference

Every XRDConversionConfig rule picks one of these built-in strategies. Each is designed with bidirectional semantics — hub→spoke and spoke→hub — and the engine determines losslessness for each direction independently. Any direction the engine can't prove round-trips exactly requires acknowledgeLossy: true on the rule, or the whole config is rejected.

The default posture is fail-closed: any hub or spoke field left unclaimed by a rule, and not structurally identical on both sides, is a validation error — never a silent drop. See XRDConversionConfig for the full rule-authoring model.

All strategies at a glance

Strategy What it does Typically lossless?
fieldRename Renames a field, preserving its value and shape exactly. Always
scalarToObject Hub scalar ⇄ spoke object wrapping it under a key. Usually
objectToScalar Hub object ⇄ spoke scalar extracted from a key. Usually
singletonArrayToObject Hub single-element array ⇄ spoke object. Conditional
objectToSingletonArray Hub object ⇄ spoke single-element array. Conditional
fieldsToMap Several hub sibling fields ⇄ one spoke map. Conditional
mapToFields Hub map ⇄ several spoke sibling fields. Conditional
toAnnotation Stashes a hub field's value into a spoke annotation. Conditional (restoreOnReverse)
toLabel Stashes a hub field's value into a spoke label. Conditional (restoreOnReverse)
fromAnnotation Restores a spoke field from a hub annotation (inverse of toAnnotation). Conditional (stashOnReverse)
fromLabel Restores a spoke field from a hub label (inverse of toLabel). Conditional (stashOnReverse)
enumRemap Bidirectionally maps a scalar field's enumerated values. Conditional
defaultValue Injects a default for a field that only exists on one side. One direction always lossy
constant Forces a field to a fixed value on the side it exists on. One direction always lossy
delete Intentionally drops a field that exists on only one side. Always requires acknowledgement
jsonPatch Escape hatch: raw RFC 6902 JSON Patch per direction. Lossy unless losslessOverride
forEach Applies a nested rule list to each element of an array. Depends on nested rules
typeCoerce Converts a scalar's JSON type (string/int/number/bool). Conditional (onFractionalInteger; integers beyond 2⁵³)
scalarToFields Decomposes one scalar into several fields via regex + template. Lossy unless losslessOverride
fieldsToScalar Joins several fields into one scalar via template + regex. Lossy unless losslessOverride
arrayToMapByKey Array of objects ⇄ map keyed by one of their fields. Array→map yes, map→array no
mapToArrayByKey Map ⇄ array of objects keyed by one of their fields. Map→array no, array→map yes
numericScale Rescales a numeric field by a fixed factor. Conditional (integer side)
listJoin Array of scalars ⇄ delimited string. Always
listSplit Delimited string ⇄ array of scalars. Always
quantity Kubernetes Quantity string ⇄ integer millivalue. Integer→canonical string is lossy; string→milli is exact at milli resolution
duration Go duration string ⇄ integer seconds. Integer→canonical string is lossy; sub-second strings error
mapKeyRename Rename known keys in a free-form map; other keys pass through. Always (injective renames)
cel Arbitrary CEL expressions over declared paths. Always lossy (requires acknowledgeLossy; no losslessOverride)

Reading the examples

Every strategy page follows the same shape:

  1. What it does and when to use it.
  2. Lossy/lossless semantics, called out explicitly per direction.
  3. An XRD schema snippet showing the field on the hub version and the corresponding field on a spoke version.
  4. The XRDConversionConfig rule that maps between them.
  5. Example objects at both versions, so you can see the exact before/after shape.

All examples are self-contained and runnable through convctl test — see Getting Started and the CLI Reference. For complete configs combining several strategies against a whole schema, see Examples; for a single fixture exercising every strategy in this reference at once — with a rule-by-rule index of where each one lives — see Kitchen sink.