Skip to content

inherited_characteristic + reverse_inherited_characteristic — bring up typed characteristic values across the assignment graph #172

@matt-beanland

Description

@matt-beanland

Description

Diffo already has inherited_place and inherited_party DSL declarations (and their backing Diffo.Provider.Calculations.InheritedPlace / InheritedParty calc modules) that bring up PlaceRef / PartyRef records from instances reached by traversing AssignmentRelationship by alias. The sibling for typed characteristics is missing.

The use case showed up in diffo_example#10 (the turtles-down direction — a path bringing up its card's and shelf's typed characteristics via the port-then-slot assignment chain) and diffo_example#32 (the turtles-up direction — a shelf bringing up its cards' typed characteristics via outgoing slot assignments). We wrote both calculations locally in diffo_example and they work; they belong upstream.

What we'd find useful

Two new DSL declarations inside characteristics do, mirroring the existing inherited_place and inherited_party:

  • inherited_characteristic :role, via: [...] — generate a calc that traverses incoming AssignmentRelationship by alias (this instance is the target) to reach source instances, then reads the typed characteristic value declared with the given role on each source.
  • reverse_inherited_characteristic :role, alias: ..., characteristic: ... — generate a calc that traverses outgoing AssignmentRelationship (optionally filtered by alias) to reach assignee instances, then reads the typed characteristic value on each. Useful when the assigner wants to compose its assignees into its own view.

Backing calc modules (Diffo.Provider.Calculations.InheritedCharacteristic and Diffo.Provider.Calculations.ReverseInheritedCharacteristic) take the same via: / alias: semantics as the existing inherited-place/party calcs and the FieldVia* family.

Why it matters

Typed characteristics are how Diffo 0.4.0 expresses structured per-instance values; bringing them up across the assignment graph is the live-characteristics promise made concrete. Without an upstream primitive, every consumer writes the same calc (we wrote two flavours) — declaring the relationship is enough, the consumer shouldn't have to wire the calc logic. The pattern is identical to what inherited_place / inherited_party do for the universal Place/Party resources; the only structural difference is that typed characteristics are per-resource (e.g. ShelfCharacteristic, CardCharacteristic), so the backing calc needs to know which characteristic module to query — addressable via the role name in the DSL plus the resource's existing characteristic :role, Module declaration on each source.

This also closes the turtles-all-the-way-up/down loop from diffo_example#10: each instance shows itself, brings up its immediate-below (or immediate-above), and the chain composes naturally because every step is the same primitive.

A possible direction

Our local implementations live at:

Both mirror the shape of Diffo.Provider.Calculations.InheritedPlace — same traversal mechanics, just the final read targets the typed characteristic resource. Each is around 30-40 lines.

The DSL side could reuse the existing inherited_place / inherited_party transformer pattern in TransformInheritedRefs (or similar). The inherited_characteristic declaration would need to resolve role → typed characteristic module via the source's characteristics() declaration; for our local version we passed characteristic_module: explicitly, but the DSL macro can hide that by looking it up at compile time.

Happy to move our local implementations upstream if the shape is welcome. Related: diffo_example#10, diffo_example#32.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions