Hi Matt,
Great to hear that you're progressing with your implementation!
> Can anyone confirm this interpretation, or better yet show it to me in the spec?
Yes, you're on the right path ;)
There are two separate aspects here: a) invocation of value extractors in order to obtain a container's elements and b) rules for constructing property paths.
On a), value extractors are called to cascade into containers when processing @Valid (where 2.0 generalizes the 1.1 notion of cascaded validation, see [1]) and when processing container element constraints (e.g. List<@Positive Integer>).
On b), the rules are described in [2]. Specifically:
"If a parameter ... of a ... constructor is traversed:
* a ... ConstructorNode object is added to the Path
* ... a ParameterNode object is added to the Path
* if the parameter ... is a List or an array, the following
Node object added contains the index value in getIndex().
And:
"For a container element constraint:
* if the corresponding value extractor has specified a node name when
calling one of the receiver methods, a ContainerElementNode object
with that name is added to the Path"
I.e. you'll see path nodes with the name returned by a value extractor in the second case, but not the first. One more case where you will see a node with that name, is nested cascaded validation (Map<String, List<@Valid Address>>).
And finally, no node will be appended if the extractor doesn't provide a node name. This is to suppress addition of a node for wrapper-style containers such as Optional<@Positive Integer> or @Positive OptionalInt.
Hth,
--Gunnar