🚀 Enhancements
fix: implement MarshalYAML on Condition to make sure it survives marshalling roundtrip @jorrite (#3006)
## what- Add
Condition.MarshalYAML()topkg/condition/condition.goso awhen:condition survives being marshaled back to YAML. pkg/condition/condition_test.go: addTestConditionYAMLMarshalRoundTrip(predicate/CEL/all/any-not shapes) andTestConditionYAMLEmptyMarshalRoundTrip, asserting the marshaled-then-decoded value matches the expected shape and evaluates identically to the original across representative facts.pkg/project/config/config_baseref_test.go: addTestSaveAndLoadProjectRecord_FieldWhenSurvivesRoundTrip, exercising the realSaveProjectRecord/LoadProjectRecordpath with a field-levelwhen:condition (mirroringexamples/scaffolding/scaffold.yaml'senable_vendoring/vendor_versionpair) and asserting the reloaded condition evaluates correctly.- No change to
condition.Evaluate, CEL compilation, or the JSON marshal/unmarshal paths — out of scope, and unaffected since the fix reuses the samenode.value()reconstructionMarshalJSONalready relies on.
why
Condition's only field,node *Node, is unexported.Conditionalready implementsUnmarshalYAML,UnmarshalJSON, andMarshalJSON, but had noMarshalYAML. Without one,yaml.Marshalfalls back to reflecting the struct, sees zero exported fields, and writes{}— silently discarding the condition instead of erroring.- This corrupted
atmos scaffold generate's project record (.atmos/scaffold.yaml): aspec.fields[].whenCEL string (e.g."answers.topology == 'multi'") rendered correctly on firstgenerate, but got written back aswhen: {}. Since--updatere-reads that same record on every subsequent run, the project was permanently stuck failing schema validation (expected string, but got object) with no way to self-heal. spec.files[].whennever showed the same symptom, but not because that path is correct —SaveProjectRecordnever copiestemplateConfig.Spec.Filesinto the persisted record at all (onlyFields,Delimiters,Source,BaseRef,Valuesare copied), so there's nothing there to corrupt. Verified this holds with amatrix:-expandedfiles[]entry too: generation succeeds, butspec.filesis simply absent from the written record.- The fix reconstructs the original string/list/map form via
node.value()— the same methodMarshalJSONalready uses. This isn't new behavior:cloneCommand(cmd/cmd_utils.go) already JSON round-trips every custom command'sTask.Whenthrough this exact normalization on every invocation, so a bare CEL string already canonicalizes to!cel <expr>there today. Extending it toMarshalYAMLjust makes YAML and JSON serialization consistent with each other.
references
- n/a
Summary by CodeRabbit
-
Bug Fixes
- Preserved field-level YAML conditions when project records are saved and loaded.
- Ensured predicate, CEL, compound, and empty conditions retain their values during YAML round trips.
-
Tests
- Added coverage confirming condition formatting and evaluation remain consistent after serialization and reloading.