# KBX FE Grid Status Dictionary Hardening v59

## 1. 냉정한 진단

v58까지 `KbxDataGrid`의 `type:'status'`는 사실상 정렬 힌트였다. 가운데 정렬은 했지만 다음을 보장하지 못했다.

- Domain/API raw value와 사용자 Label 분리
- 제품 공통 Status Dictionary
- semantic 상태 색상/텍스트 일관성
- Filter에서 사용자 Label 검색
- CSV Export에서 사용자 Label 출력
- unknown/new 상태 fail-safe
- Demo API와 실제 Backend의 canonical value parity

그 결과 `READY`, `IN_PROGRESS`, `mismatch` 같은 canonical 상태가 화면에 직접 노출되거나, 화면마다 문자열을 별도 번역하는 기술부채가 생길 수 있었다. 이는 KBX Design System의 Domain 상태와 UI Semantic 상태 분리, Status 난립 방지, 색상 이외 상태표시 원칙과 맞지 않는다.

## 2. v59 원칙

```text
Domain/API raw value
        ↓ (변경하지 않음)
KbxGridStatusMap
        ↓
resolveKbxGridStatus()
        ↓
Label + Semantic + Unknown 여부
        ↓
KbxStatus / Filter / CSV
```

UI는 raw value를 mutate하지 않는다. Search/API/Domain contract는 canonical 값을 사용하고 화면 렌더링만 사용자 친화 Label로 변환한다.

## 3. KbxGridStatusMap

`KbxGridColumn`에 `statusMap`을 추가했다.

```ts
interface KbxGridStatusMap {
  definitions: readonly KbxStatusDefinition[]
  unknownLabel?: string
}
```

Application의 `type:'status'` Grid Column은 모두 명시적인 `statusMap`을 가져야 한다. v59 Gate가 이를 전체 `apps/web/src`에서 검사한다.

## 4. 순수 Resolver

`packages/kbx-ui/src/grid/status.ts`에 `resolveKbxGridStatus()`를 추가했다.

역할은 한 가지다.

- 입력 raw value 보존
- dictionary lookup
- 사용자 label 반환
- semantic 반환
- dictionary에 없는 값은 `unknown=true`

unknown 값은 숨기지 않는다.

```text
정의되지 않음 · NEW_VENDOR_STATE
```

으로 노출하고 warning semantic + dashed border를 사용한다. 새로운 Backend 상태가 FE보다 먼저 배포되어도 “그럴듯한 정상 상태”로 위장되지 않는다.

## 5. KbxDataGrid 1.6.0

Status Column은 이제 다음 계약을 가진다.

### Cell

`KbxStatus` renderer 사용.

### Filter

사용자에게 보이는 Label을 대상으로 검색한다. Row raw value는 변경하지 않는다.

### CSV

status는 사용자 Label로 출력한다. 다른 값은 canonical/raw value를 유지한다.

### Unknown

dictionary 미등록 값은 명시적인 unknown 상태로 보여준다.

## 6. KbxStatus 1.1.0

기존 dot-only 의미표현을 보강했다.

- `ready`
- `info`
- `pending`
- `processing`
- `completed`
- `warning`
- `hold`
- `error`
- `cancelled`
- `disabled`

semantic에 따라 Dot + Text + Surface + Border를 함께 사용한다. 색상만으로 의미를 전달하지 않는다.

unknown 상태는 `data-unknown`과 `정의되지 않음` 텍스트를 함께 사용한다.

## 7. 제품 Status Catalog

`apps/web/src/registry/statusCatalog.ts`를 제품 공통 UI Dictionary로 추가했다.

현재 포함:

- OMS 주문 Lifecycle
- OMS 주문 출고상태
- OMS 재고상태
- OMS Claim
- Common Work Severity
- Common Work Item
- Reconcile
- WMS Work
- UX Experiment
- External Data Freshness

업무별 상태를 Shared UI Component에 넣지는 않았다. UI Component는 Dictionary contract만 알고, 제품 App Registry가 업무 상태 집합을 가진다.

## 8. Demo 정상화

기존 OMS Order Demo는 검색용 `status`는 canonical `READY`인데 `shipmentStatus`는 `출고대기`처럼 pre-translated Label이었다.

v59에서는 목록 Projection을 canonical 값으로 정렬했다.

```text
NEW
READY
HOLD
SHIPPED
CONFIRMED
```

사용자 화면에서는 Status Catalog를 통해 각각 한국어 Label로 보인다.

## 9. 실제 화면 적용

다음 상태 Grid가 모두 명시적인 Dictionary를 사용한다.

- OMS 주문관리: 재고 / 출고상태
- OMS 반품·클레임
- COMMON 업무 예외 센터: 중요도 / 처리상태
- COMMON 업무 데이터 대사
- WMS 물류 작업
- UX 실험·점진배포
- 외부 데이터 상태
- Component Catalog Demo

주문 Detail Drawer의 page-local `statusSemantic()`도 제거하고 공통 Dictionary Resolver를 사용하도록 바꿨다.

## 10. Static HTML/JavaScript Reference

순수 FE Reference에도 `statusSemantic()` / `statusChip()`을 추가했다.

T01 주문, T02 품목, T06 WMS Queue, T07 Reconcile에서 text + semantic surface 형태로 상태를 표시한다.

알 수 없는 상태는:

```text
정의되지 않음 · <raw value>
```

으로 보인다.

Forced Colors에서도 border/text cue를 유지한다.

## 11. QA Gate

신규 `validate-fe-grid-status-v59.mjs`가 다음을 검사한다.

1. KbxStatus semantic contract
2. KbxGridStatusMap contract
3. pure resolver 존재
4. unknown fail-visible
5. Grid Cell Renderer
6. Filter label mapping
7. CSV label export
8. product Status Catalog
9. 모든 application `type:'status'` column의 `statusMap`
10. Demo canonical shipment status
11. Order Drawer page-local semantic 제거
12. unit contract
13. Static HTML/JS parity
14. KbxDataGrid/KbxStatus component version

## 12. 전체 검증

최종 전체 정적/계약 회귀:

- Design Tokens: 161
- Screens: 20
- Components: 86
- Core APIs: 34
- TypeScript/Vue script units: 314
- T01~T09: 9/9
- Design Debt: 116 <= 131
- Design-Code Parity: PASS
- Release Governance: major PASS
- v41~v59 FE regression: PASS

## 13. Runtime Evidence 제한

현재 Artifact 환경에는:

- `pnpm-lock.yaml` 없음
- `node_modules` 없음
- Playwright/Vitest 미설치
- npm registry DNS `EAI_AGAIN`

따라서 Vite build/Vitest/Playwright Runtime PASS는 주장하지 않는다. Runtime readiness는 계속 `blocked`다.

## 14. 다음 P0

v59 이후 우선순위는 상태 Component를 더 늘리는 것이 아니다.

### P0-1 — Backend/API Status Canonicalization

OMS 주문 상세 API는 아직 `작성/확정/출고완료` 같은 display 문자열을 반환하고, 주문 검색 API는 canonical code를 반환한다. 같은 Aggregate의 Query 계약이 다르다. 다음 단계에서 API raw status를 canonical code로 통일하고 UI Dictionary가 Label을 전담하는 방향을 검토해야 한다.

### P0-2 — Status Dictionary Governance

새 Domain 상태가 추가될 때:

```text
Domain State
→ API Contract
→ Status Catalog
→ Filter Option
→ E2E Fixture
```

가 함께 변경되는 Gate가 필요하다. 지금 v59 Gate는 Grid statusMap 존재를 강제하지만 Backend enum과 UI dictionary의 완전한 자동 parity까지는 검증하지 않는다.

### P0-3 — 실제 Browser Evidence

Registry 접근 가능한 CI에서 Playwright를 실행해 Light/Dark/forced-colors에서 status label, unknown state, filter, CSV를 실제로 검증해야 한다.
