---
phase: 19-reference-item-model-tags-schema
plan: 02
subsystem: services
tags: [item-service, thread-service, coalesce, reference-items, catalog-link]

requires:
  - phase: 19-reference-item-model-tags-schema
    plan: 01
    provides: globalItemId FK on items and threadCandidates, tags tables
provides:
  - COALESCE merge pattern in item service for transparent reference item data
  - Branched thread resolution (reference vs standalone items)
  - Catalog-linked candidates with merged global item display data
  - Cleaned items route without link/unlink endpoints
affects: [19-03, client-hooks, mcp-tools]

tech-stack:
  added: []
  patterns:
    - "COALESCE merge: LEFT JOIN globalItems with CASE WHEN for name, weight, price, image"
    - "Branched resolution: candidate.globalItemId determines reference vs standalone item creation"

key-files:
  created: []
  modified:
    - src/server/services/item.service.ts
    - src/server/services/thread.service.ts
    - src/server/routes/items.ts
    - tests/services/item.service.test.ts
    - tests/services/thread.service.test.ts

key-decisions:
  - "COALESCE with CASE WHEN pattern ensures standalone items are unaffected by globalItems JOIN"
  - "Reference item resolution omits weight/price/productUrl - those come from global item via COALESCE on read"
  - "Image fallback: item's own imageFilename takes precedence, global imageUrl used as fallback"

patterns-established:
  - "Reference items: service layer transparently merges global data via SQL COALESCE, clients see unified shape"
  - "Branched resolution: resolveThread checks candidate.globalItemId to determine item creation strategy"

requirements-completed: [CATFLOW-03, CATFLOW-04, CATFLOW-05, CATFLOW-06]

duration: 8min
completed: 2026-04-05
---

# Phase 19 Plan 02: Item & Thread Service COALESCE Merge Summary

**COALESCE merge pattern in item/thread services for transparent reference item data, branched thread resolution, and link/unlink endpoint removal**

## Performance

- **Duration:** 8 min
- **Started:** 2026-04-05T18:31:23Z
- **Completed:** 2026-04-05T18:39:00Z
- **Tasks:** 2
- **Files modified:** 5

## Accomplishments

- Item service getAllItems and getItemById use LEFT JOIN + COALESCE to transparently merge global item data for reference items
- createItem accepts globalItemId, looks up global item for brand+model fallback name (items.name is NOT NULL)
- duplicateItem preserves globalItemId and purchasePriceCents from source
- Thread service getThreadWithCandidates merges global item data for catalog-linked candidates
- createCandidate stores globalItemId on candidate row
- resolveThread branches: reference items get globalItemId set with no weight/price copy; standalone items get full data copy
- Removed link/unlink endpoints from items route (replaced by direct globalItemId FK)

## Task Commits

Each task was committed atomically:

1. **Task 1: Item service COALESCE merge + reference item creation + tests** - `d1ffd79` (feat)
2. **Task 2: Thread service candidate globalItemId + branched resolution + route cleanup + tests** - `8a5ee73` (feat)

## Files Created/Modified

- `src/server/services/item.service.ts` - LEFT JOIN globalItems with COALESCE in getAllItems/getItemById, globalItemId in createItem/duplicateItem/updateItem
- `src/server/services/thread.service.ts` - LEFT JOIN globalItems in getThreadWithCandidates, globalItemId in createCandidate, branched resolveThread
- `src/server/routes/items.ts` - Removed link/unlink endpoints and imports of linkItemToGlobal, unlinkItemFromGlobal, linkItemSchema
- `tests/services/item.service.test.ts` - 10 new tests for reference item creation, merged data retrieval, purchasePriceCents
- `tests/services/thread.service.test.ts` - 6 new tests for catalog-linked candidates and branched resolution

## Decisions Made

- Used COALESCE with CASE WHEN pattern (not simple COALESCE) to ensure standalone items are completely unaffected by the LEFT JOIN
- Reference item resolution intentionally omits weight, price, and productUrl from the insert - those come from the global item via COALESCE on read
- Image fallback order: item's own imageFilename first, global item's imageUrl second (user uploads override catalog images)

## Deviations from Plan

None - plan executed exactly as written.

## Known Stubs

None - all data paths are fully wired.

---
*Phase: 19-reference-item-model-tags-schema*
*Completed: 2026-04-05*
