docs/specs/20260715-154549-agent-feature.mdadded
---
spec: agent-feature
status: done
owner: feature-pipeline-agent
related-routes:
files:
  - src/api/v2/projects/assettypes/systemtypemappings/asset.type.systemtype.mappings.routes.ts
  - src/api/v2/projects/assettypes/systemtypemappings/asset.type.systemtype.mappings.controller.ts
  - src/api/v2/projects/assettypes/systemtypemappings/asset.type.systemtype.mappings.validator.ts
  - src/services/asset.type.systemtype.mappings.service.ts
  - src/api/v2/projects/assettypes/systemtypemappings/asset.type.systemtype.mappings.routes.ts
  - src/api/v2/projects/assettypes/systemtypemappings/asset.type.systemtype.mappings.controller.ts
  - src/services/asset.type.systemtype.mappings.service.ts
  - src/api/v2/projects/assettypes/systemtypemappings/asset.type.systemtype.mappings.routes.ts
  - src/api/v2/projects/assettypes/systemtypemappings/asset.type.systemtype.mappings.controller.ts
  - src/services/asset.type.systemtype.mappings.service.ts
  - src/api/v2/projects/assettypes/systemtypemappings/asset.type.systemtype.mappings.routes.ts
  - src/api/v2/projects/assettypes/systemtypemappings/asset.type.systemtype.mappings.controller.ts
  - src/services/asset.type.systemtype.mappings.service.ts
  - src/services/asset.service.ts
  - src/models/egress.ts
  - src/services/asset.service.ts
  - src/models/egress.ts
---

# agent-feature

## Goal

Adds four API2 endpoints to manage AssetType↔SystemType mappings (create,
delete, list, get-by-id) backed by the existing xyz.AssetTypeSystemTypeMapping
table, and augments both Asset GET endpoints to include a scalar systemId
field derived from the asset's AssetType→SystemType mapping (null when no
mapping exists). No new tables are introduced — all target tables already
exist. This is a modify feature entirely on API2 + Postgres.


## Behavior changes

- POST /api/v2/projects/{projectId}/asset-types/system-type-mappings
- DELETE /api/v2/projects/{projectId}/asset-types/system-type-mappings/{mappingId}
- GET /api/v2/projects/{projectId}/asset-types/system-type-mappings/{mappingId}
- GET /api/v2/projects/{projectId}/asset-types/system-type-mappings
- GET /api/v2/projects/{projectId}/assets/{assetId}
- GET /api/v2/projects/{projectId}/assets

## 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

- Updating Asset write endpoints (POST/PUT/PATCH) to accept systemId.
- Returning a nested SystemType object (only the scalar systemId is returned).
- Bulk create/delete of mappings.
- Cascade/blocking behavior when an AssetType or SystemType is deleted.

## 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

- **New router placement.** The `system-type-mappings` router is mounted at
  `/:projectId/asset-types/system-type-mappings` in `api.v2.routes.ts` *before*
  the `/:projectId/asset-types` router so the more specific mappings path is not
  swallowed by the asset-types `GET /:assetTypeId` route.
- **`systemId` semantics.** The spec names the new Asset field `systemId` but
  describes it as being derived from the AssetType→SystemType mapping, which
  yields a *SystemType* id. Implemented literally: field name `systemId`,
  value = the `SystemTypeId` of the mapping for the asset's assetType (or null).
  Flagged as an open question for the reviewer.
- **`systemId` is read-only and optional.** Added to the `Asset` interface as
  `systemId?: string | null`, populated only on the GET (list/get-by-id) paths.
  POST/PATCH responses are left byte-for-byte unchanged (regression safety and
  in line with the "write endpoints unchanged" out-of-scope note).
- **Derivation query.** `assets.service` calls a new
  `getAssetTypeToSystemTypeMap(projectId)` helper (one query returning all
  project mappings) and looks up each asset's `assetTypeId`. Simple and avoids a
  per-asset query.
- **DB layer.** Mappings service assumes the sibling naming convention for the
  existing table: `fn_GetAssetTypeSystemTypeMappingList`,
  `fn_GetAssetTypeSystemTypeMapping`, `fn_InsertAssetTypeSystemTypeMapping`,
  `usp_DeleteAssetTypeSystemTypeMapping`. No new tables (per spec).
- **egress.ts.** Left untouched — Asset/mapping shapes follow the repo
  convention of living in their service files, not `models/egress.ts`. Noted as
  a deviation from the spec's `files:` list.