Map an IFCX attribute key (+ its value, when known) onto the layer-op
componentKey vocabulary. Diff keys and op keys are deliberately the
same words (02 §2.2).
bsi::ifc::v5a::<Set>::<Member> keys carry an author-chosen set name
that is NOT required to start with Pset_/Qto_ — changeSetToOps
builds pset:<name>/qset:<name> unconditionally from the mutation
type, for any name, and @ifc-lite/collab's structured-attribute
inflation already disambiguates custom set names by VALUE SHAPE
(typed-record → pset, plain finite number → quantity) rather than by
matching the set name against those two prefixes. This used a
name-only regex instead, so any pset/qset whose author-chosen name
didn't start with Pset_/Qto_ silently fell through to a
one-off-per-attribute attr:<key> bucket instead of pset:<name> /
qset:<name> — e.g. a whole-pset tombstone
(buildDeltaNodes's tombstone-component case) looks up the base
state's members by pset:<name>, found nothing under that bucket,
and nulled zero attributes: a "delete this property set" edit on a
custom-named set silently did nothing.
A null value (an in-flight member deletion, not a whole-component
tombstone) carries no shape to disambiguate from, so it keeps the old
literal-prefix-only classification here. That is NOT sufficient on its
own — for a custom set name it would route the delete to a different
component than the live value sits in — so callers folding a stack must
go through resolveComponentKey, which resolves a deletion against the
components already present instead of guessing. See its docstring.
Map an IFCX attribute key (+ its value, when known) onto the layer-op componentKey vocabulary. Diff keys and op keys are deliberately the same words (02 §2.2).
bsi::ifc::v5a::<Set>::<Member>keys carry an author-chosen set name that is NOT required to start withPset_/Qto_—changeSetToOpsbuildspset:<name>/qset:<name>unconditionally from the mutation type, for any name, and@ifc-lite/collab's structured-attribute inflation already disambiguates custom set names by VALUE SHAPE (typed-record → pset, plain finite number → quantity) rather than by matching the set name against those two prefixes. This used a name-only regex instead, so any pset/qset whose author-chosen name didn't start withPset_/Qto_silently fell through to a one-off-per-attributeattr:<key>bucket instead ofpset:<name>/qset:<name>— e.g. a whole-pset tombstone (buildDeltaNodes'stombstone-componentcase) looks up the base state's members bypset:<name>, found nothing under that bucket, and nulled zero attributes: a "delete this property set" edit on a custom-named set silently did nothing.A
nullvalue (an in-flight member deletion, not a whole-component tombstone) carries no shape to disambiguate from, so it keeps the old literal-prefix-only classification here. That is NOT sufficient on its own — for a custom set name it would route the delete to a different component than the live value sits in — so callers folding a stack must go throughresolveComponentKey, which resolves a deletion against the components already present instead of guessing. See its docstring.