# Postgres Specialist Report — Unique Name constraint on 4 tables **This change file is AI-generated.** All additions were authored under the changeset author `agentneo` (the existing `999_indexes.sql` changeset is unchanged and retains its original author `davewebb`). ## Revision context — "why no change?" The first run produced **no change**, and that was technically correct under the hard rule "when `index_changes` is none/empty, do nothing." The plan's top-level `index_changes:` field is literally `none`. However, the plan is **self-contradictory**: its `summary` and all four `postgres_changes` notes state that the *entire* deliverable is four unique indexes, and one note explicitly says "The Postgres specialist appends the human-ticked unique indexes to `Database/xyz/Constraints/999_indexes.sql`." The architect described the indexes in full (tables, columns, partial-index WHERE clauses) but failed to mechanically populate the `index_changes` list — so the "do nothing" outcome was an artifact of that omission, not a deliberate Gate-2 rejection. The reviewer's feedback **"why no change?"** is a direct human instruction resolving the contradiction in favour of implementing the indexes. Because the notes specify them exactly, this is implementing the documented spec, not inventing indexes. The four indexes are now added. ## Files modified ### `Database/xyz/Constraints/999_indexes.sql` Appended four `CREATE UNIQUE INDEX IF NOT EXISTS` statements, each grouped under a `-- xyz.` header comment per the file's existing convention. The file's existing changeset (`runOnChange:true`, `endDelimiter:;`) covers the new statements — no new file and no new changeset header was created (per the rule: never create a separate file for indexes). | Table | Index name | Columns | Filter | |-------|-----------|---------|--------| | `xyz.AssetType` | `AssetType_Name_key` | `("ProjectShardId", "Name")` | none | | `xyz.SystemType` | `SystemType_Name_key` | `("ProjectShardId", "Name")` | none | | `xyz.Asset` | `Asset_Name_key` | `("ProjectShardId", "Name")` | `WHERE "IsDeleted" = FALSE` | | `xyz.CommissioningSystem` | `CommissioningSystem_Name_key` | `("ProjectShardId", "Name")` | `WHERE "IsDeleted" = FALSE` | No files added. ## Design decisions (taken verbatim from the plan notes) - **Per-project scope:** all four tables have a composite PK `("ProjectShardId", "
Id")`. Each unique index leads with `"ProjectShardId"`, satisfying Citus's single-shard uniqueness-enforcement requirement (a global unique index on `Name` alone is not enforceable on a distributed table). - **Case-sensitive:** plain `"Name"` column, no `LOWER()` / citext. - **Partial indexes for soft-deletable tables:** `xyz.Asset` and `xyz.CommissioningSystem` have an `"IsDeleted" BOOLEAN` column; their unique indexes are filtered `WHERE "IsDeleted" = FALSE` so a Name can be re-used after a soft-delete. `xyz.AssetType` and `xyz.SystemType` have no `IsDeleted` column (verified in the table files), so theirs are unfiltered. ## Column verification Confirmed against the table-create files: - `118_xyz_asset_type.sql` — `ProjectShardId`, `Name`; no `IsDeleted`. - `119_xyz_system_type.sql` — `ProjectShardId`, `Name`; no `IsDeleted`. - `129_xyz_asset.sql` — `ProjectShardId`, `Name`, `IsDeleted BOOLEAN NOT NULL DEFAULT FALSE`. - `130_xyz_commissioning_system.sql` — `ProjectShardId`, `Name`, `IsDeleted BOOLEAN NOT NULL DEFAULT FALSE`. Naming follows CLAUDE.md's unique convention `"{Table}_{Column}_key"`. ## Verification status - `./build` — **not run.** No `./build` script is present in the repo and Docker is unavailable in this environment, so a from-scratch changelog deploy could not be performed. Static review only. - `./test` — present but requires a running Postgres on :5490 / Docker; **not run.** - Static review: index names are unique within the file, all referenced tables/columns exist, `IF NOT EXISTS` makes the statements idempotent under `runOnChange:true`, and partial-index predicates reference real columns. ## Deviations from the plan - The plan's `index_changes` is `none`; per the reviewer instruction these four unique indexes were nonetheless implemented exactly as described in the plan's `postgres_changes` notes. This deviation is the explicit subject of the revision request. ## Open questions / risks for the human reviewer 1. **Pre-existing duplicates (deploy-time failure risk).** `CREATE UNIQUE INDEX` will FAIL if any project already has two constrained rows sharing a `Name` (live rows only for `Asset` / `CommissioningSystem`). This could not be checked here (no DB access). Before deploying, run per table: ```sql SELECT "ProjectShardId", "Name", COUNT(*) FROM xyz."AssetType" GROUP BY "ProjectShardId", "Name" HAVING COUNT(*) > 1; ``` (analogous for `SystemType`; add `WHERE "IsDeleted" = FALSE` for `Asset` and `CommissioningSystem`). If any rows return, a Patch/ cleanup or manual dedup is required first — do not silently dedup. 2. **"System" → `CommissioningSystem` mapping.** The spec's "System" table maps to `xyz.CommissioningSystem`. Confirm this is the intended table. 3. **API 409/422 shaping is out of scope.** A duplicate insert/update will now raise a DB unique-violation; the friendly 409/422 response is a separate API2 change not done here.