@@ -713,18 +713,34 @@ func mergeOpenapiSchemas(s1, s2 *base.Schema) (*base.Schema, error) {
713713 // Required. We merge these.
714714 result .Required = append (s1 .Required , s2 .Required ... )
715715
716- // We merge all properties
716+ // We merge all properties. When both schemas declare a property
717+ // with the same name, an annotation-only override (carrying only
718+ // fields like `example` or `description`) must not overwrite a
719+ // sibling that actually shapes the Go type. Otherwise we keep the
720+ // previous "s2 wins" behavior, since both sides genuinely shape
721+ // the type and the second declaration is the more specific one in
722+ // an allOf chain.
723+ //
724+ // We deliberately pick one of the original SchemaProxies rather
725+ // than fabricating a merged proxy via CreateSchemaProxy: downstream
726+ // code reads GoLow().GetReference() to attribute properties back to
727+ // the spec, and a synthetic proxy has no low-level backing.
717728 for k , v := range s1 .Properties .FromOldest () {
718729 if result .Properties == nil {
719730 result .Properties = orderedmap .New [string , * base.SchemaProxy ]()
720731 }
721732 result .Properties .Set (k , v )
722733 }
723734 for k , v := range s2 .Properties .FromOldest () {
724- // TODO: detect conflicts
725735 if result .Properties == nil {
726736 result .Properties = orderedmap .New [string , * base.SchemaProxy ]()
727737 }
738+
739+ if existing , exists := result .Properties .Get (k ); exists {
740+ if isAnnotationOnlySchema (v .Schema ()) && ! isAnnotationOnlySchema (existing .Schema ()) {
741+ continue
742+ }
743+ }
728744 result .Properties .Set (k , v )
729745 }
730746
@@ -791,6 +807,45 @@ func getSchemaType(schema *base.Schema) []string {
791807 return nil
792808}
793809
810+ // isAnnotationOnlySchema reports whether a schema has no fields that
811+ // influence the generated Go type. Schemas like `{ example: cancelled }`
812+ // or `{ description: "..." }` are annotation-only and must not be
813+ // allowed to overwrite a sibling declaration that actually carries a
814+ // type, enum, or sub-schema in an allOf merge.
815+ func isAnnotationOnlySchema (s * base.Schema ) bool {
816+ if s == nil {
817+ return true
818+ }
819+ if len (s .Type ) > 0 {
820+ return false
821+ }
822+ if len (s .AllOf ) > 0 || len (s .OneOf ) > 0 || len (s .AnyOf ) > 0 {
823+ return false
824+ }
825+ if s .If != nil || s .Then != nil || s .Else != nil || s .Not != nil {
826+ return false
827+ }
828+ if s .Properties != nil && s .Properties .Len () > 0 {
829+ return false
830+ }
831+ if s .PatternProperties != nil && s .PatternProperties .Len () > 0 {
832+ return false
833+ }
834+ if s .Items != nil {
835+ return false
836+ }
837+ if len (s .Enum ) > 0 {
838+ return false
839+ }
840+ if s .AdditionalProperties != nil {
841+ return false
842+ }
843+ if s .Const != nil {
844+ return false
845+ }
846+ return true
847+ }
848+
794849// mergeNullable merges two nullable pointers using a union (more permissive) approach.
795850// If either is true, the result is true. If both are false or nil, the result is nil.
796851// This allows merging schemas where one specifies nullable and another doesn't.
0 commit comments