Both generator targets reuse a payload type across registers by name alone, without checking that the registers agree on what that type describes. A register with a payloadSpec and an interfaceType names its payload after the interface type, so a second register declaring the same interface type receives the type built for the first one, whatever its own element type, length, or members happen to be.
The C# target keeps a set of already emitted names and skips any repeat, in Device.tt. The Python target looks the payload up in a module-level list and reuses whatever it finds, in Python.cs. Neither compares the second register against the type it is handed.
What can go wrong
Two registers declaring the same interfaceType can differ in their payloadSpec members, in their element type, or in their declared length. In each case the second register is described by a type built for the first.
The C# target is partly self-correcting, because the conversion code is generated per register and assigns members by name, so a register whose members differ from the struct it inherits should reference a member that does not exist and fail to compile. Whether a mismatch only in element width also fails to compile has not been checked, so that half is reasoning from the templates rather than a measured result.
The Python target has no equivalent backstop. The emitted class is simply reused, and a register of a different width decodes against the wrong element type with no diagnostic.
Why this is worth recording now
It is latent rather than live. The only place in the published schemas where two registers share an interface type over a payloadSpec is Rgb0 and Rgb1 in device.behavior, and those are correct by construction, since Rgb1 is a YAML merge of the anchor on Rgb0 and cannot diverge from it.
What changes the calculus is that the runtime emitter in pyharp now mirrors this reuse deliberately, so both Python paths depend on the behavior being either correct or explicitly validated. See harp-tech/python#19 for that change and the reasoning behind matching the generator rather than adding a client-side check.
Possible resolutions
Validate in the generator, rejecting a repeated interface type whose register disagrees on element type, length, or members. This is the smaller change and keeps the check in one place per target, at the cost of every target implementing it independently.
Make the reuse an explicit schema declaration instead of something inferred from a repeated string. A named payload section in device.yml would let the schema carry the intent, so a generator validates a declared reference rather than guessing whether two matching strings mean the same type. A proposal for this is being prepared against the protocol repository and will be linked here once filed.
Related Issues
harp-tech/protocol#118
harp-tech/python#19
Both generator targets reuse a payload type across registers by name alone, without checking that the registers agree on what that type describes. A register with a
payloadSpecand aninterfaceTypenames its payload after the interface type, so a second register declaring the same interface type receives the type built for the first one, whatever its own element type, length, or members happen to be.The C# target keeps a set of already emitted names and skips any repeat, in Device.tt. The Python target looks the payload up in a module-level list and reuses whatever it finds, in Python.cs. Neither compares the second register against the type it is handed.
What can go wrong
Two registers declaring the same
interfaceTypecan differ in theirpayloadSpecmembers, in their element type, or in their declared length. In each case the second register is described by a type built for the first.The C# target is partly self-correcting, because the conversion code is generated per register and assigns members by name, so a register whose members differ from the struct it inherits should reference a member that does not exist and fail to compile. Whether a mismatch only in element width also fails to compile has not been checked, so that half is reasoning from the templates rather than a measured result.
The Python target has no equivalent backstop. The emitted class is simply reused, and a register of a different width decodes against the wrong element type with no diagnostic.
Why this is worth recording now
It is latent rather than live. The only place in the published schemas where two registers share an interface type over a
payloadSpecisRgb0andRgb1in device.behavior, and those are correct by construction, sinceRgb1is a YAML merge of the anchor onRgb0and cannot diverge from it.What changes the calculus is that the runtime emitter in pyharp now mirrors this reuse deliberately, so both Python paths depend on the behavior being either correct or explicitly validated. See harp-tech/python#19 for that change and the reasoning behind matching the generator rather than adding a client-side check.
Possible resolutions
Validate in the generator, rejecting a repeated interface type whose register disagrees on element type, length, or members. This is the smaller change and keeps the check in one place per target, at the cost of every target implementing it independently.
Make the reuse an explicit schema declaration instead of something inferred from a repeated string. A named payload section in
device.ymlwould let the schema carry the intent, so a generator validates a declared reference rather than guessing whether two matching strings mean the same type. A proposal for this is being prepared against the protocol repository and will be linked here once filed.Related Issues
harp-tech/protocol#118
harp-tech/python#19