From 0f28fa425f03df1c4598d0a49b3de3cd9131f4c9 Mon Sep 17 00:00:00 2001 From: Roman Berezkin Date: Thu, 28 May 2026 17:41:01 +0300 Subject: [PATCH] Preserve object-level default map when walking properties - `synthesizeValue` overlays a map-typed `s.Default` on top of the walked properties via `deepMergeOverride`, mirroring the precedence used for `x-example`. - Object schemas that declared both `properties` and a `default` map no longer drop the default - author-supplied values win on conflicts, unset fields fall back to the synthesized walk. - Package doc updated: rule 4 mentions the default-map overlay, rule 5 is scoped to non-object schemas. Signed-off-by: Roman Berezkin --- .../packages/values/schema/defaults/generator.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/internal/packagecmd/internal/packages/values/schema/defaults/generator.go b/internal/packagecmd/internal/packages/values/schema/defaults/generator.go index 1b67110f..77d66bc6 100644 --- a/internal/packagecmd/internal/packages/values/schema/defaults/generator.go +++ b/internal/packagecmd/internal/packages/values/schema/defaults/generator.go @@ -9,7 +9,10 @@ // 3. `enum`, preferring `default` if set, else the first enum value. // 4. Object walk — explicit `type: object`, structural `properties`, or // composition keywords (allOf / oneOf / anyOf) contribute properties. -// 5. `default` (deep-copied so callers may mutate the result). +// If `default` is a map, it is overlaid on top of the walked properties +// (same precedence rule as `x-example`), so a schema can declare both +// a property set and a richer default map without losing either. +// 5. `default` for non-object schemas (deep-copied so callers may mutate). // 6. Array walk — one element synthesized from `items.schema`. // 7. Scalar placeholder by type: 123 for integer/number, true for boolean, // a pattern-matching string via reggen for string. @@ -132,7 +135,16 @@ func synthesizeValue(s *spec.Schema) (any, error) { } if isObject(s) { - return synthesizeObject(s) + base, err := synthesizeObject(s) + if err != nil { + return nil, err + } + + if def, ok := s.Default.(map[string]any); ok { + deepMergeOverride(base, def) + } + + return base, nil } if s.Default != nil {