Skip to content

JSON surfacing of inherited (and reverse-inherited) characteristics, places, parties #173

@matt-beanland

Description

@matt-beanland

Description

Diffo.Provider.Extension.Transformers.TransformInheritedRefs today injects the inherited_place and inherited_party declarations as Ash calculations, so they're loadable via Ash.load(instance, [:my_inherited_role]) and the result reaches the in-memory record. But the BaseInstance jason customize doesn't include those calcs in the place / relatedParty JSON arrays on encode — the brought-up values are invisible to any consumer reading via the TMF surface (TMF638, MCP-tool-output, REST clients).

The same gap will apply to the proposed inherited_characteristic and reverse_inherited_characteristic from #172 — they'll be loadable but won't appear in resourceCharacteristic / serviceCharacteristic arrays.

The bring-up is incomplete without JSON surfacing. The whole point of declaring inherited_place :exchange, via: [:uplink], source_role: :location (or similar) is for the inherited value to participate in the standard view of the instance — the place where consumers actually look.

What we'd find useful

When an instance is JSON-encoded, inherited and reverse-inherited results appear automatically in the corresponding TMF array — no per-consumer customize required. Convention:

{
  "resourceCharacteristic": [
    {"name": "shelf",  "value": {...local self...}},
    {"name": "slots",  "value": {...local pool...}},
    {"name": "card",   "value": {...inherited singular from parent...}},
    {"name": "cards",  "value": [{...}, {...}]}              // reverse-inherited array from children
  ],
  "relatedParty": [
    {...local PartyRef...},
    {...inherited PartyRef from upstream traversal...}
  ],
  "place": [
    {...local PlaceRef...},
    {...inherited PlaceRef from upstream traversal...}
  ]
}

Conventional ordering within each array:

  1. Local self / pool — what this instance declared directly
  2. Inherited singulars — single-value brought-up from a parent assigner via incoming-assignment traversal
  3. Inherited arrays — multi-value brought-up from parent assigners (rare but possible with multi-hop or repeated alias)
  4. Reverse-inherited arrays — brought-up from child assignees (outgoing-assignment traversal)

Applies symmetrically to characteristics, places, and parties.

Why it matters

Today, a consumer who declares inherited_place :exchange, via: [:uplink], source_role: :location and then encodes the instance to JSON sees no inherited exchange — the calc exists but the result never makes it into the place array. The bring-up promise of the DSL isn't kept at the consumer-visible surface.

This becomes more pointed once #172 lands and inherited_characteristic / reverse_inherited_characteristic exist — the choreography pattern (every instance shows itself plus brings up its immediate neighbours) only operates at the API level, not at the TMF JSON view. TMF638 readers, MCP tool callers, and any HTTP consumer would all miss the brought-up data.

It also fixes a symmetry break: the declaration says "this inherited thing IS part of me," but the JSON view contradicts that — the inherited thing isn't shown. Either inherited belongs in the view or it doesn't. We think it does.

A possible direction

The DSL declarations (inherited_place, inherited_party, future inherited_characteristic, future reverse_inherited_characteristic) already give the framework everything it needs to know:

  • The role name (where in the JSON array to inject)
  • The calc to load
  • The expected shape (singular vs array)

So either:

  • TransformInheritedRefs (and the new TransformInheritedCharacteristic) also adds a jason customize step that loads + injects per declaration, or
  • A new sibling jason transformer that runs over all inherited-style declarations after BaseInstance's existing customize, appending the loaded values in the conventional order

Either shape lets the consumer write nothing extra — the DSL declaration is enough for both the API-level load AND the TMF JSON appearance.

Related:

  • #172 — the proposed inherited_characteristic / reverse_inherited_characteristic DSL declarations themselves; this JSON surfacing should land as part of the same enhancement scope (or paired)
  • #169 — the same family of "the DSL knows enough to surface this, the JSON view doesn't yet" gap for typed characteristics; if both Surface typed characteristics and pools in Instance JSON encoding #169 and this land together, every typed/inherited/reverse-inherited characteristic surfaces uniformly

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