V13-FE-006: consolidate approved UI and contract hardening
This commit is contained in:
+116
@@ -0,0 +1,116 @@
|
||||
# KBX API Contract Governance v14
|
||||
|
||||
## 1. 목적
|
||||
|
||||
API Route, Method, Permission, 성공 Status, Idempotency와 Error discriminator가 Frontend/Backend에서 독립적으로 변하는 문제를 막는다.
|
||||
|
||||
## 2. Source of Truth
|
||||
|
||||
`contracts/api/kbx.api.json`
|
||||
|
||||
Operation은 최소 다음을 가진다.
|
||||
|
||||
```text
|
||||
id
|
||||
method
|
||||
path
|
||||
permission
|
||||
kind
|
||||
idempotency
|
||||
successStatuses
|
||||
```
|
||||
|
||||
`source`는 Reference Foundation의 FastEndpoint 위치를 추적하기 위한 정보다.
|
||||
|
||||
## 3. 생성물
|
||||
|
||||
```text
|
||||
contracts/api/kbx.api.json
|
||||
↓
|
||||
generated/api-manifest.json
|
||||
↓
|
||||
packages/kbx-contracts/src/generated/apiCatalog.ts
|
||||
backend/Shared/Contracts/Generated/KbxApiCatalog.g.cs
|
||||
contracts/api/openapi.kbx.json
|
||||
```
|
||||
|
||||
Source SHA가 TS/C# Catalog에서 일치해야 한다.
|
||||
|
||||
## 4. Web 사용법
|
||||
|
||||
업무 모듈은 다음을 직접 사용하지 않는다.
|
||||
|
||||
```text
|
||||
axios
|
||||
fetch
|
||||
'/api/...'
|
||||
```
|
||||
|
||||
대신:
|
||||
|
||||
```ts
|
||||
kbxApi.request<OrderSearchResponse>(
|
||||
'oms.orders.search',
|
||||
{ query: filter },
|
||||
)
|
||||
```
|
||||
|
||||
화면이 URL과 HTTP Method를 소유하지 않게 한다.
|
||||
|
||||
## 5. Idempotency
|
||||
|
||||
```text
|
||||
none
|
||||
supported
|
||||
required
|
||||
```
|
||||
|
||||
`required` Operation은 안정적인 Idempotency Key 없이 Client가 호출할 수 없다.
|
||||
|
||||
중요: Retry할 때는 새 Key를 생성하지 않고 최초 Key를 재사용한다.
|
||||
|
||||
## 6. FastEndpoints parity
|
||||
|
||||
`validate-api-governance.mjs`는 Backend의 실제:
|
||||
|
||||
```text
|
||||
Get/Post/Put/Patch/Delete
|
||||
Route
|
||||
Permissions(...)
|
||||
```
|
||||
|
||||
을 스캔하고 Contract와 양방향 비교한다.
|
||||
|
||||
- Backend에만 존재 → FAIL
|
||||
- Contract에만 존재 → FAIL
|
||||
- Permission 불일치 → FAIL
|
||||
|
||||
## 7. OpenAPI
|
||||
|
||||
`KbxApiContractOperationFilter`는 실제 Swashbuckle 문서에 다음을 추가한다.
|
||||
|
||||
```text
|
||||
operationId
|
||||
x-kbx-permission
|
||||
x-kbx-idempotency
|
||||
x-kbx-kind
|
||||
```
|
||||
|
||||
`KbxProblemOpenApiOperationFilter`는 표준 Problem response family를 노출한다.
|
||||
|
||||
## 8. DTO Shape 검증
|
||||
|
||||
Reference Foundation에는 실행 가능한 .NET Host/Solution이 없으므로 실제 Request/Response Property Schema 비교는 여기서 통과했다고 간주하지 않는다.
|
||||
|
||||
Host Repository에서는 실제 Swashbuckle OpenAPI를 생성하고 `validate-live-openapi.mjs`를 Mandatory Gate로 사용한다.
|
||||
|
||||
이는 Contract 이름만 비교하고 실제 DTO Shape drift를 놓치는 것을 방지하기 위한 2단계 검증이다.
|
||||
|
||||
## 9. 금지
|
||||
|
||||
- Screen에서 Route 문자열 작성
|
||||
- Screen마다 Axios Error parsing
|
||||
- LLM이 임의 API URL 생성
|
||||
- POST 실패를 무조건 자동 Retry
|
||||
- Idempotency가 없는 강한 Mutation 자동 Retry
|
||||
- 404/403을 화면마다 서로 다른 문구로 해석
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
# v14 API Drift Findings
|
||||
|
||||
v13 Web 호출과 Backend Endpoint를 대조하면서 실제 Drift 2종을 발견했다.
|
||||
|
||||
## 1. OMS 주문 출고지시
|
||||
|
||||
Frontend:
|
||||
|
||||
```text
|
||||
POST /api/oms/orders/ship
|
||||
```
|
||||
|
||||
Backend Endpoint가 없었다.
|
||||
|
||||
v14에서 `oms.orders.ship` Operation과 FastEndpoint를 추가했고, Idempotency-Key 및 `kbx.command_receipts` replay boundary를 추가했다.
|
||||
|
||||
## 2. OMS Claim Workflow
|
||||
|
||||
Frontend:
|
||||
|
||||
```text
|
||||
POST /api/oms/claims/{id}/transitions/{transition}
|
||||
```
|
||||
|
||||
Backend Endpoint가 없었다.
|
||||
|
||||
v14에서는 권한을 명확히 하기 위해 generic dynamic transition URL 대신 다음 네 Operation으로 분해했다.
|
||||
|
||||
```text
|
||||
oms.claims.approve
|
||||
oms.claims.hold
|
||||
oms.claims.start
|
||||
oms.claims.complete
|
||||
```
|
||||
|
||||
각 Endpoint는 자신의 Permission을 명시한다.
|
||||
|
||||
## 결과
|
||||
|
||||
v14 이후 Backend Route와 API Contract는 46개가 1:1로 대응하며, Frontend business code의 raw `/api` 문자열은 0건이다.
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
# KBX v13 — API Field Contract
|
||||
|
||||
## 목표
|
||||
|
||||
Frontend의 `field="orderQty"`, Server Validation의 `field="orderQty"`, OpenAPI 문서의 Field 식별자를 동일하게 유지한다.
|
||||
|
||||
## C# Attribute
|
||||
|
||||
```csharp
|
||||
[property: KbxFieldKey(KbxFieldKeys.OrderQty)] decimal? OrderQty
|
||||
```
|
||||
|
||||
`KbxFieldOpenApiSchemaFilter`는 Swashbuckle Schema에 다음 vendor extension을 추가한다.
|
||||
|
||||
```text
|
||||
x-kbx-field-key
|
||||
x-kbx-field-label
|
||||
x-kbx-sensitive
|
||||
x-kbx-masking
|
||||
```
|
||||
|
||||
등록 예:
|
||||
|
||||
```csharp
|
||||
builder.Services.AddSwaggerGen(options =>
|
||||
{
|
||||
options.AddKbxFieldContract();
|
||||
});
|
||||
```
|
||||
|
||||
## 중요한 경계
|
||||
|
||||
OpenAPI extension은 구조/표시 계약을 제공한다.
|
||||
다음은 OpenAPI Field Metadata로 생성하지 않는다.
|
||||
|
||||
- 업무 상태 전이
|
||||
- 재고 가능 여부
|
||||
- 권한 최종 판정
|
||||
- 동시성 판정
|
||||
- Domain invariant
|
||||
|
||||
## Validation Problem
|
||||
|
||||
Row 오류는 canonical key를 사용한다.
|
||||
|
||||
```json
|
||||
{
|
||||
"field": "orderQty",
|
||||
"rowKey": "line-7",
|
||||
"code": "QUANTITY_POSITIVE",
|
||||
"message": "수량은 0보다 커야 합니다."
|
||||
}
|
||||
```
|
||||
|
||||
DB column이 `quantity`여도 사용자/API 오류 계약의 FieldKey는 `orderQty`를 사용한다.
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
# KBX v10 Application Shell & Navigation
|
||||
|
||||
## 목적
|
||||
|
||||
화면 내부 UX가 표준화되어도 메뉴, 작업 전환, 최근업무, 즐겨찾기, 미저장 화면 종료가 제각각이면 제품은 하나의 시스템처럼 느껴지지 않는다. v10은 Global Header, Side Navigation, Workspace Tabs, 메뉴검색을 하나의 Shell 계약으로 고정한다.
|
||||
|
||||
## 화면 문법
|
||||
|
||||
```text
|
||||
┌──────────────────────────────────────────────────────────────────────┐
|
||||
│ KBX │ OMS │ 메뉴 검색 (Ctrl+K) 작업 2 │ 알림 3 │ 사용자 │
|
||||
├──────────┬───────────────────────────────────────────────────────────┤
|
||||
│ 즐겨찾기 │ 주문관리 × │ 재고현황 × │ 구매등록 ● × │ 더보기 2 │
|
||||
│ 최근메뉴 ├───────────────────────────────────────────────────────────┤
|
||||
│ │ │
|
||||
│ OMS │ 업무 화면 │
|
||||
│ 주문 │ │
|
||||
│ 클레임 │ │
|
||||
│ ERP │ │
|
||||
│ WMS │ │
|
||||
└──────────┴───────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 핵심 규칙
|
||||
|
||||
1. 메뉴검색은 메뉴명, ScreenId, 업무 키워드만 검색한다. 자연어 Command 실행기가 아니다.
|
||||
2. `Ctrl+K`는 가속키이며 상단 메뉴검색 버튼이 항상 존재한다.
|
||||
3. 권한 없는 메뉴는 Side Navigation, 검색, 즐겨찾기, 최근메뉴에서 제외한다.
|
||||
4. Workspace Tab은 `screenId + path`로 식별한다. Router URL과 별도 가상 화면 상태를 만들지 않는다.
|
||||
5. 화면 표시 Tab은 기본 10개이며 초과 화면은 `더보기 N`에 보관한다. 강제 종료하지 않는다.
|
||||
6. Transaction이 dirty인 상태에서 Tab을 닫으면 계속 편집 / 변경 버리기 / 저장 후 이동을 제공한다.
|
||||
7. 즐겨찾기와 최근메뉴는 업무 이동을 단축하는 기능이며 Form Layout 자유편집으로 확장하지 않는다.
|
||||
8. WMS 현장 Task URL은 Side Menu에 직접 노출하지 않는다. `WMS-WORK-001` 작업 Queue에서 실제 Task를 선택해 진입한다.
|
||||
|
||||
## Source of Truth
|
||||
|
||||
- Screen identity: Screen Registry
|
||||
- 메뉴 진입 가능 화면: Navigation Catalog
|
||||
- 현재 URL: Vue Router
|
||||
- 열린 업무: Workspace Store
|
||||
- 화면 저장 여부: 개별 화면 Dirty State + Workspace Lifecycle Registry
|
||||
|
||||
서로 중복 상태를 만들지 않고 명시적으로 연결한다.
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
# Architecture guardrails
|
||||
|
||||
## Must
|
||||
|
||||
- Business modules import `@kbx/ui`, not PrimeVue/AG Grid directly.
|
||||
- Search/list screens use server-side read models.
|
||||
- Bulk selection distinguishes explicit IDs from all-filtered selection.
|
||||
- Domain mutation is server validated; UI state is not authoritative.
|
||||
- Errors are normalized into KBX validation/business/conflict contracts.
|
||||
- Golden Screen keyboard path is covered by Playwright.
|
||||
|
||||
## Must not
|
||||
|
||||
- Build 80-line `gridOptions` in each screen.
|
||||
- Client-join orders/customers/items to produce a business grid.
|
||||
- Trust AI or Zod as final business validation.
|
||||
- Use physical delete for normal transaction cancellation.
|
||||
- Add new UX patterns when an existing KBX component/template already covers the use case.
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
# KBX v15 Authorization & Sensitive Data Governance
|
||||
|
||||
## 경계
|
||||
- Frontend 권한은 메뉴/Command/Workflow의 UX 표현을 결정한다.
|
||||
- Backend는 모든 업무 Command/Query의 최종 권한을 검증한다.
|
||||
- Role은 KBX가 고정하지 않는다. 배포 조직이 Role → Permission을 매핑한다.
|
||||
- 민감정보는 기본 마스킹한다. 전체보기와 비마스킹 Export는 별도 Permission으로 분리한다.
|
||||
- 전체보기는 서버에서 권한을 다시 확인하고 disclosure audit을 남긴다.
|
||||
- AI의 실제 실행권한은 `현재 사용자 권한 ∩ AI 허용 Action`이며 Domain Validation을 대체하지 않는다.
|
||||
|
||||
## 주문 수취인 정책
|
||||
`receiverName`, `phone`, `postalCode`, `address1`, `address2`는 기본 masked이다.
|
||||
- 조회: `oms.order.read`
|
||||
- 전체보기: `oms.order.recipient.unmask`
|
||||
- 비마스킹 Export: `oms.order.recipient.export`
|
||||
- AI: masked-only
|
||||
- Telemetry: 원문 금지
|
||||
|
||||
`KbxMaskedValue`의 전체보기 버튼은 권한의 최종 방어선이 아니다. 원문 데이터는 서버가 권한을 확인한 뒤 반환해야 한다.
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
# Backend integration notes
|
||||
|
||||
## FastEndpoints validation contract
|
||||
|
||||
Register the KBX validation response builder when configuring FastEndpoints:
|
||||
|
||||
```csharp
|
||||
app.UseFastEndpoints(c =>
|
||||
{
|
||||
KbxFastEndpoints.ConfigureErrors(c);
|
||||
});
|
||||
```
|
||||
|
||||
This keeps automatic FluentValidation failures aligned with the frontend `KbxValidationProblem` contract. Row-specific validation remains inside the application handler because a stable UI `rowKey` (`ClientId`) is more useful than an array index such as `Lines[7]`.
|
||||
|
||||
## DbUp
|
||||
|
||||
Run `backend/Database/Migrations/20260808_001_oms_order_golden_screen.sql` through the application's normal DbUp pipeline. The migration is intentionally self-contained for the starter; a production modular monolith should retain schema/table ownership within each module and reference contracts rather than centralizing business tables in a shared module.
|
||||
|
||||
## Transaction boundary
|
||||
|
||||
`RegisterOrderHandler` keeps order header, order lines, audit record and outbox message in one PostgreSQL transaction. External integration dispatch is explicitly outside that transaction.
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
# KBX Component Catalog Acceptance v11
|
||||
|
||||
## 필수 상태
|
||||
입력 계열은 Default, Required, Readonly, Disabled, Error, Keyboard를 재현한다. 장시간 처리 컴포넌트는 Loading을 추가한다.
|
||||
|
||||
## 밀도
|
||||
Visual baseline은 Desktop Compact, Desktop Comfortable, WMS Touch 세 종류를 유지한다.
|
||||
|
||||
## 접근성
|
||||
- 모든 업무 입력은 프로그램적으로 연결된 Label을 가진다.
|
||||
- Error는 색상뿐 아니라 텍스트 및 aria-invalid/aria-describedby로 전달한다.
|
||||
- 상태는 색상만으로 의미를 전달하지 않는다.
|
||||
- Focus Indicator를 제거하지 않는다.
|
||||
- Keyboard 기능이 Mouse-only 기능을 만들지 않는다.
|
||||
|
||||
## 변경 규칙
|
||||
공통 컴포넌트의 의도된 시각 변경은 baseline 갱신 사유와 Component Version 변경을 같은 PR에 남긴다.
|
||||
+196
@@ -0,0 +1,196 @@
|
||||
# KBX v24 Component Interaction Contract
|
||||
|
||||
## 1. 목적
|
||||
|
||||
v23은 Screen Type과 Home/Shell의 구조를 고정했다. v24는 그 화면 안에서 사용자가 실제 입력·조회·검증·복구를 반복할 때 각 Component가 동일한 행동을 보이도록 **runtime interaction contract**를 고정한다.
|
||||
|
||||
## 2. Field State
|
||||
|
||||
공통 상태:
|
||||
|
||||
```text
|
||||
default
|
||||
changed
|
||||
warning
|
||||
ai-suggested
|
||||
error
|
||||
readonly
|
||||
disabled
|
||||
```
|
||||
|
||||
우선순위:
|
||||
|
||||
```text
|
||||
Error
|
||||
> Warning
|
||||
> Changed / AI Suggested
|
||||
> Default
|
||||
```
|
||||
|
||||
Readonly와 Disabled는 상태 의미가 다르다. Readonly는 업무값을 확인·복사하는 상태이고 Disabled는 현재 업무조건에서 상호작용 자체가 허용되지 않는 상태다.
|
||||
|
||||
적용 Component:
|
||||
|
||||
- KbxInput / KbxTextField
|
||||
- KbxNumberField / KbxMoneyField / KbxQuantityField
|
||||
- KbxDateField / KbxDateRange
|
||||
- KbxSelect
|
||||
- KbxLookup
|
||||
- KbxCheckbox / KbxRadio
|
||||
- KbxTextarea
|
||||
- KbxBarcodeField
|
||||
|
||||
AI Suggested는 확정값으로 보이지 않도록 별도 semantic state를 사용하며, Error/Warning이 발생하면 AI 상태보다 검증 결과를 우선한다.
|
||||
|
||||
## 3. Async State
|
||||
|
||||
`KbxDataState`가 다음을 공통 처리한다.
|
||||
|
||||
```text
|
||||
Loading
|
||||
Empty
|
||||
Error
|
||||
Recovery Action
|
||||
```
|
||||
|
||||
첫 조회 Loading은 결과 대신 상태를 표시한다. 기존 데이터가 있는 재조회는 데이터를 지우지 않고 `재조회 중...`을 보조적으로 표시한다.
|
||||
|
||||
Error는 해결이 필요한 상태이므로 사라지는 Toast만 사용하지 않는다.
|
||||
|
||||
## 4. Grid Runtime
|
||||
|
||||
`KbxDataGrid`는 AG Grid wrapper가 아니라 업무 Grid contract다.
|
||||
|
||||
v24 보강:
|
||||
|
||||
- Loading / Empty / Error
|
||||
- 기존 데이터 유지 재조회
|
||||
- Changed Cell
|
||||
- Invalid Cell + message tooltip
|
||||
- Selection count
|
||||
- Cell error count
|
||||
- CSV export
|
||||
- Summary count/sum/custom
|
||||
- F2 Lookup
|
||||
- Enter editing navigation
|
||||
- Column resize/move/pin/sort preference emit
|
||||
|
||||
### Preference 경계
|
||||
|
||||
금지:
|
||||
|
||||
```text
|
||||
Vertical Slice
|
||||
-> AG Grid ColumnState 저장
|
||||
```
|
||||
|
||||
권장:
|
||||
|
||||
```text
|
||||
AG Grid state
|
||||
↓ adapter
|
||||
KbxGridColumnPreference
|
||||
↓
|
||||
Tenant/User/Screen/ScreenVersion Preference Store
|
||||
```
|
||||
|
||||
`KbxDataGrid`는 `preferenceChanged`, `preferenceReset`을 emit하고 실제 영속화 정책은 Application Preference 계층이 담당한다.
|
||||
|
||||
## 5. Search Panel
|
||||
|
||||
v24부터 `KbxSearchFieldType`은 다음을 모두 지원한다.
|
||||
|
||||
```text
|
||||
text
|
||||
date
|
||||
date-range
|
||||
select
|
||||
lookup
|
||||
checkbox
|
||||
```
|
||||
|
||||
Reset은 무조건 null로 지우지 않고 Field의 `defaultValue`를 복원한다.
|
||||
|
||||
추가 UX:
|
||||
|
||||
- 상세조건 active count
|
||||
- 마지막 조회조건 기억
|
||||
- 저장 조건 진입점
|
||||
- Checkbox 상세조건
|
||||
|
||||
Preference 영속화는 SearchPanel 내부 LocalStorage가 아니라 사용자 Preference 계층으로 연결한다.
|
||||
|
||||
## 6. Keyboard Manager
|
||||
|
||||
전역 단축키를 화면마다 window keydown으로 등록하지 않는다.
|
||||
|
||||
우선순위:
|
||||
|
||||
```text
|
||||
Editor
|
||||
↓
|
||||
Dialog
|
||||
↓
|
||||
Grid
|
||||
↓
|
||||
Page
|
||||
↓
|
||||
Application
|
||||
```
|
||||
|
||||
보호:
|
||||
|
||||
```text
|
||||
F5
|
||||
Ctrl+L
|
||||
Ctrl+T
|
||||
Ctrl+W
|
||||
Ctrl+R
|
||||
```
|
||||
|
||||
Scanner keyboard-wedge capture는 일반 shortcut과 다른 입력 lifecycle이므로 `KbxBarcodeCapture` 내부 capture listener를 허용한다.
|
||||
|
||||
## 7. Tabs
|
||||
|
||||
`KbxTabs`는 native Tab 이동만으로 끝나지 않는다.
|
||||
|
||||
```text
|
||||
ArrowLeft
|
||||
ArrowRight
|
||||
Home
|
||||
End
|
||||
```
|
||||
|
||||
으로 활성 Tab과 focus를 같이 이동한다. `aria-controls`, `tabpanel`, roving tabindex를 유지한다.
|
||||
|
||||
## 8. Component Catalog
|
||||
|
||||
Catalog는 Component 이름 목록이 아니라 독립 재현 환경이다.
|
||||
|
||||
v24에서 실제 화면으로 재현하는 상태:
|
||||
|
||||
- Changed
|
||||
- Warning
|
||||
- AI Suggested
|
||||
- Loading
|
||||
- Empty
|
||||
- Error
|
||||
- Search + checkbox/remember
|
||||
- Grid changed/error/summary/personalization/export
|
||||
- Tabs keyboard
|
||||
|
||||
## 9. 기술부채 Ratchet
|
||||
|
||||
v23 최종 design debt literal count:
|
||||
|
||||
```text
|
||||
429
|
||||
```
|
||||
|
||||
v24:
|
||||
|
||||
```text
|
||||
366
|
||||
```
|
||||
|
||||
이번 수치를 새 baseline으로 고정하여 이후 변경은 366을 다시 넘을 수 없다.
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
# KBX Component Versioning v11
|
||||
|
||||
KBX 공통 Component 변경은 화면별 임의 수정 대신 Component Version으로 식별한다.
|
||||
|
||||
- Major: 기존 업무 사용방법 또는 Public API를 깨는 변경
|
||||
- Minor: 호환 가능한 기능/상태 추가
|
||||
- Patch: 사용방법을 바꾸지 않는 결함 수정
|
||||
|
||||
Visual baseline 변경만으로 자동 Major로 올리는 것은 아니다. **사용자가 학습한 동작 계약이 바뀌는지**를 먼저 판단한다.
|
||||
|
||||
운영 재현 시 ScreenVersion과 Component Manifest Version을 함께 확인할 수 있어야 한다.
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
# KBX Configuration Governance v22
|
||||
|
||||
## 목적
|
||||
|
||||
설정도 코드와 동일한 계약으로 취급한다. 개발자의 로컬 설정이 운영 의미를 결정하거나, 운영 Secret이 Repository에 들어가거나, 동일 Release가 환경별로 다른 방식으로 빌드되는 상태를 방지한다.
|
||||
|
||||
## 경계
|
||||
|
||||
- **Static configuration**: DB 연결, Hangfire/SignalR 사용여부, Provider credential reference, Observability endpoint, 업로드 한도처럼 배포/프로세스 생명주기에 속하는 값.
|
||||
- **Dynamic runtime state**: Experiment rollout/kill switch, Runtime incident/read-only 상태처럼 이미 DB 기반 관리 계약이 있는 값. 이 값을 다시 appsettings로 복제하지 않는다.
|
||||
- **Domain rule**: 재고부족, 출고 가능 상태, 가격/수량 규칙. Configuration으로 우회하지 않는다.
|
||||
|
||||
## Source precedence
|
||||
|
||||
`defaults → appsettings → environment-specific appsettings → environment → secret-store → command-line`
|
||||
|
||||
Secret은 환경변수/User Secrets/Secret Store 경로만 허용한다. Repository 예제에는 실제 값을 넣지 않는다.
|
||||
|
||||
## Fail fast
|
||||
|
||||
`KbxConfigurationStartupValidator`는 첫 업무 요청 전에 Required/Conditional Required/Enum/Integer Range/URI/Production Safety를 검증한다. 오류 메시지에는 Secret 값을 출력하지 않는다.
|
||||
|
||||
## Configuration fingerprint
|
||||
|
||||
운영 재현용 fingerprint는 **non-secret 설정만 canonical order로 SHA-256** 한다. 이 값은 배포/로그에 남길 수 있지만 Secret fingerprint나 Secret value를 포함하지 않는다.
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
# KBX Configuration Reference
|
||||
|
||||
Source SHA-256: `e09186d626b034a524b7cdf78f1e4bb6adbc112975b3baf4fd6194b1c1abd771`
|
||||
|
||||
Settings: **33** · Environments: **4**
|
||||
|
||||
| Key | Env | Category | Type | Secret | Restart |
|
||||
|---|---|---|---|---:|---:|
|
||||
| `Kbx:Runtime:Environment` | `KBX__Runtime__Environment` | runtime | enum | no | yes |
|
||||
| `Kbx:Runtime:ReadOnly` | `KBX__Runtime__ReadOnly` | runtime | boolean | no | no |
|
||||
| `ConnectionStrings:Main` | `ConnectionStrings__Main` | database | connection-string | yes | yes |
|
||||
| `Kbx:Database:MigrationsMode` | `KBX__Database__MigrationsMode` | database | enum | no | yes |
|
||||
| `Kbx:Database:CommandTimeoutSeconds` | `KBX__Database__CommandTimeoutSeconds` | database | integer | no | yes |
|
||||
| `Kbx:Hangfire:Enabled` | `KBX__Hangfire__Enabled` | background-jobs | boolean | no | yes |
|
||||
| `Kbx:Hangfire:WorkerCount` | `KBX__Hangfire__WorkerCount` | background-jobs | integer | no | yes |
|
||||
| `Kbx:SignalR:Enabled` | `KBX__SignalR__Enabled` | realtime | boolean | no | yes |
|
||||
| `Kbx:Outbox:DispatcherEnabled` | `KBX__Outbox__DispatcherEnabled` | messaging | boolean | no | yes |
|
||||
| `Kbx:Inbox:CleanupEnabled` | `KBX__Inbox__CleanupEnabled` | messaging | boolean | no | yes |
|
||||
| `Kbx:Integration:DispatcherEnabled` | `KBX__Integration__DispatcherEnabled` | integration | boolean | no | yes |
|
||||
| `Kbx:ExternalData:RefreshEnabled` | `KBX__ExternalData__RefreshEnabled` | external-data | boolean | no | yes |
|
||||
| `Kbx:ExternalData:ObservationRetentionEnabled` | `KBX__ExternalData__ObservationRetentionEnabled` | external-data | boolean | no | yes |
|
||||
| `Kbx:Experiments:Enabled` | `KBX__Experiments__Enabled` | experiments | boolean | no | no |
|
||||
| `Kbx:Logging:MinimumLevel` | `KBX__Logging__MinimumLevel` | logging | enum | no | yes |
|
||||
| `Kbx:Logging:JsonConsoleEnabled` | `KBX__Logging__JsonConsoleEnabled` | logging | boolean | no | yes |
|
||||
| `Kbx:Telemetry:Enabled` | `KBX__Telemetry__Enabled` | observability | boolean | no | yes |
|
||||
| `Kbx:Telemetry:OtlpEndpoint` | `KBX__Telemetry__OtlpEndpoint` | observability | uri | no | yes |
|
||||
| `Kbx:Telegram:Enabled` | `KBX__Telegram__Enabled` | alerting | boolean | no | yes |
|
||||
| `Kbx:Telegram:BotToken` | `KBX__Telegram__BotToken` | alerting | string | yes | yes |
|
||||
| `Kbx:Telegram:ChatId` | `KBX__Telegram__ChatId` | alerting | string | yes | yes |
|
||||
| `ExternalProviders:Krx:Enabled` | `ExternalProviders__Krx__Enabled` | provider | boolean | no | yes |
|
||||
| `ExternalProviders:Krx:AuthKey` | `ExternalProviders__Krx__AuthKey` | provider | string | yes | yes |
|
||||
| `ExternalProviders:OpenDart:Enabled` | `ExternalProviders__OpenDart__Enabled` | provider | boolean | no | yes |
|
||||
| `ExternalProviders:OpenDart:ApiKey` | `ExternalProviders__OpenDart__ApiKey` | provider | string | yes | yes |
|
||||
| `ExternalProviders:Kis:Enabled` | `ExternalProviders__Kis__Enabled` | provider | boolean | no | yes |
|
||||
| `ExternalProviders:Kis:Environment` | `ExternalProviders__Kis__Environment` | provider | enum | no | yes |
|
||||
| `ExternalProviders:Kis:AppKey` | `ExternalProviders__Kis__AppKey` | provider | string | yes | yes |
|
||||
| `ExternalProviders:Kis:AppSecret` | `ExternalProviders__Kis__AppSecret` | provider | string | yes | yes |
|
||||
| `Kbx:Security:RequireHttps` | `KBX__Security__RequireHttps` | security | boolean | no | yes |
|
||||
| `Kbx:Health:ReadinessEnabled` | `KBX__Health__ReadinessEnabled` | runtime | boolean | no | yes |
|
||||
| `Kbx:Import:MaxUploadBytes` | `KBX__Import__MaxUploadBytes` | import | integer | no | yes |
|
||||
| `Kbx:Import:MaxRows` | `KBX__Import__MaxRows` | import | integer | no | yes |
|
||||
|
||||
## Environment profiles
|
||||
|
||||
### Development
|
||||
- Migration: `startup-apply-allowed`
|
||||
- Provider network: `opt-in`
|
||||
- HTTPS required: `false`
|
||||
- Endpoint override allowed: `true`
|
||||
- Gates: `static-governance`
|
||||
### Test
|
||||
- Migration: `ephemeral-apply`
|
||||
- Provider network: `forbidden`
|
||||
- HTTPS required: `false`
|
||||
- Endpoint override allowed: `true`
|
||||
- Gates: `static-governance`, `build`, `unit`, `integration`, `scenario`
|
||||
### Staging
|
||||
- Migration: `predeploy`
|
||||
- Provider network: `opt-in`
|
||||
- HTTPS required: `true`
|
||||
- Endpoint override allowed: `false`
|
||||
- Gates: `static-governance`, `build`, `unit`, `integration`, `scenario`, `migration-dry-run`
|
||||
### Production
|
||||
- Migration: `predeploy`
|
||||
- Provider network: `opt-in`
|
||||
- HTTPS required: `true`
|
||||
- Endpoint override allowed: `false`
|
||||
- Gates: `static-governance`, `build`, `unit`, `integration`, `scenario`, `migration-dry-run`, `configuration-validation`, `release-governance`
|
||||
+340
@@ -0,0 +1,340 @@
|
||||
# KBX Foundation v34
|
||||
## Core Component Recovery · Permission · Home Navigation Hardening
|
||||
|
||||
## 1. 목적
|
||||
|
||||
v34는 T01~T09 Screen Template 자체의 상태 계약을 확장한 v33 다음 단계다. 목표는 Template Manifest에 선언된 핵심 Business Component가 실제 운영 수준의 오류·복구·권한·접근성 계약을 갖도록 하고, Home Workbench의 탐색 상태가 권한/카탈로그 변경에도 일관되도록 만드는 것이다.
|
||||
|
||||
핵심 원칙은 다음과 같다.
|
||||
|
||||
- Template은 Core Component 이름만 나열하지 않고 실제 Manifest와 독립 Catalog Scenario까지 폐쇄적으로 연결한다.
|
||||
- Component 내부 비동기/오류 상태를 화면 Slice가 매번 재구현하지 않는다.
|
||||
- Frontend Permission은 UX 표현과 defense-in-depth를 담당하고 Backend Permission/Domain Validation을 대체하지 않는다.
|
||||
- Import/AI/WMS 입력은 Client 편의 검증 뒤에도 Server validation, idempotency, staging, domain rule을 최종 기준으로 둔다.
|
||||
- Home은 열린 업무/고정 업무를 중복 집계하지 않고, Permission이나 Catalog 변경으로 사라진 Module filter를 안전하게 복구한다.
|
||||
|
||||
---
|
||||
|
||||
## 2. T01~T09 Core Component Closure
|
||||
|
||||
`kbxTemplateManifest.coreComponents`에 포함되는 모든 Component는 다음 세 조건을 만족해야 한다.
|
||||
|
||||
```text
|
||||
Template Manifest
|
||||
↓
|
||||
Component Manifest
|
||||
↓
|
||||
Component Catalog Scenario
|
||||
```
|
||||
|
||||
v34 Governance Gate는 세 계층 중 하나라도 누락되면 실패한다.
|
||||
|
||||
v33 기준으로 독립 Catalog Scenario가 없던 다음 Core Component를 추가했다.
|
||||
|
||||
- KbxLookupDialog
|
||||
- KbxValidationSummary
|
||||
- KbxExceptionSummary
|
||||
- KbxExceptionDetailDrawer
|
||||
- KbxRecordLifecycle
|
||||
- KbxBarcodeCapture
|
||||
- KbxNetworkIndicator
|
||||
- KbxWmsActionButton
|
||||
|
||||
AI/Utility의 운영 상태 재현성을 위해 다음 Scenario도 보강했다.
|
||||
|
||||
- KbxAiAssistant
|
||||
- KbxUtilityRail
|
||||
|
||||
이로써 "Template 계약에는 존재하지만 독립 상태를 재현할 수 없는 Component"를 허용하지 않는다.
|
||||
|
||||
---
|
||||
|
||||
## 3. Lookup 고도화
|
||||
|
||||
### 3.1 비동기 응답 역전 방지
|
||||
|
||||
Lookup 입력은 사용자가 빠르게 값을 바꾸는 경우 먼저 보낸 요청이 나중에 도착할 수 있다. v34는 request sequence를 사용하여 오래된 응답이 최신 입력을 덮어쓰지 못하게 한다.
|
||||
|
||||
```text
|
||||
A 검색 요청
|
||||
B 검색 요청
|
||||
B 응답 반영
|
||||
A 응답 도착 → 폐기
|
||||
```
|
||||
|
||||
### 3.2 상태 표준화
|
||||
|
||||
Lookup Dialog가 다음 상태를 직접 책임진다.
|
||||
|
||||
- Provider unavailable
|
||||
- Loading
|
||||
- Error
|
||||
- Empty
|
||||
- Ready
|
||||
- Paging
|
||||
- Selected row
|
||||
|
||||
오류 후 `다시 조회`가 가능하며, 결과가 없을 때 빈 Grid처럼 보이지 않고 명시적인 Empty 상태를 제공한다.
|
||||
|
||||
### 3.3 Keyboard / Accessibility
|
||||
|
||||
- Arrow Up/Down: 결과 이동
|
||||
- Enter: 선택
|
||||
- Esc: 닫기
|
||||
- 선택 후 원래 Input으로 Focus restore
|
||||
- `aria-selected`
|
||||
- 선택 Row 자동 Scroll
|
||||
|
||||
### 3.4 Provider Metadata Column
|
||||
|
||||
Lookup별 결과 컬럼을 페이지가 직접 Table로 만들지 않도록 `KbxLookupColumnDefinition`을 추가했다.
|
||||
|
||||
지원 source:
|
||||
|
||||
- code
|
||||
- displayName
|
||||
- secondaryText
|
||||
- status
|
||||
- metadata.*
|
||||
|
||||
### 3.5 Primitive 경계
|
||||
|
||||
`KbxLookupDialog`는 PrimeVue Dialog/Button을 직접 import하지 않고 `KbxDialog`, `KbxButton`을 조합한다. Vertical Slice뿐 아니라 Business Component 내부에서도 가능한 한 KBX Primitive 계층을 유지한다.
|
||||
|
||||
---
|
||||
|
||||
## 4. Excel Import 고도화
|
||||
|
||||
### 4.1 Session Isolation
|
||||
|
||||
Import Session이 변경되면 이전 Session의 local mapping을 초기화한다. 서로 다른 파일/업무의 Mapping이 조용히 섞이는 것을 차단한다.
|
||||
|
||||
### 4.2 Client Preflight
|
||||
|
||||
서버 검증 이전에 사용자 피드백을 빠르게 제공하기 위해 다음을 확인한다.
|
||||
|
||||
- `.xlsx` 확장자
|
||||
- maxFileSize
|
||||
- Required field mapping 누락
|
||||
- 동일 Target field 중복 Mapping
|
||||
|
||||
Client preflight는 편의 검증이며 Server staging/business validation을 대체하지 않는다.
|
||||
|
||||
### 4.3 Mapping 1:1 계약
|
||||
|
||||
동일 System Field에 여러 Excel Column이 연결된 경우 Commit 전에 오류로 표시한다. Required import field가 연결되지 않은 경우도 Commit을 차단한다.
|
||||
|
||||
### 4.4 Commit 확인
|
||||
|
||||
대량 반영은 `KbxConfirm` high-risk confirmation을 거친다. 반영 전 신규/수정/정상/오류 범위를 사용자가 확인할 수 있는 기존 Import 정책을 유지한다.
|
||||
|
||||
### 4.5 Terminal Result
|
||||
|
||||
다음을 구분한다.
|
||||
|
||||
- Completed
|
||||
- PartiallyCompleted
|
||||
- Failed
|
||||
- Cancelled
|
||||
|
||||
부분 완료는 성공 건과 제외된 오류 건을 분리해서 설명하고 오류 데이터 재처리 경로를 제공한다. 실패 시 `KbxImportFailure`의 code/title/detail을 표현할 수 있다.
|
||||
|
||||
---
|
||||
|
||||
## 5. Exception Action Permission
|
||||
|
||||
`KbxExceptionDetailDrawer`를 `KbxDrawer + KbxButton` 조합으로 정리하고 Permission Host를 자동 소비한다.
|
||||
|
||||
Action은 다음 정책을 지원한다.
|
||||
|
||||
```text
|
||||
permissionMode = hide
|
||||
permissionMode = disable
|
||||
```
|
||||
|
||||
Permission Host가 없는데 permission이 명시된 Action은 안전한 방향으로 실행을 허용하지 않는다.
|
||||
|
||||
화면 Shell의 접근권한과 Exception Drawer 내부 Action 권한이 분리되어 누락되는 문제를 줄인다.
|
||||
|
||||
---
|
||||
|
||||
## 6. AI Guard 고도화
|
||||
|
||||
### 6.1 Proposal Permission
|
||||
|
||||
`KbxProposalPanel`은 proposal의 `requiredPermission`을 Permission Host로 확인한다.
|
||||
|
||||
### 6.2 Proposal Validation State
|
||||
|
||||
AI Proposal은 다음 상태를 가질 수 있다.
|
||||
|
||||
```text
|
||||
pending
|
||||
validated
|
||||
invalid
|
||||
stale
|
||||
```
|
||||
|
||||
validation 정보가 존재하는 Proposal은 `validated` 상태에서만 Apply가 가능하다. AI confidence를 실행 권한이나 Domain validation의 대체값으로 사용하지 않는다.
|
||||
|
||||
### 6.3 Answer Action Capability / Permission
|
||||
|
||||
AI 답변에 포함된 Action은 각각:
|
||||
|
||||
- requiredCapability
|
||||
- requiredPermission
|
||||
|
||||
을 확인한다.
|
||||
|
||||
AI가 설명할 수 있다는 이유로 사용자가 실행할 수 없는 Action을 노출/실행하지 않는다.
|
||||
|
||||
### 6.4 User-facing Context 최소화
|
||||
|
||||
AI Panel은 내부 `screenId`를 기본 사용자 문구로 직접 노출하지 않고 `currentScreenLabel`을 사용한다. ScreenId는 Grounding/Telemetry용 내부 식별자로 유지한다.
|
||||
|
||||
### 6.5 Error Recovery
|
||||
|
||||
AI 질문 실패 시 일반 오류 상태를 표시하고 마지막 질문을 다시 시도할 수 있다. 네트워크 오류를 Answer처럼 취급하지 않는다.
|
||||
|
||||
---
|
||||
|
||||
## 7. WMS Barcode Capture 고도화
|
||||
|
||||
### 7.1 Bounded Buffer
|
||||
|
||||
Scanner keyboard wedge가 비정상적으로 긴 데이터를 보내더라도 buffer를 무제한 확장하지 않는다.
|
||||
|
||||
기본 maxLength:
|
||||
|
||||
```text
|
||||
256
|
||||
```
|
||||
|
||||
### 7.2 Duplicate Debounce
|
||||
|
||||
동일 Scanner가 극히 짧은 간격으로 같은 Barcode를 중복 전송하는 장비 bounce를 Client에서 억제한다.
|
||||
|
||||
기본 debounce:
|
||||
|
||||
```text
|
||||
180ms
|
||||
```
|
||||
|
||||
하지만 일정 시간이 지난 동일 Barcode는 정상 재스캔이므로 허용한다.
|
||||
|
||||
이 처리는 UX 보조이며 Server Idempotency가 최종 중복 방어선이다.
|
||||
|
||||
### 7.3 입력 Source
|
||||
|
||||
- keyboard-wedge
|
||||
- manual
|
||||
- camera (`submitCamera` entry)
|
||||
|
||||
를 동일 Barcode Event 계약으로 연결한다.
|
||||
|
||||
### 7.4 Context Reset
|
||||
|
||||
- Esc: 현재 buffer 초기화
|
||||
- visibility change: 숨겨진 화면의 미완성 scanner buffer 제거
|
||||
- exposed `reset()`
|
||||
|
||||
백그라운드/화면전환 후 오래된 Scanner 조각이 다음 Barcode와 합쳐지는 위험을 낮춘다.
|
||||
|
||||
---
|
||||
|
||||
## 8. Home Workbench / Navigation
|
||||
|
||||
### 8.1 Open / Pinned 중복 제거
|
||||
|
||||
Home Source Summary에서 `open`은 `!pinned` Workspace만 집계한다.
|
||||
|
||||
```text
|
||||
Pinned 3
|
||||
Open 5
|
||||
```
|
||||
|
||||
가 실제로 서로 다른 집합이 되며, 고정 탭을 열린 업무에도 다시 집계하지 않는다.
|
||||
|
||||
### 8.2 Permission / Catalog Change Recovery
|
||||
|
||||
현재 선택된 Module이 권한 변경이나 화면 Catalog 변경으로 더 이상 존재하지 않으면 Module filter를 자동으로 `ALL`로 복구한다.
|
||||
|
||||
숨겨진 Module filter 때문에 Home이 빈 화면처럼 남는 상태를 방지한다.
|
||||
|
||||
### 8.3 검색 결과 접근성
|
||||
|
||||
현재 Module 조건에서 접근 가능한 화면 수를 `aria-live`로 제공한다.
|
||||
|
||||
Home은 기존 v31~v33의 다음 Navigation hardening을 유지한다.
|
||||
|
||||
- multi-instance workspace key
|
||||
- pinned tab capacity protection
|
||||
- tab roving focus
|
||||
- Ctrl+K menu search
|
||||
- focus restore
|
||||
- skip-to-content
|
||||
- recent route sanitization
|
||||
- encoded traversal/backslash/control rejection
|
||||
- query/hash 제거 후 recent persistence
|
||||
- preference size/field limit
|
||||
|
||||
---
|
||||
|
||||
## 9. Design Debt
|
||||
|
||||
v33 baseline:
|
||||
|
||||
```text
|
||||
268
|
||||
```
|
||||
|
||||
v34 구현 후:
|
||||
|
||||
```text
|
||||
131
|
||||
```
|
||||
|
||||
Lookup/Import/AI/WMS Component의 hard-coded spacing/width를 semantic/component token으로 이동한 결과다.
|
||||
|
||||
v34 release에서는 131을 새 ratchet baseline으로 설정하여 이후 132 이상으로 다시 증가하면 Governance Gate가 실패하도록 한다.
|
||||
|
||||
---
|
||||
|
||||
## 10. 변경하지 않은 경계
|
||||
|
||||
v34는 다음을 UI Framework 내부로 끌어오지 않는다.
|
||||
|
||||
- Domain business rule
|
||||
- Backend permission enforcement
|
||||
- Import staging/business validation
|
||||
- AI entity/domain validation
|
||||
- WMS server idempotency
|
||||
- Transaction concurrency source of truth
|
||||
|
||||
즉 KBX는 사용방법과 오류복구 UX를 표준화하고 Domain/Backend가 가능한 업무와 최종 정합성을 결정한다.
|
||||
|
||||
---
|
||||
|
||||
## 11. 다음 검증 단계
|
||||
|
||||
Repository Governance Gate와 별도로 실환경에서 다음을 수행해야 한다.
|
||||
|
||||
1. Vitest Component Contract
|
||||
- Lookup race/error/paging/focus restore
|
||||
- Import mapping guard/session reset/partial result
|
||||
- Proposal permission/validation
|
||||
- Barcode duplicate/buffer/camera
|
||||
|
||||
2. Playwright
|
||||
- T01 lookup/search/error recovery
|
||||
- T03 transaction dirty/conflict/save
|
||||
- T08 import mapping → validation → commit → partial result
|
||||
- T09 scan duplicate/network recovery
|
||||
- Home permission change/module filter recovery
|
||||
|
||||
3. .NET/PostgreSQL Integration
|
||||
- Permission enforcement
|
||||
- Import staging
|
||||
- Idempotency
|
||||
- Outbox/Audit
|
||||
- AI Proposal domain revalidation
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
# KBX Deployment Runbook v22
|
||||
|
||||
## 1. Build once
|
||||
|
||||
Commit SHA 하나에서 Release Artifact를 한 번만 빌드한다. Staging 통과 후 Production을 별도로 rebuild하지 않는다.
|
||||
|
||||
## 2. Pre-deploy gates
|
||||
|
||||
1. `node scripts/validate-kbx.mjs`
|
||||
2. frontend/backend build + unit/integration tests
|
||||
3. canonical scenario evidence
|
||||
4. target-environment configuration binding + `ValidateOrThrow()`
|
||||
5. DbUp migration validation/dry-run
|
||||
6. Release Impact 확인
|
||||
|
||||
## 3. Migration
|
||||
|
||||
Production은 `Kbx:Database:MigrationsMode=predeploy`만 허용한다. Schema 변경 실패를 애플리케이션 첫 요청에서 발견하지 않는다.
|
||||
|
||||
Destructive SQL은 기본 금지하며 정말 필요한 경우 `KBX-DESTRUCTIVE-MIGRATION-APPROVED: <ticket/ADR>` marker와 Migration Guide가 필요하다. Expand → backfill → contract의 단계적 변경을 우선한다.
|
||||
|
||||
## 4. Deploy
|
||||
|
||||
환경별 설정/Secret을 외부 주입하고 동일 Artifact를 시작한다. 시작 시 configuration fingerprint와 KBX contract version을 기록한다. Secret 값 자체는 기록하지 않는다.
|
||||
|
||||
## 5. Post-deploy
|
||||
|
||||
- readiness/로그/OTel 확인
|
||||
- Outbox/Inbox/Hangfire backlog 확인
|
||||
- Runtime banner/Exception Center 확인
|
||||
- Golden scenario smoke 실행
|
||||
- 심각한 이상이면 Release rollback 또는 runtime read-only/kill switch 정책 사용
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
# KBX v12 — Design ↔ Code Parity
|
||||
|
||||
## 목적
|
||||
|
||||
KBX의 Design System과 Vue 구현이 시간이 지나면서 서로 다른 이름·상태·Token을 갖는 것을 방지한다.
|
||||
|
||||
## Source of Truth
|
||||
|
||||
```text
|
||||
Design Token JSON
|
||||
↓
|
||||
CSS Variables
|
||||
↓
|
||||
Token Manifest
|
||||
↓
|
||||
Figma Variable Contract
|
||||
```
|
||||
|
||||
Component는:
|
||||
|
||||
```text
|
||||
Component Manifest
|
||||
+ Component Catalog
|
||||
↓
|
||||
Figma Component Contract
|
||||
```
|
||||
|
||||
으로 연결한다.
|
||||
|
||||
## Token 경계
|
||||
|
||||
- Primitive: 원시 spacing/color/typography/radius
|
||||
- Semantic: surface/text/border/action/status/focus
|
||||
- Component: control/grid/header/label 등 실제 UI 계약
|
||||
|
||||
Component CSS는 가능하면 Semantic/Component Token을 소비한다.
|
||||
새 raw hex/px literal은 기본적으로 기술부채 증가로 간주한다.
|
||||
|
||||
## Design Debt Ratchet
|
||||
|
||||
기존 코드에는 historical hardcoded value가 남아 있다.
|
||||
이를 v12에서 일괄 변경하면 Visual Regression 위험이 크므로 기존 수치를 baseline으로 고정한다.
|
||||
|
||||
```text
|
||||
현재 부채 <= baseline : PASS
|
||||
현재 부채 > baseline : FAIL
|
||||
새 파일의 hardcode : baseline 0에서 시작
|
||||
```
|
||||
|
||||
즉 기존 부채는 리팩토링할 때 감소시키고, 새로운 부채는 늘리지 않는다.
|
||||
|
||||
## Core Component parity
|
||||
|
||||
Design System v1.0의 34개 1차 필수 Component는 모두:
|
||||
|
||||
- Public component manifest
|
||||
- Component Catalog
|
||||
- Figma component contract
|
||||
|
||||
에 존재해야 한다.
|
||||
|
||||
## 금지
|
||||
|
||||
- Figma에서만 존재하는 임의 업무 Component
|
||||
- 코드에서만 존재하는 Core Component 상태
|
||||
- 화면별 임의 Token 이름
|
||||
- Semantic 의미 없이 raw color를 상태값으로 사용
|
||||
- Component 이름 변경 후 SemVer/Migration 미검토
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
# KBX Design Token Policy v4
|
||||
|
||||
## 목적
|
||||
|
||||
Token은 브랜드 장식이 아니라 화면 편차를 줄이는 운영 장치다.
|
||||
|
||||
## 3계층
|
||||
|
||||
1. Primitive: gray, blue, spacing 등 원시 값
|
||||
2. Semantic: surface, border, text, primary, warning, danger
|
||||
3. Component: control-height, grid-row-height, header-height 등
|
||||
|
||||
업무 화면에서는 가능한 한 Primitive 색상 값을 직접 사용하지 않고 Semantic/Component Token을 소비한다.
|
||||
|
||||
## Density
|
||||
|
||||
- `compact`: OMS/ERP Desktop 기본
|
||||
- `comfortable`: 일반 관리자/가독성 우선
|
||||
- `touch`: WMS PDA/Mobile
|
||||
|
||||
사용자가 Compact/Comfortable를 선택하더라도 **버튼 위치, Form 구조, Keyboard 계약은 변경되지 않는다.** 밀도는 학습해야 하는 다른 UX가 되어서는 안 된다.
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
# KBX Environment Promotion Checklist v22
|
||||
|
||||
## Staging
|
||||
- 동일 Release Artifact 확인
|
||||
- Secret/Configuration 주입
|
||||
- Configuration Validator PASS
|
||||
- DbUp dry-run + apply
|
||||
- API/Scenario smoke
|
||||
- External provider는 승인된 테스트 정책에 따라서만 opt-in
|
||||
|
||||
## Production
|
||||
- Staging에서 검증한 Artifact SHA와 동일
|
||||
- `Environment=Production`
|
||||
- `MigrationsMode=predeploy`
|
||||
- `RequireHttps=true`
|
||||
- Secret placeholder 없음
|
||||
- migration dry-run evidence
|
||||
- release-impact / scenario evidence 보존
|
||||
- 배포 후 config fingerprint 기록
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
# ERP Reference Screens v4
|
||||
|
||||
## ERP-MST-ITEM-001 품목관리
|
||||
|
||||
`KbxMasterPage`를 검증하는 기준정보 Golden Reference다.
|
||||
|
||||
왼쪽은 검색 가능한 품목 목록, 오른쪽은 현재 품목의 상세정보다. 신규/복사/사용중지 같은 익숙한 ERP 동작을 유지한다.
|
||||
|
||||
특히 **삭제보다 사용중지**를 기본 업무 문법으로 둔다. 이미 주문·입출고 이력이 있는 Master를 물리 삭제하는 UX는 기준 패턴으로 채택하지 않는다.
|
||||
|
||||
품목 복사 시 ID, 품목코드, Barcode, Version과 같은 Identity/고유속성은 복사하지 않는 것이 원칙이다.
|
||||
|
||||
## ERP-INV-001 재고현황
|
||||
|
||||
`KbxMasterDetailPage` 기준 화면이다.
|
||||
|
||||
왼쪽은 품목별 요약 재고, 오른쪽은 창고·로케이션별 상세 재고를 보여준다. 사용자가 별도 창고 화면을 반복적으로 열어 현재고의 근거를 찾지 않게 한다.
|
||||
|
||||
Read Model은 UI가 필요한 형태로 서버에서 Projection한다. Frontend가 품목/창고/재고 API를 따로 호출해서 Join하지 않는다.
|
||||
|
||||
### 재고 수량 의미
|
||||
|
||||
- 현재고(On Hand)
|
||||
- 할당(Allocated)
|
||||
- 보류(Hold)
|
||||
- 가용(Available)
|
||||
|
||||
를 각각 분리한다. `재고`라는 숫자 하나로 모든 의미를 표현하지 않는다.
|
||||
|
||||
실제 제품에서는 Available 계산 Rule을 Domain/Inventory 정책에서 단일화하고 Projection은 그 결과를 반영해야 한다.
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
bcbd97987501eb95efd3c2e3165e8d9116552f9773854f8df5a3a82b3ace2bea package.json
|
||||
b384f21d0ff924a4f477698e1b7a33ce913035523c6a1df62d3139f87c53ddd3 README.md
|
||||
634d91bf127ca7c36d0344f2c6c559d05e9aef6559ea9ec5399f3b58c62fb1c7 docs/frontend/KBX-FE-QA-Hardening-v41.md
|
||||
0a7476d469585409cf54994d7d8cc84d650dc1b36ee0f3200485366b495ef61e docs/validation-report-v41.md
|
||||
9311fea9260d0fdbc6d538006b387082b5841b46610835e7227656225161e932 docs/evidence/v41-golden-fe-parity.json
|
||||
+1
@@ -0,0 +1 @@
|
||||
design debt ratchet PASS: 131 <= 131
|
||||
+1
@@ -0,0 +1 @@
|
||||
Golden FE parity v41 PASS: controlled AG Grid selection, detail Drawer context, Lookup focus continuation, and dirty intra-screen transitions.
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"version": "v41",
|
||||
"scope": "actual-vue-golden-screen-parity-hardening",
|
||||
"generatedAt": "2026-08-11T18:05:00+09:00",
|
||||
"validation": {
|
||||
"typescriptVueScriptUnits": 300,
|
||||
"screenRecipes": 9,
|
||||
"recipeVerification": "9/9",
|
||||
"screenDefinitions": 20,
|
||||
"componentDefinitions": 84,
|
||||
"componentCatalogEntries": 66,
|
||||
"navigationEntries": 16,
|
||||
"canonicalScenarios": 30,
|
||||
"designTokens": 153,
|
||||
"designDebt": 131,
|
||||
"validateKbx": "PASS",
|
||||
"goldenFeParityV41": "PASS"
|
||||
},
|
||||
"fixed": [
|
||||
"OMS-ORD-001 order link no-op -> detail drawer",
|
||||
"OMS-ORD-001 row double click -> detail drawer context preservation",
|
||||
"KbxDataGrid controlled selection synchronization",
|
||||
"KbxDataGrid grid-scoped error navigation",
|
||||
"KbxLookup direct-code/dialog selection focus continuation",
|
||||
"KbxUnsavedChangesDialog focus trap/Escape/focus restore",
|
||||
"ERP-MST-ITEM-001 dirty master transition guard",
|
||||
"OMS-ORD-002 item lookup next-cell focus and dirty new/copy guard",
|
||||
"Removed visible no-op OMS hold command",
|
||||
"Side navigation favorite accessible names"
|
||||
],
|
||||
"remaining": [
|
||||
{
|
||||
"severity": "P1",
|
||||
"item": "Excel standard is incomplete; OMS list currently uses current-page CSV export, not the full KBX Excel contract."
|
||||
},
|
||||
{
|
||||
"severity": "P1",
|
||||
"item": "Actual Vue/PrimeVue/AG Grid runtime Playwright and visual regression not executed because dependencies are not installed in this artifact."
|
||||
},
|
||||
{
|
||||
"severity": "P2",
|
||||
"item": "Legacy source-string validators should be migrated to semantic/AST checks."
|
||||
}
|
||||
],
|
||||
"runtimeE2E": {
|
||||
"status": "NOT_EXECUTED",
|
||||
"reason": "No node_modules and no pnpm executable in execution container; static/full governance gates only."
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
TypeScript syntax transpile passed for 300 TS/Vue script units.
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
{
|
||||
"version": "v44",
|
||||
"scope": "T01-T09 Light/Dark + T01 empty recovery + runtime center + 1280 + Forced Colors",
|
||||
"passed": 28,
|
||||
"total": 28,
|
||||
"results": [
|
||||
{
|
||||
"name": "OMS-ORD-001 light template",
|
||||
"pass": true,
|
||||
"detail": "T01"
|
||||
},
|
||||
{
|
||||
"name": "ERP-MST-ITEM-001 light template",
|
||||
"pass": true,
|
||||
"detail": "T02"
|
||||
},
|
||||
{
|
||||
"name": "OMS-ORD-002 light template",
|
||||
"pass": true,
|
||||
"detail": "T03"
|
||||
},
|
||||
{
|
||||
"name": "ERP-PRICE-001 light template",
|
||||
"pass": true,
|
||||
"detail": "T04"
|
||||
},
|
||||
{
|
||||
"name": "ERP-INV-001 light template",
|
||||
"pass": true,
|
||||
"detail": "T05"
|
||||
},
|
||||
{
|
||||
"name": "WMS-WORK-001 light template",
|
||||
"pass": true,
|
||||
"detail": "T06"
|
||||
},
|
||||
{
|
||||
"name": "COMMON-REC-001 light template",
|
||||
"pass": true,
|
||||
"detail": "T07"
|
||||
},
|
||||
{
|
||||
"name": "OMS-ORD-003 light template",
|
||||
"pass": true,
|
||||
"detail": "T08"
|
||||
},
|
||||
{
|
||||
"name": "WMS-PICK-001 light template",
|
||||
"pass": true,
|
||||
"detail": "wms-mobile"
|
||||
},
|
||||
{
|
||||
"name": "dark mode retained",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "OMS-ORD-001 dark template",
|
||||
"pass": true,
|
||||
"detail": "T01"
|
||||
},
|
||||
{
|
||||
"name": "ERP-MST-ITEM-001 dark template",
|
||||
"pass": true,
|
||||
"detail": "T02"
|
||||
},
|
||||
{
|
||||
"name": "OMS-ORD-002 dark template",
|
||||
"pass": true,
|
||||
"detail": "T03"
|
||||
},
|
||||
{
|
||||
"name": "ERP-PRICE-001 dark template",
|
||||
"pass": true,
|
||||
"detail": "T04"
|
||||
},
|
||||
{
|
||||
"name": "ERP-INV-001 dark template",
|
||||
"pass": true,
|
||||
"detail": "T05"
|
||||
},
|
||||
{
|
||||
"name": "WMS-WORK-001 dark template",
|
||||
"pass": true,
|
||||
"detail": "T06"
|
||||
},
|
||||
{
|
||||
"name": "COMMON-REC-001 dark template",
|
||||
"pass": true,
|
||||
"detail": "T07"
|
||||
},
|
||||
{
|
||||
"name": "OMS-ORD-003 dark template",
|
||||
"pass": true,
|
||||
"detail": "T08"
|
||||
},
|
||||
{
|
||||
"name": "WMS-PICK-001 dark template",
|
||||
"pass": true,
|
||||
"detail": "wms-mobile"
|
||||
},
|
||||
{
|
||||
"name": "T01 empty state visible",
|
||||
"pass": true,
|
||||
"detail": "현재 필터에 해당하는 주문이 없습니다.\n다른 빠른 필터를 선택해 보세요."
|
||||
},
|
||||
{
|
||||
"name": "T01 reset restores rows",
|
||||
"pass": true,
|
||||
"detail": "8"
|
||||
},
|
||||
{
|
||||
"name": "runtime center aria expanded",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "runtime center focused",
|
||||
"pass": true,
|
||||
"detail": "systemPanel"
|
||||
},
|
||||
{
|
||||
"name": "runtime center focus restored",
|
||||
"pass": true,
|
||||
"detail": "jobsButton"
|
||||
},
|
||||
{
|
||||
"name": "1280 document horizontal overflow zero",
|
||||
"pass": true,
|
||||
"detail": "0"
|
||||
},
|
||||
{
|
||||
"name": "forced colors active",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "forced colors T01 remains visible",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "runtime errors zero",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"iteration": 44,
|
||||
"scope": "theme state, runtime center navigation, empty recovery, forced colors",
|
||||
"release": {
|
||||
"contractVersion": "1.25.0",
|
||||
"mode": "fe-theme-state-runtime-panel-forced-colors-hardening"
|
||||
},
|
||||
"changes": [
|
||||
"Actual AppVersion aligned to v44",
|
||||
"Non-modal operations/notifications runtime panel with aria-expanded, Escape and focus restoration",
|
||||
"Menu search closes runtime panel from Ctrl+K, Header and Home entry paths",
|
||||
"T01 empty state exposes one-step search reset and immediate re-query",
|
||||
"Forced Colors system-color bridge for KBX semantic theme",
|
||||
"Reference HTML/JS system center aligned with actual Vue non-modal shell contract",
|
||||
"Public component versions and catalog scenarios updated"
|
||||
],
|
||||
"browserEvidence": {
|
||||
"featureQa": "14/14 PASS",
|
||||
"templateThemeMatrix": "28/28 PASS",
|
||||
"t09": "wms-mobile Light/Dark PASS",
|
||||
"forcedColors": "PASS",
|
||||
"runtimeErrors": 0
|
||||
},
|
||||
"governance": {
|
||||
"validateKbx": "PASS",
|
||||
"tokens": 153,
|
||||
"recipes": 9,
|
||||
"recipeVerification": "9/9",
|
||||
"screens": 20,
|
||||
"components": 84,
|
||||
"catalogEntries": 66,
|
||||
"navigationEntries": 16,
|
||||
"scenarios": 30,
|
||||
"fields": 51,
|
||||
"apiOperations": 66,
|
||||
"typescriptVueUnits": 305,
|
||||
"designDebt": 116,
|
||||
"designDebtRatchet": 131
|
||||
},
|
||||
"liveVueRuntime": {
|
||||
"executed": false,
|
||||
"reason": "pnpm/workspace dependencies are not installed and package registry access is unavailable in this execution environment",
|
||||
"notClaimed": [
|
||||
"Vite live runtime",
|
||||
"vue-tsc semantic typecheck",
|
||||
"PrimeVue/AG Grid actual-runtime Playwright",
|
||||
"screen-reader automation"
|
||||
]
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
{
|
||||
"version": "v44",
|
||||
"harness": "exact FE Reference HTML/CSS/JS via Playwright set_content; CSP static-tested separately",
|
||||
"passed": 14,
|
||||
"total": 14,
|
||||
"results": [
|
||||
{
|
||||
"name": "home renders",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "default light",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "jobs panel opens",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "jobs trigger expanded",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "jobs panel receives focus",
|
||||
"pass": true,
|
||||
"detail": "systemPanel"
|
||||
},
|
||||
{
|
||||
"name": "runtime panel is non-modal",
|
||||
"pass": true,
|
||||
"detail": "None"
|
||||
},
|
||||
{
|
||||
"name": "Esc closes runtime panel",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "Esc restores trigger focus",
|
||||
"pass": true,
|
||||
"detail": "jobsButton"
|
||||
},
|
||||
{
|
||||
"name": "dark applied",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "dark aria action",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "T01 opens",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "forced colors active",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "forced colors semantic override",
|
||||
"pass": true,
|
||||
"detail": "LinkText"
|
||||
},
|
||||
{
|
||||
"name": "runtime errors zero",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
{
|
||||
"version": "v45",
|
||||
"total": 14,
|
||||
"passed": 14,
|
||||
"failed": 0,
|
||||
"checks": [
|
||||
{
|
||||
"name": "home-default",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "t01-open",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "remember-enabled",
|
||||
"pass": true,
|
||||
"detail": "{'favorites': ['OMS-ORD-001', 'ERP-INV-001', 'WMS-WORK-001'], 'density': 'compact', 'collapsed': False, 'theme': 'light', 'orderRememberSearch': True, 'orderSavedDefaults': {'channel': '쿠팡', 'status': '출고대기', 'exception': 'all'}}"
|
||||
},
|
||||
{
|
||||
"name": "safe-channel-saved",
|
||||
"pass": true,
|
||||
"detail": "{'channel': '쿠팡', 'status': '출고대기', 'exception': 'all'}"
|
||||
},
|
||||
{
|
||||
"name": "safe-status-saved",
|
||||
"pass": true,
|
||||
"detail": "{'channel': '쿠팡', 'status': '출고대기', 'exception': 'all'}"
|
||||
},
|
||||
{
|
||||
"name": "free-text-not-persisted",
|
||||
"pass": true,
|
||||
"detail": "{\"favorites\":[\"OMS-ORD-001\",\"ERP-INV-001\",\"WMS-WORK-001\"],\"density\":\"compact\",\"collapsed\":false,\"theme\":\"light\",\"orderRememberSearch\":true,\"orderSavedDefaults\":{\"channel\":\"쿠팡\",\"status\":\"출고대기\",\"exception\":\"all\"}}"
|
||||
},
|
||||
{
|
||||
"name": "dark-theme",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "restored-channel",
|
||||
"pass": true,
|
||||
"detail": "쿠팡"
|
||||
},
|
||||
{
|
||||
"name": "restored-status",
|
||||
"pass": true,
|
||||
"detail": "출고대기"
|
||||
},
|
||||
{
|
||||
"name": "restored-remember",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "keyword-cleared-on-restore",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "theme-restored",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "home-1280-no-document-overflow",
|
||||
"pass": true,
|
||||
"detail": "0"
|
||||
},
|
||||
{
|
||||
"name": "runtime-errors-zero",
|
||||
"pass": true,
|
||||
"detail": "[]"
|
||||
}
|
||||
]
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
{
|
||||
"version": "v46",
|
||||
"scope": "Reference HTML/CSS/JavaScript using in-memory Web Storage shim because direct-origin navigation is administrator-blocked",
|
||||
"checks": [
|
||||
{
|
||||
"name": "initial home renders",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "dark theme applied",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "dirty tab marked",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "clean tab persisted to session",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "dirty tab excluded from session",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "dirty exclusion count persisted",
|
||||
"pass": true,
|
||||
"detail": "1"
|
||||
},
|
||||
{
|
||||
"name": "dirty form value not persisted",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "first document runtime error free",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "theme preference restored",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "clean workspace restored with resumed marker",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "dirty workspace not restored",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "home workspace has keyboard entry tab",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "home explains dirty non-restoration",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "home quick-start distinguishes resumed work",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "resume marker clears after activation",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "restored document runtime error free",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "storage-blocked shell still renders",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "storage-blocked runtime error free",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "1280 home has no document horizontal overflow",
|
||||
"pass": true,
|
||||
"detail": "0"
|
||||
},
|
||||
{
|
||||
"name": "1280 runtime error free",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
}
|
||||
],
|
||||
"passed": 20,
|
||||
"total": 20,
|
||||
"allPassed": true
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
{
|
||||
"version": "v47",
|
||||
"suite": "FE Reference actionable problem + theme",
|
||||
"passed": 12,
|
||||
"total": 12,
|
||||
"checks": [
|
||||
{
|
||||
"name": "home rendered",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "dark theme active",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "T01 opened",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "exception row selected",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "business problem visible",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "problem has recovery action",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "problem uses color independent label",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "recovery action dismisses problem",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "blocked order remains selected",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "dismiss closes problem",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "1280 document overflow",
|
||||
"pass": true,
|
||||
"detail": "0"
|
||||
},
|
||||
{
|
||||
"name": "runtime page errors",
|
||||
"pass": true,
|
||||
"detail": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"foundationIteration": 47,
|
||||
"contractVersion": "1.28.0",
|
||||
"focus": "actionable command problem recovery without increasing BE coupling",
|
||||
"problemTypes": ["business-rule", "permission", "not-found", "integration", "system"],
|
||||
"specializedElsewhere": ["validation", "conflict"],
|
||||
"components": {
|
||||
"KbxProblemFeedback": "1.0.0",
|
||||
"KbxListPage": "1.8.0",
|
||||
"KbxMasterPage": "1.7.0",
|
||||
"KbxTransactionPage": "1.7.0"
|
||||
},
|
||||
"goldenScreens": [
|
||||
"OMS-ORD-001 bulk ship",
|
||||
"OMS-ORD-002 save/confirm",
|
||||
"ERP-MST-ITEM-001 load/save/deactivate"
|
||||
],
|
||||
"security": {
|
||||
"serverProblemActions": "default-deny; explicit allow-list only",
|
||||
"rawHtml": false,
|
||||
"domainTruth": "server-owned"
|
||||
},
|
||||
"browserReference": {
|
||||
"passed": 12,
|
||||
"total": 12,
|
||||
"theme": "KBX Business Dark",
|
||||
"viewportChecks": ["1440x900", "1280x720"],
|
||||
"runtimeErrors": 0
|
||||
},
|
||||
"aggregate": {
|
||||
"designTokens": 153,
|
||||
"screenRecipes": 9,
|
||||
"screenDefinitions": 20,
|
||||
"components": 85,
|
||||
"catalogEntries": 70,
|
||||
"tsVueUnits": 307,
|
||||
"designDebt": 116,
|
||||
"designDebtRatchet": 131
|
||||
},
|
||||
"limitation": "Live Vite/Vue/PrimeVue/AG Grid E2E was not executed because dependency installation/package registry access is unavailable in this environment."
|
||||
}
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
{
|
||||
"iteration": 48,
|
||||
"scope": "Reference T05-T08 real search/file completion loops with KBX Business Dark theme",
|
||||
"browser": "Chromium inline-asset harness",
|
||||
"checks": [
|
||||
{
|
||||
"name": "home boots",
|
||||
"status": "PASS",
|
||||
"detail": "홈"
|
||||
},
|
||||
{
|
||||
"name": "dark theme applies",
|
||||
"status": "PASS",
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "T05 opens",
|
||||
"status": "PASS",
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "T05 enters loading",
|
||||
"status": "PASS",
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "T05 search filters master",
|
||||
"status": "PASS",
|
||||
"detail": "ERP > 재고\n재고현황\nT05\n★\n도움말\nAI\n제안\n조회 F3\n재고이동\n엑셀 ▼\n창고\n전체\n서울센터\n부산센터\n인천센터\n품목\n조회\n현재 조회\n품목 1개\n창고 전체\n조건 초기화\n품목\n현재고\n재고 품목 목록\n품목코드\t품목명\t현재고\nABC002\t양말\t721\n창고 / Location\nABC002 양말 · 721개\n품목별 창고 로케이션 재고\n창고\tLocation\t현재고\t할당\t보류\n서울센터\tA-02-01\t421\t50\t0\n부산센터\tB-02"
|
||||
},
|
||||
{
|
||||
"name": "T05 empty state actionable",
|
||||
"status": "PASS",
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "T05 empty recovery resets",
|
||||
"status": "PASS",
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "T06 opens",
|
||||
"status": "PASS",
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "T06 enters loading",
|
||||
"status": "PASS",
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "T06 worker filter changes rows",
|
||||
"status": "PASS",
|
||||
"detail": "WMS > 작업관리\n오늘 물류작업\nT06\n★\n도움말\nAI\n제안\n조회 F3\n작업자 배정\nWave 생성\n예외만 보기\n작업자\n통합검색\n조회\n입고대기\n218\n적치대기\n43\n피킹대기\n927\n검수대기\n122\n예외\n12\n지금 처리할 업무\n표시 1건\n선택 0건\n지연 1건\n빠른필터 해제\n물류 작업 대기열\n\t작업번호\t작업유형\tWave\t작업자\t진행률\tSLA\t상태\n\tWK-260811-182\t검수\tW260811-11\t박물류\t91%\t16:55\t지연"
|
||||
},
|
||||
{
|
||||
"name": "T06 empty state actionable",
|
||||
"status": "PASS",
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "T06 recovery restores queue",
|
||||
"status": "PASS",
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "T07 opens",
|
||||
"status": "PASS",
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "T07 enters loading",
|
||||
"status": "PASS",
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "T07 reason filter narrows rows",
|
||||
"status": "PASS",
|
||||
"detail": "COMMON > 운영\n재고·출고 대사\nT07\n☆\n도움말\nAI\n제안\n조회 F3\n재처리\n엑셀 ▼\n기준일\n통합검색\n불일치만 표시\n조회\n전체 12,482\n정상 12,439\n불일치 43\n표시 1\n선택 0\nOMS WMS 재고 출고 대사\n\t주문번호\tOMS\tWMS\t차이\t원인\t처리상태\n\tORD-003\t5\t0\t-5\t연계실패\t미처리"
|
||||
},
|
||||
{
|
||||
"name": "T07 has selectable mismatch",
|
||||
"status": "PASS",
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "T07 resolution mutates reference state",
|
||||
"status": "PASS",
|
||||
"detail": "COMMON > 운영\n재고·출고 대사\nT07\n☆\n도움말\nAI\n제안\n조회 F3\n재처리\n엑셀 ▼\n기준일\n통합검색\n불일치만 표시\n조회\n전체 12,482\n정상 12,439\n불일치 43\n표시 0\n선택 0\n조건에 맞는 대사 건이 없습니다.\n불일치 필터 또는 검색어를 초기화해 보세요.\n다시 조회"
|
||||
},
|
||||
{
|
||||
"name": "T08 opens",
|
||||
"status": "PASS",
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "T08 six visual steps",
|
||||
"status": "PASS",
|
||||
"detail": "6"
|
||||
},
|
||||
{
|
||||
"name": "T08 actual file input exists",
|
||||
"status": "PASS",
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "T08 reads actual File name",
|
||||
"status": "PASS",
|
||||
"detail": "OMS > 주문\n주문 Excel 업로드\nT08\n☆\n도움말\nAI\n제안\n양식 다운로드\n최근 업로드 결과\n1\n파일\n2\n매핑\n3\n검증\n4\n미리보기\n5\n반영\n6\n결과\norders-v48.xlsx\n\n0.00MB · 파일 선택 완료 · 다음 단계에서 필드 매핑을 확인합니다.\n\n파일 다시 선택 업로드 양식 다운로드\n다음"
|
||||
},
|
||||
{
|
||||
"name": "T08 next enabled after file",
|
||||
"status": "PASS",
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "T08 rejects non Excel extension",
|
||||
"status": "PASS",
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "T08 mapping step",
|
||||
"status": "PASS",
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "T08 validation step",
|
||||
"status": "PASS",
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "T08 preview step",
|
||||
"status": "PASS",
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "T08 commit step",
|
||||
"status": "PASS",
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "1280 document horizontal overflow zero",
|
||||
"status": "PASS",
|
||||
"detail": "0"
|
||||
},
|
||||
{
|
||||
"name": "runtime page errors zero",
|
||||
"status": "PASS",
|
||||
"detail": "[]"
|
||||
},
|
||||
{
|
||||
"name": "console errors zero",
|
||||
"status": "PASS",
|
||||
"detail": "[]"
|
||||
}
|
||||
],
|
||||
"passed": 30,
|
||||
"failed": 0,
|
||||
"limitations": [
|
||||
"CSP meta removed only for inline-asset harness; CSP is verified by static governance gates.",
|
||||
"This is not Vite/Vue/PrimeVue/AG Grid live-runtime E2E."
|
||||
]
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"iteration": 48,
|
||||
"scope": "T04-T08 actionable problem recovery + T05-T08 real Reference completion loops",
|
||||
"actualVue": {
|
||||
"T04": "ERP item-price fast entry save problems preserve validation/grid context and expose retry/dismiss.",
|
||||
"T06": "Operations queue claim/resolve/retry problems preserve queue and selection context.",
|
||||
"T07": "Reconcile exception command problems preserve reconcile filter/selection context.",
|
||||
"T08": "Order Excel upload/mapping/validation/commit/download problems use shared actionable problem recovery."
|
||||
},
|
||||
"templates": [
|
||||
"KbxFastEntryPage",
|
||||
"KbxMasterDetailPage",
|
||||
"KbxQueuePage",
|
||||
"KbxWorkQueuePage",
|
||||
"KbxReconcilePage",
|
||||
"KbxImportPage"
|
||||
],
|
||||
"problemComponent": {
|
||||
"name": "KbxProblemFeedback",
|
||||
"version": "1.1.0",
|
||||
"validationSummary": "first 3 messages + remaining count; field/cell validation remains authoritative"
|
||||
},
|
||||
"reference": {
|
||||
"T05": "real warehouse/item filtering + loading + empty reset + master-detail context",
|
||||
"T06": "real worker/keyword/quick-filter query + loading + empty recovery + selection actions",
|
||||
"T07": "real date/keyword/mismatch filter + loading + empty recovery + resolution mutation",
|
||||
"T08": "actual browser File input/drop + extension/20MB guard + six visual steps"
|
||||
},
|
||||
"securityBoundaries": [
|
||||
"Problem UI does not grant permission or bypass server/domain validation.",
|
||||
"Reference File handling validates presentation constraints only; import staging/domain validation remains server-owned.",
|
||||
"No dynamic v-html/HTML injection was added."
|
||||
],
|
||||
"liveRuntimeEvidence": {
|
||||
"executed": false,
|
||||
"reason": "Workspace package dependencies are not installed and package registry access is restricted in this environment; actual Vue/Vite/PrimeVue/AG Grid runtime is not claimed."
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"passed": 10,
|
||||
"total": 10,
|
||||
"results": [
|
||||
{
|
||||
"name": "home-title",
|
||||
"ok": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "home-attention-collapsed-count",
|
||||
"ok": true,
|
||||
"detail": "8"
|
||||
},
|
||||
{
|
||||
"name": "home-attention-toggle-present",
|
||||
"ok": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "home-attention-toggle-collapsed",
|
||||
"ok": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "home-attention-expanded-count",
|
||||
"ok": true,
|
||||
"detail": "10"
|
||||
},
|
||||
{
|
||||
"name": "home-attention-toggle-expanded",
|
||||
"ok": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "home-attention-keyboard-collapse",
|
||||
"ok": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "dark-theme-root",
|
||||
"ok": true,
|
||||
"detail": "dark"
|
||||
},
|
||||
{
|
||||
"name": "home-1280-horizontal-overflow",
|
||||
"ok": true,
|
||||
"detail": "0"
|
||||
},
|
||||
{
|
||||
"name": "runtime-errors-zero",
|
||||
"ok": true,
|
||||
"detail": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"foundationIteration": 49,
|
||||
"contractVersion": "1.30.0",
|
||||
"focus": "FE Workbench/Home attention disclosure and truthful WMS Demo/Production boundaries",
|
||||
"changes": {
|
||||
"home": [
|
||||
"Collapsed attention shows 8 items; explicit disclosure can expand up to 24",
|
||||
"Attention source composition is visible without adding dashboard charts",
|
||||
"Reference HTML/JavaScript mirrors the same disclosure contract"
|
||||
],
|
||||
"t06": [
|
||||
"KbxQueuePage exposes exact empty/error/retry copy and empty recovery action",
|
||||
"WMS-WORK-001 returns filtered demo rows only in Demo Mode",
|
||||
"Normal runtime throws WMS_WORK_QUERY_NOT_CONNECTED instead of returning a fake empty queue"
|
||||
],
|
||||
"t09": [
|
||||
"Receiving/putaway/counting client-only scanner scenarios are Demo-only",
|
||||
"Normal runtime shows a non-dismissible WMS_EXECUTION_API_NOT_CONNECTED integration problem",
|
||||
"Blocked production flows hide sticky actions",
|
||||
"Hardcoded online state was replaced with useKbxNetworkState"
|
||||
]
|
||||
},
|
||||
"securityAndTruthBoundary": [
|
||||
"Theme and Demo Mode do not grant domain authority",
|
||||
"Client-only scanner transitions are not represented as production success",
|
||||
"Server validation, idempotency and audit remain authoritative"
|
||||
],
|
||||
"browserReferenceEvidence": "docs/evidence/v49-browser-workbench-theme.json"
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"checks": {
|
||||
"home_title": true,
|
||||
"attention_total_28": true,
|
||||
"attention_visible_8": true,
|
||||
"truthful_toggle_24": true,
|
||||
"toggle_collapsed_aria": true,
|
||||
"dark_theme": true,
|
||||
"attention_visible_24": true,
|
||||
"expanded_aria": true,
|
||||
"overflow_centers": true,
|
||||
"expanded_count_copy": true,
|
||||
"jobs_panel_open": true,
|
||||
"jobs_header_expanded": true,
|
||||
"panel_escape_close": true,
|
||||
"center_focus_restore": true,
|
||||
"no_document_horizontal_overflow_1280": true,
|
||||
"runtime_errors_zero": true
|
||||
},
|
||||
"passed": 16,
|
||||
"total": 16,
|
||||
"runtimeErrors": []
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"foundationIteration": 50,
|
||||
"contractVersion": "1.31.0",
|
||||
"focus": "FE operational truth: retryable vs non-retryable errors + bounded Home attention overflow navigation",
|
||||
"components": {
|
||||
"KbxTemplateStateBoundary": "1.3.0",
|
||||
"KbxHomePage": "1.10.0",
|
||||
"KbxListPage": "1.9.0",
|
||||
"KbxMasterPage": "1.8.0",
|
||||
"KbxTransactionPage": "1.8.0",
|
||||
"KbxFastEntryPage": "1.9.0",
|
||||
"KbxMasterDetailPage": "1.8.0",
|
||||
"KbxQueuePage": "1.9.0",
|
||||
"KbxWorkQueuePage": "1.6.0",
|
||||
"KbxReconcilePage": "1.7.0",
|
||||
"KbxImportPage": "1.6.0",
|
||||
"KbxWmsMobilePage": "1.8.0"
|
||||
},
|
||||
"screenVersions": {
|
||||
"WMS-WORK-001": "1.2.0"
|
||||
},
|
||||
"behavior": [
|
||||
"all major T01-T09 template shells can suppress meaningless retry for permanent errors",
|
||||
"WMS_WORK_QUERY_NOT_CONNECTED is non-retryable instead of presenting an endless retry loop",
|
||||
"Home keeps 8-item default density and uses a truthful bounded 24-item expansion when the total is larger",
|
||||
"remaining Home attention routes directly to Work Center or Notification Center",
|
||||
"Reference HTML/JavaScript mirrors the same bounded disclosure and Light/Dark behavior"
|
||||
],
|
||||
"browserReferenceEvidence": "docs/evidence/v50-browser-operational-truth.json",
|
||||
"aggregateValidation": "docs/evidence/v50-validate-kbx.log",
|
||||
"browserPass": "16/16",
|
||||
"designDebt": 116,
|
||||
"designDebtRatchet": 131,
|
||||
"limitations": [
|
||||
"package registry/workspace dependencies unavailable in this execution environment",
|
||||
"no claim of live Vite/Vue/PrimeVue/AG Grid Playwright E2E",
|
||||
"reference browser evidence uses the delivered HTML/CSS/JavaScript assets in an inline Chromium harness"
|
||||
]
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
KBX v51 FE Presentation Workbench Hardening - Validation Evidence
|
||||
|
||||
Command:
|
||||
node scripts/validate-kbx.mjs
|
||||
|
||||
Result: PASS
|
||||
|
||||
Selected evidence:
|
||||
- TypeScript syntax transpile passed for 308 TS/Vue script units.
|
||||
- Screen governance PASS (20 screens).
|
||||
- Template completeness PASS for 8 desktop templates + T09 mobile, Home, navigation, Golden Screens.
|
||||
- FE runtime v42 through FE operational truth v50 regression gates PASS.
|
||||
- FE presentation/workbench hardening v51 contract PASS.
|
||||
- design debt ratchet PASS: 116 <= 131.
|
||||
- design-code parity PASS: 161 tokens, 34 core components, 85 total components.
|
||||
- release governance PASS: major.
|
||||
|
||||
Environment limitation:
|
||||
- pnpm is not installed and node_modules are not present, so Vite bundle build / Playwright runtime was not executed in this environment.
|
||||
- Chromium is installed, but the headless screenshot process did not terminate reliably in this container; no screenshot evidence is claimed.
|
||||
+1
@@ -0,0 +1 @@
|
||||
e5591f8df77587eb934490c89b19e4fe1a1178839d10dd3aa10d1388be74fd18 kbx-foundation-v52-fe-operational-navigation-screen-anatomy.zip
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
v52 FE environment limitations
|
||||
- node available
|
||||
- pnpm unavailable
|
||||
- root node_modules unavailable
|
||||
- Chromium static screenshot attempt timed out in the container; screenshot not used as release evidence
|
||||
- Vite/Vitest/Playwright browser execution was therefore not claimed
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
a33c3b809dc6cb5dcb1a4c71859a0c173a17bc03195db610660939fb19ee441b docs/evidence/v53-validate-kbx-full.log
|
||||
8427e2a08b805d173e21294057958a1dcf5e0700871e4cb1559b858576829507 docs/frontend/KBX-FE-Golden-Screen-Interaction-Hardening-v53.md
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
KBX Foundation v53 — Runtime validation environment limitations
|
||||
Date: 2026-08-12 (Asia/Seoul)
|
||||
|
||||
Available:
|
||||
- node: available
|
||||
- npm: available
|
||||
|
||||
Unavailable in this execution environment:
|
||||
- pnpm
|
||||
- root node_modules
|
||||
- apps/web/node_modules
|
||||
|
||||
Therefore NOT executed / NOT claimed:
|
||||
- pnpm workspace install
|
||||
- Vite production build
|
||||
- Vue application runtime typecheck via installed workspace dependencies
|
||||
- Vitest runtime/component suite
|
||||
- Playwright actual Vue/Vite browser E2E
|
||||
|
||||
Executed successfully:
|
||||
- node scripts/validate-typescript-syntax.mjs
|
||||
- node scripts/validate-fe-reference.mjs
|
||||
- node scripts/validate-fe-golden-interaction-v53.mjs
|
||||
- node scripts/measure-design-debt.mjs
|
||||
- node scripts/validate-kbx.mjs
|
||||
|
||||
Full regression evidence:
|
||||
- docs/evidence/v53-validate-kbx-full.log
|
||||
|
||||
Interpretation:
|
||||
Static/syntax/contract/governance regression PASS must not be represented as a real Vite/Playwright runtime PASS.
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
PASS: home module rail derives action pressure from the same attention truth as the home queue
|
||||
PASS: home module rail is keyboard navigable without requiring mouse selection
|
||||
PASS: home module rail exposes stable focus targets
|
||||
PASS: module selection lands focus on the filtered work explorer
|
||||
PASS: home module rail preserves active/focus cues in high-contrast navigation
|
||||
PASS: standard section heading owns count units instead of forcing screen-specific count markup
|
||||
PASS: KbxMasterPage.vue exposes standard list-actions work-surface actions
|
||||
PASS: KbxMasterPage.vue exposes standard detail-actions work-surface actions
|
||||
PASS: KbxTransactionPage.vue exposes standard header-actions work-surface actions
|
||||
PASS: KbxTransactionPage.vue exposes standard detail-actions work-surface actions
|
||||
PASS: KbxQueuePage.vue exposes standard queue-actions work-surface actions
|
||||
PASS: KbxReconcilePage.vue exposes standard comparison-actions work-surface actions
|
||||
PASS: OMS order registration makes current record/status/error context explicit
|
||||
PASS: OMS order registration exposes row-add and error recovery beside the detail work surface
|
||||
PASS: OMS order detail count uses business-meaningful unit
|
||||
PASS: ERP purchase draft participates in workspace dirty-state recovery
|
||||
PASS: ERP purchase grid supports both F2 lookup and direct item-code resolution
|
||||
PASS: ERP purchase draft has field/row validation before save intent
|
||||
PASS: ERP purchase does not fabricate authoritative workflow state on the client
|
||||
PASS: ERP purchase detail supports real fast-entry grid interactions
|
||||
PASS: ERP inventory move gives immediate cross-field and stock UX validation
|
||||
PASS: ERP inventory move resolves item availability into the editing grid
|
||||
PASS: ERP inventory move does not simulate Domain workflow transitions on the client
|
||||
PASS: ERP inventory move participates in the common save keyboard contract
|
||||
PASS: WMS picking exposes task context and next instruction through the T09 template contract
|
||||
PASS: WMS picking dedicates a persistent notice surface to offline/retry truth
|
||||
PASS: WMS picking tells the operator what to do next and prevents ambiguous repeat scans
|
||||
PASS: WMS scanner labels use operator-facing Korean terminology
|
||||
PASS: static HTML reference identifies the v53 golden-screen interaction build
|
||||
PASS: static JavaScript reference mirrors module attention pressure
|
||||
PASS: static JavaScript module rail mirrors keyboard navigation
|
||||
PASS: static HTML reference visibly exposes module keyboard focus
|
||||
PASS: FE golden-screen interaction hardening v53 contract
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
296dc43c598451ad1b4d3f36934679c12b0e7f19c9331c81555f8451a2410a69 packages/kbx-ui/src/shell/KbxHomePage.vue
|
||||
c16370652a993292a9560359bca750c4d29d5e9320eeca351dbc9817066c0fa9 packages/kbx-ui/src/components/KbxRecordNavigator.vue
|
||||
d5a01b4d5fbb104567d065beab8b9b7cd5af3b3bdfebf84df38dee334fb807f0 packages/kbx-ui/src/components/KbxWorkflowBar.vue
|
||||
38659b94d8799425530815f28ff7757a47cbb647976dae1b03d65b75813c9304 packages/kbx-ui/src/components/KbxFastEntryPage.vue
|
||||
e3d93c65e1029b0deed4e187619ac29643af55a6ad8928ea63e6d99358fff066 packages/kbx-ui/src/components/KbxMasterDetailPage.vue
|
||||
aa279bd3656aa1a549d76ceec901122f8e77661c5577ef691dda22ee524462e5 apps/web/fe-reference/index.html
|
||||
d1d121a87e46e54646f81e55d139d29fcd97083358ad5f32c914b04b6863cae4 apps/web/fe-reference/kbx-fe.js
|
||||
6078a2803d5f843679214d278feccb862ea0fd967f62a95504cf7930439ed958 apps/web/fe-reference/kbx-fe.css
|
||||
51b4c29240bb0e93464e1849201b4cc6d8396a5c9d0a3b39d4a351c99d86da53 docs/evidence/v54-validate-kbx-full.log
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
KBX v54 runtime validation environment
|
||||
====================================
|
||||
|
||||
Observed in the current container:
|
||||
- Chromium: /usr/bin/chromium
|
||||
- pnpm: not installed / not on PATH
|
||||
- project root node_modules: missing
|
||||
|
||||
What was executed successfully:
|
||||
- node --check apps/web/fe-reference/kbx-fe.js
|
||||
- node scripts/validate-typescript-syntax.mjs -> 309 TS/Vue script units PASS
|
||||
- node scripts/validate-fe-work-surface-v54.mjs -> PASS
|
||||
- node scripts/validate-kbx.mjs -> full PASS
|
||||
|
||||
What was NOT claimed:
|
||||
- Vite production build PASS
|
||||
- Vitest runtime PASS
|
||||
- Playwright browser E2E PASS
|
||||
|
||||
Static Chromium QA attempt:
|
||||
- A local HTTP server plus Chromium headless screenshot at 1440x900 was attempted.
|
||||
- Chromium did not terminate within the bounded execution window.
|
||||
- stderr contained DBus connection errors and zygote termination communication errors.
|
||||
- No screenshot from this attempt is accepted as evidence.
|
||||
|
||||
This limitation is environmental evidence, not a frontend success claim.
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
PASS: home operational triage includes 긴급·예외 lane
|
||||
PASS: home operational triage includes 미저장 lane
|
||||
PASS: home operational triage includes 진행 작업 lane
|
||||
PASS: home operational triage includes 알림 lane
|
||||
PASS: home triage limits each source lane instead of letting one source monopolize the workbench
|
||||
PASS: home exposes visible triage volume and stable lane anatomy
|
||||
PASS: home module rail retains keyboard-first wayfinding
|
||||
PASS: record navigator exposes record position and hard previous/next boundaries
|
||||
PASS: record navigator has explicit previous/next interaction contract
|
||||
PASS: record navigator preserves keyboard focus and high-contrast feedback
|
||||
PASS: T02 item master exposes current-record wayfinding beside the detail work surface
|
||||
PASS: T02 record navigation is fail-closed while the detail has unsaved changes
|
||||
PASS: T02 record navigation reuses the existing dirty-safe selection path instead of bypassing it
|
||||
PASS: workflow bar explicitly tells the user what can happen next
|
||||
PASS: workflow bar exposes current step semantics and progress
|
||||
PASS: workflow bar explains terminal/blocked states instead of showing an unexplained blank action area
|
||||
PASS: T04 owns fast-entry grid title and row-count anatomy
|
||||
PASS: T04 reserves standard grid-level actions beside the work-surface heading
|
||||
PASS: real T04 item-price screen uses the standard work-surface heading/count contract
|
||||
PASS: real T04 exposes error pressure beside the entry surface
|
||||
PASS: T05 exposes standard master-actions location
|
||||
PASS: T05 exposes standard detail-actions location
|
||||
PASS: T05 exposes standard bottom-actions location
|
||||
PASS: T05 gives each pane an explicit purpose instead of relying on layout alone
|
||||
PASS: T05 exposes per-pane data volume for scanable master/detail context
|
||||
PASS: real inventory explorer uses master purpose/count contract
|
||||
PASS: real inventory explorer uses detail purpose/count contract
|
||||
PASS: real inventory explorer uses history purpose/count contract
|
||||
PASS: new record navigator is governed in the component manifest
|
||||
PASS: workflow explicit-next-action change is versioned
|
||||
PASS: T04 work-surface contract change is versioned
|
||||
PASS: T05 pane anatomy change is versioned
|
||||
PASS: home triage/navigation change is versioned
|
||||
PASS: static HTML reference identifies the v54 work-surface build
|
||||
PASS: static reference CSP preserves connect-src 'self'
|
||||
PASS: static reference CSP preserves frame-src 'none'
|
||||
PASS: static reference CSP preserves worker-src 'none'
|
||||
PASS: static reference CSP preserves object-src 'none'
|
||||
PASS: static reference CSP preserves frame-ancestors 'none'
|
||||
PASS: static JavaScript reference mirrors four-lane home operational triage
|
||||
PASS: mixed urgent overflow no longer routes incorrectly to notifications only
|
||||
PASS: static T02 reference mirrors dirty-safe previous/next record navigation
|
||||
PASS: static T04 reference mirrors the named fast-entry work surface
|
||||
PASS: static CSS has concrete triage, record navigation, and fast-entry surfaces
|
||||
PASS: v54 static styles do not rely on undefined theme variables
|
||||
PASS: static reference preserves high-contrast/focus cues for new v54 interactions
|
||||
PASS: FE work-surface triage and wayfinding hardening v54 contract
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
8706b28ac06d29844076cd16817b85505eb7ccf10501fd16d52b1b7b6609afdd docs/frontend/KBX-FE-Recovery-Workbench-Hardening-v55.md
|
||||
76f71e2cf2bf452ee14c3d411afc834959834e255fcaa9cadb107deefc0e592e docs/evidence/v55-validate-kbx-full.log
|
||||
59dfe38e5bf4236c8895ed911ce67e89fd835cfeed12fc29251a333ea244564d docs/evidence/v55-fe-recovery-workbench-validation.txt
|
||||
c1f4687f5a1cc2646d28f20a10cc44b065eb8ec60752c3e16f0509300e0e3998 docs/evidence/v55-environment-limitations.txt
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
KBX Foundation v55 runtime evidence limitations
|
||||
Date: 2026-08-12 Asia/Seoul
|
||||
|
||||
Available:
|
||||
- node v22.16.0
|
||||
- chromium /usr/bin/chromium
|
||||
- static Node-based KBX validators
|
||||
|
||||
Unavailable in project runtime:
|
||||
- pnpm executable
|
||||
- root node_modules
|
||||
- apps/web node_modules
|
||||
|
||||
Therefore not claimed:
|
||||
- Vite production build PASS
|
||||
- Vitest browser/component runtime PASS
|
||||
- Vue/PrimeVue/AG Grid Playwright E2E PASS
|
||||
|
||||
Chromium static screenshot attempt:
|
||||
- command: chromium --headless=new --no-sandbox ... file://.../apps/web/fe-reference/index.html#import
|
||||
- result: timeout exit 124 after 20 seconds
|
||||
- dominant stderr: DBus connection errors in container
|
||||
- screenshot was not accepted as release evidence
|
||||
|
||||
Policy:
|
||||
Static/source/contract/syntax validation and browser runtime validation remain separate claims.
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
PASS: T08 Vue import owns file stage
|
||||
PASS: T08 Vue import owns mapping stage
|
||||
PASS: T08 Vue import owns validation stage
|
||||
PASS: T08 Vue import owns preview stage
|
||||
PASS: T08 Vue import owns commit stage
|
||||
PASS: T08 Vue import owns result stage
|
||||
PASS: T08 maps server status to explicit preview and commit work surfaces
|
||||
PASS: T08 exposes a dedicated preview surface before mutation
|
||||
PASS: T08 preview supports deterministic first/next validation-error focus
|
||||
PASS: T08 avoids inventing projected create/update counts when server preview does not supply them
|
||||
PASS: T08 commit surface warns against duplicate user submission during background mutation
|
||||
PASS: T06 compatibility wrapper preserves queueTitle?:string
|
||||
PASS: T06 compatibility wrapper preserves queueDescription?:string
|
||||
PASS: T06 compatibility wrapper preserves queueCount?:number
|
||||
PASS: T06 compatibility wrapper preserves queueCountUnit?:string
|
||||
PASS: T06 wrapper forwards work-surface title/count/actions instead of silently dropping them
|
||||
PASS: T06 distinguishes visible-page pressure metrics from authoritative total count
|
||||
PASS: real T06 queue exposes authoritative total volume in the standard heading
|
||||
PASS: real T06 lets operators jump directly to the next urgent exception without rescanning the grid
|
||||
PASS: real T06 provides an explicit mine-work shortcut using the existing server filter contract
|
||||
PASS: T07 exposes explicit all/mismatch/pending/resolved quick-result navigation
|
||||
PASS: T07 keeps row comparison context in a dedicated evidence drawer
|
||||
PASS: T07 custom detail action respects frontend permission expression while backend remains authoritative
|
||||
PASS: T07 resolution policy forbids client-side reconciliation truth mutation
|
||||
PASS: T09 records only server-confirmed scans, including idempotent retry confirmation
|
||||
PASS: T09 start/exception mutation failures now produce actionable operator feedback
|
||||
PASS: T09 explicitly separates scan readiness from last authoritative server confirmation
|
||||
PASS: T09 direct quantity action is fail-closed during offline/pending/server-confirmation states
|
||||
PASS: T08 governance now matches the six-stage Business UX standard
|
||||
PASS: T06 governance distinguishes authoritative total and visible pressure
|
||||
PASS: T07 governance requires evidence-preserving row detail
|
||||
PASS: T09 governance requires authoritative scan truth visibility
|
||||
PASS: six-stage T08 business component change is versioned
|
||||
PASS: T06 wrapper contract fix is versioned
|
||||
PASS: static HTML reference identifies the v55 recovery-workbench build
|
||||
PASS: static reference CSP preserves connect-src 'self'
|
||||
PASS: static reference CSP preserves frame-src 'none'
|
||||
PASS: static reference CSP preserves worker-src 'none'
|
||||
PASS: static reference CSP preserves object-src 'none'
|
||||
PASS: static reference CSP preserves frame-ancestors 'none'
|
||||
PASS: static HTML/JavaScript T08 retains the same six-stage import grammar
|
||||
PASS: static T08 mirrors validation error focus navigation
|
||||
PASS: static T09 distinguishes last confirmed scan from buffered/invalid input
|
||||
PASS: static CSS provides concrete recovery/scan-truth work surfaces
|
||||
PASS: new static styles use defined theme variables and retain high-contrast support
|
||||
PASS: FE recovery workbench hardening v55 contract
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
Files /mnt/data/kbx-v56-work/kbx-v55-work/kbx-foundation-v55-fe-recovery-workbench-hardening/README.md and /mnt/data/kbx-v56-work/kbx-foundation-v56-fe-interaction-closure-hardening/README.md differ
|
||||
Files /mnt/data/kbx-v56-work/kbx-v55-work/kbx-foundation-v55-fe-recovery-workbench-hardening/apps/web/fe-reference/index.html and /mnt/data/kbx-v56-work/kbx-foundation-v56-fe-interaction-closure-hardening/apps/web/fe-reference/index.html differ
|
||||
Files /mnt/data/kbx-v56-work/kbx-v55-work/kbx-foundation-v55-fe-recovery-workbench-hardening/apps/web/fe-reference/kbx-fe.css and /mnt/data/kbx-v56-work/kbx-foundation-v56-fe-interaction-closure-hardening/apps/web/fe-reference/kbx-fe.css differ
|
||||
Files /mnt/data/kbx-v56-work/kbx-v55-work/kbx-foundation-v55-fe-recovery-workbench-hardening/apps/web/fe-reference/kbx-fe.js and /mnt/data/kbx-v56-work/kbx-foundation-v56-fe-interaction-closure-hardening/apps/web/fe-reference/kbx-fe.js differ
|
||||
Files /mnt/data/kbx-v56-work/kbx-v55-work/kbx-foundation-v55-fe-recovery-workbench-hardening/apps/web/src/App.vue and /mnt/data/kbx-v56-work/kbx-foundation-v56-fe-interaction-closure-hardening/apps/web/src/App.vue differ
|
||||
Files /mnt/data/kbx-v56-work/kbx-v55-work/kbx-foundation-v55-fe-recovery-workbench-hardening/apps/web/src/http/demo/installFrontendDemoApi.ts and /mnt/data/kbx-v56-work/kbx-foundation-v56-fe-interaction-closure-hardening/apps/web/src/http/demo/installFrontendDemoApi.ts differ
|
||||
Files /mnt/data/kbx-v56-work/kbx-v55-work/kbx-foundation-v55-fe-recovery-workbench-hardening/apps/web/src/modules/common/operations/OperationsQueuePage.vue and /mnt/data/kbx-v56-work/kbx-foundation-v56-fe-interaction-closure-hardening/apps/web/src/modules/common/operations/OperationsQueuePage.vue differ
|
||||
Files /mnt/data/kbx-v56-work/kbx-v55-work/kbx-foundation-v55-fe-recovery-workbench-hardening/apps/web/src/modules/common/operations/operationsApi.ts and /mnt/data/kbx-v56-work/kbx-foundation-v56-fe-interaction-closure-hardening/apps/web/src/modules/common/operations/operationsApi.ts differ
|
||||
Files /mnt/data/kbx-v56-work/kbx-v55-work/kbx-foundation-v55-fe-recovery-workbench-hardening/apps/web/src/modules/wms/picking/useWmsPicking.ts and /mnt/data/kbx-v56-work/kbx-foundation-v56-fe-interaction-closure-hardening/apps/web/src/modules/wms/picking/useWmsPicking.ts differ
|
||||
Files /mnt/data/kbx-v56-work/kbx-v55-work/kbx-foundation-v55-fe-recovery-workbench-hardening/apps/web/src/modules/wms/picking/wmsRetryQueue.ts and /mnt/data/kbx-v56-work/kbx-foundation-v56-fe-interaction-closure-hardening/apps/web/src/modules/wms/picking/wmsRetryQueue.ts differ
|
||||
Files /mnt/data/kbx-v56-work/kbx-v55-work/kbx-foundation-v55-fe-recovery-workbench-hardening/design/figma/components.contract.json and /mnt/data/kbx-v56-work/kbx-foundation-v56-fe-interaction-closure-hardening/design/figma/components.contract.json differ
|
||||
Only in /mnt/data/kbx-v56-work/kbx-foundation-v56-fe-interaction-closure-hardening/docs/evidence: v56-environment-limitations.txt
|
||||
Only in /mnt/data/kbx-v56-work/kbx-foundation-v56-fe-interaction-closure-hardening/docs/evidence: v56-fe-interaction-closure-validation.txt
|
||||
Only in /mnt/data/kbx-v56-work/kbx-foundation-v56-fe-interaction-closure-hardening/docs/evidence: v56-typescript-syntax.log
|
||||
Only in /mnt/data/kbx-v56-work/kbx-foundation-v56-fe-interaction-closure-hardening/docs/evidence: v56-validate-kbx-full.log
|
||||
Only in /mnt/data/kbx-v56-work/kbx-foundation-v56-fe-interaction-closure-hardening/docs/frontend: KBX-FE-Interaction-Closure-Hardening-v56.md
|
||||
Files /mnt/data/kbx-v56-work/kbx-v55-work/kbx-foundation-v55-fe-recovery-workbench-hardening/generated/component-manifest.json and /mnt/data/kbx-v56-work/kbx-foundation-v56-fe-interaction-closure-hardening/generated/component-manifest.json differ
|
||||
Files /mnt/data/kbx-v56-work/kbx-v55-work/kbx-foundation-v55-fe-recovery-workbench-hardening/generated/release-impact.json and /mnt/data/kbx-v56-work/kbx-foundation-v56-fe-interaction-closure-hardening/generated/release-impact.json differ
|
||||
Files /mnt/data/kbx-v56-work/kbx-v55-work/kbx-foundation-v55-fe-recovery-workbench-hardening/generated/release-notes.md and /mnt/data/kbx-v56-work/kbx-foundation-v56-fe-interaction-closure-hardening/generated/release-notes.md differ
|
||||
Files /mnt/data/kbx-v56-work/kbx-v55-work/kbx-foundation-v55-fe-recovery-workbench-hardening/package.json and /mnt/data/kbx-v56-work/kbx-foundation-v56-fe-interaction-closure-hardening/package.json differ
|
||||
Files /mnt/data/kbx-v56-work/kbx-v55-work/kbx-foundation-v55-fe-recovery-workbench-hardening/packages/kbx-ui/src/components/KbxDialog.vue and /mnt/data/kbx-v56-work/kbx-foundation-v56-fe-interaction-closure-hardening/packages/kbx-ui/src/components/KbxDialog.vue differ
|
||||
Files /mnt/data/kbx-v56-work/kbx-v55-work/kbx-foundation-v55-fe-recovery-workbench-hardening/packages/kbx-ui/src/components/KbxDrawer.vue and /mnt/data/kbx-v56-work/kbx-foundation-v56-fe-interaction-closure-hardening/packages/kbx-ui/src/components/KbxDrawer.vue differ
|
||||
Files /mnt/data/kbx-v56-work/kbx-v55-work/kbx-foundation-v55-fe-recovery-workbench-hardening/packages/kbx-ui/src/components/KbxExcelImport.vue and /mnt/data/kbx-v56-work/kbx-foundation-v56-fe-interaction-closure-hardening/packages/kbx-ui/src/components/KbxExcelImport.vue differ
|
||||
Files /mnt/data/kbx-v56-work/kbx-v55-work/kbx-foundation-v55-fe-recovery-workbench-hardening/packages/kbx-ui/src/components/KbxLookupDialog.vue and /mnt/data/kbx-v56-work/kbx-foundation-v56-fe-interaction-closure-hardening/packages/kbx-ui/src/components/KbxLookupDialog.vue differ
|
||||
Files /mnt/data/kbx-v56-work/kbx-v55-work/kbx-foundation-v55-fe-recovery-workbench-hardening/packages/kbx-ui/src/registry/componentManifest.ts and /mnt/data/kbx-v56-work/kbx-foundation-v56-fe-interaction-closure-hardening/packages/kbx-ui/src/registry/componentManifest.ts differ
|
||||
Only in /mnt/data/kbx-v56-work/kbx-foundation-v56-fe-interaction-closure-hardening/scripts: validate-fe-interaction-closure-v56.mjs
|
||||
Files /mnt/data/kbx-v56-work/kbx-v55-work/kbx-foundation-v55-fe-recovery-workbench-hardening/scripts/validate-kbx.mjs and /mnt/data/kbx-v56-work/kbx-foundation-v56-fe-interaction-closure-hardening/scripts/validate-kbx.mjs differ
|
||||
Files /mnt/data/kbx-v56-work/kbx-v55-work/kbx-foundation-v55-fe-recovery-workbench-hardening/tests/e2e/kbx-keyboard-regression.spec.ts and /mnt/data/kbx-v56-work/kbx-foundation-v56-fe-interaction-closure-hardening/tests/e2e/kbx-keyboard-regression.spec.ts differ
|
||||
Files /mnt/data/kbx-v56-work/kbx-v55-work/kbx-foundation-v55-fe-recovery-workbench-hardening/tests/e2e/oms-order-import.spec.ts and /mnt/data/kbx-v56-work/kbx-foundation-v56-fe-interaction-closure-hardening/tests/e2e/oms-order-import.spec.ts differ
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
KBX Foundation v56 runtime evidence limitations
|
||||
|
||||
- Node.js is available: v22.16.0.
|
||||
- pnpm is not installed in PATH.
|
||||
- Corepack attempted to resolve pnpm, but this isolated environment cannot reach registry.npmjs.org (EAI_AGAIN).
|
||||
- No project node_modules are present, so Vite production build, Vitest, and Playwright Vue runtime suites were not executed.
|
||||
- Chromium is installed at /usr/bin/chromium.
|
||||
- A static FE Reference render was attempted through a local HTTP server with headless Chromium at 1440x900.
|
||||
- Chromium did not produce a screenshot and was terminated after 12 seconds; logs show unavailable DBus and zygote communication errors.
|
||||
- Therefore v56 does NOT claim browser-runtime PASS. Static/contract/syntax/governance PASS and browser-runtime PASS remain deliberately separate evidence classes.
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
PASS: Dialog restores focus only to a still-valid invoking control after overlay close
|
||||
PASS: Dialog restores focus without unexpected viewport jumps
|
||||
PASS: Drawer restores focus only to a still-valid invoking control after overlay close
|
||||
PASS: Drawer restores focus without unexpected viewport jumps
|
||||
PASS: Dialog supports an explicit owner-managed focus lifecycle escape hatch
|
||||
PASS: Lookup disables generic Dialog restore because successful selection intentionally advances to the next field
|
||||
PASS: T08 transfers keyboard focus to the new six-stage work surface after DOM replacement
|
||||
PASS: T08 gives every import stage an explicit focus landing
|
||||
PASS: T06 FE consumes the authoritative bulk claim result instead of treating HTTP 200 as all-success
|
||||
PASS: T06 renders partial bulk outcome as a persistent inline receipt
|
||||
PASS: T06 partial bulk receipt exposes concrete recovery actions
|
||||
PASS: T09 safe-retry storage is tenant/user-scope partitioned and anonymous contexts remain session-only
|
||||
PASS: T09 retires the old unscoped shared-PDA retry queue instead of replaying it
|
||||
PASS: T09 bounds and validates browser-persisted retry commands before replay
|
||||
PASS: T09 binds retry persistence to the same tenant/user UI scope used by the application shell
|
||||
PASS: Runtime feedback/telemetry metadata identifies the actual v56 FE build instead of stale v51
|
||||
PASS: Demo API mirrors the real partial bulk claim response contract
|
||||
PASS: Demo T08 supplies a complete required-field mapping so browser Golden Flow can actually reach validation
|
||||
PASS: Static HTML reference identifies the v56 interaction-closure build
|
||||
PASS: Static HTML/JavaScript T08 mirrors stage-focus continuity without stealing focus on progress polling rerenders
|
||||
PASS: Static T07 no longer fabricates reconciliation success by mutating reference truth client-side
|
||||
PASS: Static import stage focus remains visible in normal and forced-color themes
|
||||
PASS: T08 E2E no longer depends on a missing binary workbook fixture
|
||||
PASS: T08 E2E asserts stage-focus continuity through terminal result
|
||||
PASS: Keyboard regression explicitly covers Lookup Esc focus restoration
|
||||
PASS: T08 focus lifecycle change is component-versioned
|
||||
PASS: Overlay focus lifecycle changes are component-versioned
|
||||
PASS: FE interaction closure hardening v56 contract
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
.gitea/workflows/kbx-quality-gate.yaml
|
||||
.gitea/workflows/kbx-release-readiness.yaml
|
||||
README.md
|
||||
apps/web/fe-reference/index.html
|
||||
apps/web/src/App.vue
|
||||
apps/web/src/http/demo/installFrontendDemoApi.ts
|
||||
apps/web/src/modules/common/operations/OperationsQueuePage.vue
|
||||
apps/web/src/modules/common/operations/operations.definition.ts
|
||||
apps/web/src/modules/common/reconcile/ReconcilePage.vue
|
||||
apps/web/src/modules/common/reconcile/reconcile.definition.ts
|
||||
apps/web/src/modules/common/reconcile/reconcileApi.ts
|
||||
apps/web/src/modules/wms/picking/picking.definition.ts
|
||||
apps/web/src/modules/wms/picking/useWmsPicking.ts
|
||||
Only in ./docs/evidence: v57-corepack-attempt.log
|
||||
Only in ./docs/evidence: v57-environment-limitations.txt
|
||||
Only in ./docs/evidence: v57-fe-authoritative-outcome-validation.txt
|
||||
Only in ./docs/evidence: v57-runtime-readiness.json
|
||||
Only in ./docs/evidence: v57-typescript-syntax.log
|
||||
Only in ./docs/evidence: v57-validate-kbx-full.log
|
||||
Only in ./docs/frontend: KBX-FE-Authoritative-Outcome-Runtime-Evidence-v57.md
|
||||
generated/release-impact.json
|
||||
generated/release-notes.md
|
||||
generated/screen-manifest.json
|
||||
package.json
|
||||
Only in .: playwright.config.ts
|
||||
Only in ./scripts: report-fe-runtime-readiness.mjs
|
||||
Only in ./scripts: validate-fe-authoritative-outcome-v57.mjs
|
||||
scripts/validate-fe-interaction-closure-v56.mjs
|
||||
scripts/validate-kbx.mjs
|
||||
tests/e2e/common-operations.spec.ts
|
||||
tests/e2e/common-reconcile.spec.ts
|
||||
tests/e2e/wms-picking.spec.ts
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
KBX v57 runtime environment limitations
|
||||
======================================
|
||||
|
||||
Static/contract validation:
|
||||
- node scripts/validate-kbx.mjs: PASS
|
||||
- node scripts/validate-typescript-syntax.mjs: PASS (310 TS/Vue script units)
|
||||
- node scripts/validate-fe-authoritative-outcome-v57.mjs: PASS
|
||||
|
||||
Runtime readiness:
|
||||
- Node: v22.16.0
|
||||
- packageManager declared: pnpm@10.33.4
|
||||
- pnpm-lock.yaml: absent
|
||||
- node_modules: absent
|
||||
- @playwright/test installed: no
|
||||
- vitest installed: no
|
||||
- Playwright config/demo server wiring: present
|
||||
|
||||
Dependency installation attempt:
|
||||
- `corepack pnpm --version` attempted to resolve pnpm 10.33.4.
|
||||
- It failed because registry.npmjs.org DNS lookup returned EAI_AGAIN in this container.
|
||||
- The full captured error is in docs/evidence/v57-corepack-attempt.log.
|
||||
|
||||
Consequences:
|
||||
- No pnpm-lock.yaml was fabricated by hand.
|
||||
- No dependency install success is claimed.
|
||||
- No Vite production build/typecheck runtime is claimed.
|
||||
- No Vitest runtime PASS is claimed.
|
||||
- No Playwright browser E2E PASS is claimed.
|
||||
|
||||
Release policy:
|
||||
- Static KBX governance may pass independently.
|
||||
- Release Readiness workflow fails when pnpm-lock.yaml is absent so runtime evidence cannot be silently treated as PASS.
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
PASS: Runtime metadata identifies the v57 authoritative-outcome build
|
||||
PASS: T06 API preserves authoritative resolve counts
|
||||
PASS: T06 maps HTTP success to authoritative business outcome instead of all-success
|
||||
PASS: T06 resolved telemetry is emitted only for actual server-resolved work
|
||||
PASS: T06 persists and focuses the authoritative action receipt
|
||||
PASS: T06 partial resolve gives explicit recovery guidance
|
||||
PASS: T07 API preserves authoritative exception-creation counts
|
||||
PASS: T07 consumes server result counts without client inference
|
||||
PASS: T07 partial result persists as a focusable recovery receipt
|
||||
PASS: T07 routes to resolution work without mutating source truth
|
||||
PASS: Demo API carries real T06/T07 contract field: sourceModule
|
||||
PASS: Demo API carries real T06/T07 contract field: referenceNo
|
||||
PASS: Demo API carries real T06/T07 contract field: ageMinutes
|
||||
PASS: Demo API carries real T06/T07 contract field: allowManualResolution
|
||||
PASS: Demo API carries real T06/T07 contract field: expectedValue
|
||||
PASS: Demo API carries real T06/T07 contract field: actualValue
|
||||
PASS: Demo API carries real T06/T07 contract field: differenceValue
|
||||
PASS: Demo API carries real T06/T07 contract field: summary
|
||||
PASS: Demo Reconcile mirrors authoritative create-exception outcome
|
||||
PASS: Demo resolve advances server-like version when authoritative state changes
|
||||
PASS: T09 scan mutation captures issuing tenant/user scope
|
||||
PASS: T09 success/ambiguity always reads or writes the captured issuing scope
|
||||
PASS: T09 stale in-flight response cannot mutate a newly switched operator UI
|
||||
PASS: T09 retry replay is pinned to its captured scope for the full async loop
|
||||
PASS: T09 operator scope switch abandons prior task truth and clears prior server-confirmation display
|
||||
PASS: Root packageManager uses an exact Corepack-compatible pnpm semver
|
||||
PASS: Root declares actual Playwright and Vitest test runners
|
||||
PASS: Root exposes executable FE QA script: test:unit
|
||||
PASS: Root exposes executable FE QA script: test:e2e
|
||||
PASS: Root exposes executable FE QA script: test:e2e:desktop
|
||||
PASS: Root exposes executable FE QA script: test:e2e:wms
|
||||
PASS: Root exposes executable FE QA script: test:fe
|
||||
PASS: Root exposes executable FE QA script: validate:fe-authoritative-outcome
|
||||
PASS: Playwright owns baseURL and starts the demo runtime when no external URL is supplied
|
||||
PASS: Playwright defines the KBX 1440×900 desktop evidence project
|
||||
PASS: Playwright defines the KBX 390×844 WMS evidence project
|
||||
PASS: Playwright retains failure evidence instead of ceremonial pass/fail only
|
||||
PASS: T06 E2E proves HTTP 200 + business rejection and focusable recovery receipt
|
||||
PASS: T06 E2E proves corrected demo DTO drives urgent work navigation
|
||||
PASS: T07 E2E proves HTTP 200 + skipped business outcome and recovery focus
|
||||
PASS: T07 E2E protects the source-truth policy in the actual UI flow
|
||||
PASS: T09 E2E models response loss followed by one safe retry
|
||||
PASS: T09 E2E asserts the exact idempotency key is reused across ambiguous retry
|
||||
PASS: T09 E2E separates local scan from server-confirmed truth
|
||||
PASS: Quality Gate executes runtime FE evidence step when reproducible install is available: pnpm typecheck:web
|
||||
PASS: Quality Gate executes runtime FE evidence step when reproducible install is available: pnpm test:unit
|
||||
PASS: Quality Gate executes runtime FE evidence step when reproducible install is available: pnpm build:web:demo
|
||||
PASS: Quality Gate executes runtime FE evidence step when reproducible install is available: pnpm test:e2e
|
||||
PASS: Quality Gate reports missing lockfile as blocked runtime evidence instead of fake PASS
|
||||
PASS: Release Readiness fails closed when reproducible FE runtime evidence cannot be installed
|
||||
PASS: Static HTML reference identifies the v57 runtime-evidence build
|
||||
PASS: apps/web/src/modules/common/operations/operations.definition.ts versions the v57 behavior contract
|
||||
PASS: apps/web/src/modules/common/reconcile/reconcile.definition.ts versions the v57 behavior contract
|
||||
PASS: apps/web/src/modules/wms/picking/picking.definition.ts versions the v57 behavior contract
|
||||
PASS: FE authoritative outcome + runtime evidence hardening v57 contract
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"generatedAt": "2026-08-11T23:26:30.605Z",
|
||||
"node": "v22.16.0",
|
||||
"packageManager": "pnpm@10.33.4",
|
||||
"lockfile": false,
|
||||
"nodeModules": false,
|
||||
"playwrightConfig": true,
|
||||
"playwrightInstalled": false,
|
||||
"vitestInstalled": false,
|
||||
"demoRuntimeScript": true,
|
||||
"e2eScript": true,
|
||||
"status": "blocked",
|
||||
"blockers": [
|
||||
"pnpm-lock.yaml is absent; reproducible dependency install evidence is not available.",
|
||||
"node_modules is absent in this artifact environment.",
|
||||
"@playwright/test is declared but not installed in this artifact environment.",
|
||||
"vitest is declared but not installed in this artifact environment."
|
||||
]
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
CHANGED README.md
|
||||
CHANGED apps/web/fe-reference/index.html
|
||||
CHANGED apps/web/src/App.vue
|
||||
CHANGED apps/web/src/http/demo/installFrontendDemoApi.ts
|
||||
CHANGED apps/web/src/modules/common/ux-metrics/UxMetricsPage.vue
|
||||
CHANGED apps/web/src/modules/erp/items/ItemMasterPage.vue
|
||||
CHANGED apps/web/src/modules/oms/claims/ClaimsPage.vue
|
||||
CHANGED apps/web/src/modules/oms/claims/claims.definition.ts
|
||||
CHANGED apps/web/src/modules/wms/work/WmsWorkPage.vue
|
||||
CHANGED apps/web/src/modules/wms/work/work.definition.ts
|
||||
CHANGED apps/web/src/modules/wms/work/workApi.ts
|
||||
CHANGED design/figma/components.contract.json
|
||||
ADDED docs/evidence/v58-browser-render-attempt.log
|
||||
ADDED docs/evidence/v58-corepack-attempt.log
|
||||
ADDED docs/evidence/v58-environment-limitations.txt
|
||||
ADDED docs/evidence/v58-fe-workflow-queue-runtime-validation.txt
|
||||
ADDED docs/evidence/v58-runtime-readiness.json
|
||||
ADDED docs/evidence/v58-typescript-syntax.log
|
||||
ADDED docs/evidence/v58-validate-kbx-full.log
|
||||
ADDED docs/frontend/KBX-FE-Workflow-Queue-Runtime-Hardening-v58.md
|
||||
CHANGED generated/component-manifest.json
|
||||
CHANGED generated/release-impact.json
|
||||
CHANGED generated/release-notes.md
|
||||
CHANGED generated/screen-manifest.json
|
||||
CHANGED package.json
|
||||
CHANGED packages/kbx-ui/src/registry/componentManifest.ts
|
||||
CHANGED packages/kbx-ui/src/shell/KbxHomePage.vue
|
||||
CHANGED scripts/validate-fe-authoritative-outcome-v57.mjs
|
||||
CHANGED scripts/validate-fe-work-surface-v54.mjs
|
||||
ADDED scripts/validate-fe-workflow-queue-runtime-v58.mjs
|
||||
CHANGED scripts/validate-kbx.mjs
|
||||
CHANGED tests/e2e/oms-claims.spec.ts
|
||||
ADDED tests/e2e/wms-work-queue.spec.ts
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
56cbb8126c3d1c0a70f4ee5b354643ccef3d2eed78e321ec719e62063b8be4e9 apps/web/src/modules/oms/claims/ClaimsPage.vue
|
||||
654ba1bc858fa562439ddb8cb5197d5e9661847cb08a64cf76c33f0ac09d199c apps/web/src/modules/oms/claims/claims.definition.ts
|
||||
9807023ec3e3cf35ffc6bdec01efd8451ed5361bb07684baaa8a85c7589fc9bb apps/web/src/modules/wms/work/WmsWorkPage.vue
|
||||
54c1c59f0aca07e85d1619749608a5236a328e3253ef97e1651d7517e328059d apps/web/src/modules/wms/work/workApi.ts
|
||||
8d5868c65769a188c110988a2179fbe35c1efd6407c354bf5dca4bc9a65f4301 packages/kbx-ui/src/shell/KbxHomePage.vue
|
||||
b8bd773b2b7a972def8686e753f7d8827a4f975031cebce805c68444caafa6c0 scripts/validate-fe-workflow-queue-runtime-v58.mjs
|
||||
a96579c63005a745569f023959f15e2a6fbb8271284e3f86adb5b67660308f98 docs/evidence/v58-validate-kbx-full.log
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
KBX v58 Runtime Evidence Limitations
|
||||
|
||||
1. Repository artifact has no pnpm-lock.yaml and no node_modules.
|
||||
2. Corepack pnpm@10.33.4 download was attempted again in this environment.
|
||||
3. registry.npmjs.org DNS lookup failed with EAI_AGAIN, so dependencies cannot be installed reproducibly here.
|
||||
4. Therefore Vite production build, Vue typecheck, Vitest runtime, and Playwright runtime are NOT claimed as PASS.
|
||||
5. System Chromium 144 static-reference --dump-dom was attempted with headless/no-sandbox flags.
|
||||
6. Chromium timed out with DBus/zygote errors and produced no DOM evidence; it is NOT counted as browser PASS.
|
||||
7. node scripts/validate-kbx.mjs and v58 static/runtime-contract gates PASS, but these are intentionally separated from browser runtime evidence.
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
PASS: Runtime metadata identifies the v58 workflow/queue build
|
||||
PASS: OMS claims removes the invalid grid selection v-model contract
|
||||
PASS: OMS claims passes real selection count to CommandBar and consumes KbxDataGrid selectionChanged
|
||||
PASS: OMS claims connects every visible workflow transition to a real API action
|
||||
PASS: OMS claims bulk transition continues through per-row failures and records partial outcome
|
||||
PASS: OMS claims leaves a persistent focusable authoritative action receipt
|
||||
PASS: OMS claims workflow represents HOLD as an explicit domain state instead of an orphan command
|
||||
PASS: OMS claims screen versions the repaired workflow contract
|
||||
PASS: Demo claims carries real DTO/state field: status:'REQUESTED'
|
||||
PASS: Demo claims carries real DTO/state field: status:'APPROVED'
|
||||
PASS: Demo claims carries real DTO/state field: status:'IN_PROGRESS'
|
||||
PASS: Demo claims carries real DTO/state field: status:'HOLD'
|
||||
PASS: Demo claims carries real DTO/state field: channelName
|
||||
PASS: Demo claims carries real DTO/state field: requestedQty
|
||||
PASS: Demo claims carries real DTO/state field: ownerName
|
||||
PASS: Demo claims mirrors backend transition preconditions
|
||||
PASS: Demo invalid claim transition rejects as an actual HTTP/business failure rather than resolving a fake success
|
||||
PASS: T06 WMS queue starts with all statuses and loads actionable work on entry
|
||||
PASS: T06 WMS queue computes status summary independently from the active status filter
|
||||
PASS: T06 WMS queue surfaces blocked work even before the user narrows to BLOCKED
|
||||
PASS: T06 WMS queue fails closed instead of presenting BLOCKED work as startable
|
||||
PASS: T06 WMS screen versions the queue-signal contract without duplicating the implicit 전체 option
|
||||
PASS: Home counts only user-launchable menu screens instead of hidden utility entries
|
||||
PASS: SearchPanel whole-object v-model uses writable refs; unsafe bindings: none
|
||||
PASS: UX metrics fixes the same writable-search runtime contract instead of leaving a known sibling defect
|
||||
PASS: Claims E2E covers selection, start transition, authoritative receipt, and HOLD state
|
||||
PASS: WMS queue E2E proves initial exception visibility and fail-closed blocked action
|
||||
PASS: Static HTML reference identifies the v58 workflow/queue hardening build
|
||||
PASS: FE workflow truth + queue signal + runtime hardening v58 contract
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"generatedAt": "2026-08-11T23:50:55.386Z",
|
||||
"node": "v22.16.0",
|
||||
"packageManager": "pnpm@10.33.4",
|
||||
"lockfile": false,
|
||||
"nodeModules": false,
|
||||
"playwrightConfig": true,
|
||||
"playwrightInstalled": false,
|
||||
"vitestInstalled": false,
|
||||
"demoRuntimeScript": true,
|
||||
"e2eScript": true,
|
||||
"status": "blocked",
|
||||
"blockers": [
|
||||
"pnpm-lock.yaml is absent; reproducible dependency install evidence is not available.",
|
||||
"node_modules is absent in this artifact environment.",
|
||||
"@playwright/test is declared but not installed in this artifact environment.",
|
||||
"vitest is declared but not installed in this artifact environment."
|
||||
]
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
MODIFIED apps/web/fe-reference/index.html
|
||||
MODIFIED apps/web/fe-reference/kbx-fe.css
|
||||
MODIFIED apps/web/fe-reference/kbx-fe.js
|
||||
MODIFIED apps/web/src/App.vue
|
||||
MODIFIED apps/web/src/http/demo/installFrontendDemoApi.ts
|
||||
MODIFIED apps/web/src/modules/common/design-system/ComponentCatalogPage.vue
|
||||
MODIFIED apps/web/src/modules/common/experiments/ExperimentsPage.vue
|
||||
MODIFIED apps/web/src/modules/common/experiments/experiments.definition.ts
|
||||
MODIFIED apps/web/src/modules/common/external-data/ExternalDataStatusPage.vue
|
||||
MODIFIED apps/web/src/modules/common/external-data/external-data.definition.ts
|
||||
MODIFIED apps/web/src/modules/common/operations/operations.definition.ts
|
||||
MODIFIED apps/web/src/modules/common/reconcile/reconcile.definition.ts
|
||||
MODIFIED apps/web/src/modules/oms/claims/claims.definition.ts
|
||||
MODIFIED apps/web/src/modules/oms/orders/search/OrderDetailDrawer.vue
|
||||
MODIFIED apps/web/src/modules/oms/orders/search/order-list.definition.ts
|
||||
MODIFIED apps/web/src/modules/wms/work/work.definition.ts
|
||||
ADDED apps/web/src/registry/statusCatalog.ts
|
||||
MODIFIED design/figma/components.contract.json
|
||||
ADDED docs/evidence/v59-corepack-attempt.log
|
||||
ADDED docs/evidence/v59-design-debt.log
|
||||
ADDED docs/evidence/v59-environment-limitations.txt
|
||||
ADDED docs/evidence/v59-fe-grid-status-validation.txt
|
||||
ADDED docs/evidence/v59-runtime-readiness-output.txt
|
||||
ADDED docs/evidence/v59-runtime-readiness.json
|
||||
ADDED docs/evidence/v59-typescript-syntax.log
|
||||
ADDED docs/evidence/v59-validate-kbx-full.log
|
||||
ADDED docs/frontend/KBX-FE-Grid-Status-Dictionary-Hardening-v59.md
|
||||
MODIFIED generated/component-manifest.json
|
||||
MODIFIED generated/field-adoption-report.json
|
||||
MODIFIED generated/release-impact.json
|
||||
MODIFIED generated/release-notes.md
|
||||
MODIFIED generated/screen-manifest.json
|
||||
MODIFIED package.json
|
||||
MODIFIED packages/kbx-contracts/src/grid.ts
|
||||
MODIFIED packages/kbx-contracts/src/status.ts
|
||||
MODIFIED packages/kbx-ui/src/components/KbxDataGrid.vue
|
||||
MODIFIED packages/kbx-ui/src/components/KbxStatus.vue
|
||||
ADDED packages/kbx-ui/src/grid/status.ts
|
||||
MODIFIED packages/kbx-ui/src/index.ts
|
||||
MODIFIED packages/kbx-ui/src/registry/componentManifest.ts
|
||||
MODIFIED scripts/validate-fe-authoritative-outcome-v57.mjs
|
||||
ADDED scripts/validate-fe-grid-status-v59.mjs
|
||||
MODIFIED scripts/validate-fe-workflow-queue-runtime-v58.mjs
|
||||
MODIFIED scripts/validate-kbx.mjs
|
||||
ADDED tests/unit/kbx-grid-status.contract.spec.ts
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
8f24946c5d268f70378d0b5ac2446a71ca374747cacf14679edb6f539cd32f6f docs/frontend/KBX-FE-Grid-Status-Dictionary-Hardening-v59.md
|
||||
6242698908a9c69be2c9a70237bb5a333f215bee81adc24c518a91ea83a8092f docs/evidence/v59-validate-kbx-full.log
|
||||
9c0cf771b745814307f60165ad8bb73e675cd0e9071978c9ecbf675987fcda6e docs/evidence/v59-fe-grid-status-validation.txt
|
||||
73581ee7445889f241faa4ace8643fefa9b63d13d9f9e75d7babdf4f2994ddfd docs/evidence/v59-runtime-readiness.json
|
||||
6a2ce63e4b14b40bdf040a82d3060bd74f0b452366dad038edc876e2802093d2 docs/evidence/v59-corepack-attempt.log
|
||||
ec639b9fd37f116cd4e5b2bb6ce8bff1a46057b6d68ecc346ec645a289f571ca scripts/validate-fe-grid-status-v59.mjs
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
KBX v59 Runtime Evidence Limitations
|
||||
|
||||
1. node scripts/validate-kbx.mjs: PASS.
|
||||
2. node scripts/validate-fe-grid-status-v59.mjs: PASS.
|
||||
3. node scripts/validate-typescript-syntax.mjs: PASS (314 TS/Vue script units).
|
||||
4. Design debt ratchet: PASS (116 <= 131).
|
||||
5. pnpm-lock.yaml is absent.
|
||||
6. node_modules is absent.
|
||||
7. @playwright/test and vitest are declared but not installed in this artifact environment.
|
||||
8. corepack pnpm --version attempted on 2026-08-12 and failed because registry.npmjs.org DNS lookup returned EAI_AGAIN.
|
||||
9. Therefore Vite production/demo build, Vitest, and Playwright browser runtime are not claimed as PASS.
|
||||
10. Static HTML JavaScript syntax was checked with node --check; this is not a substitute for browser rendering.
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
PASS: Runtime metadata identifies the v59 grid-status build
|
||||
PASS: Status semantic contract covers ready/info without overloading pending
|
||||
PASS: Grid status columns require an explicit raw-value mapping contract
|
||||
PASS: Unknown domain states fail visibly through the pure status resolver
|
||||
PASS: KbxStatus renders semantic surfaces and a color-independent unknown-state signal
|
||||
PASS: KbxDataGrid status renderer contract: cellRenderer:c.type==='status'?KbxStatus
|
||||
PASS: KbxDataGrid status renderer contract: filterValueGetter:c.type==='status'
|
||||
PASS: KbxDataGrid status renderer contract: statusDisplay(column,params.value)
|
||||
PASS: KbxDataGrid status renderer contract: resolveKbxGridStatus(column.statusMap,value)
|
||||
PASS: CSV exports user labels for statuses while non-status values remain raw
|
||||
PASS: Product status dictionary includes orderLifecycle
|
||||
PASS: Product status dictionary includes orderShipment
|
||||
PASS: Product status dictionary includes claim
|
||||
PASS: Product status dictionary includes workSeverity
|
||||
PASS: Product status dictionary includes workItem
|
||||
PASS: Product status dictionary includes reconcile
|
||||
PASS: Product status dictionary includes wmsWork
|
||||
PASS: Product status dictionary includes experiment
|
||||
PASS: Product status dictionary includes externalData
|
||||
PASS: Canonical OMS/WMS values map to familiar Korean labels
|
||||
PASS: Every application Grid status column has a statusMap; missing: none
|
||||
PASS: Demo order projection uses canonical shipment values instead of pre-translated labels
|
||||
PASS: Order detail removes page-local status semantics and reuses the product dictionary
|
||||
PASS: Unit contract covers canonical preservation and unknown-state visibility
|
||||
PASS: Static HTML/JS reference mirrors semantic status and unknown-state behavior
|
||||
PASS: Static reference uses text-plus-semantic status styling
|
||||
PASS: Static HTML reference identifies the v59 status hardening build
|
||||
PASS: Component versions identify the strengthened Grid/Status contracts
|
||||
PASS: FE grid status dictionary + unknown-state hardening v59 contract
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"generatedAt": "2026-08-12T00:02:28.908Z",
|
||||
"node": "v22.16.0",
|
||||
"packageManager": "pnpm@10.33.4",
|
||||
"lockfile": false,
|
||||
"nodeModules": false,
|
||||
"playwrightConfig": true,
|
||||
"playwrightInstalled": false,
|
||||
"vitestInstalled": false,
|
||||
"demoRuntimeScript": true,
|
||||
"e2eScript": true,
|
||||
"status": "blocked",
|
||||
"blockers": [
|
||||
"pnpm-lock.yaml is absent; reproducible dependency install evidence is not available.",
|
||||
"node_modules is absent in this artifact environment.",
|
||||
"@playwright/test is declared but not installed in this artifact environment.",
|
||||
"vitest is declared but not installed in this artifact environment."
|
||||
]
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"generatedAt": "2026-08-12T00:04:42.878Z",
|
||||
"node": "v22.16.0",
|
||||
"packageManager": "pnpm@10.33.4",
|
||||
"lockfile": false,
|
||||
"nodeModules": false,
|
||||
"playwrightConfig": true,
|
||||
"playwrightInstalled": false,
|
||||
"vitestInstalled": false,
|
||||
"demoRuntimeScript": true,
|
||||
"e2eScript": true,
|
||||
"status": "blocked",
|
||||
"blockers": [
|
||||
"pnpm-lock.yaml is absent; reproducible dependency install evidence is not available.",
|
||||
"node_modules is absent in this artifact environment.",
|
||||
"@playwright/test is declared but not installed in this artifact environment.",
|
||||
"vitest is declared but not installed in this artifact environment."
|
||||
]
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
MODIFIED README.md
|
||||
MODIFIED apps/web/fe-reference/index.html
|
||||
MODIFIED apps/web/fe-reference/kbx-fe.js
|
||||
MODIFIED apps/web/src/App.vue
|
||||
MODIFIED apps/web/src/http/demo/installFrontendDemoApi.ts
|
||||
MODIFIED apps/web/src/modules/oms/orders/register/OrderRegisterPage.vue
|
||||
MODIFIED apps/web/src/modules/oms/orders/register/order-register.definition.ts
|
||||
MODIFIED apps/web/src/modules/oms/orders/register/orderRegisterApi.ts
|
||||
MODIFIED apps/web/src/modules/oms/orders/register/useOrderRegistration.ts
|
||||
MODIFIED apps/web/src/modules/oms/orders/search/order-list.definition.ts
|
||||
MODIFIED apps/web/src/modules/oms/orders/search/orderApi.ts
|
||||
MODIFIED apps/web/src/modules/oms/orders/search/useOrderSearch.ts
|
||||
MODIFIED apps/web/src/registry/statusCatalog.ts
|
||||
MODIFIED backend/Modules/OMS/Orders/Confirm/Endpoint.cs
|
||||
MODIFIED backend/Modules/OMS/Orders/Get/Endpoint.cs
|
||||
ADDED backend/Modules/OMS/Orders/OrderLifecycleStatus.cs
|
||||
ADDED backend/Modules/OMS/Orders/OrderShipmentStatus.cs
|
||||
MODIFIED backend/Modules/OMS/Orders/Register/Handler.cs
|
||||
MODIFIED backend/Modules/OMS/Orders/Ship/Endpoint.cs
|
||||
ADDED docs/evidence/v60-changed-files.txt
|
||||
ADDED docs/evidence/v60-corepack-attempt.log
|
||||
ADDED docs/evidence/v60-design-debt.log
|
||||
ADDED docs/evidence/v60-environment-limitations.txt
|
||||
ADDED docs/evidence/v60-runtime-readiness.json
|
||||
ADDED docs/evidence/v60-status-canonical-validation.txt
|
||||
ADDED docs/evidence/v60-typescript-syntax.log
|
||||
ADDED docs/evidence/v60-validate-kbx-full.log
|
||||
ADDED docs/frontend/KBX-FE-Status-Canonical-Contract-Hardening-v60.md
|
||||
MODIFIED docs/release/migration-guide.md
|
||||
MODIFIED generated/release-impact.json
|
||||
MODIFIED generated/release-notes.md
|
||||
MODIFIED generated/screen-manifest.json
|
||||
MODIFIED governance/release.json
|
||||
MODIFIED package.json
|
||||
MODIFIED scripts/validate-fe-grid-status-v59.mjs
|
||||
MODIFIED scripts/validate-kbx.mjs
|
||||
ADDED scripts/validate-status-canonical-v60.mjs
|
||||
ADDED tests/unit/kbx-order-status-canonical.contract.spec.ts
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
a6baecfe4c3acfc79ce9181065828096cde7f13f6b40e66df400bea6926e214a docs/evidence/v60-validate-kbx-full.log
|
||||
633ea799b3335437fc3a6b9370b9ce8cfdb7d33ce9b86b2fa43b88ad3f72ad71 docs/evidence/v60-status-canonical-validation.txt
|
||||
53afcf622e8afcde1a488cedc7d3d49f98876e18e8f3c9d1e39cc2dbdeeb033d docs/evidence/v60-runtime-readiness.json
|
||||
063b6357efc44318812f25da0eed348ce06202bda7ceea0e8eae0c0d45d5221a docs/frontend/KBX-FE-Status-Canonical-Contract-Hardening-v60.md
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
KBX v60 runtime evidence limitations
|
||||
|
||||
- Full repository static/governance regression: PASS.
|
||||
- TypeScript/Vue syntax transpile: PASS.
|
||||
- Design debt ratchet: PASS.
|
||||
- Browser/Vite/Vitest/Playwright runtime evidence: BLOCKED in this artifact environment.
|
||||
- pnpm-lock.yaml: absent.
|
||||
- node_modules: absent.
|
||||
- @playwright/test / vitest: declared but not installed.
|
||||
- `corepack pnpm --version`: failed while fetching pnpm@10.33.4 because registry.npmjs.org DNS returned EAI_AGAIN.
|
||||
- dotnet CLI: unavailable in this container, therefore changed ASP.NET Core code was not compiled here.
|
||||
|
||||
This artifact intentionally does not report Runtime E2E or backend compile PASS.
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"generatedAt": "2026-08-12T10:19:18.977Z",
|
||||
"node": "v22.16.0",
|
||||
"packageManager": "pnpm@10.33.4",
|
||||
"lockfile": false,
|
||||
"nodeModules": false,
|
||||
"playwrightConfig": true,
|
||||
"playwrightInstalled": false,
|
||||
"vitestInstalled": false,
|
||||
"demoRuntimeScript": true,
|
||||
"e2eScript": true,
|
||||
"status": "blocked",
|
||||
"blockers": [
|
||||
"pnpm-lock.yaml is absent; reproducible dependency install evidence is not available.",
|
||||
"node_modules is absent in this artifact environment.",
|
||||
"@playwright/test is declared but not installed in this artifact environment.",
|
||||
"vitest is declared but not installed in this artifact environment."
|
||||
]
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
PASS: Runtime metadata identifies the v60 canonical-status build
|
||||
PASS: OMS backend lifecycle constants are explicit canonical codes
|
||||
PASS: Order GET returns canonical database status without server-side label translation
|
||||
PASS: Order register persists and returns DRAFT through the shared backend constant
|
||||
PASS: Order confirm validates and returns canonical lifecycle codes
|
||||
PASS: OMS backend shipment projection vocabulary is explicit canonical code
|
||||
PASS: Ship request requires CONFIRMED aggregate lifecycle state
|
||||
PASS: Ship request also requires READY shipment projection state for ids and filter modes
|
||||
PASS: Ship request no longer mixes shipment projection states into aggregate lifecycle or fabricates a lifecycle transition
|
||||
PASS: Order lifecycle catalog maps canonical code through familiar label: { value:'NEW', label:'신규'
|
||||
PASS: Order lifecycle catalog maps canonical code through familiar label: { value:'DRAFT', label:'작성'
|
||||
PASS: Order lifecycle catalog maps canonical code through familiar label: { value:'CONFIRMED', label:'확정'
|
||||
PASS: Order lifecycle catalog maps canonical code through familiar label: { value:'SHIPPED', label:'출고완료'
|
||||
PASS: Order lifecycle dictionary no longer uses display labels as raw values
|
||||
PASS: Frontend lifecycle catalog covers backend status DRAFT
|
||||
PASS: Frontend lifecycle catalog covers backend status CONFIRMED
|
||||
PASS: Frontend lifecycle catalog covers backend status ALLOCATED
|
||||
PASS: Frontend lifecycle catalog covers backend status PICKING
|
||||
PASS: Frontend lifecycle catalog covers backend status CHECKED
|
||||
PASS: Frontend lifecycle catalog covers backend status SHIPPED
|
||||
PASS: Status catalog owns select-option projection and canonical filter normalization
|
||||
PASS: Order transaction command/workflow uses canonical status: from:['DRAFT']
|
||||
PASS: Order transaction command/workflow uses canonical status: to:'CONFIRMED'
|
||||
PASS: Order transaction command/workflow uses canonical status: allowedStatuses:['NEW','DRAFT']
|
||||
PASS: Order transaction command/workflow uses canonical status: allowedStatuses:['DRAFT']
|
||||
PASS: Order transaction command/workflow uses canonical status: permissionByStatus:{NEW:'oms.order.create',DRAFT:'oms.order.write'}
|
||||
PASS: Order transaction policy does not use Korean display labels as machine state
|
||||
PASS: Unsaved order state is an explicit client NEW code before server DRAFT
|
||||
PASS: Transaction context displays catalog labels while commands receive raw canonical status
|
||||
PASS: Order status search options are generated from the same catalog as Grid rendering
|
||||
PASS: Route and saved-search status values are validated against the canonical shipment catalog
|
||||
PASS: Order search no longer carries a second hard-coded status allow-list
|
||||
PASS: Demo order detail/register/confirm use canonical lifecycle codes
|
||||
PASS: Demo projection uses shipmentStatus as its single query/bulk source of truth
|
||||
PASS: Demo confirm reproduces authoritative DRAFT-only transition failure
|
||||
PASS: Demo detail fallback translates projection semantics into lifecycle canonical codes instead of reusing shipment state blindly
|
||||
PASS: Static HTML/JS reference stores OMS statuses as canonical codes
|
||||
PASS: Static reference keeps familiar labels and migrates legacy saved display values to canonical codes
|
||||
PASS: Static reference separates shipment and lifecycle display semantics
|
||||
PASS: Static HTML reference identifies the v60 canonical-status build
|
||||
PASS: Unit contract covers raw/display separation, catalog options and invalid persisted display labels
|
||||
PASS: OMS canonical status API + Grid/Filter/Workflow parity hardening v60 contract
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
# KBX Excel Import v1 — Foundation v5
|
||||
|
||||
## 목적
|
||||
|
||||
모든 입력형 업무 화면에서 별도의 Excel 업로드 기능을 다시 만들지 않고 다음 공통 파이프라인을 사용한다.
|
||||
|
||||
`Template → Upload → Mapping → Staging → Validation → Preview → Commit → Audit`
|
||||
|
||||
## 핵심 원칙
|
||||
|
||||
1. Excel은 DB에 직접 반영하지 않는다.
|
||||
2. 원본 행 번호를 유지한다.
|
||||
3. Exact → Alias → Saved Mapping → AI Suggestion의 순서로 매핑하되, 저장 매핑은 동일 source signature에 대해서만 우선 적용한다.
|
||||
4. AI 추천은 미매핑 컬럼만 제안하며 ImportDefinition에 없는 필드를 만들 수 없다.
|
||||
5. 참조 마스터는 행마다 조회하지 않고 batch resolve한다.
|
||||
6. 정상 데이터만 Commit 대상으로 사용한다.
|
||||
7. Commit Job은 Import Session 상태 전이와 Row status로 중복 실행에 안전해야 한다.
|
||||
8. 원본 XLSX와 staging은 14일 기본 보관 후 제거한다. Domain/Audit 데이터는 보존한다.
|
||||
9. SignalR 진행률은 편의 채널이고 Source of Truth는 PostgreSQL Import Session이다.
|
||||
10. 브라우저 종료 후에도 세션 ID로 진행상태를 복구할 수 있다.
|
||||
|
||||
## 사용자 흐름
|
||||
|
||||
- `엑셀 > 업로드 양식 다운로드`
|
||||
- 또는 기존 Excel 선택
|
||||
- 자동매핑 확인
|
||||
- 필요 시 `쿠팡 주문양식` 같은 이름으로 매핑 저장
|
||||
- 검증 실행
|
||||
- 오류 행은 화면 100건 미리보기 또는 오류 XLSX 다운로드
|
||||
- 정상 건 반영
|
||||
- 대량 작업은 Hangfire + SignalR로 진행상태 표시
|
||||
|
||||
## 저장 구조
|
||||
|
||||
- `kbx.import_sessions`: 수명주기, 매핑, 건수, 진행률
|
||||
- `kbx.import_files`: 원본 XLSX bytea. 초기 Modular Monolith용 교체 가능 Adapter 경계
|
||||
- `kbx.import_rows`: raw/normalized/error/warning/domain key staging
|
||||
- `kbx.saved_import_mappings`: 사용자별 재사용 매핑
|
||||
- `kbx.import_job_receipts`: 향후 job observability 확장 지점
|
||||
|
||||
원본 파일을 session metadata와 별도 table에 둬 일반 조회에서 대용량 bytea가 함께 읽히지 않게 한다.
|
||||
|
||||
## 주문 Import 예제
|
||||
|
||||
`oms.orders.v1`은 주문번호별 여러 Excel 행을 하나의 Header/Detail Transaction으로 그룹화한다.
|
||||
|
||||
검증 시 거래처/창고/품목 코드는 distinct 목록을 한 번에 조회하고 실제 Domain ID로 resolve한다. 같은 주문번호의 Header 값이 행마다 다르면 `ORDER_HEADER_CONFLICT`로 차단한다.
|
||||
|
||||
기존 주문은 `NEW/DRAFT` 상태만 Excel update를 허용한다. Commit에서는 주문별 transaction 안에서 Header, Lines, Audit, Outbox, staging status를 같이 변경한다.
|
||||
|
||||
## AI Mapping
|
||||
|
||||
`IImportMappingSuggester`가 확장 지점이다. 기본 구현은 No-op이다. 실제 AI Adapter는 다음 계약을 지켜야 한다.
|
||||
|
||||
- 미매핑 컬럼만 입력
|
||||
- ImportDefinition 내 target field만 반환
|
||||
- confidence/reason 반환
|
||||
- 자동 저장 금지
|
||||
- 데이터 Commit 금지
|
||||
- 사용자 확정 또는 검증 이후만 사용
|
||||
|
||||
이 경계로 LLM hallucination이 DB field나 Domain Entity 생성으로 연결되지 않게 한다.
|
||||
|
||||
## Host wiring
|
||||
|
||||
필요 NuGet/Frontend package 예:
|
||||
|
||||
- ClosedXML
|
||||
- Hangfire.AspNetCore 및 현재 프로젝트가 사용하는 PostgreSQL Hangfire storage
|
||||
- Microsoft.AspNetCore.SignalR은 ASP.NET Core shared framework 사용
|
||||
- `@microsoft/signalr` client
|
||||
|
||||
등록 예:
|
||||
|
||||
```csharp
|
||||
builder.Services.AddKbxExcelImport();
|
||||
// 기존 Hangfire/Npgsql/FastEndpoints 등록
|
||||
|
||||
app.MapKbxExcelImport();
|
||||
```
|
||||
|
||||
Hangfire recurring job에서 `PurgeExpiredImportsJob`을 하루 한 번 실행한다.
|
||||
|
||||
## 향후 교체 지점
|
||||
|
||||
트래픽 증가 시 다음은 인터페이스를 유지하며 교체한다.
|
||||
|
||||
- PostgreSQL bytea → object storage
|
||||
- 전체 Workbook parsing → streaming OpenXML reader
|
||||
- 100k 행 memory grouping → keyset/chunk pipeline
|
||||
- Noop AI Mapping → 승인된 내부 AI tool adapter
|
||||
|
||||
처음부터 분산 스토리지·자체 Excel engine을 만들지는 않는다. 측정된 병목이 생긴 뒤 교체한다.
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
# KBX v18 Executable Scenario Governance
|
||||
|
||||
## 목적
|
||||
|
||||
정적 Screen/Field/API/권한 계약이 맞아도 실제 통합 흐름은 깨질 수 있다. v18은 KBX Definition of Done의 Vitest/Playwright/API Contract 요구를 **업무 Scenario Catalog**로 묶는다.
|
||||
|
||||
## 3계층
|
||||
|
||||
1. **Contract** — 순수 TypeScript/C# 계약, 상태머신, Mapping, Policy
|
||||
2. **Integration** — ephemeral PostgreSQL, 실제 Migration/SQL/Domain/Idempotency/Outbox
|
||||
3. **E2E** — Playwright로 Keyboard/Mouse/Focus/Scanner/Excel/Workspace 실제 흐름
|
||||
|
||||
Screenshot은 Evidence 중 하나일 뿐 업무 성공의 대체물이 아니다.
|
||||
|
||||
## Canonical Scenario
|
||||
|
||||
Golden Screen과 주요 운영 경계는 최소 하나의 canonical scenario를 가져야 한다.
|
||||
|
||||
- OMS-ORD-001: 대량출고 부분성공 + Idempotency replay
|
||||
- OMS-ORD-002: F2/F8 Keyboard + concurrency conflict
|
||||
- OMS-ORD-003: Excel 4 valid / 2 invalid partial commit
|
||||
- WMS-PICK-001: scan replay + wrong-item no-retry
|
||||
- COMMON-OPS-001: stale event 방어
|
||||
- COMMON-REC-001: grace period
|
||||
- ERP-INV-MOVE-001: workflow/DB constraint
|
||||
- Application Shell: unsaved tab
|
||||
|
||||
## 원칙
|
||||
|
||||
- Route 문자열 대신 API `operationId`를 Scenario에서 사용한다.
|
||||
- `idempotency=required` API 호출 Scenario는 고정 idempotency key reference가 필수다.
|
||||
- 실제 고객/주문/전화번호/주소를 Fixture로 복사하지 않는다.
|
||||
- Test 결과는 Scenario ID + Contract Version + Evidence hash로 재현 가능해야 한다.
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
# KBX Experiment Rollout Playbook v17
|
||||
|
||||
1. Draft 0% 상태에서 대상 Screen과 Metric/Guardrail을 검토한다.
|
||||
2. 내부/소규모 5~10%부터 시작한다.
|
||||
3. Variant별 최소 표본 이전에는 성공/실패를 단정하지 않는다.
|
||||
4. Task P95, Validation Failure, 전체 Manual Intervention Rate를 함께 본다.
|
||||
5. Guardrail 위반 시 즉시 rollback한다.
|
||||
6. 개선 후보여도 VOC·업무오류·현장 테스트를 함께 확인한다.
|
||||
7. 승격 시 Experiment 분기를 제거하고 canonical UX로 합친 뒤 Screen/Component Version을 필요한 수준으로 갱신한다.
|
||||
8. 장기간 남은 Feature Flag는 기술부채다. 종료된 Flag/분기는 Release 내에서 제거한다.
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
# External API notes v4
|
||||
|
||||
## AG Grid
|
||||
|
||||
KBX v4 uses the AG Grid v33+ Theming API boundary. `kbxGridTheme.ts` imports `themeQuartz` and applies compact KBX parameters. If AG Grid changes its Theme or row-selection APIs in a future major version, only `@kbx/ui` adapter code should require migration.
|
||||
|
||||
## Gitea Actions
|
||||
|
||||
Gitea Actions is intentionally close to GitHub Actions syntax and supports workflow files under `.gitea/workflows/`. The starter uses `actions/checkout@v4` plus shell steps. Air-gapped installations should mirror external actions into the internal Gitea instance or replace them with internal equivalents.
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
# KBX v21 External Data Governance
|
||||
|
||||
## 1. 목적
|
||||
|
||||
외부 Provider 응답을 화면이 직접 소비하지 않고 `Provider Adapter → Normalizer → Canonical Projection → Provenance` 경계를 통과시킨다. 외부 데이터는 읽기 입력이며 OMS/WMS/ERP Transaction의 Domain Truth가 아니다.
|
||||
|
||||
## 2. 시간 의미
|
||||
|
||||
- `providerObservedAt`: Provider가 명시한 데이터 기준시각. 제공되지 않으면 `null`로 둔다.
|
||||
- `requestedAt`: KBX가 Provider 호출을 시작한 시각.
|
||||
- `receivedAt`: 응답을 KBX가 수신한 시각.
|
||||
- `ingestedAt`: 정상화 후 KBX cache에 기록한 시각.
|
||||
- `freshUntil`: KBX 운영정책상 fresh로 표현 가능한 시각.
|
||||
- `usableUntil`: stale-while-revalidate 정책에서 마지막 정상값을 명시적으로 사용할 수 있는 최종 시각.
|
||||
|
||||
`providerObservedAt`이 없다고 `receivedAt`을 시장/공시 기준시각으로 이름을 바꾸지 않는다.
|
||||
|
||||
## 3. Freshness Mode
|
||||
|
||||
### strict
|
||||
|
||||
만료된 값을 최신값으로 사용하지 않는다. v21 Reference에서는 KIS 현재가가 이 정책을 사용한다.
|
||||
|
||||
### stale-while-revalidate
|
||||
|
||||
Fresh 기간이 지나도 `usableUntil` 안에서는 마지막 정상값을 **Stale임을 명시하여** 보여줄 수 있고 Background Refresh를 예약한다. OPENDART 회사개황/공시목록 Reference가 이 정책을 사용한다.
|
||||
|
||||
### provider-defined
|
||||
|
||||
공급자의 승인 서비스마다 의미가 달라 범용 TTL을 설정하지 않는다. KRX 승인서비스 template이 여기에 해당하며 서비스별 Definition 없이 Runtime 사용을 거부한다.
|
||||
|
||||
위 Freshness 숫자는 KBX 운영정책이며 공급자의 공식 Rate Limit 또는 데이터 보장주기를 의미하지 않는다.
|
||||
|
||||
## 4. 재현성
|
||||
|
||||
기본적으로 raw Provider payload를 DB에 저장하지 않는다. 대신 다음을 남긴다.
|
||||
|
||||
- canonical `request_descriptor` — Secret을 제외한 재현 가능한 조회조건
|
||||
- `payload_sha256`
|
||||
- `normalizer_version`
|
||||
- received/ingested timestamps
|
||||
- correlation id
|
||||
- normalized projection
|
||||
|
||||
Raw payload 저장이 필요하면 라이선스·보안·보존정책을 별도로 승인하고 `encrypted-raw` 정책으로 명시해야 한다.
|
||||
|
||||
## 5. Normalization
|
||||
|
||||
공통 Normalizer가 Provider-specific field를 business screen까지 전달하지 않는다.
|
||||
|
||||
- OPENDART 회사개황 → `KbxCompanyProfileSnapshot`
|
||||
- OPENDART 공시목록 → `KbxDisclosureSummarySnapshot`
|
||||
- KIS 현재가 → `KbxMarketPriceSnapshot`
|
||||
- KRX → 승인된 서비스별 `IKrxApprovedServiceNormalizer`
|
||||
|
||||
KRX는 서비스 명세 없이 universal field mapping을 추정하지 않는다.
|
||||
|
||||
## 6. UI
|
||||
|
||||
`KbxDataProvenance`는 Source, Fresh/Stale/Expired, 기준/수신/정규화 시각, normalizer version, payload hash를 표현한다. 일반 업무 화면에서는 compact 형태를 사용하고 상세 운영 화면에서 전체 provenance를 확인한다.
|
||||
|
||||
## 7. 실패 처리
|
||||
|
||||
- Provider failure를 Domain failure로 바꾸지 않는다.
|
||||
- strict dataset은 expired cache fallback을 금지한다.
|
||||
- stale-while-revalidate는 stale 표시와 background refresh를 함께 사용한다.
|
||||
- Secret은 request descriptor, telemetry, audit diff, UI에 저장하지 않는다.
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
# KBX External Integration & Resilience Governance v19
|
||||
|
||||
## 1. 목적
|
||||
외부연계 실패를 Business Transaction 실패와 혼동하지 않는다. Domain Commit이 완료되었다면 사용자는 동일 저장/확정 Command를 반복하지 않는다.
|
||||
|
||||
## 2. 상태 분리
|
||||
`Business State`와 `Integration State`는 별도다.
|
||||
|
||||
예: 주문 확정 완료 / WMS 출고지시 전송 대기.
|
||||
|
||||
## 3. Retry 책임
|
||||
- Polly: 요청 수명 안의 짧은 transient retry/timeout/circuit breaker.
|
||||
- Hangfire: 프로세스 재시작을 견뎌야 하는 durable retry.
|
||||
- Outbox/Inbox: 전달 유실/중복 방지.
|
||||
- PostgreSQL: attempt/receipt의 source of truth.
|
||||
|
||||
Polly retry를 장시간 스케줄러처럼 사용하지 않는다.
|
||||
|
||||
## 4. 실패 분류
|
||||
Transient: network, timeout, HTTP 408/429/5xx, circuit-open.
|
||||
Permanent: contract-invalid, HTTP 400/401/403/404, domain-rejected.
|
||||
|
||||
Permanent 실패는 자동 retry하지 않고 Operations Exception으로 승격한다.
|
||||
|
||||
## 5. 사용자 UX
|
||||
정상 저장 성공 후 연계 지연은 `저장 실패`가 아니다.
|
||||
|
||||
- 업무: 완료
|
||||
- 연계: 전송 대기 / 자동 재시도 / 실패
|
||||
|
||||
사용자가 업무 Command를 다시 눌러 중복 데이터를 만들지 않게 한다.
|
||||
|
||||
## 6. 특정 외부 서비스
|
||||
현재 첨부 KBX 기준 문서에는 KRX/OPENDART/KIS의 실제 Endpoint/Quota/Auth 규칙이 정의되어 있지 않으므로 v19 계약에는 임의로 넣지 않았다. 각 Adapter 온보딩 시 공식 공급자 문서를 기준으로 별도 Integration Definition을 추가한다.
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
# Failure / Recovery Matrix v9
|
||||
|
||||
| 상황 | 사용자 표현 | 자동 Retry | 업무 데이터 변경 | 권장 복구 |
|
||||
|---|---|---:|---:|---|
|
||||
| GET 네트워크 단절 | 연결 실패 + 재조회 | 가능 | 없음 | 동일 조회 재시도 |
|
||||
| POST 전송 전 실패, Idempotency 없음 | 결과 확인 필요 | 금지 | 불명 | 업무 조회로 상태 확인 |
|
||||
| POST/Scan, Idempotency Key 있음 | 처리상태 확인 중 | 가능 | 중복 방지 | 같은 Key로 재전송 |
|
||||
| Validation 4xx | 해당 필드 오류 | 금지 | 없음 | 입력 수정 |
|
||||
| Business Rule 4xx | 원인 + 다음 Action | 금지 | 없음 | 정상 업무 경로 |
|
||||
| 409 Version Conflict | 최신 데이터 존재 | 금지 | 없음 | 최신값 재조회 후 재판단 |
|
||||
| 외부 연계 지연 | 본 업무 완료 / 연계 대기 | 시스템 재처리 | 본 업무만 완료 | Outbox 상태 추적 |
|
||||
| Background 부분실패 | 성공/실패 건수 | 정책별 | 일부 완료 | 실패건만 재처리 |
|
||||
| DB/서비스 장애 | 읽기전용 또는 degraded | 제한 | 정책별 | Runtime Banner + 운영복구 |
|
||||
|
||||
핵심은 `실패 = 다시 눌러보세요`가 아니다. 오류 종류에 따라 **재시도해도 안전한지**를 먼저 결정한다.
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
# KBX v13 — Canonical Field Dictionary Governance
|
||||
|
||||
## 목적
|
||||
|
||||
KBX Field Dictionary는 화면을 동적으로 생성하는 Low-code 엔진이 아니다.
|
||||
동일한 업무 개념이 Vue, FastEndpoints, Excel, Audit, AI에서 서로 다른 이름/타입으로 분화되는 것을 막는 **구조적 계약**이다.
|
||||
|
||||
핵심 경계:
|
||||
|
||||
```text
|
||||
Field Dictionary
|
||||
= Key / Label / Alias / Type / Length / Precision / Lookup / Import / Export / Sensitive
|
||||
|
||||
Domain
|
||||
= 현재 상태에서 수정 가능한가 / 재고가 충분한가 / 어떤 전이가 허용되는가
|
||||
```
|
||||
|
||||
## Single Source
|
||||
|
||||
```text
|
||||
contracts/fields/kbx.fields.json
|
||||
```
|
||||
|
||||
생성물:
|
||||
|
||||
```text
|
||||
packages/kbx-contracts/src/generated/fieldCatalog.ts
|
||||
backend/Shared/Contracts/Generated/KbxFieldCatalog.g.cs
|
||||
generated/field-manifest.json
|
||||
```
|
||||
|
||||
TypeScript와 C# 파일에는 동일 Source SHA256이 기록된다.
|
||||
|
||||
## Canonical Key 규칙
|
||||
|
||||
- camelCase
|
||||
- 동일 개념은 동일 Key
|
||||
- 표시 Label은 화면 문맥에 따라 변경 가능
|
||||
- DB column name과 FieldKey는 동일할 필요 없음
|
||||
- Read Model 전용/local interaction field는 무조건 Canonical로 승격하지 않음
|
||||
|
||||
현재 예:
|
||||
|
||||
```text
|
||||
orderQty
|
||||
customerId
|
||||
customerCode
|
||||
itemId
|
||||
itemCode
|
||||
warehouseId
|
||||
warehouseCode
|
||||
availableQty
|
||||
pickedQty
|
||||
shippedQty
|
||||
```
|
||||
|
||||
## 승격 판단
|
||||
|
||||
`generated/field-adoption-report.json`이 반복되는 local field를 후보로 제시한다.
|
||||
다음 조건을 함께 만족할 때 Canonical로 승격한다.
|
||||
|
||||
1. 동일한 의미인가?
|
||||
2. 동일한 변화 이유를 가지는가?
|
||||
3. 동일 UX/API/Excel 계약을 가져야 하는가?
|
||||
|
||||
v13에서는 반복 관찰된 다음을 승격했다.
|
||||
|
||||
- ownerName
|
||||
- exceptionCount
|
||||
- occurredAt
|
||||
- referenceNo
|
||||
- specification
|
||||
|
||||
## Excel
|
||||
|
||||
업무 Import Definition에서 Label/Alias/Type/길이/정밀도를 복제하지 않는다.
|
||||
|
||||
Frontend:
|
||||
|
||||
```ts
|
||||
kbxImportField('orderQty')
|
||||
```
|
||||
|
||||
Backend:
|
||||
|
||||
```csharp
|
||||
ImportFieldDefinitionFactory.FromKbxField(KbxFieldKeys.OrderQty)
|
||||
```
|
||||
|
||||
업무별 Import Definition은 **어떤 Field를 사용하는지**만 선택하고, 구조 메타데이터는 Dictionary에서 가져온다.
|
||||
|
||||
## Zod
|
||||
|
||||
Field Dictionary로부터 다음만 재사용한다.
|
||||
|
||||
- required text의 기본 구조
|
||||
- maxLength
|
||||
- numeric type
|
||||
- lookup/entity selection의 기본 구조
|
||||
|
||||
다음은 생성하지 않는다.
|
||||
|
||||
```text
|
||||
출고완료 주문 수정 금지
|
||||
재고 8개인데 10개 출고 금지
|
||||
구매확정 이후 단가수정 금지
|
||||
```
|
||||
|
||||
이 규칙은 Server/Domain 책임이다.
|
||||
|
||||
## Sensitive
|
||||
|
||||
`sensitive=true`이면 `masking` metadata가 필수다.
|
||||
|
||||
현재 예:
|
||||
|
||||
```text
|
||||
receiverName -> name
|
||||
phone -> phone
|
||||
postalCode -> address
|
||||
address1 -> address
|
||||
address2 -> address
|
||||
```
|
||||
|
||||
이 metadata는 표시/AI/Export 정책의 근거가 될 수 있으나 권한 검증을 대체하지 않는다.
|
||||
|
||||
## 변경 규칙
|
||||
|
||||
- 신규 Field: Minor
|
||||
- Field 제거/rename: Major
|
||||
- dataType/precision/scale/lookupEntity/sensitive 변경: Major 후보
|
||||
- Label/Alias 변경: Mapping Risk가 있는 Patch
|
||||
- rename은 즉시 삭제보다 구 Key deprecation + migration을 우선
|
||||
|
||||
## 금지
|
||||
|
||||
- 화면별 동일 Field를 다른 내부 Key로 재정의
|
||||
- Excel Definition에 Alias/Precision 복사
|
||||
- AI가 존재하지 않는 FieldKey 생성
|
||||
- Field Metadata에 Domain State Machine 삽입
|
||||
- 모든 Local Read Model Field를 성급히 Canonical로 승격
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
# KBX v13 — `quantity` → `orderQty` OMS Migration
|
||||
|
||||
## 배경
|
||||
|
||||
OMS 주문 상세에서 일반적인 `quantity`와 주문 의미가 명확한 `orderQty`가 혼용되었다.
|
||||
KBX 표준의 동일 개념/동일 FieldKey 원칙에 따라 OMS 주문 입력·Excel·Validation은 `orderQty`로 정규화한다.
|
||||
|
||||
## Frontend
|
||||
|
||||
신규 payload:
|
||||
|
||||
```json
|
||||
{
|
||||
"orderQty": 10
|
||||
}
|
||||
```
|
||||
|
||||
Grid/Zod/Error field도 `orderQty`를 사용한다.
|
||||
|
||||
## Backend 호환성
|
||||
|
||||
v13 `RegisterOrderLineRequest`는 일시적으로 다음 둘을 수용한다.
|
||||
|
||||
```text
|
||||
orderQty -> canonical
|
||||
quantity -> v12 legacy input
|
||||
```
|
||||
|
||||
Server는:
|
||||
|
||||
```text
|
||||
orderQty ?? quantity
|
||||
```
|
||||
|
||||
순서로 해석한다.
|
||||
|
||||
DB column `oms.order_lines.quantity`는 이번 변경 대상이 아니다.
|
||||
Field Dictionary는 UX/API 의미 계약이며 DB physical naming을 강제하지 않는다.
|
||||
|
||||
## 제거 조건
|
||||
|
||||
legacy `quantity` wire input 제거는 다음 조건을 충족한 별도 Breaking Release에서만 수행한다.
|
||||
|
||||
- 구 Client 사용량 0 확인
|
||||
- API 소비자 공지
|
||||
- Migration Guide
|
||||
- Major change declaration
|
||||
- Contract/E2E Test 갱신
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
# KBX Foundation v4
|
||||
|
||||
## 목적
|
||||
|
||||
세 Golden Screen을 개별 예제가 아니라 재사용 가능한 제품 기반으로 승격한다. v4는 화면 구현보다 **변하지 않아야 할 UX 계약과 자동 품질 게이트**에 초점을 둔다.
|
||||
|
||||
## 새 경계
|
||||
|
||||
- `@kbx/contracts`: Screen/Component/Permission/Help/Preference/Status 계약
|
||||
- `@kbx/ui`: PrimeVue/AG Grid adapter, template, tokens, registry
|
||||
- `apps/web/src/registry`: 제품 Screen 등록
|
||||
- `generated/`: CI에서 비교 가능한 Screen/Component Manifest
|
||||
- `.gitea/workflows`: Architecture 품질 게이트
|
||||
|
||||
## Design Token 원칙
|
||||
|
||||
KBX의 시각 규칙은 페이지 scoped CSS의 임의 숫자가 아니라 token을 우선한다.
|
||||
|
||||
- Desktop 기본 입력 34px
|
||||
- Grid row 34px
|
||||
- Grid header 36px
|
||||
- Command bar 44px
|
||||
- Page header 48px
|
||||
- WMS touch target 52px
|
||||
- 기본 font 14px / Grid 13px
|
||||
|
||||
`compact`, `comfortable`, `touch` density를 token으로 전환하고, 화면 구조는 동일하게 유지한다.
|
||||
|
||||
## AG Grid
|
||||
|
||||
v4 Reference는 AG Grid Theming API를 `packages/kbx-ui/src/theme/kbxGridTheme.ts`에 격리한다. 업무 모듈은 Theme object나 Grid 옵션을 직접 소유하지 않는다.
|
||||
|
||||
## Registry
|
||||
|
||||
Screen Registry의 실질적인 목적은 메뉴 생성이 아니다. 다음 기능의 공통 식별자를 확보하는 것이다.
|
||||
|
||||
- Help
|
||||
- Permission
|
||||
- Telemetry
|
||||
- 사용자 제안
|
||||
- 개인 Grid/Layout 설정
|
||||
- AI Screen Context
|
||||
- 테스트 및 운영 재현
|
||||
|
||||
## 중요한 제한
|
||||
|
||||
Screen Definition을 DB 기반 Low-code Builder로 확장하지 않는다. 코드 우선 정의를 유지하고, Metadata는 반복성이 충분히 확인된 영역에만 사용한다.
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
# KBX Foundation v7 — Governance & Scaffolding
|
||||
|
||||
## 목적
|
||||
KBX v7은 화면 표준을 문서가 아니라 생성기·Registry·CI 규칙으로 강제한다. 신규 화면은 빈 Vue 파일에서 시작하지 않고 `create-kbx-screen.mjs`로 생성한다.
|
||||
|
||||
## 신규 화면 생성
|
||||
```bash
|
||||
pnpm kbx:new-screen -- \
|
||||
--module ERP \
|
||||
--area PUR \
|
||||
--number 002 \
|
||||
--type list \
|
||||
--name "구매조회" \
|
||||
--path erp/purchases/search \
|
||||
--permission erp.purchase.read
|
||||
```
|
||||
|
||||
생성물: Screen Definition, 표준 Template Page, route metadata, 업무 체크리스트, Help stub.
|
||||
|
||||
## 품질 게이트
|
||||
- Screen ID 중복 금지
|
||||
- `helpKey === screenId`
|
||||
- 최소 1개의 화면 권한 요구
|
||||
- 업무 모듈의 PrimeVue/AG Grid 직접 import 금지
|
||||
- Generated Screen Registry drift 금지
|
||||
- Component/Screen Manifest drift 금지
|
||||
- Scaffolder 자체 Contract Test
|
||||
|
||||
## UX Utility Rail
|
||||
도움말·AI·사용자제안은 `KbxUtilityRail` 한 곳에 둔다. AI가 없어도 업무는 완전해야 하며, AI 답변에서 업무 데이터 변경을 직접 수행하지 않는다.
|
||||
|
||||
## 사용자 제안
|
||||
사용자가 입력하는 것은 유형과 자유문장 정도로 최소화한다. Screen ID/version/route/app version/user role/filter key 같은 진단 정보는 선택적으로 붙이되 고객명·전화번호·주소·주문 원문 등 업무 데이터는 자동 첨부하지 않는다.
|
||||
|
||||
## AI
|
||||
AI Context는 allow-list 방식이다. 화면 DOM 전체나 서버 객체 전체를 전달하지 않는다. `allowedCapabilities`는 explain/suggest/draft/execute로 구분하고, 기본 UI Context는 execute를 포함하지 않는다. 실제 변경은 Proposal → Permission → Entity Resolve → Domain Validation → Command 경계를 지켜야 한다.
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
# KBX FE Authoritative Outcome & Runtime Evidence Hardening v57
|
||||
|
||||
## 1. 냉정한 진단
|
||||
|
||||
v56까지 KBX의 공통 Template, Overlay Focus, Excel 6단계, Home Triage, WMS retry/idempotency 계약은 문서와 정적 회귀 기준에서 상당히 성숙했다. 그러나 현장 QA 관점에서는 두 종류의 위험이 남아 있었다.
|
||||
|
||||
첫째, **Transport Success와 Business Success가 혼재**했다. HTTP 200이어도 Backend가 `ResolvedCount=0, RejectedCount=1` 또는 `CreatedOrUpdatedCount=0, SkippedCount=1`을 반환할 수 있는데 일부 FE가 응답 body를 버리거나 성공 Telemetry를 기록할 수 있었다. 업무시스템에서 이는 "통신 성공"을 "업무 성공"으로 오해하는 전형적인 운영 사실성 결함이다.
|
||||
|
||||
둘째, **테스트 코드 존재와 실행 가능한 테스트 체계가 혼재**했다. Playwright spec은 많았지만 root에 `@playwright/test`, baseURL, webServer, viewport project가 없었다. 상대 URL을 쓰는 E2E가 실제 CI에서 어떻게 실행되는지가 계약화되지 않았다. `packageManager: pnpm@10` 역시 Corepack이 요구하는 정확한 semver가 아니었다.
|
||||
|
||||
v57은 새 추상화를 추가하는 대신 이 두 종류의 "마지막 10%"를 닫는다.
|
||||
|
||||
## 2. T06 — Authoritative Operations Outcome
|
||||
|
||||
### 문제
|
||||
|
||||
`ClaimWorkItemsResponse`는 이미 `claimedCount/skippedCount`를 제공했지만 Resolve 결과도 동일한 수준으로 사용자에게 남아야 한다. 기존 구조에서는 resolve HTTP 200 후 실제 `rejectedCount`가 있어도 Drawer가 닫히고 성공 Telemetry가 남을 수 있었다.
|
||||
|
||||
### 변경
|
||||
|
||||
- `ResolveWorkItemsResponse(resolvedCount, rejectedCount)`를 실제 Page가 소비
|
||||
- Claim/Resolve를 하나의 `actionReceipt`로 통합
|
||||
- 요청/성공/건너뜀 또는 거절 건수를 persistent inline receipt로 표시
|
||||
- partial result이면 `최신 상태 조회`를 명시
|
||||
- 결과 Receipt로 Focus 이동
|
||||
- `exception.resolved` Telemetry는 `resolvedCount > 0`인 경우에만 기록
|
||||
|
||||
즉 HTTP status는 transport outcome이고, 업무 성공 여부는 response body의 authoritative count가 결정한다.
|
||||
|
||||
## 3. T07 — Reconcile Outcome & Recovery
|
||||
|
||||
### 문제
|
||||
|
||||
Backend의 Create Exceptions 응답은 `CreatedOrUpdatedCount/SkippedCount`를 반환한다. FE가 이를 `unknown`으로 버리면 실행시점에 이미 정상/해결 상태로 바뀐 Row를 사용자가 "등록 성공"으로 오해할 수 있다.
|
||||
|
||||
### 변경
|
||||
|
||||
- `CreateReconcileExceptionsResponse`를 명시적 Type으로 승격
|
||||
- 요청/생성·갱신/건너뜀 count 표시
|
||||
- partial이면 Receipt Focus + `최신 대사 조회`
|
||||
- 생성된 항목이 있으면 `예외 센터 보기`
|
||||
- 원천 OMS/WMS 값은 Reconcile 화면에서 수정하지 않음
|
||||
|
||||
Reconcile은 데이터 수정화면이 아니라 Evidence → Exception Workflow 전환 화면으로 유지한다.
|
||||
|
||||
## 4. T09 — Shared PDA Scope-Race Hardening
|
||||
|
||||
### 문제
|
||||
|
||||
v56에서 retry queue 자체는 tenant/user scope별로 분리했지만, Scan request가 진행 중인 순간 사용자가 교체되면 callback 시점의 "현재 scope"와 command 발행 시점 scope가 달라질 수 있었다.
|
||||
|
||||
이 상황에서 response-loss command가 새 사용자의 queue에 들어가거나 이전 작업의 server confirmation이 새 사용자 화면에 나타나는 것은 공용 PDA에서 심각한 교대근무 경계 침범이다.
|
||||
|
||||
### 변경
|
||||
|
||||
- `ScopedScanMutation { command, scope }`
|
||||
- Scan 발행 시 scope snapshot
|
||||
- success/error 모두 captured scope로 retry item 제거/등록
|
||||
- callback 시 현재 scope와 다르면 현재 UI/task/lastConfirmedScan을 변경하지 않음
|
||||
- retry flush 전체 loop도 시작 scope에 pin
|
||||
- scope change 시 task telemetry abandon, last confirmed scan/overlay context reset
|
||||
|
||||
Idempotency는 중복 실행 방지이고, scope isolation은 사용자/tenant 경계 보호다. 둘을 별개로 유지한다.
|
||||
|
||||
## 5. Demo API — Runtime Contract Repair
|
||||
|
||||
정적 검증이 통과해도 Demo API가 실제 DTO와 다르면 Browser E2E는 제품 코드를 검증하지 못한다.
|
||||
|
||||
v57은 Operations demo data를 `sourceModule/sourceType/sourceId/referenceNo/sourceScreenId/code/severity/status/owner/ageMinutes/actions` 계약으로, Reconcile data를 `expectedValue/actualValue/differenceValue/reason/status/summary` 계약으로 정렬했다.
|
||||
|
||||
Demo Resolve도 authoritative state 변경 시 version을 증가시키고, Reconcile Create Exceptions는 `createdOrUpdatedCount/skippedCount`를 반환한다.
|
||||
|
||||
Demo는 별도 제품이 아니라 실제 FE 계약을 재현하는 실행 fixture여야 한다.
|
||||
|
||||
## 6. Browser QA Runner 계약
|
||||
|
||||
새 `playwright.config.ts`는 다음을 고정한다.
|
||||
|
||||
- 기본 Demo URL: `http://127.0.0.1:4173`
|
||||
- 외부 `KBX_E2E_BASE_URL`이 없으면 `pnpm dev:web:demo` 자동 기동
|
||||
- Desktop Chromium: 1440×900
|
||||
- WMS Mobile Chromium: 390×844
|
||||
- trace: retain-on-failure
|
||||
- screenshot: only-on-failure
|
||||
- video: retain-on-failure
|
||||
|
||||
Root scripts는 `test:unit`, `test:e2e`, `test:e2e:desktop`, `test:e2e:wms`, `test:fe`를 제공한다.
|
||||
|
||||
추가 E2E는 다음 운영 사고를 직접 모델링한다.
|
||||
|
||||
1. Operations HTTP 200 + `rejectedCount=1` → 성공으로 가장하지 않음
|
||||
2. Reconcile HTTP 200 + `skippedCount=1` → partial receipt 및 복구
|
||||
3. WMS 첫 Scan response loss → 동일 idempotency key로 1회 안전 재전송 → server confirmed truth 이후에만 최근 확인 갱신
|
||||
|
||||
## 7. CI/Release Gate
|
||||
|
||||
`.gitea/workflows/kbx-quality-gate.yaml`은 lockfile 존재 시:
|
||||
|
||||
`install → typecheck → unit → demo build → Chromium install → Playwright E2E`
|
||||
|
||||
를 실행한다.
|
||||
|
||||
Release Readiness는 lockfile이 없으면 runtime evidence를 BLOCKED로 기록하고 실패한다. 운영 배포 후보가 "정적 계약 PASS"만으로 runtime 검증을 통과한 것처럼 승격되지 않게 한다.
|
||||
|
||||
## 8. 현재 검증 결과
|
||||
|
||||
`node scripts/validate-kbx.mjs` 전체 PASS.
|
||||
|
||||
- 161 tokens
|
||||
- 20 screens
|
||||
- 86 components
|
||||
- 34 core APIs
|
||||
- 310 TypeScript/Vue script units
|
||||
- T01~T09 9/9
|
||||
- Design Debt 116 <= 131
|
||||
- Design-Code Parity PASS
|
||||
- Release Governance major PASS
|
||||
|
||||
## 9. Runtime 제한
|
||||
|
||||
현재 artifact 환경은 npm registry DNS lookup이 `EAI_AGAIN`으로 실패한다. 정확한 `pnpm@10.33.4`를 Corepack이 내려받지 못하므로 lockfile 생성/의존성 설치/Playwright browser runtime 실행을 완료하지 못했다.
|
||||
|
||||
따라서 다음은 **PASS라고 주장하지 않는다**.
|
||||
|
||||
- `pnpm install --frozen-lockfile`
|
||||
- Vue production typecheck/build
|
||||
- Vitest runtime suite
|
||||
- Playwright Desktop/WMS E2E
|
||||
|
||||
실행계약은 구성했지만 Runtime Evidence는 환경이 준비된 CI/개발 머신에서 반드시 생성해야 한다.
|
||||
|
||||
## 10. 다음 P0
|
||||
|
||||
1. Registry 접근 가능한 환경에서 `pnpm-lock.yaml` 생성 및 고정
|
||||
2. Desktop 1440×900 Golden Screen runtime suite 실행
|
||||
3. WMS 390×844 response-loss / offline / operator-scope-switch 시나리오 실행
|
||||
4. Playwright trace/screenshot/video를 Release Evidence로 보존
|
||||
5. Browser PASS 이후에만 "현장 사용 PASS" 상태로 승격
|
||||
|
||||
다음 단계에서도 새로운 UI Framework보다는 이 Runtime Evidence와 실제 업무 완료율을 우선한다.
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
# KBX FE Completion v37
|
||||
|
||||
## 목적
|
||||
|
||||
이번 단계의 우선순위는 Backend 연계 완성도가 아니라 **브라우저에서 직접 확인되는 FE 완성도**다. 기존 계약/Manifest/BE 연계 구조를 제거하지 않되, 화면을 보기 위해 API가 먼저 완성되어야 하는 흐름을 분리한다.
|
||||
|
||||
## 추가 산출물
|
||||
|
||||
`apps/web/fe-reference/`
|
||||
|
||||
- `index.html`: 실제 Application Shell + 홈 + T01~T09
|
||||
- `kbx-fe.css`: KBX Dense Desktop / WMS Touch 시각 규칙
|
||||
- `kbx-fe.js`: 순수 JavaScript 상호작용
|
||||
- `README.md`: 실행 및 검증 방법
|
||||
|
||||
## FE Reference Lab이 검증하는 계약
|
||||
|
||||
### Application Shell
|
||||
|
||||
- Global Header 56px
|
||||
- Side Navigation 220px / Collapsed 56px
|
||||
- Workspace Tabs 40px
|
||||
- Ctrl+K 메뉴 검색
|
||||
- 모듈 필터
|
||||
- 즐겨찾기 / 최근 메뉴
|
||||
- Compact / Comfortable 밀도
|
||||
|
||||
### Home
|
||||
|
||||
Home은 KPI Card Dashboard가 아니라 업무 시작점으로 구성한다.
|
||||
|
||||
1. 확인 필요: 실패/예외/중요 알림/진행 작업
|
||||
2. 바로 시작: 미저장/열린 업무/즐겨찾기/최근
|
||||
3. 모듈별 업무: OMS/ERP/WMS/공통 메뉴 탐색
|
||||
|
||||
### T01 Search/List
|
||||
|
||||
Search → Quick Filter → Context → Bulk Action → Grid → Summary → Detail Drawer.
|
||||
|
||||
### T02 Master CRUD
|
||||
|
||||
목록과 상세를 동시에 유지하고, Form을 2-column Dense layout으로 구성한다.
|
||||
|
||||
### T03 Header + Detail Transaction
|
||||
|
||||
Header Form → Editable Detail Grid → Summary. 변경 시 Workspace Tab dirty 상태가 시각적으로 나타난다.
|
||||
|
||||
### T04 Fast Grid Entry
|
||||
|
||||
Enter 이동, contenteditable 기반 실제 셀 편집, Excel Paste를 전제로 한 Grid-first 화면을 제공한다.
|
||||
|
||||
### T05 Master/Detail Explorer
|
||||
|
||||
품목 → 창고/Location → 재고이력을 한 화면에서 확인한다.
|
||||
|
||||
### T06 Work Queue
|
||||
|
||||
그래프 Dashboard 대신 지금 처리해야 할 Queue와 예외를 먼저 노출한다.
|
||||
|
||||
### T07 Reconcile
|
||||
|
||||
Expected / Actual / Difference / Reason / Resolution 문법을 유지한다.
|
||||
|
||||
### T08 Excel Import
|
||||
|
||||
파일 → 매핑 → 검증 → 반영 → 결과 5단계를 실제 버튼으로 진행할 수 있다.
|
||||
|
||||
### T09 WMS Mobile
|
||||
|
||||
390px 기준 Picking 화면, 54px CTA, 네트워크 상태, 스캔/수량/예외 피드백을 독립 검증한다.
|
||||
|
||||
## 구현 원칙
|
||||
|
||||
- FE 원형에서 Backend DTO/Endpoint를 호출하지 않는다.
|
||||
- 사용자 업무 문법을 확인하기 위해 Mock data를 명시적으로 사용한다.
|
||||
- Component API를 늘리기 전에 실제 화면에서 반복되는 UI 패턴을 먼저 확인한다.
|
||||
- Reference Lab에서 검증된 패턴만 `@kbx/ui`에 승격한다.
|
||||
- PrimeVue/AG Grid 교체를 목표로 하지 않는다. 실제 제품에서는 기존 KBX wrapper를 유지한다.
|
||||
|
||||
## 승격 기준
|
||||
|
||||
Reference Lab의 패턴을 공통 컴포넌트로 승격할 때 다음 3개를 모두 만족해야 한다.
|
||||
|
||||
1. 동일한 업무 의미인가?
|
||||
2. 동일한 변화 이유를 가지는가?
|
||||
3. 동일한 UX 계약을 가져야 하는가?
|
||||
|
||||
단순히 HTML/CSS가 비슷하다는 이유로 공통화하지 않는다.
|
||||
|
||||
## 다음 FE 작업 우선순위
|
||||
|
||||
1. Golden Screen `OMS-ORD-001`을 Lab과 실제 Vue 페이지 사이에 Visual Parity 맞추기
|
||||
2. `OMS-ORD-002` Header/Detail Keyboard flow 완성
|
||||
3. `KbxDataGrid` 변경/오류/편집/Clipboard 상태의 시각 카탈로그 강화
|
||||
4. `KbxLookup` F2/Enter/Esc/Focus Restore 브라우저 조작성 강화
|
||||
5. 홈과 Side Navigation의 화면 수가 100+로 늘어났을 때 정보 탐색성 검증
|
||||
6. 1280px Compact Desktop 회귀 검증
|
||||
7. WMS 390×844 실제 Touch/Scanner 상태 회귀 검증
|
||||
+485
@@ -0,0 +1,485 @@
|
||||
# KBX Foundation v53 — FE Golden Screen Interaction Hardening
|
||||
|
||||
## 1. 작업 목적
|
||||
|
||||
v53의 목표는 v52에서 정리한 화면 Anatomy를 **실제 사용자가 조작하는 Vue/HTML/JavaScript 업무 흐름**으로 한 단계 더 내리는 것이다.
|
||||
|
||||
새 Backend 추상화나 Runtime UI Engine을 늘리지 않는다. 기존 KBX 계약을 유지한 채 다음 네 가지를 우선한다.
|
||||
|
||||
1. Home이 모듈 선택 이후 실제 업무 탐색과 예외 압력까지 연결되어야 한다.
|
||||
2. T02/T03/T06/T07의 Section 안에서 사용자가 할 수 있는 Action이 예측 가능한 위치에 있어야 한다.
|
||||
3. 얇은 Vertical Slice는 Lookup, Grid Editing, Dirty, Validation, Keyboard를 실제 FE 코드로 구현하되 Server Command가 없는 상태를 성공으로 가장하지 않는다.
|
||||
4. WMS T09는 작업자가 다음 Scan/대기/복구 행동을 즉시 판단할 수 있어야 한다.
|
||||
|
||||
근거 문서: `KBX Business UX-AX Standard v1.0`, `KBX Design System v1.0`, `KBX Reference Screens v1.0`, `KBX Implementation Contract v1.0`.
|
||||
|
||||
---
|
||||
|
||||
## 2. 냉정한 진단
|
||||
|
||||
### 2.1 컴포넌트 수는 이미 충분하다
|
||||
|
||||
현재 문제는 `KbxButton`, `KbxLookup`, `KbxDataGrid`, Template의 개수가 부족한 것이 아니다. 공통 계약과 Manifest는 강하다. 문제는 **화면에서 계약을 얼마나 실제 업무행동으로 연결했는가**다.
|
||||
|
||||
계약만 강하고 조작이 비어 있으면 다음 문제가 생긴다.
|
||||
|
||||
- 버튼은 보이지만 실제 Command가 없다.
|
||||
- 상태가 바뀌는 것처럼 보이지만 Server Domain truth가 아니다.
|
||||
- Grid는 보이지만 행 추가/Lookup/오류 이동이 끊긴다.
|
||||
- Home에 정보가 많아도 키보드로 업무영역을 빠르게 좁히기 어렵다.
|
||||
- WMS가 메시지는 보여도 작업자가 다음 행동을 즉시 알기 어렵다.
|
||||
|
||||
### 2.2 가장 위험한 결함은 “가짜 성공”이다
|
||||
|
||||
Purchase / Inventory Move 같은 얇은 화면에서 Server Command 연결 없이 Client가 workflow status를 바꾸는 방식은 시연 화면에서는 그럴듯하지만 업무시스템에서는 위험하다.
|
||||
|
||||
```text
|
||||
사용자가 확정 클릭
|
||||
→ Client status = Confirmed
|
||||
→ 실제 DB/Domain은 미변경
|
||||
```
|
||||
|
||||
이것은 단순 미완성보다 나쁘다. 화면이 잘못된 업무 사실을 전달하기 때문이다.
|
||||
|
||||
v53에서는 로컬 입력, Lookup, Grid 편집, Validation, Dirty, Keyboard는 실제로 동작시키되 Server Command가 없는 저장/확정/출고/입고는 **fail-closed**로 처리한다. UI는 “상태를 변경하지 않았습니다”라고 명시한다.
|
||||
|
||||
### 2.3 Home은 “메뉴 수”보다 “업무 압력”을 보여줘야 한다
|
||||
|
||||
v52 Home Module Rail은 screen/favorite/open count를 제공했다. 하지만 실무에서 사용자는 화면 개수보다 **지금 확인할 일이 어디에 몰렸는가**를 먼저 봐야 한다.
|
||||
|
||||
따라서 v53은 동일 Attention source에서 모듈별 확인 필요 건수를 계산한다. 별도 집계 truth를 만들지 않는다.
|
||||
|
||||
### 2.4 Section Action은 페이지마다 흩어지면 안 된다
|
||||
|
||||
Master/Transaction/Queue/Reconcile에서 해당 작업면의 행 추가, 오류 이동, 선택 작업 같은 Action이 임의 위치에 생기면 Predictable Layout이 무너진다.
|
||||
|
||||
따라서 Section Header가 count/unit뿐 아니라 표준 action slot을 소유하게 했다.
|
||||
|
||||
---
|
||||
|
||||
## 3. v53 적용 내용
|
||||
|
||||
## 3.1 Home — Attention-aware Module Rail + Keyboard
|
||||
|
||||
`KbxHomePage.vue`의 module rail을 다음처럼 강화했다.
|
||||
|
||||
- `전체 / OMS / ERP / WMS / 공통`
|
||||
- 화면 수
|
||||
- `확인 필요` 건수
|
||||
- 즐겨찾기 수
|
||||
- 열린 Workspace 수
|
||||
- ArrowLeft / ArrowRight
|
||||
- Home / End
|
||||
- 선택 후 `kbx-home-explorer` scroll + focus
|
||||
- stable `data-home-module`
|
||||
- forced-colors에서 active/focus cue 유지
|
||||
|
||||
중요한 점은 module attention을 별도 하드코딩하지 않고 Home Attention Queue와 **동일 truth**에서 파생한다는 것이다.
|
||||
|
||||
Home의 판단 흐름은 다음으로 정리된다.
|
||||
|
||||
```text
|
||||
검색
|
||||
→ 운영상태
|
||||
→ 업무영역 + 확인 필요 압력
|
||||
→ 확인 필요
|
||||
→ 바로 시작
|
||||
→ 전체 업무 Explorer
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3.2 Standard Section Header — Count Unit + Action Surface
|
||||
|
||||
`KbxSectionHeader`에 `countUnit`을 추가했다.
|
||||
|
||||
예:
|
||||
|
||||
```text
|
||||
주문 상품 · 3종
|
||||
품목 목록 · 218개
|
||||
현재 작업 Queue · 42건
|
||||
```
|
||||
|
||||
화면이 매번 `건/개/종` markup을 직접 만들지 않는다.
|
||||
|
||||
다음 Template에는 표준 Section Action slot을 추가했다.
|
||||
|
||||
- `KbxMasterPage`: `list-actions`, `detail-actions`
|
||||
- `KbxTransactionPage`: `header-actions`, `detail-actions`
|
||||
- `KbxQueuePage`: `queue-actions`
|
||||
- `KbxReconcilePage`: `comparison-actions`
|
||||
|
||||
목적은 Slot 자유도를 늘리는 것이 아니라 **업무면 안의 Action 위치를 고정**하는 것이다.
|
||||
|
||||
---
|
||||
|
||||
## 3.3 Golden Screen — OMS 주문등록
|
||||
|
||||
`OMS-ORD-002`에 다음을 보강했다.
|
||||
|
||||
### 현재 Context
|
||||
|
||||
화면 상단 Context에 다음 정보를 명시한다.
|
||||
|
||||
- 현재 주문 / 신규
|
||||
- 현재 상태
|
||||
- Dirty 여부
|
||||
- 주문 상품 수
|
||||
- 현재 Validation 오류 수
|
||||
|
||||
### Detail Work Surface
|
||||
|
||||
`주문 상품` Section Header에:
|
||||
|
||||
- `행 추가`
|
||||
- `첫/다음 오류`
|
||||
|
||||
Action을 배치했다.
|
||||
|
||||
`행 추가` 후 품목코드 셀로 Focus가 이동한다.
|
||||
|
||||
`오류 이동`은 Grid의 `focusError()` 계약을 사용한다.
|
||||
|
||||
상세 건수는 일반 `건`이 아니라 업무 의미에 맞는 `종`을 사용한다.
|
||||
|
||||
---
|
||||
|
||||
## 3.4 ERP 구매등록 — 얇은 Wrapper에서 실제 FE Draft로
|
||||
|
||||
기존의 매우 얇은 조립 화면을 실제 입력 가능한 Draft FE로 보강했다.
|
||||
|
||||
구현 범위:
|
||||
|
||||
- `KbxTransactionPage`
|
||||
- `KbxFormSection / KbxFormGrid`
|
||||
- 거래처 Lookup
|
||||
- 창고 Lookup
|
||||
- Item F2 Lookup
|
||||
- Item code 직접 입력 + `resolveByCode`
|
||||
- 행 추가 / 행 복제
|
||||
- 수량 / 단가 편집
|
||||
- 금액 즉시 재계산
|
||||
- `KbxValidationError`
|
||||
- Header/Row validation
|
||||
- Dirty state
|
||||
- Workspace navigation guard
|
||||
- F8 save intent
|
||||
- 오류 Focus
|
||||
|
||||
Validation 예:
|
||||
|
||||
```text
|
||||
거래처 필수
|
||||
입고창고 필수
|
||||
최소 1개 품목
|
||||
품목 필수
|
||||
수량 > 0
|
||||
단가 >= 0
|
||||
```
|
||||
|
||||
### 중요한 보안/정합성 경계
|
||||
|
||||
Server Save/Confirm Command가 Foundation에 연결되지 않은 상태에서 다음을 하지 않는다.
|
||||
|
||||
```text
|
||||
status.value = CONFIRMED
|
||||
저장 성공 Toast
|
||||
가짜 Audit
|
||||
가짜 Domain commit
|
||||
```
|
||||
|
||||
대신 사용자가 저장/확정을 시도하면 서버 Command가 연결되지 않았으며 **업무 상태를 변경하지 않았음**을 명시한다.
|
||||
|
||||
즉, v53의 Purchase는 “실제 FE Draft interaction”까지 완성했지만 “End-to-End 업무 완료”라고 주장하지 않는다.
|
||||
|
||||
---
|
||||
|
||||
## 3.5 ERP 재고이동 — 실제 입력과 즉시 UX 검증
|
||||
|
||||
Inventory Move도 동일 원칙으로 보강했다.
|
||||
|
||||
구현:
|
||||
|
||||
- 출발창고 / 도착창고 Lookup
|
||||
- 품목 F2 Lookup / code resolve
|
||||
- Item metadata의 available quantity 반영
|
||||
- 행 추가 / 복제 / Focus
|
||||
- 이동수량 편집
|
||||
- Dirty / F8 / Workspace guard
|
||||
- Validation summary / cell focus
|
||||
|
||||
즉시 UX validation:
|
||||
|
||||
```text
|
||||
출발창고 필수
|
||||
도착창고 필수
|
||||
출발창고 != 도착창고
|
||||
품목 필수
|
||||
이동수량 > 0
|
||||
이동수량 <= 가용재고
|
||||
```
|
||||
|
||||
하지만 최종 재고 정합성은 Client 값으로 확정하지 않는다. Server Domain validation 이전에 `확정/출고/입고완료` state를 Client에서 만들지 않는다.
|
||||
|
||||
---
|
||||
|
||||
## 3.6 WMS Picking — 다음 행동 중심 T09
|
||||
|
||||
WMS Picking의 가장 중요한 UX는 “예쁜 Mobile 화면”이 아니라 **작업자가 지금 무엇을 해야 하는가**다.
|
||||
|
||||
v53은 `KbxWmsMobilePage`의 `taskContext`와 `instruction`을 실제 화면에 연결했다.
|
||||
|
||||
별도 `notice` surface에서 다음을 구분한다.
|
||||
|
||||
- 온라인 처리 메시지
|
||||
- 오프라인
|
||||
- 전송 대기 Command
|
||||
- 동기화 중
|
||||
- Server 확인 대기
|
||||
|
||||
작업 본문에는 `다음 행동`을 명시한다.
|
||||
|
||||
예:
|
||||
|
||||
```text
|
||||
위치 스캔
|
||||
상품 스캔
|
||||
서버 확인 중
|
||||
피킹 완료
|
||||
스캔 대기
|
||||
```
|
||||
|
||||
Server 응답 대기 중에는 중복 Scan을 하지 말라는 행동 지침을 표시한다.
|
||||
|
||||
Offline + pending command 상태에서는 동일 Command Key를 이용한 안전한 replay 원칙을 설명하고, Server validation이 필요한 Scan을 성공으로 가장하지 않는다.
|
||||
|
||||
---
|
||||
|
||||
## 3.7 Queue / Reconcile 실사용 Action 위치
|
||||
|
||||
`WMS-WORK-001`:
|
||||
|
||||
- Queue Section Header에 `선택 작업 시작`
|
||||
- 선택 0건이면 disabled
|
||||
|
||||
`COMMON-RECON-001`:
|
||||
|
||||
- Comparison Section Header에 `선택건 예외 등록`
|
||||
- 선택 0건이면 disabled
|
||||
|
||||
화면의 Command Bar를 무작정 늘리는 대신 해당 work surface의 contextual action으로 배치했다.
|
||||
|
||||
---
|
||||
|
||||
## 3.8 순수 HTML / JavaScript Reference parity
|
||||
|
||||
`apps/web/fe-reference`에도 v53 Home interaction을 반영했다.
|
||||
|
||||
- module별 attention count
|
||||
- ArrowLeft / ArrowRight / Home / End
|
||||
- 선택 후 explorer scroll/focus
|
||||
- focus-visible
|
||||
- v53 build label
|
||||
|
||||
Vue만 계약을 갖고 순수 FE Reference가 낡는 이중화를 방지한다.
|
||||
|
||||
---
|
||||
|
||||
## 4. Theme 적용 원칙
|
||||
|
||||
v51~v52의 Module Identity Theme을 그대로 유지한다.
|
||||
|
||||
- OMS: Blue
|
||||
- ERP: Violet
|
||||
- WMS: Teal
|
||||
- COMMON: Neutral
|
||||
|
||||
이 색은 **Navigation / Chrome / Module Context identification** 용도다.
|
||||
|
||||
다음 의미색과 분리한다.
|
||||
|
||||
- success
|
||||
- warning
|
||||
- danger
|
||||
- info
|
||||
- disabled
|
||||
|
||||
즉, WMS가 Teal이라고 “성공”이 Teal인 것은 아니다. Domain state와 module identity를 같은 색 체계로 합치지 않는다.
|
||||
|
||||
Dark / forced-colors에서도 동일한 업무 의미와 Keyboard contract를 유지한다.
|
||||
|
||||
---
|
||||
|
||||
## 5. 보안·업무 Truth 원칙
|
||||
|
||||
v53에서 가장 중요한 보강은 보이는 보안보다 **truthful UI**다.
|
||||
|
||||
### Frontend가 할 수 있는 것
|
||||
|
||||
- Required/format/cross-field 1차 UX validation
|
||||
- Lookup resolve
|
||||
- Grid editing
|
||||
- Dirty state
|
||||
- 작업 전 Command intent
|
||||
- 오류 위치 이동
|
||||
- Permission 기반 표시
|
||||
|
||||
### Frontend가 최종 결정하면 안 되는 것
|
||||
|
||||
- 재고 확정
|
||||
- 구매 확정
|
||||
- 재고이동 workflow transition
|
||||
- 출고/입고 성공
|
||||
- 권한 최종 승인
|
||||
- Concurrency 승패
|
||||
- Audit truth
|
||||
|
||||
최종 truth는 Server/Domain/DB constraint가 책임진다.
|
||||
|
||||
Server Command가 없을 때 Client state machine으로 성공을 흉내 내지 않는 것이 v53의 명시적 정책이다.
|
||||
|
||||
---
|
||||
|
||||
## 6. 검증 결과
|
||||
|
||||
실행:
|
||||
|
||||
```bash
|
||||
node scripts/validate-kbx.mjs
|
||||
```
|
||||
|
||||
전체 PASS.
|
||||
|
||||
주요 결과:
|
||||
|
||||
- Design tokens: **161**
|
||||
- Screen definitions: **20**
|
||||
- Component definitions: **85**
|
||||
- Core component APIs: **34**
|
||||
- Navigation entries: **16**
|
||||
- T01~T09 Recipe verification: **9/9**
|
||||
- TypeScript/Vue script syntax: **308 units PASS**
|
||||
- v41~v52 FE regression: **PASS**
|
||||
- 신규 `validate-fe-golden-interaction-v53.mjs`: **PASS**
|
||||
- Component catalog: **34 core APIs / 70 entries**
|
||||
- Design Debt Ratchet: **116 <= 131 PASS**
|
||||
- Design-Code Parity: **161 tokens / 34 core / 85 total PASS**
|
||||
- Release Governance: **major PASS**
|
||||
|
||||
전체 로그:
|
||||
|
||||
`docs/evidence/v53-validate-kbx-full.log`
|
||||
|
||||
---
|
||||
|
||||
## 7. Runtime 검증 제한
|
||||
|
||||
현재 작업 환경에서:
|
||||
|
||||
- `node`: 사용 가능
|
||||
- `npm`: 사용 가능
|
||||
- `pnpm`: 없음
|
||||
- root `node_modules`: 없음
|
||||
- `apps/web/node_modules`: 없음
|
||||
|
||||
따라서 이번 실행에서 다음을 수행했다고 주장하지 않는다.
|
||||
|
||||
- Vite production build
|
||||
- Vitest browser/component runtime
|
||||
- Playwright 실제 Vue runtime E2E
|
||||
|
||||
정적 generator/contract/syntax/regression gate와 실제 browser runtime 검증은 별개의 증거다.
|
||||
|
||||
---
|
||||
|
||||
## 8. 다음 우선순위
|
||||
|
||||
### P0 — Dependency 복원 후 Golden Screen Browser QA
|
||||
|
||||
화면:
|
||||
|
||||
1. `OMS-ORD-001`
|
||||
2. `OMS-ORD-002`
|
||||
3. `WMS-PICK-001`
|
||||
|
||||
Viewport/환경:
|
||||
|
||||
- 1440×900
|
||||
- 1280×720
|
||||
- Light
|
||||
- Dark
|
||||
- forced-colors
|
||||
- WMS 390×844
|
||||
|
||||
검증:
|
||||
|
||||
- Mouse
|
||||
- Keyboard only
|
||||
- Focus order
|
||||
- Drawer restore
|
||||
- Grid selection/edit
|
||||
- Lookup F2/Enter/Esc
|
||||
- Bulk result
|
||||
- Validation recovery
|
||||
- WMS duplicate/network/retry
|
||||
|
||||
### P0 — Purchase / Inventory Move 실제 Server Command 연결
|
||||
|
||||
현재 FE Draft contract는 유지한다.
|
||||
|
||||
연결할 것은:
|
||||
|
||||
```text
|
||||
Save / Confirm / Move Confirm / Ship / Receive
|
||||
↓
|
||||
FastEndpoint
|
||||
↓
|
||||
Application Command
|
||||
↓
|
||||
Domain validation
|
||||
↓
|
||||
PostgreSQL transaction
|
||||
↓
|
||||
Audit + Outbox
|
||||
```
|
||||
|
||||
연결 이후에도 Client가 authoritative state를 먼저 바꾸면 안 된다.
|
||||
|
||||
### P1 — OMS-ORD-001 실전 밀도 고도화
|
||||
|
||||
다음 단계는 새 컴포넌트 추가보다:
|
||||
|
||||
- all-filtered selection
|
||||
- Bulk partial failure recovery
|
||||
- Drawer context continuity
|
||||
- saved layout
|
||||
- Excel export/import continuity
|
||||
- quick filter exception pressure
|
||||
|
||||
을 실제 Browser 시나리오로 검증하는 것이다.
|
||||
|
||||
---
|
||||
|
||||
## 9. 최종 판단
|
||||
|
||||
v53에서 중요한 변화는 코드량 증가가 아니다.
|
||||
|
||||
```text
|
||||
Template이 있다
|
||||
→ Template 안에서 행동 위치가 예측 가능하다
|
||||
|
||||
Button이 있다
|
||||
→ 실제 FE interaction이 있다
|
||||
|
||||
Workflow가 보인다
|
||||
→ Server가 없으면 성공을 가장하지 않는다
|
||||
|
||||
Home에 메뉴가 있다
|
||||
→ 지금 확인할 업무 압력까지 보인다
|
||||
|
||||
WMS에 메시지가 있다
|
||||
→ 작업자가 다음 행동을 즉시 안다
|
||||
```
|
||||
|
||||
이 방향이 유지되어야 KBX가 “BE 계약이 강한 UI Framework”를 넘어 **실제 현장 사용자가 빠르고 안전하게 쓰는 업무 Frontend**가 된다.
|
||||
+240
@@ -0,0 +1,240 @@
|
||||
# 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를 실제로 검증해야 한다.
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
# KBX FE Interaction Closure Hardening v56
|
||||
|
||||
## 1. Executive judgement
|
||||
|
||||
v55 closed major recovery-workbench gaps, but the remaining risk was no longer template count. It was interaction closure: keyboard focus disappearing after DOM replacement, overlays returning users to nowhere, HTTP 200 being treated as all-success for bulk work, browser-persisted WMS retry commands crossing operator sessions, and runtime feedback metadata reporting an old app version.
|
||||
|
||||
The v56 rule is simple: a user action is not complete until the UI preserves context, reports the authoritative outcome, and leaves the operator at a deterministic next action.
|
||||
|
||||
## 2. Changes
|
||||
|
||||
### Overlay focus lifecycle
|
||||
- KbxDialog 1.1.0 and KbxDrawer 1.1.0 capture the invoking element on open and restore it after the overlay has actually hidden.
|
||||
- Restoration is fail-safe: disconnected or disabled targets are ignored.
|
||||
- KbxDialog supports `restoreFocus=false` for components that own a richer focus contract.
|
||||
- KbxLookupDialog uses that opt-out because KbxLookup intentionally advances to the next form field after a successful selection; generic restore must not override that behavior.
|
||||
|
||||
### T08 Excel Import focus continuity
|
||||
- KbxExcelImport 1.3.0 gives all six stages a programmatic focus landing.
|
||||
- When server status replaces one stage DOM with another, focus moves to the new stage heading instead of falling back to `body`.
|
||||
- Error-row first/next focus remains independent from stage focus.
|
||||
- The static HTML/JavaScript reference mirrors the same behavior and avoids stealing focus during repeated progress rerenders.
|
||||
|
||||
### T06 authoritative bulk receipt
|
||||
- The real Claim endpoint already returns `ClaimedCount` and `SkippedCount`, but FE typed it as `unknown` and discarded the result.
|
||||
- v56 consumes the real response and renders a persistent inline receipt: requested / claimed / skipped.
|
||||
- Partial processing offers `내 업무 보기` and `최신 상태 조회`; a generic success toast is not considered sufficient.
|
||||
- Demo API now matches the production response shape.
|
||||
|
||||
### WMS shared-PDA retry isolation
|
||||
- v1 stored unacknowledged scan commands in one unscoped localStorage key.
|
||||
- This is unsafe on shared PDAs because a later operator/session could inherit an old retry queue.
|
||||
- v56 uses a hashed tenant/user UI scope for authenticated persistent storage; anonymous/default contexts are session-only.
|
||||
- The old unscoped v1 queue is explicitly retired and never replayed.
|
||||
- Parsed browser data is shape-validated and queue length is bounded before replay.
|
||||
- Idempotency key reuse and sequential replay remain unchanged.
|
||||
|
||||
### Runtime provenance correction
|
||||
- `App.vue` still reported `v51-fe-presentation-workbench` while the source had advanced through v55.
|
||||
- v56 corrects runtime `app-version` to `v56-fe-interaction-closure-hardening` so suggestions, telemetry, and operational reproduction do not record a stale build identity.
|
||||
|
||||
### Static FE truth hardening
|
||||
- Static T07 previously simulated reconciliation by directly setting `actual = expected` and `difference = 0` in client memory.
|
||||
- That contradicted the real v55 Reconcile policy.
|
||||
- v56 changes the static reference to register a reprocessing request while explicitly preserving OMS/WMS source truth.
|
||||
|
||||
### E2E readiness repair
|
||||
- The OMS Import Playwright scenario referenced an XLSX fixture that did not exist in the repository.
|
||||
- v56 uses an in-memory browser file for the Demo API flow, removing an untracked binary prerequisite.
|
||||
- Demo Import now provides a complete required-field mapping so the Golden Flow can actually proceed to validation.
|
||||
- Keyboard regression now explicitly asserts F2 Lookup -> Esc -> invoking customer-code field focus restoration.
|
||||
|
||||
## 3. QA criticism
|
||||
|
||||
The previous project direction had several mature contracts but some tests were ceremonial: a Golden Flow could exist in source while depending on a missing binary fixture or a Demo API response shape different from the real endpoint. That creates false confidence. A QA gate must prove that the test can execute, not merely that a spec file exists.
|
||||
|
||||
Similarly, overlay accessibility cannot stop at `Esc closes`. In an ERP screen, closing a lookup or detail surface without deterministic focus restoration breaks the operator's keyboard production line. Focus is part of transactional UX, not cosmetic accessibility.
|
||||
|
||||
WMS local persistence must be treated as operational data. Idempotency protects the server from duplication, but it does not make an unscoped browser queue safe across operators. Storage isolation and idempotency solve different failure modes.
|
||||
|
||||
## 4. What was deliberately not added
|
||||
|
||||
- No new low-code/runtime metadata engine.
|
||||
- No new backend command merely to support the UI changes.
|
||||
- No optimistic domain-state mutation.
|
||||
- No broad redesign of Home after v54 stabilization.
|
||||
- No claim that Playwright/Vite/Vitest passed in this container.
|
||||
|
||||
## 5. Verification
|
||||
|
||||
Full `node scripts/validate-kbx.mjs`: PASS.
|
||||
|
||||
Current governance snapshot:
|
||||
- 161 design tokens
|
||||
- 20 screens
|
||||
- 86 components
|
||||
- 34 core APIs
|
||||
- 309 TS/Vue script units
|
||||
- T01~T09 recipe verification 9/9
|
||||
- Design Debt 116 <= 131
|
||||
- Design-Code Parity PASS
|
||||
- Release Governance major PASS
|
||||
|
||||
See `docs/evidence/v56-validate-kbx-full.log` and `docs/evidence/v56-fe-interaction-closure-validation.txt`.
|
||||
|
||||
## 6. Next P0
|
||||
|
||||
The next stage should stop adding structural contracts and execute runtime evidence in a dependency-complete environment:
|
||||
|
||||
1. OMS-ORD-001 Drawer open/close and row-focus restore at 1440x900 and 1280x720.
|
||||
2. OMS-ORD-002 keyboard-only transaction from customer F2 through grid entry, validation failure, correction and F8 save.
|
||||
3. OMS-ORD-003 six-stage Import with partial completion and failed-row retry.
|
||||
4. COMMON-OPS-001 multi-select bulk claim with a true skipped subset and recovery.
|
||||
5. COMMON-REC-001 evidence Drawer, exception handoff, and proof that source values never mutate client-side.
|
||||
6. WMS-PICK-001 at 390x844: duplicate wedge scan, response-loss retry, identity-scope change, stale queue rejection, offline recovery and server-confirmed scan truth.
|
||||
|
||||
The release gate should only be promoted from contract PASS to field PASS after those flows are captured with browser traces/screenshots and repeatable fixtures.
|
||||
+270
@@ -0,0 +1,270 @@
|
||||
# KBX Foundation v52 — FE Operational Navigation & Screen Anatomy Hardening
|
||||
|
||||
## 1. 작업 목적
|
||||
|
||||
v52의 목표는 새로운 UI Framework를 만드는 것이 아니다. v51까지 축적된 계약·거버넌스·토큰·보안 경계를 유지하면서, 실제 사용자가 보는 Vue/HTML/JavaScript 계층의 업무 완성도를 높이는 것이다.
|
||||
|
||||
판단 기준은 다음 세 문장으로 고정한다.
|
||||
|
||||
1. 사용자는 화면 진입 후 3초 안에 현재 상태, 주요 행동, 다음 이동을 판단할 수 있어야 한다.
|
||||
2. T01~T09는 이름만 다른 Wrapper가 아니라 업무 유형에 맞는 정보 계층을 가져야 한다.
|
||||
3. 홈은 카드 Dashboard가 아니라 업무 런처, 이어하기, 예외 진입, 전체 업무 탐색기여야 한다.
|
||||
|
||||
근거 문서: `KBX Business UX-AX Standard v1.0`, `KBX Design System v1.0`, `KBX Reference Screens v1.0`, `KBX Implementation Contract v1.0`.
|
||||
|
||||
## 2. 냉정한 진단
|
||||
|
||||
### 2.1 강한 부분
|
||||
|
||||
- Screen Definition, Permission, Problem Contract, Concurrency, Audit, Outbox/Job 경계가 이미 강하다.
|
||||
- `@kbx/ui`가 PrimeVue/AG Grid를 감싸는 방향이 일관되어 있다.
|
||||
- T01~T09 Recipe/Manifest/Validation이 자동 검증된다.
|
||||
- 순수 `fe-reference`는 Lookup, Keyboard, Dirty, Import, WMS Scan 등 상당히 많은 실제 FE 동작을 이미 재현한다.
|
||||
|
||||
### 2.2 약한 부분
|
||||
|
||||
#### A. 계약 완성도가 사용자 화면 완성도로 착각될 위험
|
||||
|
||||
공통 Template에 slot과 props가 존재하는 것과 사용자가 화면을 즉시 이해하는 것은 다르다. v51의 일부 T02/T03/T06/T07은 구조는 맞았으나 `목록/상세`, `Header/Detail`, `Queue`, `Comparison`의 시각적 시작점과 건수·설명이 충분히 표준화되지 않았다.
|
||||
|
||||
#### B. Home은 기능은 많지만 탐색 결정이 한 단계 더 필요했다
|
||||
|
||||
기존 Home에는 검색, pulse, 예외, 바로 시작, 업무 목록이 존재했지만 사용자가 OMS/ERP/WMS 중 어디로 들어갈지 빠르게 좁히는 compact module launcher가 없었다. Side Navigation의 module switcher와 Home의 work explorer가 하나의 탐색 문법으로 연결되어야 했다.
|
||||
|
||||
#### C. 일부 Vertical Slice Page는 여전히 너무 얇다
|
||||
|
||||
현재 파일 크기만 보더라도 다음 화면은 Template 조립은 되어 있으나 실제 업무 FE orchestration의 깊이는 부족하다.
|
||||
|
||||
- `PurchasePage.vue`: 19 lines
|
||||
- `InventoryMovePage.vue`: 9 lines
|
||||
- `WmsWorkPage.vue`: 22 lines
|
||||
|
||||
파일 길이가 품질의 척도는 아니지만, 현 시점에서는 이 얇음이 “표준 컴포넌트 재사용”보다 “업무 상호작용 미구현”에 가까운 구간도 있다. 다음 단계에서는 BE 계약을 추가하기보다 실제 입력, Lookup, 상태 전이, 오류 회복, Grid Editing, Keyboard Flow를 먼저 채워야 한다.
|
||||
|
||||
#### D. 자동 검증은 강하지만 실제 Browser Visual Gate는 별도 보강이 필요
|
||||
|
||||
v52 정적/계약 검증은 모두 통과했으나 현재 작업 환경에 `pnpm`과 `node_modules`가 없어 Vite bundle/Playwright를 실행하지 않았다. Chromium direct screenshot도 container DBus/headless 제약으로 완료되지 않았다. 따라서 v52는 “코드/계약 회귀 PASS”이지 “실 브라우저 E2E 전체 PASS”라고 과장하지 않는다.
|
||||
|
||||
## 3. v52 적용 내용
|
||||
|
||||
### 3.1 Home — Operational Module Rail
|
||||
|
||||
`KbxHomePage.vue`에 모듈별 업무 런처를 추가했다.
|
||||
|
||||
표시 정보:
|
||||
|
||||
- 전체 화면 수
|
||||
- OMS / ERP / WMS / COMMON
|
||||
- 모듈별 화면 수
|
||||
- 즐겨찾기 수
|
||||
- 현재 열린 Workspace Tab 수
|
||||
|
||||
모듈 선택 시 동일 페이지의 `kbx-home-explorer`로 이동한다. 단순 CSS Filter가 아니라 “탐색 행동 → 업무 목록” 흐름으로 연결한다.
|
||||
|
||||
Home의 최종 정보 순서:
|
||||
|
||||
```text
|
||||
업무검색
|
||||
→ Operational Pulse
|
||||
→ Module Rail
|
||||
→ 확인 필요
|
||||
→ 바로 시작
|
||||
→ 모듈별 업무 탐색기
|
||||
```
|
||||
|
||||
카드형 KPI Dashboard는 추가하지 않았다.
|
||||
|
||||
### 3.2 T02 Master CRUD Anatomy
|
||||
|
||||
`KbxMasterPage`에 다음 표준 계약을 추가했다.
|
||||
|
||||
- `listTitle`
|
||||
- `listCount`
|
||||
- `detailTitle`
|
||||
- `detailDescription`
|
||||
- `list-header` / `detail-header` extension slot
|
||||
- list/detail body 분리
|
||||
|
||||
`KbxSectionHeader`를 공통 사용한다.
|
||||
|
||||
ERP 품목관리에는 다음을 적용했다.
|
||||
|
||||
```text
|
||||
품목 목록 · N건
|
||||
품목 상세 · ABC001 · 품목명
|
||||
기본정보 · 설명
|
||||
물류정보 · 설명
|
||||
```
|
||||
|
||||
즉, 단순 2-pane이 아니라 “어느 pane이 무엇이며 현재 무엇을 보고 있는가”가 명확해졌다.
|
||||
|
||||
### 3.3 T03 Header + Detail Transaction Anatomy
|
||||
|
||||
`KbxTransactionPage`에 다음을 추가했다.
|
||||
|
||||
- `headerTitle`
|
||||
- `headerDescription`
|
||||
- `detailTitle`
|
||||
- `detailDescription`
|
||||
- `detailCount`
|
||||
- Header/Detail body 분리
|
||||
|
||||
OMS 주문등록에는:
|
||||
|
||||
```text
|
||||
주문 정보
|
||||
주문 조건과 배송 기준 확인
|
||||
|
||||
주문 상품 · N건
|
||||
Enter 연속입력 / F2 Lookup / Excel 붙여넣기 문법
|
||||
```
|
||||
|
||||
을 적용했다.
|
||||
|
||||
ERP 구매등록과 재고이동에도 동일 계약을 적용했다. 거래 화면마다 임의의 섹션 문법을 만들지 않는다.
|
||||
|
||||
### 3.4 T06 Queue Anatomy
|
||||
|
||||
`KbxQueuePage`에:
|
||||
|
||||
- `queueTitle`
|
||||
- `queueDescription`
|
||||
- `queueCount`
|
||||
|
||||
를 추가했다.
|
||||
|
||||
WMS Work 화면은 “현재 작업 Queue”라는 업무 목적과 표시 건수를 명시한다. Queue는 그래프가 아니라 지금 처리할 업무를 보여준다는 기존 KBX 원칙을 강화한다.
|
||||
|
||||
### 3.5 T07 Reconcile Anatomy
|
||||
|
||||
`KbxReconcilePage`에:
|
||||
|
||||
- `comparisonTitle`
|
||||
- `comparisonDescription`
|
||||
- `comparisonCount`
|
||||
|
||||
를 추가했다.
|
||||
|
||||
COMMON Reconcile에는 “OMS ↔ WMS 대사”와 Expected / Actual / Difference / Reason / Resolution 순서를 명시했다. 대사 화면의 목적을 일반 Grid와 구별한다.
|
||||
|
||||
### 3.6 순수 HTML/JavaScript Reference parity
|
||||
|
||||
`apps/web/fe-reference`에도 동일 Home module rail을 구현했다.
|
||||
|
||||
- `data-home-module="ALL|OMS|ERP|WMS|COMMON"`
|
||||
- `aria-pressed`
|
||||
- 모듈별 screen/favorite/open count
|
||||
- 선택 후 work explorer scroll
|
||||
- 전체 업무 복귀
|
||||
- forced-colors active cue
|
||||
|
||||
Vue Shell만 개선하고 실제 정적 HTML/JS Reference가 뒤처지는 이중화를 피했다.
|
||||
|
||||
## 4. Theme 적용 원칙
|
||||
|
||||
v51에서 도입한 Module Identity Theme을 유지한다.
|
||||
|
||||
- OMS: Blue
|
||||
- ERP: Violet
|
||||
- WMS: Teal
|
||||
- COMMON: Neutral
|
||||
|
||||
중요 규칙:
|
||||
|
||||
- Module color는 navigation/chrome/context identification에만 사용한다.
|
||||
- 성공/경고/오류/취소 등 Business State color를 대체하지 않는다.
|
||||
- Dark와 forced-colors에서도 active cue를 유지한다.
|
||||
- 화면별 임의 RGB보다 token source를 우선한다.
|
||||
|
||||
## 5. FE 보안 보강
|
||||
|
||||
정적 Reference CSP를 다음처럼 더 좁혔다.
|
||||
|
||||
- `connect-src 'self'`
|
||||
- `font-src 'self'`
|
||||
- `frame-src 'none'`
|
||||
- `worker-src 'none'`
|
||||
- 기존 `object-src 'none'`, `base-uri 'none'`, `frame-ancestors 'none'`, `form-action 'self'` 유지
|
||||
|
||||
이 변경의 목적은 FE가 권한의 Source of Truth가 되는 것이 아니다. 브라우저 공격면은 브라우저 정책으로 줄이고, Permission/Business Rule/Concurrency/Entity Resolve는 Backend/Domain이 계속 최종 책임진다.
|
||||
|
||||
## 6. 검증 결과
|
||||
|
||||
`node scripts/validate-kbx.mjs` 전체 PASS.
|
||||
|
||||
주요 결과:
|
||||
|
||||
- Design tokens: 161
|
||||
- Screen definitions: 20
|
||||
- Component definitions: 85
|
||||
- Core component APIs: 34
|
||||
- Navigation entries: 16
|
||||
- T01~T09 Recipe verification: 9/9
|
||||
- TypeScript/Vue script syntax: 308 units PASS
|
||||
- FE v41~v51 regression: PASS
|
||||
- 신규 `validate-fe-anatomy-v52.mjs`: PASS
|
||||
- Design Debt Ratchet: `116 <= 131` PASS
|
||||
- Design-Code Parity: PASS
|
||||
- Release Governance: major PASS
|
||||
|
||||
전체 로그: `docs/evidence/v52-validate-kbx-full.log`
|
||||
|
||||
## 7. 브라우저 검증 제한
|
||||
|
||||
현재 environment:
|
||||
|
||||
- `node`: 사용 가능
|
||||
- `pnpm`: 없음
|
||||
- root `node_modules`: 없음
|
||||
|
||||
따라서 Vite build, Vitest, Playwright browser suite는 이번 실행에서 수행하지 않았다.
|
||||
|
||||
정적 HTML Reference를 Chromium CLI로 screenshot하려 했으나 container의 DBus/headless process가 timeout되어 시각 증거로 채택하지 않았다. 코드 검증 결과와 browser E2E 결과를 혼동하지 않는다.
|
||||
|
||||
## 8. 다음 우선순위
|
||||
|
||||
다음 단계에서 새 Framework나 Metadata Engine을 추가하면 안 된다. 우선순위는 다음과 같다.
|
||||
|
||||
### P0 — 실제 Golden Screen FE 완성
|
||||
|
||||
1. `OMS-ORD-001` 주문관리
|
||||
- Grid row/detail context
|
||||
- quick filter / bulk result
|
||||
- drawer/action continuity
|
||||
- loading/empty/error/refetch 시각 검증
|
||||
|
||||
2. `OMS-ORD-002` 주문등록
|
||||
- Header → Detail Keyboard path
|
||||
- KbxLookup 실제 focus continuation
|
||||
- Detail Grid Enter/Tab/Paste
|
||||
- validation first/next error
|
||||
- 저장/확정/동시성 UX
|
||||
|
||||
3. `WMS-PICK-001`
|
||||
- Scanner state
|
||||
- network delay/offline
|
||||
- duplicate scan
|
||||
- success/error sound/vibration abstraction
|
||||
- one-hand operation visual QA
|
||||
|
||||
### P1 — 얇은 Vertical Slice Page 보강
|
||||
|
||||
- Purchase
|
||||
- Inventory Move
|
||||
- WMS Work Queue
|
||||
|
||||
이들은 Backend 타입을 더 만드는 것보다 사용자 입력·상태·예외·복구 동작을 실제 Vue 코드에서 완성해야 한다.
|
||||
|
||||
### P1 — Visual QA Gate
|
||||
|
||||
CI/개발환경에 pnpm dependency를 복원한 뒤 1440×900 / 1280×720 / Dark / forced-colors / WMS 390×844 기준의 Playwright screenshot + interaction regression을 main gate에 넣는다.
|
||||
|
||||
## 9. 최종 판단
|
||||
|
||||
v52는 “컴포넌트 수 증가”가 아니라 **기존 컴포넌트와 Template이 실제 업무 화면에서 더 명확한 의미를 가지도록 만드는 작업**이다.
|
||||
|
||||
향후에도 다음 질문으로 리뷰한다.
|
||||
|
||||
> 사용자가 화면을 처음 보고 현재 상태, 처리할 대상, 주요 Action, 다음 이동을 설명 없이 판단할 수 있는가?
|
||||
|
||||
아니라면 Contract가 아무리 완벽해도 FE는 미완성이다.
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
# KBX FE Presentation Workbench Hardening v51
|
||||
|
||||
## 1. 판단
|
||||
|
||||
v50까지의 가장 큰 리스크는 기능 부족이 아니라 **완성도 불균형**이었다.
|
||||
|
||||
- Screen Contract, Domain 경계, 오류/권한/복구/운영 검증은 매우 강하다.
|
||||
- 반면 실제 사용자가 매일 보는 `Application Shell / Home / T01~T08`의 시각적 계층과 업무면 구분은 상대적으로 얇았다.
|
||||
- 표준 Template SFC가 존재한다는 사실만으로는 표준 화면이 현장에서 같은 품질로 보인다는 것을 보장하지 않는다.
|
||||
- 검증 스크립트가 PASS여도 사용자가 느끼는 탐색 비용, 현재 모듈 인지, 화면 유형 인지, 작업 시작 속도까지 자동으로 좋아지는 것은 아니다.
|
||||
|
||||
따라서 v51은 Backend 기능 추가보다 **실제 Vue/CSS/JavaScript 렌더링 계층**을 우선 보강한다.
|
||||
|
||||
## 2. 유지한 KBX 원칙
|
||||
|
||||
- 한국형 업무 문법과 Predictable Layout을 유지한다.
|
||||
- Card Dashboard를 추가하지 않는다.
|
||||
- 모듈 색은 `상태(success/warning/danger)`와 혼합하지 않는다.
|
||||
- Primary Action 수, Command 위치, Grid/Excel/Keyboard 계약을 바꾸지 않는다.
|
||||
- PrimeVue/AG Grid API를 Vertical Slice에 다시 노출하지 않는다.
|
||||
- 모든 화면을 Runtime JSON UI로 바꾸지 않는다.
|
||||
- UI Disabled/Hidden을 보안 경계로 간주하지 않는다. 기존 Backend Permission/Domain Validation 계약을 유지한다.
|
||||
|
||||
## 3. v51 변경
|
||||
|
||||
### 3.1 Module Identity Theme
|
||||
|
||||
OMS / ERP / WMS / COMMON에 별도의 Module Identity token을 추가했다.
|
||||
|
||||
용도:
|
||||
|
||||
- Side Navigation 활성 표시
|
||||
- Workspace Tab 활성 표시
|
||||
- Page Header 모듈 식별
|
||||
- Home 모듈 섹션 식별
|
||||
- Standard Template의 얇은 accent
|
||||
|
||||
금지:
|
||||
|
||||
- 오류/성공/경고 의미 대체
|
||||
- 행 전체 색칠
|
||||
- 사용자가 임의 CSS/색상 문자열을 주입하는 Runtime Theme
|
||||
|
||||
Light/Dark와 Windows forced-colors까지 token generator가 동일 Source of Truth에서 생성한다.
|
||||
|
||||
### 3.2 Home = 업무 시작 Workbench
|
||||
|
||||
Home 상단에 대형 KPI Card가 아닌 compact operational pulse를 추가했다.
|
||||
|
||||
- 사용 가능 화면
|
||||
- 미저장 업무
|
||||
- 진행 작업
|
||||
- 새 알림
|
||||
|
||||
각 항목은 단순 숫자가 아니라 기존 메뉴검색 / dirty resume / job center / notification center로 이어지는 작업 진입점이다.
|
||||
|
||||
모듈별 업무 목록은 OMS/ERP/WMS/COMMON identity를 명확히 하되 메뉴 깊이를 늘리지 않는다.
|
||||
|
||||
### 3.3 T01~T08 Presentation Grammar
|
||||
|
||||
표준 Template이 동일한 빈 컨테이너처럼 보이지 않도록 업무 목적에 맞는 surface hierarchy를 부여했다.
|
||||
|
||||
| Type | v51 시각/조작 강조 |
|
||||
|---|---|
|
||||
| T01 Search/List | Search 시작점 + Grid 작업면 + sticky summary |
|
||||
| T02 Master CRUD | Master list / Detail의 명확한 분리와 Detail identity |
|
||||
| T03 Header+Detail | Header form / Detail grid / Workflow의 단계 분리 |
|
||||
| T04 Fast Entry | Keyboard guide와 입력면을 최우선 |
|
||||
| T05 Master/Detail | Master context와 Detail 근거의 pane hierarchy |
|
||||
| T06 Queue | 일반 작업과 Exception 우선순위를 시각적으로 분리 |
|
||||
| T07 Reconcile | 비교/불일치 Summary를 Grid보다 먼저 인지 |
|
||||
| T08 Import | Step/Content/Result의 처리 단계 명확화 |
|
||||
|
||||
T09 WMS Mobile의 독립 Touch/Scanner 문법은 기존 계약을 유지한다.
|
||||
|
||||
### 3.4 Static HTML/JavaScript Reference parity
|
||||
|
||||
`apps/web/fe-reference`도 Vue와 별개의 장식 Demo가 되지 않도록 같은 문법을 반영했다.
|
||||
|
||||
- Home operational pulse
|
||||
- Module identity
|
||||
- T01~T08 template presentation differentiation
|
||||
- 기존 실제 검색/선택/오류/복구/Import/WMS 동작 보존
|
||||
|
||||
이 Reference는 Framework 계약을 증명하는 문서가 아니라 실제 Browser DOM/CSS/JS UX를 빠르게 확인하는 독립 FE 기준면으로 유지한다.
|
||||
|
||||
## 4. Token / 기술부채 처리
|
||||
|
||||
처음 스타일 강화 과정에서 4개의 hard-coded px literal이 Design Debt Gate에 걸렸다.
|
||||
|
||||
기준선을 올리지 않고 다음처럼 제거했다.
|
||||
|
||||
- Home max width는 token source로 이동
|
||||
- tab active indicator는 border-width token 계산 사용
|
||||
- sticky summary shadow는 border-width token 계산 사용
|
||||
|
||||
결과:
|
||||
|
||||
- Design debt baseline: 131
|
||||
- v51 measured: 116
|
||||
- Ratchet PASS
|
||||
|
||||
즉, 화면 완성도 강화 때문에 토큰 규율을 희생하지 않았다.
|
||||
|
||||
## 5. Security / Stability
|
||||
|
||||
v51은 presentation hardening이므로 Domain/Permission 계약을 약화하지 않는다.
|
||||
|
||||
- `data-kbx-module` 값은 Screen Definition의 고정 module vocabulary를 사용한다.
|
||||
- Runtime arbitrary CSS theme injection을 도입하지 않았다.
|
||||
- Status semantic과 module identity를 분리해 업무상 상태 오판 가능성을 줄였다.
|
||||
- forced-colors fallback을 generator에 포함해 접근성 모드에서 정보가 색상에 종속되지 않는다.
|
||||
- 기존 fail-closed write permission, action allow-list, server validation, concurrency/error contracts는 회귀 검증을 그대로 통과한다.
|
||||
|
||||
## 6. 검증 결과
|
||||
|
||||
`node scripts/validate-kbx.mjs` 전체 PASS.
|
||||
|
||||
핵심 결과:
|
||||
|
||||
- TypeScript/Vue script syntax: 308 units PASS
|
||||
- Screen governance: 20 screens PASS
|
||||
- Design system: 161 tokens / 85 components PASS
|
||||
- T01~T09 template completeness PASS
|
||||
- FE v41~v50 regression PASS
|
||||
- 신규 `validate-fe-presentation-v51.mjs` PASS
|
||||
- Design debt ratchet: `116 <= 131` PASS
|
||||
- Design-code parity PASS
|
||||
- Release governance PASS
|
||||
|
||||
## 7. 아직 남은 냉정한 과제
|
||||
|
||||
v51로 “보이는 껍데기” 문제는 한 단계 개선됐지만 다음은 아직 완료가 아니다.
|
||||
|
||||
1. **실제 Vite Build/Playwright Visual Evidence**
|
||||
현재 작업 환경에 pnpm/node_modules가 없어 실제 Vue bundle build와 Playwright browser E2E는 실행하지 못했다. Dependency-free syntax/governance 검증과 static FE 검증은 통과했다.
|
||||
|
||||
2. **Golden Screen의 픽셀보다 Task Metric 검증**
|
||||
OMS-ORD-001/002, WMS-PICK-001에서 Clicks/Task, Keyboard strokes, Exception resolution time을 실측해야 한다.
|
||||
|
||||
3. **Component State Catalog의 시각 깊이**
|
||||
Default만 보는 Catalog가 아니라 Readonly/Disabled/Error/Changed/AI Suggested/Dark/Forced-colors까지 독립 재현성을 더 강화해야 한다.
|
||||
|
||||
4. **FE Density 실제 사용자 검증**
|
||||
1440×900 Compact 기준으로 “한 화면에 정보가 많다”가 아니라 “필요한 정보가 빨리 보인다”는 것을 현업 시나리오로 검증해야 한다.
|
||||
|
||||
## 8. 다음 우선순위
|
||||
|
||||
다음 버전은 새 프레임워크를 만들지 말고 아래 순서가 적절하다.
|
||||
|
||||
1. OMS-ORD-001 Golden Screen의 실제 DOM/keyboard/bulk/detail drawer polish
|
||||
2. OMS-ORD-002 Header+Detail fast-entry polish
|
||||
3. ERP-MST-ITEM-001 Master CRUD field density/lookup/audit polish
|
||||
4. T04 Fast Entry의 paste/error navigation 현장성 강화
|
||||
5. Browser 실행 가능한 Playwright Visual + keyboard regression을 CI evidence로 고정
|
||||
|
||||
핵심은 더 많은 추상화가 아니라 **이미 있는 KBX 계약을 실제 화면에서 더 잘 느끼게 만드는 것**이다.
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
# KBX FE QA Hardening v38
|
||||
|
||||
## 1. 판단
|
||||
|
||||
v37의 가장 큰 문제는 컴포넌트 개수가 부족한 것이 아니었다. 화면과 계약은 많았지만 **사용자가 실제로 클릭·입력·전환했을 때 업무 상태가 끝까지 이어지는 FE 상호작용 밀도**가 낮았다.
|
||||
|
||||
30년 현장 QA 관점에서 가장 위험한 유형은 “버튼이 있으니 구현된 것처럼 보이는 UI”다. 이런 화면은 데모에서는 통과하지만 운영에서는 오조작, 중복처리, 미저장 유실, 교육비 증가로 이어진다.
|
||||
|
||||
## 2. v37에서 P0/P1로 본 결함
|
||||
|
||||
### P0 — 미저장 데이터 유실 가능
|
||||
|
||||
FE Reference Lab에서 Dirty Tab을 닫아도 보호 절차가 없었다.
|
||||
|
||||
표준은 `계속 편집 / 변경 버리기 / 저장 후 이동`을 요구한다. v38은 탭 닫기 시 동일한 3-way guard를 구현했다.
|
||||
|
||||
### P0 — 선택 0건 Workflow Action이 활성처럼 보임
|
||||
|
||||
T01 Command Bar의 `출고지시`가 행 선택과 시각적으로 결합되지 않았다. 업무 시스템에서 선택 0건 Action은 단순 미관 문제가 아니라 오조작 가능성을 높인다.
|
||||
|
||||
v38은 선택 0건에서 disabled, 선택 후 enable, Bulk Bar와 동일 selection source를 사용한다.
|
||||
|
||||
### P1 — 화면은 바뀌어 보이지만 업무 상태는 고정
|
||||
|
||||
v37의 T05/T06/T07은 Master 행, Queue 요약, mismatch checkbox가 실제 결과를 갱신하지 않는 부분이 있었다.
|
||||
|
||||
v38은 다음을 실제 상태 전이로 변경했다.
|
||||
|
||||
- T05 품목 → Location/재고이력
|
||||
- T06 작업유형/예외 → Queue Grid
|
||||
- T07 불일치 checkbox → Comparison Grid
|
||||
|
||||
### P1 — Fast Entry가 “contenteditable 데모”에 가까움
|
||||
|
||||
ERP Fast Entry는 Excel 사용자의 기대를 충족해야 한다. 단순 `contenteditable`만으로는 입력 계약이 불명확하다.
|
||||
|
||||
v38은 cell input, Enter next, TSV paste, 행 추가/복제/Fill Down, 단가 오류 표시, 오류 시 저장 disable을 넣었다.
|
||||
|
||||
### P1 — WMS 수량 입력에 browser prompt 사용
|
||||
|
||||
현장 PDA에서 `prompt()`는 제품 UX로 볼 수 없다. Focus, Touch Target, Validation, 오류 복구가 모두 통제되지 않는다.
|
||||
|
||||
v38은 수량 Dialog와 예외 신고 Dialog로 교체하고 Scanner keyboard-wedge의 Enter 종료 흐름을 구현했다.
|
||||
|
||||
### P1 — Preference 저장 실패가 Shell 초기화를 깨뜨릴 가능성
|
||||
|
||||
localStorage는 브라우저 정책/사설모드/임베드 환경에서 실패할 수 있다. Preference는 업무 Source of Truth가 아니다.
|
||||
|
||||
v38은 localStorage를 fail-soft로 격리했다. 저장소가 없어도 화면과 업무 흐름은 계속 동작한다.
|
||||
|
||||
## 3. 홈 네비게이션 평가
|
||||
|
||||
홈은 “대시보드”가 아니라 **업무 시작점**이어야 한다.
|
||||
|
||||
v38 홈 우선순위:
|
||||
|
||||
1. 실패/예외/중요 알림
|
||||
2. 미저장/열린 업무 이어서 처리
|
||||
3. 즐겨찾기/최근 업무
|
||||
4. 모듈별 표준 화면
|
||||
5. Ctrl+K 직접 검색
|
||||
|
||||
차트·장식 카드는 추가하지 않았다. 사용자가 `지금 무엇을 처리해야 하는가`와 `어디로 들어가야 하는가`를 1~2번의 조작으로 해결하는 데 집중한다.
|
||||
|
||||
## 4. 보안 관점
|
||||
|
||||
FE 보안은 버튼 숨김이 아니다. 실제 권한/업무 규칙은 서버가 책임져야 한다. 이번 Lab에서 다루는 것은 브라우저 측 공격면과 실패 격리다.
|
||||
|
||||
적용:
|
||||
|
||||
- restrictive CSP
|
||||
- 외부 script/style 없음
|
||||
- dynamic HTML escape
|
||||
- localStorage allow-list 복원
|
||||
- 개인정보 샘플 masking
|
||||
- eval/document.write/prompt 금지
|
||||
|
||||
미적용/BE 책임:
|
||||
|
||||
- Permission enforcement
|
||||
- Domain validation
|
||||
- Concurrency
|
||||
- Idempotency
|
||||
- Audit write
|
||||
- CSRF/Authentication 정책
|
||||
|
||||
Reference Lab이 이것들을 구현한 것처럼 가장하지 않는다.
|
||||
|
||||
## 5. 과유불급 기준
|
||||
|
||||
이번 단계에서 하지 않은 것:
|
||||
|
||||
- 자체 Grid Engine
|
||||
- Runtime JSON UI Builder
|
||||
- 자체 Router Framework
|
||||
- API mock server 대형화
|
||||
- FE에서 Domain Rule 복제
|
||||
- AI가 화면을 임의 생성하는 구조
|
||||
|
||||
Reference Lab은 “실제 FE 문법을 검증하는 얇은 실행 원형”으로 유지한다.
|
||||
|
||||
## 6. 다음 QA Gate
|
||||
|
||||
다음 iteration에서 우선할 항목:
|
||||
|
||||
1. 실제 Vue Golden Screen과 FE Reference의 visual/interaction parity
|
||||
2. KbxLookup F2 → Search → Enter → Focus restore 실브라우저 검증
|
||||
3. KbxDataGrid clipboard/edit/error-navigation 실 AG Grid 검증
|
||||
4. 1280×720 compact overflow 및 200% zoom 접근성
|
||||
5. WMS 390×844에서 네트워크 지연/중복 Scan/재전송 UX
|
||||
6. T08 10k/30k row 배경 Job 상태와 화면 이탈/복귀
|
||||
|
||||
이 Gate를 통과하기 전에는 새 컴포넌트 종류를 늘리는 것보다 기존 핵심 컴포넌트의 실제 브라우저 계약을 강화하는 것이 우선이다.
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
# KBX FE QA Hardening v39
|
||||
|
||||
## 1. 결론
|
||||
|
||||
v38은 T01~T09의 화면 상태와 주요 클릭 흐름을 실제 JavaScript 상태 전이로 올렸지만, 운영 FE 기준으로는 아직 다섯 가지가 부족했다.
|
||||
|
||||
1. Dirty 보호가 탭 닫기에 집중되어 화면 간 이동에는 동일하게 적용되지 않았다.
|
||||
2. Lookup은 핵심 ERP 입력 계약인데 Reference Lab에서 실제 F2 검색/선택/Focus 이동이 없었다.
|
||||
3. 도움말/AI/제안/작업/알림이 버튼과 Toast 수준이라 Utility Component의 실체가 부족했다.
|
||||
4. Excel Import가 동기식 Wizard처럼 끝나 장시간 작업의 이탈/복귀 UX를 검증할 수 없었다.
|
||||
5. WMS Mobile에서 중복 Scanner 입력과 네트워크 검증 실패가 데이터 변경으로 이어지지 않는다는 계약을 실행 증적으로 닫지 못했다.
|
||||
|
||||
v39는 BE 결합을 늘리지 않고 위 결함을 실제 HTML/CSS/JavaScript와 핵심 Vue Component에서 보완한다.
|
||||
|
||||
## 2. P0/P1 개선
|
||||
|
||||
### P0 — Dirty Navigation Guard
|
||||
|
||||
기존: Dirty Tab을 `×`로 닫을 때만 보호.
|
||||
|
||||
v39:
|
||||
|
||||
- Dirty 화면 → Side Navigation
|
||||
- Dirty 화면 → Workspace Tab
|
||||
- Dirty 화면 → Home/다른 Screen
|
||||
|
||||
모두 `계속 편집 / 변경 버리기 / 저장 후 이동`을 거친다.
|
||||
|
||||
단순 `beforeunload`에 의존하지 않고 SPA Navigation 상태에서 동일 계약을 적용한다.
|
||||
|
||||
### P0 — F2 Lookup 실제 구현
|
||||
|
||||
T02/T03의 Lookup은 다음을 실제 지원한다.
|
||||
|
||||
- F2 또는 검색 버튼
|
||||
- 코드/명칭 검색
|
||||
- Arrow Up/Down
|
||||
- Enter 선택
|
||||
- Esc 닫기
|
||||
- 선택 후 Code + Name 동시 반영
|
||||
- 선택 후 다음 Form Control로 Focus 이동
|
||||
- 취소 시 기존 Trigger Focus 복원
|
||||
|
||||
실제 `packages/kbx-ui/src/components/KbxLookup.vue`도 선택/취소 Focus 정책을 분리했다.
|
||||
|
||||
### P1 — Context-aware Home Navigation
|
||||
|
||||
홈 예외 항목은 이제 단순 화면 링크가 아니다.
|
||||
|
||||
- 재고부족 → T01 + `stock` Quick Filter
|
||||
- 처리오류 → T01 + `error` Quick Filter
|
||||
- 대사불일치 → T07 + mismatch-only
|
||||
- Excel Job → T08 + 현재 Job 단계
|
||||
|
||||
홈에서 들어간 사용자가 다시 조건을 찾는 재작업을 제거한다.
|
||||
|
||||
### P1 — Utility Drawer 실체화
|
||||
|
||||
`도움말 / AI / 제안 / 작업 / 알림`을 Toast에서 실제 Drawer로 전환했다.
|
||||
|
||||
Drawer는:
|
||||
|
||||
- Background `inert`
|
||||
- Focus Trap
|
||||
- Esc Close
|
||||
- Focus Restore
|
||||
|
||||
를 지원한다.
|
||||
|
||||
AI 빠른 동작은 설명 문자열에서 끝나지 않고 실제 화면 Context로 연결한다. 데이터 변경은 하지 않는다.
|
||||
|
||||
### P1 — T08 Background Job UX
|
||||
|
||||
T08을 6단계로 명확히 했다.
|
||||
|
||||
`파일 → 매핑 → 검증 → 미리보기 → 반영 → 결과`
|
||||
|
||||
반영 단계는 비동기 Job을 모사한다.
|
||||
|
||||
- Progress
|
||||
- 처리건수
|
||||
- 오류 제외건수
|
||||
- 다른 화면 이동 가능
|
||||
- Home/작업센터에서 복귀
|
||||
- 완료 후 결과 확인
|
||||
|
||||
FE Reference는 Domain DB Commit을 흉내내지 않고 Job UX와 상태 복구만 검증한다.
|
||||
|
||||
### P1 — WMS Scanner/Network Guard
|
||||
|
||||
- 동일 Barcode 700ms 이내 중복 입력은 무시
|
||||
- 중복 시 수량 불변
|
||||
- `unstable/offline`에서는 서버 검증이 필요한 수량을 반영하지 않음
|
||||
- Mobile 767px 이하에서는 Desktop Shell을 제거하고 T09가 viewport를 직접 점유
|
||||
|
||||
Desktop ERP를 억지로 Mobile Reflow하지 않고 WMS Mobile Template을 분리한다.
|
||||
|
||||
## 3. 30년 현장 QA 관점의 비판
|
||||
|
||||
### 화면이 많다는 것은 완성도가 아니다
|
||||
|
||||
T01~T09가 모두 존재해도 다음이 안 되면 데모다.
|
||||
|
||||
- 예외 링크가 실제 예외 필터를 적용하는가?
|
||||
- Lookup 이후 Focus가 끊기지 않는가?
|
||||
- Dirty 상태에서 다른 메뉴를 눌러도 데이터가 보호되는가?
|
||||
- Background Job 중 다른 업무를 할 수 있는가?
|
||||
- Scanner가 두 번 들어와도 두 번 처리되지 않는가?
|
||||
|
||||
v39는 이 질문을 자동/브라우저 증적으로 검증한다.
|
||||
|
||||
### FE에서 BE를 흉내내면 안 된다
|
||||
|
||||
Reference Lab에서 의도적으로 구현하지 않은 것:
|
||||
|
||||
- 실제 권한 판정
|
||||
- 재고/상태 Domain Validation
|
||||
- Audit Write
|
||||
- Idempotency Store
|
||||
- Outbox/Inbox
|
||||
- 실제 Hangfire Job
|
||||
|
||||
FE는 해당 결과를 올바르게 표현하고 복구하는 계약만 가진다.
|
||||
|
||||
### Home은 Dashboard가 아니라 Router + Workbench다
|
||||
|
||||
홈 KPI를 늘리는 것이 아니라 `예외 → 실제 필터링된 업무`, `진행 Job → 해당 Job 상태`로 이동해야 한다.
|
||||
|
||||
홈의 품질은 Card 수가 아니라 **재탐색 횟수와 Context Switch 수**로 평가해야 한다.
|
||||
|
||||
## 4. 브라우저 QA 증적
|
||||
|
||||
`apps/web/fe-reference/previews/browser-interaction-v39.json`
|
||||
|
||||
31/31 PASS:
|
||||
|
||||
- Home intent routing
|
||||
- T01 keyboard Space/Enter
|
||||
- Drawer inert/Esc recovery
|
||||
- Dirty navigation guard
|
||||
- F2 Lookup + Enter + next focus
|
||||
- Help/Suggestion Drawer
|
||||
- T08 6-step + background job leave/return
|
||||
- WMS duplicate scan guard
|
||||
- 1280×720 document overflow 없음
|
||||
- 720px zoom-pressure Shell overflow 없음
|
||||
- Runtime console/page error 0
|
||||
|
||||
추가 렌더링:
|
||||
|
||||
- `home-v39-1440x900.png`
|
||||
- `t01-order-stock-v39-1440x900.png`
|
||||
- `lookup-v39-1440x900.png`
|
||||
- `t08-job-v39-1440x900.png`
|
||||
- `t01-v39-1280x720.png`
|
||||
- `t09-picking-v39-390x844.png`
|
||||
|
||||
## 5. 다음 Gate
|
||||
|
||||
새 컴포넌트를 늘리기 전에 다음을 우선한다.
|
||||
|
||||
1. 실제 Vue Golden Screen과 Reference Lab의 Visual/Interaction parity 자동 비교
|
||||
2. 실제 AG Grid KbxDataGrid Clipboard/Paste/Error Navigation 브라우저 테스트
|
||||
3. T03 Form 전체 `Enter = 다음 입력`과 F2 Lookup 혼합 흐름 E2E
|
||||
4. 200% Browser Zoom 및 WCAG Focus/Label 자동 검사
|
||||
5. WMS `unstable/offline → online` 복구 및 Pending Command UX
|
||||
6. T08 Refresh 이후 Job API 복구 계약 — 이 단계부터는 BE Read API와 결합 필요
|
||||
|
||||
이 Gate 전에는 새 Metadata Engine, Runtime UI Builder, 별도 Grid Engine을 추가하지 않는다.
|
||||
+267
@@ -0,0 +1,267 @@
|
||||
# KBX FE QA Hardening v40
|
||||
|
||||
## 1. 결론
|
||||
|
||||
v39는 Dirty Navigation, F2 Lookup, Utility Drawer, T08 Background Job, WMS Scanner/Network Guard를 실제 FE 상호작용으로 올렸다. 그러나 30년 현장 QA 관점에서 다시 보면 **"화면에 기능이 있다"와 "업무 완료 루프가 닫힌다"는 여전히 다른 문제**였다.
|
||||
|
||||
v39의 잔여 결함은 다음과 같았다.
|
||||
|
||||
1. T01 검색조건이 일부 Quick Filter 중심이라 실제 검색 입력과 결과의 결합이 부족했다.
|
||||
2. T02 Master CRUD가 선택/조회 위주이고 신규·복사·검증·저장·Dirty 전환의 실체가 약했다.
|
||||
3. T03 Transaction이 입력 화면처럼 보이지만 Header/Detail 계산·검증·저장·확정의 실제 상태 전이가 충분하지 않았다.
|
||||
4. T06 Work Queue와 T07 Reconcile의 주요 업무 Action이 결과 데이터를 바꾸지 않는 데모 성격이 남아 있었다.
|
||||
5. 전역 F8이 화면별 저장/검증 Handler를 우회해 Dirty만 해제할 수 있는 경로가 있었다. 이는 **거짓 저장 성공 인식**을 만들 수 있는 P0 결함이다.
|
||||
6. SPA 내부 Dirty Guard는 강화됐지만 browser back/refresh/close까지 동일한 데이터 유실 방어가 필요했다.
|
||||
|
||||
v40은 새 Framework나 BE 결합을 늘리지 않고 위 결함을 **실제 HTML/CSS/JavaScript 상태 전이**로 닫는다.
|
||||
|
||||
---
|
||||
|
||||
## 2. v40 완료 기준
|
||||
|
||||
Template별 최소 업무 완료 루프를 다음처럼 강제한다.
|
||||
|
||||
- T01 Search/List: `조건 → 조회 → 결과 → 선택 → 처리`
|
||||
- T02 Master CRUD: `목록선택/신규 → 편집 → 검증 → 저장 → 다른 Master 전환`
|
||||
- T03 Header+Detail: `Header/Line 입력 → 계산 → 검증 → 저장 → 상태전이`
|
||||
- T04 Fast Entry: `입력/Paste → Validation → 오류해결 → 저장`
|
||||
- T05 Master/Detail: `Master 선택 → Detail Context 갱신 → 근거 Drill-down`
|
||||
- T06 Work Queue: `Queue 필터 → 대상선택 → 업무 Action → 결과`
|
||||
- T07 Reconcile: `불일치 필터 → 대상선택 → Resolution → 재계산 결과`
|
||||
- T08 Import: `파일 → 매핑 → 검증 → 미리보기 → Job 반영 → 결과`
|
||||
- T09 WMS Mobile: `Scan → Validation → Feedback → 수량/예외 → 다음 업무`
|
||||
|
||||
버튼이 존재하는 것만으로는 완료로 보지 않는다. **업무 상태가 실제로 바뀌고 실패 시 복구 가능해야 한다.**
|
||||
|
||||
---
|
||||
|
||||
## 3. P0/P1 개선
|
||||
|
||||
### P0 — F8 Validation Bypass 제거
|
||||
|
||||
기존 위험:
|
||||
|
||||
- Global F8
|
||||
- 화면별 Validation/Save Handler 우회
|
||||
- Dirty 상태만 해제 가능
|
||||
- 사용자는 저장되었다고 오인
|
||||
|
||||
v40:
|
||||
|
||||
- T02 F8 → `#masterSave` 실제 저장 루프
|
||||
- T03 F8 → `#saveTransaction` 실제 저장 루프
|
||||
- T04 F8 → `#fastSave` 실제 Validation Gate
|
||||
- `저장 후 이동/닫기`도 동일 `trySaveDirtyScreen()`을 사용
|
||||
|
||||
즉 Keyboard Shortcut은 편의 기능일 뿐 **업무 검증을 우회하는 별도 저장 경로가 아니다.**
|
||||
|
||||
### P0 — Browser-level Dirty Guard
|
||||
|
||||
SPA Navigation Guard에 더해:
|
||||
|
||||
- hash route 변경
|
||||
- browser back/forward 계열 route change
|
||||
- refresh
|
||||
- tab/window close
|
||||
|
||||
에서도 Dirty state를 보호한다.
|
||||
|
||||
Preference나 URL 상태보다 사용자의 미저장 업무가 우선한다.
|
||||
|
||||
### P1 — T01 실제 검색 결합
|
||||
|
||||
검색 Panel의 다음 값이 실제 결과 필터에 연결된다.
|
||||
|
||||
- 판매채널
|
||||
- 상태
|
||||
- 통합검색
|
||||
- 상세조건의 예외유형
|
||||
- 기존 Quick Filter
|
||||
|
||||
Loading 후 재렌더링되어도 입력값이 유지된다. Reset은 UI 값과 상태 모델을 함께 초기화한다.
|
||||
|
||||
### P1 — T02 Master CRUD Completion Loop
|
||||
|
||||
추가된 실제 상태:
|
||||
|
||||
- `masterDraft`
|
||||
- `new / edit` mode
|
||||
- Master 목록 검색
|
||||
- 필수값 검증
|
||||
- 신규 ItemCode 중복 검증
|
||||
- F2 Warehouse Lookup 반영
|
||||
- 신규 저장
|
||||
- 기존 수정 저장
|
||||
- 복사 시 Identity/Barcode 제외
|
||||
- 사용중지/사용재개 상태 변경
|
||||
- Dirty 상태에서 다른 Master 선택 시 3-way Guard
|
||||
|
||||
Master Detail 화면은 이제 단순 "목록 옆 Form"이 아니라 편집 업무 루프를 검증할 수 있다.
|
||||
|
||||
### P1 — T03 Transaction Completion Loop
|
||||
|
||||
Header와 Line을 실제 state로 관리한다.
|
||||
|
||||
- 주문일
|
||||
- 거래처 Lookup
|
||||
- 출고창고 Lookup
|
||||
- 수취인/연락처/주소
|
||||
- 품목코드
|
||||
- 수량
|
||||
- 단가
|
||||
- 비고
|
||||
- 수량×단가 계산
|
||||
- 총수량/총금액 Summary
|
||||
|
||||
Validation 실패 시 저장하지 않고 Summary에서 문제 위치로 Focus할 수 있다. 저장 후에만 Dirty가 해제된다. `확정`은 Dirty/Validation 상태를 통과해야 하고 `작성 → 확정` 상태전이를 실제 표시한다.
|
||||
|
||||
### P1 — T06 Queue Action 실체화
|
||||
|
||||
- 행 Selection state
|
||||
- 현재 결과 Select All
|
||||
- Selection 기반 Command enable/disable
|
||||
- 작업자 배정 → 선택 Row의 작업자 실제 변경
|
||||
- Wave 생성 → 대상 Row의 Wave 실제 변경
|
||||
- 결과 Feedback + Close
|
||||
|
||||
Queue는 Dashboard 숫자가 아니라 **지금 처리할 작업을 선택하고 결과를 확인하는 화면**이어야 한다.
|
||||
|
||||
### P1 — T07 Reconcile Resolution 실체화
|
||||
|
||||
- mismatch-only 실제 필터
|
||||
- 행 Selection
|
||||
- Selection 기반 재처리
|
||||
- 재처리 시 `Actual = Expected`
|
||||
- `Difference = 0`
|
||||
- 원인/상태를 `재처리 완료 / 해결`로 갱신
|
||||
- 처리 후 mismatch-only 목록에서 해결 Row 제거
|
||||
|
||||
비교값만 보여주고 사용자가 다른 화면에서 해결하게 만들지 않는다.
|
||||
|
||||
---
|
||||
|
||||
## 4. 보안/안정성 판단
|
||||
|
||||
### FE는 보안 경계가 아니다
|
||||
|
||||
v40 Reference Lab의 disabled/validation/guard는 오조작을 줄이는 UX다. 다음은 여전히 Server 최종 책임이다.
|
||||
|
||||
- Permission Enforcement
|
||||
- 업무 상태 전이 검증
|
||||
- 재고 정합성
|
||||
- Optimistic Concurrency
|
||||
- Idempotency
|
||||
- Audit
|
||||
- DB Constraint
|
||||
- Outbox/Inbox
|
||||
|
||||
FE가 상태를 바꿨다고 실제 Domain Transaction이 승인됐다고 간주하지 않는다.
|
||||
|
||||
### Reference Lab이 의도적으로 하지 않는 것
|
||||
|
||||
- 실제 DB Commit
|
||||
- 실제 Hangfire 실행
|
||||
- 실제 Permission API
|
||||
- 실제 Audit Write
|
||||
- 실제 외부 API 연계
|
||||
|
||||
Reference Lab의 목적은 **FE 사용 계약, 상태 표현, 실패 복구, 키보드/Focus, Navigation 품질을 BE 없이 재현**하는 것이다.
|
||||
|
||||
---
|
||||
|
||||
## 5. 브라우저 QA 증적
|
||||
|
||||
`apps/web/fe-reference/previews/browser-interaction-v40.json`
|
||||
|
||||
**49 / 49 PASS**, runtime console/page error 0.
|
||||
|
||||
주요 검증:
|
||||
|
||||
- Home actionable attention
|
||||
- Ctrl+K 메뉴 검색 / Enter 화면 이동
|
||||
- Side Nav collapse
|
||||
- Compact/Comfortable density
|
||||
- Utility Drawer inert / Focus Trap / Esc Restore
|
||||
- T01 판매채널 + keyword 실제 결과 필터
|
||||
- T01 Loading 재렌더 후 검색값 보존
|
||||
- T01 Selection 기반 출고지시 enable
|
||||
- T01 Reset 실제 조건 초기화
|
||||
- T02 blank 신규 상태에서 F8 Validation 발생
|
||||
- T02 F2 Lookup → Enter → 다음 Control Focus
|
||||
- T02 신규 저장 후 Master 목록 반영
|
||||
- T02 Dirty Master 전환 Guard
|
||||
- T03 invalid quantity 저장 차단
|
||||
- T03 수정 후 F8 저장
|
||||
- T03 저장 후 확정 상태전이
|
||||
- T04 오류 Cell 존재 시 저장 차단 / 해결 후 저장
|
||||
- T05 Master 선택 시 Detail Context 변경
|
||||
- T06 Selection → 작업자 배정 → Row 상태 변경
|
||||
- T07 Selection → 재처리 → mismatch 2 → 1
|
||||
- Dirty 상태에서 hash route 변경 차단 및 discard 후 이동
|
||||
- T08 Job 진행 중 화면 이탈/복귀 및 완료
|
||||
- T09 동일 Barcode 중복 입력 미반영
|
||||
- T09 잘못된 Barcode actionable error
|
||||
- T09 390px viewport Full View
|
||||
- 1280×720 document horizontal overflow = 0
|
||||
|
||||
### 실행환경 제한
|
||||
|
||||
현재 실행환경은 Chromium의 `file://` 및 localhost 직접 navigation이 관리 정책으로 차단된다. 따라서 Browser QA는 **운영과 동일한 DOM/CSS/JavaScript를 사용하고 CSS/JS asset만 inline한 Playwright harness**에서 수행했다.
|
||||
|
||||
- 상호작용 코드: 동일
|
||||
- DOM: 동일
|
||||
- CSS: 동일
|
||||
- production CSP meta: harness에서는 navigation 제약 때문에 제거
|
||||
- CSP/외부 script/object/base/frame 정책: `validate:fe-reference` 정적 Gate로 별도 검증
|
||||
|
||||
이 제한을 browser-native CSP E2E를 통과한 것으로 과장하지 않는다.
|
||||
|
||||
---
|
||||
|
||||
## 6. 30년 현장 QA 관점의 냉정한 판단
|
||||
|
||||
### 1. "컴포넌트 존재"를 완료율로 계산하면 안 된다
|
||||
|
||||
실무에서 사고를 만드는 것은 빠진 버튼보다 **동작하는 것처럼 보이는 버튼**이다. T06/T07처럼 Action이 Toast만 띄우면 사용자는 처리됐다고 오인할 수 있다.
|
||||
|
||||
완료 기준은 `clickable`이 아니라 `state transition + feedback + recovery`다.
|
||||
|
||||
### 2. Keyboard Shortcut은 가장 위험한 우회 경로가 될 수 있다
|
||||
|
||||
마우스 Save는 Validation을 거치는데 F8은 Dirty만 해제한다면, 숙련 사용자가 오히려 더 위험하다. ERP에서 Keyboard 생산성과 데이터 정합성은 함께 설계해야 한다.
|
||||
|
||||
### 3. Home은 숫자판이 아니라 업무 Context Router여야 한다
|
||||
|
||||
홈에서 예외 숫자를 보고 다시 메뉴를 열고 필터를 선택하게 만들면 홈은 업무를 줄이지 못한다. 홈 클릭 한 번으로 **대상 화면 + 대상 Context**까지 전달되어야 한다.
|
||||
|
||||
### 4. Template 완성도는 Happy Path보다 Recovery에서 갈린다
|
||||
|
||||
운영 화면은 다음 질문으로 봐야 한다.
|
||||
|
||||
- 잘못 입력하면 어디에서 고칠 수 있는가?
|
||||
- 저장 실패해도 입력이 남아 있는가?
|
||||
- 다른 화면으로 이동하다 데이터가 사라지지 않는가?
|
||||
- 처리 결과가 실제 Row 상태와 일치하는가?
|
||||
- 화면을 다시 열어도 사용자가 무엇을 했는지 이해할 수 있는가?
|
||||
|
||||
### 5. 지금 새 Meta Framework를 만드는 것은 기술부채다
|
||||
|
||||
현재 가장 중요한 미완성은 새로운 Template 종류가 아니다. **Reference FE와 실제 Vue/PrimeVue/AG Grid Golden Screen의 동작 일치**다.
|
||||
|
||||
이 단계에서 Runtime JSON UI Engine, Low-code Builder, 자체 Grid Engine을 추가하면 다시 BE/Framework 완성도만 올라가고 실제 FE 품질은 뒤처질 가능성이 높다.
|
||||
|
||||
---
|
||||
|
||||
## 7. 다음 Gate
|
||||
|
||||
v41 우선순위는 다음으로 제한한다.
|
||||
|
||||
1. 실제 Vue `OMS-ORD-001`과 FE Reference T01의 Interaction parity
|
||||
2. 실제 Vue `OMS-ORD-002`의 Form + F2 + Enter + F8 E2E
|
||||
3. 실제 AG Grid `KbxDataGrid` multi-cell paste / validation navigation / selection contract
|
||||
4. actual Vue Loading/Empty/Error/Conflict/Permission 상태 Browser E2E
|
||||
5. 200% browser zoom + keyboard-only + label/focus accessibility gate
|
||||
6. WMS `offline/unstable → online` pending command recovery
|
||||
7. T08 Refresh 이후 Job 복구 — 이 시점부터 실제 Read API와 결합
|
||||
|
||||
그 전에는 Template/Component 종류를 더 늘리지 않는다.
|
||||
+236
@@ -0,0 +1,236 @@
|
||||
# KBX FE QA Hardening v41
|
||||
|
||||
## 1. 결론
|
||||
|
||||
v40은 HTML/CSS/JavaScript FE Reference Lab에서 T01~T09의 완료 루프를 상당 부분 닫았다. 그러나 현장 QA 기준으로는 **Reference Lab이 좋아졌다는 사실과 실제 Vue 제품 화면이 좋아졌다는 사실을 동일시할 수 없다.**
|
||||
|
||||
v41은 BE 구조와 Metadata Framework를 확장하지 않고 실제 Vue Golden Screen과 공통 FE Component를 대조했다. 그 결과 다음 운영 결함을 확인했다.
|
||||
|
||||
1. OMS 주문관리 주문번호 Link가 `drillDownRequested`를 emit하지만 Page가 받지 않아 no-op이었다.
|
||||
2. 주문 Grid Double Click은 Detail Drawer가 아니라 Edit Page로 이동하여 조회 Context를 잃었다.
|
||||
3. 외부 `selectionState`가 비워져도 AG Grid 내부 Selection이 남을 수 있어 시각 Selection과 실제 Command 대상이 달라질 위험이 있었다.
|
||||
4. ERP 품목관리에서 Dirty 상태로 다른 Master를 선택하면 현재 편집값이 새 Row 데이터로 덮어써질 수 있었다.
|
||||
5. 주문등록 Grid F2 Lookup은 값은 반영하지만 다음 Editable Cell로 Focus가 이어지지 않았다.
|
||||
6. `KbxLookup` 직접 코드 Enter 성공은 Dialog 선택과 달리 Focus continuation이 없었다.
|
||||
7. `KbxUnsavedChangesDialog`는 Modal 외관은 있지만 Tab Focus Trap / Esc / Focus Restore가 충분하지 않았다.
|
||||
8. OMS 주문관리의 `보류` Command는 실제 Handler가 없는 no-op이었다.
|
||||
9. Grid 오류 개수에 Header/Form 오류가 섞일 수 있어 “셀 오류” 숫자가 실제 Cell 오류보다 많아질 수 있었다.
|
||||
10. Side Navigation의 즐겨찾기 버튼은 보조기술에서 대상 화면명이 빠진 동일한 이름으로 반복됐다.
|
||||
|
||||
이 문제들은 화면 미관보다 우선순위가 높다. **사용자가 보고 있는 상태와 실제 Command 대상이 다르거나, 클릭했는데 아무 일도 일어나지 않거나, 편집값이 전환 과정에서 사라지는 문제는 P0/P1 제품 결함**이다.
|
||||
|
||||
---
|
||||
|
||||
## 2. v41 수정 사항
|
||||
|
||||
### P0/P1 — OMS-ORD-001 List Context / Detail Contract
|
||||
|
||||
- 주문번호 Link `drillDownRequested` → 실제 `openDetail(row)` 연결
|
||||
- Row Double Click → 동일 Detail Drawer로 통일
|
||||
- Drawer에서만 전체 편집 Page로 승격
|
||||
- Drawer open 시 기존 Search/Grid Context 유지
|
||||
- Detail request마다 sequence를 부여하여 Drawer가 닫히거나 다른 주문으로 바뀐 뒤 늦게 도착한 응답이 현재 화면을 덮어쓰지 않게 함
|
||||
- 전화번호는 Drawer 기본 표시에서 masking
|
||||
|
||||
Reference Screen의 “목록 Context를 유지하고 복잡한 수정만 전체 Page로 전환” 규칙을 실제 Vue에 반영한다.
|
||||
|
||||
### P0 — Controlled Grid Selection
|
||||
|
||||
`KbxDataGrid`의 Selection을 AG Grid 내부 상태에만 맡기지 않는다.
|
||||
|
||||
- `selectionState` 외부 모델과 실제 Grid row selection을 동기화
|
||||
- Programmatic sync 중 `selectionChanged` 재귀 emit 차단
|
||||
- explicit selected ids가 비워지면 Grid 선택도 해제
|
||||
- active row와 selection을 rows 갱신 후 재동기화
|
||||
- multi-row는 checkbox/header checkbox를 명시
|
||||
- `selectRowByKey`, `clearSelection` API 제공
|
||||
|
||||
대량처리 화면에서 **보이는 선택과 Command payload 대상은 반드시 같은 Source of Truth**를 가져야 한다.
|
||||
|
||||
### P1 — Grid Error Scope
|
||||
|
||||
기존 `errors` 전체를 “셀 오류”로 취급하지 않고 다음 조건만 Grid Cell 오류로 본다.
|
||||
|
||||
- `rowKey` 존재
|
||||
- `field` 존재
|
||||
- 해당 field가 현재 Grid Column에 존재
|
||||
|
||||
따라서 Header Validation과 Detail Cell Validation이 섞여도 “N개 셀 오류”와 첫/다음 오류 Navigation이 실제 Grid 범위만 가리킨다.
|
||||
|
||||
### P1 — ERP-MST-ITEM-001 Dirty Master Transition
|
||||
|
||||
Master 선택과 신규 전환 전에 Dirty 여부를 검사한다.
|
||||
|
||||
- Dirty 없음 → 즉시 선택/신규
|
||||
- Dirty 있음 → pending transition 저장
|
||||
- Grid selection을 현재 item으로 복원
|
||||
- `계속 편집 / 변경 버리기 / 저장 후 이동` 선택 후 전환
|
||||
- 저장 실패 시 전환하지 않음
|
||||
|
||||
Master/Detail에서 가장 위험한 데이터 유실 패턴인 **“왼쪽 목록 클릭 한 번으로 오른쪽 미저장 편집값 소실”**을 제거한다.
|
||||
|
||||
### P1 — OMS-ORD-002 Fast Transaction Focus
|
||||
|
||||
F2 품목 Lookup 완료 후:
|
||||
|
||||
1. 원래 line/clientId와 field Context를 보존한다.
|
||||
2. 선택 item을 해당 line에 적용한다.
|
||||
3. Dialog 종료 후 `focusNextEditableCell()`로 다음 Cell에 Focus한다.
|
||||
|
||||
또한 `신규`/`주문복사`도 현재 Transaction이 Dirty면 3-way Guard를 거친다. Copy는 Order identity/version을 복사하지 않고 line clientId를 새로 만든다.
|
||||
|
||||
### P1 — KbxLookup Focus Contract
|
||||
|
||||
- Dialog 선택 → 다음 Control
|
||||
- Dialog 취소 → 원래 Trigger
|
||||
- 직접 Code Enter resolve 성공 → 다음 Control
|
||||
- Label은 `${업무명} 코드`, `${업무명} 선택된 이름`, `${업무명} 검색`으로 고유화
|
||||
|
||||
즉 Lookup 경로에 따라 Enter 후 Focus가 제각각 움직이지 않는다.
|
||||
|
||||
### P1 — KbxUnsavedChangesDialog Keyboard Safety
|
||||
|
||||
- Open 시 이전 Active Element 기억
|
||||
- 첫 focusable action으로 Focus 이동
|
||||
- Tab / Shift+Tab Focus Trap
|
||||
- Esc = 계속 편집
|
||||
- Saving 중 Esc/background close 차단
|
||||
- Close 후 원래 Focus 복원
|
||||
- `aria-describedby`로 미저장 변경 설명 연결
|
||||
|
||||
### P1 — Fake Command 제거
|
||||
|
||||
OMS 주문관리의 `보류`는 실제 backend operation/handler가 없는 상태였으므로 표시만 남겨두지 않고 제거했다.
|
||||
|
||||
**실무에서 빠진 버튼보다 더 위험한 것은 동작하는 것처럼 보이는 버튼이다.** 구현되지 않은 업무 Action을 UI 완성도로 계산하지 않는다.
|
||||
|
||||
### Accessibility — Side Navigation
|
||||
|
||||
- `주문관리 즐겨찾기 추가`처럼 대상 화면명을 accessible name에 포함
|
||||
- 즐겨찾기 목록의 현재 화면에도 `aria-current="page"` 적용
|
||||
|
||||
---
|
||||
|
||||
## 3. Home Navigation에 대한 v41 판단
|
||||
|
||||
v40에서 Home은 이미 다음 구조를 갖는다.
|
||||
|
||||
- 확인 필요
|
||||
- 바로 시작: 미저장/고정/열린 업무 우선
|
||||
- 즐겨찾기/최근/권장 중복 제거
|
||||
- 모듈별 업무
|
||||
- Ctrl+K 메뉴/화면 검색
|
||||
- 실패 작업/중요 알림/진행 작업에서 실제 Context 이동
|
||||
|
||||
v41에서는 Home에 카드나 KPI를 더 추가하지 않았다. 현재 병목은 Home 정보량 부족이 아니라 **Home에서 들어간 실제 Golden Screen이 동일한 Context/Selection/Focus 계약을 지키는가**였다.
|
||||
|
||||
따라서 Home 자체는 Side Navigation 접근성·현재상태 표현만 보강하고, 투자 우선순위를 실제 Screen parity로 옮겼다. 이것이 과유불급을 피하는 판단이다.
|
||||
|
||||
---
|
||||
|
||||
## 4. 보안·안정성 판단
|
||||
|
||||
### FE는 Trust Boundary가 아니다
|
||||
|
||||
다음 v41 수정은 오조작·데이터 유실·UI race를 방지하는 FE 안정성이다.
|
||||
|
||||
- Dirty Guard
|
||||
- controlled selection
|
||||
- stale detail response sequence guard
|
||||
- masked sensitive display
|
||||
- disabled/permission UX
|
||||
- client validation
|
||||
|
||||
하지만 다음은 Server가 최종 Enforcement 해야 한다.
|
||||
|
||||
- Permission
|
||||
- 상태전이
|
||||
- 재고/금액/Reference Integrity
|
||||
- Concurrency
|
||||
- Idempotency
|
||||
- Audit
|
||||
- DB Constraint
|
||||
- Outbox/Inbox
|
||||
|
||||
UI에서 Command가 숨거나 disabled된 것을 보안으로 간주하지 않는다.
|
||||
|
||||
### Async stale response
|
||||
|
||||
주문 A Drawer를 열고 곧 주문 B를 열거나 Drawer를 닫았을 때 A 요청이 늦게 완료되더라도 현재 Drawer 상태를 덮어쓰지 않도록 request sequence로 무효화한다.
|
||||
|
||||
이는 단순 UX polish가 아니라 운영 중 잘못된 주문 정보를 현재 주문 정보처럼 표시할 수 있는 race를 줄이는 방어선이다.
|
||||
|
||||
---
|
||||
|
||||
## 5. 잔여 결함 — 완료로 계산하지 않음
|
||||
|
||||
### P1 — Excel Standard 미완료
|
||||
|
||||
OMS 주문조회 `엑셀` Action은 현재 AG Grid CSV export에 연결되어 있다.
|
||||
|
||||
KBX 표준이 요구하는 다음 계약 전체와 같지 않다.
|
||||
|
||||
- 현재 조회결과 Excel 다운로드
|
||||
- 업로드 양식 다운로드
|
||||
- Excel 업로드
|
||||
- Excel 붙여넣기
|
||||
- 최근 업로드 결과
|
||||
|
||||
특히 현재 로드된 Row CSV와 **전체 검색조건 결과 Export**는 같은 개념이 아니다. 서버 기반 Export/Job 정책과 `KbxExcelMenu` 실제 통합이 필요하다.
|
||||
|
||||
### P1 — 실제 Vue Runtime E2E / Visual Regression
|
||||
|
||||
현재 산출물에는 `node_modules`가 없고 `pnpm` 실행파일도 없다. 따라서 v41의 실제 Vue/PrimeVue/AG Grid app runtime을 띄워 Playwright로 검증하지 못했다.
|
||||
|
||||
이번 증적은 다음 범위다.
|
||||
|
||||
- TypeScript/Vue script transpile syntax
|
||||
- static contract/parity validator
|
||||
- 전체 Foundation governance
|
||||
- design debt ratchet
|
||||
|
||||
이를 실제 runtime E2E를 통과한 것으로 표현하지 않는다.
|
||||
|
||||
### P2 — brittle legacy validator
|
||||
|
||||
v33 validator 일부는 의미가 아니라 `?'idle'` 같은 source 문자열 형태를 검사한다. 동작상 동일한 `? 'idle'` formatting도 실패한다.
|
||||
|
||||
이번에는 기존 재현성을 위해 source를 호환시켰다. 향후 AST/semantic validator로 교체하는 것이 맞지만 FE 기능 작업과 섞어 큰 리팩터링을 만들지 않았다.
|
||||
|
||||
---
|
||||
|
||||
## 6. 30년 현장 QA 관점의 판단
|
||||
|
||||
### 1. Reference Screen과 실제 제품 Screen은 별도로 합격시켜야 한다
|
||||
|
||||
Prototype이 정상이라고 Production Component가 정상인 것은 아니다. v41에서 발견한 no-op Link와 selection desync가 그 증거다.
|
||||
|
||||
### 2. Grid Selection은 데이터 정합성 UX다
|
||||
|
||||
Bulk Command에서 Checkbox는 장식이 아니다. 사용자가 선택했다고 보는 Row와 Server로 보낼 대상이 다르면 가장 위험한 유형의 업무 오류가 된다.
|
||||
|
||||
### 3. Master/Detail의 핵심 QA는 “다른 Row 클릭”이다
|
||||
|
||||
저장 버튼 Happy Path보다 먼저 확인해야 할 것은 편집 중 왼쪽 Master를 바꿨을 때 데이터가 사라지는지다.
|
||||
|
||||
### 4. Lookup의 완성도는 Popup 모양이 아니라 Focus 이후다
|
||||
|
||||
F2가 열리고 Enter로 선택되는 것까지는 절반이다. 선택 후 사용자가 Mouse를 다시 잡아야 한다면 ERP Keyboard Flow는 끊긴다.
|
||||
|
||||
### 5. 모르는 기능을 화면에 두지 않는다
|
||||
|
||||
Backend Handler가 없는 `보류`를 Command Bar에 두는 것은 “나중에 구현할 자리”가 아니라 운영 UI의 거짓 약속이다.
|
||||
|
||||
### 6. 다음에는 새 Component보다 실제 Runtime QA가 먼저다
|
||||
|
||||
v41 이후 우선순위는 다음이다.
|
||||
|
||||
1. 실제 Vue 앱 install/build/typecheck
|
||||
2. OMS-ORD-001 Playwright: F3 → selection → Drawer → bulk → reset selection
|
||||
3. OMS-ORD-002 keyboard-only: F2 → Enter → quantity → F8
|
||||
4. ERP-MST-ITEM-001 Dirty row switch 3-way Guard
|
||||
5. 1280×720 / 1440×900 / 200% zoom visual regression
|
||||
6. KbxExcelMenu + server-side full-query export
|
||||
7. Permission/Conflict/Integration failure 실제 API contract E2E
|
||||
|
||||
이 Gate 전에는 T10 추가, Runtime JSON UI Builder, 새로운 Grid abstraction을 우선하지 않는다.
|
||||
+276
@@ -0,0 +1,276 @@
|
||||
# KBX FE QA Hardening v42
|
||||
|
||||
## 1. 결론
|
||||
|
||||
v41까지의 가장 큰 성과는 Reference Lab과 실제 Vue Golden Screen을 별도 Gate로 보기 시작한 것이다. 그러나 v42에서 한 단계 더 내려가 실제 배포 가능한 FE 제품 관점으로 점검하자 더 근본적인 결함이 드러났다.
|
||||
|
||||
**`apps/web`에 독립적인 Vite application entry가 없었다.** Vue/TypeScript 파일이 수백 개 존재하고 공통 컴포넌트와 화면 정의가 정교해도 `package.json`, `index.html`, `main.ts`, Vite config가 없으면 FE 제품을 독립적으로 기동·빌드·검증할 수 없다.
|
||||
|
||||
이는 코드 양의 문제가 아니라 제품화 경계의 문제다. v42는 BE 결합을 더 강화하지 않고 **Frontend Runtime 자체를 일급 산출물**로 만든다.
|
||||
|
||||
또한 Home이 `/oms/orders?exceptionOnly=true`처럼 업무 Context를 전달해도 주문조회 composable이 route query를 소비하지 않아 실제 화면에서 Context가 사라지는 결함, Excel 버튼이 CSV export와 표준 Excel 업무를 혼동하는 문제, 200% Zoom에서 Shell chrome이 업무공간을 과도하게 점유하는 문제를 함께 보강했다.
|
||||
|
||||
---
|
||||
|
||||
## 2. P0 — 실제 Vue Web Runtime 부재
|
||||
|
||||
### 문제
|
||||
|
||||
v41까지 `apps/web/src`에는 실제 업무 화면과 shell 코드가 존재했지만 Web App을 독립 실행하는 최소 runtime contract가 완결되지 않았다.
|
||||
|
||||
이 상태에서는 다음이 불가능하거나 외부 조립에 의존한다.
|
||||
|
||||
- FE 단독 기동
|
||||
- FE 단독 typecheck/build
|
||||
- Vue Router/Pinia/TanStack Query/PrimeVue 초기화 검증
|
||||
- 실제 Golden Screen E2E 진입
|
||||
- BE 없이 Loading/Empty/Error/Lookup/Grid 상태 재현
|
||||
|
||||
### v42 조치
|
||||
|
||||
추가:
|
||||
|
||||
- `apps/web/package.json`
|
||||
- `apps/web/index.html`
|
||||
- `apps/web/tsconfig.json`
|
||||
- `apps/web/vite.config.ts`
|
||||
- `apps/web/src/main.ts`
|
||||
- `apps/web/src/App.vue`
|
||||
- `apps/web/src/styles/app.css`
|
||||
- `.env.demo`
|
||||
|
||||
Runtime은 실제 제품과 동일하게 다음을 초기화한다.
|
||||
|
||||
```text
|
||||
Vue
|
||||
→ Pinia
|
||||
→ Vue Router
|
||||
→ TanStack Query
|
||||
→ PrimeVue
|
||||
→ installKbx()
|
||||
→ KbxAppFrame
|
||||
```
|
||||
|
||||
Vite는 사용자 기술스택 기준인 **Vite 8**을 명시한다.
|
||||
|
||||
---
|
||||
|
||||
## 3. Demo Runtime은 별도 UI가 아니라 HTTP Adapter다
|
||||
|
||||
FE를 BE 없이 검증하기 위해 화면별 demo component를 만들면 다시 이중표준이 된다.
|
||||
|
||||
v42는 실제 다음 경계를 유지한다.
|
||||
|
||||
```text
|
||||
Golden Screen
|
||||
→ TanStack Query / Mutation
|
||||
→ domain API client
|
||||
→ kbxHttp(Axios)
|
||||
→ demo adapter 또는 real transport
|
||||
```
|
||||
|
||||
즉 Page, Lookup, Grid, Validation, Error Handler, Router는 운영 코드와 동일하다. Demo Mode에서 마지막 HTTP transport만 fixture adapter로 교체한다.
|
||||
|
||||
지원 fixture는 OMS 주문, Lookup, ERP 품목/재고, Queue, Reconcile, WMS Picking, Excel Import, Runtime Notification/Operation, AI/Suggestion 등 Golden Screen 검증에 필요한 업무 read/write를 포함한다.
|
||||
|
||||
알 수 없는 Demo API는 성공으로 위장하지 않고 `DEMO_OPERATION_UNSUPPORTED`로 명시적으로 실패한다.
|
||||
|
||||
---
|
||||
|
||||
## 4. 보안 — Demo Mode secure default
|
||||
|
||||
Demo Mode가 편리하다는 이유로 production/default runtime에서 fixture 또는 전체 권한이 켜지면 안 된다.
|
||||
|
||||
v42 정책:
|
||||
|
||||
```text
|
||||
normal dev/build
|
||||
→ VITE_KBX_DEMO_MODE != true
|
||||
→ real HTTP transport
|
||||
→ grantedPermissions = []
|
||||
→ secure-default preference scope
|
||||
|
||||
vite --mode demo
|
||||
→ VITE_KBX_DEMO_MODE = true
|
||||
→ demo HTTP adapter
|
||||
→ catalog demo permissions
|
||||
```
|
||||
|
||||
Demo 활성 조건은 문자열 `true`의 명시적인 일치로 제한한다.
|
||||
|
||||
**FE Permission은 여전히 UX 표현이다.** 실제 API Permission, 상태전이, Reference Integrity, Concurrency, Idempotency, Audit은 Server가 최종 Enforcement한다.
|
||||
|
||||
---
|
||||
|
||||
## 5. P0/P1 — Home은 링크 목록이 아니라 Context Router여야 한다
|
||||
|
||||
### 발견 결함
|
||||
|
||||
Home/Notification은 예외 주문으로 다음과 같은 경로를 전달할 수 있었다.
|
||||
|
||||
```text
|
||||
/oms/orders?exceptionOnly=true
|
||||
```
|
||||
|
||||
하지만 `useOrderSearch()`가 route query를 hydrate하지 않아 화면 진입 후 실제 검색조건에서는 `exceptionOnly`가 사라졌다.
|
||||
|
||||
사용자 입장에서는 “재고부족 7건”을 눌렀는데 주문조회로 이동한 뒤 다시 조건을 찾게 된다. 이는 Home navigation 완성도가 높다고 평가할 수 없다.
|
||||
|
||||
### v42 조치
|
||||
|
||||
`useOrderSearch()`가 route query를 다음 allow-list만 소비한다.
|
||||
|
||||
- `from`, `to`: ISO date 형식
|
||||
- `status`: 명시 상태 allow-list
|
||||
- `exceptionOnly`: 정확히 `true`
|
||||
- `keyword`: 최대 100자
|
||||
- `channelId`: 최대 64자
|
||||
|
||||
허용 Context가 존재하면 mount 시 자동 조회한다.
|
||||
|
||||
임의 query를 그대로 내부 filter/API로 전달하지 않는다. Home의 편의성과 입력 경계 검증을 함께 유지한다.
|
||||
|
||||
---
|
||||
|
||||
## 6. P1 — Excel은 “버튼 하나”가 아니다
|
||||
|
||||
v41의 `엑셀`은 사실상 AG Grid CSV export에 가까웠다. KBX 표준의 Excel 계약과 동일하지 않다.
|
||||
|
||||
v42는 `KbxCommandDefinition.menu = 'excel'`을 도입하여 `KbxCommandBar`가 공통 `KbxExcelMenu`를 렌더링한다.
|
||||
|
||||
표준 메뉴:
|
||||
|
||||
- 현재 조회결과 다운로드
|
||||
- 업로드 양식 다운로드
|
||||
- 엑셀 업로드
|
||||
- Excel 붙여넣기
|
||||
- 최근 업로드 결과
|
||||
|
||||
`KbxExcelMenu`는 다음 키보드 계약을 가진다.
|
||||
|
||||
- Esc: 닫기 + Trigger Focus 복원
|
||||
- ArrowUp/ArrowDown: 메뉴 이동
|
||||
- Home/End: 처음/마지막
|
||||
- outside pointer: 닫기
|
||||
- `role=menu/menuitem`
|
||||
|
||||
### 데이터 유실 방지 판단
|
||||
|
||||
현재 T01은 전체 검색결과가 Browser에 모두 로드된 경우에만 local CSV export를 허용한다.
|
||||
|
||||
서버 검색결과가 8,241건인데 현재 Browser row가 일부뿐인 경우 **부분 CSV를 “전체 조회결과”로 다운로드하지 못하게 차단**한다.
|
||||
|
||||
Full-query XLSX/Job Export API는 아직 연결되지 않았으므로 v42에서도 이를 완료로 계산하지 않는다. 이는 잔여 P1이다.
|
||||
|
||||
---
|
||||
|
||||
## 7. 200% Zoom / Narrow Desktop Shell
|
||||
|
||||
업무 Desktop 기준 1440px를 200% Zoom하면 CSS viewport는 약 720px처럼 압박된다.
|
||||
|
||||
기존 Global Header는 narrow media에서도 220px navigation column을 예약하여 실제 Workspace를 불필요하게 줄였다.
|
||||
|
||||
v42:
|
||||
|
||||
- 56rem 이하 Global Header의 첫 열을 fixed Side Nav width가 아닌 `auto`로 변경
|
||||
- Header search는 `minmax(0,1fr)`로 축소 가능
|
||||
- 40rem 이하 Header actions는 3-column row로 reflow
|
||||
- Side Nav가 expanded 상태여도 56rem 이하에서는 56px compact rail로 시각 수축
|
||||
- 상세 module/personal navigation은 숨기고 Header module selector / Ctrl+K를 핵심 이동 경로로 유지
|
||||
- WMS Mobile 정책은 기존 별도 Mobile Template 규칙을 유지
|
||||
|
||||
목표는 Desktop 화면을 Mobile 카드 UI로 바꾸는 것이 아니라 **업무 Workspace를 우선 보존**하는 것이다.
|
||||
|
||||
---
|
||||
|
||||
## 8. QA 자동화 — v42 Runtime Readiness Gate
|
||||
|
||||
신규 `scripts/validate-fe-runtime-v42.mjs`가 다음을 자동 확인한다.
|
||||
|
||||
- `apps/web` workspace/package 존재
|
||||
- Vite 8 명시
|
||||
- build 전에 `vue-tsc --noEmit`
|
||||
- main entry의 Pinia / Query / Router / PrimeVue / KBX wiring
|
||||
- Demo Mode explicit activation
|
||||
- production permission secure default
|
||||
- demo adapter가 HTTP boundary 아래에만 존재
|
||||
- unknown demo operation fail-explicit
|
||||
- WMS fixture state transition
|
||||
- Home → Order route context allow-list / 길이 제한 / auto-search
|
||||
- CommandBar → standard Excel menu
|
||||
- Excel menu keyboard/focus contract
|
||||
- duplicate Grid export 진입점 제거
|
||||
- 56rem/40rem Shell pressure 대응
|
||||
|
||||
이 Gate는 실제 Browser E2E를 대체하지 않는다. **런타임을 실행할 수 있도록 구성되어 있는지와 보안/구조 계약이 소스에서 유지되는지**를 검증한다.
|
||||
|
||||
---
|
||||
|
||||
## 9. 실행환경 제한 — 완료로 과장하지 않는 항목
|
||||
|
||||
현재 작업 컨테이너에서는 `pnpm` 실행파일이 없고 Corepack을 통한 pnpm 준비도 npm registry network 정책에 의해 실패했다.
|
||||
|
||||
따라서 이번 환경에서 다음을 실제 실행했다고 주장하지 않는다.
|
||||
|
||||
- `pnpm install`
|
||||
- `pnpm --filter @kbx/web build`
|
||||
- 실제 Vite server
|
||||
- 실제 Vue/PrimeVue/AG Grid Browser Playwright
|
||||
- 200% Zoom actual screenshot regression
|
||||
|
||||
대신 다음은 실제 실행하여 PASS했다.
|
||||
|
||||
- 304 TS/Vue syntax transpile
|
||||
- API governance
|
||||
- v42 FE runtime readiness Gate
|
||||
- 전체 `validate:kbx`
|
||||
|
||||
Network-enabled CI/dev 환경의 다음 필수 Gate는:
|
||||
|
||||
```bash
|
||||
pnpm install --frozen-lockfile
|
||||
pnpm typecheck:web
|
||||
pnpm build:web:demo
|
||||
pnpm dev:web:demo
|
||||
# Playwright actual runtime E2E
|
||||
```
|
||||
|
||||
이다.
|
||||
|
||||
---
|
||||
|
||||
## 10. 30년 현장 QA 관점의 우선순위
|
||||
|
||||
### 1. 화면 개수보다 “기동 가능한 제품”이 먼저다
|
||||
|
||||
300개 Vue/TS unit이 있어도 Application Entry가 없다면 FE 제품 완성도를 높게 평가하면 안 된다.
|
||||
|
||||
### 2. Home 카드보다 Context 전달이 중요하다
|
||||
|
||||
“오류 16”을 보여주는 것보다 그 숫자를 눌렀을 때 오류 16건이 그대로 조회되는 것이 중요하다.
|
||||
|
||||
### 3. Demo는 운영 코드와 달라지면 QA 가치가 급격히 떨어진다
|
||||
|
||||
별도 Mock Page가 아니라 동일 API boundary 아래의 adapter로 검증해야 한다.
|
||||
|
||||
### 4. Excel은 가장 쉽게 기능 착시가 생기는 영역이다
|
||||
|
||||
CSV 한 번 내려간다고 Excel 표준이 완료된 것이 아니다. 전체 조회결과, Import, Mapping, Validation, Job, Result가 하나의 제품 계약이다.
|
||||
|
||||
### 5. 200% Zoom은 접근성 QA이면서 업무 생산성 QA다
|
||||
|
||||
Side Nav와 Header가 화면의 절반을 차지하면 접근성 준수 여부 이전에 실제 업무가 불가능해진다.
|
||||
|
||||
### 6. 다음 iteration도 새 Template을 만들 시점이 아니다
|
||||
|
||||
v43 우선순위:
|
||||
|
||||
1. network-enabled actual `pnpm install/build/typecheck`
|
||||
2. 실제 Vue OMS-ORD-001 browser E2E
|
||||
3. OMS-ORD-002 keyboard-only E2E
|
||||
4. ERP-MST-ITEM-001 Dirty transition E2E
|
||||
5. 실제 200% Zoom / 1280×720 visual regression
|
||||
6. server-side full-query XLSX export + background job
|
||||
7. Permission / Conflict / Integration error 실제 API contract E2E
|
||||
|
||||
그 전에는 T10 추가, Runtime JSON UI Builder, 새로운 Grid abstraction을 우선하지 않는다.
|
||||
+248
@@ -0,0 +1,248 @@
|
||||
# KBX FE QA Hardening v43
|
||||
## Semantic Theme · Home Navigation · Visual Hierarchy · FE Reference Parity
|
||||
|
||||
### 1. 이번 버전의 결론
|
||||
|
||||
v43의 핵심은 색상 변경이 아니다. KBX의 기존 `surface / text / border / primary / success / warning / danger / focus / AI` 의미 토큰을 기반으로 **KBX Business Light / KBX Business Dark** 두 표현 모드를 만들고, 같은 의미 계약을 Application Shell, Home, PrimeVue, AG Grid, 공통 Component, FE Reference Lab까지 공유하도록 정리한 것이다.
|
||||
|
||||
첨부 `KBX Business UX/AX Standard v1.0`, `KBX Design System v1.0`, `KBX Reference Screens v1.0`, `KBX Implementation Contract v1.0`은 Light/Dark 모드 자체를 명시적으로 요구하지 않는다. 따라서 Dark Theme는 표준 문서의 직접 요구사항이라고 과장하지 않는다. 이번 Theme는 사용자의 요청에 따라 **기존 Semantic Token, Density, Accessibility, Predictable Layout 계약을 확장한 구현 선택**이다.
|
||||
|
||||
Reference Screen의 디자인 리뷰 순서가 업무 단계·입력량·Focus·예외·Grid·상태·복구·감사·AI를 먼저 보고 미관을 마지막에 보도록 정의한 만큼, Theme 역시 업무 판단을 흐리면 실패로 판단했다.
|
||||
|
||||
---
|
||||
|
||||
## 2. 30년 현장 QA 관점의 냉정한 v42 평가
|
||||
|
||||
v42에는 Token이 있고 UI가 정돈되어 있었지만, 이를 완성된 Theme System이라고 부르기에는 부족했다.
|
||||
|
||||
### P1 — 세 개의 서로 다른 시각 경계
|
||||
|
||||
실제 구현은 사실상 다음 세 영역으로 나뉘어 있었다.
|
||||
|
||||
1. KBX CSS semantic tokens
|
||||
2. PrimeVue Aura 기본 Theme
|
||||
3. AG Grid Quartz 개별 파라미터
|
||||
4. 별도의 FE Reference Lab CSS
|
||||
|
||||
같은 `primary`, `surface`, `warning`, `focus` 의미가 제품 영역에 따라 미묘하게 달라질 수 있었다. 이 상태에서 Dark Mode를 추가하면 화면마다 다른 Dark가 만들어질 가능성이 높다.
|
||||
|
||||
### P1 — Home의 의미 우선순위가 색상으로 충분히 보강되지 않음
|
||||
|
||||
Home의 정보 구조 자체는 `확인 필요 → 바로 시작 → 모듈별 업무`로 올바른 방향이었지만, 긴급 예외·미저장·고정 업무·일반 메뉴가 시각적으로 비슷해 스캔 속도가 떨어질 수 있었다.
|
||||
|
||||
### P1 — 하드코딩 색상 잔존
|
||||
|
||||
Network Indicator, Dialog/Toast Shadow, FE Reference surface 등에 일부 직접 색상이 남아 Theme 전환 시 의미 토큰과 어긋날 여지가 있었다.
|
||||
|
||||
### P1 — Dark Mode의 대표적인 함정 재현
|
||||
|
||||
첫 Dark 시각 QA에서 실제로 결함이 발견됐다. `.kbx-app[data-theme="dark"]`의 배경 변수는 Dark로 바뀌었지만 상위 `body`에서 이미 계산된 Light text color가 일부 자식에게 상속되어 **배경은 어둡고 글자는 검은 가짜 Dark Mode**가 만들어졌다.
|
||||
|
||||
이 문제는 정적 코드 리뷰만으로는 지나치기 쉽다. 실제 렌더링을 통해 발견했고 Theme Root에서 `color/background`를 재계산하도록 수정했다.
|
||||
|
||||
---
|
||||
|
||||
## 3. v43 구현
|
||||
|
||||
### 3.1 KBX Business Theme 계약
|
||||
|
||||
Token source version을 `1.9.0`으로 올리고 다음 Theme mode를 명시했다.
|
||||
|
||||
- `KBX Business Light`
|
||||
- `KBX Business Dark`
|
||||
|
||||
Theme는 primitive color를 화면마다 다시 정의하지 않는다. Semantic Token override만 가진다.
|
||||
|
||||
주요 Dark semantic 예:
|
||||
|
||||
- surface / muted / subtle / hover
|
||||
- text / text-muted
|
||||
- border / border-strong
|
||||
- primary
|
||||
- success / warning / danger
|
||||
- focus
|
||||
- info/success/warning/danger surface + border
|
||||
- changed / AI suggested
|
||||
- overlay / overlay shadow
|
||||
|
||||
Density는 Theme와 독립적으로 유지한다. `compact / comfortable / touch`의 업무 밀도 계약을 Theme가 바꾸지 않는다.
|
||||
|
||||
### 3.2 Theme Runtime API
|
||||
|
||||
`applyKbxTheme()`, `normalizeKbxThemeMode()`, `nextKbxThemeMode()`를 공통 Theme 경계로 추가했다.
|
||||
|
||||
Theme preference는 `light | dark` allow-list만 허용한다. 알 수 없는 값은 `light`로 fail-safe 한다.
|
||||
|
||||
중요한 보안 원칙:
|
||||
|
||||
> Theme는 presentation preference일 뿐 Permission, Domain State, Business Rule, AI Capability를 결정하지 않는다.
|
||||
|
||||
### 3.3 Application Shell
|
||||
|
||||
Global Header에 Icon-only가 아닌 명시적 Theme Action을 추가했다.
|
||||
|
||||
- `☀ Light`
|
||||
- `☾ Dark`
|
||||
- 스크린리더용 `밝은 테마로 변경 / 어두운 테마로 변경`
|
||||
- 40rem 이하에서 기존 Header action과 함께 4-column으로 재배치
|
||||
|
||||
Preference는 기존 사용자 UI preference 저장 경계를 재사용한다.
|
||||
|
||||
### 3.4 PrimeVue / AG Grid Theme Bridge
|
||||
|
||||
PrimeVue Dark selector를 KBX root의 `[data-kbx-theme="dark"]`와 연결했다.
|
||||
|
||||
AG Grid Quartz는 다음 KBX semantic variable을 참조한다.
|
||||
|
||||
- foreground → text
|
||||
- background → surface
|
||||
- accent → primary
|
||||
- border → border
|
||||
- header → surface-subtle
|
||||
- row hover → surface-hover
|
||||
- selected row → info-surface
|
||||
|
||||
따라서 업무 Page가 PrimeVue/AG Grid Theme API를 각각 직접 조정하지 않아도 된다.
|
||||
|
||||
### 3.5 Home Navigation Visual Hierarchy
|
||||
|
||||
Home에 새로운 Dashboard Card를 더 넣지 않았다.
|
||||
|
||||
대신 현재 구조에서 업무 우선순위만 강화했다.
|
||||
|
||||
- `확인 필요`: warning border
|
||||
- 긴급/치명 예외: danger left marker
|
||||
- Dirty work: warning semantic surface
|
||||
- Pinned/Favorite: info semantic surface
|
||||
- Module header: subtle surface + primary code
|
||||
- active module filter: info surface
|
||||
|
||||
이 설계의 목적은 “예쁘게 보이기”가 아니라 사용자가 Home에 들어온 뒤 **어디부터 처리해야 하는지를 1~2초 안에 스캔**하도록 하는 것이다.
|
||||
|
||||
### 3.6 Side Navigation
|
||||
|
||||
현재 업무 Screen에는 primary inset marker를 추가했다. Background 전체를 강하게 칠하지 않고 현재 위치를 안정적으로 인식하도록 했다.
|
||||
|
||||
### 3.7 Shared Component hard-code 감소
|
||||
|
||||
- Network Indicator text/danger → semantic token
|
||||
- Unsaved Changes Dialog shadow → semantic shadow token
|
||||
- Toast shadow → semantic shadow token
|
||||
- Reference HTML fixed white surfaces 제거
|
||||
|
||||
Design Debt는 `122 → 116`으로 감소했다.
|
||||
|
||||
### 3.8 FE Reference Lab Theme parity
|
||||
|
||||
BE 없이 실행되는 실제 HTML/CSS/JavaScript Reference Lab에도 같은 Light/Dark 의미 체계를 적용했다.
|
||||
|
||||
Theme toggle은:
|
||||
|
||||
- local preference allow-list
|
||||
- aria-label 갱신
|
||||
- `documentElement.colorScheme` 갱신
|
||||
- Light/Dark 즉시 전환
|
||||
- `?theme=light|dark` 결정적 preview 지원
|
||||
|
||||
T01~T09의 업무 상호작용은 Theme에 의해 바뀌지 않는다.
|
||||
|
||||
---
|
||||
|
||||
## 4. 보안 판단
|
||||
|
||||
Theme 기능에서 가장 중요한 보안 원칙은 **표현 상태가 권한 상태로 승격되지 않는 것**이다.
|
||||
|
||||
v43에서는 다음을 금지한다.
|
||||
|
||||
- Dark/Light preference에 따라 Permission 변경
|
||||
- Theme 값으로 API route 또는 Domain Action 결정
|
||||
- 저장된 임의 Theme 문자열을 class/style로 그대로 주입
|
||||
- 업무 오류/상태를 색만으로 표현
|
||||
|
||||
Theme 값은 `light | dark` allow-list 후 dataset과 color-scheme에만 적용한다.
|
||||
|
||||
Frontend의 Disabled/Hidden/Theme 표시는 UX일 뿐 보안 경계가 아니다. Permission Enforcement와 상태전이/정합성은 Server가 최종 책임져야 한다.
|
||||
|
||||
---
|
||||
|
||||
## 5. 브라우저 QA
|
||||
|
||||
실제 FE Reference HTML/CSS/JavaScript asset을 Chromium에 inline하여 검증했다. 실행환경 정책상 localhost/file navigation이 차단되므로 production CSP가 제거된 inline harness를 사용했으며, CSP 자체는 별도 정적 Gate에서 검증한다.
|
||||
|
||||
Theme Browser QA: **16 / 16 PASS**
|
||||
|
||||
검증 항목:
|
||||
|
||||
- Light 기본값
|
||||
- Dark Toggle
|
||||
- Dark text 실제 computed-color 재계산
|
||||
- Theme button aria-label
|
||||
- T01~T08 Dark render
|
||||
- T09 WMS mobile 전용 render + mode
|
||||
- 1280px document horizontal overflow 없음
|
||||
- Light 복귀
|
||||
- JavaScript runtime/page error 0
|
||||
|
||||
시각 증적:
|
||||
|
||||
- Home Light 1440×900
|
||||
- Home Dark 1440×900
|
||||
- T01 Dark 1440×900
|
||||
|
||||
---
|
||||
|
||||
## 6. 전체 Governance 결과
|
||||
|
||||
최종 `validate:kbx` PASS.
|
||||
|
||||
- Design Tokens: 153
|
||||
- Screen Recipes: 9
|
||||
- Recipe Verification: 9/9
|
||||
- Screen Definitions: 20
|
||||
- Components: 84
|
||||
- Catalog Entries: 66
|
||||
- Navigation Entries: 16
|
||||
- Canonical Scenarios: 30
|
||||
- Canonical Fields: 51
|
||||
- API Operations: 66
|
||||
- TS/Vue Script Units: 305
|
||||
- Design Debt: 116 <= ratchet 131
|
||||
- Release Impact: required major / declared major
|
||||
|
||||
Theme 전용 자동 Gate도 전체 validator에 포함했다.
|
||||
|
||||
---
|
||||
|
||||
## 7. 아직 완료라고 부르지 않는 부분
|
||||
|
||||
### P1 — 실제 Vite/PrimeVue/AG Grid Live Theme E2E
|
||||
|
||||
v42에서 만든 실제 Vite Runtime은 소스 계약상 준비되어 있으나, 현재 실행환경은 package registry 접근이 차단되어 `pnpm install`과 실제 Vite runtime 기동을 수행할 수 없다.
|
||||
|
||||
따라서 이번 16/16 브라우저 증적은 **FE Reference Lab의 실제 HTML/CSS/JS 증적**이며, Vue/PrimeVue/AG Grid live-runtime Theme E2E로 대체해서 주장하지 않는다.
|
||||
|
||||
### P1 — Full-query XLSX
|
||||
|
||||
v42에서 남긴 Server-side 전체 검색조건 XLSX / Hangfire export는 Theme와 별개이며 여전히 후속 과제다.
|
||||
|
||||
### P2 — Forced Colors / OS High Contrast
|
||||
|
||||
Light/Dark와 별개로 Windows Forced Colors 및 실제 Screen Reader 환경 검증은 추가 현장 QA가 필요하다.
|
||||
|
||||
### P2 — 실제 PDA 장비 Dark Theme
|
||||
|
||||
T09은 390×844 브라우저 Reference 검증을 통과했지만 Scanner/PDA의 저휘도·창고 조명·장갑 조건에서 Dark Theme가 실제로 우월하다고 단정할 근거는 아직 없다. 현장 사용성 검증 후 WMS 기본 Theme를 결정해야 한다.
|
||||
|
||||
---
|
||||
|
||||
## 8. 다음 우선순위
|
||||
|
||||
1. Network-enabled 환경에서 실제 Vite install/typecheck/build
|
||||
2. Vue App Light/Dark Playwright Visual Regression
|
||||
3. OMS-ORD-001 / 002 / ERP-MST-ITEM-001 Theme parity
|
||||
4. 1280×720 / 1440×900 / 200% Zoom × Light/Dark matrix
|
||||
5. WMS 390×844 실장비 contrast/scan feedback
|
||||
6. Forced Colors / keyboard-only / screen-reader QA
|
||||
7. Server-side full-query XLSX
|
||||
|
||||
새로운 T10/T11이나 더 큰 Metadata Theme Engine은 필요하지 않다. 현재는 **하나의 의미 토큰 계약이 기존 T01~T09 전체에서 실제로 유지되는지**를 더 깊게 검증하는 것이 우선이다.
|
||||
+340
@@ -0,0 +1,340 @@
|
||||
# KBX FE QA Hardening v44
|
||||
## Theme State · Runtime Center · Empty Recovery · Forced Colors · Home/Shell Parity
|
||||
|
||||
## 1. 결론
|
||||
|
||||
v44의 목적은 Light/Dark 팔레트를 하나 더 다듬는 것이 아니다. v43에서 정리한 Semantic Theme가 실제 업무 상태와 복구 흐름을 방해하지 않는지 검증하고, **Home/Application Shell → 시스템 작업/알림 → T01~T09 → Empty/Error/Permission/Conflict/Network**의 표현 계약을 같은 FE 언어로 유지하는 것이 목적이다.
|
||||
|
||||
이번 버전에서 새 화면 유형이나 대형 Theme Engine은 만들지 않았다. 기존 T01~T09와 KBX Component 계약을 실제 HTML/CSS/JavaScript와 Vue 런타임 코드에 더 정확하게 연결했다.
|
||||
|
||||
첨부 표준의 핵심 근거는 다음이다.
|
||||
|
||||
- Business UX/AX: Familiar First, Keyboard Accelerated/Mouse Complete, Grid First, Exception Driven, Predictable Layout, Explicit State, Audit by Default.
|
||||
- Design System: Semantic Color, Density, Focus, Empty/Loading/Error, KbxDataGrid/KbxTemplate 계층.
|
||||
- Reference Screens: 1440×900 Desktop Shell, WMS 390×844 별도 Mobile Template, Empty/Error 복구, Home/Queue의 actionable navigation.
|
||||
- Implementation Contract: KBX는 사용방법을 결정하고 Domain은 가능한 업무를 결정한다. Frontend Permission/Validation은 UX 방어선이며 Server Validation/Concurrency/DB Constraint/Idempotency/Audit가 최종 책임을 가진다.
|
||||
|
||||
Light/Dark 자체는 첨부 표준의 직접 요구사항이 아니다. v43/v44 Theme는 사용자 요구에 따라 기존 Semantic Token·Accessibility·Predictable Layout 계약을 확장한 구현 선택이다.
|
||||
|
||||
---
|
||||
|
||||
## 2. 30년 현장 QA 관점에서 본 v43 잔여 결함
|
||||
|
||||
### P1 — Application version traceability가 실제 코드와 달랐다
|
||||
|
||||
`apps/web/src/App.vue`가 `v42-fe-runtime` 문자열을 계속 사용하고 있었다. Theme/Navigation이 v43까지 진화했는데 운영 제안·문제재현 Context의 AppVersion이 과거 버전을 가리키면 화면 캡처와 이력이 맞지 않는다.
|
||||
|
||||
v44:
|
||||
|
||||
```text
|
||||
App artifact version
|
||||
v42-fe-runtime
|
||||
→ v44-fe-theme-state
|
||||
```
|
||||
|
||||
Release도 Foundation 44 / Contract 1.25.0으로 맞췄다.
|
||||
|
||||
### P1 — 작업/알림 Center가 Shell의 접근성 계약을 충분히 갖지 못했다
|
||||
|
||||
실제 Vue Runtime의 작업/알림 Panel은 비모달 방식이 타당했지만 다음이 약했다.
|
||||
|
||||
- Header Trigger의 open state를 Screen Reader가 알기 어려움
|
||||
- Panel을 키보드로 즉시 닫는 명시적 계약 부족
|
||||
- 닫은 뒤 Trigger Focus Restore 부족
|
||||
- Ctrl+K 외 Header/Home 메뉴검색 버튼 경로에서 Panel과 Menu Search가 겹칠 가능성
|
||||
|
||||
v44는 System Center를 **비모달 Runtime Panel**로 고정했다.
|
||||
|
||||
```text
|
||||
작업 / 알림 Trigger
|
||||
→ aria-controls
|
||||
→ aria-expanded
|
||||
→ 비모달 Panel focus
|
||||
→ Esc / 닫기
|
||||
→ Trigger focus restore
|
||||
```
|
||||
|
||||
Menu Search는 Ctrl+K, Header Trigger, Home Trigger 모두 같은 `openMenuSearch()` 경로를 사용하며 Runtime Panel을 먼저 정리한다.
|
||||
|
||||
### P1 — Reference HTML과 실제 Vue가 System Center에서 다른 문법을 사용했다
|
||||
|
||||
v43 Reference Lab에서는 작업/알림을 공통 Modal Drawer로 열었지만 실제 Vue는 비모달 Panel이었다.
|
||||
|
||||
이중표준을 제거했다.
|
||||
|
||||
```text
|
||||
Help / AI / Suggestion / 상세조회
|
||||
→ Drawer / Modal 성격
|
||||
|
||||
작업 Center / 알림 Center
|
||||
→ 비모달 System Panel
|
||||
```
|
||||
|
||||
작업 진행을 보면서 현재 화면을 유지할 수 있어야 하기 때문이다.
|
||||
|
||||
### P1 — T01 Empty가 상태는 표시하지만 복구 루프가 한 단계 부족했다
|
||||
|
||||
조회 결과가 0건일 때 사용자가 다시 Search Panel을 찾아 조건을 지워야 했다. 업무형 Empty는 장식용 일러스트가 아니라 즉시 복구 Action이 중요하다.
|
||||
|
||||
v44:
|
||||
|
||||
```text
|
||||
KbxTemplateStateBoundary
|
||||
→ emptyActionLabel / emptyAction
|
||||
|
||||
KbxListPage
|
||||
→ Template contract로 전달
|
||||
|
||||
OMS-ORD-001
|
||||
→ [조회조건 초기화]
|
||||
→ 기본 조건 복원
|
||||
→ 즉시 재조회
|
||||
```
|
||||
|
||||
즉 `empty → manual filter editing`이 아니라 `empty → one action → ready`의 루프로 닫았다.
|
||||
|
||||
### P2 — Light/Dark만으로는 Windows 고대비 사용자를 보장할 수 없었다
|
||||
|
||||
Dark Theme를 잘 만드는 것과 OS Forced Colors를 지원하는 것은 다른 문제다. 사용자가 Windows High Contrast/Forced Colors를 사용하면 브라우저가 제품 색을 강제로 재매핑한다.
|
||||
|
||||
v44는 Forced Colors를 제3의 KBX Theme로 만들지 않았다.
|
||||
|
||||
```text
|
||||
KBX Light / Dark
|
||||
↓
|
||||
OS forced-colors active
|
||||
↓
|
||||
Canvas / CanvasText / Highlight / LinkText 우선
|
||||
```
|
||||
|
||||
이 방식이 안정적이다. 업무 상태를 임의 RGB로 다시 싸우지 않고 OS 접근성 정책을 존중한다.
|
||||
|
||||
---
|
||||
|
||||
## 3. Theme 적용 원칙
|
||||
|
||||
v44 Theme 판단 기준은 다음과 같다.
|
||||
|
||||
```text
|
||||
색이 예쁜가? 후순위
|
||||
업무 우선순위가 보이는가? 우선
|
||||
상태를 색 없이도 아는가? 필수
|
||||
Focus가 보이는가? 필수
|
||||
Grid 데이터가 읽히는가? 필수
|
||||
복구 Action이 보이는가? 필수
|
||||
```
|
||||
|
||||
Light/Dark에서도 Control Height, Grid Row, Command Bar, Search Panel, WMS Touch Target은 변하지 않는다. Theme 전환 때문에 사용자가 학습한 업무 위치와 밀도가 달라지면 실패다.
|
||||
|
||||
Forced Colors에서도 `aria-current`, `aria-selected`, `aria-pressed`에 Highlight outline을 제공하여 현재 위치와 선택을 색상 배경만으로 의존하지 않게 했다.
|
||||
|
||||
---
|
||||
|
||||
## 4. 실제 코드 보강
|
||||
|
||||
### Application Shell / Home
|
||||
|
||||
- `KbxGlobalHeader`
|
||||
- operation/notification open state props
|
||||
- `aria-controls`
|
||||
- `aria-expanded`
|
||||
- `KbxApplicationShell`
|
||||
- Header open-state pass-through
|
||||
- `KbxAppFrame`
|
||||
- non-modal Runtime Panel
|
||||
- `role="region"`
|
||||
- explicit close button
|
||||
- Esc close
|
||||
- focus restore
|
||||
- route change 시 안전하게 close
|
||||
- Help/AI/Suggestion과 중첩 방지
|
||||
- Ctrl+K/Header/Home menu search 경로 통합
|
||||
- App version을 v44 FE artifact와 일치
|
||||
|
||||
### Template State
|
||||
|
||||
- `KbxTemplateStateBoundary 1.2.0`
|
||||
- `emptyActionLabel`
|
||||
- `emptyAction`
|
||||
- `KbxListPage 1.7.0`
|
||||
- Empty Recovery contract 노출
|
||||
- `KbxDataState 1.1.1`
|
||||
- Idle/Loading/Error를 semantic surface로 표현
|
||||
- `OMS-ORD-001`
|
||||
- `[조회조건 초기화] → resetAndSearch()`
|
||||
|
||||
### Theme / Accessibility
|
||||
|
||||
- generated KBX token CSS에 `@media (forced-colors: active)` 추가
|
||||
- Light/Dark 의미 토큰을 OS System Color로 안전하게 override
|
||||
- Focus `Highlight`
|
||||
- surface `Canvas`
|
||||
- text `CanvasText`
|
||||
- primary `LinkText`
|
||||
- 앱 CSS와 Reference CSS 모두 current/selected/pressed 상태의 고대비 outline 적용
|
||||
|
||||
### Component versioning / Catalog
|
||||
|
||||
공개 계약 변경을 버전 없이 숨기지 않았다.
|
||||
|
||||
- `KbxGlobalHeader 1.4.0`
|
||||
- `KbxApplicationShell 1.6.0`
|
||||
- `KbxListPage 1.7.0`
|
||||
- `KbxTemplateStateBoundary 1.2.0`
|
||||
- `KbxDataState 1.1.1`
|
||||
|
||||
Catalog에도 다음 재현 상태를 보강했다.
|
||||
|
||||
- ListPage Empty Recovery
|
||||
- ApplicationShell Runtime Center
|
||||
- DataState Forced Colors
|
||||
- TemplateStateBoundary Empty Recovery
|
||||
|
||||
---
|
||||
|
||||
## 5. 보안 판단
|
||||
|
||||
v44 변경은 FE 보안과 Domain 보안을 섞지 않는다.
|
||||
|
||||
Frontend 책임:
|
||||
|
||||
- Panel/Drawer 중첩 및 Focus 오류 방지
|
||||
- route 변경 시 stale UI 정리
|
||||
- Theme preference 표현 상태만 보존
|
||||
- Empty/Error의 명시적 복구 UX
|
||||
- Forced Colors 접근성
|
||||
- 메뉴검색과 Runtime Panel 상태 충돌 방지
|
||||
|
||||
Server 최종 책임:
|
||||
|
||||
- Permission enforcement
|
||||
- Entity existence
|
||||
- Business Rule
|
||||
- Optimistic Concurrency
|
||||
- DB Constraint
|
||||
- Import staging/commit
|
||||
- Idempotency
|
||||
- Audit / Outbox / Inbox
|
||||
- AI proposal revalidation
|
||||
|
||||
Theme 값, `aria-expanded`, disabled 상태가 보안 판단의 Source of Truth가 되어서는 안 된다.
|
||||
|
||||
---
|
||||
|
||||
## 6. Browser QA
|
||||
|
||||
### Feature Browser QA
|
||||
|
||||
정확한 FE Reference HTML/CSS/JavaScript asset을 Chromium `set_content` harness로 실행했다.
|
||||
|
||||
결과:
|
||||
|
||||
```text
|
||||
14 / 14 PASS
|
||||
```
|
||||
|
||||
검증:
|
||||
|
||||
- Home Light render
|
||||
- 작업 Center open
|
||||
- `aria-expanded=true`
|
||||
- Panel focus
|
||||
- 비모달 유지(shell inert 아님)
|
||||
- Esc close
|
||||
- Trigger focus restore
|
||||
- Dark 전환
|
||||
- T01 Dark
|
||||
- Forced Colors active
|
||||
- system semantic color override
|
||||
- runtime error 0
|
||||
|
||||
### T01~T09 Matrix
|
||||
|
||||
```text
|
||||
28 / 28 PASS
|
||||
```
|
||||
|
||||
검증:
|
||||
|
||||
- T01~T09 Light
|
||||
- T01~T09 Dark
|
||||
- T09 `wms-mobile` 전용 mode
|
||||
- T01 Empty 표시
|
||||
- Empty → 조회조건 초기화 → 8 rows 복구
|
||||
- Runtime center open/focus/Esc/focus restore
|
||||
- 1280×720 document horizontal overflow = 0
|
||||
- Forced Colors active
|
||||
- Forced Colors T01 Grid visible
|
||||
- runtime error = 0
|
||||
|
||||
T09는 Desktop `[data-template]` 존재 여부를 검사하지 않았다. 실제 표준대로 `#app[data-mode="wms-mobile"]`인지 검증했다. 테스트가 구현을 잘못 가정하면 테스트도 결함이다.
|
||||
|
||||
---
|
||||
|
||||
## 7. 전체 Foundation Gate
|
||||
|
||||
최종 `node scripts/validate-kbx.mjs` 결과 전체 PASS.
|
||||
|
||||
```text
|
||||
Design Tokens 153
|
||||
Screen Recipes 9
|
||||
Recipe Verification 9/9
|
||||
Screen Definitions 20
|
||||
Component Definitions 84
|
||||
Component Catalog Entries 66
|
||||
Navigation Entries 16
|
||||
Canonical Scenarios 30
|
||||
Canonical Fields 51
|
||||
API Operations 66
|
||||
TS/Vue Script Units 305
|
||||
Design Debt 116 / ratchet 131
|
||||
```
|
||||
|
||||
Release Governance:
|
||||
|
||||
```text
|
||||
Foundation iteration 44
|
||||
Contract 1.25.0
|
||||
Required major
|
||||
Declared major
|
||||
PASS
|
||||
```
|
||||
|
||||
Design Debt를 Theme/State 기능 추가 대가로 올리지 않았다.
|
||||
|
||||
---
|
||||
|
||||
## 8. 증적 한계
|
||||
|
||||
이 실행환경에는 `pnpm` 실행파일과 `node_modules`가 없으며 package registry를 사용할 수 없다.
|
||||
|
||||
따라서 다음을 실행했다고 주장하지 않는다.
|
||||
|
||||
- 실제 Vite dev server
|
||||
- 실제 Vue/PrimeVue/AG Grid live runtime
|
||||
- `vue-tsc` full semantic typecheck
|
||||
- 실제 runtime Playwright
|
||||
- 실제 Screen Reader 자동화
|
||||
|
||||
이번 v44의 실제 Vue 코드는 syntax/static/governance gate로 검증했고, Browser QA는 동일한 Reference HTML/CSS/JavaScript asset을 Chromium에서 실행했다.
|
||||
|
||||
이 구분을 지키는 것이 QA 증적의 신뢰성에 중요하다.
|
||||
|
||||
---
|
||||
|
||||
## 9. 다음 우선순위
|
||||
|
||||
다음 버전에서 T10/T11, 새로운 Theme Engine, Runtime JSON Builder를 추가하는 것은 우선순위가 아니다.
|
||||
|
||||
순서는 다음이 적절하다.
|
||||
|
||||
1. network-enabled CI에서 `pnpm install → typecheck → Vite build`
|
||||
2. 실제 Vue Golden Screen Light/Dark Playwright
|
||||
3. `1440×900 / 1280×720 / 200% Zoom × Light/Dark`
|
||||
4. Windows Forced Colors 실제 Vue Visual Regression
|
||||
5. keyboard-only `OMS-ORD-002` F2→Enter→Grid→F8
|
||||
6. T02/T03 Conflict/Permission/Business Error 실제 API E2E
|
||||
7. Server-side full-query XLSX + Hangfire Job
|
||||
8. 실제 WMS PDA/Scanner에서 조도·장갑·네트워크 불안정 현장 QA
|
||||
|
||||
핵심은 화면 개수를 늘리는 것이 아니라 **현재 9개 Template의 성공/실패/복구 상태를 실제 제품에서 끝까지 재현 가능하게 만드는 것**이다.
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
# KBX FE QA Hardening v45
|
||||
|
||||
## 1. 목적
|
||||
|
||||
v45는 컴포넌트 수를 늘리는 버전이 아니다. v44까지 완성한 Theme/State/Shell 위에서 **사용자가 반복 조회를 줄이면서도 개인정보·업무 민감값·화면 버전 충돌을 Preference에 남기지 않는가**, 그리고 **Home의 우선 항목이 정확한 후속 Context까지 이어지는가**를 실제 FE 품질 기준으로 닫는다.
|
||||
|
||||
## 2. QA 비판
|
||||
|
||||
기존 `KbxSearchPanel`에는 `remember`, `savedSearch` 계약이 있었지만 Golden T01에서 사용하지 않았다. 더 큰 문제는 기존 `screenPreferenceStore`가 `screenId`만 localStorage key로 사용하고 JSON을 그대로 반환했다는 점이다. Tenant/User 격리, ScreenVersion, 크기 제한, 타입 검증, 저장소 실패 복구가 없으므로 제품 표준의 개인화/재현성 계약보다 약했다.
|
||||
|
||||
Home도 우선순위 자체는 좋아졌지만 실패/진행 Operation을 선택하면 Runtime Center 전체 목록만 열려 사용자가 같은 대상을 다시 찾을 수 있었다. Exception Driven Home이 후속 탐색을 재발생시키면 Context Router 완성도가 낮다.
|
||||
|
||||
## 3. v45 구현
|
||||
|
||||
### 3.1 Screen Preference 안전 경계
|
||||
|
||||
저장 key:
|
||||
|
||||
`kbx.screen.preference.v2:{scope}:{ScreenId}:{ScreenVersion}`
|
||||
|
||||
여기서 scope는 AppFrame이 제공하는 Tenant/User 경계다. 저장 payload는 32KB 이하, 검색 기본값은 최대 24 key, 문자열은 최대 256자로 제한한다. localStorage 접근 실패는 화면 실패로 전파하지 않는다.
|
||||
|
||||
### 3.2 OMS T01 안전한 검색 개인화
|
||||
|
||||
`OMS-ORD-001`에 실제 `KbxSearchPanel`의 `remember` / `saved-search`를 연결했다.
|
||||
|
||||
저장 허용:
|
||||
- from / to
|
||||
- channelId
|
||||
- status
|
||||
- exceptionOnly
|
||||
|
||||
저장 제외:
|
||||
- `keyword`
|
||||
|
||||
`keyword`는 주문번호·주문자·상품명 등을 포함할 수 있으므로 기본 localStorage Preference에 저장하지 않는다. Route Context가 존재하면 Home/알림에서 전달된 명시적 Context가 저장 Preference보다 우선한다.
|
||||
|
||||
### 3.3 Home → Runtime Center 정확한 Context
|
||||
|
||||
`KbxOperationCenter` / `KbxNotificationCenter`에 `activeId`를 추가했다. Home의 우선 항목에서 특정 Operation/Notification을 선택하면 Runtime Panel을 열고 해당 Row를 `aria-current`로 표시하며 Focus/scroll한다. Header에서 일반 작업센터를 여는 경우에는 특정 항목을 강제하지 않는다.
|
||||
|
||||
### 3.4 Theme parity
|
||||
|
||||
Reference HTML/JavaScript에도 같은 `기본조건 저장 / 마지막 조회조건 기억`을 구현했다. Preference UI는 기존 Light/Dark/Forced Colors semantic token을 그대로 소비하며 새로운 Theme 계층을 만들지 않는다.
|
||||
|
||||
## 4. 보안 경계
|
||||
|
||||
Preference는 UX 편의 상태다. 권한, 주문 상태, 재고, 금액, Concurrency, Idempotency, Audit를 저장하거나 결정하지 않는다. Route Context도 기존 safe workspace resolver와 서버 권한/Domain 검증을 대체하지 않는다.
|
||||
|
||||
## 5. 검증
|
||||
|
||||
- TypeScript/Vue syntax gate
|
||||
- v41 Golden FE parity
|
||||
- v42 runnable FE/source boundary
|
||||
- v43 Theme/navigation
|
||||
- v44 State/navigation
|
||||
- v45 safe preference/navigation dedicated gate
|
||||
- Reference Chromium Browser QA 14/14
|
||||
|
||||
Browser QA는 동일 HTML/CSS/JavaScript asset을 inline harness로 실행했다. 현재 환경에서 package registry가 차단되어 실제 Vite/PrimeVue/AG Grid live runtime E2E로 과장하지 않는다.
|
||||
|
||||
## 6. 다음 병목
|
||||
|
||||
다음은 새 Template이 아니라 실제 Vue Runtime에서 `pnpm install/build` 후 Search Preference를 포함한 Playwright E2E, 그리고 T02/T03의 Permission/Conflict/Integration Error와 server-side Full-query XLSX를 닫는 것이 우선이다.
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
# KBX FE QA Hardening v46
|
||||
|
||||
## 1. 목적
|
||||
|
||||
v46은 새 화면 타입이나 새 UI Framework를 추가하는 버전이 아니다. v45까지의 Theme, State, Safe Preference 기반 위에서 **업무 재개성(work resume)** 과 **T09 오류 복구**를 실제 FE 계약으로 닫는다.
|
||||
|
||||
판단 기준은 단순하다.
|
||||
|
||||
- 새로고침 후 사용자가 다시 메뉴를 찾아야 하는가?
|
||||
- 업무 위치를 복원한다고 Dirty Form/Grid 값까지 브라우저 저장소에 넣고 있지는 않은가?
|
||||
- Home에서 복원된 업무를 키보드만으로 다시 열 수 있는가?
|
||||
- WMS 초기 조회가 실패했을 때 현장 사용자가 같은 화면에서 다시 시도할 수 있는가?
|
||||
|
||||
## 2. QA 비판
|
||||
|
||||
### 2.1 Preference는 복원되지만 Workspace는 사라졌다
|
||||
|
||||
v45에서는 Theme, 즐겨찾기, 최근 메뉴, 안전한 검색 기본조건은 복원되지만 열린 Workspace Tabs는 메모리에만 있었다. 브라우저 새로고침 후 다시 메뉴를 찾아야 하므로 한국형 ERP의 다중업무 탭 문법에 비해 재개성이 약했다.
|
||||
|
||||
반대로 이를 해결한다고 전체 Form/Grid state를 localStorage에 저장하는 것은 더 나쁜 해결이다. Dirty 데이터는 오래된 서버 상태, 개인정보, 주문/재고 업무값을 브라우저 저장소에 남기고 복원 시점의 Domain truth와 충돌할 수 있다.
|
||||
|
||||
### 2.2 Home에서 active tab이 없으면 Workspace Tab keyboard entry가 사라졌다
|
||||
|
||||
Home에서는 `activeKey=null`이므로 기존 `KbxWorkspaceTabs`의 모든 main tab button이 `tabindex=-1`이었다. 화면에는 업무 탭이 보이지만 Keyboard Tab으로 탭 영역에 들어갈 수 없는 접근성 결함이다.
|
||||
|
||||
### 2.3 Navigation Preference의 최초 읽기 경로가 완전히 fail-soft가 아니었다
|
||||
|
||||
v45의 화면별 `screenPreferenceStore`는 storage 예외를 방어했지만 `workspaceStore.loadPreference()`의 최초 `localStorage.getItem()`은 try/catch 밖에 있었다. 정책상 Web Storage가 차단된 환경에서 Shell 초기화를 깨뜨릴 가능성이 있었다.
|
||||
|
||||
### 2.4 T09는 Error UI는 있었지만 실제 Retry loop가 끊겨 있었다
|
||||
|
||||
`KbxWmsMobilePage`는 `KbxTemplateStateBoundary`를 사용하지만 retry event를 상위 업무 Page로 전달하지 않았다. `WMS-PICK-001`도 TanStack Query의 loading/error 상태를 mobile template state에 연결하지 않았다. 따라서 초기 작업 조회 실패 시 공통 오류 표시는 가능해도 Recovery Action이 실제 Query 재조회로 이어지지 않았다.
|
||||
|
||||
## 3. v46 구현
|
||||
|
||||
### 3.1 Safe Workspace Resume
|
||||
|
||||
신규 `workspaceSessionStore.ts`는 `sessionStorage`만 사용한다.
|
||||
|
||||
저장:
|
||||
|
||||
- ScreenId
|
||||
- 안전한 pathname
|
||||
- Pin 여부
|
||||
- openedAt / lastActivatedAt
|
||||
|
||||
저장하지 않음:
|
||||
|
||||
- Query
|
||||
- Hash
|
||||
- Dirty Form 값
|
||||
- Dirty Grid 값
|
||||
- Lookup 입력값
|
||||
- 업무 DTO
|
||||
- Permission/Domain state
|
||||
|
||||
Dirty 탭은 session snapshot에서 제외하고 `dirtyDiscardedCount`만 제한된 숫자로 기록한다. 이 숫자는 사용자에게 “미저장 편집은 안전상 복원하지 않았다”는 사실을 설명하기 위한 것이며 업무 데이터는 포함하지 않는다.
|
||||
|
||||
복원 시 AppFrame은 다시:
|
||||
|
||||
1. 현재 permission 확인
|
||||
2. Screen registry 확인
|
||||
3. `resolveKbxSafeWorkspacePath()`로 route capability 검증
|
||||
4. 현재 Screen title 재사용
|
||||
5. `dirty=false`, `resumed=true`로 복원
|
||||
|
||||
한다. 저장된 문자열을 신뢰해 화면을 열지 않는다.
|
||||
|
||||
### 3.2 Home/Workspace Resume UX
|
||||
|
||||
복원된 업무는 Home `바로 시작`에서 일반 `열림`과 구분해 `복원`으로 표시한다. Workspace Tab에도 `↺`와 접근성 이름 `이전 세션에서 복원됨`을 표시한다.
|
||||
|
||||
사용자가 해당 업무를 실제로 열면 `resumed=false`가 되어 복원 표시는 제거된다. 이는 “복원 후보”와 “현재 사용 중인 업무”를 구분한다.
|
||||
|
||||
Home에서 active tab이 없어도 첫 Workspace Tab이 roving tabindex `0`이 되도록 수정했다. 따라서 Mouse뿐 아니라 Keyboard Tab → ArrowLeft/Right/Home/End 흐름으로 재개할 수 있다.
|
||||
|
||||
### 3.3 Storage fail-soft 통일
|
||||
|
||||
Navigation Preference의 초기 `localStorage.getItem()`도 try/catch 내부로 이동했다. Storage 차단은 Preference가 없는 상태로 degrade되며 Home/Shell 기동 자체를 실패시키지 않는다.
|
||||
|
||||
### 3.4 T09 WMS Error Recovery
|
||||
|
||||
`KbxWmsMobilePage 1.6.0`은 `retry` / `emptyAction`을 상위 Page로 전달하고 모바일 기본 retry label을 `다시 시도`로 제공한다.
|
||||
|
||||
`useWmsPicking()`은 TanStack Query 상태를:
|
||||
|
||||
- loading
|
||||
- error
|
||||
- empty
|
||||
- ready
|
||||
|
||||
로 명시적으로 매핑한다. `WmsPickingPage.vue`는 이를 `contentState`로 전달하고 `[작업 다시 불러오기]`를 `query.refetch()`에 연결한다.
|
||||
|
||||
Scanner success, 재고, idempotency 같은 Domain 결과는 여전히 Server authority다. 이번 변경은 실패 복구 UX를 연결한 것이지 Client를 업무 truth로 만든 것이 아니다.
|
||||
|
||||
## 4. Theme 적용 원칙
|
||||
|
||||
v46은 새로운 Theme를 추가하지 않는다. KBX Business Light/Dark와 Forced Colors 의미 체계를 그대로 사용한다.
|
||||
|
||||
`복원`, `미저장`, `Pin`, `Error`는 색만으로 구분하지 않으며 icon/text/aria state를 같이 사용한다. Theme 변경은 Workspace Resume 여부나 Permission, Domain state를 바꾸지 않는다.
|
||||
|
||||
## 5. Browser QA
|
||||
|
||||
Reference HTML/CSS/JavaScript에서 20개 체크를 실행했다.
|
||||
|
||||
핵심 시나리오:
|
||||
|
||||
- Dark theme 적용
|
||||
- clean 품목관리 tab open + pin
|
||||
- 주문등록 수정 → Dirty
|
||||
- session snapshot에 clean tab만 존재
|
||||
- Dirty Form 값 문자열이 snapshot에 없음
|
||||
- 새 문서에서 clean tab `복원`
|
||||
- Dirty tab 복원 안 됨
|
||||
- Home에서 Workspace 첫 tab keyboard entry 가능
|
||||
- Home에서 미저장 복원 제외 설명
|
||||
- 복원 업무를 실제 열면 resume marker 제거
|
||||
- Web Storage 접근 자체가 차단되어도 Home render
|
||||
- 1280×720 document horizontal overflow 0
|
||||
- runtime page error 0
|
||||
|
||||
실행환경의 direct-origin navigation이 관리자 정책으로 차단되어, 동일 Reference asset을 inline Playwright harness에서 실행하고 Web Storage는 in-memory shim으로 재현했다. 이는 실제 origin/sessionStorage browser E2E와 구분한다.
|
||||
|
||||
## 6. QA 자동화 자체의 기술부채 정리
|
||||
|
||||
전체 Gate에서 v46 제품 동작이 아니라 `v44|v45` 같은 과거 버전 문자열, 과거 Home purpose 문구, 특정 Component exact version을 source text로 요구하는 legacy validator가 회귀를 만들었다.
|
||||
|
||||
이를 버전마다 문자열을 추가하는 방식으로 봉합하지 않고, foundation iteration 최소값, component category/존재, minimum semver 등 **실제 보장하려던 의미 계약**으로 바꿨다. 정적 Gate도 제품과 같은 기술부채 관리 대상이어야 하며, 구현 문구를 동결하는 테스트는 리팩터링을 방해한다.
|
||||
|
||||
최종 aggregate는 153 tokens, 20 screens, 84 components, 69 catalog entries, 306 TS/Vue units이며 Design Debt는 `116 <= 131`을 유지한다.
|
||||
|
||||
## 7. 남은 병목
|
||||
|
||||
다음 우선순위는 새 Template이 아니다.
|
||||
|
||||
1. network-enabled 환경에서 실제 Vue/Vite install + build
|
||||
2. 실제 browser origin에서 sessionStorage Workspace Resume E2E
|
||||
3. OMS-ORD-001 Search Preference + Workspace Resume 조합
|
||||
4. OMS-ORD-002 keyboard-only F2 → Detail → F8
|
||||
5. T02/T03 Permission / Conflict / Integration failure
|
||||
6. WMS-PICK-001 실제 API 초기 실패 → Retry → Recovery
|
||||
7. server-side full-query XLSX / Hangfire export
|
||||
|
||||
이 항목이 닫히기 전 T10/T11 또는 새로운 Runtime UI Engine을 추가하는 것은 과유불급이다.
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
# KBX FE QA Hardening v47
|
||||
|
||||
## 1. 판단
|
||||
|
||||
v46까지 Workspace Resume, Theme, Empty/Error Recovery는 많이 안정화되었지만 실제 Golden Screen을 다시 추적하면 **Query 실패와 Command 실패의 UX가 동일한 수준으로 닫혀 있지 않았다.**
|
||||
|
||||
- 조회 실패: `KbxTemplateStateBoundary`로 복구 가능
|
||||
- Validation: Field/Cell + Summary 존재
|
||||
- Conflict: `KbxConflictResolver` 존재
|
||||
- 그러나 `business-rule / permission / not-found / integration / system`은 실제 T01/T02/T03의 저장·출고·상태변경 경로에서 공통 표현이 없고 일부는 throw로 빠질 수 있었다.
|
||||
|
||||
업무시스템에서는 이 차이가 중요하다. 조회 API가 실패한 것과 사용자의 출고지시가 업무규칙 때문에 거부된 것은 같은 `Error`가 아니다.
|
||||
|
||||
## 2. v47 원칙
|
||||
|
||||
```text
|
||||
Query state
|
||||
→ Loading / Empty / Error / Retry
|
||||
|
||||
Field validation
|
||||
→ Field / Cell / Validation Summary
|
||||
|
||||
Concurrency
|
||||
→ 내 값 vs 최신 값 / Reload
|
||||
|
||||
Command problem
|
||||
→ 업무규칙 / 권한 / 데이터 / 외부연계 / 시스템
|
||||
→ 현재 Context 유지
|
||||
→ 가능한 다음 행동
|
||||
```
|
||||
|
||||
Problem text나 Action ID는 신뢰 경계가 아니다. Server가 내려준 `actions`를 그대로 `executeCommand(action.id)`에 연결하지 않는다. `KbxProblemFeedback`은 기본적으로 action allow-list가 비어 있으며 화면이 명시적으로 허용한 ID만 렌더링한다.
|
||||
|
||||
## 3. 구현
|
||||
|
||||
### `KbxProblemFeedback 1.0.0`
|
||||
|
||||
지원:
|
||||
|
||||
- business-rule
|
||||
- permission
|
||||
- not-found
|
||||
- integration
|
||||
- system
|
||||
- correlationId
|
||||
- retryable retry
|
||||
- dismiss
|
||||
- 명시적 action allow-list
|
||||
|
||||
Validation과 Conflict는 이미 더 구체적인 UX가 있으므로 기존 전용 컴포넌트를 유지한다.
|
||||
|
||||
### Template
|
||||
|
||||
- `KbxListPage 1.8.0`
|
||||
- `KbxMasterPage 1.7.0`
|
||||
- `KbxTransactionPage 1.7.0`
|
||||
|
||||
세 Template 모두 `problem / problemActionAllowlist / retryProblem / dismissProblem` 계약을 가진다.
|
||||
|
||||
### Golden Screens
|
||||
|
||||
`OMS-ORD-001`
|
||||
- Bulk 출고 실패 시 Selection과 조회 Context 유지
|
||||
- retryable Problem이면 같은 Bulk 대상 재시도 가능
|
||||
- dismiss 후 현재 화면 유지
|
||||
|
||||
`OMS-ORD-002`
|
||||
- Save/Confirm Problem을 Form 위에서 설명
|
||||
- Validation/Conflict와 중복 표현하지 않음
|
||||
- retryable Save/Confirm 재시도
|
||||
|
||||
`ERP-MST-ITEM-001`
|
||||
- 목록 상세 로드 실패 시 현재 편집 데이터를 덮어쓰지 않음
|
||||
- Save/Deactivate Problem을 공통 Surface로 표현
|
||||
- 재시도 시 마지막 실패 operation만 재실행
|
||||
|
||||
## 4. Reference HTML/JavaScript
|
||||
|
||||
실제 예외 주문을 선택하고 출고지시하면 `업무 규칙` Problem을 보여준다. `문제 N건만 선택`은 실패대상만 다시 선택하며 현재 주문조회 화면을 떠나지 않는다.
|
||||
|
||||
Dark Theme에서도 warning/danger/info semantic surface를 사용하고 상태는 색상 외 `업무 규칙` 텍스트로 함께 표시한다.
|
||||
|
||||
## 5. QA
|
||||
|
||||
Reference Browser QA: **12/12 PASS**
|
||||
|
||||
- Home render
|
||||
- Dark Theme
|
||||
- T01 open
|
||||
- 예외 Row 선택
|
||||
- Business Problem 표시
|
||||
- 복구 Action 존재
|
||||
- 색상 외 유형 Label
|
||||
- 실패건 재선택
|
||||
- Problem dismiss
|
||||
- 1280px horizontal overflow 0
|
||||
- runtime page error 0
|
||||
|
||||
증적: `docs/evidence/v47-browser-problem-theme.json`
|
||||
|
||||
## 6. 완료로 계산하지 않는 것
|
||||
|
||||
이 실행환경은 package registry 제한 때문에 실제 Vite + Vue + PrimeVue + AG Grid live runtime을 설치해 Playwright로 실행하지 못한다. 따라서 v47 실제 Vue 변경은 syntax/static/governance Gate로, 브라우저 상호작용은 동일 Reference HTML/CSS/JavaScript로 분리하여 증적한다.
|
||||
|
||||
또한 Client Problem UI는 보안 경계가 아니다. Permission, Domain rule, Concurrency, Idempotency, Integration state의 최종 truth는 Server에 남는다.
|
||||
|
||||
## 7. 다음 우선순위
|
||||
|
||||
1. 실제 Vue origin에서 T01 bulk business-rule / permission E2E
|
||||
2. T03 save integration/system retry E2E
|
||||
3. T02 stale-version conflict + command problem 병행 검증
|
||||
4. Full-query XLSX / Hangfire 결과 Problem 연결
|
||||
5. WMS scan command의 server-rejected / integration-degraded UX
|
||||
|
||||
새 Template/Metadata Engine 확장은 이 항목보다 우선하지 않는다.
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
# KBX FE QA Hardening v48
|
||||
|
||||
## 1. 판단
|
||||
|
||||
v47에서 T01/T02/T03의 `business-rule / permission / not-found / integration / system` Command Problem을 현재 업무 Context 안에서 복구할 수 있게 했지만, 실제 FE 전체를 다시 비교하면 **템플릿 유형별 실패 UX와 Reference 동작 수준이 달랐다.**
|
||||
|
||||
- T04/T06/T07/T08 actual Vue는 Mutation/Import 실패 시 일부 경로가 공통 Actionable Problem Surface까지 연결되지 않았다.
|
||||
- Reference T05/T06/T07의 검색조건은 화면에 존재해도 조회 Action 일부가 Toast 또는 키보드 경로에 의존했다.
|
||||
- Search Panel 내부 `[조회]`가 `type="button"`인데 click binding이 없는 경로가 있어 Mouse Complete를 위반했다.
|
||||
- T08은 샘플 파일명을 상태에 넣는 방식이라 실제 Browser File 선택/Drop의 최소 계약을 재현하지 못했다.
|
||||
- T08 데이터 흐름은 6단계인데 `.import-steps` 기본 CSS는 5열이었다.
|
||||
|
||||
v48은 새 Template이나 Metadata Engine을 추가하지 않고 이 **FE completion gap**만 닫는다.
|
||||
|
||||
## 2. 완료 루프 기준
|
||||
|
||||
```text
|
||||
T04 Fast Entry
|
||||
입력/붙여넣기 → 검증 → 저장 → 실패 원인/재시도
|
||||
|
||||
T05 Master-Detail
|
||||
검색조건 → Loading → 결과/Empty → Master 선택 → Detail/History
|
||||
|
||||
T06 Work Queue
|
||||
검색/빠른필터 → 대상 선택 → 배정/Wave → 실패/결과
|
||||
|
||||
T07 Reconcile
|
||||
조건/불일치 → 대상 선택 → 재처리 → 결과/복구
|
||||
|
||||
T08 Import
|
||||
실제 File → Mapping → Validation → Preview → Commit/Job → Result
|
||||
```
|
||||
|
||||
버튼이 존재하거나 Toast가 발생하는 것만으로 완료하지 않는다.
|
||||
|
||||
## 3. Actual Vue 보강
|
||||
|
||||
### T04 `ERP-PRICE-001`
|
||||
|
||||
`useItemPriceFastEntry`에 `KbxProblem` 상태, 실패 작업, retry/dismiss를 연결했다. Local/Grid validation은 기존 Cell/Validation Summary를 유지하고, Server에서 인식 가능한 비-validation Problem만 Template Actionable Surface로 보낸다.
|
||||
|
||||
### T06 `COMMON Operations Queue`
|
||||
|
||||
claim/resolve/retry 등 Mutation 실패를 `KbxWorkQueuePage`의 Problem Surface로 연결한다. 실패 시 Queue Context를 지우지 않고, 성공했을 때만 Selection/Detail 상태를 정리한다.
|
||||
|
||||
### T07 `COMMON Reconcile`
|
||||
|
||||
예외 생성/재처리 계열 실패도 현재 조회조건과 Selection을 유지한다. Retry는 마지막 실패 작업의 안전한 Closure만 재실행한다.
|
||||
|
||||
### T08 `OMS-ORD-003`
|
||||
|
||||
Upload, Mapping 저장, 검증, Commit, Template/Error download를 동일 Problem boundary로 연결했다. Import의 Server Staging/Domain Validation 책임은 변경하지 않는다.
|
||||
|
||||
## 4. Template 계약 통일
|
||||
|
||||
다음 Template이 `problem / problemActionAllowlist / retryProblem / dismissProblem`을 공통 지원한다.
|
||||
|
||||
- KbxFastEntryPage
|
||||
- KbxMasterDetailPage
|
||||
- KbxQueuePage
|
||||
- KbxWorkQueuePage
|
||||
- KbxReconcilePage
|
||||
- KbxImportPage
|
||||
|
||||
`KbxProblemFeedback 1.1.0`은 Validation Problem이 상위로 올라오는 경우 첫 3개 메시지와 잔여 건수를 제한적으로 요약한다. 이것은 Field/Cell validation을 대체하지 않는다.
|
||||
|
||||
## 5. Reference HTML/JavaScript 고도화
|
||||
|
||||
### T05
|
||||
|
||||
창고/품목 입력값을 실제 fixture Projection에 적용한다. 조회 시 Loading, 결과, Empty, 조건 초기화를 실제 상태 전이로 재현한다.
|
||||
|
||||
### T06
|
||||
|
||||
작업자/통합검색과 빠른필터를 실제 Queue rows에 함께 적용한다. 0건이면 Empty Recovery가 제공되고, Selection은 현재 visible rows 기준이다.
|
||||
|
||||
### T07
|
||||
|
||||
기준일/통합검색/불일치 조건을 실제 reconcile rows에 적용하고 재처리는 `Actual=Expected`, `Difference=0`, `상태=해결`로 Reference state를 변경한다.
|
||||
|
||||
### T08
|
||||
|
||||
샘플 파일명 주입을 제거했다. 실제 `<input type="file">`과 Drop Event의 `File`을 사용하며 `.xlsx/.xls`, 20MB presentation guard를 적용한다. 6단계 흐름에 맞춰 visual grid도 6열로 정합화했다.
|
||||
|
||||
File 확장자/크기 확인은 FE 편의 검증일 뿐, 실제 Import의 File parsing/Staging/Normalization/Business Validation/Commit은 Server 계약이다.
|
||||
|
||||
## 6. Chromium QA에서 추가 발견한 결함
|
||||
|
||||
초기 v48 QA에서 T05/T06/T07의 Search Panel `[조회]`가 `type="button"`인데 click handler가 없어 **Enter/F3은 되지만 마우스 조회는 안 되는 결함**을 발견했다.
|
||||
|
||||
이를 각각 `inventorySearchInline`, `queueSearchInline`, `reconcileSearchInline`로 명시적으로 bind하고 v48 Gate에 회귀 조건을 추가했다.
|
||||
|
||||
이 결함은 Screenshot이나 정적 구조 검사만으로 발견하기 어렵다. `Keyboard Accelerated`와 `Mouse Complete`를 별개의 QA 축으로 검증해야 하는 이유다.
|
||||
|
||||
## 7. Theme
|
||||
|
||||
KBX Business Light/Dark의 semantic token 계약은 유지한다. 이번 변경은 Theme palette를 확장하지 않고 검색/Empty/Problem/File state가 Dark에서도 동일한 정보 위계와 Focus를 갖게 한다.
|
||||
|
||||
Theme는 Permission이나 Domain rule에 관여하지 않는다.
|
||||
|
||||
## 8. Browser QA
|
||||
|
||||
Reference Chromium inline-asset harness: **30/30 PASS**
|
||||
|
||||
- Home boot + Dark Theme
|
||||
- T05 실제 검색 → Loading → 결과 축소 → Empty → Reset
|
||||
- T06 작업자 검색 → Loading → 결과 축소 → Empty → Reset
|
||||
- T07 원인 검색 → Loading → mismatch selection → Resolution state mutation
|
||||
- T08 실제 File input, File name 반영, 잘못된 extension 거부, 6-step, Mapping/Validation/Preview/Commit 이동
|
||||
- 1280×720 document horizontal overflow 0
|
||||
- runtime/page/console error 0
|
||||
|
||||
증적: `docs/evidence/v48-browser-template-completion.json`
|
||||
|
||||
Preview:
|
||||
|
||||
- `apps/web/fe-reference/previews/v48-t05-inventory-dark-1440x900.png`
|
||||
- `apps/web/fe-reference/previews/v48-t08-file-dark-1440x900.png`
|
||||
|
||||
CSP meta는 inline harness에서만 제거했으며 Production CSP는 기존 정적 Governance Gate로 검증한다. 이 증적을 actual Vite/Vue/PrimeVue/AG Grid runtime E2E라고 주장하지 않는다.
|
||||
|
||||
## 9. 남은 우선순위
|
||||
|
||||
1. network-enabled 환경 actual Vue install/typecheck/build
|
||||
2. T04/T06/T07/T08 actual runtime Problem Playwright
|
||||
3. T08 실제 XLSX Template/Upload/Staging/Job/Result E2E
|
||||
4. T05/T06/T07 API Loading/Empty/Error/Permission/Integration failure E2E
|
||||
5. 1440×900 / 1280×720 / 200% Zoom × Light/Dark/Forced Colors visual regression
|
||||
|
||||
새 T10/T11, Runtime JSON UI, 자체 Grid/Theme Engine은 이 항목보다 우선하지 않는다.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user