# KBX FE Workflow Truth · Queue Signal · Runtime Hardening v58

## 1. 냉정한 진단

v57까지 KBX는 T01~T09 Template, Home Workbench, Import Recovery, Reconcile/Operations authoritative outcome, WMS retry/idempotency, Playwright 실행계약까지 상당히 정리되었다. 그러나 실제 FE 코드를 다시 보면 여전히 “문서/정적 Gate는 강한데 Browser Interaction은 틀릴 수 있는” 결함이 남아 있었다.

이번에 발견한 핵심은 네 가지다.

1. **Selection Contract 불일치** — OMS 클레임은 `KbxDataGrid`가 `update:selection`을 제공하지 않는데 `v-model:selection`을 사용하고 있었다. 동시에 `KbxListPage`에 `selectionCount`를 전달하지 않아 Command의 `requiresSelection` 계약도 실제 선택과 연결되지 않았다.
2. **Workflow UI와 실제 Command 불일치** — WorkflowBar는 `start/complete`를 노출하지만 Page command handler는 `approve/hold`만 API로 전송했다. 보이는 Action과 실행 가능한 Action이 달랐다.
3. **Queue Signal 왜곡** — WMS 작업 Queue의 기본 검색이 READY였고 예외 카운터를 현재 rows에서 계산해, 실제 BLOCKED 작업이 존재해도 첫 화면에서 `확인 필요 0`으로 보일 수 있었다.
4. **Vue writable model 위험** — `KbxSearchPanel`은 새 객체를 `update:modelValue`로 반환하는데 일부 Page는 `const search = reactive(...)` 전체 객체를 `v-model`로 넘겼다. 정적 TypeScript Syntax Gate는 통과하지만 Browser에서 const reactive binding 재할당 위험이 있는 패턴이다.

이 네 가지는 모두 “코드량/컴포넌트 수”가 아니라 **실제 업무 실행 Truth** 문제다.

## 2. OMS-CLM-001 — Selection/Workflow Closure

### 기존 문제

- `v-model:selection` + `selection="multiple"`가 동시에 존재
- 실제 `KbxDataGrid`는 `selectionChanged`만 emit
- `KbxListPage`의 selection count는 0으로 남을 수 있음
- WorkflowBar `start/complete`가 보이지만 command handler는 실행하지 않음
- `HOLD`는 Command에는 있으나 Workflow state에는 없음
- 여러 건 승인/보류 중 일부 실패하면 첫 실패에서 전체 흐름이 끊길 수 있음

### v58 변경

- `@selection-changed="selected=$event"`로 실제 Grid 계약 사용
- `:selection-count="selected.length"`를 T01 CommandBar까지 전달
- `approve / hold / start / complete` 전체 Transition을 동일 실행경로로 연결
- `HOLD`를 semantic `hold` 상태로 명시
- Transition별 server result를 기다린 뒤 재조회
- 행별 실패를 수집하고 나머지 행은 계속 처리
- `요청 / 성공 / 실패`를 persistent receipt로 표시
- 처리 후 receipt로 Keyboard Focus 이동

중요한 원칙은 성공 Toast를 늘린 것이 아니라, **선택 → Command → Server → 결과 → 재조회 → Focus**가 한 업무 흐름으로 닫히게 한 것이다.

## 3. Demo Claims — Runtime Fixture를 실제 계약으로 복원

기존 Demo Claims는 실제 Backend DTO와 달랐다.

- `status: '접수'`처럼 한글 표시값을 API value로 사용
- `channelName`, `requestedQty`, `ownerName` 누락
- 모든 transition이 현재 상태와 무관하게 성공

v58은 Demo를 다음 실제 업무 상태로 정렬했다.

`REQUESTED → APPROVED → IN_PROGRESS → COMPLETED`

그리고 `REQUESTED → HOLD`를 별도로 둔다.

허용되지 않은 Transition은 실제 Axios rejection + `business-rule` Problem으로 처리한다. Demo는 보기 좋은 샘플 데이터가 아니라 **Browser Runtime에서 실제 FE 계약을 검증하는 실행 Fixture**여야 한다.

## 4. WMS-WORK-001 — Queue Signal Truth

### 기존 문제

기본 검색조건이 `status='READY'`였다. 따라서 화면 진입 후 사용자가 조회하면 BLOCKED/IN_PROGRESS가 기본적으로 제외된다.

더 큰 문제는 예외 카운터를 `rows.filter(status==='BLOCKED')`로 계산한 것이다. 현재 rows가 READY만 포함하면 실제 예외가 있어도 `0`으로 보인다.

T06 Work Queue가 사용자의 “지금 처리해야 할 업무”를 보여줘야 한다는 목적과 충돌한다.

### v58 변경

- 기본 상태를 전체로 변경
- 화면 진입 시 자동으로 내 작업 조회
- `WmsWorkSearchSummary` 도입
- 상태 필터 적용 전 summary rows에서 `ready / inProgress / blocked` 계산
- Grid count는 현재 필터 결과, Summary/Exception은 동일 기본조건의 전체 업무 압력을 표시
- BLOCKED 선택 시 `작업 시작`을 disabled
- “예외 작업은 행을 열어 원인을 먼저 확인”이라는 다음 행동 표시
- Double Click은 작업 상세 Context 진입을 유지

즉 상태 필터를 좁혀도 “예외가 존재한다”는 운영 신호는 사라지지 않는다.

## 5. SearchPanel Whole-object v-model Runtime Contract

이번에 추가한 Gate는 다음 패턴을 전체 `apps/web/src`에서 금지한다.

```text
const search = reactive(...)
<KbxSearchPanel v-model="search" />
```

`KbxSearchPanel`이 `update:modelValue`로 **새 객체**를 반환하는 구조에서는 writable `ref`가 맞다.

v58에서 수정한 화면:

- OMS Claims
- WMS Work Queue
- COMMON UX Metrics
- ERP Item Master

Component Catalog는 이미 `ref<Record<string,unknown>>`를 사용하고 있어 그대로 유지한다.

이 Gate의 의미는 특정 화면 1건을 고치는 데 있지 않다. **같은 Runtime-class 결함이 다시 추가되는 것을 자동 차단**한다는 데 있다.

## 6. Home — 화면 수의 사실성

Home의 `사용 가능 화면`과 `전체 N개 화면`이 `props.entries.length`를 사용하면 `menuVisible=false`인 Utility/Hidden entry까지 사용자에게 실행 가능한 화면처럼 계산될 수 있다.

v58은 `menuVisible !== false`인 launchable entry만 센다.

작은 숫자 차이처럼 보이지만 Home은 업무 탐색의 Source of Truth다. 숫자가 실제 Explorer와 다르면 사용자는 Navigation 자체를 신뢰하지 않게 된다.

## 7. QA/E2E 보강

추가 E2E:

### OMS Claims

- F3 조회
- 실제 Grid row selection
- WorkflowBar 승인
- authoritative receipt Focus
- APPROVED → 처리 시작
- HOLD 상태 명시 / 다음 Action 없음 확인

### WMS Work Queue

- 화면 진입만으로 전체 내 작업 조회
- BLOCKED 작업이 초기 결과에서 보임
- 예외 요약이 1건을 표시
- 예외 필터 적용
- BLOCKED 작업에서 시작 Action disabled

Runtime package 설치가 가능한 환경에서는 이 시나리오가 기존 Playwright Desktop/WMS project에 그대로 포함된다.

## 8. 검증 결과

`node scripts/validate-kbx.mjs` 전체 PASS.

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

v58 전용 Gate도 PASS.

## 9. Runtime 제한 — 여전히 BLOCKED

현재 container에서 Corepack `pnpm@10.33.4` 실행을 다시 시도했으나 npm registry DNS가 `EAI_AGAIN`으로 실패한다.

따라서 다음은 PASS라고 주장하지 않는다.

- `pnpm install --frozen-lockfile`
- Vue production typecheck/build
- Vitest runtime
- Playwright Desktop/WMS runtime

System Chromium을 사용한 Static Reference `--dump-dom`도 다시 시도했으나 DBus/zygote 환경에서 timeout되었다.

정적/계약 검증 PASS와 Browser Runtime PASS는 계속 분리한다.

## 10. 다음 P0 — Status Grid Rendering

v58 이후 가장 우선순위가 높은 FE Component 부채는 `KbxDataGrid`의 `type:'status'`다.

현재 Grid는 status type을 가운데 정렬하는 수준이며 `KbxStatusDefinition(value/label/semantic)`을 Cell Renderer까지 일관되게 연결하지 않는다. 이 상태에서는 Backend value인 `REQUESTED`, `IN_PROGRESS`, `BLOCKED`가 사용자 화면에 그대로 노출될 가능성이 있다.

다음 단계에서는 새 UI Framework가 아니라:

1. Domain status value → KBX Status Definition mapping
2. Grid status cell에서 Familiar Korean label + semantic cue
3. Filter/Excel export는 canonical value와 표시 label의 역할 분리
4. 미정의 상태는 조용히 오표시하지 않고 Unknown/Fallback 정책 적용
5. OMS/WMS/ERP 상태 Dictionary 중복 제거

를 우선하는 것이 맞다.

이 작업은 미관 개선이 아니라 **Familiar First + Explicit State + Hallucination/오표시 방지**를 Grid 핵심 계약에 내리는 작업이다.
