docs/specs/20260625-141024-agent-feature.mdadded---
spec: agent-feature
status: done
owner: feature-pipeline-agent
related-routes:
files:
# (none listed)
---
# agent-feature
## Goal
Enforce uniqueness of "Name" per entity scope on five existing project-scoped
xyz tables: AssetType, SystemType, Asset, CommissioningSystem (the "System"
in the spec), and CommissioningWorkflow (the "Workflow" in the spec).
Uniqueness is scoped per project (ProjectShardId) and, where the entity has a
natural parent scope, per that parent. Duplicates must be cleaned up before the
unique constraint is applied. No new tables are introduced.
## Behavior changes
- {db_calls: [usp_InsertAssetType, usp_InsertSystemType, usp_InsertAsset, usp_UpdateAsset,
usp_InsertCommissioningSystem, usp_UpdateCommissioningSystem, usp_InsertCommissioningWorkflow,
usp_UpdateCommissioningWorkflow], kind: error_mapping, notes: "For each of the\
\ five entity write paths (create + update where applicable),\ncatch the DB unique-violation\
\ (DB_ConstraintViolationError / SQLSTATE 23505\nor the explicit RAISE from the\
\ amended procs) and map it to a 409 Conflict\nusing a typed error from src/types/errortypes.ts\
\ (e.g. a Conflict/duplicate\nerror), with a clear message naming the entity and\
\ the colliding name.\nDo NOT add inline SQL \u2014 continue to call the existing\
\ usp_* procedures via\nthe existing const query strings. The actual service filenames\
\ must be\nconfirmed against the repo; the names above are the conventional lowercase\n\
dot-separated forms.\n", services: [asset.types.service.ts, system.types.service.ts,
assets.service.ts, commissioning.systems.service.ts, commissioning.workflows.service.ts]}
- {kind: tests_and_dtos, notes: "No DTO shape changes are required (Name field already\
\ exists). However,\nbecause the write procs are amended, existing API2 unit/e2e\
\ tests, mocks,\nand fixtures for these five entities must be updated so that:\n\
\ - duplicate-name create/update returns 409,\n - non-duplicate create/update\
\ still returns the existing success codes.\nUpdate fixtures/mocks so `npm test`\
\ stays green.\n"}
## Acceptance criteria
- [ ] The behavior described above is implemented on the listed files.
- [ ] Existing behavior is unchanged when the new feature is not exercised.
- [ ] Unit and (where observable) e2e tests cover the new behavior.
## Out of scope
- No changes to read endpoints or read procedures/functions.
- No new columns added to any table; only constraint/index + proc error handling.
- No case-insensitive normalization unless the open question is resolved that way at Gate 2.
- No new entities or tables.
## Notes / open questions
- Generated by the feature pipeline from the approved architect plan. If any
acceptance criterion is ambiguous, ask the human before implementing.
## Decisions
- **The five entities did not previously exist in api2.** A repo-wide search found
no services, routes, or `usp_Insert*/usp_Update*` references for AssetType,
SystemType, Asset, CommissioningSystem, or CommissioningWorkflow. Per the
explicit "implement the FULL API2 surface needed" instruction, the write paths
(create + update where the spec lists an update proc) were created from scratch
as new project-scoped resources, mirroring the established `categoryTypes`
sibling (routes → controller → validator → service).
- **409 mapping** reuses the existing `ResourceConflictError` (409) from
`src/types/errortypes.ts` rather than introducing a new error type. Each write
service catches `DB_ConstraintViolationError` (already produced by `db.ts` for
SQLSTATE 23505) — or an explicit proc `RAISE` whose message contains
"already exists" — and rethrows it as a `ResourceConflictError` naming the
entity and colliding name.
- **No inline SQL.** Services continue to `CALL xyz."usp_*"(...)` via const query
strings, matching the repo convention.
- **Scoping** follows the natural parent hierarchy: AssetType/SystemType are
project-scoped; Asset is scoped by `assetTypeId`; CommissioningSystem by
`systemTypeId`; CommissioningWorkflow by `commissioningSystemId`. Parent ids are
supplied in the request body for create.
- **Authority:** writes are guarded by `PROJECT_EDIT` (no asset/commissioning
specific authority exists in `authorities.types.ts`).
- **No read endpoints added** (spec out-of-scope). Resources expose only the
enumerated write paths.
- **e2e coverage:** happy-path 201/204 and duplicate-name 409 assertions require
the DB-side proc + unique-constraint migration, which lives outside this API
repo. e2e specs therefore cover the validation (400) and authorization (403)
paths, which execute before the DB layer; the 201/409 behavior is covered by
service unit tests that stub the `query` layer.
- Build/tests were **not run**: `node_modules` is not installed in this
environment and the task instruction forbids installing the toolchain. The
pipeline runs the suite afterward.