Context
Discovered while reviewing #389255c57 (fix(csharp): payload-generator resolves bare @payloadRef in a named package).
PayloadCodegen.FindObject (server/csharp/MetaObjects.Codegen/PayloadCodegen.cs:44) matches an object.value by bare short name only:
private static MetaData? FindObject(MetaData root, string name) =>
root.Children().FirstOrDefault(c => c.Type == TYPE_OBJECT && c.Name == name);
The just-merged fix strips the package qualifier off a resolved @payloadRef before calling FindObject — correct for the common case, but it means the top-level payload lookup now inherits the same cross-package short-name collision risk that fixtures/template-output-render-conformance/xpkg-collision/ was built to describe (see that fixture's README: "the two colliding VOs share the BARE payload-record type name Note; each port's runner hand-authors ... the payload record for that reason").
Failure scenario
Two different packages each declare an object.value named Note with different fields. A template.output's bare @payloadRef resolves (ADR-0042) to pkgB::Note, gets stripped to Note, and FindObject returns whichever Note root.Children() enumerates first — silently emitting the WRONG package's shape under the right file/record name, with no error.
Note
This is a pre-existing, systemic characteristic of the C# port's bare-name-match convention — the SAME pattern (CSharpNaming.StripPkg + bare FindObject) is used by ~7 other generators (RoutesGenerator, EntityGenerator, M2MNavigation, OutputParserGenerator, DbContextGenerator, ReverseFinders, PackageBindingResolver), so this isn't unique to PayloadCodegen — fixing it properly likely means auditing/fixing the pattern port-wide, not just here. PayloadCodegen already has an FQN-exact resolver (ResolveObjectRef, used by the verify field-tree path) that could be a model for a safer variant.
Suggested fix direction
Prefer FQN-exact resolution when the ref contains :: (mirroring ResolveObjectRef's own logic) before falling back to bare-name match, across the affected generators — scoped as its own effort given the number of call sites.
Context
Discovered while reviewing #389255c57 (fix(csharp): payload-generator resolves bare @payloadRef in a named package).
PayloadCodegen.FindObject(server/csharp/MetaObjects.Codegen/PayloadCodegen.cs:44) matches anobject.valueby bare short name only:The just-merged fix strips the package qualifier off a resolved
@payloadRefbefore callingFindObject— correct for the common case, but it means the top-level payload lookup now inherits the same cross-package short-name collision risk thatfixtures/template-output-render-conformance/xpkg-collision/was built to describe (see that fixture's README: "the two colliding VOs share the BARE payload-record type nameNote; each port's runner hand-authors ... the payload record for that reason").Failure scenario
Two different packages each declare an
object.valuenamedNotewith different fields. Atemplate.output's bare@payloadRefresolves (ADR-0042) topkgB::Note, gets stripped toNote, andFindObjectreturns whicheverNoteroot.Children()enumerates first — silently emitting the WRONG package's shape under the right file/record name, with no error.Note
This is a pre-existing, systemic characteristic of the C# port's bare-name-match convention — the SAME pattern (
CSharpNaming.StripPkg+ bareFindObject) is used by ~7 other generators (RoutesGenerator,EntityGenerator,M2MNavigation,OutputParserGenerator,DbContextGenerator,ReverseFinders,PackageBindingResolver), so this isn't unique toPayloadCodegen— fixing it properly likely means auditing/fixing the pattern port-wide, not just here.PayloadCodegenalready has an FQN-exact resolver (ResolveObjectRef, used by the verify field-tree path) that could be a model for a safer variant.Suggested fix direction
Prefer FQN-exact resolution when the ref contains
::(mirroringResolveObjectRef's own logic) before falling back to bare-name match, across the affected generators — scoped as its own effort given the number of call sites.