feat(versioning): unified version family for contracts and products (Phase 1)#446
Merged
Conversation
This was referenced May 27, 2026
Introduce version_family_id as a canonical, immutable grouping key on
both data_contracts and data_products so all versions of an entity are
retrievable via a single indexed equality lookup. Replaces the brittle
parent-walk + base_name heuristics with a stable family identity that
survives renames and is propagated through every clone path.
Backend
- Alembic j1_add_version_family_id: add column, backfill via recursive
parent-walk CTE, then mark NOT NULL on both tables.
- before_insert event listeners on DataContractDb/DataProductDb seed
id and version_family_id so the NOT NULL invariant holds on any
insert path (repo, demo loader, direct ORM construction).
- Repositories get get_family_versions and list_family_representatives
with personal-draft visibility filtering and a window-function-based
latest-visible collapse.
- All clone paths (create_new_version, ContractCloner,
clone_product_for_new_version) propagate version_family_id from the
source. ContractCloner no longer mutates name with a _v<version>
suffix — name stays stable across the family, version is its own
column.
- GET /api/data-contracts/{id}/versions rewritten and new
GET /api/data-products/{id}/versions added — both visibility-aware
and returning a lightweight EntityVersionRow shape.
Frontend
- versionFamilyId on DataContract, DataContractSummary, DataProduct.
- New shared components/common/version-selector.tsx generic over
entityKind: 'contract' | 'product'.
- New components/common/version-navigator.tsx wraps prev/next chevrons
+ selector and hides itself for single-version families.
- Wired into data-contract-details and data-product-details, replacing
the contract-specific implementation.
Deferred to follow-up PRs: list-view collapse with include_history
toggle, unified EntityVersionPicker (entity vs family-follow-latest),
and *_family_id reference-storage columns on join tables.
See docs/prds/prd-version-family-and-unified-selector.md (PRD #442).
363467b to
0d2da07
Compare
larsgeorge-db
added a commit
that referenced
this pull request
May 28, 2026
…se 2) Make the data-contracts and data-products list views show one row per version_family_id by default, with a "Show all versions" toggle that expands every visible version. Each collapsed row surfaces a small "N versions" badge so users can see at a glance which entities have history, without paying the row-count cost. Builds on PRD #442 / PR #446 (Phase 1). Backend - DataContractSummary + DataProduct API models gain a `versionCount` (alias `version_count`) field. Populated only on the collapsed view so the expanded view never lies about per-row counts. - DataContractsManager._query_contracts: drop the legacy base_name Python-side dedupe in favor of canonical version_family_id grouping, and return (rows, family_counts) so the builder can attach counts without a second query. Same shape feeds both collapsed and include_history paths. - DataContractsManager._build_contract_summaries now accepts an optional family_counts map and emits parent/family/draft/changeSummary fields that were previously left null on the summary even though they exist on the row. - list_contracts_from_db and the /data-contracts route propagate the include_history flag end-to-end (route was already accepting it). - DataProductsManager.list_products: collapse by version_family_id after the visibility cascade, attach version_count to the surviving row. include_history=True bypasses the collapse and suppresses the count. - /data-products route surfaces include_history. Frontend - DataContractListItem and DataProduct gain `versionCount`. - New components/common/version-count-badge.tsx — compact pill that hides itself for single-version families and otherwise renders a click-through history hint next to the version cell. - data-contracts.tsx and data-products.tsx: family badge next to the version column + "Show all versions" switch in the toolbar; both list-fetch paths plumb include_history through the query string. Tests - 5 new unit tests cover the collapse default, the family-count emission, include_history bypass, and product-side parity. Note: existing dev uvicorn instances have a stale --reload-dir pointing at the pre-Phase-1 src/api layout (see /tmp/backend.log statreload trace). Restart the backend to pick up the new include_history wiring locally.
larsgeorge-db
added a commit
that referenced
this pull request
May 28, 2026
…se 2) Make the data-contracts and data-products list views show one row per version_family_id by default, with a "Show all versions" toggle that expands every visible version. Each collapsed row surfaces a small "N versions" badge so users can see at a glance which entities have history, without paying the row-count cost. Builds on PRD #442 / PR #446 (Phase 1). Backend - DataContractSummary + DataProduct API models gain a `versionCount` (alias `version_count`) field. Populated only on the collapsed view so the expanded view never lies about per-row counts. - DataContractsManager._query_contracts: drop the legacy base_name Python-side dedupe in favor of canonical version_family_id grouping, and return (rows, family_counts) so the builder can attach counts without a second query. Same shape feeds both collapsed and include_history paths. - DataContractsManager._build_contract_summaries now accepts an optional family_counts map and emits parent/family/draft/changeSummary fields that were previously left null on the summary even though they exist on the row. - list_contracts_from_db and the /data-contracts route propagate the include_history flag end-to-end (route was already accepting it). - DataProductsManager.list_products: collapse by version_family_id after the visibility cascade, attach version_count to the surviving row. include_history=True bypasses the collapse and suppresses the count. - /data-products route surfaces include_history. Frontend - DataContractListItem and DataProduct gain `versionCount`. - New components/common/version-count-badge.tsx — compact pill that hides itself for single-version families and otherwise renders a click-through history hint next to the version cell. - data-contracts.tsx and data-products.tsx: family badge next to the version column + "Show all versions" switch in the toolbar; both list-fetch paths plumb include_history through the query string. Tests - 5 new unit tests cover the collapse default, the family-count emission, include_history bypass, and product-side parity. Note: existing dev uvicorn instances have a stale --reload-dir pointing at the pre-Phase-1 src/api layout (see /tmp/backend.log statreload trace). Restart the backend to pick up the new include_history wiring locally.
larsgeorge-db
added a commit
that referenced
this pull request
May 28, 2026
…se 2) (#447) Make the data-contracts and data-products list views show one row per version_family_id by default, with a "Show all versions" toggle that expands every visible version. Each collapsed row surfaces a small "N versions" badge so users can see at a glance which entities have history, without paying the row-count cost. Builds on PRD #442 / PR #446 (Phase 1). Backend - DataContractSummary + DataProduct API models gain a `versionCount` (alias `version_count`) field. Populated only on the collapsed view so the expanded view never lies about per-row counts. - DataContractsManager._query_contracts: drop the legacy base_name Python-side dedupe in favor of canonical version_family_id grouping, and return (rows, family_counts) so the builder can attach counts without a second query. Same shape feeds both collapsed and include_history paths. - DataContractsManager._build_contract_summaries now accepts an optional family_counts map and emits parent/family/draft/changeSummary fields that were previously left null on the summary even though they exist on the row. - list_contracts_from_db and the /data-contracts route propagate the include_history flag end-to-end (route was already accepting it). - DataProductsManager.list_products: collapse by version_family_id after the visibility cascade, attach version_count to the surviving row. include_history=True bypasses the collapse and suppresses the count. - /data-products route surfaces include_history. Frontend - DataContractListItem and DataProduct gain `versionCount`. - New components/common/version-count-badge.tsx — compact pill that hides itself for single-version families and otherwise renders a click-through history hint next to the version cell. - data-contracts.tsx and data-products.tsx: family badge next to the version column + "Show all versions" switch in the toolbar; both list-fetch paths plumb include_history through the query string. Tests - 5 new unit tests cover the collapse default, the family-count emission, include_history bypass, and product-side parity. Note: existing dev uvicorn instances have a stale --reload-dir pointing at the pre-Phase-1 src/api layout (see /tmp/backend.log statreload trace). Restart the backend to pick up the new include_history wiring locally.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Introduces
version_family_idas a canonical, immutable grouping key on bothdata_contractsanddata_productsso all versions of an entity are retrievable via a single indexed equality lookup. Replaces the brittle parent-walk +base_nameheuristics with a stable family identity that survives renames and is propagated through every clone path.This is Phase 1 of the PRD: backend foundation + shared version navigator in detail views. Subsequent phases (list-view collapse, unified
EntityVersionPicker, reference-storage*_family_idcolumns) ship as follow-up PRs.See PRD:
docs/prds/prd-version-family-and-unified-selector.md(GH #442).Backend
j1_add_version_family_id— adds column, backfills via recursive parent-walk CTE, marksNOT NULLon both tables.before_insertlisteners onDataContractDb/DataProductDbguarantee theNOT NULLinvariant on every insert path (repo, demo loader, direct ORM construction in tests).get_family_versions+list_family_representativeswith personal-draft visibility filtering and a window-function-based latest-visible collapse.create_new_version,ContractCloner,clone_product_for_new_version) propagateversion_family_idfrom the source.ContractClonerno longer mutatesnamewith_v<version>— name stays stable across the family; version is its own column.GET /api/data-contracts/{id}/versionsrewritten and newGET /api/data-products/{id}/versionsadded — both visibility-aware, returning a lightweightEntityVersionRowshape.Frontend
versionFamilyIdonDataContract,DataContractSummary,DataProduct.components/common/version-selector.tsx— generic overentityKind: 'contract' | 'product'.components/common/version-navigator.tsx— prev/next chevrons + selector, hides itself for single-version families.data-contract-details.tsxanddata-product-details.tsx. Old contract-specific selector deleted.Deferred to follow-up PRs
?include_historytoggle, family/version badge, expandable history.EntityVersionPicker— unified picker with entity-pinned vs family-follow-latest modes.*_family_idnullable columns + check constraints on join tables (output port → contract first).Test plan
test_version_family.py+ repo + clone regression)/api/data-contracts/{id}/versionsreturns properEntityVersionRowpayloadnameno longer gets_v…suffix andVersionNavigatorappears in details view/versionsendpoint