Fix the wire compatibility check for proto fields
Your task
An AI agent tidied up the .proto files of a gRPC service and said it only "reorganized fields and improved comments". Protobuf encodes each field by its number and type, never by its name or position, and many older clients still talk to this service. So you compare the old and new field descriptors before anything ships.
An assistant wrote detect_proto_breaks(old_fields, new_fields). It is in the editor. It has a bug. Find it and fix it.
Input and output:
old_fieldsandnew_fields: the message's fields before and after the change, each writtennumber|name|type. Innew_fields, an entry namedreservedmarks a number that was retired on purpose.- Return the changes that break old clients.
Rules the tests check:
- Split an entry on
|. Skip it unless it has exactly 3 parts and the number is one or more digits from 0 to 9. Read the number as a whole number. - In
old_fields, skip entries namedreserved. Innew_fields, an entry namedreservedreserves its number and is not a field. - If a number appears twice among one side's fields, keep only its first entry.
- For each old field, by its number:
new_fieldshas no field with that number, and the number is not reserved:removed:<number>new_fieldshas a field with that number and a different type:type_changed:<number>
renumbered:<name>for each old field whose name appears innew_fieldsat a different number. If a name appears at more than one new number, use the first. Report each name once.- A field renamed at the same number is fine, and so is a new field.
- Output the number findings first, by number ascending, then the renumbered findings, sorted by name in plain string order.
Examples
detect_proto_breaks(old_fields: list[str], new_fields: list[str]) -> list[str]Example 1
Input
detect_proto_breaks(["1|user_id|int64", "2|email|string", "3|created_at|int64"], ["1|user_id|int64", "2|email_address|string", "3|created_at|int64"])Output
[]Example 2
Input
detect_proto_breaks(["1|user_id|int64", "2|email|string", "3|score|int32"], ["1|user_id|int64", "2|score|int32", "3|email|string"])Output
["type_changed:2", "type_changed:3", "renumbered:email", "renumbered:score"]