Description
Diffo.Type.Dynamic is a valid Ash.Type.NewType with two declared fields: :type (module) and :value (struct). Ash can resolve a reference path as far as .value — but no further, because :value is declared as type: :struct with no specific module. Ash's ref resolver cannot determine what fields exist beyond that point.
This means filter expressions like:
filter expr(name == :cvc and not is_nil(value.dynamic.value.svlan))
fail with Invalid reference value.dynamic.value.svlan, even though at runtime Diffo.Type.Dynamic knows exactly what type its :value holds — via dynamic_constraints/1, which can resolve the full field structure for a given type.
What we need
When a Diffo.Type.Dynamic value carries a known type (e.g. CvcValue), the fields of that type should be traversable in Ash expressions. The information exists — dynamic_constraints/1 computes it — but it is not currently surfaced to Ash's ref resolution path.
Why it matters
Without this, aggregate filters and expression calculations cannot reach inside dynamically-typed values, even when the type is well-known at the point of use. This limits the expressiveness of aggregates over Characteristic values and blocks a class of useful query patterns.
A possible direction
It may be that the right solution is an Ash.Type.Dynamic — a first-class Ash type that integrates this pattern properly, making dynamic field traversal a supported capability of the Ash type system rather than something Diffo works around. This is worth exploring with the Ash core team.
Description
Diffo.Type.Dynamic is a valid Ash.Type.NewType with two declared fields: :type (module) and :value (struct). Ash can resolve a reference path as far as .value — but no further, because :value is declared as type: :struct with no specific module. Ash's ref resolver cannot determine what fields exist beyond that point.
This means filter expressions like:
fail with Invalid reference value.dynamic.value.svlan, even though at runtime Diffo.Type.Dynamic knows exactly what type its :value holds — via dynamic_constraints/1, which can resolve the full field structure for a given type.
What we need
When a Diffo.Type.Dynamic value carries a known type (e.g. CvcValue), the fields of that type should be traversable in Ash expressions. The information exists — dynamic_constraints/1 computes it — but it is not currently surfaced to Ash's ref resolution path.
Why it matters
Without this, aggregate filters and expression calculations cannot reach inside dynamically-typed values, even when the type is well-known at the point of use. This limits the expressiveness of aggregates over Characteristic values and blocks a class of useful query patterns.
A possible direction
It may be that the right solution is an Ash.Type.Dynamic — a first-class Ash type that integrates this pattern properly, making dynamic field traversal a supported capability of the Ash type system rather than something Diffo works around. This is worth exploring with the Ash core team.