Skip to content

Reference-storage: output port → contract family (family-follow-latest scope) (#442) #453

@larsgeorge-db

Description

@larsgeorge-db

Follow-up to PRD #442 / PR #448.

Today an output port stores only `contractId` — a pin to a specific contract version. To deliver user story 7 ("As a producer, I want a port to follow the contract family's latest published version so it auto-tracks updates"), the data model needs a second reference column.

Backend changes

  • Add nullable `contract_family_id` to the output-port storage (likely on `data_product_output_ports` or whatever the current home is — verify during scoping). Migration must be additive: existing rows keep `contract_id` pinned, new rows can populate either column but not both.
  • Validation: enforce mutually-exclusive `{contract_id, contract_family_id}` at the API model layer. One of them must be populated when a contract is attached; clearing the link clears both.
  • Resolution: when reading a port for downstream code (compliance checks, lineage, etc.), if `contract_family_id` is set, resolve via the existing `GET /api/data-contracts/families/{family_id}/latest` endpoint (already shipped in feat(versioning): role-aware family collapse, family-latest resolver, EntityVersionPicker (#442 phase 3) #448) using the calling persona's visibility. Cache the resolved id within a single request.
  • Surface both columns in the `OutputPort` Pydantic model with camelCase serialization (`contractId`, `contractFamilyId`) — follow the `serialization_alias` pattern landed in the feat(versioning): role-aware family collapse, family-latest resolver, EntityVersionPicker (#442 phase 3) #448 follow-up commit.

Frontend changes

  • Flip `link-contract-to-port-dialog` to `allowedScopes={['entity', 'family']}`. The picker already supports both modes; today the dialog only writes `contractId` so the family branch is hidden.
  • Persist whichever scope the user picks: `{contractId}` for entity-pinned, `{contractFamilyId}` for family-follow-latest. Clear the other column.
  • Indicator badge on the linked-contract chip: a small "↪ latest" / "📌 pinned" pill next to the contract name on the product details view so the producer can tell at a glance which mode this port is in.
  • Tooltip on the badge explaining what changes if a new version of the family is published.

Tests

  • Backend: round-trip a port through both scopes; resolution returns the right concrete row per caller persona.
  • Backend: mutual-exclusion validation rejects `{contract_id, contract_family_id}` both set.
  • Frontend: Vitest for the dialog covering both write paths and the badge rendering.

Out of scope

  • Same work for MDM sources — tracked separately.
  • Bulk migration of existing entity-pinned ports to family-follow-latest. The PRD treats this as an explicit, opt-in producer action; no auto-migration.

Metadata

Metadata

Assignees

No one assigned

    Labels

    scope/contractsData Contract related featurescope/productsData Produc related featuretech/javascriptPull requests that update javascript codetech/pythonPull requests that update python codetype/featureFeature requests

    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