Context
PR #416 added a range INDEX on `CatalogueCategoryProperty.uid` (migration `20260511120000_add_catalogue_category_property_uid_index`) instead of a uniqueness CONSTRAINT, to avoid dirty-state risk if duplicate UIDs exist in prod data.
Every other identifier in the schema (`CatalogueItem.uid`, `Unit.uid`, `Manufacturer.name`, `User.uid`, `Role.uid`, `CatalogueCategoryPropertyType.uid`) uses a CONSTRAINT. CatalogueCategoryProperty is the inconsistent one.
Steps
- Audit prod for duplicates: `MATCH (p:CatalogueCategoryProperty) WITH p.uid AS u, count(*) AS c WHERE c > 1 RETURN u, c`.
- If clean → add migration `CREATE CONSTRAINT CatalogueCategoryProperty_uid_unique IF NOT EXISTS FOR (p:CatalogueCategoryProperty) REQUIRE p.uid IS UNIQUE` + drop the bare INDEX (constraint creates its own).
- If duplicates exist → data-cleanup migration first, then constraint.
Why
Closes the semantic gap, prevents accidental duplicate-UID creation in code paths that don't go through MERGE.
Context
PR #416 added a range INDEX on `CatalogueCategoryProperty.uid` (migration `20260511120000_add_catalogue_category_property_uid_index`) instead of a uniqueness CONSTRAINT, to avoid dirty-state risk if duplicate UIDs exist in prod data.
Every other identifier in the schema (`CatalogueItem.uid`, `Unit.uid`, `Manufacturer.name`, `User.uid`, `Role.uid`, `CatalogueCategoryPropertyType.uid`) uses a CONSTRAINT. CatalogueCategoryProperty is the inconsistent one.
Steps
Why
Closes the semantic gap, prevents accidental duplicate-UID creation in code paths that don't go through MERGE.